Skip to content

Commit

Permalink
feat: allow sending a custom proxy timeout error
Browse files Browse the repository at this point in the history
When using `proxyTimeout` it's very difficult to tell the difference
between a regular socket hangup and a timeout because, in both cases, an
`ECONNRESET` error is thrown. Ideally we should be able to identify when
a proxy request has failed because it took too long, this would allow us
to do things like send appropriate `504` status code.

Suddenly throwing a different error would probably be considered a
breaking change because it's possible that users of http-proxy are
relying on the `ECONNRESET` error. I decided to add the custom timeout
error behind a new option for now so that people can opt into using it.

If you set this option:

```js
var proxy = httpProxy.createProxyServer({
  target: 'http://example.com',
  proxyTimeout: 100,
  proxyTimeoutCustomError: true
});
```

Then the error that gets thrown will have a message of `"The proxy
request timed out"` and a code of `ETIMEDOUT` to match Node.js:
https://nodejs.org/api/errors.html#common-system-errors

This allows for custom error handling code like this:

```js
proxy.on('error', function(err, req, res) {
  if (err.code === 'ETIMEDOUT') {
    res.writeHead(504);
  } else {
    res.writeHead(503);
  }
  // ...
});
```

Resolves http-party#1331.
  • Loading branch information
rowanmanning committed Oct 7, 2023
1 parent 9b96cd7 commit 23aeb28
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -374,6 +374,7 @@ proxyServer.listen(8015);
```
* **headers**: object with extra headers to be added to target requests.
* **proxyTimeout**: timeout (in millis) for outgoing proxy requests
* **proxyTimeoutCustomError**: true/false, default: false - specify whether you want to throw a custom `ETIMEDOUT` error when the `proxyTimeout` is reached. If false then the default `ECONNRESET` error will be thrown.
* **timeout**: timeout (in millis) for incoming requests
* **followRedirects**: true/false, Default: false - specify whether you want to follow redirects
* **selfHandleResponse** true/false, if set to true, none of the webOutgoing passes are called and it's your responsibility to appropriately return the response by listening and acting on the `proxyRes` event
Expand Down
7 changes: 6 additions & 1 deletion lib/http-proxy/passes/web-incoming.js
Expand Up @@ -138,7 +138,12 @@ module.exports = {
// show an error page at the initial request
if(options.proxyTimeout) {
proxyReq.setTimeout(options.proxyTimeout, function() {
proxyReq.abort();
if (options.proxyTimeoutCustomError) {
var timeoutError = new Error('The proxy request timed out');
timeoutError.code = 'ETIMEDOUT';
return proxyReq.destroy(timeoutError);
}
proxyReq.destroy();
});
}

Expand Down
35 changes: 35 additions & 0 deletions test/lib-http-proxy-passes-web-incoming-test.js
Expand Up @@ -287,6 +287,41 @@ describe('#createProxyServer.web() using own http server', function () {
}, function() {}).end();
});

it('should proxy the request with custom timeout errors (proxyTimeoutCustomError)', function(done) {
var proxy = httpProxy.createProxyServer({
target: 'http://127.0.0.1:45002',
proxyTimeout: 100,
proxyTimeoutCustomError: true
});

require('net').createServer().listen(45002);

var proxyServer = http.createServer(requestHandler);

var started = new Date().getTime();
function requestHandler(req, res) {
proxy.once('error', function (err, errReq, errRes) {
proxyServer.close();
expect(err).to.be.an(Error);
expect(errReq).to.be.equal(req);
expect(errRes).to.be.equal(res);
expect(new Date().getTime() - started).to.be.greaterThan(99);
expect(err.code).to.be('ETIMEDOUT');
done();
});

proxy.web(req, res);
}

proxyServer.listen('8087');

http.request({
hostname: '127.0.0.1',
port: '8087',
method: 'GET',
}, function() {}).end();
});

it('should proxy the request and handle timeout error', function(done) {
var proxy = httpProxy.createProxyServer({
target: 'http://127.0.0.1:45001',
Expand Down

0 comments on commit 23aeb28

Please sign in to comment.