undici 5.13.0 → 5.14.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/api/Connector.md +3 -1
- package/lib/core/connect.js +62 -19
- package/lib/fetch/body.js +38 -7
- package/lib/fetch/file.js +9 -6
- package/lib/fetch/formdata.js +5 -2
- package/lib/fetch/headers.js +22 -10
- package/lib/fetch/index.js +27 -17
- package/lib/fetch/request.js +1 -1
- package/lib/fetch/response.js +1 -1
- package/lib/fetch/symbols.js +2 -1
- package/lib/fetch/util.js +5 -4
- package/lib/fileapi/filereader.js +30 -0
- package/lib/fileapi/util.js +3 -9
- package/package.json +3 -3
- package/types/connector.d.ts +3 -1
package/docs/api/Connector.md
CHANGED
|
@@ -24,8 +24,10 @@ Once you call `buildConnector`, it will return a connector function, which takes
|
|
|
24
24
|
* **hostname** `string` (required)
|
|
25
25
|
* **host** `string` (optional)
|
|
26
26
|
* **protocol** `string` (required)
|
|
27
|
-
* **port** `
|
|
27
|
+
* **port** `string` (required)
|
|
28
28
|
* **servername** `string` (optional)
|
|
29
|
+
* **localAddress** `string | null` (optional) Local address the socket should connect from.
|
|
30
|
+
* **httpSocket** `Socket` (optional) Establish secure connection on a given socket rather than creating a new socket. It can only be sent on TLS update.
|
|
29
31
|
|
|
30
32
|
### Basic example
|
|
31
33
|
|
package/lib/core/connect.js
CHANGED
|
@@ -4,6 +4,7 @@ const net = require('net')
|
|
|
4
4
|
const assert = require('assert')
|
|
5
5
|
const util = require('./util')
|
|
6
6
|
const { InvalidArgumentError, ConnectTimeoutError } = require('./errors')
|
|
7
|
+
|
|
7
8
|
let tls // include tls conditionally since it is not always available
|
|
8
9
|
|
|
9
10
|
// TODO: session re-use does not wait for the first
|
|
@@ -11,15 +12,73 @@ let tls // include tls conditionally since it is not always available
|
|
|
11
12
|
// resolve the same servername multiple times even when
|
|
12
13
|
// re-use is enabled.
|
|
13
14
|
|
|
15
|
+
let SessionCache
|
|
16
|
+
if (global.FinalizationRegistry) {
|
|
17
|
+
SessionCache = class WeakSessionCache {
|
|
18
|
+
constructor (maxCachedSessions) {
|
|
19
|
+
this._maxCachedSessions = maxCachedSessions
|
|
20
|
+
this._sessionCache = new Map()
|
|
21
|
+
this._sessionRegistry = new global.FinalizationRegistry((key) => {
|
|
22
|
+
if (this._sessionCache.size < this._maxCachedSessions) {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ref = this._sessionCache.get(key)
|
|
27
|
+
if (ref !== undefined && ref.deref() === undefined) {
|
|
28
|
+
this._sessionCache.delete(key)
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get (sessionKey) {
|
|
34
|
+
const ref = this._sessionCache.get(sessionKey)
|
|
35
|
+
return ref ? ref.deref() : null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
set (sessionKey, session) {
|
|
39
|
+
if (this._maxCachedSessions === 0) {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this._sessionCache.set(sessionKey, new WeakRef(session))
|
|
44
|
+
this._sessionRegistry.register(session, sessionKey)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
SessionCache = class SimpleSessionCache {
|
|
49
|
+
constructor (maxCachedSessions) {
|
|
50
|
+
this._maxCachedSessions = maxCachedSessions
|
|
51
|
+
this._sessionCache = new Map()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get (sessionKey) {
|
|
55
|
+
return this._sessionCache.get(sessionKey)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
set (sessionKey, session) {
|
|
59
|
+
if (this._maxCachedSessions === 0) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (this._sessionCache.size >= this._maxCachedSessions) {
|
|
64
|
+
// remove the oldest session
|
|
65
|
+
const { value: oldestKey } = this._sessionCache.keys().next()
|
|
66
|
+
this._sessionCache.delete(oldestKey)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this._sessionCache.set(sessionKey, session)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
14
74
|
function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
|
|
15
75
|
if (maxCachedSessions != null && (!Number.isInteger(maxCachedSessions) || maxCachedSessions < 0)) {
|
|
16
76
|
throw new InvalidArgumentError('maxCachedSessions must be a positive integer or zero')
|
|
17
77
|
}
|
|
18
78
|
|
|
19
79
|
const options = { path: socketPath, ...opts }
|
|
20
|
-
const sessionCache = new
|
|
80
|
+
const sessionCache = new SessionCache(maxCachedSessions == null ? 100 : maxCachedSessions)
|
|
21
81
|
timeout = timeout == null ? 10e3 : timeout
|
|
22
|
-
maxCachedSessions = maxCachedSessions == null ? 100 : maxCachedSessions
|
|
23
82
|
|
|
24
83
|
return function connect ({ hostname, host, protocol, port, servername, localAddress, httpSocket }, callback) {
|
|
25
84
|
let socket
|
|
@@ -47,25 +106,9 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
|
|
|
47
106
|
|
|
48
107
|
socket
|
|
49
108
|
.on('session', function (session) {
|
|
50
|
-
//
|
|
51
|
-
if (maxCachedSessions === 0) {
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (sessionCache.size >= maxCachedSessions) {
|
|
56
|
-
// remove the oldest session
|
|
57
|
-
const { value: oldestKey } = sessionCache.keys().next()
|
|
58
|
-
sessionCache.delete(oldestKey)
|
|
59
|
-
}
|
|
60
|
-
|
|
109
|
+
// TODO (fix): Can a session become invalid once established? Don't think so?
|
|
61
110
|
sessionCache.set(sessionKey, session)
|
|
62
111
|
})
|
|
63
|
-
.on('error', function (err) {
|
|
64
|
-
if (sessionKey && err.code !== 'UND_ERR_INFO') {
|
|
65
|
-
// TODO (fix): Only delete for session related errors.
|
|
66
|
-
sessionCache.delete(sessionKey)
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
112
|
} else {
|
|
70
113
|
assert(!httpSocket, 'httpSocket can only be sent on TLS update')
|
|
71
114
|
socket = net.connect({
|
package/lib/fetch/body.js
CHANGED
|
@@ -7,17 +7,19 @@ const { FormData } = require('./formdata')
|
|
|
7
7
|
const { kState } = require('./symbols')
|
|
8
8
|
const { webidl } = require('./webidl')
|
|
9
9
|
const { DOMException, structuredClone } = require('./constants')
|
|
10
|
-
const { Blob } = require('buffer')
|
|
10
|
+
const { Blob, File: NativeFile } = require('buffer')
|
|
11
11
|
const { kBodyUsed } = require('../core/symbols')
|
|
12
12
|
const assert = require('assert')
|
|
13
13
|
const { isErrored } = require('../core/util')
|
|
14
14
|
const { isUint8Array, isArrayBuffer } = require('util/types')
|
|
15
|
-
const { File } = require('./file')
|
|
15
|
+
const { File: UndiciFile } = require('./file')
|
|
16
16
|
const { StringDecoder } = require('string_decoder')
|
|
17
17
|
const { parseMIMEType, serializeAMimeType } = require('./dataURL')
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
let ReadableStream = globalThis.ReadableStream
|
|
20
|
+
|
|
21
|
+
/** @type {globalThis['File']} */
|
|
22
|
+
const File = NativeFile ?? UndiciFile
|
|
21
23
|
|
|
22
24
|
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
|
|
23
25
|
function extractBody (object, keepalive = false) {
|
|
@@ -142,7 +144,33 @@ function extractBody (object, keepalive = false) {
|
|
|
142
144
|
source = object
|
|
143
145
|
|
|
144
146
|
// Set length to unclear, see html/6424 for improving this.
|
|
145
|
-
|
|
147
|
+
length = (() => {
|
|
148
|
+
const prefixLength = prefix.length
|
|
149
|
+
const boundaryLength = boundary.length
|
|
150
|
+
let bodyLength = 0
|
|
151
|
+
|
|
152
|
+
for (const [name, value] of object) {
|
|
153
|
+
if (typeof value === 'string') {
|
|
154
|
+
bodyLength +=
|
|
155
|
+
prefixLength +
|
|
156
|
+
Buffer.byteLength(`; name="${escape(normalizeLinefeeds(name))}"\r\n\r\n${normalizeLinefeeds(value)}\r\n`)
|
|
157
|
+
} else {
|
|
158
|
+
bodyLength +=
|
|
159
|
+
prefixLength +
|
|
160
|
+
Buffer.byteLength(`; name="${escape(normalizeLinefeeds(name))}"` + (value.name ? `; filename="${escape(value.name)}"` : '')) +
|
|
161
|
+
2 + // \r\n
|
|
162
|
+
`Content-Type: ${
|
|
163
|
+
value.type || 'application/octet-stream'
|
|
164
|
+
}\r\n\r\n`.length
|
|
165
|
+
|
|
166
|
+
// value is a Blob or File, and \r\n
|
|
167
|
+
bodyLength += value.size + 2
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
bodyLength += boundaryLength + 4 // --boundary--
|
|
172
|
+
return bodyLength
|
|
173
|
+
})()
|
|
146
174
|
|
|
147
175
|
// Set type to `multipart/form-data; boundary=`,
|
|
148
176
|
// followed by the multipart/form-data boundary string generated
|
|
@@ -348,7 +376,10 @@ function bodyMixinMethods (instance) {
|
|
|
348
376
|
let busboy
|
|
349
377
|
|
|
350
378
|
try {
|
|
351
|
-
busboy = Busboy({
|
|
379
|
+
busboy = Busboy({
|
|
380
|
+
headers,
|
|
381
|
+
defParamCharset: 'utf8'
|
|
382
|
+
})
|
|
352
383
|
} catch (err) {
|
|
353
384
|
// Error due to headers:
|
|
354
385
|
throw Object.assign(new TypeError(), { cause: err })
|
|
@@ -361,7 +392,7 @@ function bodyMixinMethods (instance) {
|
|
|
361
392
|
const { filename, encoding, mimeType } = info
|
|
362
393
|
const chunks = []
|
|
363
394
|
|
|
364
|
-
if (encoding.toLowerCase() === 'base64') {
|
|
395
|
+
if (encoding === 'base64' || encoding.toLowerCase() === 'base64') {
|
|
365
396
|
let base64chunk = ''
|
|
366
397
|
|
|
367
398
|
value.on('data', (chunk) => {
|
package/lib/fetch/file.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { Blob } = require('buffer')
|
|
3
|
+
const { Blob, File: NativeFile } = require('buffer')
|
|
4
4
|
const { types } = require('util')
|
|
5
5
|
const { kState } = require('./symbols')
|
|
6
6
|
const { isBlobLike } = require('./util')
|
|
@@ -329,11 +329,14 @@ function convertLineEndingsNative (s) {
|
|
|
329
329
|
// rollup) will warn about circular dependencies. See:
|
|
330
330
|
// https://github.com/nodejs/undici/issues/1629
|
|
331
331
|
function isFileLike (object) {
|
|
332
|
-
return
|
|
333
|
-
object
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
332
|
+
return (
|
|
333
|
+
(NativeFile && object instanceof NativeFile) ||
|
|
334
|
+
object instanceof File || (
|
|
335
|
+
object &&
|
|
336
|
+
(typeof object.stream === 'function' ||
|
|
337
|
+
typeof object.arrayBuffer === 'function') &&
|
|
338
|
+
object[Symbol.toStringTag] === 'File'
|
|
339
|
+
)
|
|
337
340
|
)
|
|
338
341
|
}
|
|
339
342
|
|
package/lib/fetch/formdata.js
CHANGED
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const { isBlobLike, toUSVString, makeIterator } = require('./util')
|
|
4
4
|
const { kState } = require('./symbols')
|
|
5
|
-
const { File, FileLike, isFileLike } = require('./file')
|
|
5
|
+
const { File: UndiciFile, FileLike, isFileLike } = require('./file')
|
|
6
6
|
const { webidl } = require('./webidl')
|
|
7
|
-
const { Blob } = require('buffer')
|
|
7
|
+
const { Blob, File: NativeFile } = require('buffer')
|
|
8
|
+
|
|
9
|
+
/** @type {globalThis['File']} */
|
|
10
|
+
const File = NativeFile ?? UndiciFile
|
|
8
11
|
|
|
9
12
|
// https://xhr.spec.whatwg.org/#formdata
|
|
10
13
|
class FormData {
|
package/lib/fetch/headers.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
'use strict'
|
|
4
4
|
|
|
5
5
|
const { kHeadersList } = require('../core/symbols')
|
|
6
|
-
const { kGuard } = require('./symbols')
|
|
6
|
+
const { kGuard, kHeadersCaseInsensitive } = require('./symbols')
|
|
7
7
|
const { kEnumerableProperty } = require('../core/util')
|
|
8
8
|
const {
|
|
9
9
|
makeIterator,
|
|
@@ -96,27 +96,27 @@ class HeadersList {
|
|
|
96
96
|
|
|
97
97
|
// 1. If list contains name, then set name to the first such
|
|
98
98
|
// header’s name.
|
|
99
|
-
|
|
100
|
-
const exists = this[kHeadersMap].get(
|
|
99
|
+
const lowercaseName = name.toLowerCase()
|
|
100
|
+
const exists = this[kHeadersMap].get(lowercaseName)
|
|
101
101
|
|
|
102
102
|
// 2. Append (name, value) to list.
|
|
103
103
|
if (exists) {
|
|
104
|
-
this[kHeadersMap].set(name, `${exists}, ${value}`)
|
|
104
|
+
this[kHeadersMap].set(lowercaseName, { name: exists.name, value: `${exists.value}, ${value}` })
|
|
105
105
|
} else {
|
|
106
|
-
this[kHeadersMap].set(name,
|
|
106
|
+
this[kHeadersMap].set(lowercaseName, { name, value })
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
// https://fetch.spec.whatwg.org/#concept-header-list-set
|
|
111
111
|
set (name, value) {
|
|
112
112
|
this[kHeadersSortedMap] = null
|
|
113
|
-
|
|
113
|
+
const lowercaseName = name.toLowerCase()
|
|
114
114
|
|
|
115
115
|
// 1. If list contains name, then set the value of
|
|
116
116
|
// the first such header to value and remove the
|
|
117
117
|
// others.
|
|
118
118
|
// 2. Otherwise, append header (name, value) to list.
|
|
119
|
-
return this[kHeadersMap].set(name, value)
|
|
119
|
+
return this[kHeadersMap].set(lowercaseName, { name, value })
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
// https://fetch.spec.whatwg.org/#concept-header-list-delete
|
|
@@ -137,14 +137,26 @@ class HeadersList {
|
|
|
137
137
|
// 2. Return the values of all headers in list whose name
|
|
138
138
|
// is a byte-case-insensitive match for name,
|
|
139
139
|
// separated from each other by 0x2C 0x20, in order.
|
|
140
|
-
return this[kHeadersMap].get(name.toLowerCase()) ?? null
|
|
140
|
+
return this[kHeadersMap].get(name.toLowerCase())?.value ?? null
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
* [Symbol.iterator] () {
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
// use the lowercased name
|
|
145
|
+
for (const [name, { value }] of this[kHeadersMap]) {
|
|
146
|
+
yield [name, value]
|
|
146
147
|
}
|
|
147
148
|
}
|
|
149
|
+
|
|
150
|
+
get [kHeadersCaseInsensitive] () {
|
|
151
|
+
/** @type {string[]} */
|
|
152
|
+
const flatList = []
|
|
153
|
+
|
|
154
|
+
for (const { name, value } of this[kHeadersMap].values()) {
|
|
155
|
+
flatList.push(name, value)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return flatList
|
|
159
|
+
}
|
|
148
160
|
}
|
|
149
161
|
|
|
150
162
|
// https://fetch.spec.whatwg.org/#headers-class
|
package/lib/fetch/index.js
CHANGED
|
@@ -39,7 +39,7 @@ const {
|
|
|
39
39
|
readableStreamClose,
|
|
40
40
|
isomorphicEncode
|
|
41
41
|
} = require('./util')
|
|
42
|
-
const { kState, kHeaders, kGuard, kRealm } = require('./symbols')
|
|
42
|
+
const { kState, kHeaders, kGuard, kRealm, kHeadersCaseInsensitive } = require('./symbols')
|
|
43
43
|
const assert = require('assert')
|
|
44
44
|
const { safelyExtractBody } = require('./body')
|
|
45
45
|
const {
|
|
@@ -61,8 +61,7 @@ const { webidl } = require('./webidl')
|
|
|
61
61
|
|
|
62
62
|
/** @type {import('buffer').resolveObjectURL} */
|
|
63
63
|
let resolveObjectURL
|
|
64
|
-
|
|
65
|
-
let ReadableStream
|
|
64
|
+
let ReadableStream = globalThis.ReadableStream
|
|
66
65
|
|
|
67
66
|
const nodeVersion = process.versions.node.split('.')
|
|
68
67
|
const nodeMajor = Number(nodeVersion[0])
|
|
@@ -781,8 +780,11 @@ async function mainFetch (fetchParams, recursive = false) {
|
|
|
781
780
|
// https://fetch.spec.whatwg.org/#concept-scheme-fetch
|
|
782
781
|
// given a fetch params fetchParams
|
|
783
782
|
async function schemeFetch (fetchParams) {
|
|
783
|
+
// Note: since the connection is destroyed on redirect, which sets fetchParams to a
|
|
784
|
+
// cancelled state, we do not want this condition to trigger *unless* there have been
|
|
785
|
+
// no redirects. See https://github.com/nodejs/undici/issues/1776
|
|
784
786
|
// 1. If fetchParams is canceled, then return the appropriate network error for fetchParams.
|
|
785
|
-
if (isCancelled(fetchParams)) {
|
|
787
|
+
if (isCancelled(fetchParams) && fetchParams.request.redirectCount === 0) {
|
|
786
788
|
return makeAppropriateNetworkError(fetchParams)
|
|
787
789
|
}
|
|
788
790
|
|
|
@@ -840,8 +842,8 @@ async function schemeFetch (fetchParams) {
|
|
|
840
842
|
const response = makeResponse({
|
|
841
843
|
statusText: 'OK',
|
|
842
844
|
headersList: [
|
|
843
|
-
['content-length', length],
|
|
844
|
-
['content-type', type]
|
|
845
|
+
['content-length', { name: 'Content-Length', value: length }],
|
|
846
|
+
['content-type', { name: 'Content-Type', value: type }]
|
|
845
847
|
]
|
|
846
848
|
})
|
|
847
849
|
|
|
@@ -870,7 +872,7 @@ async function schemeFetch (fetchParams) {
|
|
|
870
872
|
return makeResponse({
|
|
871
873
|
statusText: 'OK',
|
|
872
874
|
headersList: [
|
|
873
|
-
['content-type', mimeType]
|
|
875
|
+
['content-type', { name: 'Content-Type', value: mimeType }]
|
|
874
876
|
],
|
|
875
877
|
body: safelyExtractBody(dataURLStruct.body)[0]
|
|
876
878
|
})
|
|
@@ -1135,12 +1137,12 @@ async function httpRedirectFetch (fetchParams, response) {
|
|
|
1135
1137
|
return makeNetworkError('URL scheme must be a HTTP(S) scheme')
|
|
1136
1138
|
}
|
|
1137
1139
|
|
|
1138
|
-
// 7. If request’s redirect count is
|
|
1140
|
+
// 7. If request’s redirect count is 20, then return a network error.
|
|
1139
1141
|
if (request.redirectCount === 20) {
|
|
1140
1142
|
return makeNetworkError('redirect count exceeded')
|
|
1141
1143
|
}
|
|
1142
1144
|
|
|
1143
|
-
// 8. Increase request’s redirect count by
|
|
1145
|
+
// 8. Increase request’s redirect count by 1.
|
|
1144
1146
|
request.redirectCount += 1
|
|
1145
1147
|
|
|
1146
1148
|
// 9. If request’s mode is "cors", locationURL includes credentials, and
|
|
@@ -1195,36 +1197,44 @@ async function httpRedirectFetch (fetchParams, response) {
|
|
|
1195
1197
|
}
|
|
1196
1198
|
}
|
|
1197
1199
|
|
|
1198
|
-
// 13. If request’s
|
|
1200
|
+
// 13. If request’s current URL’s origin is not same origin with locationURL’s
|
|
1201
|
+
// origin, then for each headerName of CORS non-wildcard request-header name,
|
|
1202
|
+
// delete headerName from request’s header list.
|
|
1203
|
+
if (!sameOrigin(requestCurrentURL(request), locationURL)) {
|
|
1204
|
+
// https://fetch.spec.whatwg.org/#cors-non-wildcard-request-header-name
|
|
1205
|
+
request.headersList.delete('authorization')
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// 14. If request’s body is non-null, then set request’s body to the first return
|
|
1199
1209
|
// value of safely extracting request’s body’s source.
|
|
1200
1210
|
if (request.body != null) {
|
|
1201
1211
|
assert(request.body.source)
|
|
1202
1212
|
request.body = safelyExtractBody(request.body.source)[0]
|
|
1203
1213
|
}
|
|
1204
1214
|
|
|
1205
|
-
//
|
|
1215
|
+
// 15. Let timingInfo be fetchParams’s timing info.
|
|
1206
1216
|
const timingInfo = fetchParams.timingInfo
|
|
1207
1217
|
|
|
1208
|
-
//
|
|
1218
|
+
// 16. Set timingInfo’s redirect end time and post-redirect start time to the
|
|
1209
1219
|
// coarsened shared current time given fetchParams’s cross-origin isolated
|
|
1210
1220
|
// capability.
|
|
1211
1221
|
timingInfo.redirectEndTime = timingInfo.postRedirectStartTime =
|
|
1212
1222
|
coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability)
|
|
1213
1223
|
|
|
1214
|
-
//
|
|
1224
|
+
// 17. If timingInfo’s redirect start time is 0, then set timingInfo’s
|
|
1215
1225
|
// redirect start time to timingInfo’s start time.
|
|
1216
1226
|
if (timingInfo.redirectStartTime === 0) {
|
|
1217
1227
|
timingInfo.redirectStartTime = timingInfo.startTime
|
|
1218
1228
|
}
|
|
1219
1229
|
|
|
1220
|
-
//
|
|
1230
|
+
// 18. Append locationURL to request’s URL list.
|
|
1221
1231
|
request.urlList.push(locationURL)
|
|
1222
1232
|
|
|
1223
|
-
//
|
|
1233
|
+
// 19. Invoke set request’s referrer policy on redirect on request and
|
|
1224
1234
|
// actualResponse.
|
|
1225
1235
|
setRequestReferrerPolicyOnRedirect(request, actualResponse)
|
|
1226
1236
|
|
|
1227
|
-
//
|
|
1237
|
+
// 20. Return the result of running main fetch given fetchParams and true.
|
|
1228
1238
|
return mainFetch(fetchParams, true)
|
|
1229
1239
|
}
|
|
1230
1240
|
|
|
@@ -1930,7 +1940,7 @@ async function httpNetworkFetch (
|
|
|
1930
1940
|
origin: url.origin,
|
|
1931
1941
|
method: request.method,
|
|
1932
1942
|
body: fetchParams.controller.dispatcher.isMockActive ? request.body && request.body.source : body,
|
|
1933
|
-
headers:
|
|
1943
|
+
headers: request.headersList[kHeadersCaseInsensitive],
|
|
1934
1944
|
maxRedirections: 0,
|
|
1935
1945
|
bodyTimeout: 300_000,
|
|
1936
1946
|
headersTimeout: 300_000
|
package/lib/fetch/request.js
CHANGED
package/lib/fetch/response.js
CHANGED
|
@@ -436,7 +436,7 @@ function makeAppropriateNetworkError (fetchParams) {
|
|
|
436
436
|
// otherwise return a network error.
|
|
437
437
|
return isAborted(fetchParams)
|
|
438
438
|
? makeNetworkError(new DOMException('The operation was aborted.', 'AbortError'))
|
|
439
|
-
: makeNetworkError(
|
|
439
|
+
: makeNetworkError('Request was cancelled.')
|
|
440
440
|
}
|
|
441
441
|
|
|
442
442
|
// https://whatpr.org/fetch/1392.html#initialize-a-response
|
package/lib/fetch/symbols.js
CHANGED
package/lib/fetch/util.js
CHANGED
|
@@ -863,6 +863,8 @@ function isReadableStreamLike (stream) {
|
|
|
863
863
|
)
|
|
864
864
|
}
|
|
865
865
|
|
|
866
|
+
const MAXIMUM_ARGUMENT_LENGTH = 65535
|
|
867
|
+
|
|
866
868
|
/**
|
|
867
869
|
* @see https://infra.spec.whatwg.org/#isomorphic-decode
|
|
868
870
|
* @param {number[]|Uint8Array} input
|
|
@@ -871,13 +873,12 @@ function isomorphicDecode (input) {
|
|
|
871
873
|
// 1. To isomorphic decode a byte sequence input, return a string whose code point
|
|
872
874
|
// length is equal to input’s length and whose code points have the same values
|
|
873
875
|
// as the values of input’s bytes, in the same order.
|
|
874
|
-
let output = ''
|
|
875
876
|
|
|
876
|
-
|
|
877
|
-
|
|
877
|
+
if (input.length < MAXIMUM_ARGUMENT_LENGTH) {
|
|
878
|
+
return String.fromCharCode(...input)
|
|
878
879
|
}
|
|
879
880
|
|
|
880
|
-
return
|
|
881
|
+
return input.reduce((previous, current) => previous + String.fromCharCode(current), '')
|
|
881
882
|
}
|
|
882
883
|
|
|
883
884
|
/**
|
|
@@ -182,8 +182,13 @@ class FileReader extends EventTarget {
|
|
|
182
182
|
set onloadend (fn) {
|
|
183
183
|
webidl.brandCheck(this, FileReader)
|
|
184
184
|
|
|
185
|
+
if (this[kEvents].loadend) {
|
|
186
|
+
this.removeEventListener('loadend', this[kEvents].loadend)
|
|
187
|
+
}
|
|
188
|
+
|
|
185
189
|
if (typeof fn === 'function') {
|
|
186
190
|
this[kEvents].loadend = fn
|
|
191
|
+
this.addEventListener('loadend', fn)
|
|
187
192
|
} else {
|
|
188
193
|
this[kEvents].loadend = null
|
|
189
194
|
}
|
|
@@ -198,8 +203,13 @@ class FileReader extends EventTarget {
|
|
|
198
203
|
set onerror (fn) {
|
|
199
204
|
webidl.brandCheck(this, FileReader)
|
|
200
205
|
|
|
206
|
+
if (this[kEvents].error) {
|
|
207
|
+
this.removeEventListener('error', this[kEvents].error)
|
|
208
|
+
}
|
|
209
|
+
|
|
201
210
|
if (typeof fn === 'function') {
|
|
202
211
|
this[kEvents].error = fn
|
|
212
|
+
this.addEventListener('error', fn)
|
|
203
213
|
} else {
|
|
204
214
|
this[kEvents].error = null
|
|
205
215
|
}
|
|
@@ -214,8 +224,13 @@ class FileReader extends EventTarget {
|
|
|
214
224
|
set onloadstart (fn) {
|
|
215
225
|
webidl.brandCheck(this, FileReader)
|
|
216
226
|
|
|
227
|
+
if (this[kEvents].loadstart) {
|
|
228
|
+
this.removeEventListener('loadstart', this[kEvents].loadstart)
|
|
229
|
+
}
|
|
230
|
+
|
|
217
231
|
if (typeof fn === 'function') {
|
|
218
232
|
this[kEvents].loadstart = fn
|
|
233
|
+
this.addEventListener('loadstart', fn)
|
|
219
234
|
} else {
|
|
220
235
|
this[kEvents].loadstart = null
|
|
221
236
|
}
|
|
@@ -230,8 +245,13 @@ class FileReader extends EventTarget {
|
|
|
230
245
|
set onprogress (fn) {
|
|
231
246
|
webidl.brandCheck(this, FileReader)
|
|
232
247
|
|
|
248
|
+
if (this[kEvents].progress) {
|
|
249
|
+
this.removeEventListener('progress', this[kEvents].progress)
|
|
250
|
+
}
|
|
251
|
+
|
|
233
252
|
if (typeof fn === 'function') {
|
|
234
253
|
this[kEvents].progress = fn
|
|
254
|
+
this.addEventListener('progress', fn)
|
|
235
255
|
} else {
|
|
236
256
|
this[kEvents].progress = null
|
|
237
257
|
}
|
|
@@ -246,8 +266,13 @@ class FileReader extends EventTarget {
|
|
|
246
266
|
set onload (fn) {
|
|
247
267
|
webidl.brandCheck(this, FileReader)
|
|
248
268
|
|
|
269
|
+
if (this[kEvents].load) {
|
|
270
|
+
this.removeEventListener('load', this[kEvents].load)
|
|
271
|
+
}
|
|
272
|
+
|
|
249
273
|
if (typeof fn === 'function') {
|
|
250
274
|
this[kEvents].load = fn
|
|
275
|
+
this.addEventListener('load', fn)
|
|
251
276
|
} else {
|
|
252
277
|
this[kEvents].load = null
|
|
253
278
|
}
|
|
@@ -262,8 +287,13 @@ class FileReader extends EventTarget {
|
|
|
262
287
|
set onabort (fn) {
|
|
263
288
|
webidl.brandCheck(this, FileReader)
|
|
264
289
|
|
|
290
|
+
if (this[kEvents].abort) {
|
|
291
|
+
this.removeEventListener('abort', this[kEvents].abort)
|
|
292
|
+
}
|
|
293
|
+
|
|
265
294
|
if (typeof fn === 'function') {
|
|
266
295
|
this[kEvents].abort = fn
|
|
296
|
+
this.addEventListener('abort', fn)
|
|
267
297
|
} else {
|
|
268
298
|
this[kEvents].abort = null
|
|
269
299
|
}
|
package/lib/fileapi/util.js
CHANGED
|
@@ -191,25 +191,19 @@ function readOperation (fr, blob, type, encodingName) {
|
|
|
191
191
|
|
|
192
192
|
/**
|
|
193
193
|
* @see https://w3c.github.io/FileAPI/#fire-a-progress-event
|
|
194
|
+
* @see https://dom.spec.whatwg.org/#concept-event-fire
|
|
194
195
|
* @param {string} e The name of the event
|
|
195
196
|
* @param {import('./filereader').FileReader} reader
|
|
196
197
|
*/
|
|
197
198
|
function fireAProgressEvent (e, reader) {
|
|
199
|
+
// The progress event e does not bubble. e.bubbles must be false
|
|
200
|
+
// The progress event e is NOT cancelable. e.cancelable must be false
|
|
198
201
|
const event = new ProgressEvent(e, {
|
|
199
202
|
bubbles: false,
|
|
200
203
|
cancelable: false
|
|
201
204
|
})
|
|
202
205
|
|
|
203
206
|
reader.dispatchEvent(event)
|
|
204
|
-
try {
|
|
205
|
-
// eslint-disable-next-line no-useless-call
|
|
206
|
-
reader[`on${e}`]?.call(reader, event)
|
|
207
|
-
} catch (err) {
|
|
208
|
-
// Prevent the error from being swallowed
|
|
209
|
-
queueMicrotask(() => {
|
|
210
|
-
throw err
|
|
211
|
-
})
|
|
212
|
-
}
|
|
213
207
|
}
|
|
214
208
|
|
|
215
209
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "undici",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.14.0",
|
|
4
4
|
"description": "An HTTP/1.1 client, written from scratch for Node.js",
|
|
5
5
|
"homepage": "https://undici.nodejs.org",
|
|
6
6
|
"bugs": {
|
|
@@ -89,12 +89,12 @@
|
|
|
89
89
|
"proxy": "^1.0.2",
|
|
90
90
|
"proxyquire": "^2.1.3",
|
|
91
91
|
"semver": "^7.3.5",
|
|
92
|
-
"sinon": "^
|
|
92
|
+
"sinon": "^15.0.0",
|
|
93
93
|
"snazzy": "^9.0.0",
|
|
94
94
|
"standard": "^17.0.0",
|
|
95
95
|
"table": "^6.8.0",
|
|
96
96
|
"tap": "^16.1.0",
|
|
97
|
-
"tsd": "^0.
|
|
97
|
+
"tsd": "^0.25.0",
|
|
98
98
|
"typescript": "^4.8.4",
|
|
99
99
|
"wait-on": "^6.0.0"
|
|
100
100
|
},
|
package/types/connector.d.ts
CHANGED
|
@@ -16,8 +16,10 @@ declare namespace buildConnector {
|
|
|
16
16
|
hostname: string
|
|
17
17
|
host?: string
|
|
18
18
|
protocol: string
|
|
19
|
-
port:
|
|
19
|
+
port: string
|
|
20
20
|
servername?: string
|
|
21
|
+
localAddress?: string | null
|
|
22
|
+
httpSocket?: Socket
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export type Callback = (...args: CallbackArgs) => void
|