Path: blob/master/src/packages/project/servers/browser/http-server.ts
1449 views
/*1* This file is part of CoCalc: Copyright © 2022 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6This is an express http server that is meant to receive connections7only from web browser clients that signed in as collaborators on8this projects. It serves both HTTP and websocket connections, which9should be proxied through some hub.10*/1112import compression from "compression";13import express from "express";14import { createServer } from "http";15import { writeFile } from "node:fs/promises";16import { join } from "node:path";1718import basePath from "@cocalc/backend/base-path";19import initWebsocket from "@cocalc/project/browser-websocket/server";20import initWebsocketFs from "../websocketfs";21import initSyncFs from "../sync-fs";22import { browserPortFile, project_id } from "@cocalc/project/data";23import initDirectoryListing from "@cocalc/project/directory-listing";24import { getOptions } from "@cocalc/project/init-program";25import * as kucalc from "@cocalc/project/kucalc";26import { getLogger } from "@cocalc/project/logger";27import { once } from "@cocalc/util/async-utils";28import initRootSymbolicLink from "./root-symlink";2930const winston = getLogger("browser-http-server");3132export default async function init(): Promise<void> {33winston.info("starting server...");3435const base = join(basePath, project_id, "raw") + "/";3637const app = express();38app.disable("x-powered-by"); // https://github.com/sagemathinc/cocalc/issues/61013940const server = createServer(app);4142// WEBSOCKET SERVERS43// **CRITICAL:** This *must* be above the app.use(compression())44// middleware below, since compressing/uncompressing the websocket45// would otherwise happen, and that slows it down a lot.46// Setup the ws websocket server, which is used by clients47// for direct websocket connections to the project, and also48// serves primus.js, which is the relevant client library.49winston.info("initializing websocket server");50// We have to explicitly also include the base as a parameter51// to initWebsocket, since of course it makes deeper user of server.52app.use(base, initWebsocket(server, base));53initWebsocketFs(server, base);54// This uses its own internal lz4 compression:55initSyncFs(server, base);5657// CRITICAL: keep this after the websocket stuff or anything you do not58// want to have compressed.59// suggested by http://expressjs.com/en/advanced/best-practice-performance.html#use-gzip-compression60app.use(compression());6162winston.info("creating root symbolic link");63await initRootSymbolicLink();6465if (kucalc.IN_KUCALC) {66// Add /health (used as a health check for Kubernetes) and /metrics (Prometheus)67winston.info("initializing KuCalc only health metrics server");68kucalc.init_health_metrics(app, project_id);69}7071// Setup the directory_listing/... server, which is used to provide directory listings72// to the hub (at least in KuCalc). It is still used by HUB! But why? Maybe it is only73// for the deprecated public access to a project? If so, we can get rid of all of that.74winston.info("initializing directory listings server (DEPRECATED)");75app.use(base, initDirectoryListing());7677const options = getOptions();78server.listen(options.browserPort, options.hostname);79await once(server, "listening");80const address = server.address();81if (address == null || typeof address == "string") {82// null = failed; string doesn't happen since that's for unix domain83// sockets, which we aren't using.84// This is probably impossible, but it makes typescript happier.85throw Error("failed to assign a port");86}87const assignedPort = address.port; // may be a server assigned random port.88winston.info(89`Started -- port=${assignedPort}, host='${options.hostname}', base='${base}'`,90);9192winston.info(`Writing port to ${browserPortFile}`);93await writeFile(browserPortFile, `${assignedPort}`);94}959697