Skip to content

Commit e8d0504

Browse files
author
vhess
committed
Add documentation for undici code path
1 parent 769957b commit e8d0504

File tree

1 file changed

+222
-4
lines changed

1 file changed

+222
-4
lines changed

README.md

Lines changed: 222 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Contributors:
3030
August 21, 2025 STATUS compared to [http-proxy](https://www.npmjs.com/package/http-proxy) and [httpxy](https://www.npmjs.com/package/httpxy):
3131

3232
- Library entirely rewritten in Typescript in a modern style, with many typings added internally and strict mode enabled.
33+
- **HTTP/2 Support**: Full HTTP/2 support via [undici](https://github.com/nodejs/undici) with callback-based request/response lifecycle hooks.
3334
- All dependent packages updated to latest versions, addressing all security vulnerabilities according to `pnpm audit`.
3435
- Code rewritten to not use deprecated/insecure API's, e.g., using `URL` instead of `parse`.
3536
- Fixed socket leaks in the Websocket proxy code, going beyond [http-proxy-node16](https://www.npmjs.com/package/http-proxy-node16) to also instrument and logging socket counts. Also fixed an issue with uncatchable errors when using websockets.
@@ -89,7 +90,9 @@ This is the original user's guide, but with various updates.
8990
- [Setup a stand-alone proxy server with latency](#setup-a-stand-alone-proxy-server-with-latency)
9091
- [Using HTTPS](#using-https)
9192
- [Proxying WebSockets](#proxying-websockets)
93+
- [HTTP/2 Support with Undici](#http2-support-with-undici)
9294
- [Options](#options)
95+
- [Configuration Compatibility](#configuration-compatibility)
9396
- [Listening for proxy events](#listening-for-proxy-events)
9497
- [Shutdown](#shutdown)
9598
- [Miscellaneous](#miscellaneous)
@@ -115,6 +118,10 @@ import { createProxyServer } from "http-proxy-3";
115118
const proxy = createProxyServer(options); // See below
116119
```
117120

121+
http-proxy-3 supports two request processing paths:
122+
- **Native Path**: Uses Node.js native `http`/`https` modules (default)
123+
- **Undici Path**: Uses [undici](https://github.com/nodejs/undici) for HTTP/2 support (when `undici` option is provided)
124+
118125
Unless listen(..) is invoked on the object, this does not create a webserver. See below.
119126

120127
An object is returned with four methods:
@@ -218,6 +225,8 @@ server.listen(5050);
218225
This example shows how you can proxy a request using your own HTTP server that
219226
modifies the outgoing proxy request by adding a special header.
220227

228+
##### Using Traditional Events (Native HTTP/HTTPS)
229+
221230
```js
222231
import * as http from "node:http";
223232
import { createProxyServer } from "http-proxy-3";
@@ -248,6 +257,38 @@ console.log("listening on port 5050");
248257
server.listen(5050);
249258
```
250259

260+
##### Using Callbacks (Undici HTTP/2)
261+
262+
```js
263+
import * as http from "node:http";
264+
import { createProxyServer } from "http-proxy-3";
265+
266+
// Create a proxy server with undici and HTTP/2 support
267+
const proxy = createProxyServer({
268+
target: "https://127.0.0.1:5050",
269+
undici: {
270+
agentOptions: { allowH2: true },
271+
// Modify the request before it's sent
272+
onBeforeRequest: async (requestOptions, req, res, options) => {
273+
requestOptions.headers['X-Special-Proxy-Header'] = 'foobar';
274+
requestOptions.headers['X-HTTP2-Enabled'] = 'true';
275+
},
276+
// Access the response after it's received
277+
onAfterResponse: async (response, req, res, options) => {
278+
console.log(`Proxied ${req.url} -> ${response.statusCode}`);
279+
}
280+
}
281+
});
282+
283+
const server = http.createServer((req, res) => {
284+
// The headers are modified via the onBeforeRequest callback
285+
proxy.web(req, res);
286+
});
287+
288+
console.log("listening on port 5050");
289+
server.listen(5050);
290+
```
291+
251292
**[Back to top](#table-of-contents)**
252293

253294
#### Modify a response from a proxied server
@@ -398,6 +439,103 @@ proxyServer.listen(8015);
398439

399440
**[Back to top](#table-of-contents)**
400441

442+
#### HTTP/2 Support with Undici
443+
444+
http-proxy-3 supports HTTP/2 through [undici](https://github.com/nodejs/undici), a modern HTTP client. When undici is enabled, the proxy can communicate with HTTP/2 servers and provides enhanced performance and features.
445+
446+
##### Basic HTTP/2 Setup
447+
448+
```js
449+
import { createProxyServer } from "http-proxy-3";
450+
import { Agent, setGlobalDispatcher } from "undici";
451+
452+
// Enable HTTP/2 for all fetch operations
453+
setGlobalDispatcher(new Agent({ allowH2: true }));
454+
455+
// Create a proxy with HTTP/2 support
456+
const proxy = createProxyServer({
457+
target: "https://http2-server.example.com",
458+
undici: {
459+
agentOptions: { allowH2: true }
460+
}
461+
});
462+
```
463+
464+
##### Simple HTTP/2 Enablement
465+
466+
```js
467+
// Shorthand to enable undici with defaults
468+
const proxy = createProxyServer({
469+
target: "https://http2-server.example.com",
470+
undici: true // Uses default configuration
471+
});
472+
```
473+
474+
##### Advanced Configuration with Callbacks
475+
476+
```js
477+
const proxy = createProxyServer({
478+
target: "https://api.example.com",
479+
undici: {
480+
// Undici agent configuration
481+
agentOptions: {
482+
allowH2: true,
483+
connect: {
484+
rejectUnauthorized: false, // For self-signed certs
485+
timeout: 10000
486+
}
487+
},
488+
// Undici request options
489+
requestOptions: {
490+
headersTimeout: 30000,
491+
bodyTimeout: 60000
492+
},
493+
// Called before making the undici request
494+
onBeforeRequest: async (requestOptions, req, res, options) => {
495+
// Modify outgoing request
496+
requestOptions.headers['X-API-Key'] = 'your-api-key';
497+
requestOptions.headers['X-Request-ID'] = Math.random().toString(36);
498+
},
499+
// Called after receiving the undici response
500+
onAfterResponse: async (response, req, res, options) => {
501+
// Access full response object
502+
console.log(`Status: ${response.statusCode}`);
503+
console.log('Headers:', response.headers);
504+
// Note: response.body is a stream, not the actual body content
505+
}
506+
}
507+
});
508+
```
509+
510+
##### HTTP/2 with HTTPS Proxy
511+
512+
```js
513+
import { readFileSync } from "node:fs";
514+
515+
const proxy = createProxyServer({
516+
target: "https://http2-target.example.com",
517+
ssl: {
518+
key: readFileSync("server-key.pem"),
519+
cert: readFileSync("server-cert.pem")
520+
},
521+
undici: {
522+
agentOptions: {
523+
allowH2: true,
524+
connect: { rejectUnauthorized: false }
525+
}
526+
},
527+
secure: false // Skip SSL verification for self-signed certs
528+
}).listen(8443);
529+
```
530+
531+
**Important Notes:**
532+
- When `undici` option is provided, the proxy uses undici's HTTP client instead of Node.js native `http`/`https` modules
533+
- undici automatically handles HTTP/2 negotiation when `allowH2: true` is set
534+
- The `onBeforeRequest` and `onAfterResponse` callbacks are only available in the undici code path
535+
- Traditional `proxyReq` and `proxyRes` events are not emitted in the undici path - use the callbacks instead
536+
537+
**[Back to top](#table-of-contents)**
538+
401539
### Options
402540

403541
`httpProxy.createProxyServer` supports the following options:
@@ -491,6 +629,14 @@ proxyServer.listen(8015);
491629
};
492630
```
493631
632+
- **ca**: Optionally override the trusted CA certificates. This is passed to https.request.
633+
634+
- **undici**: Enable undici for HTTP/2 support. Set to `true` for defaults, or provide custom configuration:
635+
- `agentOptions`: Configuration for undici Agent (see [undici Agent.Options](https://github.com/nodejs/undici/blob/main/docs/api/Agent.md))
636+
- `requestOptions`: Configuration for undici requests (see [undici Dispatcher.RequestOptions](https://github.com/nodejs/undici/blob/main/docs/api/Dispatcher.md#dispatcherrequestoptions))
637+
- `onBeforeRequest`: Async callback called before making the undici request
638+
- `onAfterResponse`: Async callback called after receiving the undici response
639+
494640
**NOTE:**
495641
`options.ws` and `options.ssl` are optional.
496642
`options.target` and `options.forward` cannot both be missing
@@ -502,6 +648,51 @@ If you are using the `proxyServer.listen` method, the following options are also
502648
503649
**[Back to top](#table-of-contents)**
504650
651+
### Configuration Compatibility
652+
653+
The following table shows which configuration options are compatible with different code paths:
654+
655+
| Option | Native HTTP/HTTPS | Undici HTTP/2 | Notes |
656+
|--------|-------------------|---------------|--------|
657+
| `target` | ✅ | ✅ | Core option, works in both paths |
658+
| `forward` | ✅ | ✅ | Core option, works in both paths |
659+
| `agent` | ✅ | ❌ | Native agents only, use `undici.agentOptions` instead |
660+
| `ssl` | ✅ | ✅ | HTTPS server configuration |
661+
| `ws` | ✅ | ❌ | WebSocket proxying uses native path only |
662+
| `xfwd` | ✅ | ✅ | X-Forwarded headers |
663+
| `secure` | ✅ | ✅ | SSL certificate verification |
664+
| `toProxy` | ✅ | ✅ | Proxy-to-proxy configuration |
665+
| `prependPath` | ✅ | ✅ | Path manipulation |
666+
| `ignorePath` | ✅ | ✅ | Path manipulation |
667+
| `localAddress` | ✅ | ✅ | Local interface binding |
668+
| `changeOrigin` | ✅ | ✅ | Host header rewriting |
669+
| `preserveHeaderKeyCase` | ✅ | ✅ | Header case preservation |
670+
| `auth` | ✅ | ✅ | Basic authentication |
671+
| `hostRewrite` | ✅ | ✅ | Redirect hostname rewriting |
672+
| `autoRewrite` | ✅ | ✅ | Automatic redirect rewriting |
673+
| `protocolRewrite` | ✅ | ✅ | Protocol rewriting on redirects |
674+
| `cookieDomainRewrite` | ✅ | ✅ | Cookie domain rewriting |
675+
| `cookiePathRewrite` | ✅ | ✅ | Cookie path rewriting |
676+
| `headers` | ✅ | ✅ | Extra headers to add |
677+
| `proxyTimeout` | ✅ | ✅ | Outgoing request timeout |
678+
| `timeout` | ✅ | ✅ | Incoming request timeout |
679+
| `followRedirects` | ✅ | ✅ | Redirect following |
680+
| `selfHandleResponse` | ✅ | ✅ | Manual response handling |
681+
| `buffer` | ✅ | ✅ | Request body stream |
682+
| `method` | ✅ | ✅ | HTTP method override |
683+
| `ca` | ✅ | ✅ | Custom CA certificates |
684+
| `undici` | ❌ | ✅ | Undici-specific configuration |
685+
686+
**Code Path Selection:**
687+
- **Native Path**: Used by default, supports HTTP/1.1 and WebSockets
688+
- **Undici Path**: Activated when `undici` option is provided, supports HTTP/2
689+
690+
**Event Compatibility:**
691+
- **Native Path**: Emits traditional events (`proxyReq`, `proxyRes`, `proxyReqWs`)
692+
- **Undici Path**: Uses callback functions (`onBeforeRequest`, `onAfterResponse`) instead of events
693+
694+
**[Back to top](#table-of-contents)**
695+
505696
### Listening for proxy events
506697
507698
- `error`: The error event is emitted if the request to the target fail. **We do not do any error handling of messages passed between client and proxy, and messages passed between proxy and target, so it is recommended that you listen on errors and handle them.**
@@ -512,11 +703,13 @@ If you are using the `proxyServer.listen` method, the following options are also
512703
- `close`: This event is emitted once the proxy websocket was closed.
513704
- (DEPRECATED) `proxySocket`: Deprecated in favor of `open`.
514705
706+
**Note**: When using the undici code path (HTTP/2), the `proxyReq` and `proxyRes` events are **not** emitted. Instead, use the `onBeforeRequest` and `onAfterResponse` callback functions in the `undici` configuration.
707+
708+
#### Traditional Events (Native HTTP/HTTPS path)
709+
515710
```js
516711
import { createProxyServer } from "http-proxy-3";
517-
// Error example
518-
//
519-
// Http Proxy Server with bad target
712+
520713
const proxy = createProxyServer({
521714
target: "http://localhost:9005",
522715
});
@@ -528,7 +721,6 @@ proxy.on("error", (err, req, res) => {
528721
res.writeHead(500, {
529722
"Content-Type": "text/plain",
530723
});
531-
532724
res.end("Something went wrong. And we are reporting a custom error message.");
533725
});
534726
@@ -545,6 +737,32 @@ proxy.on("open", (proxySocket) => {
545737
// listen for messages coming FROM the target here
546738
proxySocket.on("data", hybiParseAndLogMessage);
547739
});
740+
```
741+
742+
#### Callback Functions (Undici/HTTP2 path)
743+
744+
```js
745+
import { createProxyServer } from "http-proxy-3";
746+
747+
const proxy = createProxyServer({
748+
target: "https://api.example.com",
749+
undici: {
750+
agentOptions: { allowH2: true },
751+
// Called before making the undici request
752+
onBeforeRequest: async (requestOptions, req, res, options) => {
753+
// Modify the outgoing request
754+
requestOptions.headers['X-Custom-Header'] = 'added-by-callback';
755+
console.log('Making request to:', requestOptions.origin + requestOptions.path);
756+
},
757+
// Called after receiving the undici response
758+
onAfterResponse: async (response, req, res, options) => {
759+
// Access the full response object
760+
console.log(`Response: ${response.statusCode}`, response.headers);
761+
// Note: response.body is a stream that will be piped to res automatically
762+
}
763+
}
764+
});
765+
```
548766

549767
// Listen for the `close` event on `proxy`.
550768
proxy.on("close", (res, socket, head) => {

0 commit comments

Comments
 (0)