undici 6.14.1 → 6.16.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.
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { webidl } = require('../fetch/webidl')
4
4
  const { kEnumerableProperty } = require('../../core/util')
5
+ const { kConstruct } = require('../../core/symbols')
5
6
  const { MessagePort } = require('node:worker_threads')
6
7
 
7
8
  /**
@@ -11,10 +12,16 @@ class MessageEvent extends Event {
11
12
  #eventInit
12
13
 
13
14
  constructor (type, eventInitDict = {}) {
14
- webidl.argumentLengthCheck(arguments, 1, { header: 'MessageEvent constructor' })
15
+ if (type === kConstruct) {
16
+ super(arguments[1], arguments[2])
17
+ return
18
+ }
19
+
20
+ const prefix = 'MessageEvent constructor'
21
+ webidl.argumentLengthCheck(arguments, 1, prefix)
15
22
 
16
- type = webidl.converters.DOMString(type)
17
- eventInitDict = webidl.converters.MessageEventInit(eventInitDict)
23
+ type = webidl.converters.DOMString(type, prefix, 'type')
24
+ eventInitDict = webidl.converters.MessageEventInit(eventInitDict, prefix, 'eventInitDict')
18
25
 
19
26
  super(type, eventInitDict)
20
27
 
@@ -67,14 +74,28 @@ class MessageEvent extends Event {
67
74
  ) {
68
75
  webidl.brandCheck(this, MessageEvent)
69
76
 
70
- webidl.argumentLengthCheck(arguments, 1, { header: 'MessageEvent.initMessageEvent' })
77
+ webidl.argumentLengthCheck(arguments, 1, 'MessageEvent.initMessageEvent')
71
78
 
72
79
  return new MessageEvent(type, {
73
80
  bubbles, cancelable, data, origin, lastEventId, source, ports
74
81
  })
75
82
  }
83
+
84
+ static createFastMessageEvent (type, init) {
85
+ const messageEvent = new MessageEvent(kConstruct, type, init)
86
+ messageEvent.#eventInit = init
87
+ messageEvent.#eventInit.data ??= null
88
+ messageEvent.#eventInit.origin ??= ''
89
+ messageEvent.#eventInit.lastEventId ??= ''
90
+ messageEvent.#eventInit.source ??= null
91
+ messageEvent.#eventInit.ports ??= []
92
+ return messageEvent
93
+ }
76
94
  }
77
95
 
96
+ const { createFastMessageEvent } = MessageEvent
97
+ delete MessageEvent.createFastMessageEvent
98
+
78
99
  /**
79
100
  * @see https://websockets.spec.whatwg.org/#the-closeevent-interface
80
101
  */
@@ -82,9 +103,10 @@ class CloseEvent extends Event {
82
103
  #eventInit
83
104
 
84
105
  constructor (type, eventInitDict = {}) {
85
- webidl.argumentLengthCheck(arguments, 1, { header: 'CloseEvent constructor' })
106
+ const prefix = 'CloseEvent constructor'
107
+ webidl.argumentLengthCheck(arguments, 1, prefix)
86
108
 
87
- type = webidl.converters.DOMString(type)
109
+ type = webidl.converters.DOMString(type, prefix, 'type')
88
110
  eventInitDict = webidl.converters.CloseEventInit(eventInitDict)
89
111
 
90
112
  super(type, eventInitDict)
@@ -116,11 +138,12 @@ class ErrorEvent extends Event {
116
138
  #eventInit
117
139
 
118
140
  constructor (type, eventInitDict) {
119
- webidl.argumentLengthCheck(arguments, 1, { header: 'ErrorEvent constructor' })
141
+ const prefix = 'ErrorEvent constructor'
142
+ webidl.argumentLengthCheck(arguments, 1, prefix)
120
143
 
121
144
  super(type, eventInitDict)
122
145
 
123
- type = webidl.converters.DOMString(type)
146
+ type = webidl.converters.DOMString(type, prefix, 'type')
124
147
  eventInitDict = webidl.converters.ErrorEventInit(eventInitDict ?? {})
125
148
 
126
149
  this.#eventInit = eventInitDict
@@ -202,17 +225,17 @@ const eventInit = [
202
225
  {
203
226
  key: 'bubbles',
204
227
  converter: webidl.converters.boolean,
205
- defaultValue: false
228
+ defaultValue: () => false
206
229
  },
207
230
  {
208
231
  key: 'cancelable',
209
232
  converter: webidl.converters.boolean,
210
- defaultValue: false
233
+ defaultValue: () => false
211
234
  },
212
235
  {
213
236
  key: 'composed',
214
237
  converter: webidl.converters.boolean,
215
- defaultValue: false
238
+ defaultValue: () => false
216
239
  }
217
240
  ]
218
241
 
@@ -221,31 +244,29 @@ webidl.converters.MessageEventInit = webidl.dictionaryConverter([
221
244
  {
222
245
  key: 'data',
223
246
  converter: webidl.converters.any,
224
- defaultValue: null
247
+ defaultValue: () => null
225
248
  },
226
249
  {
227
250
  key: 'origin',
228
251
  converter: webidl.converters.USVString,
229
- defaultValue: ''
252
+ defaultValue: () => ''
230
253
  },
231
254
  {
232
255
  key: 'lastEventId',
233
256
  converter: webidl.converters.DOMString,
234
- defaultValue: ''
257
+ defaultValue: () => ''
235
258
  },
236
259
  {
237
260
  key: 'source',
238
261
  // Node doesn't implement WindowProxy or ServiceWorker, so the only
239
262
  // valid value for source is a MessagePort.
240
263
  converter: webidl.nullableConverter(webidl.converters.MessagePort),
241
- defaultValue: null
264
+ defaultValue: () => null
242
265
  },
243
266
  {
244
267
  key: 'ports',
245
268
  converter: webidl.converters['sequence<MessagePort>'],
246
- get defaultValue () {
247
- return []
248
- }
269
+ defaultValue: () => new Array(0)
249
270
  }
250
271
  ])
251
272
 
@@ -254,17 +275,17 @@ webidl.converters.CloseEventInit = webidl.dictionaryConverter([
254
275
  {
255
276
  key: 'wasClean',
256
277
  converter: webidl.converters.boolean,
257
- defaultValue: false
278
+ defaultValue: () => false
258
279
  },
259
280
  {
260
281
  key: 'code',
261
282
  converter: webidl.converters['unsigned short'],
262
- defaultValue: 0
283
+ defaultValue: () => 0
263
284
  },
264
285
  {
265
286
  key: 'reason',
266
287
  converter: webidl.converters.USVString,
267
- defaultValue: ''
288
+ defaultValue: () => ''
268
289
  }
269
290
  ])
270
291
 
@@ -273,22 +294,22 @@ webidl.converters.ErrorEventInit = webidl.dictionaryConverter([
273
294
  {
274
295
  key: 'message',
275
296
  converter: webidl.converters.DOMString,
276
- defaultValue: ''
297
+ defaultValue: () => ''
277
298
  },
278
299
  {
279
300
  key: 'filename',
280
301
  converter: webidl.converters.USVString,
281
- defaultValue: ''
302
+ defaultValue: () => ''
282
303
  },
283
304
  {
284
305
  key: 'lineno',
285
306
  converter: webidl.converters['unsigned long'],
286
- defaultValue: 0
307
+ defaultValue: () => 0
287
308
  },
288
309
  {
289
310
  key: 'colno',
290
311
  converter: webidl.converters['unsigned long'],
291
- defaultValue: 0
312
+ defaultValue: () => 0
292
313
  },
293
314
  {
294
315
  key: 'error',
@@ -299,5 +320,6 @@ webidl.converters.ErrorEventInit = webidl.dictionaryConverter([
299
320
  module.exports = {
300
321
  MessageEvent,
301
322
  CloseEvent,
302
- ErrorEvent
323
+ ErrorEvent,
324
+ createFastMessageEvent
303
325
  }
@@ -2,13 +2,34 @@
2
2
 
3
3
  const { maxUnsigned16Bit } = require('./constants')
4
4
 
5
+ const BUFFER_SIZE = 16386
6
+
5
7
  /** @type {import('crypto')} */
6
8
  let crypto
9
+ let buffer = null
10
+ let bufIdx = BUFFER_SIZE
11
+
7
12
  try {
8
13
  crypto = require('node:crypto')
9
14
  /* c8 ignore next 3 */
10
15
  } catch {
16
+ crypto = {
17
+ // not full compatibility, but minimum.
18
+ randomFillSync: function randomFillSync (buffer, _offset, _size) {
19
+ for (let i = 0; i < buffer.length; ++i) {
20
+ buffer[i] = Math.random() * 255 | 0
21
+ }
22
+ return buffer
23
+ }
24
+ }
25
+ }
11
26
 
27
+ function generateMask () {
28
+ if (bufIdx === BUFFER_SIZE) {
29
+ bufIdx = 0
30
+ crypto.randomFillSync((buffer ??= Buffer.allocUnsafe(BUFFER_SIZE)), 0, BUFFER_SIZE)
31
+ }
32
+ return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]]
12
33
  }
13
34
 
14
35
  class WebsocketFrameSend {
@@ -17,11 +38,12 @@ class WebsocketFrameSend {
17
38
  */
18
39
  constructor (data) {
19
40
  this.frameData = data
20
- this.maskKey = crypto.randomBytes(4)
21
41
  }
22
42
 
23
43
  createFrame (opcode) {
24
- const bodyLength = this.frameData?.byteLength ?? 0
44
+ const frameData = this.frameData
45
+ const maskKey = generateMask()
46
+ const bodyLength = frameData?.byteLength ?? 0
25
47
 
26
48
  /** @type {number} */
27
49
  let payloadLength = bodyLength // 0-125
@@ -43,10 +65,10 @@ class WebsocketFrameSend {
43
65
  buffer[0] = (buffer[0] & 0xF0) + opcode // opcode
44
66
 
45
67
  /*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> */
46
- buffer[offset - 4] = this.maskKey[0]
47
- buffer[offset - 3] = this.maskKey[1]
48
- buffer[offset - 2] = this.maskKey[2]
49
- buffer[offset - 1] = this.maskKey[3]
68
+ buffer[offset - 4] = maskKey[0]
69
+ buffer[offset - 3] = maskKey[1]
70
+ buffer[offset - 2] = maskKey[2]
71
+ buffer[offset - 1] = maskKey[3]
50
72
 
51
73
  buffer[1] = payloadLength
52
74
 
@@ -61,8 +83,8 @@ class WebsocketFrameSend {
61
83
  buffer[1] |= 0x80 // MASK
62
84
 
63
85
  // mask body
64
- for (let i = 0; i < bodyLength; i++) {
65
- buffer[offset + i] = this.frameData[i] ^ this.maskKey[i % 4]
86
+ for (let i = 0; i < bodyLength; ++i) {
87
+ buffer[offset + i] = frameData[i] ^ maskKey[i & 3]
66
88
  }
67
89
 
68
90
  return buffer
@@ -6,6 +6,7 @@ const { kReadyState, kSentClose, kResponse, kReceivedClose } = require('./symbol
6
6
  const { channels } = require('../../core/diagnostics')
7
7
  const { isValidStatusCode, failWebsocketConnection, websocketMessageReceived, utf8Decode } = require('./util')
8
8
  const { WebsocketFrameSend } = require('./frame')
9
+ const { CloseEvent } = require('./events')
9
10
 
10
11
  // This code was influenced by ws released under the MIT license.
11
12
  // Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
@@ -55,6 +56,12 @@ class ByteParser extends Writable {
55
56
 
56
57
  this.#info.fin = (buffer[0] & 0x80) !== 0
57
58
  this.#info.opcode = buffer[0] & 0x0F
59
+ this.#info.masked = (buffer[1] & 0x80) === 0x80
60
+
61
+ if (this.#info.masked) {
62
+ failWebsocketConnection(this.ws, 'Frame cannot be masked')
63
+ return callback()
64
+ }
58
65
 
59
66
  // If we receive a fragmented message, we use the type of the first
60
67
  // frame to parse the full message as binary/text, when it's terminated
@@ -102,6 +109,13 @@ class ByteParser extends Writable {
102
109
 
103
110
  this.#info.closeInfo = this.parseCloseBody(body)
104
111
 
112
+ if (this.#info.closeInfo.error) {
113
+ const { code, reason } = this.#info.closeInfo
114
+
115
+ callback(new CloseEvent('close', { wasClean: false, reason, code }))
116
+ return
117
+ }
118
+
105
119
  if (this.ws[kSentClose] !== sentCloseFrameState.SENT) {
106
120
  // If an endpoint receives a Close frame and did not previously send a
107
121
  // Close frame, the endpoint MUST send a Close frame in response. (When
@@ -239,7 +253,7 @@ class ByteParser extends Writable {
239
253
  }
240
254
  }
241
255
 
242
- if (this.#byteOffset === 0) {
256
+ if (this.#byteOffset === 0 && this.#info.payloadLength !== 0) {
243
257
  callback()
244
258
  break
245
259
  }
@@ -310,16 +324,16 @@ class ByteParser extends Writable {
310
324
  }
311
325
 
312
326
  if (code !== undefined && !isValidStatusCode(code)) {
313
- return null
327
+ return { code: 1002, reason: 'Invalid status code', error: true }
314
328
  }
315
329
 
316
330
  try {
317
331
  reason = utf8Decode(reason)
318
332
  } catch {
319
- return null
333
+ return { code: 1007, reason: 'Invalid UTF-8', error: true }
320
334
  }
321
335
 
322
- return { code, reason }
336
+ return { code, reason, error: false }
323
337
  }
324
338
 
325
339
  get closingInfo () {
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { kReadyState, kController, kResponse, kBinaryType, kWebSocketURL } = require('./symbols')
4
4
  const { states, opcodes } = require('./constants')
5
- const { MessageEvent, ErrorEvent } = require('./events')
5
+ const { ErrorEvent, createFastMessageEvent } = require('./events')
6
6
  const { isUtf8 } = require('node:buffer')
7
7
 
8
8
  /* globals Blob */
@@ -51,15 +51,16 @@ function isClosed (ws) {
51
51
  * @see https://dom.spec.whatwg.org/#concept-event-fire
52
52
  * @param {string} e
53
53
  * @param {EventTarget} target
54
+ * @param {(...args: ConstructorParameters<typeof Event>) => Event} eventFactory
54
55
  * @param {EventInit | undefined} eventInitDict
55
56
  */
56
- function fireEvent (e, target, eventConstructor = Event, eventInitDict = {}) {
57
+ function fireEvent (e, target, eventFactory = (type, init) => new Event(type, init), eventInitDict = {}) {
57
58
  // 1. If eventConstructor is not given, then let eventConstructor be Event.
58
59
 
59
60
  // 2. Let event be the result of creating an event given eventConstructor,
60
61
  // in the relevant realm of target.
61
62
  // 3. Initialize event’s type attribute to e.
62
- const event = new eventConstructor(e, eventInitDict) // eslint-disable-line new-cap
63
+ const event = eventFactory(e, eventInitDict)
63
64
 
64
65
  // 4. Initialize any other IDL attributes of event as described in the
65
66
  // invocation of this algorithm.
@@ -103,19 +104,26 @@ function websocketMessageReceived (ws, type, data) {
103
104
  // -> type indicates that the data is Binary and binary type is "arraybuffer"
104
105
  // a new ArrayBuffer object, created in the relevant Realm of the
105
106
  // WebSocket object, whose contents are data
106
- dataForEvent = new Uint8Array(data).buffer
107
+ dataForEvent = toArrayBuffer(data)
107
108
  }
108
109
  }
109
110
 
110
111
  // 3. Fire an event named message at the WebSocket object, using MessageEvent,
111
112
  // with the origin attribute initialized to the serialization of the WebSocket
112
113
  // object’s url's origin, and the data attribute initialized to dataForEvent.
113
- fireEvent('message', ws, MessageEvent, {
114
+ fireEvent('message', ws, createFastMessageEvent, {
114
115
  origin: ws[kWebSocketURL].origin,
115
116
  data: dataForEvent
116
117
  })
117
118
  }
118
119
 
120
+ function toArrayBuffer (buffer) {
121
+ if (buffer.byteLength === buffer.buffer.byteLength) {
122
+ return buffer.buffer
123
+ }
124
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)
125
+ }
126
+
119
127
  /**
120
128
  * @see https://datatracker.ietf.org/doc/html/rfc6455
121
129
  * @see https://datatracker.ietf.org/doc/html/rfc2616
@@ -195,8 +203,9 @@ function failWebsocketConnection (ws, reason) {
195
203
 
196
204
  if (reason) {
197
205
  // TODO: process.nextTick
198
- fireEvent('error', ws, ErrorEvent, {
199
- error: new Error(reason)
206
+ fireEvent('error', ws, (type, init) => new ErrorEvent(type, init), {
207
+ error: new Error(reason),
208
+ message: reason
200
209
  })
201
210
  }
202
211
  }
@@ -2,8 +2,8 @@
2
2
 
3
3
  const { webidl } = require('../fetch/webidl')
4
4
  const { URLSerializer } = require('../fetch/data-url')
5
- const { getGlobalOrigin } = require('../fetch/global')
6
- const { staticPropertyDescriptors, states, sentCloseFrameState, opcodes, emptyBuffer } = require('./constants')
5
+ const { environmentSettingsObject } = require('../fetch/util')
6
+ const { staticPropertyDescriptors, states, sentCloseFrameState, opcodes } = require('./constants')
7
7
  const {
8
8
  kWebSocketURL,
9
9
  kReadyState,
@@ -16,21 +16,22 @@ const {
16
16
  const {
17
17
  isConnecting,
18
18
  isEstablished,
19
- isClosed,
20
19
  isClosing,
21
20
  isValidSubprotocol,
22
- failWebsocketConnection,
23
21
  fireEvent
24
22
  } = require('./util')
25
- const { establishWebSocketConnection } = require('./connection')
23
+ const { establishWebSocketConnection, closeWebSocketConnection } = require('./connection')
26
24
  const { WebsocketFrameSend } = require('./frame')
27
25
  const { ByteParser } = require('./receiver')
28
26
  const { kEnumerableProperty, isBlobLike } = require('../../core/util')
29
27
  const { getGlobalDispatcher } = require('../../global')
30
28
  const { types } = require('node:util')
29
+ const { ErrorEvent } = require('./events')
31
30
 
32
31
  let experimentalWarned = false
33
32
 
33
+ const FastBuffer = Buffer[Symbol.species]
34
+
34
35
  // https://websockets.spec.whatwg.org/#interface-definition
35
36
  class WebSocket extends EventTarget {
36
37
  #events = {
@@ -51,7 +52,8 @@ class WebSocket extends EventTarget {
51
52
  constructor (url, protocols = []) {
52
53
  super()
53
54
 
54
- webidl.argumentLengthCheck(arguments, 1, { header: 'WebSocket constructor' })
55
+ const prefix = 'WebSocket constructor'
56
+ webidl.argumentLengthCheck(arguments, 1, prefix)
55
57
 
56
58
  if (!experimentalWarned) {
57
59
  experimentalWarned = true
@@ -60,13 +62,13 @@ class WebSocket extends EventTarget {
60
62
  })
61
63
  }
62
64
 
63
- const options = webidl.converters['DOMString or sequence<DOMString> or WebSocketInit'](protocols)
65
+ const options = webidl.converters['DOMString or sequence<DOMString> or WebSocketInit'](protocols, prefix, 'options')
64
66
 
65
- url = webidl.converters.USVString(url)
67
+ url = webidl.converters.USVString(url, prefix, 'url')
66
68
  protocols = options.protocols
67
69
 
68
70
  // 1. Let baseURL be this's relevant settings object's API base URL.
69
- const baseURL = getGlobalOrigin()
71
+ const baseURL = environmentSettingsObject.settingsObject.baseUrl
70
72
 
71
73
  // 1. Let urlRecord be the result of applying the URL parser to url with baseURL.
72
74
  let urlRecord
@@ -159,12 +161,14 @@ class WebSocket extends EventTarget {
159
161
  close (code = undefined, reason = undefined) {
160
162
  webidl.brandCheck(this, WebSocket)
161
163
 
164
+ const prefix = 'WebSocket.close'
165
+
162
166
  if (code !== undefined) {
163
- code = webidl.converters['unsigned short'](code, { clamp: true })
167
+ code = webidl.converters['unsigned short'](code, prefix, 'code', { clamp: true })
164
168
  }
165
169
 
166
170
  if (reason !== undefined) {
167
- reason = webidl.converters.USVString(reason)
171
+ reason = webidl.converters.USVString(reason, prefix, 'reason')
168
172
  }
169
173
 
170
174
  // 1. If code is present, but is neither an integer equal to 1000 nor an
@@ -194,67 +198,7 @@ class WebSocket extends EventTarget {
194
198
  }
195
199
 
196
200
  // 3. Run the first matching steps from the following list:
197
- if (isClosing(this) || isClosed(this)) {
198
- // If this's ready state is CLOSING (2) or CLOSED (3)
199
- // Do nothing.
200
- } else if (!isEstablished(this)) {
201
- // If the WebSocket connection is not yet established
202
- // Fail the WebSocket connection and set this's ready state
203
- // to CLOSING (2).
204
- failWebsocketConnection(this, 'Connection was closed before it was established.')
205
- this[kReadyState] = WebSocket.CLOSING
206
- } else if (this[kSentClose] === sentCloseFrameState.NOT_SENT) {
207
- // If the WebSocket closing handshake has not yet been started
208
- // Start the WebSocket closing handshake and set this's ready
209
- // state to CLOSING (2).
210
- // - If neither code nor reason is present, the WebSocket Close
211
- // message must not have a body.
212
- // - If code is present, then the status code to use in the
213
- // WebSocket Close message must be the integer given by code.
214
- // - If reason is also present, then reasonBytes must be
215
- // provided in the Close message after the status code.
216
-
217
- this[kSentClose] = sentCloseFrameState.PROCESSING
218
-
219
- const frame = new WebsocketFrameSend()
220
-
221
- // If neither code nor reason is present, the WebSocket Close
222
- // message must not have a body.
223
-
224
- // If code is present, then the status code to use in the
225
- // WebSocket Close message must be the integer given by code.
226
- if (code !== undefined && reason === undefined) {
227
- frame.frameData = Buffer.allocUnsafe(2)
228
- frame.frameData.writeUInt16BE(code, 0)
229
- } else if (code !== undefined && reason !== undefined) {
230
- // If reason is also present, then reasonBytes must be
231
- // provided in the Close message after the status code.
232
- frame.frameData = Buffer.allocUnsafe(2 + reasonByteLength)
233
- frame.frameData.writeUInt16BE(code, 0)
234
- // the body MAY contain UTF-8-encoded data with value /reason/
235
- frame.frameData.write(reason, 2, 'utf-8')
236
- } else {
237
- frame.frameData = emptyBuffer
238
- }
239
-
240
- /** @type {import('stream').Duplex} */
241
- const socket = this[kResponse].socket
242
-
243
- socket.write(frame.createFrame(opcodes.CLOSE), (err) => {
244
- if (!err) {
245
- this[kSentClose] = sentCloseFrameState.SENT
246
- }
247
- })
248
-
249
- // Upon either sending or receiving a Close control frame, it is said
250
- // that _The WebSocket Closing Handshake is Started_ and that the
251
- // WebSocket connection is in the CLOSING state.
252
- this[kReadyState] = states.CLOSING
253
- } else {
254
- // Otherwise
255
- // Set this's ready state to CLOSING (2).
256
- this[kReadyState] = WebSocket.CLOSING
257
- }
201
+ closeWebSocketConnection(this, code, reason, reasonByteLength)
258
202
  }
259
203
 
260
204
  /**
@@ -264,9 +208,10 @@ class WebSocket extends EventTarget {
264
208
  send (data) {
265
209
  webidl.brandCheck(this, WebSocket)
266
210
 
267
- webidl.argumentLengthCheck(arguments, 1, { header: 'WebSocket.send' })
211
+ const prefix = 'WebSocket.send'
212
+ webidl.argumentLengthCheck(arguments, 1, prefix)
268
213
 
269
- data = webidl.converters.WebSocketSendData(data)
214
+ data = webidl.converters.WebSocketSendData(data, prefix, 'data')
270
215
 
271
216
  // 1. If this's ready state is CONNECTING, then throw an
272
217
  // "InvalidStateError" DOMException.
@@ -319,7 +264,7 @@ class WebSocket extends EventTarget {
319
264
  // increase the bufferedAmount attribute by the length of the
320
265
  // ArrayBuffer in bytes.
321
266
 
322
- const value = Buffer.from(data)
267
+ const value = new FastBuffer(data)
323
268
  const frame = new WebsocketFrameSend(value)
324
269
  const buffer = frame.createFrame(opcodes.BINARY)
325
270
 
@@ -340,7 +285,7 @@ class WebSocket extends EventTarget {
340
285
  // not throw an exception must increase the bufferedAmount attribute
341
286
  // by the length of data’s buffer in bytes.
342
287
 
343
- const ab = Buffer.from(data, data.byteOffset, data.byteLength)
288
+ const ab = new FastBuffer(data, data.byteOffset, data.byteLength)
344
289
 
345
290
  const frame = new WebsocketFrameSend(ab)
346
291
  const buffer = frame.createFrame(opcodes.BINARY)
@@ -364,7 +309,7 @@ class WebSocket extends EventTarget {
364
309
  const frame = new WebsocketFrameSend()
365
310
 
366
311
  data.arrayBuffer().then((ab) => {
367
- const value = Buffer.from(ab)
312
+ const value = new FastBuffer(ab)
368
313
  frame.frameData = value
369
314
  const buffer = frame.createFrame(opcodes.BINARY)
370
315
 
@@ -517,9 +462,8 @@ class WebSocket extends EventTarget {
517
462
  this[kResponse] = response
518
463
 
519
464
  const parser = new ByteParser(this)
520
- parser.on('drain', function onParserDrain () {
521
- this.ws[kResponse].socket.resume()
522
- })
465
+ parser.on('drain', onParserDrain)
466
+ parser.on('error', onParserError.bind(this))
523
467
 
524
468
  response.socket.ws = this
525
469
  this[kByteParser] = parser
@@ -595,12 +539,12 @@ webidl.converters['sequence<DOMString>'] = webidl.sequenceConverter(
595
539
  webidl.converters.DOMString
596
540
  )
597
541
 
598
- webidl.converters['DOMString or sequence<DOMString>'] = function (V) {
542
+ webidl.converters['DOMString or sequence<DOMString>'] = function (V, prefix, argument) {
599
543
  if (webidl.util.Type(V) === 'Object' && Symbol.iterator in V) {
600
544
  return webidl.converters['sequence<DOMString>'](V)
601
545
  }
602
546
 
603
- return webidl.converters.DOMString(V)
547
+ return webidl.converters.DOMString(V, prefix, argument)
604
548
  }
605
549
 
606
550
  // This implements the propsal made in https://github.com/whatwg/websockets/issues/42
@@ -608,16 +552,12 @@ webidl.converters.WebSocketInit = webidl.dictionaryConverter([
608
552
  {
609
553
  key: 'protocols',
610
554
  converter: webidl.converters['DOMString or sequence<DOMString>'],
611
- get defaultValue () {
612
- return []
613
- }
555
+ defaultValue: () => new Array(0)
614
556
  },
615
557
  {
616
558
  key: 'dispatcher',
617
559
  converter: (V) => V,
618
- get defaultValue () {
619
- return getGlobalDispatcher()
620
- }
560
+ defaultValue: () => getGlobalDispatcher()
621
561
  },
622
562
  {
623
563
  key: 'headers',
@@ -647,6 +587,16 @@ webidl.converters.WebSocketSendData = function (V) {
647
587
  return webidl.converters.USVString(V)
648
588
  }
649
589
 
590
+ function onParserDrain () {
591
+ this.ws[kResponse].socket.resume()
592
+ }
593
+
594
+ function onParserError (err) {
595
+ fireEvent('error', this, () => new ErrorEvent('error', { error: err, message: err.reason }))
596
+
597
+ closeWebSocketConnection(this, err.code)
598
+ }
599
+
650
600
  module.exports = {
651
601
  WebSocket
652
602
  }