Skip to content

Commit 99acfb2

Browse files
authored
feat(proxy): use native fetch + event.app.fetch (#1141)
1 parent ab09098 commit 99acfb2

4 files changed

Lines changed: 70 additions & 115 deletions

File tree

docs/2.utils/5.proxy.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ icon: arcticons:super-proxy
88
99
<!-- automd:jsdocs src="../../src/utils/proxy.ts" -->
1010

11-
### `fetchWithEvent(event, req, init?, options?: { fetch: F })`
11+
### `fetchWithEvent(event, req, init?)`
1212

1313
Make a fetch request with the event's context and headers.
1414

src/utils/internal/proxy.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,6 @@ export const ignoredHeaders: Set<string> = new Set([
1515
"accept",
1616
]);
1717

18-
export function getFetch<T = typeof fetch>(_fetch?: T): T {
19-
if (_fetch) {
20-
return _fetch;
21-
}
22-
if (globalThis.fetch) {
23-
return globalThis.fetch as T;
24-
}
25-
throw new Error(
26-
"fetch is not available. Try importing `node-fetch-native/polyfill` for Node.js.",
27-
);
28-
}
29-
3018
export function rewriteCookieProperty(
3119
header: string,
3220
map: string | Record<string, string>,

src/utils/proxy.ts

Lines changed: 36 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
import type { H3EventContext } from "../types/context.ts";
21
import type { H3Event } from "../event.ts";
32

43
import { splitSetCookieString } from "cookie-es";
5-
import { sanitizeStatusMessage, sanitizeStatusCode } from "./sanitize.ts";
4+
import { sanitizeStatusMessage } from "./sanitize.ts";
65
import { HTTPError } from "../error.ts";
76
import {
87
PayloadMethods,
9-
getFetch,
108
ignoredHeaders,
119
mergeHeaders,
1210
rewriteCookieProperty,
@@ -17,12 +15,7 @@ export interface ProxyOptions {
1715
headers?: HeadersInit;
1816
forwardHeaders?: string[];
1917
filterHeaders?: string[];
20-
fetchOptions?: RequestInit & { duplex?: "half" | "full" } & {
21-
ignoreResponseError?: boolean;
22-
};
23-
fetch?: typeof fetch;
24-
sendStream?: boolean;
25-
streamRequest?: boolean;
18+
fetchOptions?: RequestInit & { duplex?: "half" | "full" };
2619
cookieDomainRewrite?: string | Record<string, string>;
2720
cookiePathRewrite?: string | Record<string, string>;
2821
onResponse?: (event: H3Event, response: Response) => void;
@@ -37,16 +30,9 @@ export async function proxyRequest(
3730
opts: ProxyOptions = {},
3831
): Promise<BodyInit | undefined | null> {
3932
// Request Body
40-
let body;
41-
let duplex: "half" | "full" | undefined;
42-
if (PayloadMethods.has(event.req.method)) {
43-
if (opts.streamRequest) {
44-
body = event.req.body;
45-
duplex = "half";
46-
} else {
47-
body = await event.req.arrayBuffer();
48-
}
49-
}
33+
const requestBody = PayloadMethods.has(event.req.method)
34+
? event.req.body
35+
: undefined;
5036

5137
// Method
5238
const method = opts.fetchOptions?.method || event.req.method;
@@ -66,8 +52,8 @@ export async function proxyRequest(
6652
...opts,
6753
fetchOptions: {
6854
method,
69-
body,
70-
duplex,
55+
body: requestBody,
56+
duplex: requestBody ? "half" : undefined,
7157
...opts.fetchOptions,
7258
headers: fetchHeaders,
7359
},
@@ -82,17 +68,20 @@ export async function proxy(
8268
target: string,
8369
opts: ProxyOptions = {},
8470
): Promise<BodyInit | undefined | null> {
71+
const fetchOptions: RequestInit = {
72+
headers: opts.headers as HeadersInit,
73+
...opts.fetchOptions,
74+
};
75+
8576
let response: Response | undefined;
8677
try {
87-
response = await getFetch(opts.fetch)(target, {
88-
headers: opts.headers as HeadersInit,
89-
ignoreResponseError: true, // make $ofetch.raw transparent
90-
...opts.fetchOptions,
91-
});
78+
response =
79+
target[0] === "/" && event.app?.fetch
80+
? await event.app._fetch(target, fetchOptions, { ...event.context })
81+
: await fetch(target, fetchOptions);
9282
} catch (error) {
9383
throw new HTTPError({ status: 502, cause: error });
9484
}
95-
event.res.status = sanitizeStatusCode(response.status, event.res.status);
9685
event.res.statusText = sanitizeStatusMessage(response.statusText);
9786

9887
const cookies: string[] = [];
@@ -134,17 +123,6 @@ export async function proxy(
134123
await opts.onResponse(event, response);
135124
}
136125

137-
// Directly send consumed _data
138-
if ((response as any)._data !== undefined) {
139-
return (response as any)._data;
140-
}
141-
142-
// Send at once
143-
if (opts.sendStream === false) {
144-
return new Uint8Array(await response.arrayBuffer());
145-
}
146-
147-
// Send as stream
148126
return response.body;
149127
}
150128

@@ -176,26 +154,26 @@ export function getProxyRequestHeaders(
176154
/**
177155
* Make a fetch request with the event's context and headers.
178156
*/
179-
export function fetchWithEvent<
180-
T = unknown,
181-
_R = unknown,
182-
F extends (req: RequestInfo | URL, opts?: any) => any = typeof fetch,
183-
>(
157+
export async function fetchWithEvent(
184158
event: H3Event,
185159
req: RequestInfo | URL,
186-
init?: RequestInit & { context?: H3EventContext },
187-
options?: { fetch: F },
188-
): unknown extends T ? ReturnType<F> : T {
189-
return getFetch(options?.fetch)(req, {
190-
...init,
191-
context: init?.context || event.context,
192-
headers: mergeHeaders(
193-
getProxyRequestHeaders(event, {
194-
host: typeof req === "string" && req.startsWith("/"),
195-
}),
196-
init?.headers,
197-
),
198-
} satisfies RequestInit & {
199-
context?: H3EventContext;
200-
});
160+
init?: RequestInit,
161+
): Promise<Response> {
162+
if (typeof req !== "string" || req[0] !== "/" || !event.app) {
163+
return fetch(req, init);
164+
}
165+
166+
return await event.app!._fetch(
167+
req,
168+
{
169+
...init,
170+
headers: mergeHeaders(
171+
getProxyRequestHeaders(event, {
172+
host: typeof req === "string" && req.startsWith("/"),
173+
}),
174+
init?.headers,
175+
),
176+
},
177+
{ ...event.context },
178+
);
201179
}

0 commit comments

Comments
 (0)