Skip to content

Commit 035a8be

Browse files
committed
Separate proxy redirect handling from fetch transport
1 parent 03742c6 commit 035a8be

1 file changed

Lines changed: 47 additions & 31 deletions

File tree

‎src/client/proxy.ts‎

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -256,42 +256,58 @@ function send(url: URL, method: string, headers: Headers, body: Buffer | undefin
256256
});
257257
}
258258

259+
interface ProxyRequestState {
260+
url: URL;
261+
method: string;
262+
headers: Headers;
263+
body: Buffer | undefined;
264+
}
265+
266+
function followRedirect(state: ProxyRequestState, raw: RawResponse,
267+
policy: Request['redirect'], redirects: number): boolean {
268+
const location = raw.headers.get('location');
269+
if (policy === 'manual' || !REDIRECTS.has(raw.status) || !location) return false;
270+
if (policy === 'error') throw new TypeError(`Unexpected redirect from ${state.url.origin}`);
271+
if (redirects >= 20) throw new TypeError('Too many redirects');
272+
273+
const next = new URL(location, state.url);
274+
if (raw.status === 303 || ((raw.status === 301 || raw.status === 302) && state.method === 'POST')) {
275+
if (state.method !== 'HEAD') state.method = 'GET';
276+
state.body = undefined;
277+
for (const name of ['content-type', 'content-length', 'content-encoding', 'content-language', 'content-location']) {
278+
state.headers.delete(name);
279+
}
280+
}
281+
if (next.origin !== state.url.origin) {
282+
for (const name of ['authorization', 'cookie', 'proxy-authorization', 'host']) state.headers.delete(name);
283+
}
284+
state.url = next;
285+
return true;
286+
}
287+
288+
function fetchResponse(raw: RawResponse, state: ProxyRequestState, redirected: boolean): Response {
289+
const response = new Response(NULL_BODY.has(raw.status) || state.method === 'HEAD' ? null : new Uint8Array(raw.body), {
290+
status: raw.status, statusText: raw.statusText, headers: raw.headers,
291+
});
292+
Object.defineProperty(response, 'url', { value: state.url.toString() });
293+
Object.defineProperty(response, 'redirected', { value: redirected });
294+
return response;
295+
}
296+
259297
/** `fetch` built on `node:http(s)` so requests can use an `Agent` (proxy tunnels). */
260298
function agentFetch(agentFor: (url: URL) => HttpAgent): typeof fetch {
261299
return async (input, init) => {
262300
const initial = new Request(input, init);
263-
let url = new URL(initial.url);
264-
let method = initial.method;
265-
const headers = new Headers(initial.headers);
266-
let body = initial.body ? Buffer.from(await initial.arrayBuffer()) : undefined;
267-
301+
const state: ProxyRequestState = {
302+
url: new URL(initial.url),
303+
method: initial.method,
304+
headers: new Headers(initial.headers),
305+
body: initial.body ? Buffer.from(await initial.arrayBuffer()) : undefined,
306+
};
268307
for (let redirects = 0; ; redirects += 1) {
269-
const raw = await send(url, method, headers, body, agentFor(url), initial.signal);
270-
const location = raw.headers.get('location');
271-
if (initial.redirect !== 'manual' && REDIRECTS.has(raw.status) && location) {
272-
if (initial.redirect === 'error') throw new TypeError(`Unexpected redirect from ${url.origin}`);
273-
if (redirects >= 20) throw new TypeError('Too many redirects');
274-
const next = new URL(location, url);
275-
if (raw.status === 303 || ((raw.status === 301 || raw.status === 302) && method === 'POST')) {
276-
if (method !== 'HEAD') method = 'GET';
277-
body = undefined;
278-
for (const name of ['content-type', 'content-length', 'content-encoding', 'content-language', 'content-location']) {
279-
headers.delete(name);
280-
}
281-
}
282-
if (next.origin !== url.origin) {
283-
for (const name of ['authorization', 'cookie', 'proxy-authorization', 'host']) headers.delete(name);
284-
}
285-
url = next;
286-
continue;
287-
}
288-
289-
const response = new Response(NULL_BODY.has(raw.status) || method === 'HEAD' ? null : new Uint8Array(raw.body), {
290-
status: raw.status, statusText: raw.statusText, headers: raw.headers,
291-
});
292-
Object.defineProperty(response, 'url', { value: url.toString() });
293-
Object.defineProperty(response, 'redirected', { value: redirects > 0 });
294-
return response;
308+
const raw = await send(state.url, state.method, state.headers, state.body, agentFor(state.url), initial.signal);
309+
if (followRedirect(state, raw, initial.redirect, redirects)) continue;
310+
return fetchResponse(raw, state, redirects > 0);
295311
}
296312
};
297313
}

0 commit comments

Comments
 (0)