1
1
const axios = require ( 'axios' ) ;
2
2
const { CLI_VERSION } = require ( '../constants' ) ;
3
+ const { addCookieToJar, getCookieStringForUrl } = require ( './cookies' ) ;
4
+
5
+ const redirectResponseCodes = [ 301 , 302 , 303 , 307 , 308 ] ;
6
+ const METHOD_CHANGING_REDIRECTS = [ 301 , 302 , 303 ] ;
7
+
8
+ const saveCookies = ( url , headers ) => {
9
+ if ( headers [ 'set-cookie' ] ) {
10
+ let setCookieHeaders = Array . isArray ( headers [ 'set-cookie' ] )
11
+ ? headers [ 'set-cookie' ]
12
+ : [ headers [ 'set-cookie' ] ] ;
13
+ for ( let setCookieHeader of setCookieHeaders ) {
14
+ if ( typeof setCookieHeader === 'string' && setCookieHeader . length ) {
15
+ addCookieToJar ( setCookieHeader , url ) ;
16
+ }
17
+ }
18
+ }
19
+ } ;
20
+
21
+ const createRedirectConfig = ( error , redirectUrl ) => {
22
+ const requestConfig = {
23
+ ...error . config ,
24
+ url : redirectUrl ,
25
+ headers : { ...error . config . headers }
26
+ } ;
27
+
28
+ const statusCode = error . response . status ;
29
+ const originalMethod = ( error . config . method || 'get' ) . toLowerCase ( ) ;
30
+
31
+ // For 301, 302, 303: change method to GET unless it was HEAD
32
+ if ( METHOD_CHANGING_REDIRECTS . includes ( statusCode ) && originalMethod !== 'head' ) {
33
+ requestConfig . method = 'get' ;
34
+ requestConfig . data = undefined ;
35
+
36
+ // Clean up headers that are no longer relevant
37
+ delete requestConfig . headers [ 'content-length' ] ;
38
+ delete requestConfig . headers [ 'Content-Length' ] ;
39
+ delete requestConfig . headers [ 'content-type' ] ;
40
+ delete requestConfig . headers [ 'Content-Type' ] ;
41
+ }
42
+
43
+ return requestConfig ;
44
+ } ;
3
45
4
46
/**
5
47
* Function that configures axios with timing interceptors
6
48
* Important to note here that the timings are not completely accurate.
7
49
* @see https://github.com/axios/axios/issues/695
8
50
* @returns {axios.AxiosInstance }
9
51
*/
10
- function makeAxiosInstance ( ) {
52
+ function makeAxiosInstance ( { requestMaxRedirects = 5 , disableCookies } = { } ) {
53
+ let redirectCount = 0 ;
54
+
11
55
/** @type {axios.AxiosInstance } */
12
56
const instance = axios . create ( {
13
57
proxy : false ,
58
+ maxRedirects : 0 ,
14
59
headers : {
15
60
"User-Agent" : `bruno-runtime/${ CLI_VERSION } `
16
61
}
17
62
} ) ;
18
63
19
64
instance . interceptors . request . use ( ( config ) => {
20
65
config . headers [ 'request-start-time' ] = Date . now ( ) ;
66
+
67
+ // Add cookies to request if available and not disabled
68
+ if ( ! disableCookies ) {
69
+ const cookieString = getCookieStringForUrl ( config . url ) ;
70
+ if ( cookieString && typeof cookieString === 'string' && cookieString . length ) {
71
+ config . headers [ 'cookie' ] = cookieString ;
72
+ }
73
+ }
74
+
21
75
return config ;
22
76
} ) ;
23
77
@@ -26,13 +80,51 @@ function makeAxiosInstance() {
26
80
const end = Date . now ( ) ;
27
81
const start = response . config . headers [ 'request-start-time' ] ;
28
82
response . headers [ 'request-duration' ] = end - start ;
83
+ redirectCount = 0 ;
84
+
29
85
return response ;
30
86
} ,
31
87
( error ) => {
32
88
if ( error . response ) {
33
89
const end = Date . now ( ) ;
34
90
const start = error . config . headers [ 'request-start-time' ] ;
35
91
error . response . headers [ 'request-duration' ] = end - start ;
92
+
93
+ if ( redirectResponseCodes . includes ( error . response . status ) ) {
94
+ if ( redirectCount >= requestMaxRedirects ) {
95
+ const err = new Error ( `Maximum redirects (${ requestMaxRedirects } ) exceeded` ) ;
96
+ err . originalError = error ;
97
+ return Promise . reject ( err ) ;
98
+ }
99
+
100
+ const locationHeader = error . response . headers . location ;
101
+ if ( ! locationHeader ) {
102
+ return Promise . reject ( new Error ( 'Redirect location header missing' ) ) ;
103
+ }
104
+
105
+ redirectCount ++ ;
106
+ let redirectUrl = locationHeader ;
107
+
108
+ if ( ! locationHeader . match ( / ^ h t t p s ? : \/ \/ / i) ) {
109
+ const URL = require ( 'url' ) ;
110
+ redirectUrl = URL . resolve ( error . config . url , locationHeader ) ;
111
+ }
112
+
113
+ if ( ! disableCookies ) {
114
+ saveCookies ( redirectUrl , error . response . headers ) ;
115
+ }
116
+
117
+ const requestConfig = createRedirectConfig ( error , redirectUrl ) ;
118
+
119
+ if ( ! disableCookies ) {
120
+ const cookieString = getCookieStringForUrl ( redirectUrl ) ;
121
+ if ( cookieString && typeof cookieString === 'string' && cookieString . length ) {
122
+ requestConfig . headers [ 'cookie' ] = cookieString ;
123
+ }
124
+ }
125
+
126
+ return instance ( requestConfig ) ;
127
+ }
36
128
}
37
129
return Promise . reject ( error ) ;
38
130
}
0 commit comments