Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/project/servers/hub/handle-message.ts
1450 views
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
import { jupyter_execute_response } from "@cocalc/util/message";
33
34
const logger = getLogger("handle-message-from-hub");
35
36
export default async function handleMessage(
37
socket: CoCalcSocket,
38
mesg: Message,
39
) {
40
logger.debug("received a message", {
41
event: mesg.event,
42
id: mesg.id,
43
"...": "...",
44
});
45
46
// We can't just log this in general, since it can be big.
47
// So only uncomment this for low level debugging, unfortunately.
48
// logger.debug("received ", mesg);
49
50
if (getClient().handle_mesg(mesg, socket)) {
51
return;
52
}
53
54
switch (mesg.event) {
55
case "heartbeat":
56
logger.debug(`received heartbeat on socket '${socket.id}'`);
57
// Update the last hearbeat timestamp, so we know socket is working.
58
socket.heartbeat = new Date();
59
return;
60
61
case "ping":
62
// ping message is used only for debugging purposes.
63
socket.write_mesg("json", message.pong({ id: mesg.id }));
64
return;
65
66
case "named_server_port":
67
handleNamedServer(socket, mesg);
68
return;
69
70
case "project_exec":
71
// this is no longer used by web browser clients; however it *is* used by the HTTP api served
72
// by the hub to api key users, so do NOT remove it! E.g., the latex endpoint, the compute
73
// server, etc., use it. The web browser clients use the websocket api.
74
exec_shell_code(socket, mesg);
75
return;
76
77
case "jupyter_execute":
78
try {
79
const outputs = await jupyterExecute(mesg as any);
80
socket.write_mesg(
81
"json",
82
jupyter_execute_response({ id: mesg.id, output: outputs }),
83
);
84
} catch (err) {
85
socket.write_mesg(
86
"json",
87
message.error({
88
id: mesg.id,
89
error: `${err}`,
90
}),
91
);
92
}
93
return;
94
95
case "jupyter_kernels":
96
try {
97
socket.write_mesg(
98
"json",
99
message.jupyter_kernels({
100
kernels: await get_kernel_data(),
101
id: mesg.id,
102
}),
103
);
104
} catch (err) {
105
socket.write_mesg(
106
"json",
107
message.error({
108
id: mesg.id,
109
error: `${err}`,
110
}),
111
);
112
}
113
return;
114
115
// Reading and writing files to/from project and sending over socket
116
case "read_file_from_project":
117
read_file_from_project(socket, mesg);
118
return;
119
120
case "write_file_to_project":
121
write_file_to_project(socket, mesg);
122
return;
123
124
case "write_text_file_to_project":
125
writeTextFileToProject(socket, mesg);
126
return;
127
128
case "read_text_file_from_project":
129
readTextFileFromProject(socket, mesg);
130
return;
131
132
case "print_to_pdf":
133
print_to_pdf(socket, mesg);
134
return;
135
136
case "send_signal":
137
if (
138
mesg.pid &&
139
(mesg.signal == 2 || mesg.signal == 3 || mesg.signal == 9)
140
) {
141
processKill(mesg.pid, mesg.signal);
142
} else {
143
if (mesg.id) {
144
socket.write_mesg(
145
"json",
146
message.error({
147
id: mesg.id,
148
error: "invalid pid or signal (must be 2,3,9)",
149
}),
150
);
151
}
152
return;
153
}
154
if (mesg.id != null) {
155
// send back confirmation that a signal was sent
156
socket.write_mesg("json", message.signal_sent({ id: mesg.id }));
157
}
158
return;
159
160
case "save_blob":
161
handle_save_blob_message(mesg);
162
return;
163
164
case "error":
165
logger.error(`ERROR from hub: ${mesg.error}`);
166
return;
167
168
case "hello":
169
// No action -- this is used by the hub to send an initial control message that has no effect, so that
170
// we know this socket will be used for control messages.
171
logger.info(`hello from hub -- sending back our version = ${version}`);
172
socket.write_mesg("json", message.version({ version }));
173
return;
174
175
default:
176
if (mesg.id != null) {
177
// only respond with error if there is an id -- otherwise response has no meaning to hub.
178
const err = message.error({
179
id: mesg.id,
180
error: `Project ${project_id} does not implement handling mesg with event='${mesg.event}'`,
181
});
182
socket.write_mesg("json", err);
183
} else {
184
logger.debug(`Dropping unknown message with event='${mesg.event}'`);
185
}
186
}
187
}
188
189