diff --git a/README.md b/README.md index a6174238..5078c6d9 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,8 @@ H3 has a concept of composable utilities that accept `event` (from `eventHandler - `createError({ statusCode, statusMessage, data? })` - `sendProxy(event, { target, headers?, fetchOptions?, fetch?, sendStream? })` - `proxyRequest(event, { target, headers?, fetchOptions?, fetch?, sendStream? })` +- `fetchWithEvent(event, req, init, { fetch? }?)` +- `getProxyRequestHeaders(event)` - `sendNoContent(event, code = 204)` - `setResponseStatus(event, status)` - `getResponseStatus(event)` diff --git a/src/utils/proxy.ts b/src/utils/proxy.ts index 4782bc62..e76a2bcb 100644 --- a/src/utils/proxy.ts +++ b/src/utils/proxy.ts @@ -35,13 +35,7 @@ export async function proxyRequest( } // Headers - const headers = Object.create(null); - const reqHeaders = getRequestHeaders(event); - for (const name in reqHeaders) { - if (!ignoredHeaders.has(name)) { - headers[name] = reqHeaders[name]; - } - } + const headers = getProxyRequestHeaders(event); if (opts.fetchOptions?.headers) { Object.assign(headers, opts.fetchOptions.headers); } @@ -65,14 +59,7 @@ export async function sendProxy( target: string, opts: ProxyOptions = {} ) { - const _fetch = opts.fetch || globalThis.fetch; - if (!_fetch) { - throw new Error( - "fetch is not available. Try importing `node-fetch-native/polyfill` for Node.js." - ); - } - - const response = await _fetch(target, { + const response = await _getFetch(opts.fetch)(target, { headers: opts.headers as HeadersInit, ...opts.fetchOptions, }); @@ -90,19 +77,61 @@ export async function sendProxy( } try { - if (response.body) { - if (opts.sendStream === false) { - const data = new Uint8Array(await response.arrayBuffer()); - event.node.res.end(data); - } else { - for await (const chunk of response.body as any as AsyncIterable) { - event.node.res.write(chunk); - } - event.node.res.end(); - } + if ((response as any)._data) { + return event.node.res.end((response as any)._data); + } + if (opts.sendStream === false) { + const data = new Uint8Array(await response.arrayBuffer()); + return event.node.res.end(data); + } + for await (const chunk of response.body as any as AsyncIterable) { + event.node.res.write(chunk); } - } catch (error) { event.node.res.end(); + } catch (error) { + event.node.res.end((response as any)._data); throw error; } } + +export function getProxyRequestHeaders(event: H3Event) { + const headers = Object.create(null); + const reqHeaders = getRequestHeaders(event); + for (const name in reqHeaders) { + if (!ignoredHeaders.has(name)) { + headers[name] = reqHeaders[name]; + } + } + return headers; +} + +export function fetchWithEvent( + event: H3Event, + req: RequestInfo, + init?: RequestInit, + options?: { fetch: typeof fetch } +) { + return _getFetch(options?.fetch)(req, { + ...init, + // @ts-ignore (context is used for unenv and local fetch) + context: init.context || event.context, + headers: { + ...getProxyRequestHeaders(event), + ...init?.headers, + }, + }); +} + +// -- internal utils -- + +function _getFetch(_fetch?: typeof fetch) { + if (_fetch) { + return _fetch; + } + if (globalThis.fetch) { + return globalThis.fetch; + } + throw new Error( + "fetch is not available. Try importing `node-fetch-native/polyfill` for Node.js." + ); +}