Path: blob/master/src/packages/project/servers/hub/handle-message.ts
1450 views
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6Handle a general message from the hub. These are the generic message,7as opposed to the messages specific to "client" functionality such as8database queries.9*/1011import processKill from "@cocalc/backend/misc/process-kill";12import { CoCalcSocket } from "@cocalc/backend/tcp/enable-messaging-protocol";13import { handle_save_blob_message } from "@cocalc/project/blobs";14import { getClient } from "@cocalc/project/client";15import { project_id } from "@cocalc/project/data";16import { exec_shell_code } from "@cocalc/project/exec_shell_code";17import { get_kernel_data } from "@cocalc/jupyter/kernel/kernel-data";18import jupyterExecute from "@cocalc/jupyter/stateless-api/execute";19import { getLogger } from "@cocalc/project/logger";20import handleNamedServer from "@cocalc/project/named-servers";21import { print_to_pdf } from "@cocalc/project/print_to_pdf";22import {23read_file_from_project,24write_file_to_project,25} from "@cocalc/project/read_write_files";26import * as message from "@cocalc/util/message";27import { version } from "@cocalc/util/smc-version";28import { Message } from "./types";29import writeTextFileToProject from "./write-text-file-to-project";30import readTextFileFromProject from "./read-text-file-from-project";31import { jupyter_execute_response } from "@cocalc/util/message";3233const logger = getLogger("handle-message-from-hub");3435export default async function handleMessage(36socket: CoCalcSocket,37mesg: Message,38) {39logger.debug("received a message", {40event: mesg.event,41id: mesg.id,42"...": "...",43});4445// 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);4849if (getClient().handle_mesg(mesg, socket)) {50return;51}5253switch (mesg.event) {54case "heartbeat":55logger.debug(`received heartbeat on socket '${socket.id}'`);56// Update the last hearbeat timestamp, so we know socket is working.57socket.heartbeat = new Date();58return;5960case "ping":61// ping message is used only for debugging purposes.62socket.write_mesg("json", message.pong({ id: mesg.id }));63return;6465case "named_server_port":66handleNamedServer(socket, mesg);67return;6869case "project_exec":70// this is no longer used by web browser clients; however it *is* used by the HTTP api served71// by the hub to api key users, so do NOT remove it! E.g., the latex endpoint, the compute72// server, etc., use it. The web browser clients use the websocket api.73exec_shell_code(socket, mesg);74return;7576case "jupyter_execute":77try {78const outputs = await jupyterExecute(mesg as any);79socket.write_mesg(80"json",81jupyter_execute_response({ id: mesg.id, output: outputs }),82);83} catch (err) {84socket.write_mesg(85"json",86message.error({87id: mesg.id,88error: `${err}`,89}),90);91}92return;9394case "jupyter_kernels":95try {96socket.write_mesg(97"json",98message.jupyter_kernels({99kernels: await get_kernel_data(),100id: mesg.id,101}),102);103} catch (err) {104socket.write_mesg(105"json",106message.error({107id: mesg.id,108error: `${err}`,109}),110);111}112return;113114// Reading and writing files to/from project and sending over socket115case "read_file_from_project":116read_file_from_project(socket, mesg);117return;118119case "write_file_to_project":120write_file_to_project(socket, mesg);121return;122123case "write_text_file_to_project":124writeTextFileToProject(socket, mesg);125return;126127case "read_text_file_from_project":128readTextFileFromProject(socket, mesg);129return;130131case "print_to_pdf":132print_to_pdf(socket, mesg);133return;134135case "send_signal":136if (137mesg.pid &&138(mesg.signal == 2 || mesg.signal == 3 || mesg.signal == 9)139) {140processKill(mesg.pid, mesg.signal);141} else {142if (mesg.id) {143socket.write_mesg(144"json",145message.error({146id: mesg.id,147error: "invalid pid or signal (must be 2,3,9)",148}),149);150}151return;152}153if (mesg.id != null) {154// send back confirmation that a signal was sent155socket.write_mesg("json", message.signal_sent({ id: mesg.id }));156}157return;158159case "save_blob":160handle_save_blob_message(mesg);161return;162163case "error":164logger.error(`ERROR from hub: ${mesg.error}`);165return;166167case "hello":168// No action -- this is used by the hub to send an initial control message that has no effect, so that169// we know this socket will be used for control messages.170logger.info(`hello from hub -- sending back our version = ${version}`);171socket.write_mesg("json", message.version({ version }));172return;173174default:175if (mesg.id != null) {176// only respond with error if there is an id -- otherwise response has no meaning to hub.177const err = message.error({178id: mesg.id,179error: `Project ${project_id} does not implement handling mesg with event='${mesg.event}'`,180});181socket.write_mesg("json", err);182} else {183logger.debug(`Dropping unknown message with event='${mesg.event}'`);184}185}186}187188189