undici 6.10.1 → 6.10.2
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/README.md +2 -2
- package/lib/core/connect.js +1 -1
- package/lib/dispatcher/client-h2.js +6 -6
- package/lib/handler/retry-handler.js +2 -4
- package/lib/web/cookies/index.js +1 -1
- package/lib/web/eventsource/eventsource.js +11 -0
- package/lib/web/fetch/util.js +36 -0
- package/lib/web/websocket/receiver.js +2 -3
- package/lib/web/websocket/util.js +1 -3
- package/package.json +1 -1
- package/types/dispatcher.d.ts +2 -2
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ npm i undici
|
|
|
17
17
|
|
|
18
18
|
## Benchmarks
|
|
19
19
|
|
|
20
|
-
The benchmark is a simple getting data [example](benchmarks/benchmark.js) using a
|
|
20
|
+
The benchmark is a simple getting data [example](https://github.com/nodejs/undici/blob/main/benchmarks/benchmark.js) using a
|
|
21
21
|
50 TCP connections with a pipelining depth of 10 running on Node 20.10.0.
|
|
22
22
|
|
|
23
23
|
| _Tests_ | _Samples_ | _Result_ | _Tolerance_ | _Difference with slowest_ |
|
|
@@ -35,7 +35,7 @@ The benchmark is a simple getting data [example](benchmarks/benchmark.js) using
|
|
|
35
35
|
| undici - stream | 15 | 20317.29 req/sec | ± 2.13 % | + 448.46 % |
|
|
36
36
|
| undici - dispatch | 10 | 24883.28 req/sec | ± 1.54 % | + 571.72 % |
|
|
37
37
|
|
|
38
|
-
The benchmark is a simple sending data [example](benchmarks/post-benchmark.js) using a
|
|
38
|
+
The benchmark is a simple sending data [example](https://github.com/nodejs/undici/blob/main/benchmarks/post-benchmark.js) using a
|
|
39
39
|
50 TCP connections with a pipelining depth of 10 running on Node 20.10.0.
|
|
40
40
|
|
|
41
41
|
| _Tests_ | _Samples_ | _Result_ | _Tolerance_ | _Difference with slowest_ |
|
package/lib/core/connect.js
CHANGED
|
@@ -185,7 +185,7 @@ function setupTimeout (onConnectTimeout, timeout) {
|
|
|
185
185
|
function onConnectTimeout (socket) {
|
|
186
186
|
let message = 'Connect Timeout Error'
|
|
187
187
|
if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
|
|
188
|
-
message
|
|
188
|
+
message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')})`
|
|
189
189
|
}
|
|
190
190
|
util.destroy(socket, new ConnectTimeoutError(message))
|
|
191
191
|
}
|
|
@@ -394,6 +394,12 @@ function writeH2 (client, request) {
|
|
|
394
394
|
if (request.onHeaders(Number(statusCode), realHeaders, stream.resume.bind(stream), '') === false) {
|
|
395
395
|
stream.pause()
|
|
396
396
|
}
|
|
397
|
+
|
|
398
|
+
stream.on('data', (chunk) => {
|
|
399
|
+
if (request.onData(chunk) === false) {
|
|
400
|
+
stream.pause()
|
|
401
|
+
}
|
|
402
|
+
})
|
|
397
403
|
})
|
|
398
404
|
|
|
399
405
|
stream.once('end', () => {
|
|
@@ -418,12 +424,6 @@ function writeH2 (client, request) {
|
|
|
418
424
|
util.destroy(stream, err)
|
|
419
425
|
})
|
|
420
426
|
|
|
421
|
-
stream.on('data', (chunk) => {
|
|
422
|
-
if (request.onData(chunk) === false) {
|
|
423
|
-
stream.pause()
|
|
424
|
-
}
|
|
425
|
-
})
|
|
426
|
-
|
|
427
427
|
stream.once('close', () => {
|
|
428
428
|
session[kOpenStreams] -= 1
|
|
429
429
|
// TODO(HTTP/2): unref only if current streams count is 0
|
|
@@ -242,14 +242,12 @@ class RetryHandler {
|
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
const { start, size, end = size } = range
|
|
245
|
-
|
|
246
245
|
assert(
|
|
247
|
-
start != null && Number.isFinite(start)
|
|
246
|
+
start != null && Number.isFinite(start),
|
|
248
247
|
'content-range mismatch'
|
|
249
248
|
)
|
|
250
|
-
assert(Number.isFinite(start))
|
|
251
249
|
assert(
|
|
252
|
-
end != null && Number.isFinite(end)
|
|
250
|
+
end != null && Number.isFinite(end),
|
|
253
251
|
'invalid content-length'
|
|
254
252
|
)
|
|
255
253
|
|
package/lib/web/cookies/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const { parseMIMEType } = require('../fetch/data-url')
|
|
|
10
10
|
const { MessageEvent } = require('../websocket/events')
|
|
11
11
|
const { isNetworkError } = require('../fetch/response')
|
|
12
12
|
const { delay } = require('./util')
|
|
13
|
+
const { kEnumerableProperty } = require('../../core/util')
|
|
13
14
|
|
|
14
15
|
let experimentalWarned = false
|
|
15
16
|
|
|
@@ -459,6 +460,16 @@ const constantsPropertyDescriptors = {
|
|
|
459
460
|
Object.defineProperties(EventSource, constantsPropertyDescriptors)
|
|
460
461
|
Object.defineProperties(EventSource.prototype, constantsPropertyDescriptors)
|
|
461
462
|
|
|
463
|
+
Object.defineProperties(EventSource.prototype, {
|
|
464
|
+
close: kEnumerableProperty,
|
|
465
|
+
onerror: kEnumerableProperty,
|
|
466
|
+
onmessage: kEnumerableProperty,
|
|
467
|
+
onopen: kEnumerableProperty,
|
|
468
|
+
readyState: kEnumerableProperty,
|
|
469
|
+
url: kEnumerableProperty,
|
|
470
|
+
withCredentials: kEnumerableProperty
|
|
471
|
+
})
|
|
472
|
+
|
|
462
473
|
webidl.converters.EventSourceInitDict = webidl.dictionaryConverter([
|
|
463
474
|
{ key: 'withCredentials', converter: webidl.converters.boolean, defaultValue: false }
|
|
464
475
|
])
|
package/lib/web/fetch/util.js
CHANGED
|
@@ -44,6 +44,12 @@ function responseLocationURL (response, requestFragment) {
|
|
|
44
44
|
// 3. If location is a header value, then set location to the result of
|
|
45
45
|
// parsing location with response’s URL.
|
|
46
46
|
if (location !== null && isValidHeaderValue(location)) {
|
|
47
|
+
if (!isValidEncodedURL(location)) {
|
|
48
|
+
// Some websites respond location header in UTF-8 form without encoding them as ASCII
|
|
49
|
+
// and major browsers redirect them to correctly UTF-8 encoded addresses.
|
|
50
|
+
// Here, we handle that behavior in the same way.
|
|
51
|
+
location = normalizeBinaryStringToUtf8(location)
|
|
52
|
+
}
|
|
47
53
|
location = new URL(location, responseURL(response))
|
|
48
54
|
}
|
|
49
55
|
|
|
@@ -57,6 +63,36 @@ function responseLocationURL (response, requestFragment) {
|
|
|
57
63
|
return location
|
|
58
64
|
}
|
|
59
65
|
|
|
66
|
+
/**
|
|
67
|
+
* @see https://www.rfc-editor.org/rfc/rfc1738#section-2.2
|
|
68
|
+
* @param {string} url
|
|
69
|
+
* @returns {boolean}
|
|
70
|
+
*/
|
|
71
|
+
function isValidEncodedURL (url) {
|
|
72
|
+
for (const c of url) {
|
|
73
|
+
const code = c.charCodeAt(0)
|
|
74
|
+
// Not used in US-ASCII
|
|
75
|
+
if (code >= 0x80) {
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
// Control characters
|
|
79
|
+
if ((code >= 0x00 && code <= 0x1F) || code === 0x7F) {
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* If string contains non-ASCII characters, assumes it's UTF-8 encoded and decodes it.
|
|
88
|
+
* Since UTF-8 is a superset of ASCII, this will work for ASCII strings as well.
|
|
89
|
+
* @param {string} value
|
|
90
|
+
* @returns {string}
|
|
91
|
+
*/
|
|
92
|
+
function normalizeBinaryStringToUtf8 (value) {
|
|
93
|
+
return Buffer.from(value, 'binary').toString('utf8')
|
|
94
|
+
}
|
|
95
|
+
|
|
60
96
|
/** @returns {URL} */
|
|
61
97
|
function requestCurrentURL (request) {
|
|
62
98
|
return request.urlList[request.urlList.length - 1]
|
|
@@ -12,8 +12,6 @@ const { WebsocketFrameSend } = require('./frame')
|
|
|
12
12
|
// Copyright (c) 2013 Arnout Kazemier and contributors
|
|
13
13
|
// Copyright (c) 2016 Luigi Pinca and contributors
|
|
14
14
|
|
|
15
|
-
const textDecoder = new TextDecoder('utf-8', { fatal: true })
|
|
16
|
-
|
|
17
15
|
class ByteParser extends Writable {
|
|
18
16
|
#buffers = []
|
|
19
17
|
#byteOffset = 0
|
|
@@ -316,7 +314,8 @@ class ByteParser extends Writable {
|
|
|
316
314
|
}
|
|
317
315
|
|
|
318
316
|
try {
|
|
319
|
-
|
|
317
|
+
// TODO: optimize this
|
|
318
|
+
reason = new TextDecoder('utf-8', { fatal: true }).decode(reason)
|
|
320
319
|
} catch {
|
|
321
320
|
return null
|
|
322
321
|
}
|
|
@@ -68,8 +68,6 @@ function fireEvent (e, target, eventConstructor = Event, eventInitDict = {}) {
|
|
|
68
68
|
target.dispatchEvent(event)
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
const textDecoder = new TextDecoder('utf-8', { fatal: true })
|
|
72
|
-
|
|
73
71
|
/**
|
|
74
72
|
* @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
|
|
75
73
|
* @param {import('./websocket').WebSocket} ws
|
|
@@ -89,7 +87,7 @@ function websocketMessageReceived (ws, type, data) {
|
|
|
89
87
|
// -> type indicates that the data is Text
|
|
90
88
|
// a new DOMString containing data
|
|
91
89
|
try {
|
|
92
|
-
dataForEvent =
|
|
90
|
+
dataForEvent = new TextDecoder('utf-8', { fatal: true }).decode(data)
|
|
93
91
|
} catch {
|
|
94
92
|
failWebsocketConnection(ws, 'Received invalid UTF-8 in text frame.')
|
|
95
93
|
return
|
package/package.json
CHANGED
package/types/dispatcher.d.ts
CHANGED
|
@@ -19,8 +19,8 @@ declare class Dispatcher extends EventEmitter {
|
|
|
19
19
|
connect(options: Dispatcher.ConnectOptions): Promise<Dispatcher.ConnectData>;
|
|
20
20
|
connect(options: Dispatcher.ConnectOptions, callback: (err: Error | null, data: Dispatcher.ConnectData) => void): void;
|
|
21
21
|
/** Compose a chain of dispatchers */
|
|
22
|
-
compose(dispatchers: Dispatcher[
|
|
23
|
-
compose(...dispatchers: Dispatcher[
|
|
22
|
+
compose(dispatchers: Dispatcher.DispatcherInterceptor[]): Dispatcher.ComposedDispatcher;
|
|
23
|
+
compose(...dispatchers: Dispatcher.DispatcherInterceptor[]): Dispatcher.ComposedDispatcher;
|
|
24
24
|
/** Performs an HTTP request. */
|
|
25
25
|
request(options: Dispatcher.RequestOptions): Promise<Dispatcher.ResponseData>;
|
|
26
26
|
request(options: Dispatcher.RequestOptions, callback: (err: Error | null, data: Dispatcher.ResponseData) => void): void;
|