diff --git a/packages/bruno-cli/package.json b/packages/bruno-cli/package.json index f62f9dc64..22bc16602 100644 --- a/packages/bruno-cli/package.json +++ b/packages/bruno-cli/package.json @@ -37,7 +37,6 @@ "fs-extra": "^10.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", - "json-bigint": "^1.0.0", "lodash": "^4.17.21", "qs": "^6.11.0", "socks-proxy-agent": "^8.0.2", diff --git a/packages/bruno-cli/src/runner/prepare-request.js b/packages/bruno-cli/src/runner/prepare-request.js index de0dbd143..2e312878e 100644 --- a/packages/bruno-cli/src/runner/prepare-request.js +++ b/packages/bruno-cli/src/runner/prepare-request.js @@ -1,5 +1,5 @@ +const { get, each, filter, find, compact } = require('lodash'); const { get, each, filter } = require('lodash'); -var JSONbig = require('json-bigint'); const decomment = require('decomment'); const crypto = require('node:crypto'); const { mergeHeaders, mergeScripts, mergeVars, getTreePathFromCollectionToItem } = require('../utils/collection'); @@ -31,7 +31,8 @@ const prepareRequest = (item = {}, collection = {}) => { method: request.method, url: request.url, headers: headers, - pathParams: request?.params?.filter((param) => param.type === 'path') + pathParams: request?.params?.filter((param) => param.type === 'path'), + responseType: 'arraybuffer' }; const collectionAuth = get(collection, 'root.request.auth'); @@ -96,16 +97,10 @@ const prepareRequest = (item = {}, collection = {}) => { if (!contentTypeDefined) { axiosRequest.headers['content-type'] = 'application/json'; } - let jsonBody; try { - jsonBody = decomment(request?.body?.json); + axiosRequest.data = decomment(request?.body?.json); } catch (error) { - jsonBody = request?.body?.json; - } - try { - axiosRequest.data = JSONbig.parse(jsonBody); - } catch (error) { - axiosRequest.data = jsonBody; + axiosRequest.data = request?.body?.json; } } diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index 35cafb590..dd9d450f0 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -19,7 +19,7 @@ const { makeAxiosInstance } = require('../utils/axios-instance'); const { addAwsV4Interceptor, resolveAwsV4Credentials } = require('./awsv4auth-helper'); const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../utils/proxy-util'); const path = require('path'); -const { createFormData } = require('../utils/common'); +const { createFormData, parseDataFromResponse } = require('../utils/common'); const { getCookieStringForUrl, saveCookies, shouldUseCookies } = require('../utils/cookies'); const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/; @@ -227,6 +227,9 @@ const runSingleRequest = async function ( /** @type {import('axios').AxiosResponse} */ response = await axiosInstance(request); + const { data } = parseDataFromResponse(response, request.__brunoDisableParsingResponseJson); + response.data = data; + // Prevents the duration on leaking to the actual result responseTime = response.headers.get('request-duration'); response.headers.delete('request-duration'); @@ -237,6 +240,8 @@ const runSingleRequest = async function ( } } catch (err) { if (err?.response) { + const { data } = parseDataFromResponse(err?.response); + err.response.data = data; response = err.response; // Prevents the duration on leaking to the actual result diff --git a/packages/bruno-cli/src/utils/common.js b/packages/bruno-cli/src/utils/common.js index 16c2d1a7b..dd45be501 100644 --- a/packages/bruno-cli/src/utils/common.js +++ b/packages/bruno-cli/src/utils/common.js @@ -2,6 +2,7 @@ const fs = require('fs'); const FormData = require('form-data'); const { forOwn } = require('lodash'); const path = require('path'); +const iconv = require('iconv-lite'); const lpad = (str, width) => { let paddedStr = str; @@ -43,9 +44,35 @@ const createFormData = (datas, collectionPath) => { return form; }; +const parseDataFromResponse = (response, disableParsingResponseJson = false) => { + // Parse the charset from content type: https://stackoverflow.com/a/33192813 + const charsetMatch = /charset=([^()<>@,;:"/[\]?.=\s]*)/i.exec(response.headers['content-type'] || ''); + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec#using_exec_with_regexp_literals + const charsetValue = charsetMatch?.[1]; + const dataBuffer = Buffer.from(response.data); + // Overwrite the original data for backwards compatibility + let data; + if (iconv.encodingExists(charsetValue)) { + data = iconv.decode(dataBuffer, charsetValue); + } else { + data = iconv.decode(dataBuffer, 'utf-8'); + } + // Try to parse response to JSON, this can quietly fail + try { + // Filter out ZWNBSP character + // https://gist.github.com/antic183/619f42b559b78028d1fe9e7ae8a1352d + data = data.replace(/^\uFEFF/, ''); + if (!disableParsingResponseJson) { + data = JSON.parse(data); + } + } catch { } + + return { data, dataBuffer }; +}; module.exports = { lpad, rpad, - createFormData + createFormData, + parseDataFromResponse }; diff --git a/packages/bruno-cli/tests/runner/prepare-request.spec.js b/packages/bruno-cli/tests/runner/prepare-request.spec.js index 8d8738af6..37d3e34d3 100644 --- a/packages/bruno-cli/tests/runner/prepare-request.spec.js +++ b/packages/bruno-cli/tests/runner/prepare-request.spec.js @@ -6,14 +6,18 @@ describe('prepare-request: prepareRequest', () => { describe('Decomments request body', () => { it('If request body is valid JSON', async () => { const body = { mode: 'json', json: '{\n"test": "{{someVar}}" // comment\n}' }; - const expected = { test: '{{someVar}}' }; + const expected = `{ +\"test\": \"{{someVar}}\" +}`; const result = prepareRequest({ request: { body } }); expect(result.data).toEqual(expected); }); it('If request body is not valid JSON', async () => { const body = { mode: 'json', json: '{\n"test": {{someVar}} // comment\n}' }; - const expected = '{\n"test": {{someVar}} \n}'; + const expected = `{ +\"test\": {{someVar}} +}`; const result = prepareRequest({ request: { body } }); expect(result.data).toEqual(expected); }); diff --git a/packages/bruno-tests/collection/echo/echo bigint.bru b/packages/bruno-tests/collection/echo/echo bigint.bru deleted file mode 100644 index ef981c723..000000000 --- a/packages/bruno-tests/collection/echo/echo bigint.bru +++ /dev/null @@ -1,42 +0,0 @@ -meta { - name: echo bigint - type: http - seq: 6 -} - -post { - url: {{host}}/api/echo/json - body: json - auth: none -} - -headers { - foo: bar -} - -auth:basic { - username: asd - password: j -} - -auth:bearer { - token: -} - -body:json { - { - "hello": 990531470713421825, - "decimal": 1.0, - "decimal2": 1.00, - "decimal3": 1.00200, - "decimal4": 0.00 - } -} - -assert { - res.status: eq 200 -} - -tests { - // todo: add tests once lossless json echo server is ready -} diff --git a/packages/bruno-tests/collection/echo/echo numbers.bru b/packages/bruno-tests/collection/echo/echo numbers.bru new file mode 100644 index 000000000..8f68fe558 --- /dev/null +++ b/packages/bruno-tests/collection/echo/echo numbers.bru @@ -0,0 +1,44 @@ +meta { + name: echo numbers + type: http + seq: 1 +} + +post { + url: {{echo-host}} + body: json + auth: none +} + +body:json { + { + "integer": 123, + "negativeInteger": -99, + "zero": 0, + "float": 2.718, + "negativeFloat": -1.618, + "largeDouble": 12345.678901234567, + "smallDouble": 9.876e-12, + "booleanTrue": true, + "booleanFalse": false + } +} + +assert { + res.body.integer: eq 123 + res.body.integer: isNumber + res.body.negativeInteger: eq -99 + res.body.negativeInteger: isNumber + res.body.zero: eq 0 + res.body.zero: isNumber + res.body.float: eq 2.718 + res.body.float: isNumber + res.body.negativeFloat: eq -1.618 + res.body.negativeFloat: isNumber + res.body.largeDouble: eq 12345.678901234567 + res.body.largeDouble: isNumber + res.body.smallDouble: eq 9.876e-12 + res.body.smallDouble: isNumber + res.body.booleanTrue: eq true + res.body.booleanFalse: eq false +}