-
-
Notifications
You must be signed in to change notification settings - Fork 654
Description
I am faced with such a problem that I need to close the connection and the client itself according to the condition
How do I get a connection (Client) before it becomes available and the next request goes through it?
My Agent keeps a pool of connections through different proxy servers, and I need to close clients that don't suit me for any reason.
How can I do this without completely rewriting the clients and agent? It turns out that the response has no access to either the client or the connection.
Maybe make an additional implementation with acquire/release clients or proxy wrappers over clients of an existing agent.
Add low-level API for capturing and managing sessions/clients in Dispatcher
Problem Description
Currently, undici
does not provide a convenient way to:
- Capture an individual HTTP client (or “session”) from the connection pool (
Dispatcher
) to perform a series of requests. - Close or release specific connections based on response outcome (e.g., on a 403), without affecting the rest of the pool.
- Fine-tune the lifecycle of connections: controlling counts, re-opening, and manual shutdown.
The existing Agent
/Dispatcher
API only offers a global pool (connections
), but lacks something like a session()
or acquire()
method to carve out its own sub-pool of connections that you can explicitly close or return.
Expected Behavior
-
Method
dispatcher.session(options?: { connections?: number; }): Dispatcher & SessionControls
- Returns a
Dispatcher
-compatible object (a Session) that:- Implements the same interface as
Dispatcher
(so you can pass it anywhere aDispatcher
is expected). - Owns its own subset of TCP connections (optionally limited by
connections
). - Exposes additional methods:
close(): Promise<void>
— force-close all connections in this session.release(): Promise<void>
— gracefully return all open connections back to the parent pool.
- Implements the same interface as
- Returns a
-
Customizable at creation: allow passing TLS settings, timeouts, proxy options, etc.
-
Backward compatibility: if
session()
isn’t called,Dispatcher
continues working as before.
Usage Example
import { buildHttpProxyConnector } from '@undicijs/proxy';
import { Agent, request, type Dispatcher } from 'undici';
const connector = buildHttpProxyConnector('http://0.0.0.0:8001/', {
requestTls: { rejectUnauthorized: false },
});
const dispatcher = new Agent({
connect: (opts, cb) => connector(opts, cb),
connections: 50,
});
async function callApiWithSession(dispatcher: Dispatcher) {
// Acquire a session (which is itself a Dispatcher) with two dedicated connections
const session = dispatcher.session({ connections: 2 });
try {
const resp = await session.request('https://api.ipify.org');
if (resp.statusCode === 403) {
// Force-close all connections in this session
await session.close();
return null;
}
return await resp.body.text();
} finally {
// On success or after errors (if not closed), return connections to the pool
await session.release();
}
}
async function main() {
const ip = await callApiWithSession(dispatcher);
console.log(ip);
}
main();
Motivation
-
Error handling per-session.
On a 403 or 5xx error, you can tear down only the affected connections instead of the entireAgent
. -
Batching requests.
Some workflows need to group multiple requests under a controlled max-connection session. -
Flexible optimization.
Create multiple sessions with different configs (TLS, timeouts, proxies) without instantiating entirely newAgent
objects.
Proposed API Sketch
interface Dispatcher {
/**
* Create a new session that uses a subset of this Dispatcher’s pool.
* The returned object implements the Dispatcher interface.
*/
session(options?: {
connections?: number;
// ...other config
}): Dispatcher & SessionControls;
}
/** Extra control methods on a Session */
interface SessionControls {
/** Force-close all TCP connections in this session. */
close(): Promise<void>;
/** Gracefully return open connections to the parent pool. */
release(): Promise<void>;
}
Alternatives & Current Workarounds
- New
Agent(...)
per use case — heavyweight, as each Agent creates its own pool. - Internal hacks — manually closing
Client
sockets viaresp.body.socket.close()
orutil.hijack()
, which is unstable and unsupported.
An official low-level session API will simplify consumer code, make behavior predictable, and eliminate these fragile workarounds. Please consider adding this in the next major release!