Skip to content

Commit aac97c2

Browse files
nodejs-github-botdanielleadams
authored andcommitted
deps: update undici to 5.8.0
PR-URL: #43886 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com>
1 parent acfc33c commit aac97c2

File tree

8 files changed

+163
-15
lines changed

8 files changed

+163
-15
lines changed

deps/undici/src/docs/best-practices/proxy.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ import { createServer } from 'http'
2020
import proxy from 'proxy'
2121

2222
const server = await buildServer()
23-
const proxy = await buildProxy()
23+
const proxyServer = await buildProxy()
2424

2525
const serverUrl = `http://localhost:${server.address().port}`
26-
const proxyUrl = `http://localhost:${proxy.address().port}`
26+
const proxyUrl = `http://localhost:${proxyServer.address().port}`
2727

2828
server.on('request', (req, res) => {
2929
console.log(req.url) // '/hello?foo=bar'
@@ -47,7 +47,7 @@ console.log(response.statusCode) // 200
4747
console.log(JSON.parse(data)) // { hello: 'world' }
4848

4949
server.close()
50-
proxy.close()
50+
proxyServer.close()
5151
client.close()
5252

5353
function buildServer () {
@@ -73,12 +73,12 @@ import { createServer } from 'http'
7373
import proxy from 'proxy'
7474

7575
const server = await buildServer()
76-
const proxy = await buildProxy()
76+
const proxyServer = await buildProxy()
7777

7878
const serverUrl = `http://localhost:${server.address().port}`
79-
const proxyUrl = `http://localhost:${proxy.address().port}`
79+
const proxyUrl = `http://localhost:${proxyServer.address().port}`
8080

81-
proxy.authenticate = function (req, fn) {
81+
proxyServer.authenticate = function (req, fn) {
8282
fn(null, req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:pass').toString('base64')}`)
8383
}
8484

@@ -107,7 +107,7 @@ console.log(response.statusCode) // 200
107107
console.log(JSON.parse(data)) // { hello: 'world' }
108108

109109
server.close()
110-
proxy.close()
110+
proxyServer.close()
111111
client.close()
112112

113113
function buildServer () {
@@ -124,3 +124,4 @@ function buildProxy () {
124124
})
125125
}
126126
```
127+

deps/undici/src/lib/balanced-pool.js

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ const { parseOrigin } = require('./core/util')
1818
const kFactory = Symbol('factory')
1919

2020
const kOptions = Symbol('options')
21+
const kGreatestCommonDivisor = Symbol('kGreatestCommonDivisor')
22+
const kCurrentWeight = Symbol('kCurrentWeight')
23+
const kIndex = Symbol('kIndex')
24+
const kWeight = Symbol('kWeight')
25+
const kMaxWeightPerServer = Symbol('kMaxWeightPerServer')
26+
const kErrorPenalty = Symbol('kErrorPenalty')
27+
28+
function getGreatestCommonDivisor (a, b) {
29+
if (b === 0) return a
30+
return getGreatestCommonDivisor(b, a % b)
31+
}
2132

2233
function defaultFactory (origin, opts) {
2334
return new Pool(origin, opts)
@@ -28,6 +39,11 @@ class BalancedPool extends PoolBase {
2839
super()
2940

3041
this[kOptions] = opts
42+
this[kIndex] = -1
43+
this[kCurrentWeight] = 0
44+
45+
this[kMaxWeightPerServer] = this[kOptions].maxWeightPerServer || 100
46+
this[kErrorPenalty] = this[kOptions].errorPenalty || 15
3147

3248
if (!Array.isArray(upstreams)) {
3349
upstreams = [upstreams]
@@ -42,6 +58,7 @@ class BalancedPool extends PoolBase {
4258
for (const upstream of upstreams) {
4359
this.addUpstream(upstream)
4460
}
61+
this._updateBalancedPoolStats()
4562
}
4663

4764
addUpstream (upstream) {
@@ -54,12 +71,40 @@ class BalancedPool extends PoolBase {
5471
))) {
5572
return this
5673
}
74+
const pool = this[kFactory](upstreamOrigin, Object.assign({}, this[kOptions]))
75+
76+
this[kAddClient](pool)
77+
pool.on('connect', () => {
78+
pool[kWeight] = Math.min(this[kMaxWeightPerServer], pool[kWeight] + this[kErrorPenalty])
79+
})
80+
81+
pool.on('connectionError', () => {
82+
pool[kWeight] = Math.max(1, pool[kWeight] - this[kErrorPenalty])
83+
this._updateBalancedPoolStats()
84+
})
85+
86+
pool.on('disconnect', (...args) => {
87+
const err = args[2]
88+
if (err && err.code === 'UND_ERR_SOCKET') {
89+
// decrease the weight of the pool.
90+
pool[kWeight] = Math.max(1, pool[kWeight] - this[kErrorPenalty])
91+
this._updateBalancedPoolStats()
92+
}
93+
})
94+
95+
for (const client of this[kClients]) {
96+
client[kWeight] = this[kMaxWeightPerServer]
97+
}
5798

58-
this[kAddClient](this[kFactory](upstreamOrigin, Object.assign({}, this[kOptions])))
99+
this._updateBalancedPoolStats()
59100

60101
return this
61102
}
62103

104+
_updateBalancedPoolStats () {
105+
this[kGreatestCommonDivisor] = this[kClients].map(p => p[kWeight]).reduce(getGreatestCommonDivisor, 0)
106+
}
107+
63108
removeUpstream (upstream) {
64109
const upstreamOrigin = parseOrigin(upstream).origin
65110

@@ -100,10 +145,42 @@ class BalancedPool extends PoolBase {
100145
return
101146
}
102147

103-
this[kClients].splice(this[kClients].indexOf(dispatcher), 1)
104-
this[kClients].push(dispatcher)
148+
const allClientsBusy = this[kClients].map(pool => pool[kNeedDrain]).reduce((a, b) => a && b, true)
149+
150+
if (allClientsBusy) {
151+
return
152+
}
153+
154+
let counter = 0
155+
156+
let maxWeightIndex = this[kClients].findIndex(pool => !pool[kNeedDrain])
157+
158+
while (counter++ < this[kClients].length) {
159+
this[kIndex] = (this[kIndex] + 1) % this[kClients].length
160+
const pool = this[kClients][this[kIndex]]
161+
162+
// find pool index with the largest weight
163+
if (pool[kWeight] > this[kClients][maxWeightIndex][kWeight] && !pool[kNeedDrain]) {
164+
maxWeightIndex = this[kIndex]
165+
}
166+
167+
// decrease the current weight every `this[kClients].length`.
168+
if (this[kIndex] === 0) {
169+
// Set the current weight to the next lower weight.
170+
this[kCurrentWeight] = this[kCurrentWeight] - this[kGreatestCommonDivisor]
171+
172+
if (this[kCurrentWeight] <= 0) {
173+
this[kCurrentWeight] = this[kMaxWeightPerServer]
174+
}
175+
}
176+
if (pool[kWeight] >= this[kCurrentWeight] && (!pool[kNeedDrain])) {
177+
return pool
178+
}
179+
}
105180

106-
return dispatcher
181+
this[kCurrentWeight] = this[kClients][maxWeightIndex][kWeight]
182+
this[kIndex] = maxWeightIndex
183+
return this[kClients][maxWeightIndex]
107184
}
108185
}
109186

deps/undici/src/lib/core/request.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,27 @@ const {
77
const assert = require('assert')
88
const util = require('./util')
99

10+
// tokenRegExp and headerCharRegex have been lifted from
11+
// https://github.com/nodejs/node/blob/main/lib/_http_common.js
12+
13+
/**
14+
* Verifies that the given val is a valid HTTP token
15+
* per the rules defined in RFC 7230
16+
* See https://tools.ietf.org/html/rfc7230#section-3.2.6
17+
*/
18+
const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/
19+
20+
/**
21+
* Matches if val contains an invalid field-vchar
22+
* field-value = *( field-content / obs-fold )
23+
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
24+
* field-vchar = VCHAR / obs-text
25+
*/
26+
const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/
27+
28+
// Verifies that a given path is valid does not contain control chars \x00 to \x20
29+
const invalidPathRegex = /[^\u0021-\u00ff]/
30+
1031
const kHandler = Symbol('handler')
1132

1233
const channels = {}
@@ -54,10 +75,14 @@ class Request {
5475
method !== 'CONNECT'
5576
) {
5677
throw new InvalidArgumentError('path must be an absolute URL or start with a slash')
78+
} else if (invalidPathRegex.exec(path) !== null) {
79+
throw new InvalidArgumentError('invalid request path')
5780
}
5881

5982
if (typeof method !== 'string') {
6083
throw new InvalidArgumentError('method must be a string')
84+
} else if (tokenRegExp.exec(method) === null) {
85+
throw new InvalidArgumentError('invalid request method')
6186
}
6287

6388
if (upgrade && typeof upgrade !== 'string') {
@@ -301,6 +326,10 @@ function processHeader (request, key, val) {
301326
key.toLowerCase() === 'expect'
302327
) {
303328
throw new NotSupportedError('expect header not supported')
329+
} else if (tokenRegExp.exec(key) === null) {
330+
throw new InvalidArgumentError('invalid header key')
331+
} else if (headerCharRegex.exec(val) !== null) {
332+
throw new InvalidArgumentError(`invalid ${key} header`)
304333
} else {
305334
request.headers += `${key}: ${val}\r\n`
306335
}

deps/undici/src/lib/fetch/body.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,10 @@ function bodyMixinMethods (instance) {
291291
const chunks = []
292292

293293
for await (const chunk of consumeBody(this[kState].body)) {
294+
if (!isUint8Array(chunk)) {
295+
throw new TypeError('Expected Uint8Array chunk')
296+
}
297+
294298
// Assemble one final large blob with Uint8Array's can exhaust memory.
295299
// That's why we create create multiple blob's and using references
296300
chunks.push(new Blob([chunk]))
@@ -314,6 +318,10 @@ function bodyMixinMethods (instance) {
314318
let offset = 0
315319

316320
for await (const chunk of consumeBody(this[kState].body)) {
321+
if (!isUint8Array(chunk)) {
322+
throw new TypeError('Expected Uint8Array chunk')
323+
}
324+
317325
buffer.set(chunk, offset)
318326
offset += chunk.length
319327
}
@@ -331,6 +339,10 @@ function bodyMixinMethods (instance) {
331339
let size = 0
332340

333341
for await (const chunk of consumeBody(this[kState].body)) {
342+
if (!isUint8Array(chunk)) {
343+
throw new TypeError('Expected Uint8Array chunk')
344+
}
345+
334346
chunks.push(chunk)
335347
size += chunk.byteLength
336348
}
@@ -355,6 +367,10 @@ function bodyMixinMethods (instance) {
355367
const textDecoder = new TextDecoder()
356368

357369
for await (const chunk of consumeBody(this[kState].body)) {
370+
if (!isUint8Array(chunk)) {
371+
throw new TypeError('Expected Uint8Array chunk')
372+
}
373+
358374
result += textDecoder.decode(chunk, { stream: true })
359375
}
360376

deps/undici/src/lib/handler/redirect.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ function shouldRemoveHeader (header, removeContent, unknownOrigin) {
186186
return (
187187
(header.length === 4 && header.toString().toLowerCase() === 'host') ||
188188
(removeContent && header.toString().toLowerCase().indexOf('content-') === 0) ||
189-
(unknownOrigin && header.length === 13 && header.toString().toLowerCase() === 'authorization')
189+
(unknownOrigin && header.length === 13 && header.toString().toLowerCase() === 'authorization') ||
190+
(unknownOrigin && header.length === 6 && header.toString().toLowerCase() === 'cookie')
190191
)
191192
}
192193

deps/undici/src/lib/mock/mock-utils.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const {
88
kOrigin,
99
kGetNetConnect
1010
} = require('./mock-symbols')
11-
const { buildURL } = require('../core/util')
11+
const { buildURL, nop } = require('../core/util')
1212

1313
function matchValue (match, value) {
1414
if (typeof match === 'string') {
@@ -288,6 +288,7 @@ function mockDispatch (opts, handler) {
288288
const responseHeaders = generateKeyValues(headers)
289289
const responseTrailers = generateKeyValues(trailers)
290290

291+
handler.abort = nop
291292
handler.onHeaders(statusCode, responseHeaders, resume, getStatusText(statusCode))
292293
handler.onData(Buffer.from(responseData))
293294
handler.onComplete(responseTrailers)

deps/undici/src/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "undici",
3-
"version": "5.7.0",
3+
"version": "5.8.0",
44
"description": "An HTTP/1.1 client, written from scratch for Node.js",
55
"homepage": "https://undici.nodejs.org",
66
"bugs": {

deps/undici/undici.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2227,6 +2227,9 @@ Content-Type: ${value.type || "application/octet-stream"}\r
22272227
}
22282228
const chunks = [];
22292229
for await (const chunk of consumeBody(this[kState].body)) {
2230+
if (!isUint8Array(chunk)) {
2231+
throw new TypeError("Expected Uint8Array chunk");
2232+
}
22302233
chunks.push(new Blob([chunk]));
22312234
}
22322235
return new Blob(chunks, { type: this.headers.get("Content-Type") || "" });
@@ -2241,6 +2244,9 @@ Content-Type: ${value.type || "application/octet-stream"}\r
22412244
const buffer2 = new Uint8Array(contentLength);
22422245
let offset2 = 0;
22432246
for await (const chunk of consumeBody(this[kState].body)) {
2247+
if (!isUint8Array(chunk)) {
2248+
throw new TypeError("Expected Uint8Array chunk");
2249+
}
22442250
buffer2.set(chunk, offset2);
22452251
offset2 += chunk.length;
22462252
}
@@ -2249,6 +2255,9 @@ Content-Type: ${value.type || "application/octet-stream"}\r
22492255
const chunks = [];
22502256
let size = 0;
22512257
for await (const chunk of consumeBody(this[kState].body)) {
2258+
if (!isUint8Array(chunk)) {
2259+
throw new TypeError("Expected Uint8Array chunk");
2260+
}
22522261
chunks.push(chunk);
22532262
size += chunk.byteLength;
22542263
}
@@ -2267,6 +2276,9 @@ Content-Type: ${value.type || "application/octet-stream"}\r
22672276
let result = "";
22682277
const textDecoder = new TextDecoder();
22692278
for await (const chunk of consumeBody(this[kState].body)) {
2279+
if (!isUint8Array(chunk)) {
2280+
throw new TypeError("Expected Uint8Array chunk");
2281+
}
22702282
result += textDecoder.decode(chunk, { stream: true });
22712283
}
22722284
result += textDecoder.decode();
@@ -2350,6 +2362,9 @@ var require_request = __commonJS({
23502362
} = require_errors();
23512363
var assert = require("assert");
23522364
var util = require_util();
2365+
var tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/;
2366+
var headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
2367+
var invalidPathRegex = /[^\u0021-\u00ff]/;
23532368
var kHandler = Symbol("handler");
23542369
var channels = {};
23552370
var extractBody;
@@ -2388,9 +2403,13 @@ var require_request = __commonJS({
23882403
throw new InvalidArgumentError("path must be a string");
23892404
} else if (path[0] !== "/" && !(path.startsWith("http://") || path.startsWith("https://")) && method !== "CONNECT") {
23902405
throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
2406+
} else if (invalidPathRegex.exec(path) !== null) {
2407+
throw new InvalidArgumentError("invalid request path");
23912408
}
23922409
if (typeof method !== "string") {
23932410
throw new InvalidArgumentError("method must be a string");
2411+
} else if (tokenRegExp.exec(method) === null) {
2412+
throw new InvalidArgumentError("invalid request method");
23942413
}
23952414
if (upgrade && typeof upgrade !== "string") {
23962415
throw new InvalidArgumentError("upgrade must be a string");
@@ -2562,6 +2581,10 @@ var require_request = __commonJS({
25622581
throw new InvalidArgumentError("invalid upgrade header");
25632582
} else if (key.length === 6 && key.toLowerCase() === "expect") {
25642583
throw new NotSupportedError("expect header not supported");
2584+
} else if (tokenRegExp.exec(key) === null) {
2585+
throw new InvalidArgumentError("invalid header key");
2586+
} else if (headerCharRegex.exec(val) !== null) {
2587+
throw new InvalidArgumentError(`invalid ${key} header`);
25652588
} else {
25662589
request.headers += `${key}: ${val}\r
25672590
`;
@@ -2685,7 +2708,7 @@ var require_redirect = __commonJS({
26852708
}
26862709
}
26872710
function shouldRemoveHeader(header, removeContent, unknownOrigin) {
2688-
return header.length === 4 && header.toString().toLowerCase() === "host" || removeContent && header.toString().toLowerCase().indexOf("content-") === 0 || unknownOrigin && header.length === 13 && header.toString().toLowerCase() === "authorization";
2711+
return header.length === 4 && header.toString().toLowerCase() === "host" || removeContent && header.toString().toLowerCase().indexOf("content-") === 0 || unknownOrigin && header.length === 13 && header.toString().toLowerCase() === "authorization" || unknownOrigin && header.length === 6 && header.toString().toLowerCase() === "cookie";
26892712
}
26902713
function cleanRequestHeaders(headers, removeContent, unknownOrigin) {
26912714
const ret = [];

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy