smc-hub
Version:
CoCalc: Backend webserver component
77 lines (67 loc) • 2.46 kB
text/typescript
/*
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
* License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details
*/
/*
Render a public path using the express static server.
*/
import * as fs from "fs";
import * as os_path from "path";
import { parse as parse_url } from "url";
import serveStatic from "serve-static";
import serveIndex from "serve-index";
import finalHandler from "finalhandler";
const STATIC_OPTIONS = { index: ["index.html", "index.htm"] };
const INDEX_OPTIONS = { icons: true };
// NOTE: we never clear these caches. However, there is at most one for
// every public_path, so it probably wastes very little memory. Someday
// should change to an LRU cache...
const serve_static_cache: { [dir: string]: Function } = {};
function get_serve_static(dir: string): Function {
return serve_static_cache[dir] != null
? serve_static_cache[dir]
: (serve_static_cache[dir] = serveStatic(dir, STATIC_OPTIONS));
}
const serve_index_cache: { [dir: string]: Function } = {};
function get_serve_index(dir: string): Function {
return serve_index_cache[dir] != null
? serve_index_cache[dir]
: (serve_index_cache[dir] = serveIndex(dir, INDEX_OPTIONS));
}
// res = html response object
export function render_static_path(opts: {
req: any;
res: any;
dir: string;
path: string;
}): void {
// We first test that we have access to the file (and it exists) before
// messing with the express static server. I don't know why, but for some
// reason it hangs forever when fed an unknown path, which obviously leads
// to a very bad experience for users!
const { req, res, dir, path } = opts;
// see https://stackoverflow.com/questions/14166898/node-js-with-express-how-to-remove-the-query-string-from-the-url
const pathname = parse_url(path).pathname;
if (pathname == null) {
// I think this shouldn't be possible, but typescript thinks it is.
res.sendStatus(404);
return;
}
const target = os_path.join(dir, decodeURI(pathname));
fs.access(target, fs.constants.R_OK, function (err) {
if (err != null) {
res.sendStatus(404);
return;
}
const s_static: Function = get_serve_static(dir);
const s_index: Function = get_serve_index(dir);
req.url = path === "" ? "/" : path;
s_static(req, res, function (err) {
if (err) {
finalHandler(err);
} else {
s_index(req, res, finalHandler);
}
});
});
}