Skip to content

Commit 7fef5bf

Browse files
authored
Remove configurable spawn (#77)
* Remove configurable spawn function * Expand on changeset * Biome fix * Remove type export
1 parent 00ac501 commit 7fef5bf

File tree

4 files changed

+49
-83
lines changed

4 files changed

+49
-83
lines changed

.changeset/good-hats-show.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@valtown/deno-http-worker": major
3+
---
4+
5+
Remove configurable spawn function option
6+
7+
Previously, we supported a `spawnFunc` option which let you use spawn methods
8+
other than child_process.spawn. Given the lack of useful alternatives to child_process.spawn
9+
and our efforts to really optimize this module, we're removing this option.

src/DenoHTTPWorker.test.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { type DenoHTTPWorker, newDenoHTTPWorker } from "./index.js";
33
import fs from "node:fs";
44
import path from "node:path";
55
import { Worker } from "node:worker_threads";
6-
import { type SpawnOptions, spawn } from "node:child_process";
76
import { EarlyExitDenoHTTPWorkerError } from "./DenoHTTPWorker.js";
87

98
// Uncomment this if you want to debug serial test execution
@@ -73,29 +72,6 @@ describe("DenoHTTPWorker", { timeout: 1000 }, () => {
7372
await worker.terminate();
7473
});
7574

76-
it("alternate spawnFunc can be provided", async () => {
77-
let firstArg = "";
78-
const worker = await newDenoHTTPWorker(
79-
`
80-
export default { async fetch (req: Request): Promise<Response> {
81-
let headers = {};
82-
for (let [key, value] of req.headers.entries()) {
83-
headers[key] = value;
84-
}
85-
return Response.json({ ok: req.url, headers: headers })
86-
} }
87-
`,
88-
{
89-
spawnFunc: (command: string, args: string[], options: SpawnOptions) => {
90-
firstArg = args[0] as string;
91-
return spawn(command, args, options);
92-
},
93-
}
94-
);
95-
expect(firstArg).toEqual("run");
96-
await worker.terminate();
97-
});
98-
9975
it("don't crash on socket removal", async () => {
10076
const worker = await newDenoHTTPWorker(
10177
`

src/DenoHTTPWorker.ts

Lines changed: 40 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import path, { resolve } from "node:path";
2-
import { spawn, type SpawnOptions } from "node:child_process";
2+
import {
3+
type ChildProcess,
4+
spawn,
5+
type SpawnOptions,
6+
} from "node:child_process";
37
import type { Readable } from "node:stream";
48
import readline from "node:readline";
59
import fs from "node:fs/promises";
@@ -31,26 +35,6 @@ export class EarlyExitDenoHTTPWorkerError extends Error {
3135
}
3236
}
3337

34-
export interface MinimalChildProcess {
35-
stdout: Readable | null;
36-
stderr: Readable | null;
37-
readonly pid?: number | undefined;
38-
readonly exitCode: number | null;
39-
kill(signal?: NodeJS.Signals | number): boolean;
40-
on(event: string, listener: (...args: any[]) => void): this;
41-
on(
42-
event: "close",
43-
listener: (code: number | null, signal: NodeJS.Signals | null) => void
44-
): this;
45-
on(event: "disconnect", listener: () => void): this;
46-
on(event: "error", listener: (err: Error) => void): this;
47-
on(
48-
event: "exit",
49-
listener: (code: number | null, signal: NodeJS.Signals | null) => void
50-
): this;
51-
on(event: "spawn", listener: () => void): this;
52-
}
53-
5438
export interface DenoWorkerOptions {
5539
/**
5640
* The path to the executable that should be use when spawning the subprocess.
@@ -97,16 +81,7 @@ export interface DenoWorkerOptions {
9781
/**
9882
* Callback that is called when the process is spawned.
9983
*/
100-
onSpawn?: (process: MinimalChildProcess) => void;
101-
102-
/**
103-
* Provide an alternative spawn functions. Defaults to child_process.spawn.
104-
*/
105-
spawnFunc: (
106-
command: string,
107-
args: string[],
108-
options: SpawnOptions
109-
) => MinimalChildProcess;
84+
onSpawn?: (process: ChildProcess) => void;
11085
}
11186

11287
/**
@@ -123,7 +98,6 @@ export const newDenoHTTPWorker = async (
12398
printCommandAndArguments: false,
12499
spawnOptions: {},
125100
printOutput: false,
126-
spawnFunc: spawn,
127101
...options,
128102
};
129103

@@ -203,7 +177,7 @@ export const newDenoHTTPWorker = async (
203177
console.log("Spawning deno process:", [command, ...args]);
204178
}
205179

206-
const process = _options.spawnFunc(command, args, _options.spawnOptions);
180+
const process = spawn(command, args, _options.spawnOptions);
207181
let running = false;
208182
let exited = false;
209183
// eslint-disable-next-line prefer-const
@@ -305,7 +279,7 @@ export interface DenoHTTPWorker {
305279

306280
class denoHTTPWorker implements DenoHTTPWorker {
307281
#onexitListeners: OnExitListener[];
308-
#process: MinimalChildProcess;
282+
#process: ChildProcess;
309283
#socketFile: string;
310284
#stderr: Readable;
311285
#stdout: Readable;
@@ -314,7 +288,7 @@ class denoHTTPWorker implements DenoHTTPWorker {
314288

315289
constructor(
316290
socketFile: string,
317-
process: MinimalChildProcess,
291+
process: ChildProcess,
318292
stdout: Readable,
319293
stderr: Readable
320294
) {
@@ -327,6 +301,9 @@ class denoHTTPWorker implements DenoHTTPWorker {
327301
this.#pool = new undici.Pool("http://deno", { socketPath: socketFile });
328302
}
329303

304+
/**
305+
* Force-kill the process with SIGKILL, firing exit events.
306+
*/
330307
async _terminate(code?: number, signal?: string) {
331308
if (this.#terminated) {
332309
return;
@@ -347,6 +324,11 @@ class denoHTTPWorker implements DenoHTTPWorker {
347324
return await this._terminate();
348325
}
349326

327+
/**
328+
* Kill the process with SIGINT.
329+
* This resolves once we receive the 'exit' event from the underlying
330+
* process.
331+
*/
350332
async shutdown() {
351333
this.#process.kill("SIGINT");
352334
await new Promise<void>((res) => {
@@ -358,7 +340,7 @@ class denoHTTPWorker implements DenoHTTPWorker {
358340
url: string,
359341
headers: Headers = new Headers()
360342
): Promise<WebSocket> {
361-
headers = this.#processHeaders(headers, url);
343+
headers = processHeaders(headers, url);
362344
headers.set("x-deno-worker-connection", "upgrade"); // Required for websockets
363345

364346
return new WebSocket(url, {
@@ -368,7 +350,7 @@ class denoHTTPWorker implements DenoHTTPWorker {
368350
}
369351

370352
async request(options: RequestOptions): Promise<ResponseData> {
371-
const headers = this.#processHeaders(
353+
const headers = processHeaders(
372354
options.headers || new Headers(),
373355
options.url
374356
);
@@ -390,27 +372,6 @@ class denoHTTPWorker implements DenoHTTPWorker {
390372
}));
391373
}
392374

393-
#processHeaders(headers: Headers, url: string): Headers {
394-
headers.set("x-deno-worker-url", url);
395-
396-
// To prevent the user from setting these headers, we either update them to
397-
// the real host / connection, or clear them
398-
headers.delete("x-deno-worker-host");
399-
headers.delete("x-deno-worker-connection");
400-
401-
const host = headers.get("host");
402-
if (host) {
403-
headers.set("x-deno-worker-host", host);
404-
}
405-
406-
const connection = headers.get("connection");
407-
if (connection) {
408-
headers.set("x-deno-worker-connection", connection);
409-
}
410-
411-
return headers;
412-
}
413-
414375
// We send this request to Deno so that we get a live connection in the
415376
// http.Agent and subsequent requests are do not have to wait for a new
416377
// connection.
@@ -457,3 +418,24 @@ function killUnix(pid: number) {
457418
}
458419
}
459420
}
421+
422+
function processHeaders(headers: Headers, url: string): Headers {
423+
headers.set("x-deno-worker-url", url);
424+
425+
// To prevent the user from setting these headers, we either update them to
426+
// the real host / connection, or clear them
427+
headers.delete("x-deno-worker-host");
428+
headers.delete("x-deno-worker-connection");
429+
430+
const host = headers.get("host");
431+
if (host) {
432+
headers.set("x-deno-worker-host", host);
433+
}
434+
435+
const connection = headers.get("connection");
436+
if (connection) {
437+
headers.set("x-deno-worker-connection", connection);
438+
}
439+
440+
return headers;
441+
}

src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
export {
22
DenoHTTPWorker,
33
DenoWorkerOptions,
4-
MinimalChildProcess,
54
newDenoHTTPWorker,
65
} from "./DenoHTTPWorker.js";
76

0 commit comments

Comments
 (0)