@zuplo/cli 6.73.7 → 6.73.10

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 (204) hide show
  1. package/dist/cli.js +2 -11
  2. package/dist/cli.js.map +1 -1
  3. package/dist/common/cli-dispatcher.d.ts +6 -0
  4. package/dist/common/cli-dispatcher.d.ts.map +1 -0
  5. package/dist/common/cli-dispatcher.js +19 -0
  6. package/dist/common/cli-dispatcher.js.map +1 -0
  7. package/dist/common/cli-dispatcher.test.d.ts +2 -0
  8. package/dist/common/cli-dispatcher.test.d.ts.map +1 -0
  9. package/dist/common/cli-dispatcher.test.js +78 -0
  10. package/dist/common/cli-dispatcher.test.js.map +1 -0
  11. package/dist/tsconfig.tsbuildinfo +1 -1
  12. package/node_modules/@zuplo/core/customer.cli.minified.js +253 -253
  13. package/node_modules/@zuplo/core/index.minified.js +259 -259
  14. package/node_modules/@zuplo/core/package.json +1 -1
  15. package/node_modules/@zuplo/graphql/package.json +1 -1
  16. package/node_modules/@zuplo/openapi-tools/package.json +2 -2
  17. package/node_modules/@zuplo/otel/package.json +1 -1
  18. package/node_modules/@zuplo/runtime/out/esm/{chunk-SCQAHLHW.js → chunk-7POELG2I.js} +3 -3
  19. package/node_modules/@zuplo/runtime/out/esm/{chunk-SCQAHLHW.js.map → chunk-7POELG2I.js.map} +1 -1
  20. package/node_modules/@zuplo/runtime/out/esm/index.js +1 -1
  21. package/node_modules/@zuplo/runtime/out/esm/mcp-gateway/index.js +1 -1
  22. package/node_modules/@zuplo/runtime/package.json +1 -1
  23. package/node_modules/js-yaml/README.md +5 -3
  24. package/node_modules/js-yaml/dist/js-yaml.js +3009 -2402
  25. package/node_modules/js-yaml/dist/js-yaml.js.map +1 -1
  26. package/node_modules/js-yaml/dist/js-yaml.min.js +2 -33
  27. package/node_modules/js-yaml/dist/js-yaml.min.js.map +1 -1
  28. package/node_modules/js-yaml/dist/js-yaml.mjs +3073 -2381
  29. package/node_modules/js-yaml/dist/js-yaml.mjs.map +1 -1
  30. package/node_modules/js-yaml/lib/loader.js +7 -11
  31. package/node_modules/js-yaml/lib/type/float.js +1 -1
  32. package/node_modules/js-yaml/lib/type/int.js +4 -4
  33. package/node_modules/js-yaml/package.json +10 -4
  34. package/node_modules/tar/README.md +34 -6
  35. package/node_modules/tar/dist/commonjs/create.d.ts.map +1 -1
  36. package/node_modules/tar/dist/commonjs/create.js +3 -4
  37. package/node_modules/tar/dist/commonjs/create.js.map +1 -1
  38. package/node_modules/tar/dist/commonjs/cwd-error.d.ts +1 -1
  39. package/node_modules/tar/dist/commonjs/cwd-error.d.ts.map +1 -1
  40. package/node_modules/tar/dist/commonjs/cwd-error.js.map +1 -1
  41. package/node_modules/tar/dist/commonjs/extract.js.map +1 -1
  42. package/node_modules/tar/dist/commonjs/get-write-flag.d.ts.map +1 -1
  43. package/node_modules/tar/dist/commonjs/get-write-flag.js +7 -4
  44. package/node_modules/tar/dist/commonjs/get-write-flag.js.map +1 -1
  45. package/node_modules/tar/dist/commonjs/header.d.ts.map +1 -1
  46. package/node_modules/tar/dist/commonjs/header.js +60 -37
  47. package/node_modules/tar/dist/commonjs/header.js.map +1 -1
  48. package/node_modules/tar/dist/commonjs/index.min.js +3 -3
  49. package/node_modules/tar/dist/commonjs/index.min.js.map +3 -3
  50. package/node_modules/tar/dist/commonjs/large-numbers.d.ts.map +1 -1
  51. package/node_modules/tar/dist/commonjs/large-numbers.js.map +1 -1
  52. package/node_modules/tar/dist/commonjs/list.d.ts +1 -1
  53. package/node_modules/tar/dist/commonjs/list.d.ts.map +1 -1
  54. package/node_modules/tar/dist/commonjs/list.js +2 -7
  55. package/node_modules/tar/dist/commonjs/list.js.map +1 -1
  56. package/node_modules/tar/dist/commonjs/make-command.d.ts +2 -2
  57. package/node_modules/tar/dist/commonjs/make-command.d.ts.map +1 -1
  58. package/node_modules/tar/dist/commonjs/make-command.js +3 -12
  59. package/node_modules/tar/dist/commonjs/make-command.js.map +1 -1
  60. package/node_modules/tar/dist/commonjs/mkdir.d.ts.map +1 -1
  61. package/node_modules/tar/dist/commonjs/mkdir.js +5 -6
  62. package/node_modules/tar/dist/commonjs/mkdir.js.map +1 -1
  63. package/node_modules/tar/dist/commonjs/normalize-windows-path.d.ts +1 -1
  64. package/node_modules/tar/dist/commonjs/normalize-windows-path.d.ts.map +1 -1
  65. package/node_modules/tar/dist/commonjs/normalize-windows-path.js +2 -2
  66. package/node_modules/tar/dist/commonjs/normalize-windows-path.js.map +1 -1
  67. package/node_modules/tar/dist/commonjs/options.d.ts +14 -6
  68. package/node_modules/tar/dist/commonjs/options.d.ts.map +1 -1
  69. package/node_modules/tar/dist/commonjs/options.js.map +1 -1
  70. package/node_modules/tar/dist/commonjs/pack.d.ts +8 -4
  71. package/node_modules/tar/dist/commonjs/pack.d.ts.map +1 -1
  72. package/node_modules/tar/dist/commonjs/pack.js +47 -19
  73. package/node_modules/tar/dist/commonjs/pack.js.map +1 -1
  74. package/node_modules/tar/dist/commonjs/parse.d.ts +11 -4
  75. package/node_modules/tar/dist/commonjs/parse.d.ts.map +1 -1
  76. package/node_modules/tar/dist/commonjs/parse.js +61 -26
  77. package/node_modules/tar/dist/commonjs/parse.js.map +1 -1
  78. package/node_modules/tar/dist/commonjs/path-reservations.d.ts.map +1 -1
  79. package/node_modules/tar/dist/commonjs/path-reservations.js +2 -2
  80. package/node_modules/tar/dist/commonjs/path-reservations.js.map +1 -1
  81. package/node_modules/tar/dist/commonjs/pax.d.ts +1 -1
  82. package/node_modules/tar/dist/commonjs/pax.d.ts.map +1 -1
  83. package/node_modules/tar/dist/commonjs/pax.js +30 -6
  84. package/node_modules/tar/dist/commonjs/pax.js.map +1 -1
  85. package/node_modules/tar/dist/commonjs/read-entry.d.ts +3 -3
  86. package/node_modules/tar/dist/commonjs/read-entry.d.ts.map +1 -1
  87. package/node_modules/tar/dist/commonjs/read-entry.js +2 -6
  88. package/node_modules/tar/dist/commonjs/read-entry.js.map +1 -1
  89. package/node_modules/tar/dist/commonjs/replace.d.ts.map +1 -1
  90. package/node_modules/tar/dist/commonjs/replace.js +5 -10
  91. package/node_modules/tar/dist/commonjs/replace.js.map +1 -1
  92. package/node_modules/tar/dist/commonjs/symlink-error.d.ts +2 -2
  93. package/node_modules/tar/dist/commonjs/symlink-error.d.ts.map +1 -1
  94. package/node_modules/tar/dist/commonjs/symlink-error.js.map +1 -1
  95. package/node_modules/tar/dist/commonjs/types.d.ts +9 -0
  96. package/node_modules/tar/dist/commonjs/types.d.ts.map +1 -1
  97. package/node_modules/tar/dist/commonjs/types.js +21 -1
  98. package/node_modules/tar/dist/commonjs/types.js.map +1 -1
  99. package/node_modules/tar/dist/commonjs/unpack.d.ts +4 -4
  100. package/node_modules/tar/dist/commonjs/unpack.d.ts.map +1 -1
  101. package/node_modules/tar/dist/commonjs/unpack.js +39 -64
  102. package/node_modules/tar/dist/commonjs/unpack.js.map +1 -1
  103. package/node_modules/tar/dist/commonjs/warn-method.d.ts +2 -2
  104. package/node_modules/tar/dist/commonjs/warn-method.d.ts.map +1 -1
  105. package/node_modules/tar/dist/commonjs/warn-method.js.map +1 -1
  106. package/node_modules/tar/dist/commonjs/winchars.js +1 -1
  107. package/node_modules/tar/dist/commonjs/winchars.js.map +1 -1
  108. package/node_modules/tar/dist/commonjs/write-entry.d.ts +11 -11
  109. package/node_modules/tar/dist/commonjs/write-entry.d.ts.map +1 -1
  110. package/node_modules/tar/dist/commonjs/write-entry.js +12 -13
  111. package/node_modules/tar/dist/commonjs/write-entry.js.map +1 -1
  112. package/node_modules/tar/dist/esm/create.d.ts.map +1 -1
  113. package/node_modules/tar/dist/esm/create.js +3 -4
  114. package/node_modules/tar/dist/esm/create.js.map +1 -1
  115. package/node_modules/tar/dist/esm/cwd-error.d.ts +1 -1
  116. package/node_modules/tar/dist/esm/cwd-error.d.ts.map +1 -1
  117. package/node_modules/tar/dist/esm/cwd-error.js.map +1 -1
  118. package/node_modules/tar/dist/esm/extract.js.map +1 -1
  119. package/node_modules/tar/dist/esm/get-write-flag.d.ts.map +1 -1
  120. package/node_modules/tar/dist/esm/get-write-flag.js +7 -4
  121. package/node_modules/tar/dist/esm/get-write-flag.js.map +1 -1
  122. package/node_modules/tar/dist/esm/header.d.ts.map +1 -1
  123. package/node_modules/tar/dist/esm/header.js +60 -37
  124. package/node_modules/tar/dist/esm/header.js.map +1 -1
  125. package/node_modules/tar/dist/esm/index.min.js +3 -3
  126. package/node_modules/tar/dist/esm/index.min.js.map +3 -3
  127. package/node_modules/tar/dist/esm/large-numbers.d.ts.map +1 -1
  128. package/node_modules/tar/dist/esm/large-numbers.js.map +1 -1
  129. package/node_modules/tar/dist/esm/list.d.ts +1 -1
  130. package/node_modules/tar/dist/esm/list.d.ts.map +1 -1
  131. package/node_modules/tar/dist/esm/list.js +2 -7
  132. package/node_modules/tar/dist/esm/list.js.map +1 -1
  133. package/node_modules/tar/dist/esm/make-command.d.ts +2 -2
  134. package/node_modules/tar/dist/esm/make-command.d.ts.map +1 -1
  135. package/node_modules/tar/dist/esm/make-command.js +3 -12
  136. package/node_modules/tar/dist/esm/make-command.js.map +1 -1
  137. package/node_modules/tar/dist/esm/mkdir.d.ts.map +1 -1
  138. package/node_modules/tar/dist/esm/mkdir.js +5 -6
  139. package/node_modules/tar/dist/esm/mkdir.js.map +1 -1
  140. package/node_modules/tar/dist/esm/normalize-windows-path.d.ts +1 -1
  141. package/node_modules/tar/dist/esm/normalize-windows-path.d.ts.map +1 -1
  142. package/node_modules/tar/dist/esm/normalize-windows-path.js +2 -2
  143. package/node_modules/tar/dist/esm/normalize-windows-path.js.map +1 -1
  144. package/node_modules/tar/dist/esm/options.d.ts +14 -6
  145. package/node_modules/tar/dist/esm/options.d.ts.map +1 -1
  146. package/node_modules/tar/dist/esm/options.js.map +1 -1
  147. package/node_modules/tar/dist/esm/pack.d.ts +8 -4
  148. package/node_modules/tar/dist/esm/pack.d.ts.map +1 -1
  149. package/node_modules/tar/dist/esm/pack.js +48 -20
  150. package/node_modules/tar/dist/esm/pack.js.map +1 -1
  151. package/node_modules/tar/dist/esm/parse.d.ts +11 -4
  152. package/node_modules/tar/dist/esm/parse.d.ts.map +1 -1
  153. package/node_modules/tar/dist/esm/parse.js +62 -27
  154. package/node_modules/tar/dist/esm/parse.js.map +1 -1
  155. package/node_modules/tar/dist/esm/path-reservations.d.ts.map +1 -1
  156. package/node_modules/tar/dist/esm/path-reservations.js +2 -2
  157. package/node_modules/tar/dist/esm/path-reservations.js.map +1 -1
  158. package/node_modules/tar/dist/esm/pax.d.ts +1 -1
  159. package/node_modules/tar/dist/esm/pax.d.ts.map +1 -1
  160. package/node_modules/tar/dist/esm/pax.js +30 -6
  161. package/node_modules/tar/dist/esm/pax.js.map +1 -1
  162. package/node_modules/tar/dist/esm/read-entry.d.ts +3 -3
  163. package/node_modules/tar/dist/esm/read-entry.d.ts.map +1 -1
  164. package/node_modules/tar/dist/esm/read-entry.js +2 -6
  165. package/node_modules/tar/dist/esm/read-entry.js.map +1 -1
  166. package/node_modules/tar/dist/esm/replace.d.ts.map +1 -1
  167. package/node_modules/tar/dist/esm/replace.js +6 -11
  168. package/node_modules/tar/dist/esm/replace.js.map +1 -1
  169. package/node_modules/tar/dist/esm/symlink-error.d.ts +2 -2
  170. package/node_modules/tar/dist/esm/symlink-error.d.ts.map +1 -1
  171. package/node_modules/tar/dist/esm/symlink-error.js.map +1 -1
  172. package/node_modules/tar/dist/esm/types.d.ts +9 -0
  173. package/node_modules/tar/dist/esm/types.d.ts.map +1 -1
  174. package/node_modules/tar/dist/esm/types.js +20 -0
  175. package/node_modules/tar/dist/esm/types.js.map +1 -1
  176. package/node_modules/tar/dist/esm/unpack.d.ts +4 -4
  177. package/node_modules/tar/dist/esm/unpack.d.ts.map +1 -1
  178. package/node_modules/tar/dist/esm/unpack.js +39 -64
  179. package/node_modules/tar/dist/esm/unpack.js.map +1 -1
  180. package/node_modules/tar/dist/esm/warn-method.d.ts +2 -2
  181. package/node_modules/tar/dist/esm/warn-method.d.ts.map +1 -1
  182. package/node_modules/tar/dist/esm/warn-method.js.map +1 -1
  183. package/node_modules/tar/dist/esm/winchars.js +1 -1
  184. package/node_modules/tar/dist/esm/winchars.js.map +1 -1
  185. package/node_modules/tar/dist/esm/write-entry.d.ts +11 -11
  186. package/node_modules/tar/dist/esm/write-entry.d.ts.map +1 -1
  187. package/node_modules/tar/dist/esm/write-entry.js +14 -15
  188. package/node_modules/tar/dist/esm/write-entry.js.map +1 -1
  189. package/node_modules/tar/package.json +15 -9
  190. package/node_modules/undici/docs/docs/api/Client.md +3 -0
  191. package/node_modules/undici/lib/dispatcher/agent.js +2 -2
  192. package/node_modules/undici/lib/dispatcher/client-h1.js +140 -23
  193. package/node_modules/undici/lib/dispatcher/client.js +3 -2
  194. package/node_modules/undici/lib/dispatcher/dispatcher-base.js +10 -1
  195. package/node_modules/undici/lib/dispatcher/pool-base.js +2 -2
  196. package/node_modules/undici/lib/dispatcher/pool.js +2 -2
  197. package/node_modules/undici/lib/web/cookies/parse.js +16 -23
  198. package/node_modules/undici/lib/web/websocket/permessage-deflate.js +13 -31
  199. package/node_modules/undici/lib/web/websocket/receiver.js +110 -24
  200. package/node_modules/undici/lib/web/websocket/websocket.js +8 -1
  201. package/node_modules/undici/package.json +1 -1
  202. package/node_modules/undici/types/client.d.ts +17 -0
  203. package/package.json +9 -9
  204. /package/node_modules/@zuplo/runtime/out/esm/{chunk-SCQAHLHW.js.LEGAL.txt → chunk-7POELG2I.js.LEGAL.txt} +0 -0
@@ -57,6 +57,9 @@ const EMPTY_BUF = Buffer.alloc(0)
57
57
  const FastBuffer = Buffer[Symbol.species]
58
58
  const addListener = util.addListener
59
59
  const removeAllListeners = util.removeAllListeners
60
+ const kIdleSocketValidation = Symbol('kIdleSocketValidation')
61
+ const kIdleSocketValidationTimeout = Symbol('kIdleSocketValidationTimeout')
62
+ const kSocketUsed = Symbol('kSocketUsed')
60
63
 
61
64
  let extractBody
62
65
 
@@ -279,29 +282,71 @@ class Parser {
279
282
 
280
283
  const offset = llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr
281
284
 
282
- if (ret === constants.ERROR.PAUSED_UPGRADE) {
283
- this.onUpgrade(data.slice(offset))
284
- } else if (ret === constants.ERROR.PAUSED) {
285
- this.paused = true
286
- socket.unshift(data.slice(offset))
287
- } else if (ret !== constants.ERROR.OK) {
288
- const ptr = llhttp.llhttp_get_error_reason(this.ptr)
289
- let message = ''
290
- /* istanbul ignore else: difficult to make a test case for */
291
- if (ptr) {
292
- const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0)
293
- message =
294
- 'Response does not match the HTTP/1.1 protocol (' +
295
- Buffer.from(llhttp.memory.buffer, ptr, len).toString() +
296
- ')'
285
+ if (ret !== constants.ERROR.OK) {
286
+ const body = data.subarray(offset)
287
+
288
+ if (ret === constants.ERROR.PAUSED_UPGRADE) {
289
+ this.onUpgrade(body)
290
+ } else if (ret === constants.ERROR.PAUSED) {
291
+ this.paused = true
292
+ socket.unshift(body)
293
+ } else {
294
+ throw this.createError(ret, body)
297
295
  }
298
- throw new HTTPParserError(message, constants.ERROR[ret], data.slice(offset))
299
296
  }
300
297
  } catch (err) {
301
298
  util.destroy(socket, err)
302
299
  }
303
300
  }
304
301
 
302
+ finish () {
303
+ assert(currentParser === null)
304
+ assert(this.ptr != null)
305
+ assert(!this.paused)
306
+
307
+ const { llhttp } = this
308
+
309
+ let ret
310
+
311
+ try {
312
+ currentParser = this
313
+ ret = llhttp.llhttp_finish(this.ptr)
314
+ } finally {
315
+ currentParser = null
316
+ }
317
+
318
+ if (ret === constants.ERROR.OK) {
319
+ return null
320
+ }
321
+
322
+ if (ret === constants.ERROR.PAUSED || ret === constants.ERROR.PAUSED_UPGRADE) {
323
+ this.paused = true
324
+ return null
325
+ }
326
+
327
+ return this.createError(ret, EMPTY_BUF)
328
+ }
329
+
330
+ createError (ret, data) {
331
+ const { llhttp, contentLength, bytesRead } = this
332
+
333
+ if (contentLength && bytesRead !== parseInt(contentLength, 10)) {
334
+ return new ResponseContentLengthMismatchError()
335
+ }
336
+
337
+ const ptr = llhttp.llhttp_get_error_reason(this.ptr)
338
+ let message = ''
339
+ if (ptr) {
340
+ const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0)
341
+ message =
342
+ 'Response does not match the HTTP/1.1 protocol (' +
343
+ Buffer.from(llhttp.memory.buffer, ptr, len).toString() +
344
+ ')'
345
+ }
346
+
347
+ return new HTTPParserError(message, constants.ERROR[ret], data)
348
+ }
349
+
305
350
  destroy () {
306
351
  assert(this.ptr != null)
307
352
  assert(currentParser == null)
@@ -329,6 +374,11 @@ class Parser {
329
374
  return -1
330
375
  }
331
376
 
377
+ if (client[kRunning] === 0) {
378
+ util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
379
+ return -1
380
+ }
381
+
332
382
  const request = client[kQueue][client[kRunningIdx]]
333
383
  if (!request) {
334
384
  return -1
@@ -432,6 +482,11 @@ class Parser {
432
482
  return -1
433
483
  }
434
484
 
485
+ if (client[kRunning] === 0) {
486
+ util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
487
+ return -1
488
+ }
489
+
435
490
  const request = client[kQueue][client[kRunningIdx]]
436
491
 
437
492
  /* istanbul ignore next: difficult to make a test case for */
@@ -605,6 +660,7 @@ class Parser {
605
660
  request.onComplete(headers)
606
661
 
607
662
  client[kQueue][client[kRunningIdx]++] = null
663
+ socket[kSocketUsed] = true
608
664
 
609
665
  if (socket[kWriting]) {
610
666
  assert(client[kRunning] === 0)
@@ -663,6 +719,9 @@ async function connectH1 (client, socket) {
663
719
  socket[kWriting] = false
664
720
  socket[kReset] = false
665
721
  socket[kBlocking] = false
722
+ socket[kIdleSocketValidation] = 0
723
+ socket[kIdleSocketValidationTimeout] = null
724
+ socket[kSocketUsed] = false
666
725
  socket[kParser] = new Parser(client, socket, llhttpInstance)
667
726
 
668
727
  addListener(socket, 'error', function (err) {
@@ -673,8 +732,11 @@ async function connectH1 (client, socket) {
673
732
  // On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
674
733
  // to the user.
675
734
  if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) {
676
- // We treat all incoming data so for as a valid response.
677
- parser.onMessageComplete()
735
+ const parserErr = parser.finish()
736
+ if (parserErr) {
737
+ this[kError] = parserErr
738
+ this[kClient][kOnError](parserErr)
739
+ }
678
740
  return
679
741
  }
680
742
 
@@ -693,8 +755,10 @@ async function connectH1 (client, socket) {
693
755
  const parser = this[kParser]
694
756
 
695
757
  if (parser.statusCode && !parser.shouldKeepAlive) {
696
- // We treat all incoming data so far as a valid response.
697
- parser.onMessageComplete()
758
+ const parserErr = parser.finish()
759
+ if (parserErr) {
760
+ util.destroy(this, parserErr)
761
+ }
698
762
  return
699
763
  }
700
764
 
@@ -704,10 +768,11 @@ async function connectH1 (client, socket) {
704
768
  const client = this[kClient]
705
769
  const parser = this[kParser]
706
770
 
771
+ clearIdleSocketValidation(this)
772
+
707
773
  if (parser) {
708
774
  if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {
709
- // We treat all incoming data so far as a valid response.
710
- parser.onMessageComplete()
775
+ this[kError] = parser.finish() || this[kError]
711
776
  }
712
777
 
713
778
  this[kParser].destroy()
@@ -770,7 +835,7 @@ async function connectH1 (client, socket) {
770
835
  return socket.destroyed
771
836
  },
772
837
  busy (request) {
773
- if (socket[kWriting] || socket[kReset] || socket[kBlocking]) {
838
+ if (socket[kWriting] || socket[kReset] || socket[kBlocking] || socket[kIdleSocketValidation] === 1) {
774
839
  return true
775
840
  }
776
841
 
@@ -808,6 +873,31 @@ async function connectH1 (client, socket) {
808
873
  }
809
874
  }
810
875
 
876
+ function clearIdleSocketValidation (socket) {
877
+ if (socket[kIdleSocketValidationTimeout]) {
878
+ clearTimeout(socket[kIdleSocketValidationTimeout])
879
+ socket[kIdleSocketValidationTimeout] = null
880
+ }
881
+
882
+ socket[kIdleSocketValidation] = 0
883
+ }
884
+
885
+ function scheduleIdleSocketValidation (client, socket) {
886
+ socket[kIdleSocketValidation] = 1
887
+ socket[kIdleSocketValidationTimeout] = setTimeout(() => {
888
+ socket[kIdleSocketValidationTimeout] = null
889
+ socket[kIdleSocketValidation] = 2
890
+
891
+ if (client[kSocket] === socket && !socket.destroyed) {
892
+ client[kResume]()
893
+ }
894
+ }, 0)
895
+ socket[kIdleSocketValidationTimeout].unref?.()
896
+ }
897
+
898
+ /**
899
+ * @param {import('./client.js')} client
900
+ */
811
901
  function resumeH1 (client) {
812
902
  const socket = client[kSocket]
813
903
 
@@ -822,6 +912,32 @@ function resumeH1 (client) {
822
912
  socket[kNoRef] = false
823
913
  }
824
914
 
915
+ if (client[kRunning] === 0 && client[kPending] > 0 && socket[kSocketUsed]) {
916
+ if (socket[kIdleSocketValidation] === 0) {
917
+ scheduleIdleSocketValidation(client, socket)
918
+ socket[kParser].readMore()
919
+ if (socket.destroyed) {
920
+ return
921
+ }
922
+ return
923
+ }
924
+
925
+ if (socket[kIdleSocketValidation] === 1) {
926
+ socket[kParser].readMore()
927
+ if (socket.destroyed) {
928
+ return
929
+ }
930
+ return
931
+ }
932
+ }
933
+
934
+ if (client[kRunning] === 0) {
935
+ socket[kParser].readMore()
936
+ if (socket.destroyed) {
937
+ return
938
+ }
939
+ }
940
+
825
941
  if (client[kSize] === 0) {
826
942
  if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) {
827
943
  socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE)
@@ -915,6 +1031,7 @@ function writeH1 (client, request) {
915
1031
  }
916
1032
 
917
1033
  const socket = client[kSocket]
1034
+ clearIdleSocketValidation(socket)
918
1035
 
919
1036
  const abort = (err) => {
920
1037
  if (request.aborted || request.completed) {
@@ -106,9 +106,10 @@ class Client extends DispatcherBase {
106
106
  autoSelectFamilyAttemptTimeout,
107
107
  // h2
108
108
  maxConcurrentStreams,
109
- allowH2
109
+ allowH2,
110
+ webSocket
110
111
  } = {}) {
111
- super()
112
+ super({ webSocket })
112
113
 
113
114
  if (keepAlive !== undefined) {
114
115
  throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead')
@@ -11,15 +11,24 @@ const { kDestroy, kClose, kClosed, kDestroyed, kDispatch, kInterceptors } = requ
11
11
  const kOnDestroyed = Symbol('onDestroyed')
12
12
  const kOnClosed = Symbol('onClosed')
13
13
  const kInterceptedDispatch = Symbol('Intercepted Dispatch')
14
+ const kWebSocketOptions = Symbol('webSocketOptions')
14
15
 
15
16
  class DispatcherBase extends Dispatcher {
16
- constructor () {
17
+ constructor (opts) {
17
18
  super()
18
19
 
19
20
  this[kDestroyed] = false
20
21
  this[kOnDestroyed] = null
21
22
  this[kClosed] = false
22
23
  this[kOnClosed] = []
24
+ this[kWebSocketOptions] = opts?.webSocket ?? {}
25
+ }
26
+
27
+ get webSocketOptions () {
28
+ return {
29
+ maxFragments: this[kWebSocketOptions].maxFragments ?? 131072,
30
+ maxPayloadSize: this[kWebSocketOptions].maxPayloadSize ?? 128 * 1024 * 1024
31
+ }
23
32
  }
24
33
 
25
34
  get destroyed () {
@@ -19,8 +19,8 @@ const kRemoveClient = Symbol('remove client')
19
19
  const kStats = Symbol('stats')
20
20
 
21
21
  class PoolBase extends DispatcherBase {
22
- constructor () {
23
- super()
22
+ constructor (opts) {
23
+ super(opts)
24
24
 
25
25
  this[kQueue] = new FixedQueue()
26
26
  this[kClients] = []
@@ -37,8 +37,6 @@ class Pool extends PoolBase {
37
37
  allowH2,
38
38
  ...options
39
39
  } = {}) {
40
- super()
41
-
42
40
  if (connections != null && (!Number.isFinite(connections) || connections < 0)) {
43
41
  throw new InvalidArgumentError('invalid connections')
44
42
  }
@@ -63,6 +61,8 @@ class Pool extends PoolBase {
63
61
  })
64
62
  }
65
63
 
64
+ super(options)
65
+
66
66
  this[kInterceptors] = options.interceptors?.Pool && Array.isArray(options.interceptors.Pool)
67
67
  ? options.interceptors.Pool
68
68
  : []
@@ -275,32 +275,25 @@ function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {})
275
275
  // If the attribute-name case-insensitively matches the string
276
276
  // "SameSite", the user agent MUST process the cookie-av as follows:
277
277
 
278
- // 1. Let enforcement be "Default".
279
- let enforcement = 'Default'
280
-
281
278
  const attributeValueLowercase = attributeValue.toLowerCase()
282
- // 2. If cookie-av's attribute-value is a case-insensitive match for
283
- // "None", set enforcement to "None".
284
- if (attributeValueLowercase.includes('none')) {
285
- enforcement = 'None'
286
- }
287
279
 
288
- // 3. If cookie-av's attribute-value is a case-insensitive match for
289
- // "Strict", set enforcement to "Strict".
290
- if (attributeValueLowercase.includes('strict')) {
291
- enforcement = 'Strict'
280
+ // 1. If cookie-av's attribute-value is a case-insensitive match for
281
+ // "None", append an attribute to the cookie-attribute-list with an
282
+ // attribute-name of "SameSite" and an attribute-value of "None".
283
+ if (attributeValueLowercase === 'none') {
284
+ cookieAttributeList.sameSite = 'None'
285
+ } else if (attributeValueLowercase === 'strict') {
286
+ // 2. If cookie-av's attribute-value is a case-insensitive match for
287
+ // "Strict", append an attribute to the cookie-attribute-list with
288
+ // an attribute-name of "SameSite" and an attribute-value of
289
+ // "Strict".
290
+ cookieAttributeList.sameSite = 'Strict'
291
+ } else if (attributeValueLowercase === 'lax') {
292
+ // 3. If cookie-av's attribute-value is a case-insensitive match for
293
+ // "Lax", append an attribute to the cookie-attribute-list with an
294
+ // attribute-name of "SameSite" and an attribute-value of "Lax".
295
+ cookieAttributeList.sameSite = 'Lax'
292
296
  }
293
-
294
- // 4. If cookie-av's attribute-value is a case-insensitive match for
295
- // "Lax", set enforcement to "Lax".
296
- if (attributeValueLowercase.includes('lax')) {
297
- enforcement = 'Lax'
298
- }
299
-
300
- // 5. Append an attribute to the cookie-attribute-list with an
301
- // attribute-name of "SameSite" and an attribute-value of
302
- // enforcement.
303
- cookieAttributeList.sameSite = enforcement
304
297
  } else {
305
298
  cookieAttributeList.unparsed ??= []
306
299
 
@@ -8,40 +8,35 @@ const tail = Buffer.from([0x00, 0x00, 0xff, 0xff])
8
8
  const kBuffer = Symbol('kBuffer')
9
9
  const kLength = Symbol('kLength')
10
10
 
11
- // Default maximum decompressed message size: 4 MB
12
- const kDefaultMaxDecompressedSize = 4 * 1024 * 1024
13
-
14
11
  class PerMessageDeflate {
15
12
  /** @type {import('node:zlib').InflateRaw} */
16
13
  #inflate
17
14
 
18
15
  #options = {}
19
16
 
20
- /** @type {boolean} */
21
- #aborted = false
22
-
23
- /** @type {Function|null} */
24
- #currentCallback = null
17
+ #maxPayloadSize = 0
25
18
 
26
19
  /**
27
20
  * @param {Map<string, string>} extensions
28
21
  */
29
- constructor (extensions) {
22
+ constructor (extensions, options) {
30
23
  this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover')
31
24
  this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits')
25
+
26
+ this.#maxPayloadSize = options.maxPayloadSize
32
27
  }
33
28
 
29
+ /**
30
+ * Decompress a compressed payload.
31
+ * @param {Buffer} chunk Compressed data
32
+ * @param {boolean} fin Final fragment flag
33
+ * @param {Function} callback Callback function
34
+ */
34
35
  decompress (chunk, fin, callback) {
35
36
  // An endpoint uses the following algorithm to decompress a message.
36
37
  // 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
37
38
  // payload of the message.
38
39
  // 2. Decompress the resulting data using DEFLATE.
39
-
40
- if (this.#aborted) {
41
- callback(new MessageSizeExceededError())
42
- return
43
- }
44
-
45
40
  if (!this.#inflate) {
46
41
  let windowBits = Z_DEFAULT_WINDOWBITS
47
42
 
@@ -64,23 +59,12 @@ class PerMessageDeflate {
64
59
  this.#inflate[kLength] = 0
65
60
 
66
61
  this.#inflate.on('data', (data) => {
67
- if (this.#aborted) {
68
- return
69
- }
70
-
71
62
  this.#inflate[kLength] += data.length
72
63
 
73
- if (this.#inflate[kLength] > kDefaultMaxDecompressedSize) {
74
- this.#aborted = true
64
+ if (this.#maxPayloadSize > 0 && this.#inflate[kLength] > this.#maxPayloadSize) {
65
+ callback(new MessageSizeExceededError())
75
66
  this.#inflate.removeAllListeners()
76
- this.#inflate.destroy()
77
67
  this.#inflate = null
78
-
79
- if (this.#currentCallback) {
80
- const cb = this.#currentCallback
81
- this.#currentCallback = null
82
- cb(new MessageSizeExceededError())
83
- }
84
68
  return
85
69
  }
86
70
 
@@ -93,14 +77,13 @@ class PerMessageDeflate {
93
77
  })
94
78
  }
95
79
 
96
- this.#currentCallback = callback
97
80
  this.#inflate.write(chunk)
98
81
  if (fin) {
99
82
  this.#inflate.write(tail)
100
83
  }
101
84
 
102
85
  this.#inflate.flush(() => {
103
- if (this.#aborted || !this.#inflate) {
86
+ if (!this.#inflate) {
104
87
  return
105
88
  }
106
89
 
@@ -108,7 +91,6 @@ class PerMessageDeflate {
108
91
 
109
92
  this.#inflate[kBuffer].length = 0
110
93
  this.#inflate[kLength] = 0
111
- this.#currentCallback = null
112
94
 
113
95
  callback(null, full)
114
96
  })
@@ -18,6 +18,12 @@ const {
18
18
  const { WebsocketFrameSend } = require('./frame')
19
19
  const { closeWebSocketConnection } = require('./connection')
20
20
  const { PerMessageDeflate } = require('./permessage-deflate')
21
+ const { MessageSizeExceededError } = require('../../core/errors')
22
+
23
+ function failWebsocketConnectionWithCode (ws, code, reason) {
24
+ closeWebSocketConnection(ws, code, reason, Buffer.byteLength(reason))
25
+ failWebsocketConnection(ws, reason)
26
+ }
21
27
 
22
28
  // This code was influenced by ws released under the MIT license.
23
29
  // Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
@@ -26,6 +32,7 @@ const { PerMessageDeflate } = require('./permessage-deflate')
26
32
 
27
33
  class ByteParser extends Writable {
28
34
  #buffers = []
35
+ #fragmentsBytes = 0
29
36
  #byteOffset = 0
30
37
  #loop = false
31
38
 
@@ -37,18 +44,27 @@ class ByteParser extends Writable {
37
44
  /** @type {Map<string, PerMessageDeflate>} */
38
45
  #extensions
39
46
 
47
+ /** @type {number} */
48
+ #maxFragments
49
+
50
+ /** @type {number} */
51
+ #maxPayloadSize
52
+
40
53
  /**
41
54
  * @param {import('./websocket').WebSocket} ws
42
55
  * @param {Map<string, string>|null} extensions
56
+ * @param {{ maxFragments?: number, maxPayloadSize?: number }} [options]
43
57
  */
44
- constructor (ws, extensions) {
58
+ constructor (ws, extensions, options = {}) {
45
59
  super()
46
60
 
47
61
  this.ws = ws
48
62
  this.#extensions = extensions == null ? new Map() : extensions
63
+ this.#maxFragments = options.maxFragments ?? 0
64
+ this.#maxPayloadSize = options.maxPayloadSize ?? 0
49
65
 
50
66
  if (this.#extensions.has('permessage-deflate')) {
51
- this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions))
67
+ this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions, options))
52
68
  }
53
69
  }
54
70
 
@@ -64,6 +80,19 @@ class ByteParser extends Writable {
64
80
  this.run(callback)
65
81
  }
66
82
 
83
+ #validatePayloadLength () {
84
+ if (
85
+ this.#maxPayloadSize > 0 &&
86
+ !isControlFrame(this.#info.opcode) &&
87
+ this.#info.payloadLength + this.#fragmentsBytes > this.#maxPayloadSize
88
+ ) {
89
+ failWebsocketConnectionWithCode(this.ws, 1009, 'Payload size exceeds maximum allowed size')
90
+ return false
91
+ }
92
+
93
+ return true
94
+ }
95
+
67
96
  /**
68
97
  * Runs whenever a new chunk is received.
69
98
  * Callback is called whenever there are no more chunks buffering,
@@ -152,6 +181,10 @@ class ByteParser extends Writable {
152
181
  if (payloadLength <= 125) {
153
182
  this.#info.payloadLength = payloadLength
154
183
  this.#state = parserStates.READ_DATA
184
+
185
+ if (!this.#validatePayloadLength()) {
186
+ return
187
+ }
155
188
  } else if (payloadLength === 126) {
156
189
  this.#state = parserStates.PAYLOADLENGTH_16
157
190
  } else if (payloadLength === 127) {
@@ -176,6 +209,10 @@ class ByteParser extends Writable {
176
209
 
177
210
  this.#info.payloadLength = buffer.readUInt16BE(0)
178
211
  this.#state = parserStates.READ_DATA
212
+
213
+ if (!this.#validatePayloadLength()) {
214
+ return
215
+ }
179
216
  } else if (this.#state === parserStates.PAYLOADLENGTH_64) {
180
217
  if (this.#byteOffset < 8) {
181
218
  return callback()
@@ -198,6 +235,10 @@ class ByteParser extends Writable {
198
235
 
199
236
  this.#info.payloadLength = lower
200
237
  this.#state = parserStates.READ_DATA
238
+
239
+ if (!this.#validatePayloadLength()) {
240
+ return
241
+ }
201
242
  } else if (this.#state === parserStates.READ_DATA) {
202
243
  if (this.#byteOffset < this.#info.payloadLength) {
203
244
  return callback()
@@ -210,42 +251,58 @@ class ByteParser extends Writable {
210
251
  this.#state = parserStates.INFO
211
252
  } else {
212
253
  if (!this.#info.compressed) {
213
- this.#fragments.push(body)
254
+ if (!this.writeFragments(body)) {
255
+ return
256
+ }
257
+
258
+ if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) {
259
+ failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message)
260
+ return
261
+ }
214
262
 
215
263
  // If the frame is not fragmented, a message has been received.
216
264
  // If the frame is fragmented, it will terminate with a fin bit set
217
265
  // and an opcode of 0 (continuation), therefore we handle that when
218
266
  // parsing continuation frames, not here.
219
267
  if (!this.#info.fragmented && this.#info.fin) {
220
- const fullMessage = Buffer.concat(this.#fragments)
221
- websocketMessageReceived(this.ws, this.#info.binaryType, fullMessage)
222
- this.#fragments.length = 0
268
+ websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments())
223
269
  }
224
270
 
225
271
  this.#state = parserStates.INFO
226
272
  } else {
227
- this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => {
228
- if (error) {
229
- failWebsocketConnection(this.ws, error.message)
230
- return
231
- }
232
-
233
- this.#fragments.push(data)
273
+ this.#extensions.get('permessage-deflate').decompress(
274
+ body,
275
+ this.#info.fin,
276
+ (error, data) => {
277
+ if (error) {
278
+ const code = error instanceof MessageSizeExceededError ? 1009 : 1007
279
+ failWebsocketConnectionWithCode(this.ws, code, error.message)
280
+ return
281
+ }
282
+
283
+ if (!this.writeFragments(data)) {
284
+ return
285
+ }
286
+
287
+ if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) {
288
+ failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message)
289
+ return
290
+ }
291
+
292
+ if (!this.#info.fin) {
293
+ this.#state = parserStates.INFO
294
+ this.#loop = true
295
+ this.run(callback)
296
+ return
297
+ }
298
+
299
+ websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments())
234
300
 
235
- if (!this.#info.fin) {
236
- this.#state = parserStates.INFO
237
301
  this.#loop = true
302
+ this.#state = parserStates.INFO
238
303
  this.run(callback)
239
- return
240
304
  }
241
-
242
- websocketMessageReceived(this.ws, this.#info.binaryType, Buffer.concat(this.#fragments))
243
-
244
- this.#loop = true
245
- this.#state = parserStates.INFO
246
- this.#fragments.length = 0
247
- this.run(callback)
248
- })
305
+ )
249
306
 
250
307
  this.#loop = false
251
308
  break
@@ -297,6 +354,35 @@ class ByteParser extends Writable {
297
354
  return buffer
298
355
  }
299
356
 
357
+ writeFragments (fragment) {
358
+ if (
359
+ this.#maxFragments > 0 &&
360
+ this.#fragments.length === this.#maxFragments
361
+ ) {
362
+ failWebsocketConnectionWithCode(this.ws, 1008, 'Too many message fragments')
363
+ return false
364
+ }
365
+
366
+ this.#fragmentsBytes += fragment.length
367
+ this.#fragments.push(fragment)
368
+ return true
369
+ }
370
+
371
+ consumeFragments () {
372
+ const fragments = this.#fragments
373
+
374
+ if (fragments.length === 1) {
375
+ this.#fragmentsBytes = 0
376
+ return fragments.shift()
377
+ }
378
+
379
+ const output = Buffer.concat(fragments, this.#fragmentsBytes)
380
+ this.#fragments = []
381
+ this.#fragmentsBytes = 0
382
+
383
+ return output
384
+ }
385
+
300
386
  parseCloseBody (data) {
301
387
  assert(data.length !== 1)
302
388