undici 6.6.1 → 6.7.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.
- package/{README.md → docs/README.md} +19 -15
- package/docs/{api → docs/api}/Dispatcher.md +39 -3
- package/docs/docs/api/Fetch.md +57 -0
- package/docs/{api → docs/api}/ProxyAgent.md +3 -1
- package/docs/docs/api/RetryAgent.md +45 -0
- package/docs/{api → docs/api}/RetryHandler.md +1 -1
- package/docs/{api → docs/api}/api-lifecycle.md +33 -4
- package/docs/{best-practices → docs/best-practices}/proxy.md +6 -6
- package/index-fetch.js +11 -7
- package/index.js +31 -25
- package/lib/core/request.js +72 -135
- package/lib/core/symbols.js +6 -5
- package/lib/core/tree.js +46 -26
- package/lib/core/util.js +41 -20
- package/lib/{agent.js → dispatcher/agent.js} +4 -4
- package/lib/{balanced-pool.js → dispatcher/balanced-pool.js} +3 -3
- package/lib/dispatcher/client-h1.js +1339 -0
- package/lib/dispatcher/client-h2.js +639 -0
- package/lib/dispatcher/client.js +611 -0
- package/lib/{dispatcher-base.js → dispatcher/dispatcher-base.js} +2 -2
- package/lib/{pool-base.js → dispatcher/pool-base.js} +3 -3
- package/lib/{pool-stats.js → dispatcher/pool-stats.js} +1 -1
- package/lib/{pool.js → dispatcher/pool.js} +4 -4
- package/lib/{proxy-agent.js → dispatcher/proxy-agent.js} +29 -35
- package/lib/dispatcher/retry-agent.js +35 -0
- package/lib/global.js +1 -1
- package/lib/handler/{RetryHandler.js → retry-handler.js} +2 -2
- package/lib/interceptor/{redirectInterceptor.js → redirect-interceptor.js} +1 -1
- package/lib/mock/mock-agent.js +2 -2
- package/lib/mock/mock-client.js +1 -1
- package/lib/mock/mock-interceptor.js +2 -2
- package/lib/mock/mock-pool.js +1 -1
- package/lib/mock/mock-utils.js +6 -4
- package/lib/{cache → web/cache}/cache.js +2 -4
- package/lib/{cache → web/cache}/cachestorage.js +1 -1
- package/lib/web/cache/symbols.js +5 -0
- package/lib/{cache → web/cache}/util.js +5 -9
- package/lib/{cookies → web/cookies}/parse.js +1 -1
- package/lib/{cookies → web/cookies}/util.js +76 -60
- package/lib/{eventsource → web/eventsource}/eventsource.js +2 -6
- package/lib/{fetch → web/fetch}/body.js +23 -52
- package/lib/{fetch/dataURL.js → web/fetch/data-url.js} +2 -0
- package/lib/{compat → web/fetch}/dispatcher-weakref.js +1 -1
- package/lib/{fetch → web/fetch}/file.js +2 -2
- package/lib/{fetch → web/fetch}/formdata.js +6 -67
- package/lib/{fetch → web/fetch}/headers.js +99 -71
- package/lib/{fetch → web/fetch}/index.js +40 -31
- package/lib/{fetch → web/fetch}/request.js +14 -6
- package/lib/{fetch → web/fetch}/response.js +3 -3
- package/lib/{fetch → web/fetch}/symbols.js +2 -1
- package/lib/{fetch → web/fetch}/util.js +142 -48
- package/lib/{fetch → web/fetch}/webidl.js +53 -19
- package/lib/{fileapi → web/fileapi}/filereader.js +1 -1
- package/lib/{fileapi → web/fileapi}/util.js +1 -1
- package/lib/{websocket → web/websocket}/connection.js +20 -10
- package/lib/{websocket → web/websocket}/constants.js +7 -0
- package/lib/{websocket → web/websocket}/events.js +1 -1
- package/lib/{websocket → web/websocket}/frame.js +1 -0
- package/lib/{websocket → web/websocket}/receiver.js +9 -16
- package/lib/{websocket → web/websocket}/util.js +37 -23
- package/lib/{websocket → web/websocket}/websocket.js +21 -9
- package/package.json +27 -52
- package/types/dispatcher.d.ts +1 -1
- package/types/fetch.d.ts +20 -21
- package/types/index.d.ts +2 -1
- package/types/retry-agent.d.ts +11 -0
- package/types/webidl.d.ts +6 -1
- package/docs/api/Fetch.md +0 -27
- package/docs/assets/lifecycle-diagram.png +0 -0
- package/lib/cache/symbols.js +0 -5
- package/lib/client.js +0 -2295
- package/lib/llhttp/llhttp-wasm.js +0 -3
- package/lib/llhttp/llhttp.wasm +0 -0
- package/lib/llhttp/llhttp_simd.wasm +0 -0
- /package/docs/{api → docs/api}/Agent.md +0 -0
- /package/docs/{api → docs/api}/BalancedPool.md +0 -0
- /package/docs/{api → docs/api}/CacheStorage.md +0 -0
- /package/docs/{api → docs/api}/Client.md +0 -0
- /package/docs/{api → docs/api}/Connector.md +0 -0
- /package/docs/{api → docs/api}/ContentType.md +0 -0
- /package/docs/{api → docs/api}/Cookies.md +0 -0
- /package/docs/{api → docs/api}/Debug.md +0 -0
- /package/docs/{api → docs/api}/DiagnosticsChannel.md +0 -0
- /package/docs/{api → docs/api}/DispatchInterceptor.md +0 -0
- /package/docs/{api → docs/api}/Errors.md +0 -0
- /package/docs/{api → docs/api}/EventSource.md +0 -0
- /package/docs/{api → docs/api}/MockAgent.md +0 -0
- /package/docs/{api → docs/api}/MockClient.md +0 -0
- /package/docs/{api → docs/api}/MockErrors.md +0 -0
- /package/docs/{api → docs/api}/MockPool.md +0 -0
- /package/docs/{api → docs/api}/Pool.md +0 -0
- /package/docs/{api → docs/api}/PoolStats.md +0 -0
- /package/docs/{api → docs/api}/RedirectHandler.md +0 -0
- /package/docs/{api → docs/api}/Util.md +0 -0
- /package/docs/{api → docs/api}/WebSocket.md +0 -0
- /package/docs/{best-practices → docs/best-practices}/client-certificate.md +0 -0
- /package/docs/{best-practices → docs/best-practices}/mocking-request.md +0 -0
- /package/docs/{best-practices → docs/best-practices}/writing-tests.md +0 -0
- /package/lib/{dispatcher.js → dispatcher/dispatcher.js} +0 -0
- /package/lib/{node → dispatcher}/fixed-queue.js +0 -0
- /package/lib/handler/{DecoratorHandler.js → decorator-handler.js} +0 -0
- /package/lib/handler/{RedirectHandler.js → redirect-handler.js} +0 -0
- /package/lib/{timers.js → util/timers.js} +0 -0
- /package/lib/{cookies → web/cookies}/constants.js +0 -0
- /package/lib/{cookies → web/cookies}/index.js +0 -0
- /package/lib/{eventsource → web/eventsource}/eventsource-stream.js +0 -0
- /package/lib/{eventsource → web/eventsource}/util.js +0 -0
- /package/lib/{fetch → web/fetch}/LICENSE +0 -0
- /package/lib/{fetch → web/fetch}/constants.js +0 -0
- /package/lib/{fetch → web/fetch}/global.js +0 -0
- /package/lib/{fileapi → web/fileapi}/encoding.js +0 -0
- /package/lib/{fileapi → web/fileapi}/progressevent.js +0 -0
- /package/lib/{fileapi → web/fileapi}/symbols.js +0 -0
- /package/lib/{websocket → web/websocket}/symbols.js +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { types } = require('node:util')
|
|
4
|
-
const { toUSVString } = require('
|
|
3
|
+
const { types, inspect } = require('node:util')
|
|
4
|
+
const { toUSVString } = require('../../core/util')
|
|
5
5
|
|
|
6
|
-
/** @type {import('
|
|
6
|
+
/** @type {import('../../../types/webidl').Webidl} */
|
|
7
7
|
const webidl = {}
|
|
8
8
|
webidl.converters = {}
|
|
9
9
|
webidl.util = {}
|
|
@@ -34,10 +34,14 @@ webidl.errors.invalidArgument = function (context) {
|
|
|
34
34
|
|
|
35
35
|
// https://webidl.spec.whatwg.org/#implements
|
|
36
36
|
webidl.brandCheck = function (V, I, opts = undefined) {
|
|
37
|
-
if (opts?.strict !== false
|
|
38
|
-
|
|
37
|
+
if (opts?.strict !== false) {
|
|
38
|
+
if (!(V instanceof I)) {
|
|
39
|
+
throw new TypeError('Illegal invocation')
|
|
40
|
+
}
|
|
39
41
|
} else {
|
|
40
|
-
|
|
42
|
+
if (V?.[Symbol.toStringTag] !== I.prototype[Symbol.toStringTag]) {
|
|
43
|
+
throw new TypeError('Illegal invocation')
|
|
44
|
+
}
|
|
41
45
|
}
|
|
42
46
|
}
|
|
43
47
|
|
|
@@ -132,7 +136,7 @@ webidl.util.ConvertToInt = function (V, bitLength, signedness, opts = {}) {
|
|
|
132
136
|
) {
|
|
133
137
|
throw webidl.errors.exception({
|
|
134
138
|
header: 'Integer conversion',
|
|
135
|
-
message: `Could not convert ${V} to an integer.`
|
|
139
|
+
message: `Could not convert ${webidl.util.Stringify(V)} to an integer.`
|
|
136
140
|
})
|
|
137
141
|
}
|
|
138
142
|
|
|
@@ -212,9 +216,24 @@ webidl.util.IntegerPart = function (n) {
|
|
|
212
216
|
return r
|
|
213
217
|
}
|
|
214
218
|
|
|
219
|
+
webidl.util.Stringify = function (V) {
|
|
220
|
+
const type = webidl.util.Type(V)
|
|
221
|
+
|
|
222
|
+
switch (type) {
|
|
223
|
+
case 'Symbol':
|
|
224
|
+
return `Symbol(${V.description})`
|
|
225
|
+
case 'Object':
|
|
226
|
+
return inspect(V)
|
|
227
|
+
case 'String':
|
|
228
|
+
return `"${V}"`
|
|
229
|
+
default:
|
|
230
|
+
return `${V}`
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
215
234
|
// https://webidl.spec.whatwg.org/#es-sequence
|
|
216
235
|
webidl.sequenceConverter = function (converter) {
|
|
217
|
-
return (V) => {
|
|
236
|
+
return (V, Iterable) => {
|
|
218
237
|
// 1. If Type(V) is not Object, throw a TypeError.
|
|
219
238
|
if (webidl.util.Type(V) !== 'Object') {
|
|
220
239
|
throw webidl.errors.exception({
|
|
@@ -225,7 +244,7 @@ webidl.sequenceConverter = function (converter) {
|
|
|
225
244
|
|
|
226
245
|
// 2. Let method be ? GetMethod(V, @@iterator).
|
|
227
246
|
/** @type {Generator} */
|
|
228
|
-
const method = V?.[Symbol.iterator]?.()
|
|
247
|
+
const method = typeof Iterable === 'function' ? Iterable() : V?.[Symbol.iterator]?.()
|
|
229
248
|
const seq = []
|
|
230
249
|
|
|
231
250
|
// 3. If method is undefined, throw a TypeError.
|
|
@@ -269,8 +288,8 @@ webidl.recordConverter = function (keyConverter, valueConverter) {
|
|
|
269
288
|
const result = {}
|
|
270
289
|
|
|
271
290
|
if (!types.isProxy(O)) {
|
|
272
|
-
//
|
|
273
|
-
const keys = Object.
|
|
291
|
+
// 1. Let desc be ? O.[[GetOwnProperty]](key).
|
|
292
|
+
const keys = [...Object.getOwnPropertyNames(O), ...Object.getOwnPropertySymbols(O)]
|
|
274
293
|
|
|
275
294
|
for (const key of keys) {
|
|
276
295
|
// 1. Let typedKey be key converted to an IDL value of type K.
|
|
@@ -320,7 +339,7 @@ webidl.interfaceConverter = function (i) {
|
|
|
320
339
|
if (opts.strict !== false && !(V instanceof i)) {
|
|
321
340
|
throw webidl.errors.exception({
|
|
322
341
|
header: i.name,
|
|
323
|
-
message: `Expected ${V} to be an instance of ${i.name}.`
|
|
342
|
+
message: `Expected ${webidl.util.Stringify(V)} to be an instance of ${i.name}.`
|
|
324
343
|
})
|
|
325
344
|
}
|
|
326
345
|
|
|
@@ -511,8 +530,8 @@ webidl.converters.ArrayBuffer = function (V, opts = {}) {
|
|
|
511
530
|
!types.isAnyArrayBuffer(V)
|
|
512
531
|
) {
|
|
513
532
|
throw webidl.errors.conversionFailed({
|
|
514
|
-
prefix:
|
|
515
|
-
argument:
|
|
533
|
+
prefix: webidl.util.Stringify(V),
|
|
534
|
+
argument: webidl.util.Stringify(V),
|
|
516
535
|
types: ['ArrayBuffer']
|
|
517
536
|
})
|
|
518
537
|
}
|
|
@@ -532,7 +551,12 @@ webidl.converters.ArrayBuffer = function (V, opts = {}) {
|
|
|
532
551
|
// with the [AllowResizable] extended attribute, and
|
|
533
552
|
// IsResizableArrayBuffer(V) is true, then throw a
|
|
534
553
|
// TypeError.
|
|
535
|
-
|
|
554
|
+
if (V.resizable || V.growable) {
|
|
555
|
+
throw webidl.errors.exception({
|
|
556
|
+
header: 'ArrayBuffer',
|
|
557
|
+
message: 'Received a resizable ArrayBuffer.'
|
|
558
|
+
})
|
|
559
|
+
}
|
|
536
560
|
|
|
537
561
|
// 4. Return the IDL ArrayBuffer value that is a
|
|
538
562
|
// reference to the same object as V.
|
|
@@ -552,7 +576,7 @@ webidl.converters.TypedArray = function (V, T, opts = {}) {
|
|
|
552
576
|
) {
|
|
553
577
|
throw webidl.errors.conversionFailed({
|
|
554
578
|
prefix: `${T.name}`,
|
|
555
|
-
argument:
|
|
579
|
+
argument: webidl.util.Stringify(V),
|
|
556
580
|
types: [T.name]
|
|
557
581
|
})
|
|
558
582
|
}
|
|
@@ -572,7 +596,12 @@ webidl.converters.TypedArray = function (V, T, opts = {}) {
|
|
|
572
596
|
// with the [AllowResizable] extended attribute, and
|
|
573
597
|
// IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) is
|
|
574
598
|
// true, then throw a TypeError.
|
|
575
|
-
|
|
599
|
+
if (V.buffer.resizable || V.buffer.growable) {
|
|
600
|
+
throw webidl.errors.exception({
|
|
601
|
+
header: 'ArrayBuffer',
|
|
602
|
+
message: 'Received a resizable ArrayBuffer.'
|
|
603
|
+
})
|
|
604
|
+
}
|
|
576
605
|
|
|
577
606
|
// 5. Return the IDL value of type T that is a reference
|
|
578
607
|
// to the same object as V.
|
|
@@ -604,7 +633,12 @@ webidl.converters.DataView = function (V, opts = {}) {
|
|
|
604
633
|
// with the [AllowResizable] extended attribute, and
|
|
605
634
|
// IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) is
|
|
606
635
|
// true, then throw a TypeError.
|
|
607
|
-
|
|
636
|
+
if (V.buffer.resizable || V.buffer.growable) {
|
|
637
|
+
throw webidl.errors.exception({
|
|
638
|
+
header: 'ArrayBuffer',
|
|
639
|
+
message: 'Received a resizable ArrayBuffer.'
|
|
640
|
+
})
|
|
641
|
+
}
|
|
608
642
|
|
|
609
643
|
// 4. Return the IDL DataView value that is a reference
|
|
610
644
|
// to the same object as V.
|
|
@@ -625,7 +659,7 @@ webidl.converters.BufferSource = function (V, opts = {}) {
|
|
|
625
659
|
return webidl.converters.DataView(V, opts, { ...opts, allowShared: false })
|
|
626
660
|
}
|
|
627
661
|
|
|
628
|
-
throw new TypeError(`Could not convert ${V} to a BufferSource.`)
|
|
662
|
+
throw new TypeError(`Could not convert ${webidl.util.Stringify(V)} to a BufferSource.`)
|
|
629
663
|
}
|
|
630
664
|
|
|
631
665
|
webidl.converters['sequence<ByteString>'] = webidl.sequenceConverter(
|
|
@@ -13,7 +13,7 @@ const {
|
|
|
13
13
|
kAborted
|
|
14
14
|
} = require('./symbols')
|
|
15
15
|
const { webidl } = require('../fetch/webidl')
|
|
16
|
-
const { kEnumerableProperty } = require('
|
|
16
|
+
const { kEnumerableProperty } = require('../../core/util')
|
|
17
17
|
|
|
18
18
|
class FileReader extends EventTarget {
|
|
19
19
|
constructor () {
|
|
@@ -9,7 +9,7 @@ const {
|
|
|
9
9
|
} = require('./symbols')
|
|
10
10
|
const { ProgressEvent } = require('./progressevent')
|
|
11
11
|
const { getEncoding } = require('./encoding')
|
|
12
|
-
const { serializeAMimeType, parseMIMEType } = require('../fetch/
|
|
12
|
+
const { serializeAMimeType, parseMIMEType } = require('../fetch/data-url')
|
|
13
13
|
const { types } = require('node:util')
|
|
14
14
|
const { StringDecoder } = require('string_decoder')
|
|
15
15
|
const { btoa } = require('node:buffer')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { uid, states } = require('./constants')
|
|
3
|
+
const { uid, states, sentCloseFrameState } = require('./constants')
|
|
4
4
|
const {
|
|
5
5
|
kReadyState,
|
|
6
6
|
kSentClose,
|
|
@@ -8,18 +8,19 @@ const {
|
|
|
8
8
|
kReceivedClose
|
|
9
9
|
} = require('./symbols')
|
|
10
10
|
const { fireEvent, failWebsocketConnection } = require('./util')
|
|
11
|
-
const { channels } = require('
|
|
11
|
+
const { channels } = require('../../core/diagnostics')
|
|
12
12
|
const { CloseEvent } = require('./events')
|
|
13
13
|
const { makeRequest } = require('../fetch/request')
|
|
14
14
|
const { fetching } = require('../fetch/index')
|
|
15
15
|
const { Headers } = require('../fetch/headers')
|
|
16
|
-
const {
|
|
17
|
-
const { kHeadersList } = require('
|
|
16
|
+
const { getDecodeSplit } = require('../fetch/util')
|
|
17
|
+
const { kHeadersList } = require('../../core/symbols')
|
|
18
18
|
|
|
19
19
|
/** @type {import('crypto')} */
|
|
20
20
|
let crypto
|
|
21
21
|
try {
|
|
22
22
|
crypto = require('node:crypto')
|
|
23
|
+
/* c8 ignore next 3 */
|
|
23
24
|
} catch {
|
|
24
25
|
|
|
25
26
|
}
|
|
@@ -100,7 +101,7 @@ function establishWebSocketConnection (url, protocols, ws, onEstablish, options)
|
|
|
100
101
|
const controller = fetching({
|
|
101
102
|
request,
|
|
102
103
|
useParallelQueue: true,
|
|
103
|
-
dispatcher: options.dispatcher
|
|
104
|
+
dispatcher: options.dispatcher,
|
|
104
105
|
processResponse (response) {
|
|
105
106
|
// 1. If response is a network error or its status is not 101,
|
|
106
107
|
// fail the WebSocket connection.
|
|
@@ -177,9 +178,18 @@ function establishWebSocketConnection (url, protocols, ws, onEstablish, options)
|
|
|
177
178
|
// the WebSocket Connection_.
|
|
178
179
|
const secProtocol = response.headersList.get('Sec-WebSocket-Protocol')
|
|
179
180
|
|
|
180
|
-
if (secProtocol !== null
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
if (secProtocol !== null) {
|
|
182
|
+
const requestProtocols = getDecodeSplit('sec-websocket-protocol', request.headersList)
|
|
183
|
+
|
|
184
|
+
// The client can request that the server use a specific subprotocol by
|
|
185
|
+
// including the |Sec-WebSocket-Protocol| field in its handshake. If it
|
|
186
|
+
// is specified, the server needs to include the same field and one of
|
|
187
|
+
// the selected subprotocol values in its response for the connection to
|
|
188
|
+
// be established.
|
|
189
|
+
if (!requestProtocols.includes(secProtocol)) {
|
|
190
|
+
failWebsocketConnection(ws, 'Protocol was not set in the opening handshake.')
|
|
191
|
+
return
|
|
192
|
+
}
|
|
183
193
|
}
|
|
184
194
|
|
|
185
195
|
response.socket.on('data', onSocketData)
|
|
@@ -220,7 +230,7 @@ function onSocketClose () {
|
|
|
220
230
|
// If the TCP connection was closed after the
|
|
221
231
|
// WebSocket closing handshake was completed, the WebSocket connection
|
|
222
232
|
// is said to have been closed _cleanly_.
|
|
223
|
-
const wasClean = ws[kSentClose] && ws[kReceivedClose]
|
|
233
|
+
const wasClean = ws[kSentClose] === sentCloseFrameState.SENT && ws[kReceivedClose]
|
|
224
234
|
|
|
225
235
|
let code = 1005
|
|
226
236
|
let reason = ''
|
|
@@ -230,7 +240,7 @@ function onSocketClose () {
|
|
|
230
240
|
if (result) {
|
|
231
241
|
code = result.code ?? 1005
|
|
232
242
|
reason = result.reason
|
|
233
|
-
} else if (
|
|
243
|
+
} else if (ws[kSentClose] !== sentCloseFrameState.SENT) {
|
|
234
244
|
// If _The WebSocket
|
|
235
245
|
// Connection is Closed_ and no Close control frame was received by the
|
|
236
246
|
// endpoint (such as could occur if the underlying transport connection
|
|
@@ -20,6 +20,12 @@ const states = {
|
|
|
20
20
|
CLOSED: 3
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
const sentCloseFrameState = {
|
|
24
|
+
NOT_SENT: 0,
|
|
25
|
+
PROCESSING: 1,
|
|
26
|
+
SENT: 2
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
const opcodes = {
|
|
24
30
|
CONTINUATION: 0x0,
|
|
25
31
|
TEXT: 0x1,
|
|
@@ -42,6 +48,7 @@ const emptyBuffer = Buffer.allocUnsafe(0)
|
|
|
42
48
|
|
|
43
49
|
module.exports = {
|
|
44
50
|
uid,
|
|
51
|
+
sentCloseFrameState,
|
|
45
52
|
staticPropertyDescriptors,
|
|
46
53
|
states,
|
|
47
54
|
opcodes,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { Writable } = require('node:stream')
|
|
4
|
-
const { parserStates, opcodes, states, emptyBuffer } = require('./constants')
|
|
4
|
+
const { parserStates, opcodes, states, emptyBuffer, sentCloseFrameState } = require('./constants')
|
|
5
5
|
const { kReadyState, kSentClose, kResponse, kReceivedClose } = require('./symbols')
|
|
6
|
-
const { channels } = require('
|
|
6
|
+
const { channels } = require('../../core/diagnostics')
|
|
7
7
|
const { isValidStatusCode, failWebsocketConnection, websocketMessageReceived } = require('./util')
|
|
8
8
|
const { WebsocketFrameSend } = require('./frame')
|
|
9
9
|
|
|
@@ -12,6 +12,8 @@ const { WebsocketFrameSend } = require('./frame')
|
|
|
12
12
|
// Copyright (c) 2013 Arnout Kazemier and contributors
|
|
13
13
|
// Copyright (c) 2016 Luigi Pinca and contributors
|
|
14
14
|
|
|
15
|
+
const textDecoder = new TextDecoder('utf-8', { fatal: true })
|
|
16
|
+
|
|
15
17
|
class ByteParser extends Writable {
|
|
16
18
|
#buffers = []
|
|
17
19
|
#byteOffset = 0
|
|
@@ -100,9 +102,9 @@ class ByteParser extends Writable {
|
|
|
100
102
|
|
|
101
103
|
const body = this.consume(payloadLength)
|
|
102
104
|
|
|
103
|
-
this.#info.closeInfo = this.parseCloseBody(
|
|
105
|
+
this.#info.closeInfo = this.parseCloseBody(body)
|
|
104
106
|
|
|
105
|
-
if (
|
|
107
|
+
if (this.ws[kSentClose] !== sentCloseFrameState.SENT) {
|
|
106
108
|
// If an endpoint receives a Close frame and did not previously send a
|
|
107
109
|
// Close frame, the endpoint MUST send a Close frame in response. (When
|
|
108
110
|
// sending a Close frame in response, the endpoint typically echos the
|
|
@@ -118,7 +120,7 @@ class ByteParser extends Writable {
|
|
|
118
120
|
closeFrame.createFrame(opcodes.CLOSE),
|
|
119
121
|
(err) => {
|
|
120
122
|
if (!err) {
|
|
121
|
-
this.ws[kSentClose] =
|
|
123
|
+
this.ws[kSentClose] = sentCloseFrameState.SENT
|
|
122
124
|
}
|
|
123
125
|
}
|
|
124
126
|
)
|
|
@@ -288,7 +290,7 @@ class ByteParser extends Writable {
|
|
|
288
290
|
return buffer
|
|
289
291
|
}
|
|
290
292
|
|
|
291
|
-
parseCloseBody (
|
|
293
|
+
parseCloseBody (data) {
|
|
292
294
|
// https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5
|
|
293
295
|
/** @type {number|undefined} */
|
|
294
296
|
let code
|
|
@@ -300,14 +302,6 @@ class ByteParser extends Writable {
|
|
|
300
302
|
code = data.readUInt16BE(0)
|
|
301
303
|
}
|
|
302
304
|
|
|
303
|
-
if (onlyCode) {
|
|
304
|
-
if (!isValidStatusCode(code)) {
|
|
305
|
-
return null
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
return { code }
|
|
309
|
-
}
|
|
310
|
-
|
|
311
305
|
// https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6
|
|
312
306
|
/** @type {Buffer} */
|
|
313
307
|
let reason = data.subarray(2)
|
|
@@ -322,8 +316,7 @@ class ByteParser extends Writable {
|
|
|
322
316
|
}
|
|
323
317
|
|
|
324
318
|
try {
|
|
325
|
-
|
|
326
|
-
reason = new TextDecoder('utf-8', { fatal: true }).decode(reason)
|
|
319
|
+
reason = textDecoder.decode(reason)
|
|
327
320
|
} catch {
|
|
328
321
|
return null
|
|
329
322
|
}
|
|
@@ -8,6 +8,17 @@ const { MessageEvent, ErrorEvent } = require('./events')
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @param {import('./websocket').WebSocket} ws
|
|
11
|
+
* @returns {boolean}
|
|
12
|
+
*/
|
|
13
|
+
function isConnecting (ws) {
|
|
14
|
+
// If the WebSocket connection is not yet established, and the connection
|
|
15
|
+
// is not yet closed, then the WebSocket connection is in the CONNECTING state.
|
|
16
|
+
return ws[kReadyState] === states.CONNECTING
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {import('./websocket').WebSocket} ws
|
|
21
|
+
* @returns {boolean}
|
|
11
22
|
*/
|
|
12
23
|
function isEstablished (ws) {
|
|
13
24
|
// If the server's response is validated as provided for above, it is
|
|
@@ -18,6 +29,7 @@ function isEstablished (ws) {
|
|
|
18
29
|
|
|
19
30
|
/**
|
|
20
31
|
* @param {import('./websocket').WebSocket} ws
|
|
32
|
+
* @returns {boolean}
|
|
21
33
|
*/
|
|
22
34
|
function isClosing (ws) {
|
|
23
35
|
// Upon either sending or receiving a Close control frame, it is said
|
|
@@ -28,6 +40,7 @@ function isClosing (ws) {
|
|
|
28
40
|
|
|
29
41
|
/**
|
|
30
42
|
* @param {import('./websocket').WebSocket} ws
|
|
43
|
+
* @returns {boolean}
|
|
31
44
|
*/
|
|
32
45
|
function isClosed (ws) {
|
|
33
46
|
return ws[kReadyState] === states.CLOSED
|
|
@@ -55,6 +68,8 @@ function fireEvent (e, target, eventConstructor = Event, eventInitDict = {}) {
|
|
|
55
68
|
target.dispatchEvent(event)
|
|
56
69
|
}
|
|
57
70
|
|
|
71
|
+
const textDecoder = new TextDecoder('utf-8', { fatal: true })
|
|
72
|
+
|
|
58
73
|
/**
|
|
59
74
|
* @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
|
|
60
75
|
* @param {import('./websocket').WebSocket} ws
|
|
@@ -74,7 +89,7 @@ function websocketMessageReceived (ws, type, data) {
|
|
|
74
89
|
// -> type indicates that the data is Text
|
|
75
90
|
// a new DOMString containing data
|
|
76
91
|
try {
|
|
77
|
-
dataForEvent =
|
|
92
|
+
dataForEvent = textDecoder.decode(data)
|
|
78
93
|
} catch {
|
|
79
94
|
failWebsocketConnection(ws, 'Received invalid UTF-8 in text frame.')
|
|
80
95
|
return
|
|
@@ -119,31 +134,29 @@ function isValidSubprotocol (protocol) {
|
|
|
119
134
|
return false
|
|
120
135
|
}
|
|
121
136
|
|
|
122
|
-
for (
|
|
123
|
-
const code =
|
|
137
|
+
for (let i = 0; i < protocol.length; ++i) {
|
|
138
|
+
const code = protocol.charCodeAt(i)
|
|
124
139
|
|
|
125
140
|
if (
|
|
126
|
-
code < 0x21 ||
|
|
141
|
+
code < 0x21 || // CTL, contains SP (0x20) and HT (0x09)
|
|
127
142
|
code > 0x7E ||
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
code === 32 || // SP
|
|
146
|
-
code === 9 // HT
|
|
143
|
+
code === 0x22 || // "
|
|
144
|
+
code === 0x28 || // (
|
|
145
|
+
code === 0x29 || // )
|
|
146
|
+
code === 0x2C || // ,
|
|
147
|
+
code === 0x2F || // /
|
|
148
|
+
code === 0x3A || // :
|
|
149
|
+
code === 0x3B || // ;
|
|
150
|
+
code === 0x3C || // <
|
|
151
|
+
code === 0x3D || // =
|
|
152
|
+
code === 0x3E || // >
|
|
153
|
+
code === 0x3F || // ?
|
|
154
|
+
code === 0x40 || // @
|
|
155
|
+
code === 0x5B || // [
|
|
156
|
+
code === 0x5C || // \
|
|
157
|
+
code === 0x5D || // ]
|
|
158
|
+
code === 0x7B || // {
|
|
159
|
+
code === 0x7D // }
|
|
147
160
|
) {
|
|
148
161
|
return false
|
|
149
162
|
}
|
|
@@ -190,6 +203,7 @@ function failWebsocketConnection (ws, reason) {
|
|
|
190
203
|
}
|
|
191
204
|
|
|
192
205
|
module.exports = {
|
|
206
|
+
isConnecting,
|
|
193
207
|
isEstablished,
|
|
194
208
|
isClosing,
|
|
195
209
|
isClosed,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { webidl } = require('../fetch/webidl')
|
|
4
|
-
const { URLSerializer } = require('../fetch/
|
|
4
|
+
const { URLSerializer } = require('../fetch/data-url')
|
|
5
5
|
const { getGlobalOrigin } = require('../fetch/global')
|
|
6
|
-
const { staticPropertyDescriptors, states, opcodes, emptyBuffer } = require('./constants')
|
|
6
|
+
const { staticPropertyDescriptors, states, sentCloseFrameState, opcodes, emptyBuffer } = require('./constants')
|
|
7
7
|
const {
|
|
8
8
|
kWebSocketURL,
|
|
9
9
|
kReadyState,
|
|
@@ -13,12 +13,20 @@ const {
|
|
|
13
13
|
kSentClose,
|
|
14
14
|
kByteParser
|
|
15
15
|
} = require('./symbols')
|
|
16
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
isConnecting,
|
|
18
|
+
isEstablished,
|
|
19
|
+
isClosed,
|
|
20
|
+
isClosing,
|
|
21
|
+
isValidSubprotocol,
|
|
22
|
+
failWebsocketConnection,
|
|
23
|
+
fireEvent
|
|
24
|
+
} = require('./util')
|
|
17
25
|
const { establishWebSocketConnection } = require('./connection')
|
|
18
26
|
const { WebsocketFrameSend } = require('./frame')
|
|
19
27
|
const { ByteParser } = require('./receiver')
|
|
20
|
-
const { kEnumerableProperty, isBlobLike } = require('
|
|
21
|
-
const { getGlobalDispatcher } = require('
|
|
28
|
+
const { kEnumerableProperty, isBlobLike } = require('../../core/util')
|
|
29
|
+
const { getGlobalDispatcher } = require('../../global')
|
|
22
30
|
const { types } = require('node:util')
|
|
23
31
|
|
|
24
32
|
let experimentalWarned = false
|
|
@@ -132,6 +140,8 @@ class WebSocket extends EventTarget {
|
|
|
132
140
|
// be CONNECTING (0).
|
|
133
141
|
this[kReadyState] = WebSocket.CONNECTING
|
|
134
142
|
|
|
143
|
+
this[kSentClose] = sentCloseFrameState.NOT_SENT
|
|
144
|
+
|
|
135
145
|
// The extensions attribute must initially return the empty string.
|
|
136
146
|
|
|
137
147
|
// The protocol attribute must initially return the empty string.
|
|
@@ -184,7 +194,7 @@ class WebSocket extends EventTarget {
|
|
|
184
194
|
}
|
|
185
195
|
|
|
186
196
|
// 3. Run the first matching steps from the following list:
|
|
187
|
-
if (this
|
|
197
|
+
if (isClosing(this) || isClosed(this)) {
|
|
188
198
|
// If this's ready state is CLOSING (2) or CLOSED (3)
|
|
189
199
|
// Do nothing.
|
|
190
200
|
} else if (!isEstablished(this)) {
|
|
@@ -193,7 +203,7 @@ class WebSocket extends EventTarget {
|
|
|
193
203
|
// to CLOSING (2).
|
|
194
204
|
failWebsocketConnection(this, 'Connection was closed before it was established.')
|
|
195
205
|
this[kReadyState] = WebSocket.CLOSING
|
|
196
|
-
} else if (
|
|
206
|
+
} else if (this[kSentClose] === sentCloseFrameState.NOT_SENT) {
|
|
197
207
|
// If the WebSocket closing handshake has not yet been started
|
|
198
208
|
// Start the WebSocket closing handshake and set this's ready
|
|
199
209
|
// state to CLOSING (2).
|
|
@@ -204,6 +214,8 @@ class WebSocket extends EventTarget {
|
|
|
204
214
|
// - If reason is also present, then reasonBytes must be
|
|
205
215
|
// provided in the Close message after the status code.
|
|
206
216
|
|
|
217
|
+
this[kSentClose] = sentCloseFrameState.PROCESSING
|
|
218
|
+
|
|
207
219
|
const frame = new WebsocketFrameSend()
|
|
208
220
|
|
|
209
221
|
// If neither code nor reason is present, the WebSocket Close
|
|
@@ -230,7 +242,7 @@ class WebSocket extends EventTarget {
|
|
|
230
242
|
|
|
231
243
|
socket.write(frame.createFrame(opcodes.CLOSE), (err) => {
|
|
232
244
|
if (!err) {
|
|
233
|
-
this[kSentClose] =
|
|
245
|
+
this[kSentClose] = sentCloseFrameState.SENT
|
|
234
246
|
}
|
|
235
247
|
})
|
|
236
248
|
|
|
@@ -258,7 +270,7 @@ class WebSocket extends EventTarget {
|
|
|
258
270
|
|
|
259
271
|
// 1. If this's ready state is CONNECTING, then throw an
|
|
260
272
|
// "InvalidStateError" DOMException.
|
|
261
|
-
if (this
|
|
273
|
+
if (isConnecting(this)) {
|
|
262
274
|
throw new DOMException('Sent before connected.', 'InvalidStateError')
|
|
263
275
|
}
|
|
264
276
|
|