undici 7.0.0-alpha.2 → 7.0.0-alpha.3

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 (37) hide show
  1. package/README.md +1 -1
  2. package/docs/docs/api/CacheStore.md +116 -0
  3. package/docs/docs/api/Dispatcher.md +10 -0
  4. package/index.js +6 -1
  5. package/lib/api/api-request.js +1 -1
  6. package/lib/api/readable.js +6 -6
  7. package/lib/cache/memory-cache-store.js +417 -0
  8. package/lib/core/constants.js +24 -1
  9. package/lib/core/util.js +41 -2
  10. package/lib/dispatcher/client-h1.js +100 -87
  11. package/lib/dispatcher/client-h2.js +127 -75
  12. package/lib/dispatcher/pool-base.js +3 -3
  13. package/lib/handler/cache-handler.js +359 -0
  14. package/lib/handler/cache-revalidation-handler.js +119 -0
  15. package/lib/interceptor/cache.js +171 -0
  16. package/lib/util/cache.js +224 -0
  17. package/lib/web/cache/cache.js +1 -0
  18. package/lib/web/cache/cachestorage.js +2 -0
  19. package/lib/web/eventsource/eventsource.js +2 -0
  20. package/lib/web/fetch/constants.js +12 -5
  21. package/lib/web/fetch/data-url.js +2 -2
  22. package/lib/web/fetch/formdata.js +3 -1
  23. package/lib/web/fetch/headers.js +2 -0
  24. package/lib/web/fetch/request.js +3 -1
  25. package/lib/web/fetch/response.js +3 -1
  26. package/lib/web/fetch/util.js +171 -47
  27. package/lib/web/fetch/webidl.js +16 -12
  28. package/lib/web/websocket/constants.js +67 -6
  29. package/lib/web/websocket/events.js +4 -0
  30. package/lib/web/websocket/stream/websocketerror.js +1 -1
  31. package/lib/web/websocket/websocket.js +2 -0
  32. package/package.json +7 -3
  33. package/types/cache-interceptor.d.ts +97 -0
  34. package/types/fetch.d.ts +9 -8
  35. package/types/index.d.ts +3 -0
  36. package/types/interceptors.d.ts +4 -0
  37. package/types/webidl.d.ts +7 -1
package/lib/core/util.js CHANGED
@@ -10,7 +10,7 @@ const nodeUtil = require('node:util')
10
10
  const { stringify } = require('node:querystring')
11
11
  const { EventEmitter: EE } = require('node:events')
12
12
  const { InvalidArgumentError } = require('./errors')
13
- const { headerNameLowerCasedRecord } = require('./constants')
13
+ const { headerNameLowerCasedRecord, getHeaderNameAsBuffer } = require('./constants')
14
14
  const { tree } = require('./tree')
15
15
 
16
16
  const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(v => Number(v))
@@ -436,6 +436,44 @@ function parseHeaders (headers, obj) {
436
436
  return obj
437
437
  }
438
438
 
439
+ /**
440
+ * @param {Record<string, string | string[]>} headers
441
+ * @returns {(Buffer | Buffer[])[]}
442
+ */
443
+ function encodeHeaders (headers) {
444
+ const headerNames = Object.keys(headers)
445
+
446
+ /**
447
+ * @type {Buffer[]|Buffer[][]}
448
+ */
449
+ const rawHeaders = new Array(headerNames.length * 2)
450
+
451
+ let rawHeadersIndex = 0
452
+ for (const header of headerNames) {
453
+ let rawValue
454
+ const value = headers[header]
455
+ if (Array.isArray(value)) {
456
+ rawValue = new Array(value.length)
457
+
458
+ for (let i = 0; i < value.length; i++) {
459
+ rawValue[i] = Buffer.from(value[i])
460
+ }
461
+ } else {
462
+ rawValue = Buffer.from(value)
463
+ }
464
+
465
+ const headerBuffer = getHeaderNameAsBuffer(header)
466
+
467
+ rawHeaders[rawHeadersIndex] = headerBuffer
468
+ rawHeadersIndex++
469
+
470
+ rawHeaders[rawHeadersIndex] = rawValue
471
+ rawHeadersIndex++
472
+ }
473
+
474
+ return rawHeaders
475
+ }
476
+
439
477
  /**
440
478
  * @param {Buffer[]} headers
441
479
  * @returns {string[]}
@@ -864,6 +902,7 @@ module.exports = {
864
902
  errorRequest,
865
903
  parseRawHeaders,
866
904
  parseHeaders,
905
+ encodeHeaders,
867
906
  parseKeepAliveTimeout,
868
907
  destroy,
869
908
  bodyLength,
@@ -885,6 +924,6 @@ module.exports = {
885
924
  isHttpOrHttpsPrefixed,
886
925
  nodeMajor,
887
926
  nodeMinor,
888
- safeHTTPMethods: ['GET', 'HEAD', 'OPTIONS', 'TRACE'],
927
+ safeHTTPMethods: Object.freeze(['GET', 'HEAD', 'OPTIONS', 'TRACE']),
889
928
  wrapRequestBody
890
929
  }
@@ -49,13 +49,13 @@ const {
49
49
  kMaxResponseSize,
50
50
  kOnError,
51
51
  kResume,
52
- kHTTPContext
52
+ kHTTPContext,
53
+ kClosed
53
54
  } = require('../core/symbols.js')
54
55
 
55
56
  const constants = require('../llhttp/constants.js')
56
57
  const EMPTY_BUF = Buffer.alloc(0)
57
58
  const FastBuffer = Buffer[Symbol.species]
58
- const addListener = util.addListener
59
59
  const removeAllListeners = util.removeAllListeners
60
60
 
61
61
  let extractBody
@@ -779,87 +779,13 @@ async function connectH1 (client, socket) {
779
779
  socket[kBlocking] = false
780
780
  socket[kParser] = new Parser(client, socket, llhttpInstance)
781
781
 
782
- addListener(socket, 'error', function (err) {
783
- assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
782
+ util.addListener(socket, 'error', onHttpSocketError)
783
+ util.addListener(socket, 'readable', onHttpSocketReadable)
784
+ util.addListener(socket, 'end', onHttpSocketEnd)
785
+ util.addListener(socket, 'close', onHttpSocketClose)
784
786
 
785
- const parser = this[kParser]
786
-
787
- // On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
788
- // to the user.
789
- if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) {
790
- // We treat all incoming data so for as a valid response.
791
- parser.onMessageComplete()
792
- return
793
- }
794
-
795
- this[kError] = err
796
-
797
- this[kClient][kOnError](err)
798
- })
799
- addListener(socket, 'readable', function () {
800
- this[kParser]?.readMore()
801
- })
802
- addListener(socket, 'end', function () {
803
- const parser = this[kParser]
804
-
805
- if (parser.statusCode && !parser.shouldKeepAlive) {
806
- // We treat all incoming data so far as a valid response.
807
- parser.onMessageComplete()
808
- return
809
- }
810
-
811
- util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this)))
812
- })
813
- addListener(socket, 'close', function () {
814
- const parser = this[kParser]
815
-
816
- if (parser) {
817
- if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {
818
- // We treat all incoming data so far as a valid response.
819
- parser.onMessageComplete()
820
- }
821
-
822
- this[kParser].destroy()
823
- this[kParser] = null
824
- }
825
-
826
- const err = this[kError] || new SocketError('closed', util.getSocketInfo(this))
827
-
828
- const client = this[kClient]
829
-
830
- client[kSocket] = null
831
- client[kHTTPContext] = null // TODO (fix): This is hacky...
832
-
833
- if (client.destroyed) {
834
- assert(client[kPending] === 0)
835
-
836
- // Fail entire queue.
837
- const requests = client[kQueue].splice(client[kRunningIdx])
838
- for (let i = 0; i < requests.length; i++) {
839
- const request = requests[i]
840
- util.errorRequest(client, request, err)
841
- }
842
- } else if (client[kRunning] > 0 && err.code !== 'UND_ERR_INFO') {
843
- // Fail head of pipeline.
844
- const request = client[kQueue][client[kRunningIdx]]
845
- client[kQueue][client[kRunningIdx]++] = null
846
-
847
- util.errorRequest(client, request, err)
848
- }
849
-
850
- client[kPendingIdx] = client[kRunningIdx]
851
-
852
- assert(client[kRunning] === 0)
853
-
854
- client.emit('disconnect', client[kUrl], [client], err)
855
-
856
- client[kResume]()
857
- })
858
-
859
- let closed = false
860
- socket.on('close', () => {
861
- closed = true
862
- })
787
+ socket[kClosed] = false
788
+ socket.on('close', onSocketClose)
863
789
 
864
790
  return {
865
791
  version: 'h1',
@@ -875,7 +801,7 @@ async function connectH1 (client, socket) {
875
801
  * @param {() => void} callback
876
802
  */
877
803
  destroy (err, callback) {
878
- if (closed) {
804
+ if (socket[kClosed]) {
879
805
  queueMicrotask(callback)
880
806
  } else {
881
807
  socket.on('close', callback)
@@ -931,6 +857,90 @@ async function connectH1 (client, socket) {
931
857
  }
932
858
  }
933
859
 
860
+ function onHttpSocketError (err) {
861
+ assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
862
+
863
+ const parser = this[kParser]
864
+
865
+ // On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
866
+ // to the user.
867
+ if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) {
868
+ // We treat all incoming data so for as a valid response.
869
+ parser.onMessageComplete()
870
+ return
871
+ }
872
+
873
+ this[kError] = err
874
+
875
+ this[kClient][kOnError](err)
876
+ }
877
+
878
+ function onHttpSocketReadable () {
879
+ this[kParser]?.readMore()
880
+ }
881
+
882
+ function onHttpSocketEnd () {
883
+ const parser = this[kParser]
884
+
885
+ if (parser.statusCode && !parser.shouldKeepAlive) {
886
+ // We treat all incoming data so far as a valid response.
887
+ parser.onMessageComplete()
888
+ return
889
+ }
890
+
891
+ util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this)))
892
+ }
893
+
894
+ function onHttpSocketClose () {
895
+ const parser = this[kParser]
896
+
897
+ if (parser) {
898
+ if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {
899
+ // We treat all incoming data so far as a valid response.
900
+ parser.onMessageComplete()
901
+ }
902
+
903
+ this[kParser].destroy()
904
+ this[kParser] = null
905
+ }
906
+
907
+ const err = this[kError] || new SocketError('closed', util.getSocketInfo(this))
908
+
909
+ const client = this[kClient]
910
+
911
+ client[kSocket] = null
912
+ client[kHTTPContext] = null // TODO (fix): This is hacky...
913
+
914
+ if (client.destroyed) {
915
+ assert(client[kPending] === 0)
916
+
917
+ // Fail entire queue.
918
+ const requests = client[kQueue].splice(client[kRunningIdx])
919
+ for (let i = 0; i < requests.length; i++) {
920
+ const request = requests[i]
921
+ util.errorRequest(client, request, err)
922
+ }
923
+ } else if (client[kRunning] > 0 && err.code !== 'UND_ERR_INFO') {
924
+ // Fail head of pipeline.
925
+ const request = client[kQueue][client[kRunningIdx]]
926
+ client[kQueue][client[kRunningIdx]++] = null
927
+
928
+ util.errorRequest(client, request, err)
929
+ }
930
+
931
+ client[kPendingIdx] = client[kRunningIdx]
932
+
933
+ assert(client[kRunning] === 0)
934
+
935
+ client.emit('disconnect', client[kUrl], [client], err)
936
+
937
+ client[kResume]()
938
+ }
939
+
940
+ function onSocketClose () {
941
+ this[kClosed] = true
942
+ }
943
+
934
944
  /**
935
945
  * @param {import('./client.js')} client
936
946
  */
@@ -991,7 +1001,10 @@ function writeH1 (client, request) {
991
1001
  const expectsPayload = (
992
1002
  method === 'PUT' ||
993
1003
  method === 'POST' ||
994
- method === 'PATCH'
1004
+ method === 'PATCH' ||
1005
+ method === 'QUERY' ||
1006
+ method === 'PROPFIND' ||
1007
+ method === 'PROPPATCH'
995
1008
  )
996
1009
 
997
1010
  if (util.isFormDataLike(body)) {
@@ -1319,7 +1332,7 @@ function writeBuffer (abort, body, client, request, socket, contentLength, heade
1319
1332
  socket.uncork()
1320
1333
  request.onBodySent(body)
1321
1334
 
1322
- if (!expectsPayload) {
1335
+ if (!expectsPayload && request.reset !== false) {
1323
1336
  socket[kReset] = true
1324
1337
  }
1325
1338
  }
@@ -1360,7 +1373,7 @@ async function writeBlob (abort, body, client, request, socket, contentLength, h
1360
1373
  request.onBodySent(buffer)
1361
1374
  request.onRequestSent()
1362
1375
 
1363
- if (!expectsPayload) {
1376
+ if (!expectsPayload && request.reset !== false) {
1364
1377
  socket[kReset] = true
1365
1378
  }
1366
1379
 
@@ -1487,7 +1500,7 @@ class AsyncWriter {
1487
1500
  socket.cork()
1488
1501
 
1489
1502
  if (bytesWritten === 0) {
1490
- if (!expectsPayload) {
1503
+ if (!expectsPayload && request.reset !== false) {
1491
1504
  socket[kReset] = true
1492
1505
  }
1493
1506
 
@@ -24,7 +24,10 @@ const {
24
24
  kOnError,
25
25
  kMaxConcurrentStreams,
26
26
  kHTTP2Session,
27
- kResume
27
+ kResume,
28
+ kSize,
29
+ kHTTPContext,
30
+ kClosed
28
31
  } = require('../core/symbols.js')
29
32
 
30
33
  const kOpenStreams = Symbol('open streams')
@@ -91,83 +94,37 @@ async function connectH2 (client, socket) {
91
94
  session[kOpenStreams] = 0
92
95
  session[kClient] = client
93
96
  session[kSocket] = socket
97
+ session[kHTTP2Session] = null
94
98
 
95
99
  util.addListener(session, 'error', onHttp2SessionError)
96
100
  util.addListener(session, 'frameError', onHttp2FrameError)
97
101
  util.addListener(session, 'end', onHttp2SessionEnd)
98
- util.addListener(session, 'goaway', onHTTP2GoAway)
99
- util.addListener(session, 'close', function () {
100
- const { [kClient]: client } = this
101
- const { [kSocket]: socket } = client
102
-
103
- const err = this[kSocket][kError] || this[kError] || new SocketError('closed', util.getSocketInfo(socket))
104
-
105
- client[kHTTP2Session] = null
106
-
107
- if (client.destroyed) {
108
- assert(client[kPending] === 0)
109
-
110
- // Fail entire queue.
111
- const requests = client[kQueue].splice(client[kRunningIdx])
112
- for (let i = 0; i < requests.length; i++) {
113
- const request = requests[i]
114
- util.errorRequest(client, request, err)
115
- }
116
- }
117
- })
102
+ util.addListener(session, 'goaway', onHttp2SessionGoAway)
103
+ util.addListener(session, 'close', onHttp2SessionClose)
118
104
 
119
105
  session.unref()
120
106
 
121
107
  client[kHTTP2Session] = session
122
108
  socket[kHTTP2Session] = session
123
109
 
124
- util.addListener(socket, 'error', function (err) {
125
- assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
110
+ util.addListener(socket, 'error', onHttp2SocketError)
111
+ util.addListener(socket, 'end', onHttp2SocketEnd)
112
+ util.addListener(socket, 'close', onHttp2SocketClose)
126
113
 
127
- this[kError] = err
128
-
129
- this[kClient][kOnError](err)
130
- })
131
-
132
- util.addListener(socket, 'end', function () {
133
- util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this)))
134
- })
135
-
136
- util.addListener(socket, 'close', function () {
137
- const err = this[kError] || new SocketError('closed', util.getSocketInfo(this))
138
-
139
- client[kSocket] = null
140
-
141
- if (this[kHTTP2Session] != null) {
142
- this[kHTTP2Session].destroy(err)
143
- }
144
-
145
- client[kPendingIdx] = client[kRunningIdx]
146
-
147
- assert(client[kRunning] === 0)
148
-
149
- client.emit('disconnect', client[kUrl], [client], err)
150
-
151
- client[kResume]()
152
- })
153
-
154
- let closed = false
155
- socket.on('close', () => {
156
- closed = true
157
- })
114
+ socket[kClosed] = false
115
+ socket.on('close', onSocketClose)
158
116
 
159
117
  return {
160
118
  version: 'h2',
161
119
  defaultPipelining: Infinity,
162
- write (...args) {
163
- // TODO (fix): return
164
- writeH2(client, ...args)
120
+ write (request) {
121
+ return writeH2(client, request)
165
122
  },
166
123
  resume () {
167
-
124
+ resumeH2(client)
168
125
  },
169
126
  destroy (err, callback) {
170
- if (closed) {
127
+ if (socket[kClosed]) {
171
128
  queueMicrotask(callback)
172
129
  } else {
173
130
  // Destroying the socket will trigger the session close
@@ -183,6 +140,20 @@ async function connectH2 (client, socket) {
183
140
  }
184
141
  }
185
142
 
143
+ function resumeH2 (client) {
144
+ const socket = client[kSocket]
145
+
146
+ if (socket?.destroyed === false) {
147
+ if (client[kSize] === 0 && client[kMaxConcurrentStreams] === 0) {
148
+ socket.unref()
149
+ client[kHTTP2Session].unref()
150
+ } else {
151
+ socket.ref()
152
+ client[kHTTP2Session].ref()
153
+ }
154
+ }
155
+ }
156
+
186
157
  function onHttp2SessionError (err) {
187
158
  assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
188
159
 
@@ -208,19 +179,93 @@ function onHttp2SessionEnd () {
208
179
  * This is the root cause of #3011
209
180
  * We need to handle GOAWAY frames properly, and trigger the session close
210
181
  * along with the socket right away
182
+ *
183
+ * @this {import('http2').ClientHttp2Session}
184
+ * @param {number} errorCode
211
185
  */
212
- function onHTTP2GoAway (code) {
213
- const err = new RequestAbortedError(`HTTP/2: "GOAWAY" frame received with code ${code}`)
186
+ function onHttp2SessionGoAway (errorCode) {
187
+ // We cannot recover, so best to close the session and the socket
188
+ const err = this[kError] || new SocketError(`HTTP/2: "GOAWAY" frame received with code ${errorCode}`, util.getSocketInfo(this[kSocket]))
189
+ const client = this[kClient]
214
190
 
215
- // We need to trigger the close cycle right away
216
- // We need to destroy the session and the socket
217
- // Requests should be failed with the error after the current one is handled
218
- this[kSocket][kError] = err
219
- this[kClient][kOnError](err)
191
+ client[kSocket] = null
192
+ client[kHTTPContext] = null
220
193
 
221
- this.unref()
194
+ if (this[kHTTP2Session] !== null) {
195
+ this[kHTTP2Session].destroy(err)
196
+ this[kHTTP2Session] = null
197
+ }
222
198
 
223
199
  util.destroy(this[kSocket], err)
200
+
201
+ // Fail head of pipeline.
202
+ const request = client[kQueue][client[kRunningIdx]]
203
+ client[kQueue][client[kRunningIdx]++] = null
204
+ util.errorRequest(client, request, err)
205
+
206
+ client[kPendingIdx] = client[kRunningIdx]
207
+
208
+ assert(client[kRunning] === 0)
209
+
210
+ client.emit('disconnect', client[kUrl], [client], err)
211
+
212
+ client[kResume]()
213
+ }
214
+
215
+ function onHttp2SessionClose () {
216
+ const { [kClient]: client } = this
217
+ const { [kSocket]: socket } = client
218
+
219
+ const err = this[kSocket][kError] || this[kError] || new SocketError('closed', util.getSocketInfo(socket))
220
+
221
+ client[kHTTP2Session] = null
222
+
223
+ if (client.destroyed) {
224
+ assert(client[kPending] === 0)
225
+
226
+ // Fail entire queue.
227
+ const requests = client[kQueue].splice(client[kRunningIdx])
228
+ for (let i = 0; i < requests.length; i++) {
229
+ const request = requests[i]
230
+ util.errorRequest(client, request, err)
231
+ }
232
+ }
233
+ }
234
+
235
+ function onHttp2SocketClose () {
236
+ const err = this[kError] || new SocketError('closed', util.getSocketInfo(this))
237
+
238
+ const client = this[kHTTP2Session][kClient]
239
+
240
+ client[kSocket] = null
241
+
242
+ if (this[kHTTP2Session] !== null) {
243
+ this[kHTTP2Session].destroy(err)
244
+ }
245
+
246
+ client[kPendingIdx] = client[kRunningIdx]
247
+
248
+ assert(client[kRunning] === 0)
249
+
250
+ client.emit('disconnect', client[kUrl], [client], err)
251
+
252
+ client[kResume]()
253
+ }
254
+
255
+ function onHttp2SocketError (err) {
256
+ assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
257
+
258
+ this[kError] = err
259
+
260
+ this[kClient][kOnError](err)
261
+ }
262
+
263
+ function onHttp2SocketEnd () {
264
+ util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this)))
265
+ }
266
+
267
+ function onSocketClose () {
268
+ this[kClosed] = true
224
269
  }
225
270
 
226
271
  // https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2
@@ -237,10 +282,6 @@ function writeH2 (client, request) {
237
282
  return false
238
283
  }
239
284
 
240
- if (request.aborted) {
241
- return false
242
- }
243
-
244
285
  const headers = {}
245
286
  for (let n = 0; n < reqHeaders.length; n += 2) {
246
287
  const key = reqHeaders[n + 0]
@@ -283,6 +324,8 @@ function writeH2 (client, request) {
283
324
  // We do not destroy the socket as we can continue using the session
284
325
  // the stream gets destroyed and the session remains to create new streams
285
326
  util.destroy(body, err)
327
+ client[kQueue][client[kRunningIdx]++] = null
328
+ client[kResume]()
286
329
  }
287
330
 
288
331
  try {
@@ -293,6 +336,10 @@ function writeH2 (client, request) {
293
336
  util.errorRequest(client, request, err)
294
337
  }
295
338
 
339
+ if (request.aborted) {
340
+ return false
341
+ }
342
+
296
343
  if (method === 'CONNECT') {
297
344
  session.ref()
298
345
  // We are already connected, streams are pending, first request
@@ -304,10 +351,12 @@ function writeH2 (client, request) {
304
351
  if (stream.id && !stream.pending) {
305
352
  request.onUpgrade(null, null, stream)
306
353
  ++session[kOpenStreams]
354
+ client[kQueue][client[kRunningIdx]++] = null
307
355
  } else {
308
356
  stream.once('ready', () => {
309
357
  request.onUpgrade(null, null, stream)
310
358
  ++session[kOpenStreams]
359
+ client[kQueue][client[kRunningIdx]++] = null
311
360
  })
312
361
  }
313
362
 
@@ -428,17 +477,20 @@ function writeH2 (client, request) {
428
477
  // Present specially when using pipeline or stream
429
478
  if (stream.state?.state == null || stream.state.state < 6) {
430
479
  request.onComplete([])
431
- return
432
480
  }
433
481
 
434
- // Stream is closed or half-closed-remote (6), decrement counter and cleanup
435
- // It does not have sense to continue working with the stream as we do not
436
- // have yet RST_STREAM support on client-side
437
482
  if (session[kOpenStreams] === 0) {
483
+ // Stream is closed or half-closed-remote (6), decrement counter and cleanup
484
+ // It does not have sense to continue working with the stream as we do not
485
+ // have yet RST_STREAM support on client-side
486
+
438
487
  session.unref()
439
488
  }
440
489
 
441
490
  abort(new InformationalError('HTTP/2: stream half-closed (remote)'))
491
+ client[kQueue][client[kRunningIdx]++] = null
492
+ client[kPendingIdx] = client[kRunningIdx]
493
+ client[kResume]()
442
494
  })
443
495
 
444
496
  stream.once('close', () => {
@@ -113,9 +113,9 @@ class PoolBase extends DispatcherBase {
113
113
 
114
114
  async [kClose] () {
115
115
  if (this[kQueue].isEmpty()) {
116
- return Promise.all(this[kClients].map(c => c.close()))
116
+ await Promise.all(this[kClients].map(c => c.close()))
117
117
  } else {
118
- return new Promise((resolve) => {
118
+ await new Promise((resolve) => {
119
119
  this[kClosedResolve] = resolve
120
120
  })
121
121
  }
@@ -130,7 +130,7 @@ class PoolBase extends DispatcherBase {
130
130
  item.handler.onError(err)
131
131
  }
132
132
 
133
- return Promise.all(this[kClients].map(c => c.destroy(err)))
133
+ await Promise.all(this[kClients].map(c => c.destroy(err)))
134
134
  }
135
135
 
136
136
  [kDispatch] (opts, handler) {