fix: preserve stream-backed file bodies during request interpolation (#7690)

This commit is contained in:
Hritam Shrivastava
2026-05-07 14:05:14 +05:30
committed by GitHub
parent f8bf1460bd
commit 975c638f39
4 changed files with 48 additions and 4 deletions

View File

@@ -2,6 +2,8 @@ const { interpolate } = require('@usebruno/common');
const { each, forOwn, cloneDeep, find } = require('lodash');
const { isFormData } = require('@usebruno/common').utils;
const isBinaryRequestBody = (data) => Buffer.isBuffer(data) || typeof data?.pipe === 'function';
const getContentType = (headers = {}) => {
let contentType = '';
forOwn(headers, (value, key) => {
@@ -80,7 +82,7 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
// Skip body interpolation for GraphQL requests.
if (!isGraphqlRequest) {
if (contentType.includes('json') && !Buffer.isBuffer(request.data)) {
if (contentType.includes('json') && !isBinaryRequestBody(request.data)) {
if (typeof request.data === 'string') {
if (request?.data?.length) {
request.data = _interpolate(request.data, { escapeJSONStrings: true });

View File

@@ -1,6 +1,25 @@
const { describe, it, expect } = require('@jest/globals');
const interpolateVars = require('../../src/runner/interpolate-vars');
describe('interpolate-vars: interpolateVars', () => {
it('keeps stream-backed JSON request bodies intact', () => {
const streamPayload = {
pipe: jest.fn(),
path: '/tmp/allocations.json'
};
const request = {
method: 'POST',
mode: 'file',
url: 'http://api.example/upload',
headers: { 'content-type': 'application/json' },
data: streamPayload
};
const result = interpolateVars(request, { shouldNotApply: 'value' }, null, null);
expect(result.data).toBe(streamPayload);
});
});
describe('interpolate-vars: api key header name sidecar', () => {
it('interpolates apiKeyHeaderName in lockstep with interpolated header keys', () => {
const request = {

View File

@@ -2,6 +2,8 @@ const { interpolate } = require('@usebruno/common');
const { each, forOwn, cloneDeep } = require('lodash');
const { isFormData } = require('@usebruno/common').utils;
const isBinaryRequestBody = (data) => Buffer.isBuffer(data) || typeof data?.pipe === 'function';
const getContentType = (headers = {}) => {
let contentType = '';
forOwn(headers, (value, key) => {
@@ -110,10 +112,11 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
if (typeof contentType === 'string' && !isGraphqlRequest) {
/*
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.
We explicitly avoid interpolating binary payloads because raw file bodies can be represented as
buffers or streams depending on size. Even if the selected file's content type is JSON, the
transport object itself must not be interpolated.
*/
if (contentType.includes('json') && !Buffer.isBuffer(request.data)) {
if (contentType.includes('json') && !isBinaryRequestBody(request.data)) {
if (typeof request.data === 'string') {
if (request.data.length) {
request.data = _interpolate(request.data, {

View File

@@ -426,4 +426,24 @@ describe('interpolate-vars: interpolateVars', () => {
expect(result.data).toContain('--TestBoundary123--');
});
});
describe('File body streaming', () => {
it('keeps stream-backed JSON request bodies intact', () => {
const streamPayload = {
pipe: jest.fn(),
path: '/tmp/allocations.json'
};
const request = {
method: 'POST',
mode: 'file',
url: 'http://api.example/upload',
headers: { 'content-type': 'application/json' },
data: streamPayload
};
const result = interpolateVars(request, { shouldNotApply: 'value' }, null, null);
expect(result.data).toBe(streamPayload);
});
});
});