Comprehensive event system for monitoring and customizing proxy behavior throughout the request/response lifecycle using http-proxy events.
Configure event handlers for various proxy lifecycle events through the options system.
/**
* Event handler configuration for proxy lifecycle events
*/
interface OnProxyEvent<TReq = http.IncomingMessage, TRes = http.ServerResponse> {
/** Error event handler for proxy errors */
error?: httpProxy.ErrorCallback<Error, TReq, TRes>;
/** Handler for outgoing proxy requests */
proxyReq?: httpProxy.ProxyReqCallback<http.ClientRequest, TReq, TRes>;
/** Handler for outgoing WebSocket proxy requests */
proxyReqWs?: httpProxy.ProxyReqWsCallback<http.ClientRequest, TReq>;
/** Handler for incoming proxy responses */
proxyRes?: httpProxy.ProxyResCallback<TReq, TRes>;
/** Handler for WebSocket connection open events */
open?: httpProxy.OpenCallback;
/** Handler for WebSocket connection close events */
close?: httpProxy.CloseCallback<TReq>;
/** Handler for proxy request start events */
start?: httpProxy.StartCallback<TReq, TRes>;
/** Handler for proxy request end events */
end?: httpProxy.EndCallback<TReq, TRes>;
/** Handler for connection reset errors */
econnreset?: httpProxy.EconnresetCallback<Error, TReq, TRes>;
}
interface EventOptions<TReq, TRes> {
/** Event handlers for proxy lifecycle events */
on?: OnProxyEvent<TReq, TRes>;
}Basic Event Handling:
import { createProxyMiddleware } from "http-proxy-middleware";
const proxy = createProxyMiddleware({
target: "http://api.example.com",
on: {
error: (err, req, res, target) => {
console.error("Proxy error:", err.message);
},
proxyReq: (proxyReq, req, res) => {
console.log("Sending request to:", proxyReq.path);
},
proxyRes: (proxyRes, req, res) => {
console.log("Received response:", proxyRes.statusCode);
},
},
});Handle proxy errors to provide custom error responses and logging.
/**
* Error event callback for handling proxy errors
* @param err - The error that occurred
* @param req - The original client request
* @param res - The response object to the client
* @param target - The target URL (may be undefined for WebSocket errors)
*/
type ErrorCallback<Error, TReq, TRes> = (
err: Error,
req: TReq,
res: TRes,
target?: string | Partial<URL>
) => void;Error Handling Examples:
// Comprehensive error handling
const errorHandlingProxy = createProxyMiddleware({
target: "http://api.example.com",
on: {
error: (err, req, res, target) => {
const errorInfo = {
timestamp: new Date().toISOString(),
error: err.message,
code: err.code,
url: req.url,
method: req.method,
target: target?.href || "unknown",
headers: req.headers,
};
// Log detailed error information
console.error("Proxy Error:", errorInfo);
// Send custom error response if headers not sent
if (!res.headersSent) {
const statusCode = getStatusCodeFromError(err);
res.writeHead(statusCode, {
"Content-Type": "application/json",
"X-Proxy-Error": "true",
"X-Error-Code": err.code || "UNKNOWN",
});
res.end(JSON.stringify({
error: {
message: "Proxy request failed",
code: statusCode,
details: err.message,
timestamp: errorInfo.timestamp,
requestId: req.headers["x-request-id"],
},
}));
}
// Report to monitoring system
reportError(errorInfo);
},
},
});
// Error mapping utility
function getStatusCodeFromError(err) {
switch (err.code) {
case "ENOTFOUND": return 502; // Bad Gateway
case "ECONNREFUSED": return 503; // Service Unavailable
case "ETIMEDOUT": return 504; // Gateway Timeout
case "ECONNRESET": return 502; // Bad Gateway
default: return 500; // Internal Server Error
}
}Monitor and modify outgoing proxy requests before they are sent to the target server.
/**
* Proxy request event callback for outgoing HTTP requests
* @param proxyReq - The outgoing request to the target server
* @param req - The original client request
* @param res - The response object to the client
*/
type ProxyReqCallback<TProxyReq, TReq, TRes> = (
proxyReq: TProxyReq,
req: TReq,
res: TRes
) => void;
/**
* Proxy request event callback for outgoing WebSocket requests
* @param proxyReq - The outgoing WebSocket request
* @param req - The original client request
*/
type ProxyReqWsCallback<TProxyReq, TReq> = (
proxyReq: TProxyReq,
req: TReq
) => void;Request Modification Examples:
// Request header manipulation
const requestModifyProxy = createProxyMiddleware({
target: "http://api.example.com",
on: {
proxyReq: (proxyReq, req, res) => {
// Add custom headers
proxyReq.setHeader("X-Forwarded-By", "my-proxy");
proxyReq.setHeader("X-Request-ID", generateRequestId());
proxyReq.setHeader("X-Client-IP", req.connection.remoteAddress);
// Add authentication
const apiKey = process.env.API_KEY;
if (apiKey) {
proxyReq.setHeader("Authorization", `Bearer ${apiKey}`);
}
// Modify user agent
proxyReq.setHeader("User-Agent", "MyApp/1.0 (Proxy)");
// Remove sensitive headers
proxyReq.removeHeader("cookie");
proxyReq.removeHeader("x-internal-token");
// Log request details
console.log(`β ${req.method} ${req.url} -> ${proxyReq.host}${proxyReq.path}`);
},
// WebSocket request handling
proxyReqWs: (proxyReq, req) => {
// Add WebSocket-specific headers
proxyReq.setHeader("X-WebSocket-Client", req.headers["user-agent"]);
proxyReq.setHeader("X-Forwarded-Proto", req.connection.encrypted ? "wss" : "ws");
console.log(`WebSocket connection: ${req.url}`);
},
},
});
// Request body modification (for POST/PUT requests)
const bodyModifyProxy = createProxyMiddleware({
target: "http://api.example.com",
on: {
proxyReq: (proxyReq, req, res) => {
// Handle request body modification for parsed bodies
if (req.body && (req.method === "POST" || req.method === "PUT")) {
// Modify the request body
const modifiedBody = {
...req.body,
timestamp: new Date().toISOString(),
proxy: {
version: "1.0",
source: "my-proxy",
},
};
const bodyData = JSON.stringify(modifiedBody);
// Update headers
proxyReq.setHeader("Content-Type", "application/json");
proxyReq.setHeader("Content-Length", Buffer.byteLength(bodyData));
// Write the modified body
proxyReq.write(bodyData);
}
},
},
});Monitor and process responses from the target server before they reach the client.
/**
* Proxy response event callback for incoming responses
* @param proxyRes - The response from the target server
* @param req - The original client request
* @param res - The response object to the client
*/
type ProxyResCallback<TReq, TRes> = (
proxyRes: http.IncomingMessage,
req: TReq,
res: TRes
) => void;Response Processing Examples:
// Response monitoring and header modification
const responseMonitorProxy = createProxyMiddleware({
target: "http://api.example.com",
on: {
proxyRes: (proxyRes, req, res) => {
// Log response details
const duration = Date.now() - req.startTime;
console.log(`β ${req.method} ${req.url} [${proxyRes.statusCode}] ${duration}ms`);
// Add custom response headers
res.setHeader("X-Proxy-Server", "my-proxy/1.0");
res.setHeader("X-Response-Time", `${duration}ms`);
res.setHeader("X-Upstream-Server", proxyRes.headers["server"] || "unknown");
// Security headers
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Frame-Options", "DENY");
// CORS headers (if needed)
if (req.headers.origin) {
res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
res.setHeader("Access-Control-Allow-Credentials", "true");
}
// Handle specific status codes
if (proxyRes.statusCode >= 400) {
console.warn(`Error response: ${proxyRes.statusCode} for ${req.url}`);
// Add error tracking header
res.setHeader("X-Error-Tracked", "true");
// Send error notification
notifyError({
statusCode: proxyRes.statusCode,
url: req.url,
method: req.method,
timestamp: new Date().toISOString(),
});
}
// Performance monitoring
if (duration > 5000) { // Slow response (> 5 seconds)
console.warn(`Slow response: ${duration}ms for ${req.url}`);
res.setHeader("X-Slow-Response", "true");
}
},
},
});
// Response caching and optimization
const responseCacheProxy = createProxyMiddleware({
target: "http://api.example.com",
on: {
proxyRes: (proxyRes, req, res) => {
const contentType = proxyRes.headers["content-type"];
const cacheControl = proxyRes.headers["cache-control"];
// Add caching headers for static content
if (contentType?.includes("application/json") && proxyRes.statusCode === 200) {
if (!cacheControl) {
res.setHeader("Cache-Control", "public, max-age=300"); // 5 minutes
}
// Add ETag for caching
const etag = generateETag(req.url, proxyRes.headers);
res.setHeader("ETag", etag);
}
// Compress responses if possible
const acceptEncoding = req.headers["accept-encoding"];
if (acceptEncoding?.includes("gzip") && shouldCompress(contentType)) {
res.setHeader("Content-Encoding", "gzip");
}
// Track response metrics
trackResponseMetrics({
url: req.url,
method: req.method,
statusCode: proxyRes.statusCode,
contentType,
contentLength: proxyRes.headers["content-length"],
duration: Date.now() - req.startTime,
});
},
},
});Handle WebSocket connection lifecycle events for real-time applications.
/**
* WebSocket open event callback
* @param proxySocket - The WebSocket connection to the target
*/
type OpenCallback = (proxySocket: net.Socket) => void;
/**
* WebSocket close event callback
* @param req - The original WebSocket upgrade request
* @param proxySocket - The WebSocket connection to the target
* @param proxyHead - The first packet of the tunneling stream
*/
type CloseCallback<TReq> = (
req: TReq,
proxySocket: net.Socket,
proxyHead: Buffer
) => void;WebSocket Event Examples:
// WebSocket connection monitoring
const wsMonitorProxy = createProxyMiddleware({
target: "ws://websocket.example.com",
ws: true,
on: {
open: (proxySocket) => {
const address = proxySocket.address();
console.log(`WebSocket connected to ${address.address}:${address.port}`);
// Track active connections
activeConnections.add(proxySocket);
// Set up connection monitoring
proxySocket.on("data", (data) => {
trackWebSocketData("outbound", data.length);
});
proxySocket.on("error", (error) => {
console.error("WebSocket proxy error:", error);
activeConnections.delete(proxySocket);
});
},
close: (req, proxySocket, proxyHead) => {
const address = proxySocket.address();
console.log(`WebSocket disconnected from ${address?.address}:${address?.port}`);
// Clean up connection tracking
activeConnections.delete(proxySocket);
// Log connection duration
const duration = Date.now() - req.connectionStartTime;
console.log(`WebSocket session duration: ${duration}ms`);
},
// Handle WebSocket proxy requests
proxyReqWs: (proxyReq, req) => {
req.connectionStartTime = Date.now();
console.log(`WebSocket upgrade request: ${req.url}`);
// Add custom WebSocket headers
proxyReq.setHeader("X-WebSocket-Proxy", "my-proxy/1.0");
// Authentication for WebSocket connections
const token = req.headers["sec-websocket-protocol"];
if (token) {
proxyReq.setHeader("Authorization", `Bearer ${token}`);
}
},
},
});Handle proxy request start and end events for comprehensive request tracking.
/**
* Proxy request start event callback
* @param req - The original client request
* @param res - The response object to the client
* @param target - The target URL
*/
type StartCallback<TReq, TRes> = (
req: TReq,
res: TRes,
target: string | Partial<URL>
) => void;
/**
* Proxy request end event callback
* @param req - The original client request
* @param res - The response object to the client
* @param proxyRes - The response from the target server
*/
type EndCallback<TReq, TRes> = (
req: TReq,
res: TRes,
proxyRes: http.IncomingMessage
) => void;Lifecycle Event Examples:
// Comprehensive request lifecycle tracking
const lifecycleTrackingProxy = createProxyMiddleware({
target: "http://api.example.com",
on: {
start: (req, res, target) => {
// Initialize request tracking
req.startTime = Date.now();
req.requestId = generateRequestId();
console.log(`π Starting request ${req.requestId}: ${req.method} ${req.url} -> ${target.href}`);
// Add request to active tracking
activeRequests.set(req.requestId, {
method: req.method,
url: req.url,
target: target.href,
startTime: req.startTime,
clientIP: req.connection.remoteAddress,
userAgent: req.headers["user-agent"],
});
},
end: (req, res, proxyRes) => {
const duration = Date.now() - req.startTime;
const statusCode = proxyRes.statusCode;
const contentLength = proxyRes.headers["content-length"];
console.log(`β
Completed request ${req.requestId}: [${statusCode}] ${duration}ms ${contentLength || 0} bytes`);
// Update request tracking
const requestData = activeRequests.get(req.requestId);
if (requestData) {
requestData.endTime = Date.now();
requestData.duration = duration;
requestData.statusCode = statusCode;
requestData.contentLength = contentLength;
// Move to completed requests log
completedRequests.push(requestData);
activeRequests.delete(req.requestId);
// Maintain completed requests history (keep last 1000)
if (completedRequests.length > 1000) {
completedRequests.shift();
}
}
// Performance alerting
if (duration > 10000) { // > 10 seconds
console.warn(`β οΈ Slow request detected: ${req.requestId} took ${duration}ms`);
alertSlowRequest(requestData);
}
if (statusCode >= 500) {
console.error(`β Server error: ${req.requestId} returned ${statusCode}`);
alertServerError(requestData);
}
},
econnreset: (err, req, res, target) => {
console.error(`π Connection reset: ${req.requestId || 'unknown'} - ${err.message}`);
// Clean up tracking
if (req.requestId) {
activeRequests.delete(req.requestId);
}
// Track connection reset events
connectionResets.push({
timestamp: new Date().toISOString(),
error: err.message,
url: req.url,
target: target?.href,
});
},
},
});