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/sync-client/lib/connect-to-project.ts
Views: 687
1
import Primus from "primus";
2
import { join } from "path";
3
import * as responder from "@cocalc/primus-responder";
4
import * as multiplex from "@cocalc/primus-multiplex";
5
import type {
6
ProjectWebsocket,
7
WebsocketState,
8
} from "@cocalc/sync/client/types";
9
import { apiKey, apiServer } from "@cocalc/backend/data";
10
import versionCookie from "./version-cookie";
11
import { toCookieHeader } from "./cookies";
12
import { API_COOKIE_NAME } from "@cocalc/backend/auth/cookie-names";
13
import basePath from "@cocalc/backend/base-path";
14
import getLogger from "@cocalc/backend/logger";
15
16
const logger = getLogger("sync-client:connect");
17
const log = logger.debug;
18
19
export default async function connectToProject(
20
project_id,
21
): Promise<ProjectWebsocket> {
22
if (!apiServer) {
23
throw Error("API_SERVER must be set");
24
}
25
if (!apiKey) {
26
throw Error("api key must be set (e.g., set API_KEY env variable)");
27
}
28
const server = apiServer;
29
const pathname = join(basePath, project_id, "raw/.smc/ws");
30
const target = join(server, project_id, "raw/.smc/ws");
31
log("connectToProject -- ", { pathname, target });
32
const opts = {
33
pathname,
34
transformer: "websockets",
35
plugin: { responder, multiplex },
36
} as const;
37
const Socket = Primus.createSocket(opts);
38
log("API_COOKIE_NAME = ", API_COOKIE_NAME);
39
const Cookie = toCookieHeader({
40
...versionCookie(),
41
[API_COOKIE_NAME]: apiKey,
42
});
43
const socket: ProjectWebsocket = new Socket(server, {
44
transport: {
45
// rejectUnauthorized is useful for testing and connecting to a cocalc-docker; it allows connecting to
46
// server with self-signed cert; obviously a slight risk to allow this.
47
rejectUnauthorized: false,
48
headers: { Cookie },
49
},
50
// even with this, it seems to take far too long to connect to
51
// a project, e.g., as compared to the frontend browser.
52
// I think there is maybe an issue in the proxy server.
53
reconnect: {
54
factor: 1.3,
55
min: 750,
56
max: 10000,
57
retries: 10000,
58
},
59
}) as any;
60
61
// Every single individual channel creates listeners
62
// on this socket, and we create several channels per
63
// document, so we expect a relatively large number
64
// of listeners on this socket. This is OK, because
65
// it is rare for events to fire (e.g., it happens when
66
// the network is down or project restarts).
67
socket.setMaxListeners(500);
68
69
function updateState(state: WebsocketState) {
70
if (socket.state == state) {
71
return; // nothing changed, so no need to set or emit.
72
}
73
log("state changed to ", state);
74
socket.state = state;
75
socket.emit("state", state);
76
}
77
78
updateState("offline"); // starts offline
79
80
socket.on("open", () => {
81
log("open", target);
82
updateState("online");
83
});
84
socket.on("reconnected", () => {
85
log("reconnected", target);
86
updateState("online");
87
});
88
89
socket.on("reconnect", () => {
90
log("reconnect", target);
91
updateState("offline");
92
});
93
socket.on("reconnect scheduled", () => {
94
log("reconnect scheduled", target);
95
updateState("offline");
96
});
97
98
socket.on("end", () => {
99
log("end", target);
100
// maybe todo?
101
updateState("offline");
102
});
103
104
return socket;
105
}
106
107