undici 6.2.1 → 6.3.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 +1 -1
- package/docs/api/Debug.md +62 -0
- package/docs/api/DiagnosticsChannel.md +3 -3
- package/index-fetch.js +3 -1
- package/lib/api/abort-signal.js +2 -2
- package/lib/cache/cache.js +13 -7
- package/lib/client.js +30 -29
- package/lib/compat/dispatcher-weakref.js +2 -0
- package/lib/core/diagnostics.js +202 -0
- package/lib/core/request.js +1 -17
- package/lib/fetch/body.js +6 -4
- package/lib/fetch/dataURL.js +34 -12
- package/lib/fetch/file.js +2 -8
- package/lib/fetch/index.js +7 -4
- package/lib/fetch/request.js +46 -13
- package/lib/fetch/response.js +1 -1
- package/lib/fetch/util.js +12 -4
- package/lib/fetch/webidl.js +3 -3
- package/lib/proxy-agent.js +2 -2
- package/lib/websocket/connection.js +1 -6
- package/lib/websocket/receiver.js +1 -5
- package/lib/websocket/websocket.js +1 -1
- package/package.json +16 -12
- package/types/balanced-pool.d.ts +11 -0
- package/types/client.d.ts +11 -0
- package/types/dispatcher.d.ts +1 -0
- package/types/pool.d.ts +11 -0
- package/types/proxy-agent.d.ts +0 -2
package/README.md
CHANGED
|
@@ -119,7 +119,7 @@ Returns a promise with the result of the `Dispatcher.request` method.
|
|
|
119
119
|
|
|
120
120
|
Calls `options.dispatcher.request(options)`.
|
|
121
121
|
|
|
122
|
-
See [Dispatcher.request](./docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details.
|
|
122
|
+
See [Dispatcher.request](./docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details, and [request examples](./examples/README.md) for examples.
|
|
123
123
|
|
|
124
124
|
### `undici.stream([url, options, ]factory): Promise`
|
|
125
125
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Debug
|
|
2
|
+
|
|
3
|
+
Undici (and subsenquently `fetch` and `websocket`) exposes a debug statement that can be enabled by setting `NODE_DEBUG` within the environment.
|
|
4
|
+
|
|
5
|
+
The flags availabile are:
|
|
6
|
+
|
|
7
|
+
## `undici`
|
|
8
|
+
|
|
9
|
+
This flag enables debug statements for the core undici library.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
NODE_DEBUG=undici node script.js
|
|
13
|
+
|
|
14
|
+
UNDICI 16241: connecting to nodejs.org using https:h1
|
|
15
|
+
UNDICI 16241: connecting to nodejs.org using https:h1
|
|
16
|
+
UNDICI 16241: connected to nodejs.org using https:h1
|
|
17
|
+
UNDICI 16241: sending request to GET https://nodejs.org//
|
|
18
|
+
UNDICI 16241: received response to GET https://nodejs.org// - HTTP 307
|
|
19
|
+
UNDICI 16241: connecting to nodejs.org using https:h1
|
|
20
|
+
UNDICI 16241: trailers received from GET https://nodejs.org//
|
|
21
|
+
UNDICI 16241: connected to nodejs.org using https:h1
|
|
22
|
+
UNDICI 16241: sending request to GET https://nodejs.org//en
|
|
23
|
+
UNDICI 16241: received response to GET https://nodejs.org//en - HTTP 200
|
|
24
|
+
UNDICI 16241: trailers received from GET https://nodejs.org//en
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## `fetch`
|
|
28
|
+
|
|
29
|
+
This flag enables debug statements for the `fetch` API.
|
|
30
|
+
|
|
31
|
+
> **Note**: statements are pretty similar to the ones in the `undici` flag, but scoped to `fetch`
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
NODE_DEBUG=fetch node script.js
|
|
35
|
+
|
|
36
|
+
FETCH 16241: connecting to nodejs.org using https:h1
|
|
37
|
+
FETCH 16241: connecting to nodejs.org using https:h1
|
|
38
|
+
FETCH 16241: connected to nodejs.org using https:h1
|
|
39
|
+
FETCH 16241: sending request to GET https://nodejs.org//
|
|
40
|
+
FETCH 16241: received response to GET https://nodejs.org// - HTTP 307
|
|
41
|
+
FETCH 16241: connecting to nodejs.org using https:h1
|
|
42
|
+
FETCH 16241: trailers received from GET https://nodejs.org//
|
|
43
|
+
FETCH 16241: connected to nodejs.org using https:h1
|
|
44
|
+
FETCH 16241: sending request to GET https://nodejs.org//en
|
|
45
|
+
FETCH 16241: received response to GET https://nodejs.org//en - HTTP 200
|
|
46
|
+
FETCH 16241: trailers received from GET https://nodejs.org//en
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## `websocket`
|
|
50
|
+
|
|
51
|
+
This flag enables debug statements for the `Websocket` API.
|
|
52
|
+
|
|
53
|
+
> **Note**: statements can overlap with `UNDICI` ones if `undici` or `fetch` flag has been enabled as well.
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
NODE_DEBUG=fetch node script.js
|
|
57
|
+
|
|
58
|
+
WEBSOCKET 18309: connecting to echo.websocket.org using https:h1
|
|
59
|
+
WEBSOCKET 18309: connected to echo.websocket.org using https:h1
|
|
60
|
+
WEBSOCKET 18309: sending request to GET https://echo.websocket.org//
|
|
61
|
+
WEBSOCKET 18309: connection opened <ip_address>
|
|
62
|
+
```
|
|
@@ -105,7 +105,7 @@ You can not assume that this event is related to any specific request.
|
|
|
105
105
|
import diagnosticsChannel from 'diagnostics_channel'
|
|
106
106
|
|
|
107
107
|
diagnosticsChannel.channel('undici:client:beforeConnect').subscribe(({ connectParams, connector }) => {
|
|
108
|
-
// const { host, hostname, protocol, port, servername } = connectParams
|
|
108
|
+
// const { host, hostname, protocol, port, servername, version } = connectParams
|
|
109
109
|
// connector is a function that creates the socket
|
|
110
110
|
})
|
|
111
111
|
```
|
|
@@ -118,7 +118,7 @@ This message is published after a connection is established.
|
|
|
118
118
|
import diagnosticsChannel from 'diagnostics_channel'
|
|
119
119
|
|
|
120
120
|
diagnosticsChannel.channel('undici:client:connected').subscribe(({ socket, connectParams, connector }) => {
|
|
121
|
-
// const { host, hostname, protocol, port, servername } = connectParams
|
|
121
|
+
// const { host, hostname, protocol, port, servername, version } = connectParams
|
|
122
122
|
// connector is a function that creates the socket
|
|
123
123
|
})
|
|
124
124
|
```
|
|
@@ -131,7 +131,7 @@ This message is published if it did not succeed to create new connection
|
|
|
131
131
|
import diagnosticsChannel from 'diagnostics_channel'
|
|
132
132
|
|
|
133
133
|
diagnosticsChannel.channel('undici:client:connectError').subscribe(({ error, socket, connectParams, connector }) => {
|
|
134
|
-
// const { host, hostname, protocol, port, servername } = connectParams
|
|
134
|
+
// const { host, hostname, protocol, port, servername, version } = connectParams
|
|
135
135
|
// connector is a function that creates the socket
|
|
136
136
|
console.log(`Connect failed with ${error.message}`)
|
|
137
137
|
})
|
package/index-fetch.js
CHANGED
|
@@ -4,7 +4,9 @@ const fetchImpl = require('./lib/fetch').fetch
|
|
|
4
4
|
|
|
5
5
|
module.exports.fetch = function fetch (resource, init = undefined) {
|
|
6
6
|
return fetchImpl(resource, init).catch((err) => {
|
|
7
|
-
|
|
7
|
+
if (typeof err === 'object') {
|
|
8
|
+
Error.captureStackTrace(err, this)
|
|
9
|
+
}
|
|
8
10
|
throw err
|
|
9
11
|
})
|
|
10
12
|
}
|
package/lib/api/abort-signal.js
CHANGED
|
@@ -6,9 +6,9 @@ const kSignal = Symbol('kSignal')
|
|
|
6
6
|
|
|
7
7
|
function abort (self) {
|
|
8
8
|
if (self.abort) {
|
|
9
|
-
self.abort()
|
|
9
|
+
self.abort(self[kSignal]?.reason)
|
|
10
10
|
} else {
|
|
11
|
-
self.onError(new RequestAbortedError())
|
|
11
|
+
self.onError(self[kSignal]?.reason ?? new RequestAbortedError())
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
package/lib/cache/cache.js
CHANGED
|
@@ -112,14 +112,12 @@ class Cache {
|
|
|
112
112
|
// 5.5.2
|
|
113
113
|
for (const response of responses) {
|
|
114
114
|
// 5.5.2.1
|
|
115
|
-
const responseObject = new Response(
|
|
116
|
-
const body = responseObject[kState].body
|
|
115
|
+
const responseObject = new Response(null)
|
|
117
116
|
responseObject[kState] = response
|
|
118
|
-
responseObject[kState].body = body
|
|
119
117
|
responseObject[kHeaders][kHeadersList] = response.headersList
|
|
120
118
|
responseObject[kHeaders][kGuard] = 'immutable'
|
|
121
119
|
|
|
122
|
-
responseList.push(responseObject)
|
|
120
|
+
responseList.push(responseObject.clone())
|
|
123
121
|
}
|
|
124
122
|
|
|
125
123
|
// 6.
|
|
@@ -146,8 +144,6 @@ class Cache {
|
|
|
146
144
|
webidl.brandCheck(this, Cache)
|
|
147
145
|
webidl.argumentLengthCheck(arguments, 1, { header: 'Cache.addAll' })
|
|
148
146
|
|
|
149
|
-
requests = webidl.converters['sequence<RequestInfo>'](requests)
|
|
150
|
-
|
|
151
147
|
// 1.
|
|
152
148
|
const responsePromises = []
|
|
153
149
|
|
|
@@ -155,7 +151,17 @@ class Cache {
|
|
|
155
151
|
const requestList = []
|
|
156
152
|
|
|
157
153
|
// 3.
|
|
158
|
-
for (
|
|
154
|
+
for (let request of requests) {
|
|
155
|
+
if (request === undefined) {
|
|
156
|
+
throw webidl.errors.conversionFailed({
|
|
157
|
+
prefix: 'Cache.addAll',
|
|
158
|
+
argument: 'Argument 1',
|
|
159
|
+
types: ['undefined is not allowed']
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
request = webidl.converters.RequestInfo(request)
|
|
164
|
+
|
|
159
165
|
if (typeof request === 'string') {
|
|
160
166
|
continue
|
|
161
167
|
}
|
package/lib/client.js
CHANGED
|
@@ -9,6 +9,7 @@ const net = require('net')
|
|
|
9
9
|
const http = require('http')
|
|
10
10
|
const { pipeline } = require('stream')
|
|
11
11
|
const util = require('./core/util')
|
|
12
|
+
const { channels } = require('./core/diagnostics')
|
|
12
13
|
const timers = require('./timers')
|
|
13
14
|
const Request = require('./core/request')
|
|
14
15
|
const DispatcherBase = require('./dispatcher-base')
|
|
@@ -108,21 +109,6 @@ const FastBuffer = Buffer[Symbol.species]
|
|
|
108
109
|
|
|
109
110
|
const kClosedResolve = Symbol('kClosedResolve')
|
|
110
111
|
|
|
111
|
-
const channels = {}
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
const diagnosticsChannel = require('diagnostics_channel')
|
|
115
|
-
channels.sendHeaders = diagnosticsChannel.channel('undici:client:sendHeaders')
|
|
116
|
-
channels.beforeConnect = diagnosticsChannel.channel('undici:client:beforeConnect')
|
|
117
|
-
channels.connectError = diagnosticsChannel.channel('undici:client:connectError')
|
|
118
|
-
channels.connected = diagnosticsChannel.channel('undici:client:connected')
|
|
119
|
-
} catch {
|
|
120
|
-
channels.sendHeaders = { hasSubscribers: false }
|
|
121
|
-
channels.beforeConnect = { hasSubscribers: false }
|
|
122
|
-
channels.connectError = { hasSubscribers: false }
|
|
123
|
-
channels.connected = { hasSubscribers: false }
|
|
124
|
-
}
|
|
125
|
-
|
|
126
112
|
/**
|
|
127
113
|
* @type {import('../types/client').default}
|
|
128
114
|
*/
|
|
@@ -1191,6 +1177,7 @@ async function connect (client) {
|
|
|
1191
1177
|
hostname,
|
|
1192
1178
|
protocol,
|
|
1193
1179
|
port,
|
|
1180
|
+
version: client[kHTTPConnVersion],
|
|
1194
1181
|
servername: client[kServerName],
|
|
1195
1182
|
localAddress: client[kLocalAddress]
|
|
1196
1183
|
},
|
|
@@ -1284,6 +1271,7 @@ async function connect (client) {
|
|
|
1284
1271
|
hostname,
|
|
1285
1272
|
protocol,
|
|
1286
1273
|
port,
|
|
1274
|
+
version: client[kHTTPConnVersion],
|
|
1287
1275
|
servername: client[kServerName],
|
|
1288
1276
|
localAddress: client[kLocalAddress]
|
|
1289
1277
|
},
|
|
@@ -1306,6 +1294,7 @@ async function connect (client) {
|
|
|
1306
1294
|
hostname,
|
|
1307
1295
|
protocol,
|
|
1308
1296
|
port,
|
|
1297
|
+
version: client[kHTTPConnVersion],
|
|
1309
1298
|
servername: client[kServerName],
|
|
1310
1299
|
localAddress: client[kLocalAddress]
|
|
1311
1300
|
},
|
|
@@ -1658,19 +1647,6 @@ function writeH2 (client, session, request) {
|
|
|
1658
1647
|
return false
|
|
1659
1648
|
}
|
|
1660
1649
|
|
|
1661
|
-
try {
|
|
1662
|
-
// TODO(HTTP/2): Should we call onConnect immediately or on stream ready event?
|
|
1663
|
-
request.onConnect((err) => {
|
|
1664
|
-
if (request.aborted || request.completed) {
|
|
1665
|
-
return
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
errorRequest(client, request, err || new RequestAbortedError())
|
|
1669
|
-
})
|
|
1670
|
-
} catch (err) {
|
|
1671
|
-
errorRequest(client, request, err)
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
1650
|
if (request.aborted) {
|
|
1675
1651
|
return false
|
|
1676
1652
|
}
|
|
@@ -1682,9 +1658,34 @@ function writeH2 (client, session, request) {
|
|
|
1682
1658
|
headers[HTTP2_HEADER_AUTHORITY] = host || client[kHost]
|
|
1683
1659
|
headers[HTTP2_HEADER_METHOD] = method
|
|
1684
1660
|
|
|
1661
|
+
try {
|
|
1662
|
+
// We are already connected, streams are pending.
|
|
1663
|
+
// We can call on connect, and wait for abort
|
|
1664
|
+
request.onConnect((err) => {
|
|
1665
|
+
if (request.aborted || request.completed) {
|
|
1666
|
+
return
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
err = err || new RequestAbortedError()
|
|
1670
|
+
|
|
1671
|
+
if (stream != null) {
|
|
1672
|
+
util.destroy(stream, err)
|
|
1673
|
+
|
|
1674
|
+
h2State.openStreams -= 1
|
|
1675
|
+
if (h2State.openStreams === 0) {
|
|
1676
|
+
session.unref()
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
errorRequest(client, request, err)
|
|
1681
|
+
})
|
|
1682
|
+
} catch (err) {
|
|
1683
|
+
errorRequest(client, request, err)
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1685
1686
|
if (method === 'CONNECT') {
|
|
1686
1687
|
session.ref()
|
|
1687
|
-
//
|
|
1688
|
+
// We are already connected, streams are pending, first request
|
|
1688
1689
|
// will create a new stream. We trigger a request to create the stream and wait until
|
|
1689
1690
|
// `ready` event is triggered
|
|
1690
1691
|
// We disabled endStream to allow the user to write to the stream
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const diagnosticsChannel = require('diagnostics_channel')
|
|
3
|
+
const util = require('util')
|
|
4
|
+
|
|
5
|
+
const undiciDebugLog = util.debuglog('undici')
|
|
6
|
+
const fetchDebuglog = util.debuglog('fetch')
|
|
7
|
+
const websocketDebuglog = util.debuglog('websocket')
|
|
8
|
+
let isClientSet = false
|
|
9
|
+
const channels = {
|
|
10
|
+
// Client
|
|
11
|
+
beforeConnect: diagnosticsChannel.channel('undici:client:beforeConnect'),
|
|
12
|
+
connected: diagnosticsChannel.channel('undici:client:connected'),
|
|
13
|
+
connectError: diagnosticsChannel.channel('undici:client:connectError'),
|
|
14
|
+
sendHeaders: diagnosticsChannel.channel('undici:client:sendHeaders'),
|
|
15
|
+
// Request
|
|
16
|
+
create: diagnosticsChannel.channel('undici:request:create'),
|
|
17
|
+
bodySent: diagnosticsChannel.channel('undici:request:bodySent'),
|
|
18
|
+
headers: diagnosticsChannel.channel('undici:request:headers'),
|
|
19
|
+
trailers: diagnosticsChannel.channel('undici:request:trailers'),
|
|
20
|
+
error: diagnosticsChannel.channel('undici:request:error'),
|
|
21
|
+
// WebSocket
|
|
22
|
+
open: diagnosticsChannel.channel('undici:websocket:open'),
|
|
23
|
+
close: diagnosticsChannel.channel('undici:websocket:close'),
|
|
24
|
+
socketError: diagnosticsChannel.channel('undici:websocket:socket_error'),
|
|
25
|
+
ping: diagnosticsChannel.channel('undici:websocket:ping'),
|
|
26
|
+
pong: diagnosticsChannel.channel('undici:websocket:pong')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (undiciDebugLog.enabled || fetchDebuglog.enabled) {
|
|
30
|
+
const debuglog = fetchDebuglog.enabled ? fetchDebuglog : undiciDebugLog
|
|
31
|
+
|
|
32
|
+
// Track all Client events
|
|
33
|
+
diagnosticsChannel.channel('undici:client:beforeConnect').subscribe(evt => {
|
|
34
|
+
const {
|
|
35
|
+
connectParams: { version, protocol, port, host }
|
|
36
|
+
} = evt
|
|
37
|
+
debuglog(
|
|
38
|
+
'connecting to %s using %s%s',
|
|
39
|
+
`${host}${port ? `:${port}` : ''}`,
|
|
40
|
+
protocol,
|
|
41
|
+
version
|
|
42
|
+
)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
diagnosticsChannel.channel('undici:client:connected').subscribe(evt => {
|
|
46
|
+
const {
|
|
47
|
+
connectParams: { version, protocol, port, host }
|
|
48
|
+
} = evt
|
|
49
|
+
debuglog(
|
|
50
|
+
'connected to %s using %s%s',
|
|
51
|
+
`${host}${port ? `:${port}` : ''}`,
|
|
52
|
+
protocol,
|
|
53
|
+
version
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
diagnosticsChannel.channel('undici:client:connectError').subscribe(evt => {
|
|
58
|
+
const {
|
|
59
|
+
connectParams: { version, protocol, port, host },
|
|
60
|
+
error
|
|
61
|
+
} = evt
|
|
62
|
+
debuglog(
|
|
63
|
+
'connection to %s using %s%s errored - %s',
|
|
64
|
+
`${host}${port ? `:${port}` : ''}`,
|
|
65
|
+
protocol,
|
|
66
|
+
version,
|
|
67
|
+
error.message
|
|
68
|
+
)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
diagnosticsChannel.channel('undici:client:sendHeaders').subscribe(evt => {
|
|
72
|
+
const {
|
|
73
|
+
request: { method, path, origin }
|
|
74
|
+
} = evt
|
|
75
|
+
debuglog('sending request to %s %s/%s', method, origin, path)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// Track Request events
|
|
79
|
+
diagnosticsChannel.channel('undici:request:headers').subscribe(evt => {
|
|
80
|
+
const {
|
|
81
|
+
request: { method, path, origin },
|
|
82
|
+
response: { statusCode }
|
|
83
|
+
} = evt
|
|
84
|
+
debuglog(
|
|
85
|
+
'received response to %s %s/%s - HTTP %d',
|
|
86
|
+
method,
|
|
87
|
+
origin,
|
|
88
|
+
path,
|
|
89
|
+
statusCode
|
|
90
|
+
)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
diagnosticsChannel.channel('undici:request:trailers').subscribe(evt => {
|
|
94
|
+
const {
|
|
95
|
+
request: { method, path, origin }
|
|
96
|
+
} = evt
|
|
97
|
+
debuglog('trailers received from %s %s/%s', method, origin, path)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
diagnosticsChannel.channel('undici:request:error').subscribe(evt => {
|
|
101
|
+
const {
|
|
102
|
+
request: { method, path, origin },
|
|
103
|
+
error
|
|
104
|
+
} = evt
|
|
105
|
+
debuglog(
|
|
106
|
+
'request to %s %s/%s errored - %s',
|
|
107
|
+
method,
|
|
108
|
+
origin,
|
|
109
|
+
path,
|
|
110
|
+
error.message
|
|
111
|
+
)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
isClientSet = true
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (websocketDebuglog.enabled) {
|
|
118
|
+
if (!isClientSet) {
|
|
119
|
+
const debuglog = undiciDebugLog.enabled ? undiciDebugLog : websocketDebuglog
|
|
120
|
+
diagnosticsChannel.channel('undici:client:beforeConnect').subscribe(evt => {
|
|
121
|
+
const {
|
|
122
|
+
connectParams: { version, protocol, port, host }
|
|
123
|
+
} = evt
|
|
124
|
+
debuglog(
|
|
125
|
+
'connecting to %s%s using %s%s',
|
|
126
|
+
host,
|
|
127
|
+
port ? `:${port}` : '',
|
|
128
|
+
protocol,
|
|
129
|
+
version
|
|
130
|
+
)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
diagnosticsChannel.channel('undici:client:connected').subscribe(evt => {
|
|
134
|
+
const {
|
|
135
|
+
connectParams: { version, protocol, port, host }
|
|
136
|
+
} = evt
|
|
137
|
+
debuglog(
|
|
138
|
+
'connected to %s%s using %s%s',
|
|
139
|
+
host,
|
|
140
|
+
port ? `:${port}` : '',
|
|
141
|
+
protocol,
|
|
142
|
+
version
|
|
143
|
+
)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
diagnosticsChannel.channel('undici:client:connectError').subscribe(evt => {
|
|
147
|
+
const {
|
|
148
|
+
connectParams: { version, protocol, port, host },
|
|
149
|
+
error
|
|
150
|
+
} = evt
|
|
151
|
+
debuglog(
|
|
152
|
+
'connection to %s%s using %s%s errored - %s',
|
|
153
|
+
host,
|
|
154
|
+
port ? `:${port}` : '',
|
|
155
|
+
protocol,
|
|
156
|
+
version,
|
|
157
|
+
error.message
|
|
158
|
+
)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
diagnosticsChannel.channel('undici:client:sendHeaders').subscribe(evt => {
|
|
162
|
+
const {
|
|
163
|
+
request: { method, path, origin }
|
|
164
|
+
} = evt
|
|
165
|
+
debuglog('sending request to %s %s/%s', method, origin, path)
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Track all WebSocket events
|
|
170
|
+
diagnosticsChannel.channel('undici:websocket:open').subscribe(evt => {
|
|
171
|
+
const {
|
|
172
|
+
address: { address, port }
|
|
173
|
+
} = evt
|
|
174
|
+
websocketDebuglog('connection opened %s%s', address, port ? `:${port}` : '')
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
diagnosticsChannel.channel('undici:websocket:close').subscribe(evt => {
|
|
178
|
+
const { websocket, code, reason } = evt
|
|
179
|
+
websocketDebuglog(
|
|
180
|
+
'closed connection to %s - %s %s',
|
|
181
|
+
websocket.url,
|
|
182
|
+
code,
|
|
183
|
+
reason
|
|
184
|
+
)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
diagnosticsChannel.channel('undici:websocket:socket_error').subscribe(err => {
|
|
188
|
+
websocketDebuglog('connection errored - %s', err.message)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
diagnosticsChannel.channel('undici:websocket:ping').subscribe(evt => {
|
|
192
|
+
websocketDebuglog('ping received')
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
diagnosticsChannel.channel('undici:websocket:pong').subscribe(evt => {
|
|
196
|
+
websocketDebuglog('pong received')
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports = {
|
|
201
|
+
channels
|
|
202
|
+
}
|
package/lib/core/request.js
CHANGED
|
@@ -7,6 +7,7 @@ const {
|
|
|
7
7
|
const assert = require('assert')
|
|
8
8
|
const { kHTTP2BuildRequest, kHTTP2CopyHeaders, kHTTP1BuildRequest } = require('./symbols')
|
|
9
9
|
const util = require('./util')
|
|
10
|
+
const { channels } = require('./diagnostics.js')
|
|
10
11
|
const { headerNameLowerCasedRecord } = require('./constants')
|
|
11
12
|
|
|
12
13
|
// headerCharRegex have been lifted from
|
|
@@ -25,25 +26,8 @@ const invalidPathRegex = /[^\u0021-\u00ff]/
|
|
|
25
26
|
|
|
26
27
|
const kHandler = Symbol('handler')
|
|
27
28
|
|
|
28
|
-
const channels = {}
|
|
29
|
-
|
|
30
29
|
let extractBody
|
|
31
30
|
|
|
32
|
-
try {
|
|
33
|
-
const diagnosticsChannel = require('diagnostics_channel')
|
|
34
|
-
channels.create = diagnosticsChannel.channel('undici:request:create')
|
|
35
|
-
channels.bodySent = diagnosticsChannel.channel('undici:request:bodySent')
|
|
36
|
-
channels.headers = diagnosticsChannel.channel('undici:request:headers')
|
|
37
|
-
channels.trailers = diagnosticsChannel.channel('undici:request:trailers')
|
|
38
|
-
channels.error = diagnosticsChannel.channel('undici:request:error')
|
|
39
|
-
} catch {
|
|
40
|
-
channels.create = { hasSubscribers: false }
|
|
41
|
-
channels.bodySent = { hasSubscribers: false }
|
|
42
|
-
channels.headers = { hasSubscribers: false }
|
|
43
|
-
channels.trailers = { hasSubscribers: false }
|
|
44
|
-
channels.error = { hasSubscribers: false }
|
|
45
|
-
}
|
|
46
|
-
|
|
47
31
|
class Request {
|
|
48
32
|
constructor (origin, {
|
|
49
33
|
path,
|
package/lib/fetch/body.js
CHANGED
|
@@ -14,7 +14,7 @@ const { FormData } = require('./formdata')
|
|
|
14
14
|
const { kState } = require('./symbols')
|
|
15
15
|
const { webidl } = require('./webidl')
|
|
16
16
|
const { Blob, File: NativeFile } = require('buffer')
|
|
17
|
-
const { kBodyUsed } = require('../core/symbols')
|
|
17
|
+
const { kBodyUsed, kHeadersList } = require('../core/symbols')
|
|
18
18
|
const assert = require('assert')
|
|
19
19
|
const { isErrored } = require('../core/util')
|
|
20
20
|
const { isUint8Array, isArrayBuffer } = require('util/types')
|
|
@@ -369,10 +369,12 @@ function bodyMixinMethods (instance) {
|
|
|
369
369
|
|
|
370
370
|
throwIfAborted(this[kState])
|
|
371
371
|
|
|
372
|
-
const contentType = this.headers.get('
|
|
372
|
+
const contentType = this.headers[kHeadersList].get('content-type', true)
|
|
373
|
+
|
|
374
|
+
const mimeType = contentType !== null ? parseMIMEType(contentType) : 'failure'
|
|
373
375
|
|
|
374
376
|
// If mimeType’s essence is "multipart/form-data", then:
|
|
375
|
-
if (/
|
|
377
|
+
if (mimeType !== 'failure' && mimeType.essence === 'multipart/form-data') {
|
|
376
378
|
const headers = {}
|
|
377
379
|
for (const [key, value] of this.headers) headers[key] = value
|
|
378
380
|
|
|
@@ -430,7 +432,7 @@ function bodyMixinMethods (instance) {
|
|
|
430
432
|
await busboyResolve
|
|
431
433
|
|
|
432
434
|
return responseFormData
|
|
433
|
-
} else if (/
|
|
435
|
+
} else if (mimeType !== 'failure' && mimeType.essence === 'application/x-www-form-urlencoded') {
|
|
434
436
|
// Otherwise, if mimeType’s essence is "application/x-www-form-urlencoded", then:
|
|
435
437
|
|
|
436
438
|
// 1. Let entries be the result of parsing bytes.
|
package/lib/fetch/dataURL.js
CHANGED
|
@@ -188,11 +188,28 @@ function stringPercentDecode (input) {
|
|
|
188
188
|
return percentDecode(bytes)
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
/**
|
|
192
|
+
* @param {number} byte
|
|
193
|
+
*/
|
|
191
194
|
function isHexCharByte (byte) {
|
|
192
195
|
// 0-9 A-F a-f
|
|
193
196
|
return (byte >= 0x30 && byte <= 0x39) || (byte >= 0x41 && byte <= 0x46) || (byte >= 0x61 && byte <= 0x66)
|
|
194
197
|
}
|
|
195
198
|
|
|
199
|
+
/**
|
|
200
|
+
* @param {number} byte
|
|
201
|
+
*/
|
|
202
|
+
function hexByteToNumber (byte) {
|
|
203
|
+
return (
|
|
204
|
+
// 0-9
|
|
205
|
+
byte >= 0x30 && byte <= 0x39
|
|
206
|
+
? (byte - 48)
|
|
207
|
+
// Convert to uppercase
|
|
208
|
+
// ((byte & 0xDF) - 65) + 10
|
|
209
|
+
: ((byte & 0xDF) - 55)
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
|
|
196
213
|
// https://url.spec.whatwg.org/#percent-decode
|
|
197
214
|
/** @param {Uint8Array} input */
|
|
198
215
|
function percentDecode (input) {
|
|
@@ -224,11 +241,8 @@ function percentDecode (input) {
|
|
|
224
241
|
} else {
|
|
225
242
|
// 1. Let bytePoint be the two bytes after byte in input,
|
|
226
243
|
// decoded, and then interpreted as hexadecimal number.
|
|
227
|
-
const nextTwoBytes = String.fromCharCode(input[i + 1], input[i + 2])
|
|
228
|
-
const bytePoint = Number.parseInt(nextTwoBytes, 16)
|
|
229
|
-
|
|
230
244
|
// 2. Append a byte whose value is bytePoint to output.
|
|
231
|
-
output[j++] =
|
|
245
|
+
output[j++] = (hexByteToNumber(input[i + 1]) << 4) | hexByteToNumber(input[i + 2])
|
|
232
246
|
|
|
233
247
|
// 3. Skip the next two bytes in input.
|
|
234
248
|
i += 2
|
|
@@ -590,14 +604,18 @@ function isHTTPWhiteSpace (char) {
|
|
|
590
604
|
* @param {boolean} [trailing=true]
|
|
591
605
|
*/
|
|
592
606
|
function removeHTTPWhitespace (str, leading = true, trailing = true) {
|
|
593
|
-
let
|
|
607
|
+
let lead = 0
|
|
608
|
+
let trail = str.length - 1
|
|
609
|
+
|
|
594
610
|
if (leading) {
|
|
595
|
-
while (
|
|
611
|
+
while (lead < str.length && isHTTPWhiteSpace(str.charCodeAt(lead))) lead++
|
|
596
612
|
}
|
|
613
|
+
|
|
597
614
|
if (trailing) {
|
|
598
|
-
while (
|
|
615
|
+
while (trail > 0 && isHTTPWhiteSpace(str.charCodeAt(trail))) trail--
|
|
599
616
|
}
|
|
600
|
-
|
|
617
|
+
|
|
618
|
+
return lead === 0 && trail === str.length - 1 ? str : str.slice(lead, trail + 1)
|
|
601
619
|
}
|
|
602
620
|
|
|
603
621
|
/**
|
|
@@ -616,14 +634,18 @@ function isASCIIWhitespace (char) {
|
|
|
616
634
|
* @param {boolean} [trailing=true]
|
|
617
635
|
*/
|
|
618
636
|
function removeASCIIWhitespace (str, leading = true, trailing = true) {
|
|
619
|
-
let
|
|
637
|
+
let lead = 0
|
|
638
|
+
let trail = str.length - 1
|
|
639
|
+
|
|
620
640
|
if (leading) {
|
|
621
|
-
while (
|
|
641
|
+
while (lead < str.length && isASCIIWhitespace(str.charCodeAt(lead))) lead++
|
|
622
642
|
}
|
|
643
|
+
|
|
623
644
|
if (trailing) {
|
|
624
|
-
while (
|
|
645
|
+
while (trail > 0 && isASCIIWhitespace(str.charCodeAt(trail))) trail--
|
|
625
646
|
}
|
|
626
|
-
|
|
647
|
+
|
|
648
|
+
return lead === 0 && trail === str.length - 1 ? str : str.slice(lead, trail + 1)
|
|
627
649
|
}
|
|
628
650
|
|
|
629
651
|
module.exports = {
|
package/lib/fetch/file.js
CHANGED
|
@@ -211,10 +211,7 @@ webidl.converters.BlobPart = function (V, opts) {
|
|
|
211
211
|
return webidl.converters.Blob(V, { strict: false })
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
if (
|
|
215
|
-
ArrayBuffer.isView(V) ||
|
|
216
|
-
types.isAnyArrayBuffer(V)
|
|
217
|
-
) {
|
|
214
|
+
if (ArrayBuffer.isView(V) || types.isAnyArrayBuffer(V)) {
|
|
218
215
|
return webidl.converters.BufferSource(V, opts)
|
|
219
216
|
}
|
|
220
217
|
}
|
|
@@ -282,10 +279,7 @@ function processBlobParts (parts, options) {
|
|
|
282
279
|
|
|
283
280
|
// 3. Append the result of UTF-8 encoding s to bytes.
|
|
284
281
|
bytes.push(encoder.encode(s))
|
|
285
|
-
} else if (
|
|
286
|
-
types.isAnyArrayBuffer(element) ||
|
|
287
|
-
types.isTypedArray(element)
|
|
288
|
-
) {
|
|
282
|
+
} else if (ArrayBuffer.isView(element) || types.isArrayBuffer(element)) {
|
|
289
283
|
// 2. If element is a BufferSource, get a copy of the
|
|
290
284
|
// bytes held by the buffer source, and append those
|
|
291
285
|
// bytes to bytes.
|
package/lib/fetch/index.js
CHANGED
|
@@ -1206,7 +1206,7 @@ async function httpFetch (fetchParams) {
|
|
|
1206
1206
|
// encouraged to, transmit an RST_STREAM frame.
|
|
1207
1207
|
// See, https://github.com/whatwg/fetch/issues/1288
|
|
1208
1208
|
if (request.redirect !== 'manual') {
|
|
1209
|
-
fetchParams.controller.connection.destroy()
|
|
1209
|
+
fetchParams.controller.connection.destroy(undefined, false)
|
|
1210
1210
|
}
|
|
1211
1211
|
|
|
1212
1212
|
// 2. Switch on request’s redirect mode:
|
|
@@ -1718,10 +1718,12 @@ async function httpNetworkFetch (
|
|
|
1718
1718
|
fetchParams.controller.connection = {
|
|
1719
1719
|
abort: null,
|
|
1720
1720
|
destroyed: false,
|
|
1721
|
-
destroy (err) {
|
|
1721
|
+
destroy (err, abort = true) {
|
|
1722
1722
|
if (!this.destroyed) {
|
|
1723
1723
|
this.destroyed = true
|
|
1724
|
-
|
|
1724
|
+
if (abort) {
|
|
1725
|
+
this.abort?.(err ?? new DOMException('The operation was aborted.', 'AbortError'))
|
|
1726
|
+
}
|
|
1725
1727
|
}
|
|
1726
1728
|
}
|
|
1727
1729
|
}
|
|
@@ -2152,7 +2154,8 @@ async function httpNetworkFetch (
|
|
|
2152
2154
|
} else {
|
|
2153
2155
|
const keys = Object.keys(rawHeaders)
|
|
2154
2156
|
for (let i = 0; i < keys.length; ++i) {
|
|
2155
|
-
|
|
2157
|
+
// The header names are already in lowercase.
|
|
2158
|
+
headersList.append(keys[i], rawHeaders[keys[i]], true)
|
|
2156
2159
|
}
|
|
2157
2160
|
// For H2, The header names are already in lowercase,
|
|
2158
2161
|
// so we can avoid the `HeadersList#get` call here.
|
package/lib/fetch/request.js
CHANGED
|
@@ -38,6 +38,8 @@ const requestFinalizer = new FinalizationRegistry(({ signal, abort }) => {
|
|
|
38
38
|
signal.removeEventListener('abort', abort)
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
+
let patchMethodWarning = false
|
|
42
|
+
|
|
41
43
|
// https://fetch.spec.whatwg.org/#request-class
|
|
42
44
|
class Request {
|
|
43
45
|
// https://fetch.spec.whatwg.org/#dom-request
|
|
@@ -313,21 +315,36 @@ class Request {
|
|
|
313
315
|
// 1. Let method be init["method"].
|
|
314
316
|
let method = init.method
|
|
315
317
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
if (
|
|
319
|
-
|
|
320
|
-
|
|
318
|
+
const mayBeNormalized = normalizeMethodRecord[method]
|
|
319
|
+
|
|
320
|
+
if (mayBeNormalized !== undefined) {
|
|
321
|
+
// Note: Bypass validation DELETE, GET, HEAD, OPTIONS, POST, PUT, PATCH and these lowercase ones
|
|
322
|
+
request.method = mayBeNormalized
|
|
323
|
+
} else {
|
|
324
|
+
// 2. If method is not a method or method is a forbidden method, then
|
|
325
|
+
// throw a TypeError.
|
|
326
|
+
if (!isValidHTTPToken(method)) {
|
|
327
|
+
throw new TypeError(`'${method}' is not a valid HTTP method.`)
|
|
328
|
+
}
|
|
321
329
|
|
|
322
|
-
|
|
323
|
-
|
|
330
|
+
if (forbiddenMethodsSet.has(method.toUpperCase())) {
|
|
331
|
+
throw new TypeError(`'${method}' HTTP method is unsupported.`)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// 3. Normalize method.
|
|
335
|
+
method = normalizeMethod(method)
|
|
336
|
+
|
|
337
|
+
// 4. Set request’s method to method.
|
|
338
|
+
request.method = method
|
|
324
339
|
}
|
|
325
340
|
|
|
326
|
-
|
|
327
|
-
|
|
341
|
+
if (!patchMethodWarning && request.method === 'patch') {
|
|
342
|
+
process.emitWarning('Using `patch` is highly likely to result in a `405 Method Not Allowed`. `PATCH` is much more likely to succeed.', {
|
|
343
|
+
code: 'UNDICI-FETCH-patch'
|
|
344
|
+
})
|
|
328
345
|
|
|
329
|
-
|
|
330
|
-
|
|
346
|
+
patchMethodWarning = true
|
|
347
|
+
}
|
|
331
348
|
}
|
|
332
349
|
|
|
333
350
|
// 26. If init["signal"] exists, then set signal to it.
|
|
@@ -371,6 +388,18 @@ class Request {
|
|
|
371
388
|
const abort = function () {
|
|
372
389
|
const ac = acRef.deref()
|
|
373
390
|
if (ac !== undefined) {
|
|
391
|
+
// Currently, there is a problem with FinalizationRegistry.
|
|
392
|
+
// https://github.com/nodejs/node/issues/49344
|
|
393
|
+
// https://github.com/nodejs/node/issues/47748
|
|
394
|
+
// In the case of abort, the first step is to unregister from it.
|
|
395
|
+
// If the controller can refer to it, it is still registered.
|
|
396
|
+
// It will be removed in the future.
|
|
397
|
+
requestFinalizer.unregister(abort)
|
|
398
|
+
|
|
399
|
+
// Unsubscribe a listener.
|
|
400
|
+
// FinalizationRegistry will no longer be called, so this must be done.
|
|
401
|
+
this.removeEventListener('abort', abort)
|
|
402
|
+
|
|
374
403
|
ac.abort(this.reason)
|
|
375
404
|
}
|
|
376
405
|
}
|
|
@@ -388,7 +417,11 @@ class Request {
|
|
|
388
417
|
} catch {}
|
|
389
418
|
|
|
390
419
|
util.addAbortListener(signal, abort)
|
|
391
|
-
|
|
420
|
+
// The third argument must be a registry key to be unregistered.
|
|
421
|
+
// Without it, you cannot unregister.
|
|
422
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry
|
|
423
|
+
// abort is used as the unregister key. (because it is unique)
|
|
424
|
+
requestFinalizer.register(ac, { signal, abort }, abort)
|
|
392
425
|
}
|
|
393
426
|
}
|
|
394
427
|
|
|
@@ -471,7 +504,7 @@ class Request {
|
|
|
471
504
|
// 3, If Content-Type is non-null and this’s headers’s header list does
|
|
472
505
|
// not contain `Content-Type`, then append `Content-Type`/Content-Type to
|
|
473
506
|
// this’s headers.
|
|
474
|
-
if (contentType && !this[kHeaders][kHeadersList].contains('content-type')) {
|
|
507
|
+
if (contentType && !this[kHeaders][kHeadersList].contains('content-type', true)) {
|
|
475
508
|
this[kHeaders].append('content-type', contentType)
|
|
476
509
|
}
|
|
477
510
|
}
|
package/lib/fetch/response.js
CHANGED
|
@@ -524,7 +524,7 @@ webidl.converters.XMLHttpRequestBodyInit = function (V) {
|
|
|
524
524
|
return webidl.converters.Blob(V, { strict: false })
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
-
if (
|
|
527
|
+
if (ArrayBuffer.isView(V) || types.isArrayBuffer(V)) {
|
|
528
528
|
return webidl.converters.BufferSource(V)
|
|
529
529
|
}
|
|
530
530
|
|
package/lib/fetch/util.js
CHANGED
|
@@ -584,7 +584,7 @@ function bytesMatch (bytes, metadataList) {
|
|
|
584
584
|
// https://w3c.github.io/webappsec-subresource-integrity/#grammardef-hash-with-options
|
|
585
585
|
// https://www.w3.org/TR/CSP2/#source-list-syntax
|
|
586
586
|
// https://www.rfc-editor.org/rfc/rfc5234#appendix-B.1
|
|
587
|
-
const parseHashWithOptions = /(
|
|
587
|
+
const parseHashWithOptions = /(?<algo>sha256|sha384|sha512)-(?<hash>[A-Za-z0-9+/]+={0,2}(?=\s|$))( +[!-~]*)?/i
|
|
588
588
|
|
|
589
589
|
/**
|
|
590
590
|
* @see https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata
|
|
@@ -681,7 +681,7 @@ function isCancelled (fetchParams) {
|
|
|
681
681
|
fetchParams.controller.state === 'terminated'
|
|
682
682
|
}
|
|
683
683
|
|
|
684
|
-
const
|
|
684
|
+
const normalizeMethodRecordBase = {
|
|
685
685
|
delete: 'DELETE',
|
|
686
686
|
DELETE: 'DELETE',
|
|
687
687
|
get: 'GET',
|
|
@@ -696,7 +696,14 @@ const normalizeMethodRecord = {
|
|
|
696
696
|
PUT: 'PUT'
|
|
697
697
|
}
|
|
698
698
|
|
|
699
|
+
const normalizeMethodRecord = {
|
|
700
|
+
...normalizeMethodRecordBase,
|
|
701
|
+
patch: 'patch',
|
|
702
|
+
PATCH: 'PATCH'
|
|
703
|
+
}
|
|
704
|
+
|
|
699
705
|
// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`.
|
|
706
|
+
Object.setPrototypeOf(normalizeMethodRecordBase, null)
|
|
700
707
|
Object.setPrototypeOf(normalizeMethodRecord, null)
|
|
701
708
|
|
|
702
709
|
/**
|
|
@@ -704,7 +711,7 @@ Object.setPrototypeOf(normalizeMethodRecord, null)
|
|
|
704
711
|
* @param {string} method
|
|
705
712
|
*/
|
|
706
713
|
function normalizeMethod (method) {
|
|
707
|
-
return
|
|
714
|
+
return normalizeMethodRecordBase[method.toLowerCase()] ?? method
|
|
708
715
|
}
|
|
709
716
|
|
|
710
717
|
// https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string
|
|
@@ -1213,5 +1220,6 @@ module.exports = {
|
|
|
1213
1220
|
readAllBytes,
|
|
1214
1221
|
normalizeMethodRecord,
|
|
1215
1222
|
simpleRangeHeaderValue,
|
|
1216
|
-
buildContentRange
|
|
1223
|
+
buildContentRange,
|
|
1224
|
+
parseMetadata
|
|
1217
1225
|
}
|
package/lib/fetch/webidl.js
CHANGED
|
@@ -614,15 +614,15 @@ webidl.converters.DataView = function (V, opts = {}) {
|
|
|
614
614
|
// https://webidl.spec.whatwg.org/#BufferSource
|
|
615
615
|
webidl.converters.BufferSource = function (V, opts = {}) {
|
|
616
616
|
if (types.isAnyArrayBuffer(V)) {
|
|
617
|
-
return webidl.converters.ArrayBuffer(V, opts)
|
|
617
|
+
return webidl.converters.ArrayBuffer(V, { ...opts, allowShared: false })
|
|
618
618
|
}
|
|
619
619
|
|
|
620
620
|
if (types.isTypedArray(V)) {
|
|
621
|
-
return webidl.converters.TypedArray(V, V.constructor)
|
|
621
|
+
return webidl.converters.TypedArray(V, V.constructor, { ...opts, allowShared: false })
|
|
622
622
|
}
|
|
623
623
|
|
|
624
624
|
if (types.isDataView(V)) {
|
|
625
|
-
return webidl.converters.DataView(V, opts)
|
|
625
|
+
return webidl.converters.DataView(V, opts, { ...opts, allowShared: false })
|
|
626
626
|
}
|
|
627
627
|
|
|
628
628
|
throw new TypeError(`Could not convert ${V} to a BufferSource.`)
|
package/lib/proxy-agent.js
CHANGED
|
@@ -66,7 +66,7 @@ class ProxyAgent extends DispatcherBase {
|
|
|
66
66
|
this[kProxyHeaders] = opts.headers || {}
|
|
67
67
|
|
|
68
68
|
const resolvedUrl = new URL(opts.uri)
|
|
69
|
-
const { origin, port,
|
|
69
|
+
const { origin, port, username, password } = resolvedUrl
|
|
70
70
|
|
|
71
71
|
if (opts.auth && opts.token) {
|
|
72
72
|
throw new InvalidArgumentError('opts.auth cannot be used in combination with opts.token')
|
|
@@ -97,7 +97,7 @@ class ProxyAgent extends DispatcherBase {
|
|
|
97
97
|
signal: opts.signal,
|
|
98
98
|
headers: {
|
|
99
99
|
...this[kProxyHeaders],
|
|
100
|
-
host
|
|
100
|
+
host: requestedHost
|
|
101
101
|
}
|
|
102
102
|
})
|
|
103
103
|
if (statusCode !== 200) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const diagnosticsChannel = require('diagnostics_channel')
|
|
4
3
|
const { uid, states } = require('./constants')
|
|
5
4
|
const {
|
|
6
5
|
kReadyState,
|
|
@@ -9,6 +8,7 @@ const {
|
|
|
9
8
|
kReceivedClose
|
|
10
9
|
} = require('./symbols')
|
|
11
10
|
const { fireEvent, failWebsocketConnection } = require('./util')
|
|
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')
|
|
@@ -16,11 +16,6 @@ const { Headers } = require('../fetch/headers')
|
|
|
16
16
|
const { getGlobalDispatcher } = require('../global')
|
|
17
17
|
const { kHeadersList } = require('../core/symbols')
|
|
18
18
|
|
|
19
|
-
const channels = {}
|
|
20
|
-
channels.open = diagnosticsChannel.channel('undici:websocket:open')
|
|
21
|
-
channels.close = diagnosticsChannel.channel('undici:websocket:close')
|
|
22
|
-
channels.socketError = diagnosticsChannel.channel('undici:websocket:socket_error')
|
|
23
|
-
|
|
24
19
|
/** @type {import('crypto')} */
|
|
25
20
|
let crypto
|
|
26
21
|
try {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { Writable } = require('stream')
|
|
4
|
-
const diagnosticsChannel = require('diagnostics_channel')
|
|
5
4
|
const { parserStates, opcodes, states, emptyBuffer } = require('./constants')
|
|
6
5
|
const { kReadyState, kSentClose, kResponse, kReceivedClose } = require('./symbols')
|
|
6
|
+
const { channels } = require('../core/diagnostics')
|
|
7
7
|
const { isValidStatusCode, failWebsocketConnection, websocketMessageReceived } = require('./util')
|
|
8
8
|
const { WebsocketFrameSend } = require('./frame')
|
|
9
9
|
|
|
@@ -12,10 +12,6 @@ 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 channels = {}
|
|
16
|
-
channels.ping = diagnosticsChannel.channel('undici:websocket:ping')
|
|
17
|
-
channels.pong = diagnosticsChannel.channel('undici:websocket:pong')
|
|
18
|
-
|
|
19
15
|
class ByteParser extends Writable {
|
|
20
16
|
#buffers = []
|
|
21
17
|
#byteOffset = 0
|
|
@@ -627,7 +627,7 @@ webidl.converters.WebSocketSendData = function (V) {
|
|
|
627
627
|
return webidl.converters.Blob(V, { strict: false })
|
|
628
628
|
}
|
|
629
629
|
|
|
630
|
-
if (ArrayBuffer.isView(V) || types.
|
|
630
|
+
if (ArrayBuffer.isView(V) || types.isArrayBuffer(V)) {
|
|
631
631
|
return webidl.converters.BufferSource(V)
|
|
632
632
|
}
|
|
633
633
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "undici",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.0",
|
|
4
4
|
"description": "An HTTP/1.1 client, written from scratch for Node.js",
|
|
5
5
|
"homepage": "https://undici.nodejs.org",
|
|
6
6
|
"bugs": {
|
|
@@ -75,16 +75,18 @@
|
|
|
75
75
|
"build:wasm": "node build/wasm.js --docker",
|
|
76
76
|
"lint": "standard | snazzy",
|
|
77
77
|
"lint:fix": "standard --fix | snazzy",
|
|
78
|
-
"test": "node scripts/generate-pem && npm run test:tap && npm run test:node-fetch && npm run test:fetch && npm run test:cookies && npm run test:wpt && npm run test:websocket && npm run test:jest && npm run test:typescript",
|
|
79
|
-
"test:cookies": "
|
|
80
|
-
"test:node-fetch": "
|
|
81
|
-
"test:fetch": "
|
|
82
|
-
"test:jest": "
|
|
83
|
-
"test:tap": "tap test/*.js
|
|
84
|
-
"test:
|
|
85
|
-
"test:
|
|
86
|
-
"test:
|
|
87
|
-
"test:
|
|
78
|
+
"test": "node scripts/generate-pem && npm run test:tap && npm run test:node-fetch && npm run test:fetch && npm run test:cookies && npm run test:wpt && npm run test:websocket && npm run test:jest && npm run test:typescript && npm run test:node-test",
|
|
79
|
+
"test:cookies": "borp --coverage -p \"test/cookie/*.js\"",
|
|
80
|
+
"test:node-fetch": "mocha --exit test/node-fetch",
|
|
81
|
+
"test:fetch": "npm run build:node && borp --expose-gc --coverage -p \"test/fetch/*.js\" && borp --coverage -p \"test/webidl/*.js\"",
|
|
82
|
+
"test:jest": "jest",
|
|
83
|
+
"test:tap": "tap test/*.js",
|
|
84
|
+
"test:node-test": "borp --coverage -p \"test/node-test/**/*.js\"",
|
|
85
|
+
"test:tdd": "tap test/*.js --coverage -w",
|
|
86
|
+
"test:tdd:node-test": "borp -p \"test/node-test/**/*.js\" -w",
|
|
87
|
+
"test:typescript": "tsd && tsc --skipLibCheck test/imports/undici-import.ts",
|
|
88
|
+
"test:websocket": "borp --coverage -p \"test/websocket/*.js\"",
|
|
89
|
+
"test:wpt": "node test/wpt/start-fetch.mjs && node test/wpt/start-FileAPI.mjs && node test/wpt/start-mimesniff.mjs && node test/wpt/start-xhr.mjs && node test/wpt/start-websockets.mjs && node test/wpt/start-cacheStorage.mjs",
|
|
88
90
|
"coverage": "nyc --reporter=text --reporter=html npm run test",
|
|
89
91
|
"coverage:ci": "nyc --reporter=lcov npm run test",
|
|
90
92
|
"bench": "PORT=3042 concurrently -k -s first npm:bench:server npm:bench:run",
|
|
@@ -96,10 +98,12 @@
|
|
|
96
98
|
"fuzz": "jsfuzz test/fuzzing/fuzz.js corpus"
|
|
97
99
|
},
|
|
98
100
|
"devDependencies": {
|
|
101
|
+
"@matteo.collina/tspl": "^0.1.1",
|
|
99
102
|
"@sinonjs/fake-timers": "^11.1.0",
|
|
100
103
|
"@types/node": "^18.0.3",
|
|
101
104
|
"abort-controller": "^3.0.0",
|
|
102
105
|
"atomic-sleep": "^1.0.0",
|
|
106
|
+
"borp": "^0.5.0",
|
|
103
107
|
"chai": "^4.3.4",
|
|
104
108
|
"chai-as-promised": "^7.1.1",
|
|
105
109
|
"chai-iterator": "^3.0.2",
|
|
@@ -129,7 +133,7 @@
|
|
|
129
133
|
"standard": "^17.0.0",
|
|
130
134
|
"table": "^6.8.0",
|
|
131
135
|
"tap": "^16.1.0",
|
|
132
|
-
"tsd": "^0.
|
|
136
|
+
"tsd": "^0.30.1",
|
|
133
137
|
"typescript": "^5.0.2",
|
|
134
138
|
"wait-on": "^7.0.1",
|
|
135
139
|
"ws": "^8.11.0"
|
package/types/balanced-pool.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { URL } from 'url'
|
|
|
4
4
|
|
|
5
5
|
export default BalancedPool
|
|
6
6
|
|
|
7
|
+
type BalancedPoolConnectOptions = Omit<Dispatcher.ConnectOptions, "origin">;
|
|
8
|
+
|
|
7
9
|
declare class BalancedPool extends Dispatcher {
|
|
8
10
|
constructor(url: string | string[] | URL | URL[], options?: Pool.Options);
|
|
9
11
|
|
|
@@ -15,4 +17,13 @@ declare class BalancedPool extends Dispatcher {
|
|
|
15
17
|
closed: boolean;
|
|
16
18
|
/** `true` after `pool.destroyed()` has been called or `pool.close()` has been called and the pool shutdown has completed. */
|
|
17
19
|
destroyed: boolean;
|
|
20
|
+
|
|
21
|
+
// Override dispatcher APIs.
|
|
22
|
+
override connect(
|
|
23
|
+
options: BalancedPoolConnectOptions
|
|
24
|
+
): Promise<Dispatcher.ConnectData>;
|
|
25
|
+
override connect(
|
|
26
|
+
options: BalancedPoolConnectOptions,
|
|
27
|
+
callback: (err: Error | null, data: Dispatcher.ConnectData) => void
|
|
28
|
+
): void;
|
|
18
29
|
}
|
package/types/client.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { TlsOptions } from 'tls'
|
|
|
3
3
|
import Dispatcher from './dispatcher'
|
|
4
4
|
import buildConnector from "./connector";
|
|
5
5
|
|
|
6
|
+
type ClientConnectOptions = Omit<Dispatcher.ConnectOptions, "origin">;
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* A basic HTTP/1.1 client, mapped on top a single TCP/TLS connection. Pipelining is disabled by default.
|
|
8
10
|
*/
|
|
@@ -14,6 +16,15 @@ export class Client extends Dispatcher {
|
|
|
14
16
|
closed: boolean;
|
|
15
17
|
/** `true` after `client.destroyed()` has been called or `client.close()` has been called and the client shutdown has completed. */
|
|
16
18
|
destroyed: boolean;
|
|
19
|
+
|
|
20
|
+
// Override dispatcher APIs.
|
|
21
|
+
override connect(
|
|
22
|
+
options: ClientConnectOptions
|
|
23
|
+
): Promise<Dispatcher.ConnectData>;
|
|
24
|
+
override connect(
|
|
25
|
+
options: ClientConnectOptions,
|
|
26
|
+
callback: (err: Error | null, data: Dispatcher.ConnectData) => void
|
|
27
|
+
): void;
|
|
17
28
|
}
|
|
18
29
|
|
|
19
30
|
export declare namespace Client {
|
package/types/dispatcher.d.ts
CHANGED
package/types/pool.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ import Dispatcher from "./dispatcher";
|
|
|
5
5
|
|
|
6
6
|
export default Pool
|
|
7
7
|
|
|
8
|
+
type PoolConnectOptions = Omit<Dispatcher.ConnectOptions, "origin">;
|
|
9
|
+
|
|
8
10
|
declare class Pool extends Dispatcher {
|
|
9
11
|
constructor(url: string | URL, options?: Pool.Options)
|
|
10
12
|
/** `true` after `pool.close()` has been called. */
|
|
@@ -13,6 +15,15 @@ declare class Pool extends Dispatcher {
|
|
|
13
15
|
destroyed: boolean;
|
|
14
16
|
/** Aggregate stats for a Pool. */
|
|
15
17
|
readonly stats: TPoolStats;
|
|
18
|
+
|
|
19
|
+
// Override dispatcher APIs.
|
|
20
|
+
override connect(
|
|
21
|
+
options: PoolConnectOptions
|
|
22
|
+
): Promise<Dispatcher.ConnectData>;
|
|
23
|
+
override connect(
|
|
24
|
+
options: PoolConnectOptions,
|
|
25
|
+
callback: (err: Error | null, data: Dispatcher.ConnectData) => void
|
|
26
|
+
): void;
|
|
16
27
|
}
|
|
17
28
|
|
|
18
29
|
declare namespace Pool {
|
package/types/proxy-agent.d.ts
CHANGED