Path: blob/master/src/packages/project/servers/hub/tcp-server.ts
1450 views
/*1* This file is part of CoCalc: Copyright © 2023 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/* Create the TCP server that communicates with hubs */67import { writeFile } from "node:fs/promises";8import { createServer } from "node:net";9import * as uuid from "uuid";10import enableMessagingProtocol, {11CoCalcSocket,12} from "@cocalc/backend/tcp/enable-messaging-protocol";13import { unlockSocket } from "@cocalc/backend/tcp/locked-socket";14import { hubPortFile, secretToken } from "@cocalc/project/data";15import { getOptions } from "@cocalc/project/init-program";16import { once } from "@cocalc/util/async-utils";17import handleMessage from "./handle-message";18import { getClient } from "@cocalc/project/client";1920import { getLogger } from "@cocalc/project/logger";21const winston = getLogger("hub-tcp-server");2223export default async function init(): Promise<void> {24winston.info("starting tcp server: project <--> hub...");25const server = createServer(handleConnection);26const options = getOptions();27server.listen(options.hubPort, options.hostname);28await once(server, "listening");29const address = server.address();30if (address == null || typeof address == "string") {31// null = failed; string doesn't happen since that's for unix domain32// sockets, which we aren't using.33// This is probably impossible, but it makes typescript happier.34throw Error("failed to assign a port");35}36const { port } = address;37winston.info(`hub tcp_server listening ${options.hostname}:${port}`);38await writeFile(hubPortFile, `${port}`);39}4041async function handleConnection(socket: CoCalcSocket) {42winston.info(`*new* connection from ${socket.remoteAddress}`);43socket.on("error", (err) => {44winston.error(`socket '${socket.remoteAddress}' error - ${err}`);45});46socket.on("close", () => {47winston.info(`*closed* connection from ${socket.remoteAddress}`);48});4950try {51await unlockSocket(socket, secretToken);52} catch (err) {53winston.error(54"failed to unlock socket -- ignoring any future messages and closing connection",55);56socket.destroy(new Error("invalid secret token"));57return;58}5960socket.id = uuid.v4();61socket.heartbeat = new Date(); // obviously working now62enableMessagingProtocol(socket);6364socket.on("mesg", (type, mesg) => {65getClient().active_socket(socket); // record that this socket is active now.66if (type === "json") {67// non-JSON types are handled elsewhere, e.g., for sending binary data.68// I'm not sure that any other message types are actually used though.69// winston.debug("received json mesg", mesg);70handleMessage(socket, mesg);71}72});73}747576