2020-07-21 13:20:51 +00:00
|
|
|
/**
|
|
|
|
* Various utilities and constants for communicating with the python sandbox.
|
|
|
|
*/
|
2022-06-14 06:49:59 +00:00
|
|
|
import * as MemBuffer from 'app/common/MemBuffer';
|
2022-07-04 14:14:55 +00:00
|
|
|
import log from 'app/server/lib/log';
|
2020-07-21 13:20:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SandboxError is an error type for reporting errors forwarded from the sandbox.
|
|
|
|
*/
|
2022-06-14 06:49:59 +00:00
|
|
|
export class SandboxError extends Error {
|
|
|
|
constructor(message: string) {
|
|
|
|
super("[Sandbox] " + (message || 'Python reported an error'));
|
|
|
|
}
|
2020-07-21 13:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Special msgCode values that precede msgBody to indicate what kind of message it is.
|
|
|
|
* These all cost one byte. If we needed more, we should probably switch to a number (5 bytes)
|
|
|
|
* CALL = call to the other side. The data must be an array of [func_name, arguments...]
|
|
|
|
* DATA = data must be a value to return to a call from the other side
|
|
|
|
* EXC = data must be an exception to return to a call from the other side
|
|
|
|
*/
|
2022-06-14 06:49:59 +00:00
|
|
|
export const CALL = null;
|
|
|
|
export const DATA = true;
|
|
|
|
export const EXC = false;
|
2020-07-21 13:20:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a function that takes data buffers and logs them to log.info() with the given prefix.
|
|
|
|
* The logged output is line-oriented, so that the prefix is only inserted at the start of a line.
|
|
|
|
* Binary data is encoded as with JSON.stringify.
|
|
|
|
*/
|
2022-06-14 06:49:59 +00:00
|
|
|
export function makeLinePrefixer(prefix: string, logMeta: object) {
|
2023-07-18 15:20:02 +00:00
|
|
|
return _makeLinePrefixer(prefix, logMeta, text => text.indexOf('\n'));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Same as makeLinePrefixer, but avoids splitting lines except when a line starts with '[', since
|
|
|
|
* the sandbox prefixes all log messages with "[LEVEL]" prefix.
|
|
|
|
*/
|
|
|
|
export function makeLogLinePrefixer(prefix: string, logMeta: object) {
|
|
|
|
return _makeLinePrefixer(prefix, logMeta, text => {
|
|
|
|
const newline = text.indexOf("\n[");
|
|
|
|
// If no next log message, split at the last newline. Any earlier newlines would be included.
|
|
|
|
return (newline !== -1) ? newline : text.lastIndexOf("\n");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function _makeLinePrefixer(prefix: string, logMeta: object, findLineEnd: (text: string) => number) {
|
2022-06-14 06:49:59 +00:00
|
|
|
let partial = '';
|
|
|
|
return (data: Uint8Array) => {
|
2020-07-21 13:20:51 +00:00
|
|
|
partial += MemBuffer.arrayToString(data);
|
2022-06-14 06:49:59 +00:00
|
|
|
let newline;
|
2023-07-18 15:20:02 +00:00
|
|
|
while (partial && (newline = findLineEnd(partial)) !== -1) {
|
2022-06-14 06:49:59 +00:00
|
|
|
const line = partial.slice(0, newline);
|
2020-07-21 13:20:51 +00:00
|
|
|
partial = partial.slice(newline + 1);
|
|
|
|
// Escape some parts of the string by serializing it to JSON (without the quotes).
|
2022-06-14 06:49:59 +00:00
|
|
|
log.origLog('info', "%s%s", prefix,
|
2023-07-18 15:20:02 +00:00
|
|
|
JSON.stringify(line).slice(1, -1).replace(/\\(['"\\])/g, '$1').replace(/\\n/g, '\n'),
|
2020-07-21 13:20:51 +00:00
|
|
|
logMeta);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|