undici 6.5.0 → 6.6.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.
Files changed (54) hide show
  1. package/docs/best-practices/client-certificate.md +3 -3
  2. package/lib/agent.js +5 -7
  3. package/lib/api/api-connect.js +2 -2
  4. package/lib/api/api-pipeline.js +4 -4
  5. package/lib/api/api-request.js +2 -2
  6. package/lib/api/api-stream.js +4 -4
  7. package/lib/api/api-upgrade.js +3 -3
  8. package/lib/api/readable.js +2 -2
  9. package/lib/api/util.js +1 -1
  10. package/lib/balanced-pool.js +1 -1
  11. package/lib/cache/cache.js +4 -10
  12. package/lib/cache/util.js +1 -1
  13. package/lib/client.js +14 -14
  14. package/lib/cookies/parse.js +1 -1
  15. package/lib/cookies/util.js +1 -1
  16. package/lib/core/connect.js +3 -3
  17. package/lib/core/diagnostics.js +2 -2
  18. package/lib/core/request.js +18 -13
  19. package/lib/core/tree.js +3 -5
  20. package/lib/core/util.js +15 -15
  21. package/lib/dispatcher.js +1 -1
  22. package/lib/eventsource/eventsource-stream.js +1 -1
  23. package/lib/eventsource/eventsource.js +2 -2
  24. package/lib/eventsource/util.js +9 -1
  25. package/lib/fetch/body.js +29 -21
  26. package/lib/fetch/dataURL.js +97 -17
  27. package/lib/fetch/file.js +5 -5
  28. package/lib/fetch/formdata.js +1 -1
  29. package/lib/fetch/headers.js +1 -1
  30. package/lib/fetch/index.js +28 -37
  31. package/lib/fetch/request.js +3 -2
  32. package/lib/fetch/response.js +31 -39
  33. package/lib/fetch/util.js +196 -36
  34. package/lib/fetch/webidl.js +1 -1
  35. package/lib/fileapi/util.js +2 -2
  36. package/lib/handler/RedirectHandler.js +2 -2
  37. package/lib/handler/RetryHandler.js +3 -3
  38. package/lib/llhttp/llhttp-wasm.js +3 -1
  39. package/lib/llhttp/llhttp_simd-wasm.js +3 -1
  40. package/lib/mock/mock-agent.js +2 -2
  41. package/lib/mock/mock-client.js +1 -1
  42. package/lib/mock/mock-pool.js +1 -1
  43. package/lib/mock/mock-utils.js +2 -2
  44. package/lib/mock/pending-interceptors-formatter.js +2 -2
  45. package/lib/pool.js +7 -8
  46. package/lib/proxy-agent.js +2 -2
  47. package/lib/timers.js +1 -1
  48. package/lib/websocket/connection.js +1 -1
  49. package/lib/websocket/events.js +1 -1
  50. package/lib/websocket/frame.js +1 -1
  51. package/lib/websocket/receiver.js +7 -6
  52. package/lib/websocket/util.js +1 -1
  53. package/lib/websocket/websocket.js +1 -1
  54. package/package.json +4 -7
package/lib/fetch/util.js CHANGED
@@ -1,10 +1,13 @@
1
1
  'use strict'
2
2
 
3
+ const { Transform } = require('node:stream')
4
+ const zlib = require('node:zlib')
3
5
  const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet } = require('./constants')
4
6
  const { getGlobalOrigin } = require('./global')
5
- const { performance } = require('perf_hooks')
7
+ const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = require('./dataURL')
8
+ const { performance } = require('node:perf_hooks')
6
9
  const { isBlobLike, toUSVString, ReadableStreamFrom, isValidHTTPToken } = require('../core/util')
7
- const assert = require('assert')
10
+ const assert = require('node:assert')
8
11
  const { isUint8Array } = require('util/types')
9
12
 
10
13
  // https://nodejs.org/api/crypto.html#determining-if-crypto-support-is-unavailable
@@ -12,7 +15,7 @@ const { isUint8Array } = require('util/types')
12
15
  let crypto
13
16
 
14
17
  try {
15
- crypto = require('crypto')
18
+ crypto = require('node:crypto')
16
19
  } catch {
17
20
 
18
21
  }
@@ -272,7 +275,7 @@ function coarsenTime (timestamp, crossOriginIsolatedCapability) {
272
275
  }
273
276
 
274
277
  // https://fetch.spec.whatwg.org/#clamp-and-coarsen-connection-timing-info
275
- function clampAndCoursenConnectionTimingInfo (connectionTimingInfo, defaultStartTime, crossOriginIsolatedCapability) {
278
+ function clampAndCoarsenConnectionTimingInfo (connectionTimingInfo, defaultStartTime, crossOriginIsolatedCapability) {
276
279
  if (!connectionTimingInfo?.startTime || connectionTimingInfo.startTime < defaultStartTime) {
277
280
  return {
278
281
  domainLookupStartTime: defaultStartTime,
@@ -887,29 +890,6 @@ function isReadableStreamLike (stream) {
887
890
  )
888
891
  }
889
892
 
890
- /**
891
- * @see https://infra.spec.whatwg.org/#isomorphic-decode
892
- * @param {Uint8Array} input
893
- */
894
- function isomorphicDecode (input) {
895
- // 1. To isomorphic decode a byte sequence input, return a string whose code point
896
- // length is equal to input’s length and whose code points have the same values
897
- // as the values of input’s bytes, in the same order.
898
- const length = input.length
899
- if ((2 << 15) - 1 > length) {
900
- return String.fromCharCode.apply(null, input)
901
- }
902
- let result = ''; let i = 0
903
- let addition = (2 << 15) - 1
904
- while (i < length) {
905
- if (i + addition > length) {
906
- addition = length - i
907
- }
908
- result += String.fromCharCode.apply(null, input.subarray(i, i += addition))
909
- }
910
- return result
911
- }
912
-
913
893
  /**
914
894
  * @param {ReadableStreamController<Uint8Array>} controller
915
895
  */
@@ -1007,18 +987,12 @@ function urlIsHttpHttpsScheme (url) {
1007
987
  return protocol === 'http:' || protocol === 'https:'
1008
988
  }
1009
989
 
1010
- /** @type {import('./dataURL')['collectASequenceOfCodePoints']} */
1011
- let collectASequenceOfCodePoints
1012
-
1013
990
  /**
1014
991
  * @see https://fetch.spec.whatwg.org/#simple-range-header-value
1015
992
  * @param {string} value
1016
993
  * @param {boolean} allowWhitespace
1017
994
  */
1018
995
  function simpleRangeHeaderValue (value, allowWhitespace) {
1019
- // Note: avoid circular require
1020
- collectASequenceOfCodePoints ??= require('./dataURL').collectASequenceOfCodePoints
1021
-
1022
996
  // 1. Let data be the isomorphic decoding of value.
1023
997
  // Note: isomorphic decoding takes a sequence of bytes (ie. a Uint8Array) and turns it into a string,
1024
998
  // nothing more. We obviously don't need to do that if value is a string already.
@@ -1174,6 +1148,191 @@ function buildContentRange (rangeStart, rangeEnd, fullLength) {
1174
1148
  return contentRange
1175
1149
  }
1176
1150
 
1151
+ // A Stream, which pipes the response to zlib.createInflate() or
1152
+ // zlib.createInflateRaw() depending on the first byte of the Buffer.
1153
+ // If the lower byte of the first byte is 0x08, then the stream is
1154
+ // interpreted as a zlib stream, otherwise it's interpreted as a
1155
+ // raw deflate stream.
1156
+ class InflateStream extends Transform {
1157
+ _transform (chunk, encoding, callback) {
1158
+ if (!this._inflateStream) {
1159
+ if (chunk.length === 0) {
1160
+ callback()
1161
+ return
1162
+ }
1163
+ this._inflateStream = (chunk[0] & 0x0F) === 0x08
1164
+ ? zlib.createInflate()
1165
+ : zlib.createInflateRaw()
1166
+
1167
+ this._inflateStream.on('data', this.push.bind(this))
1168
+ this._inflateStream.on('end', () => this.push(null))
1169
+ this._inflateStream.on('error', (err) => this.destroy(err))
1170
+ }
1171
+
1172
+ this._inflateStream.write(chunk, encoding, callback)
1173
+ }
1174
+
1175
+ _final (callback) {
1176
+ if (this._inflateStream) {
1177
+ this._inflateStream.end()
1178
+ this._inflateStream = null
1179
+ }
1180
+ callback()
1181
+ }
1182
+ }
1183
+
1184
+ function createInflate () {
1185
+ return new InflateStream()
1186
+ }
1187
+
1188
+ /**
1189
+ * @see https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
1190
+ * @param {import('./headers').HeadersList} headers
1191
+ */
1192
+ function extractMimeType (headers) {
1193
+ // 1. Let charset be null.
1194
+ let charset = null
1195
+
1196
+ // 2. Let essence be null.
1197
+ let essence = null
1198
+
1199
+ // 3. Let mimeType be null.
1200
+ let mimeType = null
1201
+
1202
+ // 4. Let values be the result of getting, decoding, and splitting `Content-Type` from headers.
1203
+ const values = getDecodeSplit('content-type', headers)
1204
+
1205
+ // 5. If values is null, then return failure.
1206
+ if (values === null) {
1207
+ return 'failure'
1208
+ }
1209
+
1210
+ // 6. For each value of values:
1211
+ for (const value of values) {
1212
+ // 6.1. Let temporaryMimeType be the result of parsing value.
1213
+ const temporaryMimeType = parseMIMEType(value)
1214
+
1215
+ // 6.2. If temporaryMimeType is failure or its essence is "*/*", then continue.
1216
+ if (temporaryMimeType === 'failure' || temporaryMimeType.essence === '*/*') {
1217
+ continue
1218
+ }
1219
+
1220
+ // 6.3. Set mimeType to temporaryMimeType.
1221
+ mimeType = temporaryMimeType
1222
+
1223
+ // 6.4. If mimeType’s essence is not essence, then:
1224
+ if (mimeType.essence !== essence) {
1225
+ // 6.4.1. Set charset to null.
1226
+ charset = null
1227
+
1228
+ // 6.4.2. If mimeType’s parameters["charset"] exists, then set charset to
1229
+ // mimeType’s parameters["charset"].
1230
+ if (mimeType.parameters.has('charset')) {
1231
+ charset = mimeType.parameters.get('charset')
1232
+ }
1233
+
1234
+ // 6.4.3. Set essence to mimeType’s essence.
1235
+ essence = mimeType.essence
1236
+ } else if (!mimeType.parameters.has('charset') && charset !== null) {
1237
+ // 6.5. Otherwise, if mimeType’s parameters["charset"] does not exist, and
1238
+ // charset is non-null, set mimeType’s parameters["charset"] to charset.
1239
+ mimeType.parameters.set('charset', charset)
1240
+ }
1241
+ }
1242
+
1243
+ // 7. If mimeType is null, then return failure.
1244
+ if (mimeType == null) {
1245
+ return 'failure'
1246
+ }
1247
+
1248
+ // 8. Return mimeType.
1249
+ return mimeType
1250
+ }
1251
+
1252
+ /**
1253
+ * @see https://fetch.spec.whatwg.org/#header-value-get-decode-and-split
1254
+ * @param {string|null} value
1255
+ */
1256
+ function gettingDecodingSplitting (value) {
1257
+ // 1. Let input be the result of isomorphic decoding value.
1258
+ const input = value
1259
+
1260
+ // 2. Let position be a position variable for input, initially pointing at the start of input.
1261
+ const position = { position: 0 }
1262
+
1263
+ // 3. Let values be a list of strings, initially empty.
1264
+ const values = []
1265
+
1266
+ // 4. Let temporaryValue be the empty string.
1267
+ let temporaryValue = ''
1268
+
1269
+ // 5. While position is not past the end of input:
1270
+ while (position.position < input.length) {
1271
+ // 5.1. Append the result of collecting a sequence of code points that are not U+0022 (")
1272
+ // or U+002C (,) from input, given position, to temporaryValue.
1273
+ temporaryValue += collectASequenceOfCodePoints(
1274
+ (char) => char !== '"' && char !== ',',
1275
+ input,
1276
+ position
1277
+ )
1278
+
1279
+ // 5.2. If position is not past the end of input, then:
1280
+ if (position.position < input.length) {
1281
+ // 5.2.1. If the code point at position within input is U+0022 ("), then:
1282
+ if (input.charCodeAt(position.position) === 0x22) {
1283
+ // 5.2.1.1. Append the result of collecting an HTTP quoted string from input, given position, to temporaryValue.
1284
+ temporaryValue += collectAnHTTPQuotedString(
1285
+ input,
1286
+ position
1287
+ )
1288
+
1289
+ // 5.2.1.2. If position is not past the end of input, then continue.
1290
+ if (position.position < input.length) {
1291
+ continue
1292
+ }
1293
+ } else {
1294
+ // 5.2.2. Otherwise:
1295
+
1296
+ // 5.2.2.1. Assert: the code point at position within input is U+002C (,).
1297
+ assert(input.charCodeAt(position.position) === 0x2C)
1298
+
1299
+ // 5.2.2.2. Advance position by 1.
1300
+ position.position++
1301
+ }
1302
+ }
1303
+
1304
+ // 5.3. Remove all HTTP tab or space from the start and end of temporaryValue.
1305
+ temporaryValue = removeChars(temporaryValue, true, true, (char) => char === 0x9 || char === 0x20)
1306
+
1307
+ // 5.4. Append temporaryValue to values.
1308
+ values.push(temporaryValue)
1309
+
1310
+ // 5.6. Set temporaryValue to the empty string.
1311
+ temporaryValue = ''
1312
+ }
1313
+
1314
+ // 6. Return values.
1315
+ return values
1316
+ }
1317
+
1318
+ /**
1319
+ * @see https://fetch.spec.whatwg.org/#concept-header-list-get-decode-split
1320
+ * @param {string} name lowercase header name
1321
+ * @param {import('./headers').HeadersList} list
1322
+ */
1323
+ function getDecodeSplit (name, list) {
1324
+ // 1. Let value be the result of getting name from list.
1325
+ const value = list.get(name, true)
1326
+
1327
+ // 2. If value is null, then return null.
1328
+ if (value === null) {
1329
+ return null
1330
+ }
1331
+
1332
+ // 3. Return the result of getting, decoding, and splitting value.
1333
+ return gettingDecodingSplitting(value)
1334
+ }
1335
+
1177
1336
  module.exports = {
1178
1337
  isAborted,
1179
1338
  isCancelled,
@@ -1181,7 +1340,7 @@ module.exports = {
1181
1340
  ReadableStreamFrom,
1182
1341
  toUSVString,
1183
1342
  tryUpgradeRequestToAPotentiallyTrustworthyURL,
1184
- clampAndCoursenConnectionTimingInfo,
1343
+ clampAndCoarsenConnectionTimingInfo,
1185
1344
  coarsenedSharedCurrentTime,
1186
1345
  determineRequestsReferrer,
1187
1346
  makePolicyContainer,
@@ -1213,7 +1372,6 @@ module.exports = {
1213
1372
  isReadableStreamLike,
1214
1373
  readableStreamClose,
1215
1374
  isomorphicEncode,
1216
- isomorphicDecode,
1217
1375
  urlIsLocal,
1218
1376
  urlHasHttpsScheme,
1219
1377
  urlIsHttpHttpsScheme,
@@ -1221,5 +1379,7 @@ module.exports = {
1221
1379
  normalizeMethodRecord,
1222
1380
  simpleRangeHeaderValue,
1223
1381
  buildContentRange,
1224
- parseMetadata
1382
+ parseMetadata,
1383
+ createInflate,
1384
+ extractMimeType
1225
1385
  }
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { types } = require('util')
3
+ const { types } = require('node:util')
4
4
  const { toUSVString } = require('./util')
5
5
 
6
6
  /** @type {import('../../types/webidl').Webidl} */
@@ -10,9 +10,9 @@ const {
10
10
  const { ProgressEvent } = require('./progressevent')
11
11
  const { getEncoding } = require('./encoding')
12
12
  const { serializeAMimeType, parseMIMEType } = require('../fetch/dataURL')
13
- const { types } = require('util')
13
+ const { types } = require('node:util')
14
14
  const { StringDecoder } = require('string_decoder')
15
- const { btoa } = require('buffer')
15
+ const { btoa } = require('node:buffer')
16
16
 
17
17
  /** @type {PropertyDescriptor} */
18
18
  const staticPropertyDescriptors = {
@@ -2,9 +2,9 @@
2
2
 
3
3
  const util = require('../core/util')
4
4
  const { kBodyUsed } = require('../core/symbols')
5
- const assert = require('assert')
5
+ const assert = require('node:assert')
6
6
  const { InvalidArgumentError } = require('../core/errors')
7
- const EE = require('events')
7
+ const EE = require('node:events')
8
8
 
9
9
  const redirectableStatusCodes = [300, 301, 302, 303, 307, 308]
10
10
 
@@ -1,4 +1,4 @@
1
- const assert = require('assert')
1
+ const assert = require('node:assert')
2
2
 
3
3
  const { kRetryHandlerDefaultRetry } = require('../core/symbols')
4
4
  const { RequestRetryError } = require('../core/errors')
@@ -148,10 +148,10 @@ class RetryHandler {
148
148
  return
149
149
  }
150
150
 
151
- let retryAfterHeader = headers != null && headers['retry-after']
151
+ let retryAfterHeader = headers?.['retry-after']
152
152
  if (retryAfterHeader) {
153
153
  retryAfterHeader = Number(retryAfterHeader)
154
- retryAfterHeader = isNaN(retryAfterHeader)
154
+ retryAfterHeader = Number.isNaN(retryAfterHeader)
155
155
  ? calculateRetryAfterHeader(retryAfterHeader)
156
156
  : retryAfterHeader * 1e3 // Retry-After is in seconds
157
157
  }