Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions packages/bruno-cli/src/utils/axios-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,11 @@ function makeAxiosInstance({
headers: {}
});

// Set User-Agent manually (using transformRequest to delete headers instead)
instance.defaults.headers.common = {
'User-Agent': `bruno-runtime/${CLI_VERSION}`
};
// Extend common headers with User-Agent rather than replacing the object.
// axios.create() preserves defaults.headers.common = { Accept: 'application/json, text/plain, */*' }.
// Assigning a new object (= { 'User-Agent': ... }) would nuke that default, causing servers that
// rely on content-negotiation to receive requests with no Accept header.
instance.defaults.headers.common['User-Agent'] = `bruno-runtime/${CLI_VERSION}`;
Comment thread
coderabbitai[bot] marked this conversation as resolved.

instance.interceptors.request.use((config) => {
config.headers['request-start-time'] = Date.now();
Expand Down
37 changes: 37 additions & 0 deletions packages/bruno-cli/tests/utils/axios-instance.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const { describe, it, expect } = require('@jest/globals');
const { makeAxiosInstance } = require('../../src/utils/axios-instance');

function createStubAdapter() {
let capturedConfig = null;

const adapter = (config) => {
capturedConfig = config;
return Promise.resolve({ data: {}, status: 200, statusText: 'OK', headers: {}, config });
};

adapter.getConfig = () => capturedConfig;

return adapter;
}

describe('makeAxiosInstance', () => {
it('setting User-Agent does not clobber the axios default Accept header', async () => {
const stubAdapter = createStubAdapter();
const instance = makeAxiosInstance();

await instance({ url: 'https://api.example.com/test', method: 'get', adapter: stubAdapter });

// axios.create() sets Accept by default; assigning a new object to defaults.headers.common
// would nuke it. Guard against that regression.
expect(stubAdapter.getConfig().headers['Accept']).toMatch(/application\/json/);
});

it('sets User-Agent header to bruno-runtime version', async () => {
const stubAdapter = createStubAdapter();
const instance = makeAxiosInstance();

await instance({ url: 'https://api.example.com/test', method: 'get', adapter: stubAdapter });

expect(stubAdapter.getConfig().headers['User-Agent']).toMatch(/^bruno-runtime\//);
});
});
9 changes: 5 additions & 4 deletions packages/bruno-electron/src/ipc/network/axios-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ function makeAxiosInstance({
headers: {}
});

// Set User-Agent manually (using transformRequest to delete headers instead)
instance.defaults.headers.common = {
'User-Agent': `bruno-runtime/${version}`
};
// Extend common headers with User-Agent rather than replacing the object.
// axios.create() preserves defaults.headers.common = { Accept: 'application/json, text/plain, */*' }.
// Assigning a new object (= { 'User-Agent': ... }) would nuke that default, causing servers that
// rely on content-negotiation to receive requests with no Accept header.
instance.defaults.headers.common['User-Agent'] = `bruno-runtime/${version}`;

instance.interceptors.request.use(async (config) => {
const url = URL.parse(config.url);
Expand Down
22 changes: 22 additions & 0 deletions packages/bruno-electron/tests/network/axios-instance.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,28 @@ function createStubAdapter() {
return adapter;
}

describe('axios-instance: default headers', () => {
test('setting User-Agent does not clobber the axios default Accept header', async () => {
const stubAdapter = createStubAdapter();
const instance = makeAxiosInstance();

await instance({ url: 'https://api.example.com/test', method: 'get', adapter: stubAdapter });

// axios.create() sets Accept by default; assigning a new object to defaults.headers.common
// would nuke it. Guard against that regression.
expect(stubAdapter.getConfig().headers['Accept']).toMatch(/application\/json/);
});

test('sets User-Agent header to bruno-runtime version', async () => {
const stubAdapter = createStubAdapter();
const instance = makeAxiosInstance();

await instance({ url: 'https://api.example.com/test', method: 'get', adapter: stubAdapter });

expect(stubAdapter.getConfig().headers['User-Agent']).toMatch(/^bruno-runtime\//);
});
});

describe('axios-instance: DNS lookup behavior (GitHub #7343)', () => {
let axiosInstance;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
meta {
name: echo default request headers
type: http
seq: 14
}

post {
url: {{echo-host}}
body: none
auth: none
}

tests {
test("sends Accept header with application/json by default", function() {
// The echo server reflects request headers back as response headers.
// Verifies that axios's default Accept header is not clobbered when
// setting User-Agent (regression for: defaults.headers.common object replacement).
const accept = res.getHeaders()["accept"];
expect(accept).to.be.a("string");
expect(accept).to.include("application/json");
});

test("sends User-Agent header with bruno-runtime prefix", function() {
const userAgent = res.getHeaders()["user-agent"];
expect(userAgent).to.be.a("string");
expect(userAgent).to.match(/^bruno-runtime\//);
});
}