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/handle-message.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
Handle a general message from the hub. These are the generic message,
8
as opposed to the messages specific to "client" functionality such as
9
database queries.
10
*/
11
12
import processKill from "@cocalc/backend/misc/process-kill";
13
import { CoCalcSocket } from "@cocalc/backend/tcp/enable-messaging-protocol";
14
import { handle_save_blob_message } from "@cocalc/project/blobs";
15
import { getClient } from "@cocalc/project/client";
16
import { project_id } from "@cocalc/project/data";
17
import { exec_shell_code } from "@cocalc/project/exec_shell_code";
18
import { get_kernel_data } from "@cocalc/jupyter/kernel/kernel-data";
19
import jupyterExecute from "@cocalc/jupyter/stateless-api/execute";
20
import { getLogger } from "@cocalc/project/logger";
21
import handleNamedServer from "@cocalc/project/named-servers";
22
import { print_to_pdf } from "@cocalc/project/print_to_pdf";
23
import {
24
read_file_from_project,
25
write_file_to_project,
26
} from "@cocalc/project/read_write_files";
27
import * as message from "@cocalc/util/message";
28
import { version } from "@cocalc/util/smc-version";
29
import { Message } from "./types";
30
import writeTextFileToProject from "./write-text-file-to-project";
31
import readTextFileFromProject from "./read-text-file-from-project";
32
33
const logger = getLogger("handle-message-from-hub");
34
35
export default async function handleMessage(
36
socket: CoCalcSocket,
37
mesg: Message,
38
) {
39
logger.debug("received a message", {
40
event: mesg.event,
41
id: mesg.id,
42
"...": "...",
43
});
44
45
// We can't just log this in general, since it can be big.
46
// So only uncomment this for low level debugging, unfortunately.
47
// logger.debug("received ", mesg);
48
49
if (getClient().handle_mesg(mesg, socket)) {
50
return;
51
}
52
53
switch (mesg.event) {
54
case "heartbeat":
55
logger.debug(`received heartbeat on socket '${socket.id}'`);
56
// Update the last hearbeat timestamp, so we know socket is working.
57
socket.heartbeat = new Date();
58
return;
59
60
case "ping":
61
// ping message is used only for debugging purposes.
62
socket.write_mesg("json", message.pong({ id: mesg.id }));
63
return;
64
65
case "named_server_port":
66
handleNamedServer(socket, mesg);
67
return;
68
69
case "project_exec":
70
// this is no longer used by web browser clients; however it *is* used by the HTTP api served
71
// by the hub to api key users, so do NOT remove it! E.g., the latex endpoint, the compute
72
// server, etc., use it. The web browser clients use the websocket api,
73
exec_shell_code(socket, mesg);
74
return;
75
76
case "jupyter_execute":
77
try {
78
await jupyterExecute(socket, mesg);
79
} catch (err) {
80
socket.write_mesg(
81
"json",
82
message.error({
83
id: mesg.id,
84
error: `${err}`,
85
}),
86
);
87
}
88
return;
89
90
case "jupyter_kernels":
91
try {
92
socket.write_mesg(
93
"json",
94
message.jupyter_kernels({
95
kernels: await get_kernel_data(),
96
id: mesg.id,
97
}),
98
);
99
} catch (err) {
100
socket.write_mesg(
101
"json",
102
message.error({
103
id: mesg.id,
104
error: `${err}`,
105
}),
106
);
107
}
108
return;
109
110
// Reading and writing files to/from project and sending over socket
111
case "read_file_from_project":
112
read_file_from_project(socket, mesg);
113
return;
114
115
case "write_file_to_project":
116
write_file_to_project(socket, mesg);
117
return;
118
119
case "write_text_file_to_project":
120
writeTextFileToProject(socket, mesg);
121
return;
122
123
case "read_text_file_from_project":
124
readTextFileFromProject(socket, mesg);
125
return;
126
127
case "print_to_pdf":
128
print_to_pdf(socket, mesg);
129
return;
130
131
case "send_signal":
132
if (
133
mesg.pid &&
134
(mesg.signal == 2 || mesg.signal == 3 || mesg.signal == 9)
135
) {
136
processKill(mesg.pid, mesg.signal);
137
} else {
138
if (mesg.id) {
139
socket.write_mesg(
140
"json",
141
message.error({
142
id: mesg.id,
143
error: "invalid pid or signal (must be 2,3,9)",
144
}),
145
);
146
}
147
return;
148
}
149
if (mesg.id != null) {
150
// send back confirmation that a signal was sent
151
socket.write_mesg("json", message.signal_sent({ id: mesg.id }));
152
}
153
return;
154
155
case "save_blob":
156
handle_save_blob_message(mesg);
157
return;
158
159
case "error":
160
logger.error(`ERROR from hub: ${mesg.error}`);
161
return;
162
163
case "hello":
164
// No action -- this is used by the hub to send an initial control message that has no effect, so that
165
// we know this socket will be used for control messages.
166
logger.info(`hello from hub -- sending back our version = ${version}`);
167
socket.write_mesg("json", message.version({ version }));
168
return;
169
170
default:
171
if (mesg.id != null) {
172
// only respond with error if there is an id -- otherwise response has no meaning to hub.
173
const err = message.error({
174
id: mesg.id,
175
error: `Project ${project_id} does not implement handling mesg with event='${mesg.event}'`,
176
});
177
socket.write_mesg("json", err);
178
} else {
179
logger.debug(`Dropping unknown message with event='${mesg.event}'`);
180
}
181
}
182
}
183
184