Path: blob/master/src/packages/jupyter/zmq/message.ts
1447 views
/*1This is from https://github.com/n-riesco/jmp but rewritten in typescript.23The original and all modifications in CoCalc of the code in THIS DIRECTORY4are: * BSD 3-Clause License *56*/78/*9* BSD 3-Clause License10*11* Copyright (c) 2015, Nicolas Riesco and others as credited in the AUTHORS file12* All rights reserved.13*14* Redistribution and use in source and binary forms, with or without15* modification, are permitted provided that the following conditions are met:16*17* 1. Redistributions of source code must retain the above copyright notice,18* this list of conditions and the following disclaimer.19*20* 2. Redistributions in binary form must reproduce the above copyright notice,21* this list of conditions and the following disclaimer in the documentation22* and/or other materials provided with the distribution.23*24* 3. Neither the name of the copyright holder nor the names of its contributors25* may be used to endorse or promote products derived from this software without26* specific prior written permission.27*28* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"29* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE30* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE31* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE32* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR33* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF34* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS35* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN36* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)37* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE38* POSSIBILITY OF SUCH DAMAGE.39*40*/4142import crypto from "crypto";43import { v4 as uuid } from "uuid";44import { Dealer } from "zeromq";4546const DEBUG = (global as any).DEBUG || false;4748let log: (...args: any[]) => void;49if (DEBUG) {50// eslint-disable-next-line @typescript-eslint/no-var-requires51const console = require("console");52log = (...args) => {53process.stderr.write("JMP: ");54console.error(...args);55};56} else {57try {58// eslint-disable-next-line @typescript-eslint/no-var-requires59log = require("debug")("JMP:");60} catch {61log = () => {};62}63}6465export const DELIMITER = "<IDS|MSG>";6667export interface JupyterHeader {68msg_id?: string;69username?: string;70session?: string;71msg_type?: string;72version?: string;73[key: string]: any;74}7576export interface MessageProps {77idents?: Buffer[];78header?: JupyterHeader;79parent_header?: JupyterHeader;80metadata?: any;81content?: any;82buffers?;83}8485export class Message {86idents: Buffer[];87header: JupyterHeader;88parent_header: JupyterHeader;89metadata: { [key: string]: any };90content: { [key: string]: any };91buffers: Buffer[];9293constructor(properties?: MessageProps) {94this.idents = properties?.idents ?? [];95this.header = properties?.header ?? {};96this.parent_header = properties?.parent_header ?? {};97this.metadata = properties?.metadata ?? {};98this.content = properties?.content ?? {};99this.buffers = properties?.buffers ?? [];100}101102respond(103socket: Dealer,104messageType: string,105content?: object,106metadata?: object,107protocolVersion?: string,108): Message {109const response = new Message();110response.idents = this.idents.slice();111response.header = {112msg_id: uuid(),113username: this.header.username,114session: this.header.session,115msg_type: messageType,116};117if (this.header?.version) {118response.header.version = this.header.version;119}120if (protocolVersion) {121response.header.version = protocolVersion;122}123response.parent_header = { ...this.header };124response.content = content ?? {};125response.metadata = metadata ?? {};126socket.send(response as any);127return response;128}129130static _decode(131messageFrames: Buffer[] | IArguments,132scheme = "sha256",133key = "",134): Message | null {135try {136return _decode(messageFrames, scheme, key);137} catch (err) {138log("MESSAGE: DECODE: Error:", err);139return null;140}141}142143_encode(scheme = "sha256", key = ""): (Buffer | string)[] {144const idents = this.idents;145146const header = JSON.stringify(this.header);147const parent_header = JSON.stringify(this.parent_header);148const metadata = JSON.stringify(this.metadata);149const content = JSON.stringify(this.content);150151let signature = "";152if (key) {153const hmac = crypto.createHmac(scheme, key);154const encoding = "utf8";155hmac.update(Buffer.from(header, encoding));156hmac.update(Buffer.from(parent_header, encoding));157hmac.update(Buffer.from(metadata, encoding));158hmac.update(Buffer.from(content, encoding));159signature = hmac.digest("hex");160}161162return [163...idents,164DELIMITER,165signature,166header,167parent_header,168metadata,169content,170...this.buffers,171];172}173}174175// Helper decode176function _decode(177messageFrames: Buffer[] | IArguments,178scheme: string,179key: string,180): Message | null {181// Could be an arguments object, convert to array if so182const frames = Array.isArray(messageFrames)183? messageFrames184: Array.prototype.slice.call(messageFrames);185186let i = 0;187const idents: Buffer[] = [];188for (; i < frames.length; i++) {189const frame = frames[i];190if (frame.toString() === DELIMITER) break;191idents.push(frame);192}193194if (frames.length - i < 5) {195log("MESSAGE: DECODE: Not enough message frames", frames);196return null;197}198199if (frames[i].toString() !== DELIMITER) {200log("MESSAGE: DECODE: Missing delimiter", frames);201return null;202}203204if (key) {205const obtainedSignature = frames[i + 1].toString();206const hmac = crypto.createHmac(scheme, key);207hmac.update(frames[i + 2]);208hmac.update(frames[i + 3]);209hmac.update(frames[i + 4]);210hmac.update(frames[i + 5]);211const expectedSignature = hmac.digest("hex");212213if (expectedSignature !== obtainedSignature) {214log(215"MESSAGE: DECODE: Incorrect message signature:",216"Obtained =",217obtainedSignature,218"Expected =",219expectedSignature,220);221return null;222}223}224225return new Message({226idents: idents,227header: JSON.parse(frames[i + 2].toString()),228parent_header: JSON.parse(frames[i + 3].toString()),229metadata: JSON.parse(frames[i + 4].toString()),230content: JSON.parse(frames[i + 5].toString()),231buffers: frames.slice(i + 6),232});233}234235236