Turn a function into an `http.Agent` instance
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Agent 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.