From f5840993e6dbebf9e3959556ce6012c6e8140048 Mon Sep 17 00:00:00 2001 From: Dave Cardwell Date: Tue, 26 Apr 2022 17:45:46 +0000 Subject: [PATCH 1/3] feat: pass response status & request URL to beforeRedirect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds the originally requested URL and the redirect response’s HTTP status code to the response details passed to `beforeRedirect`. --- README.md | 5 +++- index.js | 6 ++++- package-lock.json | 1 + test/test.js | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ea618ab..e41384b 100644 --- a/README.md +++ b/README.md @@ -63,10 +63,13 @@ const { http, https } = require('follow-redirects'); const options = url.parse('http://bit.ly/900913'); options.maxRedirects = 10; -options.beforeRedirect = (options, { headers }) => { +options.beforeRedirect = (options, { headers, statusCode, requestUrl }) => { // Use this to adjust the request options upon redirecting, // to inspect the latest response headers, // or to cancel the request by throwing an error + // headers = the redirect response headers + // statusCode = the redirect response code (eg. 301, 307, etc.) + // requestUrl = the requested URL that resulted in a redirect if (options.hostname === "example.com") { options.auth = "user:password"; } diff --git a/index.js b/index.js index a5b28d9..5ff7532 100644 --- a/index.js +++ b/index.js @@ -414,7 +414,11 @@ RedirectableRequest.prototype._processResponse = function (response) { // Evaluate the beforeRedirect callback if (typeof this._options.beforeRedirect === "function") { - var responseDetails = { headers: response.headers }; + var responseDetails = { + headers: response.headers, + statusCode: statusCode, + requestUrl: currentUrl, + }; try { this._options.beforeRedirect.call(null, this._options, responseDetails); } diff --git a/package-lock.json b/package-lock.json index 68be43b..0bcde7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "follow-redirects", "version": "1.14.9", "funding": [ { diff --git a/test/test.js b/test/test.js index 4e3ffc9..5e96924 100644 --- a/test/test.js +++ b/test/test.js @@ -1581,6 +1581,67 @@ describe("follow-redirects", function () { assert.equal(body[header.toLowerCase()], undefined); }); }); + + it("passes the redirect status code to beforeRedirect", function () { + app.get("/a", redirectsTo("/b")); + app.get("/b", redirectsTo("/c", 301)); + app.get("/c", redirectsTo("/d", 302)); + app.get("/d", redirectsTo("/e", 303)); + app.get("/e", redirectsTo("/f", 307)); + app.get("/f", redirectsTo("/g", 308)); + app.get("/g", sendsJson({ a: "b" })); + + const statusCodes = []; + + return server.start(app) + .then(asPromise(function (resolve, reject) { + var options = { + host: "localhost", + port: 3600, + path: "/a", + method: "GET", + beforeRedirect: function (_, response) { + statusCodes.push(response.statusCode); + }, + }; + http.get(options, concatJson(resolve, reject)).on("error", reject); + })) + .then(function (res) { + assert.deepEqual(res.responseUrl, "http://localhost:3600/g"); + assert.deepEqual(res.parsedJson, { a: "b" }); + assert.deepEqual(statusCodes, [302, 301, 302, 303, 307, 308]); + }); + }); + + it("passes the original request URL to beforeRedirect", function () { + app.get("/a", redirectsTo("/b")); + app.get("/b", redirectsTo("/c")); + app.get("/c", sendsJson({ a: "b" })); + + const urlChain = []; + + return server.start(app) + .then(asPromise(function (resolve, reject) { + var options = { + host: "localhost", + port: 3600, + path: "/a", + method: "GET", + beforeRedirect: function (_, response) { + urlChain.push(response.requestUrl); + }, + }; + http.get(options, concatJson(resolve, reject)).on("error", reject); + })) + .then(function (res) { + assert.deepEqual(res.responseUrl, "http://localhost:3600/c"); + assert.deepEqual(res.parsedJson, { a: "b" }); + assert.deepEqual(urlChain, [ + "http://localhost:3600/a", + "http://localhost:3600/b", + ]); + }); + }); }); describe("when the followRedirects option is set to false", function () { From 1aa5e3c3d0b37ac83b78c9971177700ae0de2c72 Mon Sep 17 00:00:00 2001 From: Dave Cardwell Date: Tue, 26 Apr 2022 18:31:58 +0000 Subject: [PATCH 2/3] feat: pass the request method to beforeRedirect --- README.md | 4 +++- index.js | 2 ++ test/test.js | 28 ++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e41384b..15f3419 100644 --- a/README.md +++ b/README.md @@ -63,12 +63,14 @@ const { http, https } = require('follow-redirects'); const options = url.parse('http://bit.ly/900913'); options.maxRedirects = 10; -options.beforeRedirect = (options, { headers, statusCode, requestUrl }) => { +options.beforeRedirect = (options, { headers, statusCode, requestMethod, requestUrl }) => { // Use this to adjust the request options upon redirecting, // to inspect the latest response headers, // or to cancel the request by throwing an error + // headers = the redirect response headers // statusCode = the redirect response code (eg. 301, 307, etc.) + // requestMethod = the request method that resulted in a redirect // requestUrl = the requested URL that resulted in a redirect if (options.hostname === "example.com") { options.auth = "user:password"; diff --git a/index.js b/index.js index 5ff7532..0201acd 100644 --- a/index.js +++ b/index.js @@ -366,6 +366,7 @@ RedirectableRequest.prototype._processResponse = function (response) { // care for methods not known to be safe, […] // RFC7231§6.4.2–3: For historical reasons, a user agent MAY change // the request method from POST to GET for the subsequent request. + var originalRequestMethod = this._options.method; if ((statusCode === 301 || statusCode === 302) && this._options.method === "POST" || // RFC7231§6.4.4: The 303 (See Other) status code indicates that // the server is redirecting the user agent to a different resource […] @@ -417,6 +418,7 @@ RedirectableRequest.prototype._processResponse = function (response) { var responseDetails = { headers: response.headers, statusCode: statusCode, + requestMethod: originalRequestMethod, requestUrl: currentUrl, }; try { diff --git a/test/test.js b/test/test.js index 5e96924..3b6ac8b 100644 --- a/test/test.js +++ b/test/test.js @@ -1613,6 +1613,34 @@ describe("follow-redirects", function () { }); }); + it("passes the request method to beforeRedirect", function () { + app.post("/a", redirectsTo("/b", 308)); + app.post("/b", redirectsTo("/c", 301)); + app.get("/c", redirectsTo("/d", 301)); + app.get("/d", sendsJson({ a: "b" })); + + const requestMethods = []; + + return server.start(app) + .then(asPromise(function (resolve, reject) { + var options = { + host: "localhost", + port: 3600, + path: "/a", + method: "POST", + beforeRedirect: function (_, response) { + requestMethods.push(response.requestMethod); + }, + }; + http.get(options, concatJson(resolve, reject)).on("error", reject); + })) + .then(function (res) { + assert.deepEqual(res.responseUrl, "http://localhost:3600/d"); + assert.deepEqual(res.parsedJson, { a: "b" }); + assert.deepEqual(requestMethods, ["POST", "POST", "GET"]); + }); + }); + it("passes the original request URL to beforeRedirect", function () { app.get("/a", redirectsTo("/b")); app.get("/b", redirectsTo("/c")); From 0f0e5d690123db86a5dc230057925fc5a6017e25 Mon Sep 17 00:00:00 2001 From: Dave Cardwell Date: Wed, 27 Apr 2022 01:21:34 +0000 Subject: [PATCH 3/3] Refactor around third request-specific param --- README.md | 7 ++++--- index.js | 13 ++++++++----- test/test.js | 8 ++++---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 15f3419..b0b9fa9 100644 --- a/README.md +++ b/README.md @@ -63,15 +63,16 @@ const { http, https } = require('follow-redirects'); const options = url.parse('http://bit.ly/900913'); options.maxRedirects = 10; -options.beforeRedirect = (options, { headers, statusCode, requestMethod, requestUrl }) => { +options.beforeRedirect = (options, { headers, statusCode }, { method, url }) => { // Use this to adjust the request options upon redirecting, // to inspect the latest response headers, // or to cancel the request by throwing an error // headers = the redirect response headers // statusCode = the redirect response code (eg. 301, 307, etc.) - // requestMethod = the request method that resulted in a redirect - // requestUrl = the requested URL that resulted in a redirect + + // method = the request method that resulted in a redirect + // url = the requested URL that resulted in a redirect if (options.hostname === "example.com") { options.auth = "user:password"; } diff --git a/index.js b/index.js index 0201acd..794e7dc 100644 --- a/index.js +++ b/index.js @@ -366,7 +366,7 @@ RedirectableRequest.prototype._processResponse = function (response) { // care for methods not known to be safe, […] // RFC7231§6.4.2–3: For historical reasons, a user agent MAY change // the request method from POST to GET for the subsequent request. - var originalRequestMethod = this._options.method; + var method = this._options.method; if ((statusCode === 301 || statusCode === 302) && this._options.method === "POST" || // RFC7231§6.4.4: The 303 (See Other) status code indicates that // the server is redirecting the user agent to a different resource […] @@ -414,15 +414,18 @@ RedirectableRequest.prototype._processResponse = function (response) { } // Evaluate the beforeRedirect callback - if (typeof this._options.beforeRedirect === "function") { + var beforeRedirect = this._options.beforeRedirect; + if (typeof beforeRedirect === "function") { var responseDetails = { headers: response.headers, statusCode: statusCode, - requestMethod: originalRequestMethod, - requestUrl: currentUrl, + }; + var requestDetails = { + method: method, + url: currentUrl, }; try { - this._options.beforeRedirect.call(null, this._options, responseDetails); + beforeRedirect(this._options, responseDetails, requestDetails); } catch (err) { this.emit("error", err); diff --git a/test/test.js b/test/test.js index 3b6ac8b..ede3e02 100644 --- a/test/test.js +++ b/test/test.js @@ -1628,8 +1628,8 @@ describe("follow-redirects", function () { port: 3600, path: "/a", method: "POST", - beforeRedirect: function (_, response) { - requestMethods.push(response.requestMethod); + beforeRedirect: function (_, __, request) { + requestMethods.push(request.method); }, }; http.get(options, concatJson(resolve, reject)).on("error", reject); @@ -1655,8 +1655,8 @@ describe("follow-redirects", function () { port: 3600, path: "/a", method: "GET", - beforeRedirect: function (_, response) { - urlChain.push(response.requestUrl); + beforeRedirect: function (_, __, request) { + urlChain.push(request.url); }, }; http.get(options, concatJson(resolve, reject)).on("error", reject);