Comparing version 2.0.1 to 2.0.2
@@ -159,6 +159,6 @@ 'use strict' | ||
this[kResuming] = 0 // 0, idle, 1, scheduled, 2 resuming | ||
this[kNeedDrain] = false | ||
this[kNeedDrain] = 0 // 0, idle, 1, scheduled, 2 resuming | ||
this[kResume] = () => { | ||
if (this[kResuming] === 0) { | ||
resume(this) | ||
resume(this, true) | ||
} | ||
@@ -189,3 +189,3 @@ } | ||
this[kPipelining] = value | ||
resume(this) | ||
resume(this, true) | ||
} | ||
@@ -254,3 +254,3 @@ | ||
} else { | ||
resume(this) | ||
resume(this, true) | ||
} | ||
@@ -634,8 +634,4 @@ } catch (err) { | ||
!client.running && | ||
err.code !== 'ECONNRESET' && | ||
err.code !== 'ECONNREFUSED' && | ||
err.code !== 'EHOSTUNREACH' && | ||
err.code !== 'EHOSTDOWN' && | ||
err.code !== 'UND_ERR_SOCKET' && | ||
err.code !== 'UND_ERR_INFO' | ||
err.code !== 'UND_ERR_INFO' && | ||
err.code !== 'UND_ERR_SOCKET' | ||
) { | ||
@@ -768,3 +764,8 @@ assert(client[kPendingIdx] === client[kRunningIdx]) | ||
function resume (client) { | ||
function emitDrain (client) { | ||
client[kNeedDrain] = 0 | ||
client.emit('drain') | ||
} | ||
function resume (client, sync) { | ||
if (client[kResuming] === 2) { | ||
@@ -775,3 +776,3 @@ return | ||
client[kResuming] = 2 | ||
_resume(client) | ||
_resume(client, sync) | ||
client[kResuming] = 0 | ||
@@ -786,3 +787,3 @@ | ||
function _resume (client) { | ||
function _resume (client, sync) { | ||
while (true) { | ||
@@ -818,5 +819,9 @@ if (client[kDestroyed]) { | ||
if (!client.pending) { | ||
if (client[kNeedDrain] && !client.busy) { | ||
client[kNeedDrain] = false | ||
client.emit('drain') | ||
if (client[kNeedDrain] === 2 && !client.busy) { | ||
if (sync) { | ||
client[kNeedDrain] = 1 | ||
process.nextTick(emitDrain, client) | ||
} else { | ||
emitDrain(client) | ||
} | ||
continue | ||
@@ -826,3 +831,3 @@ } | ||
} else { | ||
client[kNeedDrain] = true | ||
client[kNeedDrain] = 2 | ||
} | ||
@@ -829,0 +834,0 @@ |
@@ -72,55 +72,15 @@ 'use strict' | ||
if (headers) { | ||
if (Array.isArray(headers)) { | ||
if (headers.length % 2 !== 0) { | ||
throw new InvalidArgumentError('headers array must be even') | ||
} | ||
for (let i = 0; i < headers.length; i += 2) { | ||
processHeader(this, headers[i], headers[i + 1]) | ||
} | ||
} else if (headers && typeof headers === 'object') { | ||
for (const [key, val] of Object.entries(headers)) { | ||
if (typeof val === 'object') { | ||
throw new InvalidArgumentError(`invalid ${key} header`) | ||
} else if (val === undefined) { | ||
continue | ||
} | ||
if ( | ||
this.host === null && | ||
key.length === 4 && | ||
key.toLowerCase() === 'host' | ||
) { | ||
this.host = val | ||
this.headers += `${key}: ${val}\r\n` | ||
} else if ( | ||
this.contentLength === null && | ||
key.length === 14 && | ||
key.toLowerCase() === 'content-length' | ||
) { | ||
this.contentLength = parseInt(val) | ||
if (!Number.isFinite(this.contentLength)) { | ||
throw new InvalidArgumentError('invalid content-length header') | ||
} | ||
} else if ( | ||
key.length === 17 && | ||
key.toLowerCase() === 'transfer-encoding' | ||
) { | ||
throw new InvalidArgumentError('invalid transfer-encoding header') | ||
} else if ( | ||
key.length === 10 && | ||
key.toLowerCase() === 'connection' | ||
) { | ||
throw new InvalidArgumentError('invalid connection header') | ||
} else if ( | ||
key.length === 10 && | ||
key.toLowerCase() === 'keep-alive' | ||
) { | ||
throw new InvalidArgumentError('invalid keep-alive header') | ||
} else if ( | ||
key.length === 7 && | ||
key.toLowerCase() === 'upgrade' | ||
) { | ||
throw new InvalidArgumentError('invalid upgrade header') | ||
} else if ( | ||
key.length === 6 && | ||
key.toLowerCase() === 'expect' | ||
) { | ||
throw new NotSupportedError('expect header not supported') | ||
} else { | ||
this.headers += `${key}: ${val}\r\n` | ||
} | ||
processHeader(this, key, val) | ||
} | ||
} else if (headers != null) { | ||
throw new InvalidArgumentError('headers must be an object or an array') | ||
} | ||
@@ -198,2 +158,55 @@ | ||
function processHeader (request, key, val) { | ||
if (typeof val === 'object') { | ||
throw new InvalidArgumentError(`invalid ${key} header`) | ||
} else if (val === undefined) { | ||
return | ||
} | ||
if ( | ||
request.host === null && | ||
key.length === 4 && | ||
key.toLowerCase() === 'host' | ||
) { | ||
request.host = val | ||
request.headers += `${key}: ${val}\r\n` | ||
} else if ( | ||
request.contentLength === null && | ||
key.length === 14 && | ||
key.toLowerCase() === 'content-length' | ||
) { | ||
request.contentLength = parseInt(val) | ||
if (!Number.isFinite(request.contentLength)) { | ||
throw new InvalidArgumentError('invalid content-length header') | ||
} | ||
} else if ( | ||
key.length === 17 && | ||
key.toLowerCase() === 'transfer-encoding' | ||
) { | ||
throw new InvalidArgumentError('invalid transfer-encoding header') | ||
} else if ( | ||
key.length === 10 && | ||
key.toLowerCase() === 'connection' | ||
) { | ||
throw new InvalidArgumentError('invalid connection header') | ||
} else if ( | ||
key.length === 10 && | ||
key.toLowerCase() === 'keep-alive' | ||
) { | ||
throw new InvalidArgumentError('invalid keep-alive header') | ||
} else if ( | ||
key.length === 7 && | ||
key.toLowerCase() === 'upgrade' | ||
) { | ||
throw new InvalidArgumentError('invalid upgrade header') | ||
} else if ( | ||
key.length === 6 && | ||
key.toLowerCase() === 'expect' | ||
) { | ||
throw new NotSupportedError('expect header not supported') | ||
} else { | ||
request.headers += `${key}: ${val}\r\n` | ||
} | ||
} | ||
function clearRequestTimeout (request) { | ||
@@ -200,0 +213,0 @@ const { [kTimeout]: timeout } = request |
{ | ||
"name": "undici", | ||
"version": "2.0.1", | ||
"version": "2.0.2", | ||
"description": "An HTTP/1.1 client, written from scratch for Node.js", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -22,11 +22,11 @@ # undici | ||
Machine: 2.8GHz AMD EPYC 7402P<br/> | ||
Machine: AMD EPYC 7502P<br/> | ||
Node 14 | ||
``` | ||
http - keepalive x 6,770 ops/sec ±7.70% (75 runs sampled) | ||
undici - pipeline x 10,627 ops/sec ±5.88% (79 runs sampled) | ||
undici - request x 10,902 ops/sec ±1.28% (85 runs sampled) | ||
undici - stream x 20,144 ops/sec ±0.84% (86 runs sampled) | ||
undici - dispatch x 20,295 ops/sec ±1.00% (83 runs sampled) | ||
http - keepalive x 7,179 ops/sec ±2.32% (272 runs sampled) | ||
undici - pipeline x 16,843 ops/sec ±0.98% (279 runs sampled) | ||
undici - request x 18,738 ops/sec ±0.67% (276 runs sampled) | ||
undici - stream x 21,215 ops/sec ±0.66% (278 runs sampled) | ||
undici - dispatch x 23,540 ops/sec ±0.62% (278 runs sampled) | ||
``` | ||
@@ -36,7 +36,7 @@ | ||
``` | ||
http - keepalive x 10,337 ops/sec ±6.17% (71 runs sampled) | ||
undici - pipeline x 30,387 ops/sec ±1.37% (80 runs sampled) | ||
undici - request x 40,117 ops/sec ±3.25% (77 runs sampled) | ||
undici - stream x 40,543 ops/sec ±1.30% (80 runs sampled) | ||
undici - dispatch x 50,434 ops/sec ±2.08% (77 runs sampled) | ||
http - keepalive x 12,028 ops/sec ±2.60% (265 runs sampled) | ||
undici - pipeline x 31,321 ops/sec ±0.77% (276 runs sampled) | ||
undici - request x 36,612 ops/sec ±0.71% (277 runs sampled) | ||
undici - stream x 41,291 ops/sec ±0.90% (268 runs sampled) | ||
undici - dispatch x 47,319 ops/sec ±1.17% (263 runs sampled) | ||
``` | ||
@@ -114,3 +114,3 @@ | ||
Default: `null`. | ||
* `headers: Object|Null`, an object with header-value pairs. | ||
* `headers: Object|Array|Null`, an object with header-value pairs or an array with header-value pairs bi-indexed (`['header1', 'value1', 'header2', 'value2']`). | ||
Default: `null`. | ||
@@ -139,2 +139,15 @@ * `signal: AbortController|EventEmitter|Null` | ||
``` | ||
Or an array like this: | ||
```js | ||
[ | ||
'content-length', '123', | ||
'content-type', 'text/plain', | ||
'connection', 'keep-alive', | ||
'host', 'mysite.com', | ||
'accept', '*/*' | ||
] | ||
``` | ||
Keys are lowercased. Values are not modified. | ||
@@ -141,0 +154,0 @@ If you don't specify a `host` header, it will be derived from the `url` of the client instance. |
@@ -930,1 +930,13 @@ 'use strict' | ||
}) | ||
test('socket errors', t => { | ||
t.plan(2) | ||
const client = new Client('http://localhost:5554') | ||
t.tearDown(client.destroy.bind(client)) | ||
client.request({ path: '/', method: 'GET' }, (err, data) => { | ||
t.ok(err) | ||
t.is('ECONNREFUSED', err.code) | ||
t.end() | ||
}) | ||
}) |
@@ -9,4 +9,5 @@ 'use strict' | ||
test('multiple reconnect', (t) => { | ||
t.plan(3) | ||
t.plan(5) | ||
let n = 0 | ||
const clock = FakeTimers.install() | ||
@@ -16,28 +17,33 @@ t.teardown(clock.uninstall.bind(clock)) | ||
const server = createServer((req, res) => { | ||
res.end() | ||
n === 0 ? res.destroy() : res.end('ok') | ||
}) | ||
t.tearDown(server.close.bind(server)) | ||
const client = new Client('http://localhost:5555') | ||
t.tearDown(client.destroy.bind(client)) | ||
server.listen(0, () => { | ||
const client = new Client(`http://localhost:${server.address().port}`) | ||
t.tearDown(client.destroy.bind(client)) | ||
client.request({ path: '/', method: 'GET' }, (err, data) => { | ||
t.error(err) | ||
data.body | ||
.resume() | ||
.on('end', () => { | ||
client.request({ path: '/', method: 'GET' }, (err, data) => { | ||
t.ok(err) | ||
t.is(err.code, 'UND_ERR_SOCKET') | ||
}) | ||
client.request({ path: '/', method: 'GET' }, (err, data) => { | ||
t.error(err) | ||
data.body | ||
.resume() | ||
.on('end', () => { | ||
t.pass() | ||
}) | ||
}) | ||
client.on('disconnect', () => { | ||
if (++n === 1) { | ||
t.pass() | ||
} | ||
process.nextTick(() => { | ||
clock.tick(1000) | ||
}) | ||
}) | ||
let n = 0 | ||
client.on('disconnect', () => { | ||
if (++n === 1) { | ||
t.pass() | ||
server.listen(5555) | ||
} | ||
process.nextTick(() => { | ||
clock.tick(1000) | ||
}) | ||
}) | ||
}) |
@@ -7,3 +7,3 @@ 'use strict' | ||
test('invalid headers', (t) => { | ||
t.plan(10) | ||
t.plan(11) | ||
@@ -25,2 +25,10 @@ const client = new Client('http://localhost:3000') | ||
method: 'GET', | ||
headers: 1 | ||
}, (err, data) => { | ||
t.ok(err instanceof errors.InvalidArgumentError) | ||
}) | ||
client.request({ | ||
path: '/', | ||
method: 'GET', | ||
headers: { | ||
@@ -27,0 +35,0 @@ 'transfer-encoding': 'chunked' |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
341412
67
11109
673
48