From 4d1a55134940031a1e0ff2392ab0b08c186166f0 Mon Sep 17 00:00:00 2001 From: Daniel Bankhead Date: Wed, 31 Jan 2024 15:49:37 -0800 Subject: [PATCH] fix: Do Not Mutate Config for Redacted Retries (#597) * feat: Lazily load `https-proxy-agent` * feat: Lazily load `https-proxy-agent` * fix: Do Not Mutate Config for Redacted Retries * fix: Do Not Mutate Config for Redacted Retries * chore: remove note * chore: Remove extraneous --- src/common.ts | 33 ++++++++++++++++----------------- src/gaxios.ts | 9 ++++++++- test/test.getch.ts | 9 ++++++++- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/common.ts b/src/common.ts index de944c7..07f52f5 100644 --- a/src/common.ts +++ b/src/common.ts @@ -15,6 +15,7 @@ import {Agent} from 'http'; import {URL} from 'url'; import {pkg} from './util'; +import extend from 'extend'; /** * Support `instanceof` operator for `GaxiosError`s in different versions of this library. @@ -81,9 +82,19 @@ export class GaxiosError extends Error { ) { super(message); + // deep-copy config as we do not want to mutate + // the existing config for future retries/use + this.config = extend(true, {}, config); + if (this.response) { + this.response.config = extend(true, {}, this.response.config); + } + if (this.response) { try { - this.response.data = translateData(config.responseType, response?.data); + this.response.data = translateData( + this.config.responseType, + this.response?.data + ); } catch { // best effort - don't throw an error within an error // we could set `this.response.config.responseType = 'unknown'`, but @@ -98,22 +109,10 @@ export class GaxiosError extends Error { } if (config.errorRedactor) { - const errorRedactor = config.errorRedactor; - - // shallow-copy config for redaction as we do not want - // future requests to have redacted information - this.config = {...config}; - if (this.response) { - // copy response's config, as it may be recursively redacted - this.response = {...this.response, config: {...this.response.config}}; - } - - const results = errorRedactor({config, response}); - this.config = {...config, ...results.config}; - - if (this.response) { - this.response = {...this.response, ...results.response, config}; - } + config.errorRedactor({ + config: this.config, + response: this.response, + }); } } } diff --git a/src/gaxios.ts b/src/gaxios.ts index 09f7eb3..89917df 100644 --- a/src/gaxios.ts +++ b/src/gaxios.ts @@ -73,8 +73,10 @@ function loadProxy() { if (proxy) { HttpsProxyAgent = httpsProxyAgent; } + return proxy; } + loadProxy(); function skipProxy(url: string) { @@ -186,7 +188,12 @@ export class Gaxios { if (shouldRetry && config) { err.config.retryConfig!.currentRetryAttempt = config.retryConfig!.currentRetryAttempt; - return this._request(err.config); + + // The error's config could be redacted - therefore we only want to + // copy the retry state over to the existing config + opts.retryConfig = err.config?.retryConfig; + + return this._request(opts); } throw err; } diff --git a/test/test.getch.ts b/test/test.getch.ts index b466ae0..5bb2f61 100644 --- a/test/test.getch.ts +++ b/test/test.getch.ts @@ -713,6 +713,7 @@ describe('🎏 data handling', () => { body: 'grant_type=somesensitivedata&assertion=somesensitivedata', }; + // simulate JSON response const responseHeaders = { ...config.headers, 'content-type': 'application/json', @@ -725,9 +726,14 @@ describe('🎏 data handling', () => { .reply(404, response, responseHeaders); const instance = new Gaxios(JSON.parse(JSON.stringify(config))); + const requestConfig: GaxiosOptions = { + url: customURL.toString(), + method: 'POST', + }; + const requestConfigCopy = JSON.parse(JSON.stringify({...requestConfig})); try { - await instance.request({url: customURL.toString(), method: 'POST'}); + await instance.request(requestConfig); throw new Error('Expected a GaxiosError'); } catch (e) { @@ -735,6 +741,7 @@ describe('🎏 data handling', () => { // config should not be mutated assert.deepStrictEqual(instance.defaults, config); + assert.deepStrictEqual(requestConfig, requestConfigCopy); assert.notStrictEqual(e.config, config); // config redactions - headers