@@ -336,7 +336,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
336
336
void ( async ( ) => {
337
337
// Node.js parser is really weird.
338
338
// 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.
340
340
//
341
341
// Furthermore, Node.js 16 `response.destroy()` doesn't immediately destroy the socket,
342
342
// 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> {
723
723
return ;
724
724
}
725
725
726
- if ( options . followRedirect && response . headers . location && redirectCodes . has ( statusCode ) ) {
726
+ if ( response . headers . location && redirectCodes . has ( statusCode ) ) {
727
727
// We're being redirected, we don't care about the response.
728
728
// It'd be best to abort the request, but we can't because
729
729
// 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 ( ) ;
739
733
740
- this . _request = undefined ;
734
+ this . _cancelTimeouts ( ) ;
735
+ this . _unproxyEvents ( ) ;
741
736
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
+ }
743
741
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 ;
747
743
748
- if ( serverRequestedGet || userRequestedGet ) {
749
- updatedOptions . method = 'GET' ;
744
+ const updatedOptions = new Options ( undefined , undefined , this . options ) ;
750
745
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 ;
754
749
755
- delete updatedOptions . headers [ 'content-length' ] ;
756
- }
750
+ if ( serverRequestedGet || userRequestedGet ) {
751
+ updatedOptions . method = 'GET' ;
757
752
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 ;
762
756
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' ] ;
766
758
}
767
759
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 ) ;
773
764
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 ;
776
768
}
777
769
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
+ }
781
779
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 ;
785
791
}
786
- } else {
787
- redirectUrl . username = updatedOptions . username ;
788
- redirectUrl . password = updatedOptions . password ;
789
- }
790
792
791
- this . redirectUrls . push ( redirectUrl ) ;
792
- updatedOptions . prefixUrl = '' ;
793
- updatedOptions . url = redirectUrl ;
793
+ this . redirectUrls . push ( redirectUrl ) ;
794
+ updatedOptions . prefixUrl = '' ;
795
+ updatedOptions . url = redirectUrl ;
794
796
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
+ }
799
801
800
- this . emit ( 'redirect' , updatedOptions , typedResponse ) ;
802
+ this . emit ( 'redirect' , updatedOptions , typedResponse ) ;
801
803
802
- this . options = updatedOptions ;
804
+ this . options = updatedOptions ;
805
+
806
+ await this . _makeRequest ( ) ;
807
+ } catch ( error : any ) {
808
+ this . _beforeError ( error ) ;
809
+ return ;
810
+ }
803
811
804
- await this . _makeRequest ( ) ;
805
- } catch ( error : any ) {
806
- this . _beforeError ( error ) ;
807
812
return ;
808
813
}
809
-
810
- return ;
811
814
}
812
815
813
816
// `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.
815
818
// On the last retry, if `options.throwHttpErrors` is false, we would need to return the body,
816
819
// but that wouldn't be possible since the body would be already read in `error.response.body`.
817
820
if ( options . isStream && options . throwHttpErrors && ! isResponseOk ( typedResponse ) ) {
0 commit comments