0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-01-20 22:12:38 -05:00

fix: handle immutable response object (#12106)

* fix: handle immutable response object

* changeset
This commit is contained in:
Matt Kane 2024-10-03 09:21:11 +01:00 committed by GitHub
parent fbe1bc51d8
commit d3a74da196
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 36 additions and 12 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Handles case where an immutable Response object is returned from an endpoint

View file

@ -50,7 +50,7 @@ export async function renderEndpoint(
return new Response(null, { status: 500 });
}
const response = await handler.call(mod, context);
let response = await handler.call(mod, context);
if (!response || response instanceof Response === false) {
throw new AstroError(EndpointDidNotReturnAResponse);
@ -59,10 +59,20 @@ export async function renderEndpoint(
// Endpoints explicitly returning 404 or 500 response status should
// NOT be subject to rerouting to 404.astro or 500.astro.
if (REROUTABLE_STATUS_CODES.includes(response.status)) {
// Only `Response.redirect` headers are immutable, therefore a `try..catch` is not necessary.
// Note: `Response.redirect` can only be called with HTTP status codes: 301, 302, 303, 307, 308.
// Source: https://developer.mozilla.org/en-US/docs/Web/API/Response/redirect_static#parameters
response.headers.set(REROUTE_DIRECTIVE_HEADER, 'no');
try {
response.headers.set(REROUTE_DIRECTIVE_HEADER, 'no');
} catch (err) {
// In some cases the response may have immutable headers
// This is the case if, for example, the user directly returns a `fetch` response
// There's no clean way to check if the headers are immutable, so we just catch the error
// Note that response.clone() still has immutable headers!
if((err as Error).message?.includes('immutable')) {
response = new Response(response.body, response);
response.headers.set(REROUTE_DIRECTIVE_HEADER, 'no');
} else {
throw err;
}
}
}
return response;

View file

@ -0,0 +1,3 @@
export async function GET({ request }) {
return fetch("https://http.im/status/500", request)
}

View file

@ -137,23 +137,29 @@ describe('API routes in SSR', () => {
assert.equal(count, 2, 'Found two separate set-cookie response headers');
});
it('can return an immutable response object', async () => {
const response = await fixture.fetch('/fail');
const text = await response.text();
assert.equal(response.status, 500);
assert.equal(text, '');
});
it('Has valid api context', async () => {
const response = await fixture.fetch('/context/any');
assert.equal(response.status, 200);
const data = await response.json();
assert.equal(data.cookiesExist, true);
assert.equal(data.requestExist, true);
assert.equal(data.redirectExist, true);
assert.equal(data.propsExist, true);
assert.ok(data.cookiesExist);
assert.ok(data.requestExist);
assert.ok(data.redirectExist);
assert.ok(data.propsExist);
assert.deepEqual(data.params, { param: 'any' });
assert.match(data.generator, /^Astro v/);
assert.equal(
assert.ok(
['http://[::1]:4321/blog/context/any', 'http://127.0.0.1:4321/blog/context/any'].includes(
data.url,
),
true,
);
assert.equal(['::1', '127.0.0.1'].includes(data.clientAddress), true);
assert.ok(['::1', '127.0.0.1'].includes(data.clientAddress));
assert.equal(data.site, 'https://mysite.dev/subsite/');
});
});