Skip to content

Commit

Permalink
fix: default head requests for endpoints when no explicit head method… (
Browse files Browse the repository at this point in the history
#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 <[email protected]>

---------

Co-authored-by: Matt Kane <[email protected]>
Co-authored-by: Reuben Tier <[email protected]>
  • Loading branch information
3 people authored Feb 13, 2025
1 parent 8d4e566 commit 344e9bc
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 2 deletions.
7 changes: 7 additions & 0 deletions .changeset/afraid-turkeys-kneel.md
Original file line number Diff line number Diff line change
@@ -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.
13 changes: 11 additions & 2 deletions packages/astro/src/runtime/server/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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;
}
10 changes: 10 additions & 0 deletions packages/astro/test/core-image.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
33 changes: 33 additions & 0 deletions packages/astro/test/units/runtime/endpoints.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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);
});
});

0 comments on commit 344e9bc

Please sign in to comment.