Comparing version 3.3.1 to 3.3.2
'use strict' | ||
const { InvalidArgumentError, InvalidReturnValueError } = require('./core/errors') | ||
const Pool = require('./pool') | ||
const Client = require('./core/client') | ||
const util = require('./core/util') | ||
@@ -22,3 +23,3 @@ const { kAgentOpts, kAgentCache } = require('./core/symbols') | ||
function onDisconnect () { | ||
if (this.connected === 0 && this.pending === 0) { | ||
if (this.connected === 0 && this.size === 0) { | ||
this.off('disconnect', onDisconnect) | ||
@@ -30,3 +31,5 @@ self[kAgentCache].delete(origin) | ||
if (!pool) { | ||
pool = new Pool(origin, self[kAgentOpts]) | ||
pool = self[kAgentOpts] && self[kAgentOpts].connections === 1 | ||
? new Client(origin, self[kAgentOpts]) | ||
: new Pool(origin, self[kAgentOpts]) | ||
pool.on('disconnect', onDisconnect) | ||
@@ -33,0 +36,0 @@ self[kAgentCache].set(origin, pool) |
@@ -196,3 +196,3 @@ 'use strict' | ||
!this[kSocket].destroyed | ||
) | ||
) ? 1 : 0 | ||
} | ||
@@ -745,3 +745,3 @@ | ||
client.emit('connect') | ||
client.emit('connect', client) | ||
resume(client) | ||
@@ -825,3 +825,3 @@ } | ||
client.emit('disconnect', err) | ||
client.emit('disconnect', client, err) | ||
} | ||
@@ -828,0 +828,0 @@ |
@@ -147,3 +147,4 @@ 'use strict' | ||
return this[kHandler].onError(err) | ||
// Ensure all queued handlers are invoked before calling onError. | ||
util.queueMicrotask(() => this[kHandler].onError(err)) | ||
} | ||
@@ -150,0 +151,0 @@ } |
@@ -169,3 +169,6 @@ 'use strict' | ||
bodyLength, | ||
isBuffer | ||
isBuffer, | ||
queueMicrotask: global.queueMicrotask || (cb => Promise.resolve() | ||
.then(cb) | ||
.catch(err => setTimeout(() => { throw err }, 0))) | ||
} |
@@ -62,24 +62,22 @@ 'use strict' | ||
if (queue.isEmpty()) { | ||
if (pool[kNeedDrain]) { | ||
pool[kNeedDrain] = false | ||
pool.emit('drain') | ||
} | ||
if (pool[kNeedDrain] && !this.busy) { | ||
pool[kNeedDrain] = false | ||
pool.emit('drain') | ||
} | ||
if (pool[kClosedResolve]) { | ||
Promise | ||
.all(pool[kClients].map(c => c.close())) | ||
.then(pool[kClosedResolve]) | ||
} | ||
if (pool[kClosedResolve] && queue.isEmpty()) { | ||
Promise | ||
.all(pool[kClients].map(c => c.close())) | ||
.then(pool[kClosedResolve]) | ||
} | ||
} | ||
this[kOnConnect] = function onConnect () { | ||
this[kOnConnect] = function onConnect (client) { | ||
pool[kConnected]++ | ||
pool.emit('connect', this) | ||
pool.emit('connect', client) | ||
} | ||
this[kOnDisconnect] = function onDisconnect (err) { | ||
this[kOnDisconnect] = function onDisconnect (client, err) { | ||
pool[kConnected]-- | ||
pool.emit('disconnect', this, err) | ||
pool.emit('disconnect', client, err) | ||
} | ||
@@ -97,13 +95,48 @@ } | ||
get busy () { | ||
return this[kPending] > 0 | ||
if (this[kPending] > 0) { | ||
return true | ||
} | ||
if (this[kConnections] && this[kClients].length === this[kConnections]) { | ||
for (const { busy } of this[kClients]) { | ||
if (!busy) { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
return false | ||
} | ||
get pending () { | ||
return this[kPending] | ||
let ret = this[kPending] | ||
for (const { pending } of this[kClients]) { | ||
ret += pending | ||
} | ||
return ret | ||
} | ||
// TODO: get running () {} | ||
get running () { | ||
let ret = 0 | ||
// TODO: get size () {} | ||
for (const { running } of this[kClients]) { | ||
ret += running | ||
} | ||
return ret | ||
} | ||
get size () { | ||
let ret = this[kPending] | ||
for (const { size } of this[kClients]) { | ||
ret += size | ||
} | ||
return ret | ||
} | ||
get destroyed () { | ||
@@ -146,2 +179,5 @@ return this[kDestroyed] | ||
client.dispatch(opts, handler) | ||
if (client.busy && this.busy) { | ||
this[kNeedDrain] = true | ||
} | ||
} | ||
@@ -148,0 +184,0 @@ } catch (err) { |
{ | ||
"name": "undici", | ||
"version": "3.3.1", | ||
"version": "3.3.2", | ||
"description": "An HTTP/1.1 client, written from scratch for Node.js", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -66,2 +66,4 @@ # undici | ||
Requests are not guaranteeed to be dispatched in order of invocation. | ||
`url` can be a string or a [`URL`](https://nodejs.org/api/url.html#url_class_url) object. | ||
@@ -456,2 +458,4 @@ It should only include the protocol, hostname, and port. | ||
Multiple handler methods may be invoked in the same tick. | ||
Options: | ||
@@ -532,5 +536,5 @@ | ||
#### `client.connected: Boolean` | ||
#### `client.connected: Number` | ||
True if the client has an active connection. The client will lazily | ||
Thruthy if the client has an active connection. The client will lazily | ||
create a connection when it receives a request and will destroy it | ||
@@ -559,7 +563,9 @@ if there is no activity for the duration of the `timeout` value. | ||
* `'connect'`, emitted when a socket has been created and | ||
connected. The client will connect once `client.size > 0`. | ||
connected. The first argument is the `Client` instance. | ||
The client will connect once `client.size > 0`. | ||
* `'disconnect'`, emitted when socket has disconnected. The | ||
first argument of the event is the error which caused the | ||
socket to disconnect. The client will reconnect if or once | ||
socket to disconnect. The second argument is the | ||
`Client` instance. The client will reconnect if or once | ||
`client.size > 0`. | ||
@@ -571,3 +577,6 @@ | ||
A pool of [`Client`][] connected to the same upstream target. | ||
Implements the same api as [`Client`][]. | ||
Requests are not guaranteeed to be dispatched in order of invocation. | ||
Options: | ||
@@ -579,74 +588,2 @@ | ||
`Pool` does not guarantee that requests are dispatched in | ||
order of invocation. | ||
#### `pool.url: URL` | ||
Returns url passed to `undici.Pool(url, opts)`. | ||
#### `pool.connected: Integer` | ||
Number of active connections in pool. | ||
#### `pool.pending: Number` | ||
Number of queued requests. | ||
#### `pool.busy: Boolean` | ||
True if pool is saturated or blocked. Indicates whether dispatching | ||
further requests is meaningful. | ||
#### `pool.closed: Boolean` | ||
True after `pool.close()` has been called. | ||
#### `pool.destroyed: Boolean` | ||
True after `pool.destroyed()` has been called or `pool.close()` has been | ||
called and the client shutdown has completed. | ||
#### `pool.request(opts[, callback]): Promise|Void` | ||
Calls [`client.request(opts, callback)`][request] on one of the clients. | ||
#### `pool.stream(opts, factory[, callback]): Promise|Void` | ||
Calls [`client.stream(opts, factory, callback)`][stream] on one of the clients. | ||
#### `pool.pipeline(opts, handler): Duplex` | ||
Calls [`client.pipeline(opts, handler)`][pipeline] on one of the clients. | ||
#### `pool.upgrade(opts[, callback]): Promise|Void` | ||
Calls [`client.upgrade(opts, callback)`][upgrade] on one of the clients. | ||
#### `pool.connect(opts[, callback]): Promise|Void` | ||
Calls [`client.connect(opts, callback)`][connect] on one of the clients. | ||
#### `pool.dispatch(opts, handler): Void` | ||
Calls [`client.dispatch(opts, handler)`][dispatch] on one of the clients. | ||
#### `pool.close([callback]): Promise|Void` | ||
Calls [`client.close(callback)`](#close) on all the clients. | ||
#### `pool.destroy([err][, callback]): Promise|Void` | ||
Calls [`client.destroy(err, callback)`](#destroy) on all the clients. | ||
#### Events | ||
* `'drain'`, emitted when pool is no longer fully | ||
saturated. | ||
* `'connect'`, emitted when a client has connected, the `Client` | ||
instance is passed as argument. | ||
* `'disconnect'`, emitted when a client has disconnected. The first argument is the | ||
`Client` instance, the second is the the error that caused the disconnection. | ||
<a name='agent'></a> | ||
@@ -653,0 +590,0 @@ ### `new undici.Agent(opts)` |
@@ -6,2 +6,3 @@ 'use strict' | ||
const http = require('http') | ||
const { PassThrough } = require('stream') | ||
@@ -601,1 +602,43 @@ test('dispatch invalid opts', (t) => { | ||
}) | ||
test('ensure promise callback runs before onError', t => { | ||
t.plan(2) | ||
const server = http.createServer((req, res) => { | ||
res.end() | ||
}) | ||
t.tearDown(server.close.bind(server)) | ||
server.listen(0, () => { | ||
const client = new Client(`http://localhost:${server.address().port}`) | ||
t.tearDown(client.close.bind(client)) | ||
const stream = new PassThrough() | ||
new Promise(resolve => client.dispatch({ | ||
path: '/', | ||
method: 'POST', | ||
body: Buffer.alloc(1e6) | ||
}, { | ||
onConnect () { | ||
}, | ||
onHeaders () { | ||
resolve() | ||
}, | ||
onData () { | ||
}, | ||
onComplete () { | ||
throw new Error() | ||
}, | ||
onError (err) { | ||
t.ok(err) | ||
stream.destroy(err) | ||
} | ||
})).then(() => { | ||
stream.on('error', (err) => { | ||
t.ok(err) | ||
}) | ||
}) | ||
}) | ||
}) |
@@ -41,3 +41,3 @@ 'use strict' | ||
t.ok(err instanceof errors.RequestAbortedError) | ||
t.strictEqual(signal.listenerCount('abort'), 0) | ||
t.strictEqual(signal.listenerCount('abort'), 1) | ||
}) | ||
@@ -44,0 +44,0 @@ t.strictEqual(signal.listenerCount('abort'), 2) |
@@ -962,1 +962,35 @@ 'use strict' | ||
}) | ||
test('connected', (t) => { | ||
t.plan(5) | ||
const server = createServer((req, res) => { | ||
req.pipe(res) | ||
}) | ||
t.tearDown(server.close.bind(server)) | ||
server.listen(0, () => { | ||
const client = new Client(`http://localhost:${server.address().port}`, { | ||
pipelining: 1 | ||
}) | ||
t.tearDown(client.close.bind(client)) | ||
client.on('connect', self => { | ||
t.strictEqual(client, self) | ||
}) | ||
client.on('disconnect', self => { | ||
t.strictEqual(client, self) | ||
}) | ||
t.strictEqual(client.connected, 0) | ||
client[kConnect](() => { | ||
client.request({ | ||
path: '/', | ||
method: 'GET' | ||
}, (err) => { | ||
t.error(err) | ||
}) | ||
t.strictEqual(client.connected, 1) | ||
}) | ||
}) | ||
}) |
@@ -259,3 +259,3 @@ 'use strict' | ||
client.on('disconnect', (err) => { | ||
client.on('disconnect', (client, err) => { | ||
t.strictEqual(err.code, 'UND_ERR_SOCKET') | ||
@@ -262,0 +262,0 @@ }) |
@@ -344,3 +344,3 @@ 'use strict' | ||
test('busy', (t) => { | ||
t.plan(8 * 8 + 2 + 1) | ||
t.plan(8 * 10 + 2 + 1) | ||
@@ -383,4 +383,6 @@ const server = createServer((req, res) => { | ||
}) | ||
t.strictEqual(client.pending, Math.max(n - 2, 0)) | ||
t.strictEqual(client.busy, n > 2) | ||
t.strictEqual(client.pending, n) | ||
t.strictEqual(client.busy, n >= 2) | ||
t.strictEqual(client.size, n) | ||
t.strictEqual(client.running, 0) | ||
} | ||
@@ -387,0 +389,0 @@ }) |
@@ -11,2 +11,7 @@ import { expectAssignable } from 'tsd' | ||
expectAssignable<errors.UndiciError>(new errors.BodyTimeoutError()) | ||
expectAssignable<errors.BodyTimeoutError>(new errors.BodyTimeoutError()) | ||
expectAssignable<'BodyTimeoutError'>(new errors.BodyTimeoutError().name) | ||
expectAssignable<'UND_ERR_BODY_TIMEOUT'>(new errors.BodyTimeoutError().code) | ||
expectAssignable<errors.UndiciError>(new errors.SocketTimeoutError()) | ||
@@ -13,0 +18,0 @@ expectAssignable<errors.SocketTimeoutError>(new errors.SocketTimeoutError()) |
@@ -12,2 +12,8 @@ export = Errors | ||
/** A body exceeds the `bodyTimeout` option. */ | ||
export class BodyTimeoutError extends UndiciError { | ||
name: 'BodyTimeoutError'; | ||
code: 'UND_ERR_BODY_TIMEOUT'; | ||
} | ||
/** A socket exceeds the `socketTimeout` option. */ | ||
@@ -14,0 +20,0 @@ export class SocketTimeoutError extends UndiciError { |
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
428227
13674
740