Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/project/servers/browser/http-server.ts
Views: 687
/*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 bodyParser from "body-parser";13import compression from "compression";14import express from "express";15import { createServer } from "http";16import { writeFile } from "node:fs/promises";17import { join } from "node:path";1819import basePath from "@cocalc/backend/base-path";20import initWebsocket from "@cocalc/project/browser-websocket/server";21import initWebsocketFs from "../websocketfs";22import initSyncFs from "../sync-fs";23import { browserPortFile, project_id } from "@cocalc/project/data";24import initDirectoryListing from "@cocalc/project/directory-listing";25import { getOptions } from "@cocalc/project/init-program";26import initJupyter from "@cocalc/project/jupyter/http-server";27import * as kucalc from "@cocalc/project/kucalc";28import { getLogger } from "@cocalc/project/logger";29import initUpload from "@cocalc/project/upload";30import { once } from "@cocalc/util/async-utils";31import initRootSymbolicLink from "./root-symlink";32import initStaticServer from "./static";3334const winston = getLogger("browser-http-server");3536export default async function init(): Promise<void> {37winston.info("starting server...");3839const base = join(basePath, project_id, "raw") + "/";4041const app = express();42app.disable("x-powered-by"); // https://github.com/sagemathinc/cocalc/issues/61014344const server = createServer(app);4546// WEBSOCKET SERVERS47// **CRITICAL:** This *must* be above the app.use(compression())48// middleware below, since compressing/uncompressing the websocket49// would otherwise happen, and that slows it down a lot.50// Setup the ws websocket server, which is used by clients51// for direct websocket connections to the project, and also52// serves primus.js, which is the relevant client library.53winston.info("initializing websocket server");54// We have to explicitly also include the base as a parameter55// to initWebsocket, since of course it makes deeper user of server.56app.use(base, initWebsocket(server, base));57initWebsocketFs(server, base);58// This uses its own internal lz4 compression:59initSyncFs(server, base);6061// CRITICAL: keep this after the websocket stuff or anything you do not62// want to have compressed.63// suggested by http://expressjs.com/en/advanced/best-practice-performance.html#use-gzip-compression64app.use(compression());6566// Needed for POST file to custom path, which is used for uploading files to projects.67// parse application/x-www-form-urlencoded68app.use(bodyParser.urlencoded({ extended: true }));69// parse application/json70app.use(bodyParser.json());7172winston.info("creating root symbolic link");73await initRootSymbolicLink();7475if (kucalc.IN_KUCALC) {76// Add /health (used as a health check for Kubernetes) and /metrics (Prometheus)77winston.info("initializing KuCalc only health metrics server");78kucalc.init_health_metrics(app, project_id);79}8081// Setup the directory_listing/... server, which is used to provide directory listings82// to the hub (at least in KuCalc). It is still used by HUB! But why? Maybe it is only83// for the deprecated public access to a project? If so, we can get rid of all of that.84winston.info("initializing directory listings server (DEPRECATED)");85app.use(base, initDirectoryListing());8687// Setup the jupyter/... server, which is used by our jupyter server for blobs, etc.88winston.info("initializing Jupyter support HTTP server");89(async () => {90// if the BlobStore isn't available immediately, this will take a while to initialize, and91// we don't want to block the remainder of this setup...92app.use(base, await initJupyter());93})();9495// Setup the upload POST endpoint96winston.info("initializing file upload server");97app.use(base, initUpload());9899winston.info("initializing static server");100initStaticServer(app, base);101102const options = getOptions();103server.listen(options.browserPort, options.hostname);104await once(server, "listening");105const address = server.address();106if (address == null || typeof address == "string") {107// null = failed; string doesn't happen since that's for unix domain108// sockets, which we aren't using.109// This is probably impossible, but it makes typescript happier.110throw Error("failed to assign a port");111}112const assignedPort = address.port; // may be a server assigned random port.113winston.info(114`Started -- port=${assignedPort}, host='${options.hostname}', base='${base}'`,115);116117winston.info(`Writing port to ${browserPortFile}`);118await writeFile(browserPortFile, `${assignedPort}`);119}120121122