Skip to content

Commit a24e06b

Browse files
author
vhess
committed
wip: switch to fetch
1 parent 0ca2eb7 commit a24e06b

File tree

2 files changed

+55
-69
lines changed

2 files changed

+55
-69
lines changed

lib/http-proxy/index.ts

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -95,20 +95,31 @@ export interface ServerOptions {
9595
*/
9696
ca?: string;
9797
/** Enable undici for HTTP/2 support. Set to true for defaults, or provide custom configuration. */
98-
undici?: boolean | UndiciOptions;
98+
fetch?: boolean | FetchOptions;
9999
}
100100

101-
export interface UndiciOptions {
102-
/** Undici Agent configuration */
103-
agentOptions?: Agent.Options;
104-
/** Undici request options */
105-
requestOptions?: Dispatcher.RequestOptions;
106-
/** Called before making the undici request */
107-
onBeforeRequest?: (requestOptions: Dispatcher.RequestOptions, req: http.IncomingMessage, res: http.ServerResponse, options: NormalizedServerOptions) => void | Promise<void>;
108-
/** Called after receiving the undici response */
109-
onAfterResponse?: (response: Dispatcher.ResponseData, req: http.IncomingMessage, res: http.ServerResponse, options: NormalizedServerOptions) => void | Promise<void>;
101+
export interface FetchOptions {
102+
/** Undici Agent configuration */
103+
dispatcher?: Dispatcher;
104+
/** Undici request options */
105+
requestOptions?: RequestInit;
106+
/** Called before making the undici request */
107+
onBeforeRequest?: (
108+
requestOptions: RequestInit,
109+
req: http.IncomingMessage,
110+
res: http.ServerResponse,
111+
options: NormalizedServerOptions,
112+
) => void | Promise<void>;
113+
/** Called after receiving the undici response */
114+
onAfterResponse?: (
115+
response: Response,
116+
req: http.IncomingMessage,
117+
res: http.ServerResponse,
118+
options: NormalizedServerOptions,
119+
) => void | Promise<void>;
110120
}
111121

122+
112123
export interface NormalizedServerOptions extends ServerOptions {
113124
target?: NormalizeProxyTarget<ProxyTarget>;
114125
forward?: NormalizeProxyTarget<ProxyTargetUrl>;
@@ -238,7 +249,7 @@ export class ProxyServer<TIncomingMessage extends typeof http.IncomingMessage =
238249
private _server?: http.Server<TIncomingMessage, TServerResponse> | http2.Http2SecureServer<TIncomingMessage, TServerResponse> | null;
239250

240251
// Undici agent for this proxy server
241-
public undiciAgent?: Agent;
252+
public dispatcher?: Dispatcher;
242253

243254
/**
244255
* Creates the proxy server with specified options.
@@ -254,34 +265,7 @@ export class ProxyServer<TIncomingMessage extends typeof http.IncomingMessage =
254265
this.webPasses = Object.values(WEB_PASSES) as Array<PassFunctions<TIncomingMessage, TServerResponse, TError>['web']>;
255266
this.wsPasses = Object.values(WS_PASSES) as Array<PassFunctions<TIncomingMessage, TServerResponse, TError>['ws']>;
256267
this.on("error", this.onError);
257-
258-
// Initialize undici agent if enabled
259-
if (options.undici) {
260-
this.initializeAgent(options.undici);
261-
}
262-
}
263-
264-
/**
265-
* Initialize the single undici agent based on server options
266-
*/
267-
private initializeAgent(undiciOptions: UndiciOptions | boolean): void {
268-
const resolvedOptions = undiciOptions === true ? {} as UndiciOptions : undiciOptions as UndiciOptions;
269-
const agentOptions: Agent.Options = {
270-
allowH2: true,
271-
connect: {
272-
rejectUnauthorized: this.options.secure !== false,
273-
},
274-
...(resolvedOptions.agentOptions || {}),
275-
};
276-
277-
this.undiciAgent = new UndiciAgent(agentOptions);
278-
279-
if (this.options.followRedirects) {
280-
this.undiciAgent = this.undiciAgent.compose(
281-
interceptors.redirect({ maxRedirections: 5 })
282-
) as Agent;
283-
}
284-
}
268+
}
285269

286270
/**
287271
* Creates the proxy server with specified options.

lib/http-proxy/passes/web-incoming.ts

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ import type { Socket } from "node:net";
1717
import type Stream from "node:stream";
1818
import * as followRedirects from "follow-redirects";
1919
import type { Dispatcher } from "undici";
20-
import type { ErrorCallback, NormalizedServerOptions, NormalizeProxyTarget, ProxyServer, ProxyTarget, ProxyTargetUrl, ServerOptions, UndiciOptions } from "..";
20+
import type { ErrorCallback, FetchOptions, NormalizedServerOptions, NormalizeProxyTarget, ProxyServer, ProxyTarget, ProxyTargetUrl, ServerOptions, UndiciOptions } from "..";
2121
import * as common from "../common";
2222
import { type EditableResponse, OUTGOING_PASSES } from "./web-outgoing";
23+
import { Readable } from "node:stream";
2324

2425
export type ProxyResponse = Request & {
2526
headers: { [key: string]: string | string[] };
@@ -79,7 +80,7 @@ export function stream(req: Request, res: Response, options: NormalizedServerOpt
7980
// And we begin!
8081
server.emit("start", req, res, options.target || options.forward!);
8182

82-
if (options.undici) {
83+
if (options.fetch) {
8384
return stream2(req, res, options, _, server, cb);
8485
}
8586

@@ -220,17 +221,11 @@ async function stream2(
220221
handleError(err);
221222
});
222223

223-
const undiciOptions = options.undici === true ? {} as UndiciOptions : options.undici;
224-
if (!undiciOptions) {
225-
throw new Error("stream2 called without undici options");
224+
const fetchOptions = options.fetch === true ? {} as FetchOptions : options.fetch;
225+
if (!fetchOptions) {
226+
throw new Error("stream2 called without fetch options");
226227
}
227228

228-
const agent = server.undiciAgent
229-
230-
if (!agent) {
231-
handleError(new Error("Undici agent not initialized"));
232-
return;
233-
}
234229

235230
if (options.forward) {
236231
const outgoingOptions = common.setupOutgoing(
@@ -240,7 +235,7 @@ async function stream2(
240235
"forward",
241236
);
242237

243-
const requestOptions: Dispatcher.RequestOptions = {
238+
const requestOptions: RequestInit = {
244239
origin: new URL(outgoingOptions.url).origin,
245240
method: outgoingOptions.method as Dispatcher.HttpMethod,
246241
headers: outgoingOptions.headers || {},
@@ -255,22 +250,22 @@ async function stream2(
255250
}
256251

257252
// Call onBeforeRequest callback before making the forward request
258-
if (undiciOptions.onBeforeRequest) {
253+
if (fetchOptions.onBeforeRequest) {
259254
try {
260-
await undiciOptions.onBeforeRequest(requestOptions, req, res, options);
255+
await fetchOptions.onBeforeRequest(requestOptions, req, res, options);
261256
} catch (err) {
262257
handleError(err as Error, options.forward);
263258
return;
264259
}
265260
}
266261

267262
try {
268-
const result = await agent.request(requestOptions);
263+
const result = await fetch(outgoingOptions.url, requestOptions);
269264

270265
// Call onAfterResponse callback for forward requests (though they typically don't expect responses)
271-
if (undiciOptions.onAfterResponse) {
266+
if (fetchOptions.onAfterResponse) {
272267
try {
273-
await undiciOptions.onAfterResponse(result, req, res, options);
268+
await fetchOptions.onAfterResponse(result, req, res, options);
274269
} catch (err) {
275270
handleError(err as Error, options.forward);
276271
return;
@@ -287,13 +282,13 @@ async function stream2(
287282

288283
const outgoingOptions = common.setupOutgoing(options.ssl || {}, options, req);
289284

290-
const requestOptions: Dispatcher.RequestOptions = {
285+
const requestOptions: RequestInit = {
291286
origin: new URL(outgoingOptions.url).origin,
292287
method: outgoingOptions.method as Dispatcher.HttpMethod,
293288
headers: outgoingOptions.headers || {},
294289
path: outgoingOptions.path || "/",
295290
headersTimeout: options.proxyTimeout,
296-
...undiciOptions.requestOptions
291+
...fetchOptions.requestOptions
297292
};
298293

299294
if (options.auth) {
@@ -307,22 +302,22 @@ async function stream2(
307302
}
308303

309304
// Call onBeforeRequest callback before making the request
310-
if (undiciOptions.onBeforeRequest) {
305+
if (fetchOptions.onBeforeRequest) {
311306
try {
312-
await undiciOptions.onBeforeRequest(requestOptions, req, res, options);
307+
await fetchOptions.onBeforeRequest(requestOptions, req, res, options);
313308
} catch (err) {
314309
handleError(err as Error, options.target);
315310
return;
316311
}
317312
}
318313

319314
try {
320-
const response = await agent.request(requestOptions);
315+
const response = await fetch(outgoingOptions.url, requestOptions);
321316

322317
// Call onAfterResponse callback after receiving the response
323-
if (undiciOptions.onAfterResponse) {
318+
if (fetchOptions.onAfterResponse) {
324319
try {
325-
await undiciOptions.onAfterResponse(response, req, res, options);
320+
await fetchOptions.onAfterResponse(response, req, res, options);
326321
} catch (err) {
327322
handleError(err as Error, options.target);
328323
return;
@@ -351,18 +346,25 @@ async function stream2(
351346

352347
if (!res.writableEnded) {
353348
// Allow us to listen for when the proxy has completed
354-
response.body.on("end", () => {
349+
const nodeStream = response.body
350+
? Readable.from(response.body as AsyncIterable<Uint8Array>)
351+
: null;
352+
353+
if (nodeStream) {
354+
nodeStream.on("end", () => {
355+
server?.emit("end", req, res, fakeProxyRes);
356+
});
357+
// We pipe to the response unless its expected to be handled by the user
358+
if (!options.selfHandleResponse) {
359+
nodeStream.pipe(res);
360+
}
361+
} else {
355362
server?.emit("end", req, res, fakeProxyRes);
356-
});
357-
// We pipe to the response unless its expected to be handled by the user
358-
if (!options.selfHandleResponse) {
359-
response.body.pipe(res);
360363
}
361364
} else {
362365
server?.emit("end", req, res, fakeProxyRes);
363366
}
364367

365-
366368
} catch (err) {
367369
if (err) {
368370
handleError(err as Error, options.target);

0 commit comments

Comments
 (0)