CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/project/servers/hub/tcp-server.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2023 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/* Create the TCP server that communicates with hubs */
7
8
import { writeFile } from "node:fs/promises";
9
import { createServer } from "node:net";
10
import * as uuid from "uuid";
11
12
import enableMessagingProtocol, {
13
CoCalcSocket,
14
} from "@cocalc/backend/tcp/enable-messaging-protocol";
15
import { unlockSocket } from "@cocalc/backend/tcp/locked-socket";
16
import { hubPortFile } from "@cocalc/project/data";
17
import { getOptions } from "@cocalc/project/init-program";
18
import { getSecretToken } from "@cocalc/project/servers/secret-token";
19
import { once } from "@cocalc/util/async-utils";
20
import handleMessage from "./handle-message";
21
import { getClient } from "@cocalc/project/client";
22
23
import { getLogger } from "@cocalc/project/logger";
24
const winston = getLogger("hub-tcp-server");
25
26
export default async function init(): Promise<void> {
27
const secretToken = getSecretToken(); // could throw if not initialized yet
28
if (!secretToken || secretToken.length < 16) {
29
// being extra careful since security
30
throw Error("secret token must be defined and at least 16 characters");
31
return;
32
}
33
34
winston.info("starting tcp server: project <--> hub...");
35
const server = createServer(handleConnection);
36
const options = getOptions();
37
server.listen(options.hubPort, options.hostname);
38
await once(server, "listening");
39
const address = server.address();
40
if (address == null || typeof address == "string") {
41
// null = failed; string doesn't happen since that's for unix domain
42
// sockets, which we aren't using.
43
// This is probably impossible, but it makes typescript happier.
44
throw Error("failed to assign a port");
45
}
46
const { port } = address;
47
winston.info(`hub tcp_server listening ${options.hostname}:${port}`);
48
await writeFile(hubPortFile, `${port}`);
49
}
50
51
async function handleConnection(socket: CoCalcSocket) {
52
winston.info(`*new* connection from ${socket.remoteAddress}`);
53
socket.on("error", (err) => {
54
winston.error(`socket '${socket.remoteAddress}' error - ${err}`);
55
});
56
socket.on("close", () => {
57
winston.info(`*closed* connection from ${socket.remoteAddress}`);
58
});
59
60
try {
61
await unlockSocket(socket, getSecretToken());
62
} catch (err) {
63
winston.error(
64
"failed to unlock socket -- ignoring any future messages and closing connection"
65
);
66
socket.destroy(new Error("invalid secret token"));
67
return;
68
}
69
70
socket.id = uuid.v4();
71
socket.heartbeat = new Date(); // obviously working now
72
enableMessagingProtocol(socket);
73
74
socket.on("mesg", (type, mesg) => {
75
getClient().active_socket(socket); // record that this socket is active now.
76
if (type === "json") {
77
// non-JSON types are handled elsewhere, e.g., for sending binary data.
78
// I'm not sure that any other message types are actually used though.
79
// winston.debug("received json mesg", mesg);
80
handleMessage(socket, mesg);
81
}
82
});
83
}
84
85