0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-03-10 23:01:26 -05:00

fix: default head requests for endpoints when no explicit head method… (#13210)

* fix: default head requests for endpoints when no explicit head method implemented

* Update changeset for astro to minor

* Update changeset for default HEAD requests

* Update .changeset/afraid-turkeys-kneel.md

Co-authored-by: Reuben Tier <64310361+TheOtterlord@users.noreply.github.com>

---------

Co-authored-by: Matt Kane <m@mk.gg>
Co-authored-by: Reuben Tier <64310361+TheOtterlord@users.noreply.github.com>
This commit is contained in:
Vitalii Rybachenko 2025-02-13 06:22:19 -05:00 committed by GitHub
parent 8d4e566f54
commit 344e9bc480
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 61 additions and 2 deletions

View file

@ -0,0 +1,7 @@
---
'astro': minor
---
Handle `HEAD` requests to an endpoint when a handler is not defined.
If an endpoint defines a handler for `GET`, but does not define a handler for `HEAD`, Astro will call the `GET` handler and return the headers and status but an empty body.

View file

@ -19,8 +19,12 @@ export async function renderEndpoint(
const method = request.method.toUpperCase();
// use the exact match on `method`, fallback to ALL
const handler = mod[method] ?? mod['ALL'];
if (isPrerendered && method !== 'GET') {
let handler = mod[method] ?? mod['ALL'];
// use GET handler for HEAD requests
if (!handler && method === 'HEAD' && mod['GET']) {
handler = mod['GET'];
}
if (isPrerendered && !['GET', 'HEAD'].includes(method)) {
logger.warn(
'router',
`${url.pathname} ${bold(
@ -78,5 +82,10 @@ export async function renderEndpoint(
}
}
if (method === 'HEAD') {
// make sure HEAD responses doesnt have body
return new Response(null, response);
}
return response;
}

View file

@ -1135,6 +1135,16 @@ describe('astro:image', () => {
assert.equal(response.headers.get('content-type'), 'image/webp');
});
it('returns HEAD method ok for /_image', async () => {
const params = new URLSearchParams();
params.set('href', '/src/assets/penguin1.jpg?origWidth=207&origHeight=243&origFormat=jpg');
params.set('f', 'webp');
const response = await fixture.fetch('/some-base/_image?' + String(params), { method: 'HEAD' });
assert.equal(response.status, 200);
assert.equal(response.body, null);
assert.equal(response.headers.get('content-type'), 'image/webp');
});
it('does not interfere with query params', async () => {
let res = await fixture.fetch('/api?src=image.png');
const html = await res.text();

View file

@ -12,6 +12,7 @@ import {
const root = new URL('../../fixtures/api-routes/', import.meta.url);
const fileSystem = {
'/src/pages/incorrect.ts': `export const GET = _ => {}`,
'/src/pages/headers.ts': `export const GET = () => { return new Response('content', { status: 201, headers: { Test: 'value' } }) }`,
};
describe('endpoints', () => {
@ -44,4 +45,36 @@ describe('endpoints', () => {
await done;
assert.equal(res.statusCode, 500);
});
it('should respond with 404 if GET is not implemented', async () => {
const { req, res, done } = createRequestAndResponse({
method: 'HEAD',
url: '/incorrect-route',
});
container.handle(req, res);
await done;
assert.equal(res.statusCode, 404);
});
it('should respond with same code as GET response', async () => {
const { req, res, done } = createRequestAndResponse({
method: 'HEAD',
url: '/incorrect',
});
container.handle(req, res);
await done;
assert.equal(res.statusCode, 500); // get not returns response
});
it('should remove body and pass headers for HEAD requests', async () => {
const { req, res, done } = createRequestAndResponse({
method: 'HEAD',
url: '/headers',
});
container.handle(req, res);
await done;
assert.equal(res.statusCode, 201);
assert.equal(res.getHeaders().test, 'value');
assert.equal(res.body, undefined);
});
});