Skip to content

Instantly share code, notes, and snippets.

@laat
Created February 7, 2022 15:41
Show Gist options
  • Save laat/0c9c62bd8c164b6c89a2a6d28fdf3ffb to your computer and use it in GitHub Desktop.
Save laat/0c9c62bd8c164b6c89a2a6d28fdf3ffb to your computer and use it in GitHub Desktop.
import mime from "mime";
import { stat } from "node:fs/promises";
import http2, { Http2ServerRequest, Http2ServerResponse } from "node:http2";
import { join } from "node:path";
export type Http2Handler = (
next?: (
req: Http2ServerRequest,
res: Http2ServerResponse
) => Promise<void> | void
) => (req: Http2ServerRequest, res: Http2ServerResponse) => Promise<void>;
export const fileExists = async (path: string) => {
try {
const s = await stat(path);
if (s.isFile()) {
return true;
} else {
return false;
}
} catch (err) {
return false;
}
};
const singleValue = (values: string | string[] | undefined) =>
Array.isArray(values) ? values[0] : values;
const required = <T>(v: T): NonNullable<T> => {
if (v == null) {
throw new Error("Required");
} else {
return v as any;
}
};
const getFullPath = async (path: string) => {
try {
const stats = await stat(path);
if (stats.isDirectory()) {
if (await fileExists(join(path, "index.html"))) {
return join(path, "index.html");
}
return undefined;
} else if (stats.isFile()) {
return path;
}
} catch (err) {
// @ts-ignore
if (err.code === "ENOENT") {
return false;
}
throw err;
}
};
const {
HTTP2_HEADER_PATH,
HTTP2_HEADER_CONTENT_TYPE,
HTTP_STATUS_NOT_FOUND,
HTTP_STATUS_INTERNAL_SERVER_ERROR,
} = http2.constants;
export const serveStatic =
(options: { root: string }): Http2Handler =>
(next) =>
async (req, res) => {
const reqPath = required(singleValue(req.headers[HTTP2_HEADER_PATH]));
const fullPath = await getFullPath(join(options.root, reqPath));
if (fullPath) {
if (req.method === "GET") {
res.stream.respondWithFile(
fullPath,
{
[HTTP2_HEADER_CONTENT_TYPE]: mime.lookup(fullPath),
},
{
onError: (err) => {
if (err.code === "ENOENT") {
req.stream.respond({ ":status": HTTP_STATUS_NOT_FOUND });
} else {
req.stream.respond({
":status": HTTP_STATUS_INTERNAL_SERVER_ERROR,
});
}
req.stream.end();
},
}
);
} else if (req.method === "HEAD") {
res.writeHead(200);
} else {
return next?.(req, res);
}
} else {
return next?.(req, res);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment