Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Injectable spawn func #18

Merged
merged 5 commits into from
Jun 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "deno-http-worker",
"version": "0.0.15",
"version": "0.0.16",
"description": "",
"main": "dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"lint" : "eslint . && npm run lint:deno-bootstrap && npm run lint:deno-test-files",
"lint": "eslint . && npm run lint:deno-bootstrap && npm run lint:deno-test-files",
"lint:deno-bootstrap": "cd deno-bootstrap && deno lint",
"lint:deno-test-files": "cd src/test && deno lint",
"build": "tsc --build",
Expand Down
32 changes: 29 additions & 3 deletions src/DenoHTTPWorker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DenoHTTPWorker, newDenoHTTPWorker } from "./index.js";
import fs from "fs";
import path from "path";
import { Worker } from "worker_threads";
import { SpawnOptions, spawn } from "child_process";

// Uncomment this if you want to debug serial test execution
// const it = _it.concurrent;
Expand Down Expand Up @@ -71,6 +72,29 @@ describe("DenoHTTPWorker", { timeout: 1000 }, () => {
worker.terminate();
});

it("alternate spawnFunc can be provided", async () => {
let firstArg: string = "";
const worker = await newDenoHTTPWorker(
`
export default { async fetch (req: Request): Promise<Response> {
let headers = {};
for (let [key, value] of req.headers.entries()) {
headers[key] = value;
}
return Response.json({ ok: req.url, headers: headers })
} }
`,
{
spawnFunc: (command: string, args: string[], options: SpawnOptions) => {
firstArg = args[0] as string;
return spawn(command, args, options);
},
}
);
expect(firstArg).toEqual("run");
worker.terminate();
});

it("dont crash on socket removal", async () => {
const worker = await newDenoHTTPWorker(
`
Expand Down Expand Up @@ -104,9 +128,11 @@ describe("DenoHTTPWorker", { timeout: 1000 }, () => {
{ printOutput: true }
);
for (let i = 0; i < 10; i++) {
const json = await jsonRequest(worker, "https://localhost/hello?isee=you", {
headers: { accept: "application/json" },
});
const json = await jsonRequest(
worker,
"https://localhost/hello?isee=you",
{ headers: { accept: "application/json" } }
);
expect(json).toEqual({
ok: "https://localhost/hello?isee=you",
headers: { accept: "application/json" },
Expand Down
40 changes: 35 additions & 5 deletions src/DenoHTTPWorker.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import path, { resolve } from "node:path";
import { ChildProcess, spawn, SpawnOptions } from "node:child_process";
import { spawn, SpawnOptions } from "node:child_process";
import { Readable } from "node:stream";
import readline from "node:readline";
import http from "node:http";
Expand All @@ -20,6 +20,26 @@ interface OnExitListener {
(exitCode: number, signal: string): void;
}

interface MinimalChildProcess {
stdout: Readable | null;
stderr: Readable | null;
readonly pid?: number | undefined;
readonly exitCode: number | null;
kill(signal?: NodeJS.Signals | number): boolean;
on(event: string, listener: (...args: any[]) => void): this;
on(
event: "close",
listener: (code: number | null, signal: NodeJS.Signals | null) => void
): this;
on(event: "disconnect", listener: () => void): this;
on(event: "error", listener: (err: Error) => void): this;
on(
event: "exit",
listener: (code: number | null, signal: NodeJS.Signals | null) => void
): this;
on(event: "spawn", listener: () => void): this;
}

export interface DenoWorkerOptions {
/**
* The path to the executable that should be use when spawning the subprocess.
Expand Down Expand Up @@ -66,7 +86,16 @@ export interface DenoWorkerOptions {
/**
* Callback that is called when the process is spawned.
*/
onSpawn?: (process: ChildProcess) => void;
onSpawn?: (process: MinimalChildProcess) => void;

/**
* Provide an alternative spawn functions. Defaults to child_process.spawn.
*/
spawnFunc: (
command: string,
args: string[],
options: SpawnOptions
) => MinimalChildProcess;
}

/**
Expand All @@ -83,6 +112,7 @@ export const newDenoHTTPWorker = async (
printCommandAndArguments: false,
spawnOptions: {},
printOutput: false,
spawnFunc: spawn,
...options,
};

Expand Down Expand Up @@ -157,7 +187,7 @@ export const newDenoHTTPWorker = async (
console.log("Spawning deno process:", [command, ...args]);
}

const process = spawn(command, args, _options.spawnOptions);
const process = _options.spawnFunc(command, args, _options.spawnOptions);
let running = false;
let exited = false;
let worker: DenoHTTPWorker | undefined = undefined;
Expand Down Expand Up @@ -251,7 +281,7 @@ export interface DenoHTTPWorker {

class denoHTTPWorker {
#onexitListeners: OnExitListener[];
#process: ChildProcess;
#process: MinimalChildProcess;
#socketFile: string;
#stderr: Readable;
#stdout: Readable;
Expand All @@ -260,7 +290,7 @@ class denoHTTPWorker {

constructor(
socketFile: string,
process: ChildProcess,
process: MinimalChildProcess,
stdout: Readable,
stderr: Readable
) {
Expand Down
Loading