undici 7.27.2 → 7.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/docs/api/Client.md +3 -0
- package/docs/docs/api/Cookies.md +27 -0
- package/docs/docs/api/Socks5ProxyAgent.md +1 -0
- package/lib/cache/memory-cache-store.js +54 -9
- package/lib/cache/sqlite-cache-store.js +7 -1
- package/lib/core/request.js +12 -1
- package/lib/core/socks5-client.js +14 -4
- package/lib/core/socks5-utils.js +18 -4
- package/lib/dispatcher/agent.js +1 -1
- package/lib/dispatcher/balanced-pool.js +1 -1
- package/lib/dispatcher/client-h1.js +80 -3
- package/lib/dispatcher/client.js +3 -2
- package/lib/dispatcher/dispatcher-base.js +19 -0
- package/lib/dispatcher/pool.js +1 -1
- package/lib/dispatcher/proxy-agent.js +2 -1
- package/lib/dispatcher/socks5-proxy-agent.js +45 -34
- package/lib/handler/cache-handler.js +246 -64
- package/lib/handler/cache-revalidation-handler.js +3 -3
- package/lib/handler/retry-handler.js +24 -0
- package/lib/interceptor/cache.js +159 -52
- package/lib/util/cache.js +326 -58
- package/lib/util/date.js +23 -6
- package/lib/web/cookies/parse.js +17 -25
- package/lib/web/cookies/util.js +79 -9
- package/lib/web/websocket/permessage-deflate.js +13 -31
- package/lib/web/websocket/receiver.js +80 -23
- package/lib/web/websocket/stream/websocketstream.js +8 -1
- package/lib/web/websocket/websocket.js +8 -1
- package/package.json +1 -1
- package/types/client.d.ts +16 -0
package/docs/docs/api/Client.md
CHANGED
|
@@ -24,6 +24,9 @@ Returns: `Client`
|
|
|
24
24
|
* **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `2e3` - A number of milliseconds subtracted from server *keep-alive* hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 2 seconds.
|
|
25
25
|
* **maxHeaderSize** `number | null` (optional) - Default: `--max-http-header-size` or `16384` - The maximum length of request headers in bytes. Defaults to Node.js' --max-http-header-size or 16KiB.
|
|
26
26
|
* **maxResponseSize** `number | null` (optional) - Default: `-1` - The maximum length of response body in bytes. Set to `-1` to disable.
|
|
27
|
+
* **webSocket** `WebSocketOptions` (optional) - WebSocket-specific configuration options.
|
|
28
|
+
* **maxFragments** `number` (optional) - Default: `131072` - Maximum number of fragments in a message. Set to 0 to disable the limit.
|
|
29
|
+
* **maxPayloadSize** `number` (optional) - Default: `134217728` (128 MB) - Maximum allowed payload size in bytes for WebSocket messages. Applied to uncompressed messages, compressed frame payloads, and decompressed (permessage-deflate) messages. Set to 0 to disable the limit.
|
|
27
30
|
* **pipelining** `number | null` (optional) - Default: `1` - The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Carefully consider your workload and environment before enabling concurrent requests as pipelining may reduce performance if used incorrectly. Pipelining is sensitive to network stack settings as well as head of line blocking caused by e.g. long running requests. Set to `0` to disable keep-alive connections.
|
|
28
31
|
* **connect** `ConnectOptions | Function | null` (optional) - Default: `null`.
|
|
29
32
|
* **strictContentLength** `Boolean` (optional) - Default: `true` - Whether to treat request content length mismatches as errors. If true, an error is thrown when the request content-length header doesn't match the length of the request body. **Security Warning:** Disabling this option can expose your application to HTTP Request Smuggling attacks, where mismatched content-length headers cause servers and proxies to interpret request boundaries differently. This can lead to cache poisoning, credential hijacking, and bypassing security controls. Only disable this in controlled environments where you fully trust the request source.
|
package/docs/docs/api/Cookies.md
CHANGED
|
@@ -80,6 +80,33 @@ Arguments:
|
|
|
80
80
|
|
|
81
81
|
Returns: `Cookie[]`
|
|
82
82
|
|
|
83
|
+
## `parseCookie(cookie)`
|
|
84
|
+
|
|
85
|
+
Parses a single `Set-Cookie` header value into a `Cookie` object.
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
import { parseCookie } from 'undici'
|
|
89
|
+
|
|
90
|
+
console.log(parseCookie('undici=getSetCookies; Secure; SameSite=Lax'))
|
|
91
|
+
// {
|
|
92
|
+
// name: 'undici',
|
|
93
|
+
// value: 'getSetCookies',
|
|
94
|
+
// secure: true,
|
|
95
|
+
// sameSite: 'Lax'
|
|
96
|
+
// }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Notes:
|
|
100
|
+
|
|
101
|
+
* The cookie value is returned as it appears in the header. Percent-encoded sequences such as `%20` or `%0D%0A` are **not** decoded.
|
|
102
|
+
* `sameSite` is only set for exact case-insensitive matches of `Strict`, `Lax`, or `None`.
|
|
103
|
+
|
|
104
|
+
Arguments:
|
|
105
|
+
|
|
106
|
+
* **cookie** `string`
|
|
107
|
+
|
|
108
|
+
Returns: `Cookie | null`
|
|
109
|
+
|
|
83
110
|
## `setCookie(headers, cookie)`
|
|
84
111
|
|
|
85
112
|
Appends a cookie to the `Set-Cookie` header.
|
|
@@ -22,6 +22,7 @@ Extends: [`PoolOptions`](/docs/docs/api/Pool.md#parameter-pooloptions)
|
|
|
22
22
|
* **password** `string` (optional) - SOCKS5 proxy password for authentication. Can also be provided in the proxy URL.
|
|
23
23
|
* **connect** `Function` (optional) - Custom connector function for the proxy connection.
|
|
24
24
|
* **proxyTls** `BuildOptions` (optional) - TLS options for the proxy connection (when using SOCKS5 over TLS).
|
|
25
|
+
* **requestTls** `BuildOptions` (optional) - TLS options applied to the HTTPS connection to the target server through the SOCKS5 tunnel. Use this to configure `ca`, `cert`, `key`, `rejectUnauthorized`, `servername`, etc. for the target HTTPS endpoint.
|
|
25
26
|
|
|
26
27
|
Examples:
|
|
27
28
|
|
|
@@ -218,17 +218,62 @@ class MemoryCacheStore extends EventEmitter {
|
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
function findEntry (key, entries, now) {
|
|
221
|
-
|
|
222
|
-
entry
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
221
|
+
for (let i = 0; i < entries.length; i++) {
|
|
222
|
+
const entry = entries[i]
|
|
223
|
+
if (
|
|
224
|
+
entry.deleteAt > now &&
|
|
225
|
+
entry.method === key.method &&
|
|
226
|
+
varyMatches(key, entry)
|
|
227
|
+
) {
|
|
228
|
+
return entry
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function varyMatches (key, entry) {
|
|
234
|
+
if (entry.vary == null) {
|
|
235
|
+
return true
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
for (const headerName in entry.vary) {
|
|
239
|
+
if (Object.hasOwn(entry.vary, headerName) && !headerValueEquals(key.headers?.[headerName], entry.vary[headerName])) {
|
|
240
|
+
return false
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return true
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @param {string|string[]|null|undefined} lhs
|
|
249
|
+
* @param {string|string[]|null|undefined} rhs
|
|
250
|
+
* @returns {boolean}
|
|
251
|
+
*/
|
|
252
|
+
function headerValueEquals (lhs, rhs) {
|
|
253
|
+
if (lhs == null && rhs == null) {
|
|
254
|
+
return true
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if ((lhs == null && rhs != null) ||
|
|
258
|
+
(lhs != null && rhs == null)) {
|
|
259
|
+
return false
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (Array.isArray(lhs) && Array.isArray(rhs)) {
|
|
263
|
+
if (lhs.length !== rhs.length) {
|
|
264
|
+
return false
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
for (let i = 0; i < lhs.length; i++) {
|
|
268
|
+
if (lhs[i] !== rhs[i]) {
|
|
269
|
+
return false
|
|
227
270
|
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return true
|
|
274
|
+
}
|
|
228
275
|
|
|
229
|
-
|
|
230
|
-
}))
|
|
231
|
-
))
|
|
276
|
+
return lhs === rhs
|
|
232
277
|
}
|
|
233
278
|
|
|
234
279
|
module.exports = MemoryCacheStore
|
|
@@ -454,7 +454,13 @@ function headerValueEquals (lhs, rhs) {
|
|
|
454
454
|
return false
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
-
|
|
457
|
+
for (let i = 0; i < lhs.length; i++) {
|
|
458
|
+
if (lhs[i] !== rhs[i]) {
|
|
459
|
+
return false
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return true
|
|
458
464
|
}
|
|
459
465
|
|
|
460
466
|
return lhs === rhs
|
package/lib/core/request.js
CHANGED
|
@@ -390,7 +390,13 @@ function processHeader (request, key, val) {
|
|
|
390
390
|
} else if (typeof val[i] === 'object') {
|
|
391
391
|
throw new InvalidArgumentError(`invalid ${key} header`)
|
|
392
392
|
} else {
|
|
393
|
-
|
|
393
|
+
// Coerce primitives (and reject unsafe coercions such as functions
|
|
394
|
+
// with a crafted toString/Symbol.toPrimitive).
|
|
395
|
+
const str = `${val[i]}`
|
|
396
|
+
if (!isValidHeaderValue(str)) {
|
|
397
|
+
throw new InvalidArgumentError(`invalid ${key} header`)
|
|
398
|
+
}
|
|
399
|
+
arr.push(str)
|
|
394
400
|
}
|
|
395
401
|
}
|
|
396
402
|
val = arr
|
|
@@ -401,7 +407,12 @@ function processHeader (request, key, val) {
|
|
|
401
407
|
} else if (val === null) {
|
|
402
408
|
val = ''
|
|
403
409
|
} else {
|
|
410
|
+
// Coerce primitives (and reject unsafe coercions such as functions
|
|
411
|
+
// with a crafted toString/Symbol.toPrimitive).
|
|
404
412
|
val = `${val}`
|
|
413
|
+
if (!isValidHeaderValue(val)) {
|
|
414
|
+
throw new InvalidArgumentError(`invalid ${key} header`)
|
|
415
|
+
}
|
|
405
416
|
}
|
|
406
417
|
|
|
407
418
|
if (headerName === 'host') {
|
|
@@ -52,6 +52,7 @@ const STATES = {
|
|
|
52
52
|
INITIAL: 'initial',
|
|
53
53
|
HANDSHAKING: 'handshaking',
|
|
54
54
|
AUTHENTICATING: 'authenticating',
|
|
55
|
+
AUTHENTICATED: 'authenticated',
|
|
55
56
|
CONNECTING: 'connecting',
|
|
56
57
|
CONNECTED: 'connected',
|
|
57
58
|
ERROR: 'error',
|
|
@@ -143,6 +144,11 @@ class Socks5Client extends EventEmitter {
|
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
146
|
|
|
147
|
+
markAuthenticated () {
|
|
148
|
+
this.state = STATES.AUTHENTICATED
|
|
149
|
+
this.emit('authenticated')
|
|
150
|
+
}
|
|
151
|
+
|
|
146
152
|
/**
|
|
147
153
|
* Start the SOCKS5 handshake
|
|
148
154
|
*/
|
|
@@ -193,7 +199,7 @@ class Socks5Client extends EventEmitter {
|
|
|
193
199
|
debug('server selected auth method', method)
|
|
194
200
|
|
|
195
201
|
if (method === AUTH_METHODS.NO_AUTH) {
|
|
196
|
-
this.
|
|
202
|
+
this.markAuthenticated()
|
|
197
203
|
} else if (method === AUTH_METHODS.USERNAME_PASSWORD) {
|
|
198
204
|
this.state = STATES.AUTHENTICATING
|
|
199
205
|
this.sendAuthRequest()
|
|
@@ -258,7 +264,7 @@ class Socks5Client extends EventEmitter {
|
|
|
258
264
|
|
|
259
265
|
this.buffer = this.buffer.subarray(2)
|
|
260
266
|
debug('authentication successful')
|
|
261
|
-
this.
|
|
267
|
+
this.markAuthenticated()
|
|
262
268
|
}
|
|
263
269
|
|
|
264
270
|
/**
|
|
@@ -267,8 +273,12 @@ class Socks5Client extends EventEmitter {
|
|
|
267
273
|
* @param {number} port - Target port
|
|
268
274
|
*/
|
|
269
275
|
connect (address, port) {
|
|
270
|
-
if (this.state === STATES.CONNECTED) {
|
|
271
|
-
throw new InvalidArgumentError('
|
|
276
|
+
if (this.state === STATES.CONNECTING || this.state === STATES.CONNECTED) {
|
|
277
|
+
throw new InvalidArgumentError('Connection already in progress')
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (this.state !== STATES.AUTHENTICATED) {
|
|
281
|
+
throw new InvalidArgumentError('Client must be authenticated before CONNECT')
|
|
272
282
|
}
|
|
273
283
|
|
|
274
284
|
debug('connecting to', address, port)
|
package/lib/core/socks5-utils.js
CHANGED
|
@@ -46,12 +46,26 @@ function parseAddress (address) {
|
|
|
46
46
|
*/
|
|
47
47
|
function parseIPv6 (address) {
|
|
48
48
|
const buffer = Buffer.alloc(16)
|
|
49
|
+
let normalizedAddress = address
|
|
50
|
+
|
|
51
|
+
// Expand an embedded IPv4 tail into the last two IPv6 groups.
|
|
52
|
+
if (address.includes('.')) {
|
|
53
|
+
const lastColonIndex = address.lastIndexOf(':')
|
|
54
|
+
const ipv4Part = address.slice(lastColonIndex + 1)
|
|
55
|
+
|
|
56
|
+
if (net.isIPv4(ipv4Part)) {
|
|
57
|
+
const octets = ipv4Part.split('.').map(Number)
|
|
58
|
+
const high = ((octets[0] << 8) | octets[1]).toString(16)
|
|
59
|
+
const low = ((octets[2] << 8) | octets[3]).toString(16)
|
|
60
|
+
normalizedAddress = `${address.slice(0, lastColonIndex)}:${high}:${low}`
|
|
61
|
+
}
|
|
62
|
+
}
|
|
49
63
|
|
|
50
64
|
// Handle compressed notation (::)
|
|
51
|
-
const doubleColonIndex =
|
|
65
|
+
const doubleColonIndex = normalizedAddress.indexOf('::')
|
|
52
66
|
if (doubleColonIndex !== -1) {
|
|
53
|
-
const before =
|
|
54
|
-
const after =
|
|
67
|
+
const before = normalizedAddress.slice(0, doubleColonIndex)
|
|
68
|
+
const after = normalizedAddress.slice(doubleColonIndex + 2)
|
|
55
69
|
const beforeParts = before === '' ? [] : before.split(':')
|
|
56
70
|
const afterParts = after === '' ? [] : after.split(':')
|
|
57
71
|
|
|
@@ -66,7 +80,7 @@ function parseIPv6 (address) {
|
|
|
66
80
|
bufferIndex += 2
|
|
67
81
|
}
|
|
68
82
|
} else {
|
|
69
|
-
const parts =
|
|
83
|
+
const parts = normalizedAddress.split(':')
|
|
70
84
|
for (let i = 0; i < parts.length; i++) {
|
|
71
85
|
buffer.writeUInt16BE(parseInt(parts[i], 16), i * 2)
|
|
72
86
|
}
|
package/lib/dispatcher/agent.js
CHANGED
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
RequestContentLengthMismatchError,
|
|
11
11
|
ResponseContentLengthMismatchError,
|
|
12
12
|
RequestAbortedError,
|
|
13
|
+
InvalidArgumentError,
|
|
13
14
|
HeadersTimeoutError,
|
|
14
15
|
HeadersOverflowError,
|
|
15
16
|
SocketError,
|
|
@@ -57,6 +58,9 @@ const constants = require('../llhttp/constants.js')
|
|
|
57
58
|
const EMPTY_BUF = Buffer.alloc(0)
|
|
58
59
|
const FastBuffer = Buffer[Symbol.species]
|
|
59
60
|
const removeAllListeners = util.removeAllListeners
|
|
61
|
+
const kIdleSocketValidation = Symbol('kIdleSocketValidation')
|
|
62
|
+
const kIdleSocketValidationTimeout = Symbol('kIdleSocketValidationTimeout')
|
|
63
|
+
const kSocketUsed = Symbol('kSocketUsed')
|
|
60
64
|
|
|
61
65
|
let extractBody
|
|
62
66
|
|
|
@@ -440,6 +444,11 @@ class Parser {
|
|
|
440
444
|
return -1
|
|
441
445
|
}
|
|
442
446
|
|
|
447
|
+
if (client[kRunning] === 0) {
|
|
448
|
+
util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
|
|
449
|
+
return -1
|
|
450
|
+
}
|
|
451
|
+
|
|
443
452
|
const request = client[kQueue][client[kRunningIdx]]
|
|
444
453
|
if (!request) {
|
|
445
454
|
return -1
|
|
@@ -568,6 +577,11 @@ class Parser {
|
|
|
568
577
|
return -1
|
|
569
578
|
}
|
|
570
579
|
|
|
580
|
+
if (client[kRunning] === 0) {
|
|
581
|
+
util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
|
|
582
|
+
return -1
|
|
583
|
+
}
|
|
584
|
+
|
|
571
585
|
const request = client[kQueue][client[kRunningIdx]]
|
|
572
586
|
|
|
573
587
|
if (!request) {
|
|
@@ -746,6 +760,7 @@ class Parser {
|
|
|
746
760
|
request.onComplete(headers)
|
|
747
761
|
|
|
748
762
|
client[kQueue][client[kRunningIdx]++] = null
|
|
763
|
+
socket[kSocketUsed] = client[kPending] === 0
|
|
749
764
|
|
|
750
765
|
if (socket[kWriting]) {
|
|
751
766
|
assert(client[kRunning] === 0)
|
|
@@ -822,6 +837,9 @@ function connectH1 (client, socket) {
|
|
|
822
837
|
socket[kWriting] = false
|
|
823
838
|
socket[kReset] = false
|
|
824
839
|
socket[kBlocking] = false
|
|
840
|
+
socket[kIdleSocketValidation] = 0
|
|
841
|
+
socket[kIdleSocketValidationTimeout] = null
|
|
842
|
+
socket[kSocketUsed] = false
|
|
825
843
|
socket[kParser] = new Parser(client, socket, llhttpInstance)
|
|
826
844
|
|
|
827
845
|
util.addListener(socket, 'error', onHttpSocketError)
|
|
@@ -864,7 +882,7 @@ function connectH1 (client, socket) {
|
|
|
864
882
|
* @returns {boolean}
|
|
865
883
|
*/
|
|
866
884
|
busy (request) {
|
|
867
|
-
if (socket[kWriting] || socket[kReset] || socket[kBlocking]) {
|
|
885
|
+
if (socket[kWriting] || socket[kReset] || socket[kBlocking] || socket[kIdleSocketValidation] === 1) {
|
|
868
886
|
return true
|
|
869
887
|
}
|
|
870
888
|
|
|
@@ -944,6 +962,8 @@ function onHttpSocketEnd () {
|
|
|
944
962
|
function onHttpSocketClose () {
|
|
945
963
|
const parser = this[kParser]
|
|
946
964
|
|
|
965
|
+
clearIdleSocketValidation(this)
|
|
966
|
+
|
|
947
967
|
if (parser) {
|
|
948
968
|
if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {
|
|
949
969
|
this[kError] = parser.finish() || this[kError]
|
|
@@ -990,6 +1010,28 @@ function onSocketClose () {
|
|
|
990
1010
|
this[kClosed] = true
|
|
991
1011
|
}
|
|
992
1012
|
|
|
1013
|
+
function clearIdleSocketValidation (socket) {
|
|
1014
|
+
if (socket[kIdleSocketValidationTimeout]) {
|
|
1015
|
+
clearTimeout(socket[kIdleSocketValidationTimeout])
|
|
1016
|
+
socket[kIdleSocketValidationTimeout] = null
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
socket[kIdleSocketValidation] = 0
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
function scheduleIdleSocketValidation (client, socket) {
|
|
1023
|
+
socket[kIdleSocketValidation] = 1
|
|
1024
|
+
socket[kIdleSocketValidationTimeout] = setTimeout(() => {
|
|
1025
|
+
socket[kIdleSocketValidationTimeout] = null
|
|
1026
|
+
socket[kIdleSocketValidation] = 2
|
|
1027
|
+
|
|
1028
|
+
if (client[kSocket] === socket && !socket.destroyed) {
|
|
1029
|
+
client[kResume]()
|
|
1030
|
+
}
|
|
1031
|
+
}, 0)
|
|
1032
|
+
socket[kIdleSocketValidationTimeout].unref?.()
|
|
1033
|
+
}
|
|
1034
|
+
|
|
993
1035
|
/**
|
|
994
1036
|
* @param {import('./client.js')} client
|
|
995
1037
|
*/
|
|
@@ -1007,6 +1049,32 @@ function resumeH1 (client) {
|
|
|
1007
1049
|
socket[kNoRef] = false
|
|
1008
1050
|
}
|
|
1009
1051
|
|
|
1052
|
+
if (client[kRunning] === 0 && client[kPending] > 0 && socket[kSocketUsed]) {
|
|
1053
|
+
if (socket[kIdleSocketValidation] === 0) {
|
|
1054
|
+
scheduleIdleSocketValidation(client, socket)
|
|
1055
|
+
socket[kParser].readMore()
|
|
1056
|
+
if (socket.destroyed) {
|
|
1057
|
+
return
|
|
1058
|
+
}
|
|
1059
|
+
return
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
if (socket[kIdleSocketValidation] === 1) {
|
|
1063
|
+
socket[kParser].readMore()
|
|
1064
|
+
if (socket.destroyed) {
|
|
1065
|
+
return
|
|
1066
|
+
}
|
|
1067
|
+
return
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
if (client[kRunning] === 0) {
|
|
1072
|
+
socket[kParser].readMore()
|
|
1073
|
+
if (socket.destroyed) {
|
|
1074
|
+
return
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1010
1078
|
if (client[kSize] === 0) {
|
|
1011
1079
|
if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) {
|
|
1012
1080
|
socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE)
|
|
@@ -1067,8 +1135,16 @@ function writeH1 (client, request) {
|
|
|
1067
1135
|
}
|
|
1068
1136
|
body = bodyStream.stream
|
|
1069
1137
|
contentLength = bodyStream.length
|
|
1070
|
-
} else if (util.isBlobLike(body) && request.contentType == null
|
|
1071
|
-
|
|
1138
|
+
} else if (util.isBlobLike(body) && request.contentType == null) {
|
|
1139
|
+
const contentType = body.type
|
|
1140
|
+
if (contentType) {
|
|
1141
|
+
const contentTypeValue = `${contentType}`
|
|
1142
|
+
if (!util.isValidHeaderValue(contentTypeValue)) {
|
|
1143
|
+
util.errorRequest(client, request, new InvalidArgumentError('invalid content-type header'))
|
|
1144
|
+
return false
|
|
1145
|
+
}
|
|
1146
|
+
headers.push('content-type', contentTypeValue)
|
|
1147
|
+
}
|
|
1072
1148
|
}
|
|
1073
1149
|
|
|
1074
1150
|
if (body && typeof body.read === 'function') {
|
|
@@ -1105,6 +1181,7 @@ function writeH1 (client, request) {
|
|
|
1105
1181
|
}
|
|
1106
1182
|
|
|
1107
1183
|
const socket = client[kSocket]
|
|
1184
|
+
clearIdleSocketValidation(socket)
|
|
1108
1185
|
|
|
1109
1186
|
/**
|
|
1110
1187
|
* @param {Error} [err]
|
package/lib/dispatcher/client.js
CHANGED
|
@@ -114,7 +114,8 @@ class Client extends DispatcherBase {
|
|
|
114
114
|
useH2c,
|
|
115
115
|
initialWindowSize,
|
|
116
116
|
connectionWindowSize,
|
|
117
|
-
pingInterval
|
|
117
|
+
pingInterval,
|
|
118
|
+
webSocket
|
|
118
119
|
} = {}) {
|
|
119
120
|
if (keepAlive !== undefined) {
|
|
120
121
|
throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead')
|
|
@@ -222,7 +223,7 @@ class Client extends DispatcherBase {
|
|
|
222
223
|
throw new InvalidArgumentError('pingInterval must be a positive integer, greater or equal to 0')
|
|
223
224
|
}
|
|
224
225
|
|
|
225
|
-
super()
|
|
226
|
+
super({ webSocket })
|
|
226
227
|
|
|
227
228
|
if (typeof connect !== 'function') {
|
|
228
229
|
connect = buildConnector({
|
|
@@ -11,6 +11,7 @@ const { kDestroy, kClose, kClosed, kDestroyed, kDispatch } = require('../core/sy
|
|
|
11
11
|
|
|
12
12
|
const kOnDestroyed = Symbol('onDestroyed')
|
|
13
13
|
const kOnClosed = Symbol('onClosed')
|
|
14
|
+
const kWebSocketOptions = Symbol('webSocketOptions')
|
|
14
15
|
|
|
15
16
|
class DispatcherBase extends Dispatcher {
|
|
16
17
|
/** @type {boolean} */
|
|
@@ -25,6 +26,24 @@ class DispatcherBase extends Dispatcher {
|
|
|
25
26
|
/** @type {Array<Function>|null} */
|
|
26
27
|
[kOnClosed] = null
|
|
27
28
|
|
|
29
|
+
/**
|
|
30
|
+
* @param {import('../../types/dispatcher').DispatcherOptions} [opts]
|
|
31
|
+
*/
|
|
32
|
+
constructor (opts) {
|
|
33
|
+
super()
|
|
34
|
+
this[kWebSocketOptions] = opts?.webSocket ?? {}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @returns {import('../../types/dispatcher').WebSocketOptions}
|
|
39
|
+
*/
|
|
40
|
+
get webSocketOptions () {
|
|
41
|
+
return {
|
|
42
|
+
maxFragments: this[kWebSocketOptions].maxFragments ?? 131072,
|
|
43
|
+
maxPayloadSize: this[kWebSocketOptions].maxPayloadSize ?? 128 * 1024 * 1024 // 128 MB default
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
28
47
|
/** @returns {boolean} */
|
|
29
48
|
get destroyed () {
|
|
30
49
|
return this[kDestroyed]
|
package/lib/dispatcher/pool.js
CHANGED
|
@@ -142,7 +142,8 @@ class ProxyAgent extends DispatcherBase {
|
|
|
142
142
|
factory: agentFactory,
|
|
143
143
|
username: opts.username || username,
|
|
144
144
|
password: opts.password || password,
|
|
145
|
-
proxyTls: opts.proxyTls
|
|
145
|
+
proxyTls: opts.proxyTls,
|
|
146
|
+
requestTls: opts.requestTls
|
|
146
147
|
})
|
|
147
148
|
}
|
|
148
149
|
|