undici 7.2.0 → 7.2.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/docs/docs/api/DiagnosticsChannel.md +1 -1
- package/docs/docs/api/Dispatcher.md +1 -1
- package/lib/core/util.js +17 -12
- package/lib/handler/retry-handler.js +2 -2
- package/lib/interceptor/dns.js +78 -25
- package/lib/web/websocket/connection.js +26 -1
- package/lib/web/websocket/receiver.js +62 -28
- package/lib/web/websocket/stream/websocketstream.js +2 -2
- package/lib/web/websocket/util.js +1 -28
- package/lib/web/websocket/websocket.js +1 -2
- package/package.json +2 -2
- package/types/errors.d.ts +16 -0
|
@@ -40,7 +40,7 @@ diagnosticsChannel.channel('undici:request:bodySent').subscribe(({ request }) =>
|
|
|
40
40
|
|
|
41
41
|
## `undici:request:headers`
|
|
42
42
|
|
|
43
|
-
This message is published after the response headers have been received
|
|
43
|
+
This message is published after the response headers have been received.
|
|
44
44
|
|
|
45
45
|
```js
|
|
46
46
|
import diagnosticsChannel from 'diagnostics_channel'
|
|
@@ -652,7 +652,7 @@ return null
|
|
|
652
652
|
|
|
653
653
|
A faster version of `Dispatcher.request`. This method expects the second argument `factory` to return a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream which the response will be written to. This improves performance by avoiding creating an intermediate [`stream.Readable`](https://nodejs.org/api/stream.html#stream_readable_streams) stream when the user expects to directly pipe the response body to a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream.
|
|
654
654
|
|
|
655
|
-
As demonstrated in [Example 1 - Basic GET stream request](/docs/docs/api/Dispatcher.md#example-1
|
|
655
|
+
As demonstrated in [Example 1 - Basic GET stream request](/docs/docs/api/Dispatcher.md#example-1-basic-get-stream-request), it is recommended to use the `option.opaque` property to avoid creating a closure for the `factory` method. This pattern works well with Node.js Web Frameworks such as [Fastify](https://fastify.io). See [Example 2 - Stream to Fastify Response](/docs/docs/api/Dispatch.md#example-2-stream-to-fastify-response) for more details.
|
|
656
656
|
|
|
657
657
|
Arguments:
|
|
658
658
|
|
package/lib/core/util.js
CHANGED
|
@@ -600,20 +600,25 @@ function ReadableStreamFrom (iterable) {
|
|
|
600
600
|
async start () {
|
|
601
601
|
iterator = iterable[Symbol.asyncIterator]()
|
|
602
602
|
},
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
603
|
+
pull (controller) {
|
|
604
|
+
async function pull () {
|
|
605
|
+
const { done, value } = await iterator.next()
|
|
606
|
+
if (done) {
|
|
607
|
+
queueMicrotask(() => {
|
|
608
|
+
controller.close()
|
|
609
|
+
controller.byobRequest?.respond(0)
|
|
610
|
+
})
|
|
611
|
+
} else {
|
|
612
|
+
const buf = Buffer.isBuffer(value) ? value : Buffer.from(value)
|
|
613
|
+
if (buf.byteLength) {
|
|
614
|
+
controller.enqueue(new Uint8Array(buf))
|
|
615
|
+
} else {
|
|
616
|
+
return await pull()
|
|
617
|
+
}
|
|
614
618
|
}
|
|
615
619
|
}
|
|
616
|
-
|
|
620
|
+
|
|
621
|
+
return pull()
|
|
617
622
|
},
|
|
618
623
|
async cancel () {
|
|
619
624
|
await iterator.return()
|
|
@@ -133,7 +133,7 @@ class RetryHandler {
|
|
|
133
133
|
? Math.min(retryAfterHeader, maxTimeout)
|
|
134
134
|
: Math.min(minTimeout * timeoutFactor ** (counter - 1), maxTimeout)
|
|
135
135
|
|
|
136
|
-
setTimeout(() => cb(null), retryTimeout)
|
|
136
|
+
setTimeout(() => cb(null), retryTimeout)
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
onResponseStart (controller, statusCode, headers, statusMessage) {
|
|
@@ -277,7 +277,7 @@ class RetryHandler {
|
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
onResponseError (controller, err) {
|
|
280
|
-
if (
|
|
280
|
+
if (controller?.aborted || isDisturbed(this.opts.body)) {
|
|
281
281
|
this.handler.onResponseError?.(controller, err)
|
|
282
282
|
return
|
|
283
283
|
}
|
package/lib/interceptor/dns.js
CHANGED
|
@@ -32,7 +32,7 @@ class DNSInstance {
|
|
|
32
32
|
|
|
33
33
|
// If full, we just return the origin
|
|
34
34
|
if (ips == null && this.full) {
|
|
35
|
-
cb(null, origin
|
|
35
|
+
cb(null, origin)
|
|
36
36
|
return
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -74,9 +74,9 @@ class DNSInstance {
|
|
|
74
74
|
|
|
75
75
|
cb(
|
|
76
76
|
null,
|
|
77
|
-
`${origin.protocol}//${
|
|
77
|
+
new URL(`${origin.protocol}//${
|
|
78
78
|
ip.family === 6 ? `[${ip.address}]` : ip.address
|
|
79
|
-
}${port}`
|
|
79
|
+
}${port}`)
|
|
80
80
|
)
|
|
81
81
|
})
|
|
82
82
|
} else {
|
|
@@ -105,9 +105,9 @@ class DNSInstance {
|
|
|
105
105
|
|
|
106
106
|
cb(
|
|
107
107
|
null,
|
|
108
|
-
`${origin.protocol}//${
|
|
108
|
+
new URL(`${origin.protocol}//${
|
|
109
109
|
ip.family === 6 ? `[${ip.address}]` : ip.address
|
|
110
|
-
}${port}`
|
|
110
|
+
}${port}`)
|
|
111
111
|
)
|
|
112
112
|
}
|
|
113
113
|
}
|
|
@@ -192,6 +192,38 @@ class DNSInstance {
|
|
|
192
192
|
return ip
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
pickFamily (origin, ipFamily) {
|
|
196
|
+
const records = this.#records.get(origin.hostname)?.records
|
|
197
|
+
if (!records) {
|
|
198
|
+
return null
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const family = records[ipFamily]
|
|
202
|
+
if (!family) {
|
|
203
|
+
return null
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (family.offset == null || family.offset === maxInt) {
|
|
207
|
+
family.offset = 0
|
|
208
|
+
} else {
|
|
209
|
+
family.offset++
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const position = family.offset % family.ips.length
|
|
213
|
+
const ip = family.ips[position] ?? null
|
|
214
|
+
if (ip == null) {
|
|
215
|
+
return ip
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (Date.now() - ip.timestamp > ip.ttl) { // record TTL is already in ms
|
|
219
|
+
// We delete expired records
|
|
220
|
+
// It is possible that they have different TTL, so we manage them individually
|
|
221
|
+
family.ips.splice(position, 1)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return ip
|
|
225
|
+
}
|
|
226
|
+
|
|
195
227
|
setRecords (origin, addresses) {
|
|
196
228
|
const timestamp = Date.now()
|
|
197
229
|
const records = { records: { 4: null, 6: null } }
|
|
@@ -228,10 +260,13 @@ class DNSDispatchHandler extends DecoratorHandler {
|
|
|
228
260
|
#dispatch = null
|
|
229
261
|
#origin = null
|
|
230
262
|
#controller = null
|
|
263
|
+
#newOrigin = null
|
|
264
|
+
#firstTry = true
|
|
231
265
|
|
|
232
|
-
constructor (state, { origin, handler, dispatch }, opts) {
|
|
266
|
+
constructor (state, { origin, handler, dispatch, newOrigin }, opts) {
|
|
233
267
|
super(handler)
|
|
234
268
|
this.#origin = origin
|
|
269
|
+
this.#newOrigin = newOrigin
|
|
235
270
|
this.#opts = { ...opts }
|
|
236
271
|
this.#state = state
|
|
237
272
|
this.#dispatch = dispatch
|
|
@@ -242,21 +277,36 @@ class DNSDispatchHandler extends DecoratorHandler {
|
|
|
242
277
|
case 'ETIMEDOUT':
|
|
243
278
|
case 'ECONNREFUSED': {
|
|
244
279
|
if (this.#state.dualStack) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
280
|
+
if (!this.#firstTry) {
|
|
281
|
+
super.onResponseError(controller, err)
|
|
282
|
+
return
|
|
283
|
+
}
|
|
284
|
+
this.#firstTry = false
|
|
285
|
+
|
|
286
|
+
// Pick an ip address from the other family
|
|
287
|
+
const otherFamily = this.#newOrigin.hostname[0] === '[' ? 4 : 6
|
|
288
|
+
const ip = this.#state.pickFamily(this.#origin, otherFamily)
|
|
289
|
+
if (ip == null) {
|
|
290
|
+
super.onResponseError(controller, err)
|
|
291
|
+
return
|
|
292
|
+
}
|
|
256
293
|
|
|
257
|
-
|
|
258
|
-
|
|
294
|
+
let port
|
|
295
|
+
if (typeof ip.port === 'number') {
|
|
296
|
+
port = `:${ip.port}`
|
|
297
|
+
} else if (this.#origin.port !== '') {
|
|
298
|
+
port = `:${this.#origin.port}`
|
|
299
|
+
} else {
|
|
300
|
+
port = ''
|
|
301
|
+
}
|
|
259
302
|
|
|
303
|
+
const dispatchOpts = {
|
|
304
|
+
...this.#opts,
|
|
305
|
+
origin: `${this.#origin.protocol}//${
|
|
306
|
+
ip.family === 6 ? `[${ip.address}]` : ip.address
|
|
307
|
+
}${port}`
|
|
308
|
+
}
|
|
309
|
+
this.#dispatch(dispatchOpts, this)
|
|
260
310
|
return
|
|
261
311
|
}
|
|
262
312
|
|
|
@@ -266,7 +316,8 @@ class DNSDispatchHandler extends DecoratorHandler {
|
|
|
266
316
|
}
|
|
267
317
|
case 'ENOTFOUND':
|
|
268
318
|
this.#state.deleteRecords(this.#origin)
|
|
269
|
-
|
|
319
|
+
super.onResponseError(controller, err)
|
|
320
|
+
break
|
|
270
321
|
default:
|
|
271
322
|
super.onResponseError(controller, err)
|
|
272
323
|
break
|
|
@@ -353,14 +404,13 @@ module.exports = interceptorOpts => {
|
|
|
353
404
|
|
|
354
405
|
instance.runLookup(origin, origDispatchOpts, (err, newOrigin) => {
|
|
355
406
|
if (err) {
|
|
356
|
-
return handler.
|
|
407
|
+
return handler.onResponseError(null, err)
|
|
357
408
|
}
|
|
358
409
|
|
|
359
|
-
|
|
360
|
-
dispatchOpts = {
|
|
410
|
+
const dispatchOpts = {
|
|
361
411
|
...origDispatchOpts,
|
|
362
412
|
servername: origin.hostname, // For SNI on TLS
|
|
363
|
-
origin: newOrigin,
|
|
413
|
+
origin: newOrigin.origin,
|
|
364
414
|
headers: {
|
|
365
415
|
host: origin.host,
|
|
366
416
|
...origDispatchOpts.headers
|
|
@@ -369,7 +419,10 @@ module.exports = interceptorOpts => {
|
|
|
369
419
|
|
|
370
420
|
dispatch(
|
|
371
421
|
dispatchOpts,
|
|
372
|
-
instance.getHandler(
|
|
422
|
+
instance.getHandler(
|
|
423
|
+
{ origin, dispatch, handler, newOrigin },
|
|
424
|
+
origDispatchOpts
|
|
425
|
+
)
|
|
373
426
|
)
|
|
374
427
|
})
|
|
375
428
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { uid, states, sentCloseFrameState, emptyBuffer, opcodes } = require('./constants')
|
|
4
|
-
const {
|
|
4
|
+
const { parseExtensions, isClosed, isClosing, isEstablished, validateCloseCodeAndReason } = require('./util')
|
|
5
5
|
const { channels } = require('../../core/diagnostics')
|
|
6
6
|
const { makeRequest } = require('../fetch/request')
|
|
7
7
|
const { fetching } = require('../fetch/index')
|
|
@@ -294,7 +294,32 @@ function closeWebSocketConnection (object, code, reason, validate = false) {
|
|
|
294
294
|
}
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
+
/**
|
|
298
|
+
* @param {import('./websocket').Handler} handler
|
|
299
|
+
* @param {number} code
|
|
300
|
+
* @param {string|undefined} reason
|
|
301
|
+
* @returns {void}
|
|
302
|
+
*/
|
|
303
|
+
function failWebsocketConnection (handler, code, reason) {
|
|
304
|
+
// If _The WebSocket Connection is Established_ prior to the point where
|
|
305
|
+
// the endpoint is required to _Fail the WebSocket Connection_, the
|
|
306
|
+
// endpoint SHOULD send a Close frame with an appropriate status code
|
|
307
|
+
// (Section 7.4) before proceeding to _Close the WebSocket Connection_.
|
|
308
|
+
if (isEstablished(handler.readyState)) {
|
|
309
|
+
closeWebSocketConnection(handler, code, reason, false)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
handler.controller.abort()
|
|
313
|
+
|
|
314
|
+
if (handler.socket?.destroyed === false) {
|
|
315
|
+
handler.socket.destroy()
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
handler.onFail(code, reason)
|
|
319
|
+
}
|
|
320
|
+
|
|
297
321
|
module.exports = {
|
|
298
322
|
establishWebSocketConnection,
|
|
323
|
+
failWebsocketConnection,
|
|
299
324
|
closeWebSocketConnection
|
|
300
325
|
}
|
|
@@ -7,13 +7,13 @@ const { channels } = require('../../core/diagnostics')
|
|
|
7
7
|
const {
|
|
8
8
|
isValidStatusCode,
|
|
9
9
|
isValidOpcode,
|
|
10
|
-
failWebsocketConnection,
|
|
11
10
|
websocketMessageReceived,
|
|
12
11
|
utf8Decode,
|
|
13
12
|
isControlFrame,
|
|
14
13
|
isTextBinaryFrame,
|
|
15
14
|
isContinuationFrame
|
|
16
15
|
} = require('./util')
|
|
16
|
+
const { failWebsocketConnection } = require('./connection')
|
|
17
17
|
const { WebsocketFrameSend } = require('./frame')
|
|
18
18
|
const { PerMessageDeflate } = require('./permessage-deflate')
|
|
19
19
|
|
|
@@ -24,6 +24,7 @@ const { PerMessageDeflate } = require('./permessage-deflate')
|
|
|
24
24
|
|
|
25
25
|
class ByteParser extends Writable {
|
|
26
26
|
#buffers = []
|
|
27
|
+
#fragmentsBytes = 0
|
|
27
28
|
#byteOffset = 0
|
|
28
29
|
#loop = false
|
|
29
30
|
|
|
@@ -208,16 +209,14 @@ class ByteParser extends Writable {
|
|
|
208
209
|
this.#state = parserStates.INFO
|
|
209
210
|
} else {
|
|
210
211
|
if (!this.#info.compressed) {
|
|
211
|
-
this
|
|
212
|
+
this.writeFragments(body)
|
|
212
213
|
|
|
213
214
|
// If the frame is not fragmented, a message has been received.
|
|
214
215
|
// If the frame is fragmented, it will terminate with a fin bit set
|
|
215
216
|
// and an opcode of 0 (continuation), therefore we handle that when
|
|
216
217
|
// parsing continuation frames, not here.
|
|
217
218
|
if (!this.#info.fragmented && this.#info.fin) {
|
|
218
|
-
|
|
219
|
-
websocketMessageReceived(this.#handler, this.#info.binaryType, fullMessage)
|
|
220
|
-
this.#fragments.length = 0
|
|
219
|
+
websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments())
|
|
221
220
|
}
|
|
222
221
|
|
|
223
222
|
this.#state = parserStates.INFO
|
|
@@ -228,7 +227,7 @@ class ByteParser extends Writable {
|
|
|
228
227
|
return
|
|
229
228
|
}
|
|
230
229
|
|
|
231
|
-
this
|
|
230
|
+
this.writeFragments(data)
|
|
232
231
|
|
|
233
232
|
if (!this.#info.fin) {
|
|
234
233
|
this.#state = parserStates.INFO
|
|
@@ -237,11 +236,10 @@ class ByteParser extends Writable {
|
|
|
237
236
|
return
|
|
238
237
|
}
|
|
239
238
|
|
|
240
|
-
websocketMessageReceived(this.#handler, this.#info.binaryType,
|
|
239
|
+
websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments())
|
|
241
240
|
|
|
242
241
|
this.#loop = true
|
|
243
242
|
this.#state = parserStates.INFO
|
|
244
|
-
this.#fragments.length = 0
|
|
245
243
|
this.run(callback)
|
|
246
244
|
})
|
|
247
245
|
|
|
@@ -265,34 +263,70 @@ class ByteParser extends Writable {
|
|
|
265
263
|
return emptyBuffer
|
|
266
264
|
}
|
|
267
265
|
|
|
268
|
-
|
|
269
|
-
|
|
266
|
+
this.#byteOffset -= n
|
|
267
|
+
|
|
268
|
+
const first = this.#buffers[0]
|
|
269
|
+
|
|
270
|
+
if (first.length > n) {
|
|
271
|
+
// replace with remaining buffer
|
|
272
|
+
this.#buffers[0] = first.subarray(n, first.length)
|
|
273
|
+
return first.subarray(0, n)
|
|
274
|
+
} else if (first.length === n) {
|
|
275
|
+
// prefect match
|
|
270
276
|
return this.#buffers.shift()
|
|
277
|
+
} else {
|
|
278
|
+
let offset = 0
|
|
279
|
+
// If Buffer.allocUnsafe is used, extra copies will be made because the offset is non-zero.
|
|
280
|
+
const buffer = Buffer.allocUnsafeSlow(n)
|
|
281
|
+
while (offset !== n) {
|
|
282
|
+
const next = this.#buffers[0]
|
|
283
|
+
const length = next.length
|
|
284
|
+
|
|
285
|
+
if (length + offset === n) {
|
|
286
|
+
buffer.set(this.#buffers.shift(), offset)
|
|
287
|
+
break
|
|
288
|
+
} else if (length + offset > n) {
|
|
289
|
+
buffer.set(next.subarray(0, n - offset), offset)
|
|
290
|
+
this.#buffers[0] = next.subarray(n - offset)
|
|
291
|
+
break
|
|
292
|
+
} else {
|
|
293
|
+
buffer.set(this.#buffers.shift(), offset)
|
|
294
|
+
offset += length
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return buffer
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
writeFragments (fragment) {
|
|
303
|
+
this.#fragmentsBytes += fragment.length
|
|
304
|
+
this.#fragments.push(fragment)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
consumeFragments () {
|
|
308
|
+
const fragments = this.#fragments
|
|
309
|
+
|
|
310
|
+
if (fragments.length === 1) {
|
|
311
|
+
// single fragment
|
|
312
|
+
this.#fragmentsBytes = 0
|
|
313
|
+
return fragments.shift()
|
|
271
314
|
}
|
|
272
315
|
|
|
273
|
-
const buffer = Buffer.allocUnsafe(n)
|
|
274
316
|
let offset = 0
|
|
317
|
+
// If Buffer.allocUnsafe is used, extra copies will be made because the offset is non-zero.
|
|
318
|
+
const output = Buffer.allocUnsafeSlow(this.#fragmentsBytes)
|
|
275
319
|
|
|
276
|
-
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (length + offset === n) {
|
|
281
|
-
buffer.set(this.#buffers.shift(), offset)
|
|
282
|
-
break
|
|
283
|
-
} else if (length + offset > n) {
|
|
284
|
-
buffer.set(next.subarray(0, n - offset), offset)
|
|
285
|
-
this.#buffers[0] = next.subarray(n - offset)
|
|
286
|
-
break
|
|
287
|
-
} else {
|
|
288
|
-
buffer.set(this.#buffers.shift(), offset)
|
|
289
|
-
offset += next.length
|
|
290
|
-
}
|
|
320
|
+
for (let i = 0; i < fragments.length; ++i) {
|
|
321
|
+
const buffer = fragments[i]
|
|
322
|
+
output.set(buffer, offset)
|
|
323
|
+
offset += buffer.length
|
|
291
324
|
}
|
|
292
325
|
|
|
293
|
-
this.#
|
|
326
|
+
this.#fragments = []
|
|
327
|
+
this.#fragmentsBytes = 0
|
|
294
328
|
|
|
295
|
-
return
|
|
329
|
+
return output
|
|
296
330
|
}
|
|
297
331
|
|
|
298
332
|
parseCloseBody (data) {
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
const { createDeferredPromise, environmentSettingsObject } = require('../../fetch/util')
|
|
4
4
|
const { states, opcodes, sentCloseFrameState } = require('../constants')
|
|
5
5
|
const { webidl } = require('../../fetch/webidl')
|
|
6
|
-
const { getURLRecord, isValidSubprotocol, isEstablished,
|
|
7
|
-
const { establishWebSocketConnection, closeWebSocketConnection } = require('../connection')
|
|
6
|
+
const { getURLRecord, isValidSubprotocol, isEstablished, utf8Decode } = require('../util')
|
|
7
|
+
const { establishWebSocketConnection, failWebsocketConnection, closeWebSocketConnection } = require('../connection')
|
|
8
8
|
const { types } = require('node:util')
|
|
9
9
|
const { channels } = require('../../../core/diagnostics')
|
|
10
10
|
const { WebsocketFrameSend } = require('../frame')
|
|
@@ -87,7 +87,7 @@ function toArrayBuffer (buffer) {
|
|
|
87
87
|
if (buffer.byteLength === buffer.buffer.byteLength) {
|
|
88
88
|
return buffer.buffer
|
|
89
89
|
}
|
|
90
|
-
return
|
|
90
|
+
return new Uint8Array(buffer).buffer
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/**
|
|
@@ -156,32 +156,6 @@ function isValidStatusCode (code) {
|
|
|
156
156
|
return code >= 3000 && code <= 4999
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
/**
|
|
160
|
-
* @param {import('./websocket').Handler} handler
|
|
161
|
-
* @param {number} code
|
|
162
|
-
* @param {string|undefined} reason
|
|
163
|
-
* @returns {void}
|
|
164
|
-
*/
|
|
165
|
-
function failWebsocketConnection (handler, code, reason) {
|
|
166
|
-
// If _The WebSocket Connection is Established_ prior to the point where
|
|
167
|
-
// the endpoint is required to _Fail the WebSocket Connection_, the
|
|
168
|
-
// endpoint SHOULD send a Close frame with an appropriate status code
|
|
169
|
-
// (Section 7.4) before proceeding to _Close the WebSocket Connection_.
|
|
170
|
-
if (isEstablished(handler.readyState)) {
|
|
171
|
-
// avoid circular require - performance is not important here
|
|
172
|
-
const { closeWebSocketConnection } = require('./connection')
|
|
173
|
-
closeWebSocketConnection(handler, code, reason, false)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
handler.controller.abort()
|
|
177
|
-
|
|
178
|
-
if (handler.socket?.destroyed === false) {
|
|
179
|
-
handler.socket.destroy()
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
handler.onFail(code, reason)
|
|
183
|
-
}
|
|
184
|
-
|
|
185
159
|
/**
|
|
186
160
|
* @see https://datatracker.ietf.org/doc/html/rfc6455#section-5.5
|
|
187
161
|
* @param {number} opcode
|
|
@@ -350,7 +324,6 @@ module.exports = {
|
|
|
350
324
|
fireEvent,
|
|
351
325
|
isValidSubprotocol,
|
|
352
326
|
isValidStatusCode,
|
|
353
|
-
failWebsocketConnection,
|
|
354
327
|
websocketMessageReceived,
|
|
355
328
|
utf8Decode,
|
|
356
329
|
isControlFrame,
|
|
@@ -10,12 +10,11 @@ const {
|
|
|
10
10
|
isClosing,
|
|
11
11
|
isValidSubprotocol,
|
|
12
12
|
fireEvent,
|
|
13
|
-
failWebsocketConnection,
|
|
14
13
|
utf8Decode,
|
|
15
14
|
toArrayBuffer,
|
|
16
15
|
getURLRecord
|
|
17
16
|
} = require('./util')
|
|
18
|
-
const { establishWebSocketConnection, closeWebSocketConnection } = require('./connection')
|
|
17
|
+
const { establishWebSocketConnection, closeWebSocketConnection, failWebsocketConnection } = require('./connection')
|
|
19
18
|
const { ByteParser } = require('./receiver')
|
|
20
19
|
const { kEnumerableProperty } = require('../../core/util')
|
|
21
20
|
const { getGlobalDispatcher } = require('../../global')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "undici",
|
|
3
|
-
"version": "7.2.
|
|
3
|
+
"version": "7.2.2",
|
|
4
4
|
"description": "An HTTP/1.1 client, written from scratch for Node.js",
|
|
5
5
|
"homepage": "https://undici.nodejs.org",
|
|
6
6
|
"bugs": {
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"prepare": "husky && node ./scripts/platform-shell.js"
|
|
108
108
|
},
|
|
109
109
|
"devDependencies": {
|
|
110
|
-
"@fastify/busboy": "3.1.
|
|
110
|
+
"@fastify/busboy": "3.1.1",
|
|
111
111
|
"@matteo.collina/tspl": "^0.1.1",
|
|
112
112
|
"@sinonjs/fake-timers": "^12.0.0",
|
|
113
113
|
"@types/node": "^18.19.50",
|
package/types/errors.d.ts
CHANGED
|
@@ -33,6 +33,22 @@ declare namespace Errors {
|
|
|
33
33
|
code: 'UND_ERR_BODY_TIMEOUT'
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
export class ResponseError extends UndiciError {
|
|
37
|
+
constructor (
|
|
38
|
+
message: string,
|
|
39
|
+
code: number,
|
|
40
|
+
options: {
|
|
41
|
+
headers?: IncomingHttpHeaders | string[] | null,
|
|
42
|
+
body?: null | Record<string, any> | string
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
name: 'ResponseError'
|
|
46
|
+
code: 'UND_ERR_RESPONSE'
|
|
47
|
+
statusCode: number
|
|
48
|
+
body: null | Record<string, any> | string
|
|
49
|
+
headers: IncomingHttpHeaders | string[] | null
|
|
50
|
+
}
|
|
51
|
+
|
|
36
52
|
export class ResponseStatusCodeError extends UndiciError {
|
|
37
53
|
constructor (
|
|
38
54
|
message?: string,
|