From 2f7164fce8894de0f4a1ee0bbee1734c5ca52f86 Mon Sep 17 00:00:00 2001 From: Steven Marusa Date: Mon, 4 Oct 2021 11:35:47 -0400 Subject: [PATCH] - Remove Host header only if redirecting to a new host. - Clarify logic to operate on host instead of hostname to keep/remove Host and Authorization headers. Host header is hostname+port. --- index.js | 21 ++++++++++++++++----- test/test.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 61aab2e..e1cb4f1 100644 --- a/index.js +++ b/index.js @@ -360,9 +360,9 @@ RedirectableRequest.prototype._processResponse = function (response) { removeMatchingHeaders(/^content-/i, this._options.headers); } - // Drop the Host header, as the redirect might lead to a different host - var previousHostName = removeMatchingHeaders(/^host$/i, this._options.headers) || - url.parse(this._currentUrl).hostname; + // Prefer previous host from Host header + var previousHost = getMatchingHeader(/^host$/i, this._options.headers) || + url.parse(this._currentUrl).host; // Create the redirected request var redirectUrl = url.resolve(this._currentUrl, location); @@ -371,8 +371,9 @@ RedirectableRequest.prototype._processResponse = function (response) { var redirectUrlParts = url.parse(redirectUrl); Object.assign(this._options, redirectUrlParts); - // Drop the Authorization header if redirecting to another host - if (redirectUrlParts.hostname !== previousHostName) { + // Drop the Host & Authorization header if redirecting to another host + if (redirectUrlParts.host !== previousHost && url.parse(location).host) { + removeMatchingHeaders(/^host$/i, this._options.headers); removeMatchingHeaders(/^authorization$/i, this._options.headers); } @@ -502,6 +503,16 @@ function urlToOptions(urlObject) { return options; } +function getMatchingHeader(regex, headers) { + var lastValue; + for (var header in headers) { + if (regex.test(header)) { + lastValue = headers[header]; + } + } + return lastValue; +} + function removeMatchingHeaders(regex, headers) { var lastValue; for (var header in headers) { diff --git a/test/test.js b/test/test.js index b7d9289..67906b0 100644 --- a/test/test.js +++ b/test/test.js @@ -1219,7 +1219,7 @@ describe("follow-redirects", function () { }); describe("when redirecting to a different host while the host header is set", function () { - it("uses the new host header", function () { + it("uses the new host header if redirect host is different", function () { app.get("/a", redirectsTo(302, "http://localhost:3600/b")); app.get("/b", function (req, res) { res.write(JSON.stringify(req.headers)); @@ -1242,6 +1242,54 @@ describe("follow-redirects", function () { assert.equal(body.host, "localhost:3600"); }); }); + + it("uses the existing host header if redirect host is the same", function () { + app.get("/a", redirectsTo(302, "http://localhost:3600/b")); + app.get("/b", function (req, res) { + res.write(JSON.stringify(req.headers)); + req.pipe(res); // will invalidate JSON if non-empty + }); + + return server.start(app) + .then(asPromise(function (resolve, reject) { + var opts = url.parse("http://127.0.0.1:3600/a"); + opts.headers = { hOsT: "localhost:3600" }; + http.get(opts, resolve).on("error", reject); + })) + .then(asPromise(function (resolve, reject, res) { + assert.deepEqual(res.statusCode, 200); + assert.deepEqual(res.responseUrl, "http://localhost:3600/b"); + res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject); + })) + .then(function (str) { + var body = JSON.parse(str); + assert.equal(body.host, "localhost:3600"); + }); + }); + + it("uses the existing host header if redirect host is relative", function () { + app.get("/a", redirectsTo(302, "/b")); + app.get("/b", function (req, res) { + res.write(JSON.stringify(req.headers)); + req.pipe(res); // will invalidate JSON if non-empty + }); + + return server.start(app) + .then(asPromise(function (resolve, reject) { + var opts = url.parse("http://127.0.0.1:3600/a"); + opts.headers = { hOsT: "localhost:3600" }; + http.get(opts, resolve).on("error", reject); + })) + .then(asPromise(function (resolve, reject, res) { + assert.deepEqual(res.statusCode, 200); + assert.deepEqual(res.responseUrl, "http://127.0.0.1:3600/b"); + res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject); + })) + .then(function (str) { + var body = JSON.parse(str); + assert.equal(body.host, "localhost:3600"); + }); + }); }); describe("when the client passes an Authorization header", function () { @@ -1278,7 +1326,7 @@ describe("follow-redirects", function () { var opts = url.parse("http://127.0.0.1:3600/a"); opts.headers = { - host: "localhost", + host: "localhost:3600", authorization: "bearer my-token-1234", };