Skip to content

Commit b020255

Browse files
authored
Merge pull request #4662 from sanjaikumar-bruno/fix/cli-not-following-redirects
feat: enhance axios instance with redirect handling and cookie management in CLI
2 parents 73b0f09 + 2cd985f commit b020255

File tree

2 files changed

+102
-2
lines changed

2 files changed

+102
-2
lines changed

packages/bruno-cli/src/runner/run-single-request.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,14 @@ const runSingleRequest = async function (
314314
}
315315
}
316316

317+
let requestMaxRedirects = request.maxRedirects
318+
request.maxRedirects = 0
319+
320+
// Set default value for requestMaxRedirects if not explicitly set
321+
if (requestMaxRedirects === undefined) {
322+
requestMaxRedirects = 5; // Default to 5 redirects
323+
}
324+
317325
// Handle OAuth2 authentication
318326
if (request.oauth2) {
319327
try {
@@ -344,7 +352,7 @@ const runSingleRequest = async function (
344352
let response, responseTime;
345353
try {
346354

347-
let axiosInstance = makeAxiosInstance();
355+
let axiosInstance = makeAxiosInstance({ requestMaxRedirects: requestMaxRedirects, disableCookies: options.disableCookies });
348356
if (request.ntlmConfig) {
349357
axiosInstance=NtlmClient(request.ntlmConfig,axiosInstance.defaults)
350358
delete request.ntlmConfig;

packages/bruno-cli/src/utils/axios-instance.js

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,77 @@
11
const axios = require('axios');
22
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+
};
345

446
/**
547
* Function that configures axios with timing interceptors
648
* Important to note here that the timings are not completely accurate.
749
* @see https://github.com/axios/axios/issues/695
850
* @returns {axios.AxiosInstance}
951
*/
10-
function makeAxiosInstance() {
52+
function makeAxiosInstance({ requestMaxRedirects = 5, disableCookies } = {}) {
53+
let redirectCount = 0;
54+
1155
/** @type {axios.AxiosInstance} */
1256
const instance = axios.create({
1357
proxy: false,
58+
maxRedirects: 0,
1459
headers: {
1560
"User-Agent": `bruno-runtime/${CLI_VERSION}`
1661
}
1762
});
1863

1964
instance.interceptors.request.use((config) => {
2065
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+
2175
return config;
2276
});
2377

@@ -26,13 +80,51 @@ function makeAxiosInstance() {
2680
const end = Date.now();
2781
const start = response.config.headers['request-start-time'];
2882
response.headers['request-duration'] = end - start;
83+
redirectCount = 0;
84+
2985
return response;
3086
},
3187
(error) => {
3288
if (error.response) {
3389
const end = Date.now();
3490
const start = error.config.headers['request-start-time'];
3591
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(/^https?:\/\//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+
}
36128
}
37129
return Promise.reject(error);
38130
}

0 commit comments

Comments
 (0)