Skip to content

Commit 9346c28

Browse files
author
vhess
committed
chore: Remove all mentions of undici vs fetch, where appropriate. Update readme
1 parent 2bd026b commit 9346c28

File tree

8 files changed

+1041
-833
lines changed

8 files changed

+1041
-833
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,4 @@ jobs:
4444
- run: pnpm build
4545
- name: Test native HTTP code path
4646
run: pnpm test-native
47-
- name: Test undici HTTP/2 code path
48-
run: pnpm test-undici
47+

README.md

Lines changed: 88 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +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.
33+
- **HTTP/2 Support**: Full HTTP/2 support via fetch API with callback-based request/response lifecycle hooks.
3434
- All dependent packages updated to latest versions, addressing all security vulnerabilities according to `pnpm audit`.
3535
- Code rewritten to not use deprecated/insecure API's, e.g., using `URL` instead of `parse`.
3636
- 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.
@@ -90,7 +90,7 @@ This is the original user's guide, but with various updates.
9090
- [Setup a stand-alone proxy server with latency](#setup-a-stand-alone-proxy-server-with-latency)
9191
- [Using HTTPS](#using-https)
9292
- [Proxying WebSockets](#proxying-websockets)
93-
- [HTTP/2 Support with Undici](#http2-support-with-undici)
93+
- [HTTP/2 Support with Fetch](#http2-support-with-fetch)
9494
- [Options](#options)
9595
- [Configuration Compatibility](#configuration-compatibility)
9696
- [Listening for proxy events](#listening-for-proxy-events)
@@ -120,7 +120,7 @@ const proxy = createProxyServer(options); // See below
120120

121121
http-proxy-3 supports two request processing paths:
122122
- **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)
123+
- **Fetch Path**: Uses fetch API for HTTP/2 support (when `fetch` option is provided)
124124

125125
Unless listen(..) is invoked on the object, this does not create a webserver. See below.
126126

@@ -257,25 +257,26 @@ console.log("listening on port 5050");
257257
server.listen(5050);
258258
```
259259

260-
##### Using Callbacks (Undici HTTP/2)
260+
##### Using Callbacks (Fetch/HTTP/2)
261261

262262
```js
263263
import * as http from "node:http";
264264
import { createProxyServer } from "http-proxy-3";
265+
import { Agent } from "undici";
265266

266-
// Create a proxy server with undici and HTTP/2 support
267+
// Create a proxy server with fetch and HTTP/2 support
267268
const proxy = createProxyServer({
268269
target: "https://127.0.0.1:5050",
269-
undici: {
270-
agentOptions: { allowH2: true },
270+
fetch: {
271+
dispatcher: new Agent({ allowH2: true }),
271272
// Modify the request before it's sent
272273
onBeforeRequest: async (requestOptions, req, res, options) => {
273274
requestOptions.headers['X-Special-Proxy-Header'] = 'foobar';
274275
requestOptions.headers['X-HTTP2-Enabled'] = 'true';
275276
},
276277
// Access the response after it's received
277278
onAfterResponse: async (response, req, res, options) => {
278-
console.log(`Proxied ${req.url} -> ${response.statusCode}`);
279+
console.log(`Proxied ${req.url} -> ${response.status}`);
279280
}
280281
}
281282
});
@@ -439,37 +440,38 @@ proxyServer.listen(8015);
439440

440441
**[Back to top](#table-of-contents)**
441442

442-
#### HTTP/2 Support with Undici
443+
#### HTTP/2 Support with Fetch
443444

444-
> **⚠️ Experimental Feature**: The undici code path for HTTP/2 support is currently experimental. While it provides full HTTP/2 functionality and has comprehensive test coverage, the API and behavior may change in future versions. Use with caution in production environments.
445+
> **⚠️ Experimental Feature**: The fetch code path for HTTP/2 support is currently experimental. While it provides HTTP/2 functionality and has comprehensive test coverage, the API and behavior may change in future versions. Use with caution in production environments.
446+
447+
http-proxy-3 supports HTTP/2 through the native fetch API. When fetch is enabled, the proxy can communicate with HTTP/2 servers. The fetch code path is runtime-agnostic and works across different JavaScript runtimes (Node.js, Deno, Bun, etc.). However, this means HTTP/2 support depends on the runtime. Deno enables HTTP/2 by default, Bun currently does not and Node.js requires to set a different dispatcher. See next section for Node.js details.
445448

446-
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.
447449

448450
##### Basic HTTP/2 Setup
449451

450452
```js
451453
import { createProxyServer } from "http-proxy-3";
452-
import { Agent, setGlobalDispatcher } from "undici";
454+
import { Agent } from "undici";
453455

454-
// Enable HTTP/2 for all fetch operations
456+
// Either enable HTTP/2 for all fetch operations
455457
setGlobalDispatcher(new Agent({ allowH2: true }));
456458

457-
// Create a proxy with HTTP/2 support
459+
// Or create a proxy with HTTP/2 support using fetch
458460
const proxy = createProxyServer({
459461
target: "https://http2-server.example.com",
460-
undici: {
461-
agentOptions: { allowH2: true }
462+
fetch: {
463+
dispatcher: new Agent({ allowH2: true })
462464
}
463465
});
464466
```
465467

466-
##### Simple HTTP/2 Enablement
468+
##### Simple Fetch Enablement
467469

468470
```js
469-
// Shorthand to enable undici with defaults
471+
// Shorthand to enable fetch with defaults
470472
const proxy = createProxyServer({
471473
target: "https://http2-server.example.com",
472-
undici: true // Uses default configuration
474+
fetch: true // Uses default fetch configuration
473475
});
474476
```
475477

@@ -478,32 +480,32 @@ const proxy = createProxyServer({
478480
```js
479481
const proxy = createProxyServer({
480482
target: "https://api.example.com",
481-
undici: {
482-
// Undici agent configuration
483-
agentOptions: {
483+
fetch: {
484+
// Use undici's Agent for HTTP/2 support
485+
dispatcher: new Agent({
484486
allowH2: true,
485487
connect: {
486488
rejectUnauthorized: false, // For self-signed certs
487489
timeout: 10000
488490
}
489-
},
490-
// Undici request options
491+
}),
492+
// Additional fetch request options
491493
requestOptions: {
492494
headersTimeout: 30000,
493495
bodyTimeout: 60000
494496
},
495-
// Called before making the undici request
497+
// Called before making the fetch request
496498
onBeforeRequest: async (requestOptions, req, res, options) => {
497499
// Modify outgoing request
498500
requestOptions.headers['X-API-Key'] = 'your-api-key';
499501
requestOptions.headers['X-Request-ID'] = Math.random().toString(36);
500502
},
501-
// Called after receiving the undici response
503+
// Called after receiving the fetch response
502504
onAfterResponse: async (response, req, res, options) => {
503505
// Access full response object
504-
console.log(`Status: ${response.statusCode}`);
506+
console.log(`Status: ${response.status}`);
505507
console.log('Headers:', response.headers);
506-
// Note: response.body is a stream, not the actual body content
508+
// Note: response.body is a stream that will be piped to res automatically
507509
}
508510
}
509511
});
@@ -513,28 +515,55 @@ const proxy = createProxyServer({
513515

514516
```js
515517
import { readFileSync } from "node:fs";
518+
import { Agent } from "undici";
516519

517520
const proxy = createProxyServer({
518521
target: "https://http2-target.example.com",
519522
ssl: {
520523
key: readFileSync("server-key.pem"),
521524
cert: readFileSync("server-cert.pem")
522525
},
523-
undici: {
524-
agentOptions: {
526+
fetch: {
527+
dispatcher: new Agent({
525528
allowH2: true,
526529
connect: { rejectUnauthorized: false }
527-
}
530+
})
528531
},
529532
secure: false // Skip SSL verification for self-signed certs
530533
}).listen(8443);
531534
```
532535

536+
##### Using Custom Fetch Implementation
537+
538+
```js
539+
import { createProxyServer } from "http-proxy-3";
540+
import { fetch as undiciFetch, Agent } from "undici";
541+
542+
// Wrap undici's fetch with custom configuration
543+
function customFetch(url, opts) {
544+
opts = opts || {};
545+
opts.dispatcher = new Agent({ allowH2: true });
546+
return undiciFetch(url, opts);
547+
}
548+
549+
const proxy = createProxyServer({
550+
target: "https://api.example.com",
551+
fetch: {
552+
// Pass your custom fetch implementation
553+
customFetch,
554+
onBeforeRequest: async (requestOptions, req, res, options) => {
555+
requestOptions.headers['X-Custom'] = 'value';
556+
}
557+
}
558+
});
559+
```
560+
533561
**Important Notes:**
534-
- When `undici` option is provided, the proxy uses undici's HTTP client instead of Node.js native `http`/`https` modules
535-
- undici automatically handles HTTP/2 negotiation when `allowH2: true` is set
536-
- The `onBeforeRequest` and `onAfterResponse` callbacks are only available in the undici code path
537-
- Traditional `proxyReq` and `proxyRes` events are not emitted in the undici path - use the callbacks instead
562+
- When `fetch` option is provided, the proxy uses the fetch API instead of Node.js native `http`/`https` modules
563+
- To enable HTTP/2, pass a dispatcher (e.g., from undici with `allowH2: true`) in the fetch configuration
564+
- The `onBeforeRequest` and `onAfterResponse` callbacks are only available in the fetch code path
565+
- Traditional `proxyReq` and `proxyRes` events are not emitted in the fetch path - use the callbacks instead
566+
- The fetch approach is runtime-agnostic and doesn't require undici as a dependency for basic HTTP/1.1 proxying
538567

539568
**[Back to top](#table-of-contents)**
540569

@@ -633,11 +662,12 @@ const proxy = createProxyServer({
633662
634663
- **ca**: Optionally override the trusted CA certificates. This is passed to https.request.
635664
636-
- **undici**: Enable undici for HTTP/2 support. Set to `true` for defaults, or provide custom configuration:
637-
- `agentOptions`: Configuration for undici Agent (see [undici Agent.Options](https://github.com/nodejs/undici/blob/main/docs/api/Agent.md))
638-
- `requestOptions`: Configuration for undici requests (see [undici Dispatcher.RequestOptions](https://github.com/nodejs/undici/blob/main/docs/api/Dispatcher.md#dispatcherrequestoptions))
639-
- `onBeforeRequest`: Async callback called before making the undici request
640-
- `onAfterResponse`: Async callback called after receiving the undici response
665+
- **fetch**: Enable fetch API for HTTP/2 support. Set to `true` for defaults, or provide custom configuration:
666+
- `dispatcher`: Custom fetch dispatcher (e.g., undici Agent with `allowH2: true` for HTTP/2)
667+
- `requestOptions`: Additional fetch request options
668+
- `onBeforeRequest`: Async callback called before making the fetch request
669+
- `onAfterResponse`: Async callback called after receiving the fetch response
670+
- `customFetch`: Custom fetch implementation to use instead of the global fetch
641671
642672
**NOTE:**
643673
`options.ws` and `options.ssl` are optional.
@@ -654,15 +684,15 @@ If you are using the `proxyServer.listen` method, the following options are also
654684
655685
The following table shows which configuration options are compatible with different code paths:
656686
657-
| Option | Native HTTP/HTTPS | Undici HTTP/2 | Notes |
687+
| Option | Native HTTP/HTTPS | Fetch/HTTP/2 | Notes |
658688
|--------|-------------------|---------------|--------|
659689
| `target` | ✅ | ✅ | Core option, works in both paths |
660690
| `forward` | ✅ | ✅ | Core option, works in both paths |
661-
| `agent` | ✅ | ❌ | Native agents only, use `undici.agentOptions` instead |
691+
| `agent` | ✅ | ❌ | Native agents only, use `fetch.dispatcher` instead |
662692
| `ssl` | ✅ | ✅ | HTTPS server configuration |
663693
| `ws` | ✅ | ❌ | WebSocket proxying uses native path only |
664694
| `xfwd` | ✅ | ✅ | X-Forwarded headers |
665-
| `secure` | ✅ | | SSL certificate verification |
695+
| `secure` | ✅ | ❌¹ | SSL certificate verification |
666696
| `toProxy` | ✅ | ✅ | Proxy-to-proxy configuration |
667697
| `prependPath` | ✅ | ✅ | Path manipulation |
668698
| `ignorePath` | ✅ | ✅ | Path manipulation |
@@ -683,15 +713,18 @@ The following table shows which configuration options are compatible with differ
683713
| `buffer` | ✅ | ✅ | Request body stream |
684714
| `method` | ✅ | ✅ | HTTP method override |
685715
| `ca` | ✅ | ✅ | Custom CA certificates |
686-
| `undici` | ❌ | ✅ | Undici-specific configuration |
716+
| `fetch` | ❌ | ✅ | Fetch-specific configuration |
717+
718+
**Notes:**
719+
- ¹ `secure` is not directly supported in the fetch path. Instead, use `fetch.dispatcher` with `{connect: {rejectUnauthorized: false}}` to disable SSL certificate verification (e.g., for self-signed certificates).
687720
688721
**Code Path Selection:**
689722
- **Native Path**: Used by default, supports HTTP/1.1 and WebSockets
690-
- **Undici Path**: Activated when `undici` option is provided, supports HTTP/2
723+
- **Fetch Path**: Activated when `fetch` option is provided, supports HTTP/2 (with appropriate dispatcher)
691724
692725
**Event Compatibility:**
693726
- **Native Path**: Emits traditional events (`proxyReq`, `proxyRes`, `proxyReqWs`)
694-
- **Undici Path**: Uses callback functions (`onBeforeRequest`, `onAfterResponse`) instead of events
727+
- **Fetch Path**: Uses callback functions (`onBeforeRequest`, `onAfterResponse`) instead of events
695728
696729
**[Back to top](#table-of-contents)**
697730
@@ -705,7 +738,7 @@ The following table shows which configuration options are compatible with differ
705738
- `close`: This event is emitted once the proxy websocket was closed.
706739
- (DEPRECATED) `proxySocket`: Deprecated in favor of `open`.
707740
708-
**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.
741+
**Note**: When using the fetch code path (HTTP/2), the `proxyReq` and `proxyRes` events are **not** emitted. Instead, use the `onBeforeRequest` and `onAfterResponse` callback functions in the `fetch` configuration.
709742
710743
#### Traditional Events (Native HTTP/HTTPS path)
711744
@@ -741,25 +774,26 @@ proxy.on("open", (proxySocket) => {
741774
});
742775
```
743776

744-
#### Callback Functions (Undici/HTTP2 path)
777+
#### Callback Functions (Fetch/HTTP2 path)
745778

746779
```js
747780
import { createProxyServer } from "http-proxy-3";
781+
import { Agent } from "undici";
748782

749783
const proxy = createProxyServer({
750784
target: "https://api.example.com",
751-
undici: {
752-
agentOptions: { allowH2: true },
753-
// Called before making the undici request
785+
fetch: {
786+
dispatcher: new Agent({ allowH2: true }),
787+
// Called before making the fetch request
754788
onBeforeRequest: async (requestOptions, req, res, options) => {
755789
// Modify the outgoing request
756790
requestOptions.headers['X-Custom-Header'] = 'added-by-callback';
757-
console.log('Making request to:', requestOptions.origin + requestOptions.path);
791+
console.log('Making request to:', requestOptions.headers.host);
758792
},
759-
// Called after receiving the undici response
793+
// Called after receiving the fetch response
760794
onAfterResponse: async (response, req, res, options) => {
761795
// Access the full response object
762-
console.log(`Response: ${response.statusCode}`, response.headers);
796+
console.log(`Response: ${response.status}`, response.headers);
763797
// Note: response.body is a stream that will be piped to res automatically
764798
}
765799
}

biome.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"$schema": "https://biomejs.dev/schemas/2.2.5/schema.json",
3+
"vcs": {
4+
"enabled": false,
5+
"clientKind": "git",
6+
"useIgnoreFile": false
7+
},
8+
"files": {
9+
"ignoreUnknown": false
10+
},
11+
"formatter": {
12+
"enabled": true,
13+
"indentStyle": "space"
14+
},
15+
"linter": {
16+
"enabled": true,
17+
"rules": {
18+
"recommended": true
19+
}
20+
},
21+
"javascript": {
22+
"formatter": {
23+
"quoteStyle": "double"
24+
}
25+
},
26+
"assist": {
27+
"enabled": true,
28+
"actions": {
29+
"source": {
30+
"organizeImports": "on"
31+
}
32+
}
33+
}
34+
}

0 commit comments

Comments
 (0)