Skip to content
12 changes: 12 additions & 0 deletions http/file_server_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { getAvailablePort } from "@std/net/get-available-port";
import { concat } from "@std/bytes/concat";
import { lessThan, parse as parseSemver } from "@std/semver";
import { serveDir as unstableServeDir } from "./unstable_file_server.ts";
import { serveFile as unstableServeFile } from "./unstable_file_server.ts";

const moduleDir = dirname(fromFileUrl(import.meta.url));
const testdataDir = resolve(moduleDir, "testdata");
Expand Down Expand Up @@ -1176,3 +1177,14 @@ Deno.test("(unstable) serveDir() does not shadow existing files and directory if
assertEquals(res.status, 301);
assertEquals(res.headers.has("location"), true);
});

Deno.test("(unstable) serveFile() sends custom headers", async () => {
const req = new Request("http://localhost/testdata/test_file.txt");
const res = await unstableServeFile(req, TEST_FILE_PATH, {
headers: ["X-Extra: extra header"],
});

assertEquals(res.status, 200);
assertEquals(res.headers.get("X-Extra"), "extra header");
assertEquals(await res.text(), TEST_FILE_TEXT);
});
50 changes: 49 additions & 1 deletion http/unstable_file_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import {
serveDir as stableServeDir,
type ServeDirOptions as StableServeDirOptions,
serveFile as stableServeFile,
type ServeFileOptions as StableServeFileOptions,
} from "./file_server.ts";
export { serveFile, type ServeFileOptions } from "./file_server.ts";

/**
* Serves the files under the given directory root (opts.fsRoot).
Expand Down Expand Up @@ -61,3 +62,50 @@ export interface ServeDirOptions extends StableServeDirOptions {
*/
cleanUrls?: boolean;
}

/** Interface for serveFile options. */
export interface ServeFileOptions extends StableServeFileOptions {
/** Headers to add to each response
*
* @default {[]}
*/
headers?: string[];
}

/**
* Resolves a {@linkcode Response} with the requested file as the body.
*
* @experimental **UNSTABLE**: New API, yet to be vetted.
*
* @example Usage
* ```ts ignore
* import { serveFile } from "@std/http/file-server";
*
* Deno.serve((req) => {
* return serveFile(req, "README.md");
* });
* ```
*
* @param req The server request context used to cleanup the file handle.
* @param filePath Path of the file to serve.
* @param options Additional options.
* @returns A response for the request.
*/
export async function serveFile(
req: Request,
filePath: string,
options?: ServeFileOptions,
): Promise<Response> {
const response = await stableServeFile(req, filePath, options);

if (options?.headers) {
for (const header of options.headers) {
const headerSplit = header.split(":");
const name = headerSplit[0]!;
const value = headerSplit.slice(1).join(":");
response.headers.append(name, value);
}
}

return response;
}
Loading