undici 6.20.0 → 7.0.0-alpha.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.
Files changed (137) hide show
  1. package/README.md +6 -10
  2. package/docs/docs/api/Agent.md +0 -3
  3. package/docs/docs/api/Client.md +1 -3
  4. package/docs/docs/api/Debug.md +1 -1
  5. package/docs/docs/api/Dispatcher.md +60 -8
  6. package/docs/docs/api/EnvHttpProxyAgent.md +0 -1
  7. package/docs/docs/api/Fetch.md +1 -0
  8. package/docs/docs/api/MockAgent.md +2 -0
  9. package/docs/docs/api/MockPool.md +2 -1
  10. package/docs/docs/api/Pool.md +0 -1
  11. package/docs/docs/api/RetryAgent.md +1 -1
  12. package/docs/docs/api/RetryHandler.md +1 -1
  13. package/docs/docs/api/WebSocket.md +45 -3
  14. package/index.js +6 -6
  15. package/lib/api/abort-signal.js +2 -0
  16. package/lib/api/api-connect.js +3 -1
  17. package/lib/api/api-pipeline.js +7 -6
  18. package/lib/api/api-request.js +32 -47
  19. package/lib/api/api-stream.js +39 -50
  20. package/lib/api/api-upgrade.js +5 -3
  21. package/lib/api/readable.js +261 -64
  22. package/lib/api/util.js +2 -0
  23. package/lib/core/constants.js +11 -9
  24. package/lib/core/diagnostics.js +122 -128
  25. package/lib/core/errors.js +4 -4
  26. package/lib/core/request.js +11 -9
  27. package/lib/core/symbols.js +2 -1
  28. package/lib/core/tree.js +9 -1
  29. package/lib/core/util.js +219 -48
  30. package/lib/dispatcher/agent.js +3 -17
  31. package/lib/dispatcher/balanced-pool.js +5 -8
  32. package/lib/dispatcher/client-h1.js +278 -54
  33. package/lib/dispatcher/client-h2.js +1 -1
  34. package/lib/dispatcher/client.js +23 -34
  35. package/lib/dispatcher/dispatcher-base.js +2 -34
  36. package/lib/dispatcher/dispatcher.js +3 -24
  37. package/lib/dispatcher/fixed-queue.js +91 -49
  38. package/lib/dispatcher/pool-stats.js +2 -0
  39. package/lib/dispatcher/pool.js +3 -6
  40. package/lib/dispatcher/proxy-agent.js +6 -7
  41. package/lib/handler/decorator-handler.js +24 -0
  42. package/lib/handler/redirect-handler.js +11 -2
  43. package/lib/handler/retry-handler.js +12 -3
  44. package/lib/interceptor/dns.js +346 -0
  45. package/lib/interceptor/dump.js +2 -2
  46. package/lib/interceptor/redirect.js +11 -14
  47. package/lib/interceptor/response-error.js +4 -1
  48. package/lib/llhttp/constants.d.ts +97 -0
  49. package/lib/llhttp/constants.js +412 -192
  50. package/lib/llhttp/constants.js.map +1 -0
  51. package/lib/llhttp/llhttp-wasm.js +11 -1
  52. package/lib/llhttp/llhttp_simd-wasm.js +11 -1
  53. package/lib/llhttp/utils.d.ts +2 -0
  54. package/lib/llhttp/utils.js +9 -9
  55. package/lib/llhttp/utils.js.map +1 -0
  56. package/lib/mock/mock-agent.js +5 -8
  57. package/lib/mock/mock-client.js +9 -4
  58. package/lib/mock/mock-errors.js +3 -1
  59. package/lib/mock/mock-interceptor.js +8 -6
  60. package/lib/mock/mock-pool.js +9 -4
  61. package/lib/mock/mock-symbols.js +3 -1
  62. package/lib/mock/mock-utils.js +29 -5
  63. package/lib/web/cache/cache.js +24 -21
  64. package/lib/web/cache/cachestorage.js +1 -1
  65. package/lib/web/cookies/index.js +17 -13
  66. package/lib/web/cookies/parse.js +2 -2
  67. package/lib/web/eventsource/eventsource-stream.js +9 -8
  68. package/lib/web/eventsource/eventsource.js +10 -6
  69. package/lib/web/fetch/body.js +42 -36
  70. package/lib/web/fetch/constants.js +35 -26
  71. package/lib/web/fetch/data-url.js +1 -1
  72. package/lib/web/fetch/formdata-parser.js +2 -2
  73. package/lib/web/fetch/formdata.js +65 -54
  74. package/lib/web/fetch/headers.js +117 -85
  75. package/lib/web/fetch/index.js +55 -62
  76. package/lib/web/fetch/request.js +135 -77
  77. package/lib/web/fetch/response.js +86 -56
  78. package/lib/web/fetch/util.js +90 -64
  79. package/lib/web/fetch/webidl.js +99 -64
  80. package/lib/web/websocket/connection.js +76 -147
  81. package/lib/web/websocket/constants.js +3 -4
  82. package/lib/web/websocket/events.js +4 -2
  83. package/lib/web/websocket/frame.js +45 -3
  84. package/lib/web/websocket/receiver.js +29 -33
  85. package/lib/web/websocket/sender.js +18 -13
  86. package/lib/web/websocket/stream/websocketerror.js +83 -0
  87. package/lib/web/websocket/stream/websocketstream.js +485 -0
  88. package/lib/web/websocket/util.js +128 -77
  89. package/lib/web/websocket/websocket.js +234 -135
  90. package/package.json +20 -33
  91. package/scripts/strip-comments.js +3 -1
  92. package/types/agent.d.ts +7 -7
  93. package/types/api.d.ts +24 -24
  94. package/types/balanced-pool.d.ts +11 -11
  95. package/types/client.d.ts +11 -12
  96. package/types/diagnostics-channel.d.ts +10 -10
  97. package/types/dispatcher.d.ts +96 -97
  98. package/types/env-http-proxy-agent.d.ts +2 -2
  99. package/types/errors.d.ts +53 -47
  100. package/types/fetch.d.ts +8 -8
  101. package/types/formdata.d.ts +7 -7
  102. package/types/global-dispatcher.d.ts +4 -4
  103. package/types/global-origin.d.ts +5 -5
  104. package/types/handlers.d.ts +4 -4
  105. package/types/header.d.ts +157 -1
  106. package/types/index.d.ts +42 -46
  107. package/types/interceptors.d.ts +22 -8
  108. package/types/mock-agent.d.ts +21 -18
  109. package/types/mock-client.d.ts +4 -4
  110. package/types/mock-errors.d.ts +3 -3
  111. package/types/mock-interceptor.d.ts +19 -19
  112. package/types/mock-pool.d.ts +4 -4
  113. package/types/patch.d.ts +0 -4
  114. package/types/pool-stats.d.ts +8 -8
  115. package/types/pool.d.ts +12 -12
  116. package/types/proxy-agent.d.ts +4 -4
  117. package/types/readable.d.ts +22 -14
  118. package/types/retry-agent.d.ts +1 -1
  119. package/types/retry-handler.d.ts +8 -8
  120. package/types/util.d.ts +3 -3
  121. package/types/utility.d.ts +7 -0
  122. package/types/webidl.d.ts +44 -6
  123. package/types/websocket.d.ts +34 -1
  124. package/docs/docs/api/DispatchInterceptor.md +0 -60
  125. package/lib/interceptor/redirect-interceptor.js +0 -21
  126. package/lib/mock/pluralizer.js +0 -29
  127. package/lib/web/cache/symbols.js +0 -5
  128. package/lib/web/fetch/file.js +0 -126
  129. package/lib/web/fetch/symbols.js +0 -9
  130. package/lib/web/fileapi/encoding.js +0 -290
  131. package/lib/web/fileapi/filereader.js +0 -344
  132. package/lib/web/fileapi/progressevent.js +0 -78
  133. package/lib/web/fileapi/symbols.js +0 -10
  134. package/lib/web/fileapi/util.js +0 -391
  135. package/lib/web/websocket/symbols.js +0 -12
  136. package/types/file.d.ts +0 -39
  137. package/types/filereader.d.ts +0 -54
@@ -1,51 +1,47 @@
1
1
  'use strict'
2
2
 
3
- const { kReadyState, kController, kResponse, kBinaryType, kWebSocketURL } = require('./symbols')
4
3
  const { states, opcodes } = require('./constants')
5
- const { ErrorEvent, createFastMessageEvent } = require('./events')
6
4
  const { isUtf8 } = require('node:buffer')
7
5
  const { collectASequenceOfCodePointsFast, removeHTTPWhitespace } = require('../fetch/data-url')
8
6
 
9
- /* globals Blob */
10
-
11
7
  /**
12
- * @param {import('./websocket').WebSocket} ws
8
+ * @param {number} readyState
13
9
  * @returns {boolean}
14
10
  */
15
- function isConnecting (ws) {
11
+ function isConnecting (readyState) {
16
12
  // If the WebSocket connection is not yet established, and the connection
17
13
  // is not yet closed, then the WebSocket connection is in the CONNECTING state.
18
- return ws[kReadyState] === states.CONNECTING
14
+ return readyState === states.CONNECTING
19
15
  }
20
16
 
21
17
  /**
22
- * @param {import('./websocket').WebSocket} ws
18
+ * @param {number} readyState
23
19
  * @returns {boolean}
24
20
  */
25
- function isEstablished (ws) {
21
+ function isEstablished (readyState) {
26
22
  // If the server's response is validated as provided for above, it is
27
23
  // said that _The WebSocket Connection is Established_ and that the
28
24
  // WebSocket Connection is in the OPEN state.
29
- return ws[kReadyState] === states.OPEN
25
+ return readyState === states.OPEN
30
26
  }
31
27
 
32
28
  /**
33
- * @param {import('./websocket').WebSocket} ws
29
+ * @param {number} readyState
34
30
  * @returns {boolean}
35
31
  */
36
- function isClosing (ws) {
32
+ function isClosing (readyState) {
37
33
  // Upon either sending or receiving a Close control frame, it is said
38
34
  // that _The WebSocket Closing Handshake is Started_ and that the
39
35
  // WebSocket connection is in the CLOSING state.
40
- return ws[kReadyState] === states.CLOSING
36
+ return readyState === states.CLOSING
41
37
  }
42
38
 
43
39
  /**
44
- * @param {import('./websocket').WebSocket} ws
40
+ * @param {number} readyState
45
41
  * @returns {boolean}
46
42
  */
47
- function isClosed (ws) {
48
- return ws[kReadyState] === states.CLOSED
43
+ function isClosed (readyState) {
44
+ return readyState === states.CLOSED
49
45
  }
50
46
 
51
47
  /**
@@ -54,6 +50,7 @@ function isClosed (ws) {
54
50
  * @param {EventTarget} target
55
51
  * @param {(...args: ConstructorParameters<typeof Event>) => Event} eventFactory
56
52
  * @param {EventInit | undefined} eventInitDict
53
+ * @returns {void}
57
54
  */
58
55
  function fireEvent (e, target, eventFactory = (type, init) => new Event(type, init), eventInitDict = {}) {
59
56
  // 1. If eventConstructor is not given, then let eventConstructor be Event.
@@ -73,51 +70,19 @@ function fireEvent (e, target, eventFactory = (type, init) => new Event(type, in
73
70
 
74
71
  /**
75
72
  * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
76
- * @param {import('./websocket').WebSocket} ws
73
+ * @param {import('./websocket').Handler} handler
77
74
  * @param {number} type Opcode
78
75
  * @param {Buffer} data application data
76
+ * @returns {void}
79
77
  */
80
- function websocketMessageReceived (ws, type, data) {
81
- // 1. If ready state is not OPEN (1), then return.
82
- if (ws[kReadyState] !== states.OPEN) {
83
- return
84
- }
85
-
86
- // 2. Let dataForEvent be determined by switching on type and binary type:
87
- let dataForEvent
88
-
89
- if (type === opcodes.TEXT) {
90
- // -> type indicates that the data is Text
91
- // a new DOMString containing data
92
- try {
93
- dataForEvent = utf8Decode(data)
94
- } catch {
95
- failWebsocketConnection(ws, 'Received invalid UTF-8 in text frame.')
96
- return
97
- }
98
- } else if (type === opcodes.BINARY) {
99
- if (ws[kBinaryType] === 'blob') {
100
- // -> type indicates that the data is Binary and binary type is "blob"
101
- // a new Blob object, created in the relevant Realm of the WebSocket
102
- // object, that represents data as its raw data
103
- dataForEvent = new Blob([data])
104
- } else {
105
- // -> type indicates that the data is Binary and binary type is "arraybuffer"
106
- // a new ArrayBuffer object, created in the relevant Realm of the
107
- // WebSocket object, whose contents are data
108
- dataForEvent = toArrayBuffer(data)
109
- }
110
- }
111
-
112
- // 3. Fire an event named message at the WebSocket object, using MessageEvent,
113
- // with the origin attribute initialized to the serialization of the WebSocket
114
- // object’s url's origin, and the data attribute initialized to dataForEvent.
115
- fireEvent('message', ws, createFastMessageEvent, {
116
- origin: ws[kWebSocketURL].origin,
117
- data: dataForEvent
118
- })
78
+ function websocketMessageReceived (handler, type, data) {
79
+ handler.onMessage(type, data)
119
80
  }
120
81
 
82
+ /**
83
+ * @param {Buffer} buffer
84
+ * @returns {ArrayBuffer}
85
+ */
121
86
  function toArrayBuffer (buffer) {
122
87
  if (buffer.byteLength === buffer.buffer.byteLength) {
123
88
  return buffer.buffer
@@ -130,6 +95,7 @@ function toArrayBuffer (buffer) {
130
95
  * @see https://datatracker.ietf.org/doc/html/rfc2616
131
96
  * @see https://bugs.chromium.org/p/chromium/issues/detail?id=398407
132
97
  * @param {string} protocol
98
+ * @returns {boolean}
133
99
  */
134
100
  function isValidSubprotocol (protocol) {
135
101
  // If present, this value indicates one
@@ -176,6 +142,7 @@ function isValidSubprotocol (protocol) {
176
142
  /**
177
143
  * @see https://datatracker.ietf.org/doc/html/rfc6455#section-7-4
178
144
  * @param {number} code
145
+ * @returns {boolean}
179
146
  */
180
147
  function isValidStatusCode (code) {
181
148
  if (code >= 1000 && code < 1015) {
@@ -190,30 +157,35 @@ function isValidStatusCode (code) {
190
157
  }
191
158
 
192
159
  /**
193
- * @param {import('./websocket').WebSocket} ws
160
+ * @param {import('./websocket').Handler} handler
161
+ * @param {number} code
194
162
  * @param {string|undefined} reason
163
+ * @returns {void}
195
164
  */
196
- function failWebsocketConnection (ws, reason) {
197
- const { [kController]: controller, [kResponse]: response } = ws
165
+ function failWebsocketConnection (handler, code, reason) {
166
+ // If _The WebSocket Connection is Established_ prior to the point where
167
+ // the endpoint is required to _Fail the WebSocket Connection_, the
168
+ // endpoint SHOULD send a Close frame with an appropriate status code
169
+ // (Section 7.4) before proceeding to _Close the WebSocket Connection_.
170
+ if (isEstablished(handler.readyState)) {
171
+ // avoid circular require - performance is not important here
172
+ const { closeWebSocketConnection } = require('./connection')
173
+ closeWebSocketConnection(handler, code, reason, false)
174
+ }
198
175
 
199
- controller.abort()
176
+ handler.controller.abort()
200
177
 
201
- if (response?.socket && !response.socket.destroyed) {
202
- response.socket.destroy()
178
+ if (handler.socket?.destroyed === false) {
179
+ handler.socket.destroy()
203
180
  }
204
181
 
205
- if (reason) {
206
- // TODO: process.nextTick
207
- fireEvent('error', ws, (type, init) => new ErrorEvent(type, init), {
208
- error: new Error(reason),
209
- message: reason
210
- })
211
- }
182
+ handler.onFail(code, reason)
212
183
  }
213
184
 
214
185
  /**
215
186
  * @see https://datatracker.ietf.org/doc/html/rfc6455#section-5.5
216
187
  * @param {number} opcode
188
+ * @returns {boolean}
217
189
  */
218
190
  function isControlFrame (opcode) {
219
191
  return (
@@ -223,14 +195,27 @@ function isControlFrame (opcode) {
223
195
  )
224
196
  }
225
197
 
198
+ /**
199
+ * @param {number} opcode
200
+ * @returns {boolean}
201
+ */
226
202
  function isContinuationFrame (opcode) {
227
203
  return opcode === opcodes.CONTINUATION
228
204
  }
229
205
 
206
+ /**
207
+ * @param {number} opcode
208
+ * @returns {boolean}
209
+ */
230
210
  function isTextBinaryFrame (opcode) {
231
211
  return opcode === opcodes.TEXT || opcode === opcodes.BINARY
232
212
  }
233
213
 
214
+ /**
215
+ *
216
+ * @param {number} opcode
217
+ * @returns {boolean}
218
+ */
234
219
  function isValidOpcode (opcode) {
235
220
  return isTextBinaryFrame(opcode) || isContinuationFrame(opcode) || isControlFrame(opcode)
236
221
  }
@@ -264,6 +249,7 @@ function parseExtensions (extensions) {
264
249
  * @see https://www.rfc-editor.org/rfc/rfc7692#section-7.1.2.2
265
250
  * @description "client-max-window-bits = 1*DIGIT"
266
251
  * @param {string} value
252
+ * @returns {boolean}
267
253
  */
268
254
  function isValidClientWindowBits (value) {
269
255
  for (let i = 0; i < value.length; i++) {
@@ -277,22 +263,84 @@ function isValidClientWindowBits (value) {
277
263
  return true
278
264
  }
279
265
 
280
- // https://nodejs.org/api/intl.html#detecting-internationalization-support
281
- const hasIntl = typeof process.versions.icu === 'string'
282
- const fatalDecoder = hasIntl ? new TextDecoder('utf-8', { fatal: true }) : undefined
266
+ /**
267
+ * @see https://whatpr.org/websockets/48/7b748d3...d5570f3.html#get-a-url-record
268
+ * @param {string} url
269
+ * @param {string} [baseURL]
270
+ */
271
+ function getURLRecord (url, baseURL) {
272
+ // 1. Let urlRecord be the result of applying the URL parser to url with baseURL .
273
+ // 2. If urlRecord is failure, then throw a " SyntaxError " DOMException .
274
+ let urlRecord
275
+
276
+ try {
277
+ urlRecord = new URL(url, baseURL)
278
+ } catch (e) {
279
+ throw new DOMException(e, 'SyntaxError')
280
+ }
281
+
282
+ // 3. If urlRecord ’s scheme is " http ", then set urlRecord ’s scheme to " ws ".
283
+ // 4. Otherwise, if urlRecord ’s scheme is " https ", set urlRecord ’s scheme to " wss ".
284
+ if (urlRecord.protocol === 'http:') {
285
+ urlRecord.protocol = 'ws:'
286
+ } else if (urlRecord.protocol === 'https:') {
287
+ urlRecord.protocol = 'wss:'
288
+ }
289
+
290
+ // 5. If urlRecord ’s scheme is not " ws " or " wss ", then throw a " SyntaxError " DOMException .
291
+ if (urlRecord.protocol !== 'ws:' && urlRecord.protocol !== 'wss:') {
292
+ throw new DOMException('expected a ws: or wss: url', 'SyntaxError')
293
+ }
294
+
295
+ // If urlRecord ’s fragment is non-null, then throw a " SyntaxError " DOMException .
296
+ if (urlRecord.hash.length || urlRecord.href.endsWith('#')) {
297
+ throw new DOMException('hash', 'SyntaxError')
298
+ }
299
+
300
+ // Return urlRecord .
301
+ return urlRecord
302
+ }
303
+
304
+ // https://whatpr.org/websockets/48.html#validate-close-code-and-reason
305
+ function validateCloseCodeAndReason (code, reason) {
306
+ // 1. If code is not null, but is neither an integer equal to
307
+ // 1000 nor an integer in the range 3000 to 4999, inclusive,
308
+ // throw an "InvalidAccessError" DOMException.
309
+ if (code !== null) {
310
+ if (code !== 1000 && (code < 3000 || code > 4999)) {
311
+ throw new DOMException('invalid code', 'InvalidAccessError')
312
+ }
313
+ }
314
+
315
+ // 2. If reason is not null, then:
316
+ if (reason !== null) {
317
+ // 2.1. Let reasonBytes be the result of UTF-8 encoding reason.
318
+ // 2.2. If reasonBytes is longer than 123 bytes, then throw a
319
+ // "SyntaxError" DOMException.
320
+ const reasonBytesLength = Buffer.byteLength(reason)
321
+
322
+ if (reasonBytesLength > 123) {
323
+ throw new DOMException(`Reason must be less than 123 bytes; received ${reasonBytesLength}`, 'SyntaxError')
324
+ }
325
+ }
326
+ }
283
327
 
284
328
  /**
285
329
  * Converts a Buffer to utf-8, even on platforms without icu.
286
- * @param {Buffer} buffer
330
+ * @type {(buffer: Buffer) => string}
287
331
  */
288
- const utf8Decode = hasIntl
289
- ? fatalDecoder.decode.bind(fatalDecoder)
290
- : function (buffer) {
332
+ const utf8Decode = (() => {
333
+ if (typeof process.versions.icu === 'string') {
334
+ const fatalDecoder = new TextDecoder('utf-8', { fatal: true })
335
+ return fatalDecoder.decode.bind(fatalDecoder)
336
+ }
337
+ return function (buffer) {
291
338
  if (isUtf8(buffer)) {
292
339
  return buffer.toString('utf-8')
293
340
  }
294
341
  throw new TypeError('Invalid utf-8 received.')
295
342
  }
343
+ })()
296
344
 
297
345
  module.exports = {
298
346
  isConnecting,
@@ -310,5 +358,8 @@ module.exports = {
310
358
  isTextBinaryFrame,
311
359
  isValidOpcode,
312
360
  parseExtensions,
313
- isValidClientWindowBits
361
+ isValidClientWindowBits,
362
+ toArrayBuffer,
363
+ getURLRecord,
364
+ validateCloseCodeAndReason
314
365
  }