diff --git a/deps/undici/src/docs/api/Client.md b/deps/undici/src/docs/api/Client.md index 2b5f9e526bb634..1aca7d09d31cda 100644 --- a/deps/undici/src/docs/api/Client.md +++ b/deps/undici/src/docs/api/Client.md @@ -38,6 +38,8 @@ Furthermore, the following options can be passed: * **maxCachedSessions** `number | null` (optional) - Default: `100` - Maximum number of TLS cached sessions. Use 0 to disable TLS session caching. Default: 100. * **timeout** `number | null` (optional) - Default `10e3` * **servername** `string | null` (optional) +* **keepAlive** `boolean | null` (optional) - Default: `true` - TCP keep-alive enabled +* **keepAliveInitialDelay** `number | null` (optional) - Default: `60000` - TCP keep-alive interval for the socket in milliseconds ### Example - Basic Client instantiation diff --git a/deps/undici/src/lib/client.js b/deps/undici/src/lib/client.js index 496c568d732c5c..983e70347950d8 100644 --- a/deps/undici/src/lib/client.js +++ b/deps/undici/src/lib/client.js @@ -5,6 +5,7 @@ const assert = require('assert') const net = require('net') const util = require('./core/util') +const timers = require('./timers') const Request = require('./core/request') const DispatcherBase = require('./dispatcher-base') const { @@ -444,9 +445,9 @@ class Parser { setTimeout (value, type) { this.timeoutType = type if (value !== this.timeoutValue) { - clearTimeout(this.timeout) + timers.clearTimeout(this.timeout) if (value) { - this.timeout = setTimeout(onParserTimeout, value, this) + this.timeout = timers.setTimeout(onParserTimeout, value, this) // istanbul ignore else: only for jest if (this.timeout.unref) { this.timeout.unref() @@ -562,7 +563,7 @@ class Parser { this.llhttp.llhttp_free(this.ptr) this.ptr = null - clearTimeout(this.timeout) + timers.clearTimeout(this.timeout) this.timeout = null this.timeoutValue = null this.timeoutType = null diff --git a/deps/undici/src/lib/core/connect.js b/deps/undici/src/lib/core/connect.js index 89561f16fbe0e4..f3b5cc33edd6cf 100644 --- a/deps/undici/src/lib/core/connect.js +++ b/deps/undici/src/lib/core/connect.js @@ -120,6 +120,12 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) { }) } + // Set TCP keep alive options on the socket here instead of in connect() for the case of assigning the socket + if (options.keepAlive == null || options.keepAlive) { + const keepAliveInitialDelay = options.keepAliveInitialDelay === undefined ? 60e3 : options.keepAliveInitialDelay + socket.setKeepAlive(true, keepAliveInitialDelay) + } + const cancelTimeout = setupTimeout(() => onConnectTimeout(socket), timeout) socket diff --git a/deps/undici/src/lib/core/request.js b/deps/undici/src/lib/core/request.js index e476fc1bcc1c51..7271bc64f24901 100644 --- a/deps/undici/src/lib/core/request.js +++ b/deps/undici/src/lib/core/request.js @@ -279,9 +279,13 @@ class Request { } function processHeaderValue (key, val) { - if (val && (typeof val === 'object' && !Array.isArray(val))) { + if (val && typeof val === 'object') { throw new InvalidArgumentError(`invalid ${key} header`) - } else if (headerCharRegex.exec(val) !== null) { + } + + val = val != null ? `${val}` : '' + + if (headerCharRegex.exec(val) !== null) { throw new InvalidArgumentError(`invalid ${key} header`) } diff --git a/deps/undici/src/lib/timers.js b/deps/undici/src/lib/timers.js new file mode 100644 index 00000000000000..f96bc62f2863a1 --- /dev/null +++ b/deps/undici/src/lib/timers.js @@ -0,0 +1,89 @@ +'use strict' + +let fastNow = Date.now() +let fastNowTimeout + +const fastTimers = [] + +function onTimeout () { + fastNow = Date.now() + + let len = fastTimers.length + let idx = 0 + while (idx < len) { + const timer = fastTimers[idx] + + if (timer.expires && fastNow >= timer.expires) { + timer.expires = 0 + timer.callback(timer.opaque) + } + + if (timer.expires === 0) { + timer.active = false + if (idx !== len - 1) { + fastTimers[idx] = fastTimers.pop() + } else { + fastTimers.pop() + } + len -= 1 + } else { + idx += 1 + } + } + + if (fastTimers.length > 0) { + refreshTimeout() + } +} + +function refreshTimeout () { + if (fastNowTimeout && fastNowTimeout.refresh) { + fastNowTimeout.refresh() + } else { + clearTimeout(fastNowTimeout) + fastNowTimeout = setTimeout(onTimeout, 1e3) + if (fastNowTimeout.unref) { + fastNowTimeout.unref() + } + } +} + +class Timeout { + constructor (callback, delay, opaque) { + this.callback = callback + this.delay = delay + this.opaque = opaque + this.expires = 0 + this.active = false + + this.refresh() + } + + refresh () { + if (!this.active) { + this.active = true + fastTimers.push(this) + if (!fastNowTimeout || fastTimers.length === 1) { + refreshTimeout() + fastNow = Date.now() + } + } + + this.expires = fastNow + this.delay + } + + clear () { + this.expires = 0 + } +} + +module.exports = { + setTimeout (callback, delay, opaque) { + return new Timeout(callback, delay, opaque) + }, + clearTimeout (timeout) { + if (timeout && timeout.clear) { + timeout.clear() + } + } +} diff --git a/deps/undici/src/package.json b/deps/undici/src/package.json index 8ced1d9222cbfe..af56b2ab98fc5e 100644 --- a/deps/undici/src/package.json +++ b/deps/undici/src/package.json @@ -1,6 +1,6 @@ { "name": "undici", - "version": "5.17.1", + "version": "5.18.0", "description": "An HTTP/1.1 client, written from scratch for Node.js", "homepage": "https://undici.nodejs.org", "bugs": { diff --git a/deps/undici/undici.js b/deps/undici/undici.js index 78f633db92b70a..9fd6547bd9529d 100644 --- a/deps/undici/undici.js +++ b/deps/undici/undici.js @@ -7843,6 +7843,87 @@ var require_pool_base = __commonJS({ } }); +// lib/timers.js +var require_timers = __commonJS({ + "lib/timers.js"(exports2, module2) { + "use strict"; + var fastNow = Date.now(); + var fastNowTimeout; + var fastTimers = []; + function onTimeout() { + fastNow = Date.now(); + let len = fastTimers.length; + let idx = 0; + while (idx < len) { + const timer = fastTimers[idx]; + if (timer.expires && fastNow >= timer.expires) { + timer.expires = 0; + timer.callback(timer.opaque); + } + if (timer.expires === 0) { + timer.active = false; + if (idx !== len - 1) { + fastTimers[idx] = fastTimers.pop(); + } else { + fastTimers.pop(); + } + len -= 1; + } else { + idx += 1; + } + } + if (fastTimers.length > 0) { + refreshTimeout(); + } + } + function refreshTimeout() { + if (fastNowTimeout && fastNowTimeout.refresh) { + fastNowTimeout.refresh(); + } else { + clearTimeout(fastNowTimeout); + fastNowTimeout = setTimeout(onTimeout, 1e3); + if (fastNowTimeout.unref) { + fastNowTimeout.unref(); + } + } + } + var Timeout = class { + constructor(callback, delay, opaque) { + this.callback = callback; + this.delay = delay; + this.opaque = opaque; + this.expires = 0; + this.active = false; + this.refresh(); + } + refresh() { + if (!this.active) { + this.active = true; + fastTimers.push(this); + if (!fastNowTimeout || fastTimers.length === 1) { + refreshTimeout(); + fastNow = Date.now(); + } + } + this.expires = fastNow + this.delay; + } + clear() { + this.expires = 0; + } + }; + module2.exports = { + setTimeout(callback, delay, opaque) { + return new Timeout(callback, delay, opaque); + }, + clearTimeout(timeout) { + if (timeout && timeout.clear) { + timeout.clear(); + } + } + }; + } +}); + // lib/core/request.js var require_request2 = __commonJS({ "lib/core/request.js"(exports2, module2) { @@ -8051,9 +8132,11 @@ var require_request2 = __commonJS({ } }; function processHeaderValue(key, val) { - if (val && (typeof val === "object" && !Array.isArray(val))) { + if (val && typeof val === "object") { throw new InvalidArgumentError(`invalid ${key} header`); - } else if (headerCharRegex.exec(val) !== null) { + } + val = val != null ? `${val}` : ""; + if (headerCharRegex.exec(val) !== null) { throw new InvalidArgumentError(`invalid ${key} header`); } return `${key}: ${val}\r @@ -8204,6 +8287,10 @@ var require_connect = __commonJS({ host: hostname }); } + if (options.keepAlive == null || options.keepAlive) { + const keepAliveInitialDelay = options.keepAliveInitialDelay === void 0 ? 6e4 : options.keepAliveInitialDelay; + socket.setKeepAlive(true, keepAliveInitialDelay); + } const cancelTimeout = setupTimeout(() => onConnectTimeout(socket), timeout); socket.setNoDelay(true).once(protocol === "https:" ? "secureConnect" : "connect", function() { cancelTimeout(); @@ -8774,6 +8861,7 @@ var require_client = __commonJS({ var assert = require("assert"); var net = require("net"); var util = require_util(); + var timers = require_timers(); var Request = require_request2(); var DispatcherBase = require_dispatcher_base(); var { @@ -9130,9 +9218,9 @@ var require_client = __commonJS({ setTimeout(value, type) { this.timeoutType = type; if (value !== this.timeoutValue) { - clearTimeout(this.timeout); + timers.clearTimeout(this.timeout); if (value) { - this.timeout = setTimeout(onParserTimeout, value, this); + this.timeout = timers.setTimeout(onParserTimeout, value, this); if (this.timeout.unref) { this.timeout.unref(); } @@ -9221,7 +9309,7 @@ var require_client = __commonJS({ assert(currentParser == null); this.llhttp.llhttp_free(this.ptr); this.ptr = null; - clearTimeout(this.timeout); + timers.clearTimeout(this.timeout); this.timeout = null; this.timeoutValue = null; this.timeoutType = null; diff --git a/src/undici_version.h b/src/undici_version.h index 5e070b7c3bc565..89b01122b24b5f 100644 --- a/src/undici_version.h +++ b/src/undici_version.h @@ -2,5 +2,5 @@ // Refer to tools/update-undici.sh #ifndef SRC_UNDICI_VERSION_H_ #define SRC_UNDICI_VERSION_H_ -#define UNDICI_VERSION "5.17.1" +#define UNDICI_VERSION "5.18.0" #endif // SRC_UNDICI_VERSION_H_