undici 6.2.1 → 6.4.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/README.md CHANGED
@@ -18,34 +18,21 @@ npm i undici
18
18
  ## Benchmarks
19
19
 
20
20
  The benchmark is a simple `hello world` [example](benchmarks/benchmark.js) using a
21
- number of unix sockets (connections) with a pipelining depth of 10 running on Node 20.6.0.
22
-
23
- ### Connections 1
24
-
25
-
26
- | Tests | Samples | Result | Tolerance | Difference with slowest |
27
- |---------------------|---------|---------------|-----------|-------------------------|
28
- | http - no keepalive | 15 | 5.32 req/sec | ± 2.61 % | - |
29
- | http - keepalive | 10 | 5.35 req/sec | ± 2.47 % | + 0.44 % |
30
- | undici - fetch | 15 | 41.85 req/sec | ± 2.49 % | + 686.04 % |
31
- | undici - pipeline | 40 | 50.36 req/sec | ± 2.77 % | + 845.92 % |
32
- | undici - stream | 15 | 60.58 req/sec | ± 2.75 % | + 1037.72 % |
33
- | undici - request | 10 | 61.19 req/sec | ± 2.60 % | + 1049.24 % |
34
- | undici - dispatch | 20 | 64.84 req/sec | ± 2.81 % | + 1117.81 % |
35
-
36
-
37
- ### Connections 50
38
-
39
- | Tests | Samples | Result | Tolerance | Difference with slowest |
40
- |---------------------|---------|------------------|-----------|-------------------------|
41
- | undici - fetch | 30 | 2107.19 req/sec | ± 2.69 % | - |
42
- | http - no keepalive | 10 | 2698.90 req/sec | ± 2.68 % | + 28.08 % |
43
- | http - keepalive | 10 | 4639.49 req/sec | ± 2.55 % | + 120.17 % |
44
- | undici - pipeline | 40 | 6123.33 req/sec | ± 2.97 % | + 190.59 % |
45
- | undici - stream | 50 | 9426.51 req/sec | ± 2.92 % | + 347.35 % |
46
- | undici - request | 10 | 10162.88 req/sec | ± 2.13 % | + 382.29 % |
47
- | undici - dispatch | 50 | 11191.11 req/sec | ± 2.98 % | + 431.09 % |
48
-
21
+ 50 TCP connections with a pipelining depth of 10 running on Node 20.10.0.
22
+
23
+ Tests │ Samples │ Result │ Tolerance │ Difference with slowest │
24
+ |─────────────────────|─────────|─────────────────|───────────|─────────────────────────|
25
+ │ got │ 45 │ 1661.71 req/sec │ ± 2.93 % │ - │
26
+ node-fetch │ 20 2164.81 req/sec │ ± 2.63 % │ + 30.28 % │
27
+ │ undici - fetch │ 35 │ 2274.27 req/sec │ ± 2.70 % │ + 36.86 % │
28
+ http - no keepalive 15 │ 2376.04 req/sec ± 2.99 % │ + 42.99 % │
29
+ axios │ 25 2612.93 req/sec ± 2.89 % + 57.24 %
30
+ request │ 40 2712.19 req/sec ± 2.92 % + 63.22 %
31
+ http - keepalive │ 45 4393.25 req/sec ± 2.86 % + 164.38 %
32
+ undici - pipeline │ 45 5484.69 req/sec ± 2.87 % + 230.06 %
33
+ undici - request 55 7773.98 req/sec ± 2.93 % + 367.83 %
34
+ undici - stream │ 70 8425.96 req/sec ± 2.91 % + 407.07 %
35
+ │ undici - dispatch │ 50 │ 9488.99 req/sec │ ± 2.85 % │ + 471.04 % │
49
36
 
50
37
  ## Quick Start
51
38
 
@@ -62,9 +49,7 @@ const {
62
49
  console.log('response received', statusCode)
63
50
  console.log('headers', headers)
64
51
 
65
- for await (const data of body) {
66
- console.log('data', data)
67
- }
52
+ for await (const data of body) { console.log('data', data) }
68
53
 
69
54
  console.log('trailers', trailers)
70
55
  ```
@@ -119,7 +104,7 @@ Returns a promise with the result of the `Dispatcher.request` method.
119
104
 
120
105
  Calls `options.dispatcher.request(options)`.
121
106
 
122
- See [Dispatcher.request](./docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details.
107
+ See [Dispatcher.request](./docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details, and [request examples](./examples/README.md) for examples.
123
108
 
124
109
  ### `undici.stream([url, options, ]factory): Promise`
125
110
 
@@ -0,0 +1,62 @@
1
+ # Debug
2
+
3
+ Undici (and subsenquently `fetch` and `websocket`) exposes a debug statement that can be enabled by setting `NODE_DEBUG` within the environment.
4
+
5
+ The flags availabile are:
6
+
7
+ ## `undici`
8
+
9
+ This flag enables debug statements for the core undici library.
10
+
11
+ ```sh
12
+ NODE_DEBUG=undici node script.js
13
+
14
+ UNDICI 16241: connecting to nodejs.org using https:h1
15
+ UNDICI 16241: connecting to nodejs.org using https:h1
16
+ UNDICI 16241: connected to nodejs.org using https:h1
17
+ UNDICI 16241: sending request to GET https://nodejs.org//
18
+ UNDICI 16241: received response to GET https://nodejs.org// - HTTP 307
19
+ UNDICI 16241: connecting to nodejs.org using https:h1
20
+ UNDICI 16241: trailers received from GET https://nodejs.org//
21
+ UNDICI 16241: connected to nodejs.org using https:h1
22
+ UNDICI 16241: sending request to GET https://nodejs.org//en
23
+ UNDICI 16241: received response to GET https://nodejs.org//en - HTTP 200
24
+ UNDICI 16241: trailers received from GET https://nodejs.org//en
25
+ ```
26
+
27
+ ## `fetch`
28
+
29
+ This flag enables debug statements for the `fetch` API.
30
+
31
+ > **Note**: statements are pretty similar to the ones in the `undici` flag, but scoped to `fetch`
32
+
33
+ ```sh
34
+ NODE_DEBUG=fetch node script.js
35
+
36
+ FETCH 16241: connecting to nodejs.org using https:h1
37
+ FETCH 16241: connecting to nodejs.org using https:h1
38
+ FETCH 16241: connected to nodejs.org using https:h1
39
+ FETCH 16241: sending request to GET https://nodejs.org//
40
+ FETCH 16241: received response to GET https://nodejs.org// - HTTP 307
41
+ FETCH 16241: connecting to nodejs.org using https:h1
42
+ FETCH 16241: trailers received from GET https://nodejs.org//
43
+ FETCH 16241: connected to nodejs.org using https:h1
44
+ FETCH 16241: sending request to GET https://nodejs.org//en
45
+ FETCH 16241: received response to GET https://nodejs.org//en - HTTP 200
46
+ FETCH 16241: trailers received from GET https://nodejs.org//en
47
+ ```
48
+
49
+ ## `websocket`
50
+
51
+ This flag enables debug statements for the `Websocket` API.
52
+
53
+ > **Note**: statements can overlap with `UNDICI` ones if `undici` or `fetch` flag has been enabled as well.
54
+
55
+ ```sh
56
+ NODE_DEBUG=websocket node script.js
57
+
58
+ WEBSOCKET 18309: connecting to echo.websocket.org using https:h1
59
+ WEBSOCKET 18309: connected to echo.websocket.org using https:h1
60
+ WEBSOCKET 18309: sending request to GET https://echo.websocket.org//
61
+ WEBSOCKET 18309: connection opened <ip_address>
62
+ ```
@@ -105,7 +105,7 @@ You can not assume that this event is related to any specific request.
105
105
  import diagnosticsChannel from 'diagnostics_channel'
106
106
 
107
107
  diagnosticsChannel.channel('undici:client:beforeConnect').subscribe(({ connectParams, connector }) => {
108
- // const { host, hostname, protocol, port, servername } = connectParams
108
+ // const { host, hostname, protocol, port, servername, version } = connectParams
109
109
  // connector is a function that creates the socket
110
110
  })
111
111
  ```
@@ -118,7 +118,7 @@ This message is published after a connection is established.
118
118
  import diagnosticsChannel from 'diagnostics_channel'
119
119
 
120
120
  diagnosticsChannel.channel('undici:client:connected').subscribe(({ socket, connectParams, connector }) => {
121
- // const { host, hostname, protocol, port, servername } = connectParams
121
+ // const { host, hostname, protocol, port, servername, version } = connectParams
122
122
  // connector is a function that creates the socket
123
123
  })
124
124
  ```
@@ -131,7 +131,7 @@ This message is published if it did not succeed to create new connection
131
131
  import diagnosticsChannel from 'diagnostics_channel'
132
132
 
133
133
  diagnosticsChannel.channel('undici:client:connectError').subscribe(({ error, socket, connectParams, connector }) => {
134
- // const { host, hostname, protocol, port, servername } = connectParams
134
+ // const { host, hostname, protocol, port, servername, version } = connectParams
135
135
  // connector is a function that creates the socket
136
136
  console.log(`Connect failed with ${error.message}`)
137
137
  })
@@ -0,0 +1,96 @@
1
+ # Class: RedirectHandler
2
+
3
+ A class that handles redirection logic for HTTP requests.
4
+
5
+ ## `new RedirectHandler(dispatch, maxRedirections, opts, handler, redirectionLimitReached)`
6
+
7
+ Arguments:
8
+
9
+ - **dispatch** `function` - The dispatch function to be called after every retry.
10
+ - **maxRedirections** `number` - Maximum number of redirections allowed.
11
+ - **opts** `object` - Options for handling redirection.
12
+ - **handler** `object` - An object containing handlers for different stages of the request lifecycle.
13
+ - **redirectionLimitReached** `boolean` (default: `false`) - A flag that the implementer can provide to enable or disable the feature. If set to `false`, it indicates that the caller doesn't want to use the feature and prefers the old behavior.
14
+
15
+ Returns: `RedirectHandler`
16
+
17
+ ### Parameters
18
+
19
+ - **dispatch** `(options: Dispatch.DispatchOptions, handlers: Dispatch.DispatchHandlers) => Promise<Dispatch.DispatchResponse>` (required) - Dispatch function to be called after every redirection.
20
+ - **maxRedirections** `number` (required) - Maximum number of redirections allowed.
21
+ - **opts** `object` (required) - Options for handling redirection.
22
+ - **handler** `object` (required) - Handlers for different stages of the request lifecycle.
23
+ - **redirectionLimitReached** `boolean` (default: `false`) - A flag that the implementer can provide to enable or disable the feature. If set to `false`, it indicates that the caller doesn't want to use the feature and prefers the old behavior.
24
+
25
+ ### Properties
26
+
27
+ - **location** `string` - The current redirection location.
28
+ - **abort** `function` - The abort function.
29
+ - **opts** `object` - The options for handling redirection.
30
+ - **maxRedirections** `number` - Maximum number of redirections allowed.
31
+ - **handler** `object` - Handlers for different stages of the request lifecycle.
32
+ - **history** `Array` - An array representing the history of URLs during redirection.
33
+ - **redirectionLimitReached** `boolean` - Indicates whether the redirection limit has been reached.
34
+
35
+ ### Methods
36
+
37
+ #### `onConnect(abort)`
38
+
39
+ Called when the connection is established.
40
+
41
+ Parameters:
42
+
43
+ - **abort** `function` - The abort function.
44
+
45
+ #### `onUpgrade(statusCode, headers, socket)`
46
+
47
+ Called when an upgrade is requested.
48
+
49
+ Parameters:
50
+
51
+ - **statusCode** `number` - The HTTP status code.
52
+ - **headers** `object` - The headers received in the response.
53
+ - **socket** `object` - The socket object.
54
+
55
+ #### `onError(error)`
56
+
57
+ Called when an error occurs.
58
+
59
+ Parameters:
60
+
61
+ - **error** `Error` - The error that occurred.
62
+
63
+ #### `onHeaders(statusCode, headers, resume, statusText)`
64
+
65
+ Called when headers are received.
66
+
67
+ Parameters:
68
+
69
+ - **statusCode** `number` - The HTTP status code.
70
+ - **headers** `object` - The headers received in the response.
71
+ - **resume** `function` - The resume function.
72
+ - **statusText** `string` - The status text.
73
+
74
+ #### `onData(chunk)`
75
+
76
+ Called when data is received.
77
+
78
+ Parameters:
79
+
80
+ - **chunk** `Buffer` - The data chunk received.
81
+
82
+ #### `onComplete(trailers)`
83
+
84
+ Called when the request is complete.
85
+
86
+ Parameters:
87
+
88
+ - **trailers** `object` - The trailers received.
89
+
90
+ #### `onBodySent(chunk)`
91
+
92
+ Called when the request body is sent.
93
+
94
+ Parameters:
95
+
96
+ - **chunk** `Buffer` - The chunk of the request body sent.
package/index-fetch.js CHANGED
@@ -4,7 +4,9 @@ const fetchImpl = require('./lib/fetch').fetch
4
4
 
5
5
  module.exports.fetch = function fetch (resource, init = undefined) {
6
6
  return fetchImpl(resource, init).catch((err) => {
7
- Error.captureStackTrace(err, this)
7
+ if (typeof err === 'object') {
8
+ Error.captureStackTrace(err, this)
9
+ }
8
10
  throw err
9
11
  })
10
12
  }
package/index.js CHANGED
@@ -21,14 +21,6 @@ const DecoratorHandler = require('./lib/handler/DecoratorHandler')
21
21
  const RedirectHandler = require('./lib/handler/RedirectHandler')
22
22
  const createRedirectInterceptor = require('./lib/interceptor/redirectInterceptor')
23
23
 
24
- let hasCrypto
25
- try {
26
- require('crypto')
27
- hasCrypto = true
28
- } catch {
29
- hasCrypto = false
30
- }
31
-
32
24
  Object.assign(Dispatcher.prototype, api)
33
25
 
34
26
  module.exports.Dispatcher = Dispatcher
@@ -102,14 +94,10 @@ function makeDispatcher (fn) {
102
94
  module.exports.setGlobalDispatcher = setGlobalDispatcher
103
95
  module.exports.getGlobalDispatcher = getGlobalDispatcher
104
96
 
105
- let fetchImpl = null
106
- module.exports.fetch = async function fetch (resource) {
107
- if (!fetchImpl) {
108
- fetchImpl = require('./lib/fetch').fetch
109
- }
110
-
97
+ const fetchImpl = require('./lib/fetch').fetch
98
+ module.exports.fetch = async function fetch (init, options = undefined) {
111
99
  try {
112
- return await fetchImpl(...arguments)
100
+ return await fetchImpl(init, options)
113
101
  } catch (err) {
114
102
  if (typeof err === 'object') {
115
103
  Error.captureStackTrace(err, this)
@@ -149,11 +137,7 @@ const { parseMIMEType, serializeAMimeType } = require('./lib/fetch/dataURL')
149
137
  module.exports.parseMIMEType = parseMIMEType
150
138
  module.exports.serializeAMimeType = serializeAMimeType
151
139
 
152
- if (hasCrypto) {
153
- const { WebSocket } = require('./lib/websocket/websocket')
154
-
155
- module.exports.WebSocket = WebSocket
156
- }
140
+ module.exports.WebSocket = require('./lib/websocket/websocket').WebSocket
157
141
 
158
142
  module.exports.request = makeDispatcher(api.request)
159
143
  module.exports.stream = makeDispatcher(api.stream)
@@ -6,9 +6,9 @@ const kSignal = Symbol('kSignal')
6
6
 
7
7
  function abort (self) {
8
8
  if (self.abort) {
9
- self.abort()
9
+ self.abort(self[kSignal]?.reason)
10
10
  } else {
11
- self.onError(new RequestAbortedError())
11
+ self.onError(self[kSignal]?.reason ?? new RequestAbortedError())
12
12
  }
13
13
  }
14
14
 
@@ -6,8 +6,9 @@ const { kEnumerableProperty, isDisturbed } = require('../core/util')
6
6
  const { kHeadersList } = require('../core/symbols')
7
7
  const { webidl } = require('../fetch/webidl')
8
8
  const { Response, cloneResponse } = require('../fetch/response')
9
- const { Request } = require('../fetch/request')
10
- const { kState, kHeaders, kGuard, kRealm } = require('../fetch/symbols')
9
+ const { Request, fromInnerRequest } = require('../fetch/request')
10
+ const { Headers } = require('../fetch/headers')
11
+ const { kState, kHeaders, kGuard } = require('../fetch/symbols')
11
12
  const { fetching } = require('../fetch/index')
12
13
  const { urlIsHttpHttpsScheme, createDeferredPromise, readAllBytes } = require('../fetch/util')
13
14
  const assert = require('assert')
@@ -49,7 +50,7 @@ class Cache {
49
50
  request = webidl.converters.RequestInfo(request)
50
51
  options = webidl.converters.CacheQueryOptions(options)
51
52
 
52
- const p = await this.matchAll(request, options)
53
+ const p = this.#internalMatchAll(request, options, 1)
53
54
 
54
55
  if (p.length === 0) {
55
56
  return
@@ -64,66 +65,7 @@ class Cache {
64
65
  if (request !== undefined) request = webidl.converters.RequestInfo(request)
65
66
  options = webidl.converters.CacheQueryOptions(options)
66
67
 
67
- // 1.
68
- let r = null
69
-
70
- // 2.
71
- if (request !== undefined) {
72
- if (request instanceof Request) {
73
- // 2.1.1
74
- r = request[kState]
75
-
76
- // 2.1.2
77
- if (r.method !== 'GET' && !options.ignoreMethod) {
78
- return []
79
- }
80
- } else if (typeof request === 'string') {
81
- // 2.2.1
82
- r = new Request(request)[kState]
83
- }
84
- }
85
-
86
- // 5.
87
- // 5.1
88
- const responses = []
89
-
90
- // 5.2
91
- if (request === undefined) {
92
- // 5.2.1
93
- for (const requestResponse of this.#relevantRequestResponseList) {
94
- responses.push(requestResponse[1])
95
- }
96
- } else { // 5.3
97
- // 5.3.1
98
- const requestResponses = this.#queryCache(r, options)
99
-
100
- // 5.3.2
101
- for (const requestResponse of requestResponses) {
102
- responses.push(requestResponse[1])
103
- }
104
- }
105
-
106
- // 5.4
107
- // We don't implement CORs so we don't need to loop over the responses, yay!
108
-
109
- // 5.5.1
110
- const responseList = []
111
-
112
- // 5.5.2
113
- for (const response of responses) {
114
- // 5.5.2.1
115
- const responseObject = new Response(response.body?.source ?? null)
116
- const body = responseObject[kState].body
117
- responseObject[kState] = response
118
- responseObject[kState].body = body
119
- responseObject[kHeaders][kHeadersList] = response.headersList
120
- responseObject[kHeaders][kGuard] = 'immutable'
121
-
122
- responseList.push(responseObject)
123
- }
124
-
125
- // 6.
126
- return Object.freeze(responseList)
68
+ return this.#internalMatchAll(request, options)
127
69
  }
128
70
 
129
71
  async add (request) {
@@ -146,8 +88,6 @@ class Cache {
146
88
  webidl.brandCheck(this, Cache)
147
89
  webidl.argumentLengthCheck(arguments, 1, { header: 'Cache.addAll' })
148
90
 
149
- requests = webidl.converters['sequence<RequestInfo>'](requests)
150
-
151
91
  // 1.
152
92
  const responsePromises = []
153
93
 
@@ -155,7 +95,17 @@ class Cache {
155
95
  const requestList = []
156
96
 
157
97
  // 3.
158
- for (const request of requests) {
98
+ for (let request of requests) {
99
+ if (request === undefined) {
100
+ throw webidl.errors.conversionFailed({
101
+ prefix: 'Cache.addAll',
102
+ argument: 'Argument 1',
103
+ types: ['undefined is not allowed']
104
+ })
105
+ }
106
+
107
+ request = webidl.converters.RequestInfo(request)
108
+
159
109
  if (typeof request === 'string') {
160
110
  continue
161
111
  }
@@ -494,7 +444,7 @@ class Cache {
494
444
  * @see https://w3c.github.io/ServiceWorker/#dom-cache-keys
495
445
  * @param {any} request
496
446
  * @param {import('../../types/cache').CacheQueryOptions} options
497
- * @returns {readonly Request[]}
447
+ * @returns {Promise<readonly Request[]>}
498
448
  */
499
449
  async keys (request = undefined, options = {}) {
500
450
  webidl.brandCheck(this, Cache)
@@ -553,12 +503,12 @@ class Cache {
553
503
 
554
504
  // 5.4.2
555
505
  for (const request of requests) {
556
- const requestObject = new Request('https://a')
557
- requestObject[kState] = request
558
- requestObject[kHeaders][kHeadersList] = request.headersList
559
- requestObject[kHeaders][kGuard] = 'immutable'
560
- requestObject[kRealm] = request.client
561
-
506
+ const requestObject = fromInnerRequest(
507
+ request,
508
+ new AbortController().signal,
509
+ 'immutable',
510
+ { settingsObject: request.client }
511
+ )
562
512
  // 5.4.2.1
563
513
  requestList.push(requestObject)
564
514
  }
@@ -783,6 +733,72 @@ class Cache {
783
733
 
784
734
  return true
785
735
  }
736
+
737
+ #internalMatchAll (request, options, maxResponses = Infinity) {
738
+ // 1.
739
+ let r = null
740
+
741
+ // 2.
742
+ if (request !== undefined) {
743
+ if (request instanceof Request) {
744
+ // 2.1.1
745
+ r = request[kState]
746
+
747
+ // 2.1.2
748
+ if (r.method !== 'GET' && !options.ignoreMethod) {
749
+ return []
750
+ }
751
+ } else if (typeof request === 'string') {
752
+ // 2.2.1
753
+ r = new Request(request)[kState]
754
+ }
755
+ }
756
+
757
+ // 5.
758
+ // 5.1
759
+ const responses = []
760
+
761
+ // 5.2
762
+ if (request === undefined) {
763
+ // 5.2.1
764
+ for (const requestResponse of this.#relevantRequestResponseList) {
765
+ responses.push(requestResponse[1])
766
+ }
767
+ } else { // 5.3
768
+ // 5.3.1
769
+ const requestResponses = this.#queryCache(r, options)
770
+
771
+ // 5.3.2
772
+ for (const requestResponse of requestResponses) {
773
+ responses.push(requestResponse[1])
774
+ }
775
+ }
776
+
777
+ // 5.4
778
+ // We don't implement CORs so we don't need to loop over the responses, yay!
779
+
780
+ // 5.5.1
781
+ const responseList = []
782
+
783
+ // 5.5.2
784
+ for (const response of responses) {
785
+ // 5.5.2.1
786
+ const responseObject = new Response(kConstruct)
787
+ responseObject[kState] = response
788
+ responseObject[kHeaders] = new Headers(kConstruct)
789
+ responseObject[kHeaders][kHeadersList] = response.headersList
790
+ responseObject[kHeaders][kGuard] = 'immutable'
791
+
792
+ responseList.push(responseObject.clone())
793
+
794
+ if (responseList.length >= maxResponses) {
795
+ break
796
+ }
797
+ }
798
+
799
+ // 6.
800
+ return Object.freeze(responseList)
801
+ }
786
802
  }
787
803
 
788
804
  Object.defineProperties(Cache.prototype, {
@@ -114,7 +114,7 @@ class CacheStorage {
114
114
 
115
115
  /**
116
116
  * @see https://w3c.github.io/ServiceWorker/#cache-storage-keys
117
- * @returns {string[]}
117
+ * @returns {Promise<string[]>}
118
118
  */
119
119
  async keys () {
120
120
  webidl.brandCheck(this, CacheStorage)
package/lib/client.js CHANGED
@@ -9,6 +9,7 @@ const net = require('net')
9
9
  const http = require('http')
10
10
  const { pipeline } = require('stream')
11
11
  const util = require('./core/util')
12
+ const { channels } = require('./core/diagnostics')
12
13
  const timers = require('./timers')
13
14
  const Request = require('./core/request')
14
15
  const DispatcherBase = require('./dispatcher-base')
@@ -108,21 +109,6 @@ const FastBuffer = Buffer[Symbol.species]
108
109
 
109
110
  const kClosedResolve = Symbol('kClosedResolve')
110
111
 
111
- const channels = {}
112
-
113
- try {
114
- const diagnosticsChannel = require('diagnostics_channel')
115
- channels.sendHeaders = diagnosticsChannel.channel('undici:client:sendHeaders')
116
- channels.beforeConnect = diagnosticsChannel.channel('undici:client:beforeConnect')
117
- channels.connectError = diagnosticsChannel.channel('undici:client:connectError')
118
- channels.connected = diagnosticsChannel.channel('undici:client:connected')
119
- } catch {
120
- channels.sendHeaders = { hasSubscribers: false }
121
- channels.beforeConnect = { hasSubscribers: false }
122
- channels.connectError = { hasSubscribers: false }
123
- channels.connected = { hasSubscribers: false }
124
- }
125
-
126
112
  /**
127
113
  * @type {import('../types/client').default}
128
114
  */
@@ -1191,6 +1177,7 @@ async function connect (client) {
1191
1177
  hostname,
1192
1178
  protocol,
1193
1179
  port,
1180
+ version: client[kHTTPConnVersion],
1194
1181
  servername: client[kServerName],
1195
1182
  localAddress: client[kLocalAddress]
1196
1183
  },
@@ -1284,6 +1271,7 @@ async function connect (client) {
1284
1271
  hostname,
1285
1272
  protocol,
1286
1273
  port,
1274
+ version: client[kHTTPConnVersion],
1287
1275
  servername: client[kServerName],
1288
1276
  localAddress: client[kLocalAddress]
1289
1277
  },
@@ -1306,6 +1294,7 @@ async function connect (client) {
1306
1294
  hostname,
1307
1295
  protocol,
1308
1296
  port,
1297
+ version: client[kHTTPConnVersion],
1309
1298
  servername: client[kServerName],
1310
1299
  localAddress: client[kLocalAddress]
1311
1300
  },
@@ -1658,19 +1647,6 @@ function writeH2 (client, session, request) {
1658
1647
  return false
1659
1648
  }
1660
1649
 
1661
- try {
1662
- // TODO(HTTP/2): Should we call onConnect immediately or on stream ready event?
1663
- request.onConnect((err) => {
1664
- if (request.aborted || request.completed) {
1665
- return
1666
- }
1667
-
1668
- errorRequest(client, request, err || new RequestAbortedError())
1669
- })
1670
- } catch (err) {
1671
- errorRequest(client, request, err)
1672
- }
1673
-
1674
1650
  if (request.aborted) {
1675
1651
  return false
1676
1652
  }
@@ -1682,9 +1658,34 @@ function writeH2 (client, session, request) {
1682
1658
  headers[HTTP2_HEADER_AUTHORITY] = host || client[kHost]
1683
1659
  headers[HTTP2_HEADER_METHOD] = method
1684
1660
 
1661
+ try {
1662
+ // We are already connected, streams are pending.
1663
+ // We can call on connect, and wait for abort
1664
+ request.onConnect((err) => {
1665
+ if (request.aborted || request.completed) {
1666
+ return
1667
+ }
1668
+
1669
+ err = err || new RequestAbortedError()
1670
+
1671
+ if (stream != null) {
1672
+ util.destroy(stream, err)
1673
+
1674
+ h2State.openStreams -= 1
1675
+ if (h2State.openStreams === 0) {
1676
+ session.unref()
1677
+ }
1678
+ }
1679
+
1680
+ errorRequest(client, request, err)
1681
+ })
1682
+ } catch (err) {
1683
+ errorRequest(client, request, err)
1684
+ }
1685
+
1685
1686
  if (method === 'CONNECT') {
1686
1687
  session.ref()
1687
- // we are already connected, streams are pending, first request
1688
+ // We are already connected, streams are pending, first request
1688
1689
  // will create a new stream. We trigger a request to create the stream and wait until
1689
1690
  // `ready` event is triggered
1690
1691
  // We disabled endStream to allow the user to write to the stream
@@ -1768,7 +1769,7 @@ function writeH2 (client, session, request) {
1768
1769
 
1769
1770
  session.ref()
1770
1771
 
1771
- const shouldEndStream = method === 'GET' || method === 'HEAD'
1772
+ const shouldEndStream = method === 'GET' || method === 'HEAD' || body === null
1772
1773
  if (expectContinue) {
1773
1774
  headers[HTTP2_HEADER_EXPECT] = '100-continue'
1774
1775
  stream = session.request(headers, { endStream: shouldEndStream, signal })
@@ -28,6 +28,8 @@ class CompatFinalizer {
28
28
  })
29
29
  }
30
30
  }
31
+
32
+ unregister (key) {}
31
33
  }
32
34
 
33
35
  module.exports = function () {