@@ -336,7 +336,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
336336 void ( async ( ) => {
337337 // Node.js parser is really weird.
338338 // It emits post-request Parse Errors on the same instance as previous request. WTF.
339- // Therefore we need to check if it has been destroyed as well.
339+ // Therefore, we need to check if it has been destroyed as well.
340340 //
341341 // Furthermore, Node.js 16 `response.destroy()` doesn't immediately destroy the socket,
342342 // but makes the response unreadable. So we additionally need to check `response.readable`.
@@ -723,95 +723,98 @@ export default class Request extends Duplex implements RequestEvents<Request> {
723723 return ;
724724 }
725725
726- if ( options . followRedirect && response . headers . location && redirectCodes . has ( statusCode ) ) {
726+ if ( response . headers . location && redirectCodes . has ( statusCode ) ) {
727727 // We're being redirected, we don't care about the response.
728728 // It'd be best to abort the request, but we can't because
729729 // we would have to sacrifice the TCP connection. We don't want that.
730- response . resume ( ) ;
731-
732- this . _cancelTimeouts ( ) ;
733- this . _unproxyEvents ( ) ;
734-
735- if ( this . redirectUrls . length >= options . maxRedirects ) {
736- this . _beforeError ( new MaxRedirectsError ( this ) ) ;
737- return ;
738- }
730+ const shouldFollow = typeof options . followRedirect === 'function' ? options . followRedirect ( typedResponse ) : options . followRedirect ;
731+ if ( shouldFollow ) {
732+ response . resume ( ) ;
739733
740- this . _request = undefined ;
734+ this . _cancelTimeouts ( ) ;
735+ this . _unproxyEvents ( ) ;
741736
742- const updatedOptions = new Options ( undefined , undefined , this . options ) ;
737+ if ( this . redirectUrls . length >= options . maxRedirects ) {
738+ this . _beforeError ( new MaxRedirectsError ( this ) ) ;
739+ return ;
740+ }
743741
744- const serverRequestedGet = statusCode === 303 && updatedOptions . method !== 'GET' && updatedOptions . method !== 'HEAD' ;
745- const canRewrite = statusCode !== 307 && statusCode !== 308 ;
746- const userRequestedGet = updatedOptions . methodRewriting && canRewrite ;
742+ this . _request = undefined ;
747743
748- if ( serverRequestedGet || userRequestedGet ) {
749- updatedOptions . method = 'GET' ;
744+ const updatedOptions = new Options ( undefined , undefined , this . options ) ;
750745
751- updatedOptions . body = undefined ;
752- updatedOptions . json = undefined ;
753- updatedOptions . form = undefined ;
746+ const serverRequestedGet = statusCode === 303 && updatedOptions . method !== 'GET' && updatedOptions . method !== 'HEAD' ;
747+ const canRewrite = statusCode !== 307 && statusCode !== 308 ;
748+ const userRequestedGet = updatedOptions . methodRewriting && canRewrite ;
754749
755- delete updatedOptions . headers [ 'content-length' ] ;
756- }
750+ if ( serverRequestedGet || userRequestedGet ) {
751+ updatedOptions . method = 'GET' ;
757752
758- try {
759- // We need this in order to support UTF-8
760- const redirectBuffer = Buffer . from ( response . headers . location , 'binary' ) . toString ( ) ;
761- const redirectUrl = new URL ( redirectBuffer , url ) ;
753+ updatedOptions . body = undefined ;
754+ updatedOptions . json = undefined ;
755+ updatedOptions . form = undefined ;
762756
763- if ( ! isUnixSocketURL ( url as URL ) && isUnixSocketURL ( redirectUrl ) ) {
764- this . _beforeError ( new RequestError ( 'Cannot redirect to UNIX socket' , { } , this ) ) ;
765- return ;
757+ delete updatedOptions . headers [ 'content-length' ] ;
766758 }
767759
768- // Redirecting to a different site, clear sensitive data.
769- if ( redirectUrl . hostname !== ( url as URL ) . hostname || redirectUrl . port !== ( url as URL ) . port ) {
770- if ( 'host' in updatedOptions . headers ) {
771- delete updatedOptions . headers . host ;
772- }
760+ try {
761+ // We need this in order to support UTF-8
762+ const redirectBuffer = Buffer . from ( response . headers . location , 'binary' ) . toString ( ) ;
763+ const redirectUrl = new URL ( redirectBuffer , url ) ;
773764
774- if ( 'cookie' in updatedOptions . headers ) {
775- delete updatedOptions . headers . cookie ;
765+ if ( ! isUnixSocketURL ( url as URL ) && isUnixSocketURL ( redirectUrl ) ) {
766+ this . _beforeError ( new RequestError ( 'Cannot redirect to UNIX socket' , { } , this ) ) ;
767+ return ;
776768 }
777769
778- if ( 'authorization' in updatedOptions . headers ) {
779- delete updatedOptions . headers . authorization ;
780- }
770+ // Redirecting to a different site, clear sensitive data.
771+ if ( redirectUrl . hostname !== ( url as URL ) . hostname || redirectUrl . port !== ( url as URL ) . port ) {
772+ if ( 'host' in updatedOptions . headers ) {
773+ delete updatedOptions . headers . host ;
774+ }
775+
776+ if ( 'cookie' in updatedOptions . headers ) {
777+ delete updatedOptions . headers . cookie ;
778+ }
781779
782- if ( updatedOptions . username || updatedOptions . password ) {
783- updatedOptions . username = '' ;
784- updatedOptions . password = '' ;
780+ if ( 'authorization' in updatedOptions . headers ) {
781+ delete updatedOptions . headers . authorization ;
782+ }
783+
784+ if ( updatedOptions . username || updatedOptions . password ) {
785+ updatedOptions . username = '' ;
786+ updatedOptions . password = '' ;
787+ }
788+ } else {
789+ redirectUrl . username = updatedOptions . username ;
790+ redirectUrl . password = updatedOptions . password ;
785791 }
786- } else {
787- redirectUrl . username = updatedOptions . username ;
788- redirectUrl . password = updatedOptions . password ;
789- }
790792
791- this . redirectUrls . push ( redirectUrl ) ;
792- updatedOptions . prefixUrl = '' ;
793- updatedOptions . url = redirectUrl ;
793+ this . redirectUrls . push ( redirectUrl ) ;
794+ updatedOptions . prefixUrl = '' ;
795+ updatedOptions . url = redirectUrl ;
794796
795- for ( const hook of updatedOptions . hooks . beforeRedirect ) {
796- // eslint-disable-next-line no-await-in-loop
797- await hook ( updatedOptions , typedResponse ) ;
798- }
797+ for ( const hook of updatedOptions . hooks . beforeRedirect ) {
798+ // eslint-disable-next-line no-await-in-loop
799+ await hook ( updatedOptions , typedResponse ) ;
800+ }
799801
800- this . emit ( 'redirect' , updatedOptions , typedResponse ) ;
802+ this . emit ( 'redirect' , updatedOptions , typedResponse ) ;
801803
802- this . options = updatedOptions ;
804+ this . options = updatedOptions ;
805+
806+ await this . _makeRequest ( ) ;
807+ } catch ( error : any ) {
808+ this . _beforeError ( error ) ;
809+ return ;
810+ }
803811
804- await this . _makeRequest ( ) ;
805- } catch ( error : any ) {
806- this . _beforeError ( error ) ;
807812 return ;
808813 }
809-
810- return ;
811814 }
812815
813816 // `HTTPError`s always have `error.response.body` defined.
814- // Therefore we cannot retry if `options.throwHttpErrors` is false.
817+ // Therefore, we cannot retry if `options.throwHttpErrors` is false.
815818 // On the last retry, if `options.throwHttpErrors` is false, we would need to return the body,
816819 // but that wouldn't be possible since the body would be already read in `error.response.body`.
817820 if ( options . isStream && options . throwHttpErrors && ! isResponseOk ( typedResponse ) ) {
0 commit comments