UNPKG

smc-hub

Version:

CoCalc: Backend webserver component

77 lines (67 loc) 2.46 kB
/* * 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); } }); }); }