Repository: axios/axios (JavaScript)

Issue:
Using config.beforeRedirect prevents using proxy on redirected requests
#### Describe the bug
The support for `config.beforeRedirect` introduced a regression of https://github.com/axios/axios/issues/3369 when both `config.beforeRedirect` and a proxy are used. Both of them internally set `options.beforeRedirect`. Moreover, `config.beforeRedirect` is not re-applied on subsequent redirections.

#### To Reproduce

Adding the following test in `test/unit/adapters/http.js`
```js
  it('should support beforeRedirect and proxy with redirect', function (done) {
    var requestCount = 0;
    var totalRedirectCount = 5;
    server = http.createServer(function (req, res) {
      requestCount += 1;
      if (requestCount <= totalRedirectCount) {
        res.setHeader('Location', 'http://localhost:4444');
        res.writeHead(302);
      }
      res.end();
    }).listen(4444, function () {
      var proxyUseCount = 0;
      proxy = http.createServer(function (request, response) {
        proxyUseCount += 1;
        var parsed = url.parse(request.url);
        var opts = {
          host: parsed.hostname,
          port: parsed.port,
          path: parsed.path
        };

        http.get(opts, function (res) {
          response.writeHead(res.statusCode, res.headers);
          res.on('data', function (data) {
            response.write(data)
          });
          res.on('end', function () {
            response.end();
          });
        });
      }).listen(4000, function () {
        var configBeforeRedirectCount = 0;
        axios.get('http://localhost:4444/', {
          proxy: {
            host: 'localhost',
            port: 4000
          },
          maxRedirects: totalRedirectCount,
          beforeRedirect: function (options) {
            configBeforeRedirectCount += 1;
          }
        }).then(function (res) {
          assert.equal(totalRedirectCount, configBeforeRedirectCount, 'should invoke config.beforeRedirect option on every redirect');
          assert.equal(totalRedirectCount + 1, proxyUseCount, 'should go through proxy on every redirect');
          done();
        }).catch(done);
      });
    });
  });
```

#### Expected behavior
The test should pass, but currently 'should go through proxy on every redirect' assertion fails.

#### Environment
 - Axios Version `0.27.2`
 - Adapter `HTTP`
 - Browser `N/A`
 - Browser Version `N/A`
 - Node.js Version `N/A`
 - OS: `N/A`
 - Additional Library Versions `N/A`

#### Additional context/Screenshots
I'm working on a small PR to fix this issue.


Discussion:

Patch:
diff --git a/lib/adapters/http.js b/lib/adapters/http.js
index 66aa3f27b1..3a27c7a3c0 100755
--- a/lib/adapters/http.js
+++ b/lib/adapters/http.js
@@ -20,6 +20,15 @@ var isHttps = /https:?/;
 
 var supportedProtocols = [ 'http:', 'https:', 'file:' ];
 
+function dispatchBeforeRedirect(options) {
+  if (options.beforeRedirects.proxy) {
+    options.beforeRedirects.proxy(options);
+  }
+  if (options.beforeRedirects.config) {
+    options.beforeRedirects.config(options);
+  }
+}
+
 /**
  *
  * @param {http.ClientRequestArgs} options
@@ -59,7 +68,7 @@ function setProxy(options, configProxy, location) {
     }
   }
 
-  options.beforeRedirect = function beforeRedirect(redirectOptions) {
+  options.beforeRedirects.proxy = function beforeRedirect(redirectOptions) {
     // Configure proxy for redirected request, passing the original config proxy to apply
     // the exact same logic as if the redirected request was performed by axios directly.
     setProxy(redirectOptions, configProxy, redirectOptions.href);
@@ -190,7 +199,9 @@ module.exports = function httpAdapter(config) {
       headers: headers,
       agents: { http: config.httpAgent, https: config.httpsAgent },
       auth: auth,
-      protocol: protocol
+      protocol: protocol,
+      beforeRedirect: dispatchBeforeRedirect,
+      beforeRedirects: {}
     };
 
     if (config.socketPath) {
@@ -213,7 +224,7 @@ module.exports = function httpAdapter(config) {
         options.maxRedirects = config.maxRedirects;
       }
       if (config.beforeRedirect) {
-        options.beforeRedirect = config.beforeRedirect;
+        options.beforeRedirects.config = config.beforeRedirect;
       }
       transport = isHttpsRequest ? httpsFollow : httpFollow;
     }


Test patch:
diff --git a/test/unit/adapters/http.js b/test/unit/adapters/http.js
index c92e0afd2d..315a3ffd7c 100644
--- a/test/unit/adapters/http.js
+++ b/test/unit/adapters/http.js
@@ -204,7 +204,7 @@ describe('supports http with nodejs', function () {
         assert.equal(res.data, str);
         assert.equal(res.request.path, '/two');
         done();
-      }).catch(done);;
+      }).catch(done);
     });
   });
 
@@ -223,7 +223,7 @@ describe('supports http with nodejs', function () {
         assert.equal(res.status, 302);
         assert.equal(res.headers['location'], '/foo');
         done();
-      }).catch(done);;
+      }).catch(done);
     });
   });
 
@@ -241,7 +241,7 @@ describe('supports http with nodejs', function () {
         assert.equal(error.code, AxiosError.ERR_FR_TOO_MANY_REDIRECTS);
         assert.equal(error.message, 'Maximum number of redirects exceeded');
         done();
-      });
+      }).catch(done);
     });
   });
 
@@ -263,6 +263,56 @@ describe('supports http with nodejs', function () {
       }).catch(function (error) {
         assert.equal(error.message, 'Provided path is not allowed');
         done();
+      }).catch(done);
+    });
+  });
+
+  it('should support beforeRedirect and proxy with redirect', function (done) {
+    var requestCount = 0;
+    var totalRedirectCount = 5;
+    server = http.createServer(function (req, res) {
+      requestCount += 1;
+      if (requestCount <= totalRedirectCount) {
+        res.setHeader('Location', 'http://localhost:4444');
+        res.writeHead(302);
+      }
+      res.end();
+    }).listen(4444, function () {
+      var proxyUseCount = 0;
+      proxy = http.createServer(function (request, response) {
+        proxyUseCount += 1;
+        var parsed = url.parse(request.url);
+        var opts = {
+          host: parsed.hostname,
+          port: parsed.port,
+          path: parsed.path
+        };
+
+        http.get(opts, function (res) {
+          response.writeHead(res.statusCode, res.headers);
+          res.on('data', function (data) {
+            response.write(data)
+          });
+          res.on('end', function () {
+            response.end();
+          });
+        });
+      }).listen(4000, function () {
+        var configBeforeRedirectCount = 0;
+        axios.get('http://localhost:4444/', {
+          proxy: {
+            host: 'localhost',
+            port: 4000
+          },
+          maxRedirects: totalRedirectCount,
+          beforeRedirect: function (options) {
+            configBeforeRedirectCount += 1;
+          }
+        }).then(function (res) {
+          assert.equal(totalRedirectCount, configBeforeRedirectCount, 'should invoke config.beforeRedirect option on every redirect');
+          assert.equal(totalRedirectCount + 1, proxyUseCount, 'should go through proxy on every redirect');
+          done();
+        }).catch(done);
       });
     });
   });