Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/project/servers/hub/tcp-server.ts
1450 views
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
import enableMessagingProtocol, {
12
CoCalcSocket,
13
} from "@cocalc/backend/tcp/enable-messaging-protocol";
14
import { unlockSocket } from "@cocalc/backend/tcp/locked-socket";
15
import { hubPortFile, secretToken } from "@cocalc/project/data";
16
import { getOptions } from "@cocalc/project/init-program";
17
import { once } from "@cocalc/util/async-utils";
18
import handleMessage from "./handle-message";
19
import { getClient } from "@cocalc/project/client";
20
21
import { getLogger } from "@cocalc/project/logger";
22
const winston = getLogger("hub-tcp-server");
23
24
export default async function init(): Promise<void> {
25
winston.info("starting tcp server: project <--> hub...");
26
const server = createServer(handleConnection);
27
const options = getOptions();
28
server.listen(options.hubPort, options.hostname);
29
await once(server, "listening");
30
const address = server.address();
31
if (address == null || typeof address == "string") {
32
// null = failed; string doesn't happen since that's for unix domain
33
// sockets, which we aren't using.
34
// This is probably impossible, but it makes typescript happier.
35
throw Error("failed to assign a port");
36
}
37
const { port } = address;
38
winston.info(`hub tcp_server listening ${options.hostname}:${port}`);
39
await writeFile(hubPortFile, `${port}`);
40
}
41
42
async function handleConnection(socket: CoCalcSocket) {
43
winston.info(`*new* connection from ${socket.remoteAddress}`);
44
socket.on("error", (err) => {
45
winston.error(`socket '${socket.remoteAddress}' error - ${err}`);
46
});
47
socket.on("close", () => {
48
winston.info(`*closed* connection from ${socket.remoteAddress}`);
49
});
50
51
try {
52
await unlockSocket(socket, secretToken);
53
} catch (err) {
54
winston.error(
55
"failed to unlock socket -- ignoring any future messages and closing connection",
56
);
57
socket.destroy(new Error("invalid secret token"));
58
return;
59
}
60
61
socket.id = uuid.v4();
62
socket.heartbeat = new Date(); // obviously working now
63
enableMessagingProtocol(socket);
64
65
socket.on("mesg", (type, mesg) => {
66
getClient().active_socket(socket); // record that this socket is active now.
67
if (type === "json") {
68
// non-JSON types are handled elsewhere, e.g., for sending binary data.
69
// I'm not sure that any other message types are actually used though.
70
// winston.debug("received json mesg", mesg);
71
handleMessage(socket, mesg);
72
}
73
});
74
}
75
76