undici 6.20.0 → 7.0.0-alpha.2

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 (137) hide show
  1. package/README.md +6 -10
  2. package/docs/docs/api/Agent.md +0 -3
  3. package/docs/docs/api/Client.md +1 -3
  4. package/docs/docs/api/Debug.md +1 -1
  5. package/docs/docs/api/Dispatcher.md +60 -8
  6. package/docs/docs/api/EnvHttpProxyAgent.md +0 -1
  7. package/docs/docs/api/Fetch.md +1 -0
  8. package/docs/docs/api/MockAgent.md +2 -0
  9. package/docs/docs/api/MockPool.md +2 -1
  10. package/docs/docs/api/Pool.md +0 -1
  11. package/docs/docs/api/RetryAgent.md +1 -1
  12. package/docs/docs/api/RetryHandler.md +1 -1
  13. package/docs/docs/api/WebSocket.md +45 -3
  14. package/index.js +6 -6
  15. package/lib/api/abort-signal.js +2 -0
  16. package/lib/api/api-connect.js +3 -1
  17. package/lib/api/api-pipeline.js +7 -6
  18. package/lib/api/api-request.js +32 -47
  19. package/lib/api/api-stream.js +39 -50
  20. package/lib/api/api-upgrade.js +5 -3
  21. package/lib/api/readable.js +261 -64
  22. package/lib/api/util.js +2 -0
  23. package/lib/core/constants.js +11 -9
  24. package/lib/core/diagnostics.js +122 -128
  25. package/lib/core/errors.js +4 -4
  26. package/lib/core/request.js +11 -9
  27. package/lib/core/symbols.js +2 -1
  28. package/lib/core/tree.js +9 -1
  29. package/lib/core/util.js +219 -48
  30. package/lib/dispatcher/agent.js +3 -17
  31. package/lib/dispatcher/balanced-pool.js +5 -8
  32. package/lib/dispatcher/client-h1.js +278 -54
  33. package/lib/dispatcher/client-h2.js +1 -1
  34. package/lib/dispatcher/client.js +23 -34
  35. package/lib/dispatcher/dispatcher-base.js +2 -34
  36. package/lib/dispatcher/dispatcher.js +3 -24
  37. package/lib/dispatcher/fixed-queue.js +91 -49
  38. package/lib/dispatcher/pool-stats.js +2 -0
  39. package/lib/dispatcher/pool.js +3 -6
  40. package/lib/dispatcher/proxy-agent.js +6 -7
  41. package/lib/handler/decorator-handler.js +24 -0
  42. package/lib/handler/redirect-handler.js +11 -2
  43. package/lib/handler/retry-handler.js +12 -3
  44. package/lib/interceptor/dns.js +346 -0
  45. package/lib/interceptor/dump.js +2 -2
  46. package/lib/interceptor/redirect.js +11 -14
  47. package/lib/interceptor/response-error.js +4 -1
  48. package/lib/llhttp/constants.d.ts +97 -0
  49. package/lib/llhttp/constants.js +412 -192
  50. package/lib/llhttp/constants.js.map +1 -0
  51. package/lib/llhttp/llhttp-wasm.js +11 -1
  52. package/lib/llhttp/llhttp_simd-wasm.js +11 -1
  53. package/lib/llhttp/utils.d.ts +2 -0
  54. package/lib/llhttp/utils.js +9 -9
  55. package/lib/llhttp/utils.js.map +1 -0
  56. package/lib/mock/mock-agent.js +5 -8
  57. package/lib/mock/mock-client.js +9 -4
  58. package/lib/mock/mock-errors.js +3 -1
  59. package/lib/mock/mock-interceptor.js +8 -6
  60. package/lib/mock/mock-pool.js +9 -4
  61. package/lib/mock/mock-symbols.js +3 -1
  62. package/lib/mock/mock-utils.js +29 -5
  63. package/lib/web/cache/cache.js +24 -21
  64. package/lib/web/cache/cachestorage.js +1 -1
  65. package/lib/web/cookies/index.js +17 -13
  66. package/lib/web/cookies/parse.js +2 -2
  67. package/lib/web/eventsource/eventsource-stream.js +9 -8
  68. package/lib/web/eventsource/eventsource.js +10 -6
  69. package/lib/web/fetch/body.js +42 -36
  70. package/lib/web/fetch/constants.js +35 -26
  71. package/lib/web/fetch/data-url.js +1 -1
  72. package/lib/web/fetch/formdata-parser.js +2 -2
  73. package/lib/web/fetch/formdata.js +65 -54
  74. package/lib/web/fetch/headers.js +117 -85
  75. package/lib/web/fetch/index.js +55 -62
  76. package/lib/web/fetch/request.js +135 -77
  77. package/lib/web/fetch/response.js +86 -56
  78. package/lib/web/fetch/util.js +90 -64
  79. package/lib/web/fetch/webidl.js +99 -64
  80. package/lib/web/websocket/connection.js +76 -147
  81. package/lib/web/websocket/constants.js +3 -4
  82. package/lib/web/websocket/events.js +4 -2
  83. package/lib/web/websocket/frame.js +45 -3
  84. package/lib/web/websocket/receiver.js +29 -33
  85. package/lib/web/websocket/sender.js +18 -13
  86. package/lib/web/websocket/stream/websocketerror.js +83 -0
  87. package/lib/web/websocket/stream/websocketstream.js +485 -0
  88. package/lib/web/websocket/util.js +128 -77
  89. package/lib/web/websocket/websocket.js +234 -135
  90. package/package.json +20 -33
  91. package/scripts/strip-comments.js +3 -1
  92. package/types/agent.d.ts +7 -7
  93. package/types/api.d.ts +24 -24
  94. package/types/balanced-pool.d.ts +11 -11
  95. package/types/client.d.ts +11 -12
  96. package/types/diagnostics-channel.d.ts +10 -10
  97. package/types/dispatcher.d.ts +96 -97
  98. package/types/env-http-proxy-agent.d.ts +2 -2
  99. package/types/errors.d.ts +53 -47
  100. package/types/fetch.d.ts +8 -8
  101. package/types/formdata.d.ts +7 -7
  102. package/types/global-dispatcher.d.ts +4 -4
  103. package/types/global-origin.d.ts +5 -5
  104. package/types/handlers.d.ts +4 -4
  105. package/types/header.d.ts +157 -1
  106. package/types/index.d.ts +42 -46
  107. package/types/interceptors.d.ts +22 -8
  108. package/types/mock-agent.d.ts +21 -18
  109. package/types/mock-client.d.ts +4 -4
  110. package/types/mock-errors.d.ts +3 -3
  111. package/types/mock-interceptor.d.ts +19 -19
  112. package/types/mock-pool.d.ts +4 -4
  113. package/types/patch.d.ts +0 -4
  114. package/types/pool-stats.d.ts +8 -8
  115. package/types/pool.d.ts +12 -12
  116. package/types/proxy-agent.d.ts +4 -4
  117. package/types/readable.d.ts +22 -14
  118. package/types/retry-agent.d.ts +1 -1
  119. package/types/retry-handler.d.ts +8 -8
  120. package/types/util.d.ts +3 -3
  121. package/types/utility.d.ts +7 -0
  122. package/types/webidl.d.ts +44 -6
  123. package/types/websocket.d.ts +34 -1
  124. package/docs/docs/api/DispatchInterceptor.md +0 -60
  125. package/lib/interceptor/redirect-interceptor.js +0 -21
  126. package/lib/mock/pluralizer.js +0 -29
  127. package/lib/web/cache/symbols.js +0 -5
  128. package/lib/web/fetch/file.js +0 -126
  129. package/lib/web/fetch/symbols.js +0 -9
  130. package/lib/web/fileapi/encoding.js +0 -290
  131. package/lib/web/fileapi/filereader.js +0 -344
  132. package/lib/web/fileapi/progressevent.js +0 -78
  133. package/lib/web/fileapi/symbols.js +0 -10
  134. package/lib/web/fileapi/util.js +0 -391
  135. package/lib/web/websocket/symbols.js +0 -12
  136. package/types/file.d.ts +0 -39
  137. package/types/filereader.d.ts +0 -54
@@ -3,7 +3,6 @@
3
3
  const { webidl } = require('../fetch/webidl')
4
4
  const { kEnumerableProperty } = require('../../core/util')
5
5
  const { kConstruct } = require('../../core/symbols')
6
- const { MessagePort } = require('node:worker_threads')
7
6
 
8
7
  /**
9
8
  * @see https://html.spec.whatwg.org/multipage/comms.html#messageevent
@@ -215,7 +214,10 @@ Object.defineProperties(ErrorEvent.prototype, {
215
214
  error: kEnumerableProperty
216
215
  })
217
216
 
218
- webidl.converters.MessagePort = webidl.interfaceConverter(MessagePort)
217
+ webidl.converters.MessagePort = webidl.interfaceConverter(
218
+ webidl.is.MessagePort,
219
+ 'MessagePort'
220
+ )
219
221
 
220
222
  webidl.converters['sequence<MessagePort>'] = webidl.sequenceConverter(
221
223
  webidl.converters.MessagePort
@@ -1,8 +1,8 @@
1
1
  'use strict'
2
2
 
3
- const { maxUnsigned16Bit } = require('./constants')
3
+ const { maxUnsigned16Bit, opcodes } = require('./constants')
4
4
 
5
- const BUFFER_SIZE = 16386
5
+ const BUFFER_SIZE = 8 * 1024
6
6
 
7
7
  /** @type {import('crypto')} */
8
8
  let crypto
@@ -27,7 +27,7 @@ try {
27
27
  function generateMask () {
28
28
  if (bufIdx === BUFFER_SIZE) {
29
29
  bufIdx = 0
30
- crypto.randomFillSync((buffer ??= Buffer.allocUnsafe(BUFFER_SIZE)), 0, BUFFER_SIZE)
30
+ crypto.randomFillSync((buffer ??= Buffer.allocUnsafeSlow(BUFFER_SIZE)), 0, BUFFER_SIZE)
31
31
  }
32
32
  return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]]
33
33
  }
@@ -89,6 +89,48 @@ class WebsocketFrameSend {
89
89
 
90
90
  return buffer
91
91
  }
92
+
93
+ /**
94
+ * @param {Uint8Array} buffer
95
+ */
96
+ static createFastTextFrame (buffer) {
97
+ const maskKey = generateMask()
98
+
99
+ const bodyLength = buffer.length
100
+
101
+ // mask body
102
+ for (let i = 0; i < bodyLength; ++i) {
103
+ buffer[i] ^= maskKey[i & 3]
104
+ }
105
+
106
+ let payloadLength = bodyLength
107
+ let offset = 6
108
+
109
+ if (bodyLength > maxUnsigned16Bit) {
110
+ offset += 8 // payload length is next 8 bytes
111
+ payloadLength = 127
112
+ } else if (bodyLength > 125) {
113
+ offset += 2 // payload length is next 2 bytes
114
+ payloadLength = 126
115
+ }
116
+ const head = Buffer.allocUnsafeSlow(offset)
117
+
118
+ head[0] = 0x80 /* FIN */ | opcodes.TEXT /* opcode TEXT */
119
+ head[1] = payloadLength | 0x80 /* MASK */
120
+ head[offset - 4] = maskKey[0]
121
+ head[offset - 3] = maskKey[1]
122
+ head[offset - 2] = maskKey[2]
123
+ head[offset - 1] = maskKey[3]
124
+
125
+ if (payloadLength === 126) {
126
+ head.writeUInt16BE(bodyLength, 2)
127
+ } else if (payloadLength === 127) {
128
+ head[2] = head[3] = 0
129
+ head.writeUIntBE(bodyLength, 4, 6)
130
+ }
131
+
132
+ return [head, buffer]
133
+ }
92
134
  }
93
135
 
94
136
  module.exports = {
@@ -3,7 +3,6 @@
3
3
  const { Writable } = require('node:stream')
4
4
  const assert = require('node:assert')
5
5
  const { parserStates, opcodes, states, emptyBuffer, sentCloseFrameState } = require('./constants')
6
- const { kReadyState, kSentClose, kResponse, kReceivedClose } = require('./symbols')
7
6
  const { channels } = require('../../core/diagnostics')
8
7
  const {
9
8
  isValidStatusCode,
@@ -16,7 +15,6 @@ const {
16
15
  isContinuationFrame
17
16
  } = require('./util')
18
17
  const { WebsocketFrameSend } = require('./frame')
19
- const { closeWebSocketConnection } = require('./connection')
20
18
  const { PerMessageDeflate } = require('./permessage-deflate')
21
19
 
22
20
  // This code was influenced by ws released under the MIT license.
@@ -37,10 +35,13 @@ class ByteParser extends Writable {
37
35
  /** @type {Map<string, PerMessageDeflate>} */
38
36
  #extensions
39
37
 
40
- constructor (ws, extensions) {
38
+ /** @type {import('./websocket').Handler} */
39
+ #handler
40
+
41
+ constructor (handler, extensions) {
41
42
  super()
42
43
 
43
- this.ws = ws
44
+ this.#handler = handler
44
45
  this.#extensions = extensions == null ? new Map() : extensions
45
46
 
46
47
  if (this.#extensions.has('permessage-deflate')) {
@@ -86,12 +87,12 @@ class ByteParser extends Writable {
86
87
  const rsv3 = buffer[0] & 0x10
87
88
 
88
89
  if (!isValidOpcode(opcode)) {
89
- failWebsocketConnection(this.ws, 'Invalid opcode received')
90
+ failWebsocketConnection(this.#handler, 1002, 'Invalid opcode received')
90
91
  return callback()
91
92
  }
92
93
 
93
94
  if (masked) {
94
- failWebsocketConnection(this.ws, 'Frame cannot be masked')
95
+ failWebsocketConnection(this.#handler, 1002, 'Frame cannot be masked')
95
96
  return callback()
96
97
  }
97
98
 
@@ -105,43 +106,43 @@ class ByteParser extends Writable {
105
106
  // WebSocket connection where a PMCE is in use, this bit indicates
106
107
  // whether a message is compressed or not.
107
108
  if (rsv1 !== 0 && !this.#extensions.has('permessage-deflate')) {
108
- failWebsocketConnection(this.ws, 'Expected RSV1 to be clear.')
109
+ failWebsocketConnection(this.#handler, 1002, 'Expected RSV1 to be clear.')
109
110
  return
110
111
  }
111
112
 
112
113
  if (rsv2 !== 0 || rsv3 !== 0) {
113
- failWebsocketConnection(this.ws, 'RSV1, RSV2, RSV3 must be clear')
114
+ failWebsocketConnection(this.#handler, 1002, 'RSV1, RSV2, RSV3 must be clear')
114
115
  return
115
116
  }
116
117
 
117
118
  if (fragmented && !isTextBinaryFrame(opcode)) {
118
119
  // Only text and binary frames can be fragmented
119
- failWebsocketConnection(this.ws, 'Invalid frame type was fragmented.')
120
+ failWebsocketConnection(this.#handler, 1002, 'Invalid frame type was fragmented.')
120
121
  return
121
122
  }
122
123
 
123
124
  // If we are already parsing a text/binary frame and do not receive either
124
125
  // a continuation frame or close frame, fail the connection.
125
126
  if (isTextBinaryFrame(opcode) && this.#fragments.length > 0) {
126
- failWebsocketConnection(this.ws, 'Expected continuation frame')
127
+ failWebsocketConnection(this.#handler, 1002, 'Expected continuation frame')
127
128
  return
128
129
  }
129
130
 
130
131
  if (this.#info.fragmented && fragmented) {
131
132
  // A fragmented frame can't be fragmented itself
132
- failWebsocketConnection(this.ws, 'Fragmented frame exceeded 125 bytes.')
133
+ failWebsocketConnection(this.#handler, 1002, 'Fragmented frame exceeded 125 bytes.')
133
134
  return
134
135
  }
135
136
 
136
137
  // "All control frames MUST have a payload length of 125 bytes or less
137
138
  // and MUST NOT be fragmented."
138
139
  if ((payloadLength > 125 || fragmented) && isControlFrame(opcode)) {
139
- failWebsocketConnection(this.ws, 'Control frame either too large or fragmented')
140
+ failWebsocketConnection(this.#handler, 1002, 'Control frame either too large or fragmented')
140
141
  return
141
142
  }
142
143
 
143
144
  if (isContinuationFrame(opcode) && this.#fragments.length === 0 && !this.#info.compressed) {
144
- failWebsocketConnection(this.ws, 'Unexpected continuation frame')
145
+ failWebsocketConnection(this.#handler, 1002, 'Unexpected continuation frame')
145
146
  return
146
147
  }
147
148
 
@@ -187,7 +188,7 @@ class ByteParser extends Writable {
187
188
  // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/common/globals.h;drc=1946212ac0100668f14eb9e2843bdd846e510a1e;bpv=1;bpt=1;l=1275
188
189
  // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-array-buffer.h;l=34;drc=1946212ac0100668f14eb9e2843bdd846e510a1e
189
190
  if (upper > 2 ** 31 - 1) {
190
- failWebsocketConnection(this.ws, 'Received payload length > 2^31 bytes.')
191
+ failWebsocketConnection(this.#handler, 1009, 'Received payload length > 2^31 bytes.')
191
192
  return
192
193
  }
193
194
 
@@ -215,7 +216,7 @@ class ByteParser extends Writable {
215
216
  // parsing continuation frames, not here.
216
217
  if (!this.#info.fragmented && this.#info.fin) {
217
218
  const fullMessage = Buffer.concat(this.#fragments)
218
- websocketMessageReceived(this.ws, this.#info.binaryType, fullMessage)
219
+ websocketMessageReceived(this.#handler, this.#info.binaryType, fullMessage)
219
220
  this.#fragments.length = 0
220
221
  }
221
222
 
@@ -223,7 +224,7 @@ class ByteParser extends Writable {
223
224
  } else {
224
225
  this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => {
225
226
  if (error) {
226
- closeWebSocketConnection(this.ws, 1007, error.message, error.message.length)
227
+ failWebsocketConnection(this.#handler, 1007, error.message)
227
228
  return
228
229
  }
229
230
 
@@ -236,7 +237,7 @@ class ByteParser extends Writable {
236
237
  return
237
238
  }
238
239
 
239
- websocketMessageReceived(this.ws, this.#info.binaryType, Buffer.concat(this.#fragments))
240
+ websocketMessageReceived(this.#handler, this.#info.binaryType, Buffer.concat(this.#fragments))
240
241
 
241
242
  this.#loop = true
242
243
  this.#state = parserStates.INFO
@@ -339,7 +340,7 @@ class ByteParser extends Writable {
339
340
 
340
341
  if (opcode === opcodes.CLOSE) {
341
342
  if (payloadLength === 1) {
342
- failWebsocketConnection(this.ws, 'Received close frame with a 1-byte body.')
343
+ failWebsocketConnection(this.#handler, 1002, 'Received close frame with a 1-byte body.')
343
344
  return false
344
345
  }
345
346
 
@@ -348,12 +349,13 @@ class ByteParser extends Writable {
348
349
  if (this.#info.closeInfo.error) {
349
350
  const { code, reason } = this.#info.closeInfo
350
351
 
351
- closeWebSocketConnection(this.ws, code, reason, reason.length)
352
- failWebsocketConnection(this.ws, reason)
352
+ failWebsocketConnection(this.#handler, code, reason)
353
353
  return false
354
354
  }
355
355
 
356
- if (this.ws[kSentClose] !== sentCloseFrameState.SENT) {
356
+ // Upon receiving such a frame, the other peer sends a
357
+ // Close frame in response, if it hasn't already sent one.
358
+ if (!this.#handler.closeState.has(sentCloseFrameState.SENT) && !this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
357
359
  // If an endpoint receives a Close frame and did not previously send a
358
360
  // Close frame, the endpoint MUST send a Close frame in response. (When
359
361
  // sending a Close frame in response, the endpoint typically echos the
@@ -365,21 +367,15 @@ class ByteParser extends Writable {
365
367
  }
366
368
  const closeFrame = new WebsocketFrameSend(body)
367
369
 
368
- this.ws[kResponse].socket.write(
369
- closeFrame.createFrame(opcodes.CLOSE),
370
- (err) => {
371
- if (!err) {
372
- this.ws[kSentClose] = sentCloseFrameState.SENT
373
- }
374
- }
375
- )
370
+ this.#handler.socket.write(closeFrame.createFrame(opcodes.CLOSE))
371
+ this.#handler.closeState.add(sentCloseFrameState.SENT)
376
372
  }
377
373
 
378
374
  // Upon either sending or receiving a Close control frame, it is said
379
375
  // that _The WebSocket Closing Handshake is Started_ and that the
380
376
  // WebSocket connection is in the CLOSING state.
381
- this.ws[kReadyState] = states.CLOSING
382
- this.ws[kReceivedClose] = true
377
+ this.#handler.readyState = states.CLOSING
378
+ this.#handler.closeState.add(sentCloseFrameState.RECEIVED)
383
379
 
384
380
  return false
385
381
  } else if (opcode === opcodes.PING) {
@@ -388,10 +384,10 @@ class ByteParser extends Writable {
388
384
  // A Pong frame sent in response to a Ping frame must have identical
389
385
  // "Application data"
390
386
 
391
- if (!this.ws[kReceivedClose]) {
387
+ if (!this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
392
388
  const frame = new WebsocketFrameSend(body)
393
389
 
394
- this.ws[kResponse].socket.write(frame.createFrame(opcodes.PONG))
390
+ this.#handler.socket.write(frame.createFrame(opcodes.PONG))
395
391
 
396
392
  if (channels.ping.hasSubscribers) {
397
393
  channels.ping.publish({
@@ -4,9 +4,6 @@ const { WebsocketFrameSend } = require('./frame')
4
4
  const { opcodes, sendHints } = require('./constants')
5
5
  const FixedQueue = require('../../dispatcher/fixed-queue')
6
6
 
7
- /** @type {typeof Uint8Array} */
8
- const FastBuffer = Buffer[Symbol.species]
9
-
10
7
  /**
11
8
  * @typedef {object} SendQueueNode
12
9
  * @property {Promise<void> | null} promise
@@ -34,16 +31,25 @@ class SendQueue {
34
31
 
35
32
  add (item, cb, hint) {
36
33
  if (hint !== sendHints.blob) {
37
- const frame = createFrame(item, hint)
38
34
  if (!this.#running) {
39
- // fast-path
40
- this.#socket.write(frame, cb)
35
+ // TODO(@tsctx): support fast-path for string on running
36
+ if (hint === sendHints.text) {
37
+ // special fast-path for string
38
+ const { 0: head, 1: body } = WebsocketFrameSend.createFastTextFrame(item)
39
+ this.#socket.cork()
40
+ this.#socket.write(head)
41
+ this.#socket.write(body, cb)
42
+ this.#socket.uncork()
43
+ } else {
44
+ // direct writing
45
+ this.#socket.write(createFrame(item, hint), cb)
46
+ }
41
47
  } else {
42
48
  /** @type {SendQueueNode} */
43
49
  const node = {
44
50
  promise: null,
45
51
  callback: cb,
46
- frame
52
+ frame: createFrame(item, hint)
47
53
  }
48
54
  this.#queue.push(node)
49
55
  }
@@ -86,18 +92,17 @@ class SendQueue {
86
92
  }
87
93
 
88
94
  function createFrame (data, hint) {
89
- return new WebsocketFrameSend(toBuffer(data, hint)).createFrame(hint === sendHints.string ? opcodes.TEXT : opcodes.BINARY)
95
+ return new WebsocketFrameSend(toBuffer(data, hint)).createFrame(hint === sendHints.text ? opcodes.TEXT : opcodes.BINARY)
90
96
  }
91
97
 
92
98
  function toBuffer (data, hint) {
93
99
  switch (hint) {
94
- case sendHints.string:
95
- return Buffer.from(data)
100
+ case sendHints.text:
101
+ case sendHints.typedArray:
102
+ return new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
96
103
  case sendHints.arrayBuffer:
97
104
  case sendHints.blob:
98
- return new FastBuffer(data)
99
- case sendHints.typedArray:
100
- return new FastBuffer(data.buffer, data.byteOffset, data.byteLength)
105
+ return new Uint8Array(data)
101
106
  }
102
107
  }
103
108
 
@@ -0,0 +1,83 @@
1
+ 'use strict'
2
+
3
+ const { webidl } = require('../../fetch/webidl')
4
+ const { validateCloseCodeAndReason } = require('../util')
5
+ const { kConstruct } = require('../../../core/symbols')
6
+ const { kEnumerableProperty } = require('../../../core/util')
7
+
8
+ class WebSocketError extends DOMException {
9
+ #closeCode
10
+ #reason
11
+
12
+ constructor (message = '', init = undefined) {
13
+ message = webidl.converters.DOMString(message, 'WebSocketError', 'message')
14
+
15
+ // 1. Set this 's name to " WebSocketError ".
16
+ // 2. Set this 's message to message .
17
+ super(message, 'WebSocketError')
18
+
19
+ if (init === kConstruct) {
20
+ return
21
+ } else if (init !== null) {
22
+ init = webidl.converters.WebSocketCloseInfo(init)
23
+ }
24
+
25
+ // 3. Let code be init [" closeCode "] if it exists , or null otherwise.
26
+ let code = init.closeCode ?? null
27
+
28
+ // 4. Let reason be init [" reason "] if it exists , or the empty string otherwise.
29
+ const reason = init.reason ?? ''
30
+
31
+ // 5. Validate close code and reason with code and reason .
32
+ validateCloseCodeAndReason(code, reason)
33
+
34
+ // 6. If reason is non-empty, but code is not set, then set code to 1000 ("Normal Closure").
35
+ if (reason.length !== 0 && code === null) {
36
+ code = 1000
37
+ }
38
+
39
+ // 7. Set this 's closeCode to code .
40
+ this.#closeCode = code
41
+
42
+ // 8. Set this 's reason to reason .
43
+ this.#reason = reason
44
+ }
45
+
46
+ get closeCode () {
47
+ return this.#closeCode
48
+ }
49
+
50
+ get reason () {
51
+ return this.#reason
52
+ }
53
+
54
+ /**
55
+ * @param {string} message
56
+ * @param {number|null} code
57
+ * @param {string} reason
58
+ */
59
+ static createUnvalidatedWebSocketError (message, code, reason) {
60
+ const error = new WebSocketError(message, kConstruct)
61
+ error.#closeCode = code
62
+ error.#reason = reason
63
+ return error
64
+ }
65
+ }
66
+
67
+ const { createUnvalidatedWebSocketError } = WebSocketError
68
+ delete WebSocketError.createUnvalidatedWebSocketError
69
+
70
+ Object.defineProperties(WebSocketError.prototype, {
71
+ closeCode: kEnumerableProperty,
72
+ reason: kEnumerableProperty,
73
+ [Symbol.toStringTag]: {
74
+ value: 'WebSocketError',
75
+ writable: false,
76
+ enumerable: false,
77
+ configurable: true
78
+ }
79
+ })
80
+
81
+ webidl.is.WebSocketError = webidl.util.MakeTypeAssertion(WebSocketError.prototype)
82
+
83
+ module.exports = { WebSocketError, createUnvalidatedWebSocketError }