@@ -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). */
260298function 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