Merge pull request #5713 from barelyhuman/fix/form-values-seq-5237

fix: reimplement payload serialization for `x-www-form-encoded`
This commit is contained in:
Siddharth Gelera (reaper)
2025-10-09 18:25:28 +05:30
committed by GitHub
parent c2d40fe99f
commit 924bc2e79e
9 changed files with 33 additions and 48 deletions

View File

@@ -80,12 +80,11 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
}
}
} 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) {}
if (request.data && Array.isArray(request.data)) {
request.data = request.data.map((d) => ({
...d,
value: _interpolate(d?.value)
}));
}
} else if (contentType === 'multipart/form-data') {
if (Array.isArray(request?.data) && !(request.data instanceof FormData)) {

View File

@@ -6,7 +6,6 @@ const decomment = require('decomment');
const crypto = require('node:crypto');
const fs = require('node:fs');
const { mergeHeaders, mergeScripts, mergeVars, mergeAuth, getTreePathFromCollectionToItem } = require('../utils/collection');
const { buildFormUrlEncodedPayload } = require('../utils/form-data');
const path = require('node:path');
const { isLargeFile } = require('../utils/filesystem');
const { getFormattedOauth2Credentials } = require('../utils/oauth2');
@@ -356,7 +355,7 @@ const prepareRequest = async (item = {}, collection = {}) => {
axiosRequest.headers['content-type'] = 'application/x-www-form-urlencoded';
}
const enabledParams = filter(request.body.formUrlEncoded, (p) => p.enabled);
axiosRequest.data = buildFormUrlEncodedPayload(enabledParams);
axiosRequest.data = enabledParams;
}
if (request.body.mode === 'multipartForm') {

View File

@@ -19,7 +19,7 @@ const { shouldUseProxy, PatchedHttpsProxyAgent, getSystemProxyEnvVariables } = r
const path = require('path');
const { parseDataFromResponse } = require('../utils/common');
const { getCookieStringForUrl, saveCookies } = require('../utils/cookies');
const { createFormData } = require('../utils/form-data');
const { createFormData, buildFormUrlEncodedPayload } = require('../utils/form-data');
const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/;
const { NtlmClient } = require('axios-ntlm');
const { addDigestInterceptor } = require('@usebruno/requests');
@@ -333,7 +333,7 @@ const runSingleRequest = async function (
name => name.toLowerCase() === 'content-type'
);
if (contentTypeHeader && request.headers[contentTypeHeader] === 'application/x-www-form-urlencoded') {
request.data = qs.stringify(request.data, { arrayFormat: 'repeat' });
request.data = buildFormUrlEncodedPayload(request.data);
}
if (contentTypeHeader && request.headers[contentTypeHeader] === 'multipart/form-data') {
@@ -406,7 +406,7 @@ const runSingleRequest = async function (
});
if (request.ntlmConfig) {
axiosInstance=NtlmClient(request.ntlmConfig,axiosInstance.defaults)
axiosInstance = NtlmClient(request.ntlmConfig, axiosInstance.defaults);
delete request.ntlmConfig;
}

View File

@@ -5,20 +5,14 @@ const path = require('path');
/**
* @param {Array.<object>} params The request body Array
* @returns {object} Returns an obj with repeating key as an array of values
* {item: 2, item: 3, item1: 4} becomes {item: [2,3], item1: 4}
* @returns {string} Returns a order respecting standard compliant string of form encoded values
*/
const buildFormUrlEncodedPayload = (params) => {
return params.reduce((acc, p) => {
if (!acc[p.name]) {
acc[p.name] = p.value;
} else if (Array.isArray(acc[p.name])) {
acc[p.name].push(p.value);
} else {
acc[p.name] = [acc[p.name], p.value];
}
return acc;
}, {});
const resultParams = new URLSearchParams();
for (const param of params) {
resultParams.append(param.name, param.value);
}
return resultParams.toString();
};

View File

@@ -23,7 +23,7 @@ const { cancelTokens, saveCancelToken, deleteCancelToken } = require('../../util
const { uuid, safeStringifyJSON, safeParseJSON, parseDataFromResponse, parseDataFromRequest } = require('../../utils/common');
const { chooseFileToSave, writeBinaryFile, writeFile } = require('../../utils/filesystem');
const { addCookieToJar, getDomainsWithCookies, getCookieStringForUrl } = require('../../utils/cookies');
const { createFormData } = require('../../utils/form-data');
const { createFormData, buildFormUrlEncodedPayload } = require('../../utils/form-data');
const { findItemInCollectionByPathname, sortFolder, getAllRequestsInFolderRecursively, getEnvVars, getTreePathFromCollectionToItem, mergeVars, sortByNameThenSequence } = require('../../utils/collection');
const { getOAuth2TokenUsingAuthorizationCode, getOAuth2TokenUsingClientCredentials, getOAuth2TokenUsingPasswordCredentials, getOAuth2TokenUsingImplicitGrant, updateCollectionOauth2Credentials } = require('../../utils/oauth2');
const { preferencesUtil } = require('../../store/preferences');
@@ -424,7 +424,7 @@ const registerNetworkIpc = (mainWindow) => {
// stringify the request url encoded params
if (request.headers['content-type'] === 'application/x-www-form-urlencoded') {
request.data = qs.stringify(request.data, { arrayFormat: 'repeat' });
request.data = buildFormUrlEncodedPayload(request.data);
}
if (request.headers['content-type'] === 'multipart/form-data') {

View File

@@ -104,12 +104,11 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
} 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) {}
if (request.data && Array.isArray(request.data)) {
request.data = request.data.map((d) => ({
...d,
value: _interpolate(d?.value)
}));
}
} else if (contentType === 'multipart/form-data') {
if (Array.isArray(request?.data) && !(request.data instanceof FormData)) {

View File

@@ -422,7 +422,7 @@ const prepareRequest = async (item, collection = {}, abortController) => {
axiosRequest.headers['content-type'] = 'application/x-www-form-urlencoded';
}
const enabledParams = filter(request.body.formUrlEncoded, (p) => p.enabled);
axiosRequest.data = buildFormUrlEncodedPayload(enabledParams);
axiosRequest.data = enabledParams;
}
if (request.body.mode === 'multipartForm') {

View File

@@ -5,20 +5,14 @@ const path = require('path');
/**
* @param {Array.<object>} params The request body Array
* @returns {object} Returns an obj with repeating key as an array of values
* {item: 2, item: 3, item1: 4} becomes {item: [2,3], item1: 4}
* @returns {string} Returns a order respecting standard compliant string of form encoded values
*/
const buildFormUrlEncodedPayload = (params) => {
return params.reduce((acc, p) => {
if (!acc[p.name]) {
acc[p.name] = p.value;
} else if (Array.isArray(acc[p.name])) {
acc[p.name].push(p.value);
} else {
acc[p.name] = [acc[p.name], p.value];
}
return acc;
}, {});
const resultParams = new URLSearchParams();
for (const param of params) {
resultParams.append(param.name, param.value);
}
return resultParams.toString();
};

View File

@@ -23,7 +23,7 @@ describe('prepare-request: prepareRequest', () => {
it('should handle single key-value pair', () => {
const requestObj = [{ name: 'item', value: 2 }];
const expected = { item: 2 };
const expected = 'item=2';
const result = buildFormUrlEncodedPayload(requestObj);
expect(result).toEqual(expected);
});
@@ -33,7 +33,7 @@ describe('prepare-request: prepareRequest', () => {
{ name: 'item1', value: 2 },
{ name: 'item2', value: 3 }
];
const expected = { item1: 2, item2: 3 };
const expected = 'item1=2&item2=3';
const result = buildFormUrlEncodedPayload(requestObj);
expect(result).toEqual(expected);
});
@@ -43,7 +43,7 @@ describe('prepare-request: prepareRequest', () => {
{ name: 'item', value: 2 },
{ name: 'item', value: 3 }
];
const expected = { item: [2, 3] };
const expected = 'item=2&item=3';
const result = buildFormUrlEncodedPayload(requestObj);
expect(result).toEqual(expected);
});
@@ -54,7 +54,7 @@ describe('prepare-request: prepareRequest', () => {
{ name: 'item2', value: 3 },
{ name: 'item1', value: 4 }
];
const expected = { item1: [2, 4], item2: 3 };
const expected = 'item1=2&item2=3&item1=4';
const result = buildFormUrlEncodedPayload(requestObj);
expect(result).toEqual(expected);
});