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.
- package/README.md +6 -10
- package/docs/docs/api/Agent.md +0 -3
- package/docs/docs/api/Client.md +1 -3
- package/docs/docs/api/Debug.md +1 -1
- package/docs/docs/api/Dispatcher.md +60 -8
- package/docs/docs/api/EnvHttpProxyAgent.md +0 -1
- package/docs/docs/api/Fetch.md +1 -0
- package/docs/docs/api/MockAgent.md +2 -0
- package/docs/docs/api/MockPool.md +2 -1
- package/docs/docs/api/Pool.md +0 -1
- package/docs/docs/api/RetryAgent.md +1 -1
- package/docs/docs/api/RetryHandler.md +1 -1
- package/docs/docs/api/WebSocket.md +45 -3
- package/index.js +6 -6
- package/lib/api/abort-signal.js +2 -0
- package/lib/api/api-connect.js +3 -1
- package/lib/api/api-pipeline.js +7 -6
- package/lib/api/api-request.js +32 -47
- package/lib/api/api-stream.js +39 -50
- package/lib/api/api-upgrade.js +5 -3
- package/lib/api/readable.js +261 -64
- package/lib/api/util.js +2 -0
- package/lib/core/constants.js +11 -9
- package/lib/core/diagnostics.js +122 -128
- package/lib/core/errors.js +4 -4
- package/lib/core/request.js +11 -9
- package/lib/core/symbols.js +2 -1
- package/lib/core/tree.js +9 -1
- package/lib/core/util.js +219 -48
- package/lib/dispatcher/agent.js +3 -17
- package/lib/dispatcher/balanced-pool.js +5 -8
- package/lib/dispatcher/client-h1.js +278 -54
- package/lib/dispatcher/client-h2.js +1 -1
- package/lib/dispatcher/client.js +23 -34
- package/lib/dispatcher/dispatcher-base.js +2 -34
- package/lib/dispatcher/dispatcher.js +3 -24
- package/lib/dispatcher/fixed-queue.js +91 -49
- package/lib/dispatcher/pool-stats.js +2 -0
- package/lib/dispatcher/pool.js +3 -6
- package/lib/dispatcher/proxy-agent.js +6 -7
- package/lib/handler/decorator-handler.js +24 -0
- package/lib/handler/redirect-handler.js +11 -2
- package/lib/handler/retry-handler.js +12 -3
- package/lib/interceptor/dns.js +346 -0
- package/lib/interceptor/dump.js +2 -2
- package/lib/interceptor/redirect.js +11 -14
- package/lib/interceptor/response-error.js +4 -1
- package/lib/llhttp/constants.d.ts +97 -0
- package/lib/llhttp/constants.js +412 -192
- package/lib/llhttp/constants.js.map +1 -0
- package/lib/llhttp/llhttp-wasm.js +11 -1
- package/lib/llhttp/llhttp_simd-wasm.js +11 -1
- package/lib/llhttp/utils.d.ts +2 -0
- package/lib/llhttp/utils.js +9 -9
- package/lib/llhttp/utils.js.map +1 -0
- package/lib/mock/mock-agent.js +5 -8
- package/lib/mock/mock-client.js +9 -4
- package/lib/mock/mock-errors.js +3 -1
- package/lib/mock/mock-interceptor.js +8 -6
- package/lib/mock/mock-pool.js +9 -4
- package/lib/mock/mock-symbols.js +3 -1
- package/lib/mock/mock-utils.js +29 -5
- package/lib/web/cache/cache.js +24 -21
- package/lib/web/cache/cachestorage.js +1 -1
- package/lib/web/cookies/index.js +17 -13
- package/lib/web/cookies/parse.js +2 -2
- package/lib/web/eventsource/eventsource-stream.js +9 -8
- package/lib/web/eventsource/eventsource.js +10 -6
- package/lib/web/fetch/body.js +42 -36
- package/lib/web/fetch/constants.js +35 -26
- package/lib/web/fetch/data-url.js +1 -1
- package/lib/web/fetch/formdata-parser.js +2 -2
- package/lib/web/fetch/formdata.js +65 -54
- package/lib/web/fetch/headers.js +117 -85
- package/lib/web/fetch/index.js +55 -62
- package/lib/web/fetch/request.js +135 -77
- package/lib/web/fetch/response.js +86 -56
- package/lib/web/fetch/util.js +90 -64
- package/lib/web/fetch/webidl.js +99 -64
- package/lib/web/websocket/connection.js +76 -147
- package/lib/web/websocket/constants.js +3 -4
- package/lib/web/websocket/events.js +4 -2
- package/lib/web/websocket/frame.js +45 -3
- package/lib/web/websocket/receiver.js +29 -33
- package/lib/web/websocket/sender.js +18 -13
- package/lib/web/websocket/stream/websocketerror.js +83 -0
- package/lib/web/websocket/stream/websocketstream.js +485 -0
- package/lib/web/websocket/util.js +128 -77
- package/lib/web/websocket/websocket.js +234 -135
- package/package.json +20 -33
- package/scripts/strip-comments.js +3 -1
- package/types/agent.d.ts +7 -7
- package/types/api.d.ts +24 -24
- package/types/balanced-pool.d.ts +11 -11
- package/types/client.d.ts +11 -12
- package/types/diagnostics-channel.d.ts +10 -10
- package/types/dispatcher.d.ts +96 -97
- package/types/env-http-proxy-agent.d.ts +2 -2
- package/types/errors.d.ts +53 -47
- package/types/fetch.d.ts +8 -8
- package/types/formdata.d.ts +7 -7
- package/types/global-dispatcher.d.ts +4 -4
- package/types/global-origin.d.ts +5 -5
- package/types/handlers.d.ts +4 -4
- package/types/header.d.ts +157 -1
- package/types/index.d.ts +42 -46
- package/types/interceptors.d.ts +22 -8
- package/types/mock-agent.d.ts +21 -18
- package/types/mock-client.d.ts +4 -4
- package/types/mock-errors.d.ts +3 -3
- package/types/mock-interceptor.d.ts +19 -19
- package/types/mock-pool.d.ts +4 -4
- package/types/patch.d.ts +0 -4
- package/types/pool-stats.d.ts +8 -8
- package/types/pool.d.ts +12 -12
- package/types/proxy-agent.d.ts +4 -4
- package/types/readable.d.ts +22 -14
- package/types/retry-agent.d.ts +1 -1
- package/types/retry-handler.d.ts +8 -8
- package/types/util.d.ts +3 -3
- package/types/utility.d.ts +7 -0
- package/types/webidl.d.ts +44 -6
- package/types/websocket.d.ts +34 -1
- package/docs/docs/api/DispatchInterceptor.md +0 -60
- package/lib/interceptor/redirect-interceptor.js +0 -21
- package/lib/mock/pluralizer.js +0 -29
- package/lib/web/cache/symbols.js +0 -5
- package/lib/web/fetch/file.js +0 -126
- package/lib/web/fetch/symbols.js +0 -9
- package/lib/web/fileapi/encoding.js +0 -290
- package/lib/web/fileapi/filereader.js +0 -344
- package/lib/web/fileapi/progressevent.js +0 -78
- package/lib/web/fileapi/symbols.js +0 -10
- package/lib/web/fileapi/util.js +0 -391
- package/lib/web/websocket/symbols.js +0 -12
- package/types/file.d.ts +0 -39
- 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(
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
38
|
+
/** @type {import('./websocket').Handler} */
|
|
39
|
+
#handler
|
|
40
|
+
|
|
41
|
+
constructor (handler, extensions) {
|
|
41
42
|
super()
|
|
42
43
|
|
|
43
|
-
this
|
|
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
|
|
90
|
+
failWebsocketConnection(this.#handler, 1002, 'Invalid opcode received')
|
|
90
91
|
return callback()
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
if (masked) {
|
|
94
|
-
failWebsocketConnection(this
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
352
|
-
failWebsocketConnection(this.ws, reason)
|
|
352
|
+
failWebsocketConnection(this.#handler, code, reason)
|
|
353
353
|
return false
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
-
|
|
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.
|
|
369
|
-
|
|
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.
|
|
382
|
-
this.
|
|
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.
|
|
387
|
+
if (!this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
|
|
392
388
|
const frame = new WebsocketFrameSend(body)
|
|
393
389
|
|
|
394
|
-
this.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
95
|
-
|
|
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
|
|
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 }
|