Skip to content

Commit a60280d

Browse files
wilkmaciejkt3k
andauthored
fix(http): handle HEAD requests in serveDir (#7024)
Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
1 parent 6df7f21 commit a60280d

2 files changed

Lines changed: 57 additions & 3 deletions

File tree

http/file_server.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ export async function serveFile(
337337
}
338338

339339
async function serveDirIndex(
340+
req: Request,
340341
dirPath: string,
341342
options: {
342343
showDotfiles: boolean;
@@ -406,9 +407,13 @@ async function serveDirIndex(
406407

407408
const headers = createBaseHeaders();
408409
headers.set(HEADER.ContentType, "text/html; charset=UTF-8");
410+
if (req.method === METHOD.Head) {
411+
const pageSize = new TextEncoder().encode(page).byteLength;
412+
headers.set(HEADER.ContentLength, String(pageSize));
413+
}
409414

410415
const status = STATUS_CODE.OK;
411-
return new Response(page, {
416+
return new Response(req.method === METHOD.Head ? null : page, {
412417
status,
413418
statusText: STATUS_TEXT[status],
414419
headers,
@@ -660,7 +665,7 @@ export async function serveDir(
660665
req: Request,
661666
opts: ServeDirOptions = {},
662667
): Promise<Response> {
663-
if (req.method !== METHOD.Get) {
668+
if (req.method !== METHOD.Get && req.method !== METHOD.Head) {
664669
return createStandardResponse(STATUS_CODE.MethodNotAllowed);
665670
}
666671

@@ -796,7 +801,7 @@ async function createServeDirResponse(
796801
}
797802

798803
if (showDirListing) { // serve directory list
799-
return serveDirIndex(fsPath, { showDotfiles, target, quiet });
804+
return serveDirIndex(req, fsPath, { showDotfiles, target, quiet });
800805
}
801806

802807
return createStandardResponse(STATUS_CODE.NotFound);

http/file_server_test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,55 @@ Deno.test(async function serveFileHeadRequest() {
11531153
assertEquals(res.headers.get("content-length"), "10034");
11541154
});
11551155

1156+
Deno.test("serveDir() handles HEAD request for a file", async () => {
1157+
const req = new Request("http://localhost/test_file.txt", {
1158+
method: "HEAD",
1159+
});
1160+
const res = await serveDir(req, serveDirOptions);
1161+
1162+
assert(!res.body);
1163+
assertEquals(res.status, 200);
1164+
assertEquals(res.statusText, "OK");
1165+
assertEquals(res.headers.get("content-type"), "text/plain; charset=UTF-8");
1166+
assertEquals(res.headers.get("content-length"), `${TEST_FILE_SIZE}`);
1167+
assertEquals(res.headers.get("etag"), TEST_FILE_ETAG);
1168+
assertEquals(res.headers.get("last-modified"), TEST_FILE_LAST_MODIFIED);
1169+
assertEquals(res.headers.get("accept-ranges"), "bytes");
1170+
});
1171+
1172+
Deno.test("serveDir() handles HEAD request for index.html", async () => {
1173+
const req = new Request(
1174+
"http://localhost/http/testdata/subdir-with-index/",
1175+
{ method: "HEAD" },
1176+
);
1177+
const res = await serveDir(req, { showIndex: true });
1178+
1179+
assert(!res.body);
1180+
assertEquals(res.status, 200);
1181+
assertEquals(res.headers.get("content-type"), "text/html; charset=UTF-8");
1182+
});
1183+
1184+
Deno.test("serveDir() handles HEAD request for directory listing", async () => {
1185+
const req = new Request("http://localhost/", { method: "HEAD" });
1186+
const res = await serveDir(req, serveDirOptions);
1187+
1188+
assert(!res.body);
1189+
assertEquals(res.status, 200);
1190+
assertEquals(res.headers.get("content-type"), "text/html; charset=UTF-8");
1191+
assert(Number(res.headers.get("content-length")) > 0);
1192+
});
1193+
1194+
Deno.test("serveDir() handles HEAD request for missing file", async () => {
1195+
const req = new Request("http://localhost/no_such_file.txt", {
1196+
method: "HEAD",
1197+
});
1198+
const res = await serveDir(req, serveDirOptions);
1199+
await res.body?.cancel();
1200+
1201+
assertEquals(res.status, 404);
1202+
assertEquals(res.statusText, "Not Found");
1203+
});
1204+
11561205
Deno.test("(unstable) serveDir() serves files without the need of html extension when cleanUrls=true", async () => {
11571206
const req = new Request("http://localhost/hello");
11581207
const res = await unstableServeDir(req, {

0 commit comments

Comments
 (0)