undici 7.1.0 → 7.1.1

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 CHANGED
@@ -281,17 +281,23 @@ stalls or deadlocks when running out of connections.
281
281
 
282
282
  ```js
283
283
  // Do
284
- const headers = await fetch(url)
285
- .then(async res => {
286
- for await (const chunk of res.body) {
287
- // force consumption of body
288
- }
289
- return res.headers
290
- })
284
+ const { body, headers } = await fetch(url);
285
+ for await (const chunk of body) {
286
+ // force consumption of body
287
+ }
291
288
 
292
289
  // Do not
293
- const headers = await fetch(url)
294
- .then(res => res.headers)
290
+ const { headers } = await fetch(url);
291
+ ```
292
+
293
+ The same applies for `request` too:
294
+ ```js
295
+ // Do
296
+ const { body, headers } = await request(url);
297
+ await res.body.dump(); // force consumption of body
298
+
299
+ // Do not
300
+ const { headers } = await request(url);
295
301
  ```
296
302
 
297
303
  However, if you want to get only headers, it might be better to use `HEAD` request method. Usage of this method will obviate the need for consumption or cancelling of the response body. See [MDN - HTTP - HTTP request methods - HEAD](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) for more details.
@@ -445,6 +451,16 @@ and `undici.Agent`) which will enable the family autoselection algorithm when es
445
451
  * [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag>
446
452
  * [__Matthew Aitken__](https://github.com/KhafraDev), <https://www.npmjs.com/~khaf>
447
453
 
454
+ ## Long Term Support
455
+
456
+ Undici aligns with the Node.js LTS schedule. The following table shows the supported versions:
457
+
458
+ | Version | Node.js | End of Life |
459
+ |---------|-------------|-------------|
460
+ | 5.x | v18.x | 2024-04-30 |
461
+ | 6.x | v20.x v22.x | 2026-04-30 |
462
+ | 7.x | v24.x | 2027-04-30 |
463
+
448
464
  ## License
449
465
 
450
466
  MIT
@@ -15,6 +15,7 @@ Returns: `ProxyAgent`
15
15
  ### Parameter: `ProxyAgentOptions`
16
16
 
17
17
  Extends: [`AgentOptions`](/docs/docs/api/Agent.md#parameter-agentoptions)
18
+ > It ommits `AgentOptions#connect`.
18
19
 
19
20
  * **uri** `string | URL` (required) - The URI of the proxy server. This can be provided as a string, as an instance of the URL class, or as an object with a `uri` property of type string.
20
21
  If the `uri` is provided as a string or `uri` is an object with an `uri` property of type string, then it will be parsed into a `URL` object according to the [WHATWG URL Specification](https://url.spec.whatwg.org).
@@ -22,8 +23,8 @@ For detailed information on the parsing process and potential validation errors,
22
23
  * **token** `string` (optional) - It can be passed by a string of token for authentication.
23
24
  * **auth** `string` (**deprecated**) - Use token.
24
25
  * **clientFactory** `(origin: URL, opts: Object) => Dispatcher` (optional) - Default: `(origin, opts) => new Pool(origin, opts)`
25
- * **requestTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the request. See [TLS](https://nodejs.org/api/tls.html#tlsconnectoptions-callback).
26
- * **proxyTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the proxy server. See [TLS](https://nodejs.org/api/tls.html#tlsconnectoptions-callback).
26
+ * **requestTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the request. It extends from [`Client#ConnectOptions`](/docs/docs/api/Client.md#parameter-connectoptions).
27
+ * **proxyTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the proxy server. It extends from [`Client#ConnectOptions`](/docs/docs/api/Client.md#parameter-connectoptions).
27
28
 
28
29
  Examples:
29
30
 
@@ -35,6 +36,13 @@ const proxyAgent = new ProxyAgent('my.proxy.server')
35
36
  const proxyAgent = new ProxyAgent(new URL('my.proxy.server'))
36
37
  // or
37
38
  const proxyAgent = new ProxyAgent({ uri: 'my.proxy.server' })
39
+ // or
40
+ const proxyAgent = new ProxyAgent({
41
+ uri: new URL('my.proxy.server'),
42
+ proxyTls: {
43
+ signal: AbortSignal.timeout(1000)
44
+ }
45
+ })
38
46
  ```
39
47
 
40
48
  #### Example - Basic ProxyAgent instantiation
package/index.js CHANGED
@@ -49,15 +49,8 @@ module.exports.cacheStores = {
49
49
  MemoryCacheStore: require('./lib/cache/memory-cache-store')
50
50
  }
51
51
 
52
- try {
53
- const SqliteCacheStore = require('./lib/cache/sqlite-cache-store')
54
- module.exports.cacheStores.SqliteCacheStore = SqliteCacheStore
55
- } catch (err) {
56
- // Most likely node:sqlite was not present, since SqliteCacheStore is
57
- // optional, don't throw. Don't check specific error codes here because while
58
- // ERR_UNKNOWN_BUILTIN_MODULE is expected, users have seen other codes like
59
- // MODULE_NOT_FOUND
60
- }
52
+ const SqliteCacheStore = require('./lib/cache/sqlite-cache-store')
53
+ module.exports.cacheStores.SqliteCacheStore = SqliteCacheStore
61
54
 
62
55
  module.exports.buildConnector = buildConnector
63
56
  module.exports.errors = errors
@@ -1,9 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const { DatabaseSync } = require('node:sqlite')
4
3
  const { Writable } = require('stream')
5
4
  const { assertCacheKey, assertCacheValue } = require('../util/cache.js')
6
5
 
6
+ let DatabaseSync
7
+
7
8
  const VERSION = 3
8
9
 
9
10
  // 2gb
@@ -101,6 +102,9 @@ module.exports = class SqliteCacheStore {
101
102
  }
102
103
  }
103
104
 
105
+ if (!DatabaseSync) {
106
+ DatabaseSync = require('node:sqlite').DatabaseSync
107
+ }
104
108
  this.#db = new DatabaseSync(opts?.location ?? ':memory:')
105
109
 
106
110
  this.#db.exec(`
@@ -30,6 +30,7 @@ const {
30
30
  kClosed,
31
31
  kBodyTimeout
32
32
  } = require('../core/symbols.js')
33
+ const { channels } = require('../core/diagnostics.js')
33
34
 
34
35
  const kOpenStreams = Symbol('open streams')
35
36
 
@@ -448,6 +449,14 @@ function writeH2 (client, request) {
448
449
 
449
450
  session.ref()
450
451
 
452
+ if (channels.sendHeaders.hasSubscribers) {
453
+ let header = ''
454
+ for (const key in headers) {
455
+ header += `${key}: ${headers[key]}\r\n`
456
+ }
457
+ channels.sendHeaders.publish({ request, headers: header, socket: session[kSocket] })
458
+ }
459
+
451
460
  // TODO(metcoder95): add support for sending trailers
452
461
  const shouldEndStream = method === 'GET' || method === 'HEAD' || body === null
453
462
  if (expectContinue) {
@@ -53,8 +53,7 @@ module.exports = class WrapHandler {
53
53
  onRequestUpgrade (controller, statusCode, headers, socket) {
54
54
  const rawHeaders = []
55
55
  for (const [key, val] of Object.entries(headers)) {
56
- // TODO (fix): What if val is Array
57
- rawHeaders.push(Buffer.from(key), Buffer.from(val))
56
+ rawHeaders.push(Buffer.from(key), Array.isArray(val) ? val.map(v => Buffer.from(v)) : Buffer.from(val))
58
57
  }
59
58
 
60
59
  this.#handler.onUpgrade?.(statusCode, rawHeaders, socket)
@@ -63,8 +62,7 @@ module.exports = class WrapHandler {
63
62
  onResponseStart (controller, statusCode, headers, statusMessage) {
64
63
  const rawHeaders = []
65
64
  for (const [key, val] of Object.entries(headers)) {
66
- // TODO (fix): What if val is Array
67
- rawHeaders.push(Buffer.from(key), Buffer.from(val))
65
+ rawHeaders.push(Buffer.from(key), Array.isArray(val) ? val.map(v => Buffer.from(v)) : Buffer.from(val))
68
66
  }
69
67
 
70
68
  if (this.#handler.onHeaders?.(statusCode, rawHeaders, () => controller.resume(), statusMessage) === false) {
@@ -81,8 +79,7 @@ module.exports = class WrapHandler {
81
79
  onResponseEnd (controller, trailers) {
82
80
  const rawTrailers = []
83
81
  for (const [key, val] of Object.entries(trailers)) {
84
- // TODO (fix): What if val is Array
85
- rawTrailers.push(Buffer.from(key), Buffer.from(val))
82
+ rawTrailers.push(Buffer.from(key), Array.isArray(val) ? val.map(v => Buffer.from(v)) : Buffer.from(val))
86
83
  }
87
84
 
88
85
  this.#handler.onComplete?.(rawTrailers)
@@ -225,8 +225,11 @@ function handleResult (
225
225
 
226
226
  let headers = {
227
227
  ...opts.headers,
228
- 'if-modified-since': new Date(result.cachedAt).toUTCString(),
229
- 'if-none-match': result.etag
228
+ 'if-modified-since': new Date(result.cachedAt).toUTCString()
229
+ }
230
+
231
+ if (result.etag) {
232
+ headers['if-none-match'] = result.etag
230
233
  }
231
234
 
232
235
  if (result.vary) {
@@ -358,7 +358,7 @@ module.exports = interceptorOpts => {
358
358
  servername: origin.hostname, // For SNI on TLS
359
359
  origin: newOrigin,
360
360
  headers: {
361
- host: origin.hostname,
361
+ host: origin.host,
362
362
  ...origDispatchOpts.headers
363
363
  }
364
364
  }
@@ -16,7 +16,7 @@ class ResponseErrorHandler extends DecoratorHandler {
16
16
  }
17
17
 
18
18
  #checkContentType (contentType) {
19
- return this.#contentType.indexOf(contentType) === 0
19
+ return (this.#contentType ?? '').indexOf(contentType) === 0
20
20
  }
21
21
 
22
22
  onRequestStart (controller, context) {
@@ -81,8 +81,8 @@ class ResponseErrorHandler extends DecoratorHandler {
81
81
  }
82
82
  }
83
83
 
84
- onResponseError (err) {
85
- super.onResponseError(err)
84
+ onResponseError (controller, err) {
85
+ super.onResponseError(controller, err)
86
86
  }
87
87
  }
88
88
 
@@ -283,7 +283,7 @@ function parseMIMEType (input) {
283
283
 
284
284
  // 5. If position is past the end of input, then return
285
285
  // failure
286
- if (position.position > input.length) {
286
+ if (position.position >= input.length) {
287
287
  return 'failure'
288
288
  }
289
289
 
@@ -364,7 +364,7 @@ function parseMIMEType (input) {
364
364
  }
365
365
 
366
366
  // 6. If position is past the end of input, then break.
367
- if (position.position > input.length) {
367
+ if (position.position >= input.length) {
368
368
  break
369
369
  }
370
370
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "undici",
3
- "version": "7.1.0",
3
+ "version": "7.1.1",
4
4
  "description": "An HTTP/1.1 client, written from scratch for Node.js",
5
5
  "homepage": "https://undici.nodejs.org",
6
6
  "bugs": {
@@ -106,7 +106,7 @@
106
106
  "prepare": "husky && node ./scripts/platform-shell.js"
107
107
  },
108
108
  "devDependencies": {
109
- "@fastify/busboy": "3.0.0",
109
+ "@fastify/busboy": "3.1.0",
110
110
  "@matteo.collina/tspl": "^0.1.1",
111
111
  "@sinonjs/fake-timers": "^12.0.0",
112
112
  "@types/node": "^18.19.50",
@@ -121,7 +121,7 @@
121
121
  "https-pem": "^3.0.0",
122
122
  "husky": "^9.0.7",
123
123
  "jest": "^29.0.2",
124
- "neostandard": "^0.11.2",
124
+ "neostandard": "^0.12.0",
125
125
  "node-forge": "^1.3.1",
126
126
  "proxy": "^2.1.1",
127
127
  "tsd": "^0.31.2",
package/types/index.d.ts CHANGED
@@ -64,6 +64,7 @@ declare namespace Undici {
64
64
  const caches: typeof import('./cache').caches
65
65
  const interceptors: typeof import('./interceptors').default
66
66
  const cacheStores: {
67
- MemoryCacheStore: typeof import('./cache-interceptor').default.MemoryCacheStore
67
+ MemoryCacheStore: typeof import('./cache-interceptor').default.MemoryCacheStore,
68
+ SqliteCacheStore: typeof import('./cache-interceptor').default.SqliteCacheStore
68
69
  }
69
70
  }