From 1d12bebce4d89a19566206c6b0b8392e930b1dc3 Mon Sep 17 00:00:00 2001 From: betawait <40029520+betawait@users.noreply.github.com> Date: Sun, 9 Feb 2025 21:13:30 +0900 Subject: [PATCH] Fix: Allow empty Content-Type when no body (#1693) By default Axios will set the Content-Type for POST/PUT/PATCH requests to "application/x-www-form-urlencoded" if the Content-Type header is not specified. This explicitly sets the content type to "false" when there the body mode is set to "none", and the user has not set an explicit content type themselves. Setting the content type to false directs Axios not to send a Content-Type header. --- .../src/ipc/network/interpolate-vars.js | 72 ++++++++++--------- .../src/ipc/network/prepare-request.js | 7 ++ .../tests/network/interpolate-vars.spec.js | 9 +++ .../tests/network/prepare-request.spec.js | 18 +++++ 4 files changed, 71 insertions(+), 35 deletions(-) diff --git a/packages/bruno-electron/src/ipc/network/interpolate-vars.js b/packages/bruno-electron/src/ipc/network/interpolate-vars.js index 78c5454ed..a7206ec22 100644 --- a/packages/bruno-electron/src/ipc/network/interpolate-vars.js +++ b/packages/bruno-electron/src/ipc/network/interpolate-vars.js @@ -69,45 +69,47 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc const contentType = getContentType(request.headers); - /* - We explicitly avoid interpolating buffer values because the file content is read as a buffer object in raw body mode. - Even if the selected file's content type is JSON, this prevents the buffer object from being interpolated. - */ - if (contentType.includes('json') && !Buffer.isBuffer(request.data)) { - if (typeof request.data === 'string') { - if (request.data.length) { - request.data = _interpolate(request.data, { + if (typeof contentType === 'string') { + /* + We explicitly avoid interpolating buffer values because the file content is read as a buffer object in raw body mode. + Even if the selected file's content type is JSON, this prevents the buffer object from being interpolated. + */ + if (contentType.includes('json') && !Buffer.isBuffer(request.data)) { + if (typeof request.data === 'string') { + if (request.data.length) { + request.data = _interpolate(request.data, { escapeJSONStrings: true }); + } + } else if (typeof request.data === 'object') { + try { + const jsonDoc = JSON.stringify(request.data); + const parsed = _interpolate(jsonDoc, { + escapeJSONStrings: true + }); + request.data = JSON.parse(parsed); + } catch (err) {} } - } else if (typeof request.data === 'object') { - try { - const jsonDoc = JSON.stringify(request.data); - const parsed = _interpolate(jsonDoc, { - escapeJSONStrings: true - }); - request.data = JSON.parse(parsed); - } catch (err) {} + } else if (contentType === 'application/x-www-form-urlencoded') { + if (typeof request.data === 'object') { + try { + forOwn(request?.data, (value, key) => { + request.data[key] = _interpolate(value); + }); + } catch (err) {} + } + } else if (contentType === 'multipart/form-data') { + if (Array.isArray(request?.data) && !(request.data instanceof FormData)) { + try { + request.data = request?.data?.map(d => ({ + ...d, + value: _interpolate(d?.value) + })); + } catch (err) {} + } + } else { + request.data = _interpolate(request.data); } - } else if (contentType === 'application/x-www-form-urlencoded') { - if (typeof request.data === 'object') { - try { - forOwn(request?.data, (value, key) => { - request.data[key] = _interpolate(value); - }); - } catch (err) {} - } - } else if (contentType === 'multipart/form-data') { - if (Array.isArray(request?.data) && !(request.data instanceof FormData)) { - try { - request.data = request?.data?.map(d => ({ - ...d, - value: _interpolate(d?.value) - })); - } catch (err) {} - } - } else { - request.data = _interpolate(request.data); } each(request.pathParams, (param) => { diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js index 749e32a6d..d1c5b4b10 100644 --- a/packages/bruno-electron/src/ipc/network/prepare-request.js +++ b/packages/bruno-electron/src/ipc/network/prepare-request.js @@ -392,6 +392,13 @@ const prepareRequest = async (item, collection = {}, abortController) => { axiosRequest.data = graphqlQuery; } + // if the mode is 'none' then set the content-type header to false. #1693 + if (request.body.mode === 'none') { + if(!contentTypeDefined) { + axiosRequest.headers['content-type'] = false; + } + } + if (request.script) { axiosRequest.script = request.script; } diff --git a/packages/bruno-electron/tests/network/interpolate-vars.spec.js b/packages/bruno-electron/tests/network/interpolate-vars.spec.js index 7a769ea3a..a29029687 100644 --- a/packages/bruno-electron/tests/network/interpolate-vars.spec.js +++ b/packages/bruno-electron/tests/network/interpolate-vars.spec.js @@ -100,4 +100,13 @@ describe('interpolate-vars: interpolateVars', () => { }); }); }); + + describe('Handles content-type header set to false', () => { + it('Should result empty data', async () => { + const request = { method: 'POST', url: 'test', data: undefined, headers: { 'content-type': false } }; + + const result = interpolateVars(request, { 'test.url': 'test.com' }, null, null); + expect(result.data).toEqual(undefined); + }); + }); }); diff --git a/packages/bruno-electron/tests/network/prepare-request.spec.js b/packages/bruno-electron/tests/network/prepare-request.spec.js index 67ed5ea87..6fbd745a7 100644 --- a/packages/bruno-electron/tests/network/prepare-request.spec.js +++ b/packages/bruno-electron/tests/network/prepare-request.spec.js @@ -59,4 +59,22 @@ describe('prepare-request: prepareRequest', () => { expect(result).toEqual(expected); }); }); + + describe.each(['POST', 'PUT', 'PATCH'])('POST request with no body', (method) => { + it('Should set content-type header to false if method is ' + method + ' and there is no data in the body', async () => { + const request = { method: method, url: 'test-domain', body: { mode: 'none' } }; + const result = await prepareRequest({ request, collection: { pathname: '' } }); + expect(result.headers['content-type']).toEqual(false); + }); + it('Should respect the content-type header if explicitly set', async () => { + const request = { + method: method, + url: 'test-domain', + body: { mode: 'none' }, + headers: [{ name: 'content-type', value: 'application/json', enabled: true }] + }; + const result = await prepareRequest({ request, collection: { pathname: '' } }); + expect(result.headers['content-type']).toEqual('application/json'); + }); + }); }); \ No newline at end of file