From 57d2ebd6074a67abe2bd5e7810c8f4ed03d0026f Mon Sep 17 00:00:00 2001 From: "Matt R. Wilson" Date: Mon, 6 Apr 2020 13:18:59 -0600 Subject: [PATCH 1/3] Caseless header comparing in HTTP adapter. It was adding User-Agent and removing Authorization, but only when existing headers had the exact right casing. Node uses caseless logic when managing headers. This was causing some requests to have `User-Agent` appended to the headers object and overriding provided agent strings. Also included is an update to not override the `Content-Length` if it was already defined in the options. It can be desirable to manually specify a content length that does not match the data on hand. Especially for testing. --- lib/adapters/http.js | 15 +++++++++++---- test/unit/adapters/http.js | 39 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/lib/adapters/http.js b/lib/adapters/http.js index 32ef61b988..035d50f9c3 100755 --- a/lib/adapters/http.js +++ b/lib/adapters/http.js @@ -27,11 +27,16 @@ module.exports = function httpAdapter(config) { }; var data = config.data; var headers = config.headers; + var headerNames = {}; + + Object.keys(headers).forEach(function storeLowerName(name) { + headerNames[name.toLowerCase()] = name; + }); // Set User-Agent (required by some servers) // Only set header if it hasn't been set in config // See https://github.com/axios/axios/issues/69 - if (!headers['User-Agent'] && !headers['user-agent']) { + if (!headerNames['user-agent']) { headers['User-Agent'] = 'axios/' + pkg.version; } @@ -50,7 +55,9 @@ module.exports = function httpAdapter(config) { } // Add Content-Length header if data exists - headers['Content-Length'] = data.length; + if (!headerNames['content-length']) { + headers['Content-Length'] = data.length; + } } // HTTP basic authentication @@ -73,8 +80,8 @@ module.exports = function httpAdapter(config) { auth = urlUsername + ':' + urlPassword; } - if (auth) { - delete headers.Authorization; + if (auth && headerNames.authorization) { + delete headers[headerNames.authorization]; } var isHttpsRequest = isHttps.test(protocol); diff --git a/test/unit/adapters/http.js b/test/unit/adapters/http.js index a5c6c74026..0f9afbc6eb 100644 --- a/test/unit/adapters/http.js +++ b/test/unit/adapters/http.js @@ -16,7 +16,7 @@ describe('supports http with nodejs', function () { server = null; } if (proxy) { - proxy.close() + proxy.close(); proxy = null; } if (process.env.http_proxy) { @@ -246,7 +246,7 @@ describe('supports http with nodejs', function () { res.end(req.headers.authorization); }).listen(4444, function () { var auth = { username: 'foo', password: 'bar' }; - var headers = { Authorization: 'Bearer 1234' }; + var headers = { AuThOrIzAtIoN: 'Bearer 1234' }; // wonky casing to ensure caseless comparison axios.get('http://localhost:4444/', { auth: auth, headers: headers }).then(function (res) { var base64 = Buffer.from('foo:bar', 'utf8').toString('base64'); assert.equal(res.data, 'Basic ' + base64); @@ -255,6 +255,41 @@ describe('supports http with nodejs', function () { }); }); + it('should provides a default User-Agent header', function (done) { + server = http.createServer(function (req, res) { + res.end(req.headers['user-agent']); + }).listen(4444, function () { + axios.get('http://localhost:4444/').then(function (res) { + assert.ok(/^axios\/[\d.]+$/.test(res.data), `User-Agent header does not match: ${res.data}`); + done(); + }); + }); + }); + + it('should allow the User-Agent header to be overridden', function (done) { + server = http.createServer(function (req, res) { + res.end(req.headers['user-agent']); + }).listen(4444, function () { + var headers = { 'UsEr-AgEnT': 'foo bar' }; // wonky casing to ensure caseless comparison + axios.get('http://localhost:4444/', { headers }).then(function (res) { + assert.equal(res.data, 'foo bar'); + done(); + }); + }); + }); + + it('should allow the Content-Length header to be overridden', function (done) { + server = http.createServer(function (req, res) { + assert.strictEqual(req.headers['content-length'], '42'); + res.end(); + }).listen(4444, function () { + var headers = { 'CoNtEnT-lEnGtH': '42' }; // wonky casing to ensure caseless comparison + axios.post('http://localhost:4444/', 'foo', { headers }).then(function () { + done(); + }); + }); + }); + it('should support max content length', function (done) { var str = Array(100000).join('ж'); From fac6c3f8e99aa3a4021f22f9a71af085380afe5a Mon Sep 17 00:00:00 2001 From: Jay Date: Sun, 5 Sep 2021 12:59:25 +0200 Subject: [PATCH 2/3] Fix eslint error --- lib/adapters/http.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/adapters/http.js b/lib/adapters/http.js index 56b36ea242..108f0a3058 100755 --- a/lib/adapters/http.js +++ b/lib/adapters/http.js @@ -60,7 +60,6 @@ module.exports = function httpAdapter(config) { // Set User-Agent (required by some servers) // See https://github.com/axios/axios/issues/69 - if ('User-Agent' in headers || 'user-agent' in headers) { // User-Agent is specified; handle case where no UA header is desired if (!headers['User-Agent'] && !headers['user-agent']) { From 88434770e2dc420de1065336a81f5dfa0d18d625 Mon Sep 17 00:00:00 2001 From: "Matt R. Wilson" Date: Mon, 6 Sep 2021 09:51:13 -0600 Subject: [PATCH 3/3] fixup: update state UA logic Play nice with https://github.com/axios/axios/pull/3703 --- lib/adapters/http.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/adapters/http.js b/lib/adapters/http.js index 108f0a3058..9d8120d3bd 100755 --- a/lib/adapters/http.js +++ b/lib/adapters/http.js @@ -60,11 +60,10 @@ module.exports = function httpAdapter(config) { // Set User-Agent (required by some servers) // See https://github.com/axios/axios/issues/69 - if ('User-Agent' in headers || 'user-agent' in headers) { + if ('user-agent' in headerNames) { // User-Agent is specified; handle case where no UA header is desired - if (!headers['User-Agent'] && !headers['user-agent']) { - delete headers['User-Agent']; - delete headers['user-agent']; + if (!headers[headerNames['user-agent']]) { + delete headers[headerNames['user-agent']]; } // Otherwise, use specified value } else {