@waiting/fetch
Version:
HTTP fetch API for browser and Node.js. Handle 302/303 redirect correctly on Node.js
99 lines • 3.74 kB
JavaScript
import assert from 'node:assert';
import { fetch } from 'undici';
import { trace } from './trace.js';
import { AttributeKey } from './types.js';
import { parseRespCookie, processInitOpts, processRequestGetLikeData, processRequestPostLikeData, } from './util.js';
/**
* fetch wrapper
*
* parameter init ignored during parameter input is typeof Request
*/
export async function _fetch(input, args, requestInit, span) {
const resp = await createRequest(input, args, requestInit, span);
const res = await handleRedirect(resp, args, requestInit);
trace(AttributeKey.HandleRedirectFinish, span);
return res;
}
export async function createRequest(input, args, requestInit, span) {
let inputNew = input;
const fetchModule = fetch;
let resp;
// @ts-expect-error
delete requestInit.otelComponent;
// @ts-expect-error
delete requestInit.span;
// @ts-expect-error
delete requestInit.traceContext;
if (typeof input === 'string') {
trace(AttributeKey.ProcessRequestData);
assert(input, 'input should not be empty when typeof input is string');
assert(requestInit.method, 'requestInit.method should not be empty');
if (['GET', 'DELETE'].includes(requestInit.method)) {
inputNew = processRequestGetLikeData(input, args);
}
else if (['POST', 'PUT', 'OPTIONS'].includes(requestInit.method)) {
const body = processRequestPostLikeData(args) ?? null;
requestInit.body = body;
}
else {
throw new TypeError(`Invalid method value: "${requestInit.method}"`);
}
// fix undici not support referrer value 'client' and 'no-referrer'
const { referrer } = requestInit;
if (!referrer || referrer === 'client' || referrer === 'no-referrer') {
// @ts-expect-error
requestInit.referrer = void 0;
}
// @ts-expect-error
if (requestInit.url) {
// @ts-expect-error
delete requestInit.url;
}
trace(AttributeKey.RequestStart, span);
resp = await fetchModule(inputNew, requestInit);
}
else {
trace(AttributeKey.RequestStart, span);
resp = await fetchModule(input);
}
trace(AttributeKey.RequestFinish, span);
return resp;
}
/**
* Handle redirect case to retrieve cookies before jumping under Node.js.
* There's no effect under Browser
*
* docs: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
*/
export async function handleRedirect(resp, args, init) {
// test by test/30_cookie.test.ts
if (args.keepRedirectCookies === true && resp.status >= 301 && resp.status <= 308) {
// do NOT use resp.url, it's the final url
const location = resp.headers.get('location');
if (!location) {
return resp;
}
const url = location.toLocaleLowerCase().startsWith('http')
? location
: new URL(location, resp.url).toString();
if (url) {
const cookie = resp.headers.get('Set-Cookie');
const cookieObj = parseRespCookie(cookie);
if (cookieObj) {
args.cookies = args.cookies
? { ...args.cookies, ...cookieObj }
: { ...cookieObj };
}
const options = processInitOpts({ args, requestInit: init });
if (resp.status === 303) {
options.requestInit.method = 'GET';
return _fetch(url, options.args, options.requestInit);
}
else {
return _fetch(url, options.args, options.requestInit);
}
}
}
return resp;
}
//# sourceMappingURL=request.js.map