aboutsummaryrefslogtreecommitdiff
path: root/src/js/node
diff options
context:
space:
mode:
Diffstat (limited to 'src/js/node')
-rw-r--r--src/js/node/http.ts132
-rw-r--r--src/js/node/stream.js28
2 files changed, 113 insertions, 47 deletions
diff --git a/src/js/node/http.ts b/src/js/node/http.ts
index 0c42b247b..c8fbf20d6 100644
--- a/src/js/node/http.ts
+++ b/src/js/node/http.ts
@@ -109,6 +109,28 @@ function isValidTLSArray(obj) {
}
}
+class ERR_INVALID_ARG_TYPE extends TypeError {
+ constructor(name, expected, actual) {
+ super(`The ${name} argument must be of type ${expected}. Received type ${typeof actual}`);
+ this.code = "ERR_INVALID_ARG_TYPE";
+ }
+}
+
+function validateMsecs(numberlike: any, field: string) {
+ if (typeof numberlike !== "number" || numberlike < 0) {
+ throw new ERR_INVALID_ARG_TYPE(field, "number", numberlike);
+ }
+
+ return numberlike;
+}
+function validateFunction(callable: any, field: string) {
+ if (typeof callable !== "function") {
+ throw new ERR_INVALID_ARG_TYPE(field, "Function", callable);
+ }
+
+ return callable;
+}
+
function getHeader(headers, name) {
if (!headers) return;
const result = headers.get(name);
@@ -792,12 +814,13 @@ export class OutgoingMessage extends Writable {
headersSent = false;
sendDate = true;
req;
+ timeout;
#finished = false;
[kEndCalled] = false;
#fakeSocket;
- #timeoutTimer: Timer | null = null;
+ #timeoutTimer?: Timer;
[kAbortController]: AbortController | null = null;
// Express "compress" package uses this
@@ -894,21 +917,41 @@ export class OutgoingMessage extends Writable {
[kClearTimeout]() {
if (this.#timeoutTimer) {
clearTimeout(this.#timeoutTimer);
- this.#timeoutTimer = null;
+ this.removeAllListeners("timeout");
+ this.#timeoutTimer = undefined;
}
}
+ #onTimeout() {
+ this.#timeoutTimer = undefined;
+ this[kAbortController]?.abort();
+ this.emit("timeout");
+ }
+
setTimeout(msecs, callback) {
- if (this.#timeoutTimer) return this;
- if (callback) {
- this.on("timeout", callback);
- }
+ if (this.destroyed) return this;
+
+ this.timeout = msecs = validateMsecs(msecs, "msecs");
- this.#timeoutTimer = setTimeout(async () => {
- this.#timeoutTimer = null;
- this[kAbortController]?.abort();
- this.emit("timeout");
- }, msecs);
+ // Attempt to clear an existing timer in both cases -
+ // even if it will be rescheduled we don't want to leak an existing timer.
+ clearTimeout(this.#timeoutTimer!);
+
+ if (msecs === 0) {
+ if (callback !== undefined) {
+ validateFunction(callback, "callback");
+ this.removeListener("timeout", callback);
+ }
+
+ this.#timeoutTimer = undefined;
+ } else {
+ this.#timeoutTimer = setTimeout(this.#onTimeout.bind(this), msecs).unref();
+
+ if (callback !== undefined) {
+ validateFunction(callback, "callback");
+ this.once("timeout", callback);
+ }
+ }
return this;
}
@@ -1159,7 +1202,7 @@ export class ClientRequest extends OutgoingMessage {
#fetchRequest;
#signal: AbortSignal | null = null;
[kAbortController]: AbortController | null = null;
- #timeoutTimer: Timer | null = null;
+ #timeoutTimer?: Timer = undefined;
#options;
#finished;
@@ -1228,6 +1271,9 @@ export class ClientRequest extends OutgoingMessage {
redirect: "manual",
verbose: Boolean(__DEBUG__),
signal: this[kAbortController].signal,
+
+ // Timeouts are handled via this.setTimeout.
+ timeout: false,
},
)
.then(response => {
@@ -1352,8 +1398,6 @@ export class ClientRequest extends OutgoingMessage {
this.#socketPath = options.socketPath;
- if (options.timeout !== undefined) this.setTimeout(options.timeout, null);
-
const signal = options.signal;
if (signal) {
//We still want to control abort function and timeout so signal call our AbortController
@@ -1431,7 +1475,12 @@ export class ClientRequest extends OutgoingMessage {
this.#reusedSocket = false;
this.#host = host;
this.#protocol = protocol;
- this.#timeoutTimer = null;
+
+ var timeout = options.timeout;
+ if (timeout !== undefined && timeout !== 0) {
+ this.setTimeout(timeout, undefined);
+ }
+
const headersArray = ArrayIsArray(headers);
if (!headersArray) {
var headers = options.headers;
@@ -1486,17 +1535,8 @@ export class ClientRequest extends OutgoingMessage {
// this[kUniqueHeaders] = parseUniqueHeadersOption(options.uniqueHeaders);
- var optsWithoutSignal = options;
- if (optsWithoutSignal.signal) {
- optsWithoutSignal = ObjectAssign({}, options);
- delete optsWithoutSignal.signal;
- }
+ var { signal: _signal, ...optsWithoutSignal } = options;
this.#options = optsWithoutSignal;
-
- var timeout = options.timeout;
- if (timeout) {
- this.setTimeout(timeout);
- }
}
setSocketKeepAlive(enable = true, initialDelay = 0) {
@@ -1509,21 +1549,41 @@ export class ClientRequest extends OutgoingMessage {
[kClearTimeout]() {
if (this.#timeoutTimer) {
clearTimeout(this.#timeoutTimer);
- this.#timeoutTimer = null;
+ this.#timeoutTimer = undefined;
+ this.removeAllListeners("timeout");
}
}
- setTimeout(msecs, callback?) {
- if (this.#timeoutTimer) return this;
- if (callback) {
- this.on("timeout", callback);
- }
+ #onTimeout() {
+ this.#timeoutTimer = undefined;
+ this[kAbortController]?.abort();
+ this.emit("timeout");
+ }
- this.#timeoutTimer = setTimeout(async () => {
- this.#timeoutTimer = null;
- this[kAbortController]?.abort();
- this.emit("timeout");
- }, msecs);
+ setTimeout(msecs, callback) {
+ if (this.destroyed) return this;
+
+ this.timeout = msecs = validateMsecs(msecs, "msecs");
+
+ // Attempt to clear an existing timer in both cases -
+ // even if it will be rescheduled we don't want to leak an existing timer.
+ clearTimeout(this.#timeoutTimer!);
+
+ if (msecs === 0) {
+ if (callback !== undefined) {
+ validateFunction(callback, "callback");
+ this.removeListener("timeout", callback);
+ }
+
+ this.#timeoutTimer = undefined;
+ } else {
+ this.#timeoutTimer = setTimeout(this.#onTimeout.bind(this), msecs).unref();
+
+ if (callback !== undefined) {
+ validateFunction(callback, "callback");
+ this.once("timeout", callback);
+ }
+ }
return this;
}
diff --git a/src/js/node/stream.js b/src/js/node/stream.js
index 9344fa73f..741b2f65c 100644
--- a/src/js/node/stream.js
+++ b/src/js/node/stream.js
@@ -1,15 +1,20 @@
// Hardcoded module "node:stream" / "readable-stream"
// "readable-stream" npm package
// just transpiled
-var { isPromise, isCallable, direct, Object } = globalThis[Symbol.for("Bun.lazy")]("primordials");
-import { EventEmitter as EE } from "bun:events_native";
-import { StringDecoder } from "node:string_decoder";
-globalThis.__IDS_TO_TRACK = process.env.DEBUG_TRACK_EE?.length
- ? process.env.DEBUG_TRACK_EE.split(",")
- : process.env.DEBUG_STREAMS?.length
- ? process.env.DEBUG_STREAMS.split(",")
- : null;
+// This must go at the top of the file, before any side effects.
+// IS_BUN_DEVELOPMENT is a bundle-only global variable that is set to true when
+// building a development bundle.
+const __TRACK_EE__ = IS_BUN_DEVELOPMENT && !!process.env.DEBUG_TRACK_EE;
+const __DEBUG__ = IS_BUN_DEVELOPMENT && !!(process.env.DEBUG || process.env.DEBUG_STREAMS || __TRACK_EE__);
+
+if (__DEBUG__) {
+ globalThis.__IDS_TO_TRACK = process.env.DEBUG_TRACK_EE?.length
+ ? process.env.DEBUG_TRACK_EE.split(",")
+ : process.env.DEBUG_STREAMS?.length
+ ? process.env.DEBUG_STREAMS.split(",")
+ : null;
+}
// Separating DEBUG, DEBUG_STREAMS and DEBUG_TRACK_EE env vars makes it easier to focus on the
// events in this file rather than all debug output across all files
@@ -18,9 +23,6 @@ globalThis.__IDS_TO_TRACK = process.env.DEBUG_TRACK_EE?.length
// The events and/or all of the outputs for the given stream IDs assigned at stream construction
// By default, child_process gives
-const __TRACK_EE__ = !!process.env.DEBUG_TRACK_EE;
-const __DEBUG__ = !!(process.env.DEBUG || process.env.DEBUG_STREAMS || __TRACK_EE__);
-
var debug = __DEBUG__
? globalThis.__IDS_TO_TRACK
? // If we are tracking IDs for debug event emitters, we should prefix the debug output with the ID
@@ -32,6 +34,10 @@ var debug = __DEBUG__
: (...args) => console.log(...args.slice(0, -1))
: () => {};
+var { isPromise, isCallable, direct, Object } = globalThis[Symbol.for("Bun.lazy")]("primordials");
+import { EventEmitter as EE } from "bun:events_native";
+import { StringDecoder } from "node:string_decoder";
+
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;