Skip to content

Commit 23aeb28

Browse files
committed
feat: allow sending a custom proxy timeout error
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.
1 parent 9b96cd7 commit 23aeb28

File tree

3 files changed

+42
-1
lines changed

3 files changed

+42
-1
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ proxyServer.listen(8015);
374374
```
375375
* **headers**: object with extra headers to be added to target requests.
376376
* **proxyTimeout**: timeout (in millis) for outgoing proxy requests
377+
* **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.
377378
* **timeout**: timeout (in millis) for incoming requests
378379
* **followRedirects**: true/false, Default: false - specify whether you want to follow redirects
379380
* **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

lib/http-proxy/passes/web-incoming.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,12 @@ module.exports = {
138138
// show an error page at the initial request
139139
if(options.proxyTimeout) {
140140
proxyReq.setTimeout(options.proxyTimeout, function() {
141-
proxyReq.abort();
141+
if (options.proxyTimeoutCustomError) {
142+
var timeoutError = new Error('The proxy request timed out');
143+
timeoutError.code = 'ETIMEDOUT';
144+
return proxyReq.destroy(timeoutError);
145+
}
146+
proxyReq.destroy();
142147
});
143148
}
144149

test/lib-http-proxy-passes-web-incoming-test.js

+35
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,41 @@ describe('#createProxyServer.web() using own http server', function () {
287287
}, function() {}).end();
288288
});
289289

290+
it('should proxy the request with custom timeout errors (proxyTimeoutCustomError)', function(done) {
291+
var proxy = httpProxy.createProxyServer({
292+
target: 'http://127.0.0.1:45002',
293+
proxyTimeout: 100,
294+
proxyTimeoutCustomError: true
295+
});
296+
297+
require('net').createServer().listen(45002);
298+
299+
var proxyServer = http.createServer(requestHandler);
300+
301+
var started = new Date().getTime();
302+
function requestHandler(req, res) {
303+
proxy.once('error', function (err, errReq, errRes) {
304+
proxyServer.close();
305+
expect(err).to.be.an(Error);
306+
expect(errReq).to.be.equal(req);
307+
expect(errRes).to.be.equal(res);
308+
expect(new Date().getTime() - started).to.be.greaterThan(99);
309+
expect(err.code).to.be('ETIMEDOUT');
310+
done();
311+
});
312+
313+
proxy.web(req, res);
314+
}
315+
316+
proxyServer.listen('8087');
317+
318+
http.request({
319+
hostname: '127.0.0.1',
320+
port: '8087',
321+
method: 'GET',
322+
}, function() {}).end();
323+
});
324+
290325
it('should proxy the request and handle timeout error', function(done) {
291326
var proxy = httpProxy.createProxyServer({
292327
target: 'http://127.0.0.1:45001',

0 commit comments

Comments
 (0)