Turn a function into an `http.Agent` instance
npx @tessl/cli install tessl/npm-agent-base@7.1.0Agent Base provides an abstract base class for creating custom HTTP agents in Node.js applications. It extends the native http.Agent class with a flexible architecture that allows developers to implement custom connection logic through a connect() method that can return arbitrary Duplex streams or delegate to other agents.
npm install agent-baseimport { Agent, AgentConnectOpts } from "agent-base";For helper functions:
import { toBuffer, json, req, ThenableRequest } from "agent-base";For CommonJS:
const { Agent, toBuffer, json, req } = require("agent-base");Node.js built-in types used in the API:
import type { Duplex } from 'stream';
import * as http from 'http';
import * as https from 'https';
import * as net from 'net';
import * as tls from 'tls';import * as net from 'net';
import * as tls from 'tls';
import * as http from 'http';
import { Agent, AgentConnectOpts } from 'agent-base';
class MyAgent extends Agent {
connect(req: http.ClientRequest, opts: AgentConnectOpts) {
// secureEndpoint is true when using the "https" module
if (opts.secureEndpoint) {
return tls.connect(opts);
} else {
return net.connect(opts);
}
}
}
// Keep alive enabled means that connect() will only be
// invoked when a new connection needs to be created
const agent = new MyAgent({ keepAlive: true });
// Pass the agent option when creating the HTTP request
http.get('http://nodejs.org/api/', { agent }, (res) => {
console.log(res.headers);
res.pipe(process.stdout);
});Agent Base is built around several key components:
http.Agent that must be subclassedconnect() method for implementing custom connection logicAbstract base class for creating custom HTTP agents with flexible connection handling.
/**
* Abstract base class for creating custom HTTP agents.
* Must be extended and implement the connect() method.
*/
abstract class Agent extends http.Agent {
/**
* Create a new Agent instance
* @param opts - Standard http.Agent options including keepAlive, timeout, etc.
*/
constructor(opts?: http.AgentOptions);
/**
* Abstract method that must be implemented by subclasses.
* Called when a new connection needs to be established.
* @param req - The HTTP client request being made
* @param options - Connection options including secureEndpoint flag
* @returns Promise or direct return of Duplex stream or another Agent
*/
abstract connect(
req: http.ClientRequest,
options: AgentConnectOpts
): Promise<Duplex | http.Agent> | Duplex | http.Agent;
/**
* Determine whether this is an HTTP or HTTPS request
* @param options - Connection options to check
* @returns true if HTTPS, false if HTTP
*/
isSecureEndpoint(options?: AgentConnectOpts): boolean;
/**
* Get connection name for socket pooling (internal Node.js usage)
* @param options - Connection options
* @returns Connection name string for pooling
*/
getName(options?: AgentConnectOpts): string;
/**
* Create a socket connection (internal Node.js Agent method)
* @param req - The HTTP client request
* @param options - Connection options
* @param cb - Callback function for connection result
*/
createSocket(
req: http.ClientRequest,
options: AgentConnectOpts,
cb: (err: Error | null, s?: Duplex) => void
): void;
/**
* Create connection from current socket (internal Node.js Agent method)
* @returns Current socket for the connection
* @throws Error if no socket was returned from connect()
*/
createConnection(): Duplex;
/**
* Default port for connections (getter/setter)
* Defaults to 443 for HTTPS, 80 for HTTP
*/
defaultPort: number;
/**
* Protocol string (getter/setter)
* Defaults to 'https:' for secure endpoints, 'http:' otherwise
*/
protocol: string;
/**
* Connection options from http.Agent (inherited properties)
*/
options: Partial<net.TcpNetConnectOpts & tls.ConnectionOptions>;
/**
* Keep-alive setting (inherited from http.Agent)
*/
keepAlive: boolean;
}Type definitions for connection options passed to the connect() method.
/**
* Union type for connection options passed to connect() method.
* This is a union of HTTP and HTTPS connection options with a secureEndpoint discriminator.
*/
type AgentConnectOpts = {
/** Always false for HTTP connections */
secureEndpoint: false;
/** Protocol string, typically 'http:' */
protocol?: string;
} & net.TcpNetConnectOpts | {
/** Always true for HTTPS connections */
secureEndpoint: true;
/** Protocol string, typically 'https:' */
protocol?: string;
/** Port number for the connection */
port: number;
} & tls.ConnectionOptions;Helper functions for processing HTTP response streams.
/**
* Convert a readable stream to a Buffer
* @param stream - Readable stream to convert
* @returns Promise resolving to Buffer containing all stream data
*/
async function toBuffer(stream: Readable): Promise<Buffer>;
/**
* Parse JSON from a readable stream
* @param stream - Readable stream containing JSON data
* @returns Promise resolving to parsed JSON object
* @throws Enhanced error with input context on parse failure
*/
async function json(stream: Readable): Promise<any>;Utility function for making HTTP/HTTPS requests with Promise interface.
/**
* Create an HTTP/HTTPS request with Promise interface
* @param url - Request URL as string or URL object
* @param opts - Standard https.RequestOptions
* @returns Request object with then() method for Promise-like usage
*/
function req(
url: string | URL,
opts?: https.RequestOptions
): ThenableRequest;
/**
* HTTP request with Promise-like interface
*/
type ThenableRequest = http.ClientRequest & {
/** Promise-like then method for handling responses */
then: Promise<http.IncomingMessage>['then'];
};import { Agent, AgentConnectOpts } from 'agent-base';
import * as net from 'net';
class ProxyAgent extends Agent {
private proxyHost: string;
private proxyPort: number;
constructor(proxyHost: string, proxyPort: number, opts?: http.AgentOptions) {
super(opts);
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
}
async connect(req: http.ClientRequest, opts: AgentConnectOpts) {
// Connect to proxy server
const socket = net.connect({
host: this.proxyHost,
port: this.proxyPort
});
// Send CONNECT request through proxy
const target = `${opts.host}:${opts.port}`;
socket.write(`CONNECT ${target} HTTP/1.1\r\nHost: ${target}\r\n\r\n`);
return socket;
}
}import { Agent, AgentConnectOpts } from 'agent-base';
import * as net from 'net';
class DelayedAgent extends Agent {
private delay: number;
constructor(delay: number, opts?: http.AgentOptions) {
super(opts);
this.delay = delay;
}
async connect(req: http.ClientRequest, opts: AgentConnectOpts) {
// Wait for specified delay before connecting
await new Promise(resolve => setTimeout(resolve, this.delay));
if (opts.secureEndpoint) {
return tls.connect(opts);
} else {
return net.connect(opts);
}
}
}import { req, json, toBuffer } from 'agent-base';
// Make request and parse JSON response
const response = await req('https://api.example.com/data');
const data = await json(response);
console.log(data);
// Get response as buffer
const response2 = await req('https://api.example.com/file');
const buffer = await toBuffer(response2);
console.log(`Downloaded ${buffer.length} bytes`);connect() method can throw errors that are automatically caught and passed to the request callbackjson() function enhances parse errors with input context for better debuggingconnect() doesn't return a socket, a specific error is thrown: "No socket was returned in the connect() function"Agent Base uses multiple mechanisms to detect whether a request is HTTP or HTTPS:
secureEndpoint property: When set explicitly in optionsoptions.protocol === 'https:'This enables the same agent to work with both http and https modules automatically.