Add evaluation helpers (#33)

* Add evaluation helpers

* Make some helpers only available server-side

They don't make any sense on the client side.

* Fork the right thing
This commit is contained in:
Asher
2019-02-22 15:56:29 -06:00
committed by GitHub
parent de3fbd689f
commit 8b8bef015e
17 changed files with 306 additions and 247 deletions

View File

@@ -1,8 +1,10 @@
import { fork as cpFork } from "child_process";
import { EventEmitter } from "events";
import * as vm from "vm";
import { logger, field } from "@coder/logger";
import { NewEvalMessage, EvalFailedMessage, EvalDoneMessage, ServerMessage, EvalEventMessage } from "../proto";
import { SendableConnection } from "../common/connection";
import { ServerActiveEvalHelper, EvalHelper, ForkProvider } from "../common/helpers";
import { stringify, parse } from "../common/util";
export interface ActiveEvaluation {
@@ -11,7 +13,7 @@ export interface ActiveEvaluation {
}
declare var __non_webpack_require__: typeof require;
export const evaluate = (connection: SendableConnection, message: NewEvalMessage, onDispose: () => void): ActiveEvaluation | void => {
export const evaluate = (connection: SendableConnection, message: NewEvalMessage, onDispose: () => void, fork?: ForkProvider): ActiveEvaluation | void => {
/**
* Send the response and call onDispose.
*/
@@ -46,7 +48,10 @@ export const evaluate = (connection: SendableConnection, message: NewEvalMessage
let eventEmitter = message.getActive() ? new EventEmitter(): undefined;
const sandbox = {
eventEmitter: eventEmitter ? {
helper: eventEmitter ? new ServerActiveEvalHelper({
removeAllListeners: (event?: string): void => {
eventEmitter!.removeAllListeners(event);
},
// tslint:disable no-any
on: (event: string, cb: (...args: any[]) => void): void => {
eventEmitter!.on(event, (...args: any[]) => {
@@ -73,7 +78,7 @@ export const evaluate = (connection: SendableConnection, message: NewEvalMessage
connection.send(serverMsg.serializeBinary());
},
// tslint:enable no-any
} : undefined,
}, fork || cpFork) : new EvalHelper(),
_Buffer: Buffer,
// When the client is ran from Webpack, it will replace
// __non_webpack_require__ with require, which we then need to provide to
@@ -94,7 +99,7 @@ export const evaluate = (connection: SendableConnection, message: NewEvalMessage
let value: any; // tslint:disable-line no-any
try {
const code = `(${message.getFunction()})(${eventEmitter ? "eventEmitter, " : ""}...args);`;
const code = `(${message.getFunction()})(helper, ...args);`;
value = vm.runInNewContext(code, sandbox, {
// If the code takes longer than this to return, it is killed and throws.
timeout: message.getTimeout() || 15000,

View File

@@ -5,12 +5,14 @@ import { promisify } from "util";
import { logger, field } from "@coder/logger";
import { ClientMessage, WorkingInitMessage, ServerMessage } from "../proto";
import { evaluate, ActiveEvaluation } from "./evaluate";
import { ForkProvider } from "../common/helpers";
import { ReadWriteConnection } from "../common/connection";
export interface ServerOptions {
readonly workingDirectory: string;
readonly dataDirectory: string;
readonly builtInExtensionsDirectory: string;
readonly fork?: ForkProvider;
}
export class Server {
@@ -105,7 +107,7 @@ export class Server {
logger.trace(() => [
`dispose ${evalMessage.getId()}, ${this.evals.size} left`,
]);
});
}, this.options ? this.options.fork : undefined);
if (resp) {
this.evals.set(evalMessage.getId(), resp);
}