Skip to content
This repository was archived by the owner on Aug 20, 2024. It is now read-only.

Commit 5fe48ae

Browse files
author
Nicholas Simmons
authored
Merge pull request #26 from jgodson/master
Add connectTimeout option
2 parents cf1e799 + 77c7191 commit 5fe48ae

File tree

6 files changed

+140
-5
lines changed

6 files changed

+140
-5
lines changed

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,11 +292,24 @@ app.use(proxy('httpbin.org', {
292292
}));
293293
```
294294

295+
#### connectTimeout
296+
297+
By default, node does not express a timeout on connections.
298+
Use connectTimeout option to impose a specific timeout on the inital connection. (`connect` for http requests and `secureConnect` for https)
299+
This is useful if there are dns, network issues, or if you are uncertain if the destination is reachable.
300+
Timed-out requests will respond with 504 status code and a X-Timeout-Reason header.
301+
302+
```js
303+
app.use(proxy('httpbin.org', {
304+
connectTimeout: 2000 // in milliseconds, two seconds
305+
}));
306+
```
307+
295308

296309
#### timeout
297310

298311
By default, node does not express a timeout on connections.
299-
Use timeout option to impose a specific timeout.
312+
Use timeout option to impose a specific timeout. This includes the time taken to make the connection and can be used with or without `connectTimeout`.
300313
Timed-out requests will respond with 504 status code and a X-Timeout-Reason header.
301314

302315
```js

app/steps/sendProxyRequest.js

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,42 @@ function sendProxyRequest(Container) {
2020
});
2121
rsp.on('error', reject);
2222
});
23+
var abortRequest = function() {
24+
proxyReq.abort();
25+
};
26+
var timeoutDuration = null;
2327

2428
proxyReq.on('socket', function(socket) {
25-
if (options.timeout) {
26-
socket.setTimeout(options.timeout, function() {
27-
proxyReq.abort();
29+
var isSecure = Container.proxy.isSecure;
30+
var timeout = options.timeout;
31+
var connectTimeout = options.connectTimeout;
32+
// "secureConnect" includes time taken for "lookup" (dns), "connect" and "ready" events, as well as tls handshake.
33+
var eventListener = isSecure ? 'secureConnect' : 'connect';
34+
35+
if (connectTimeout) {
36+
timeoutDuration = connectTimeout;
37+
socket.setTimeout(connectTimeout, abortRequest);
38+
39+
socket.on(eventListener, function() {
40+
if (timeout) {
41+
timeoutDuration = timeout;
42+
socket.setTimeout(timeout, abortRequest);
43+
} else {
44+
// 0 to reset to the default of no timeout for the rest of the request
45+
socket.setTimeout(0);
46+
}
2847
});
48+
} else if (timeout) {
49+
timeoutDuration = timeout;
50+
socket.setTimeout(timeout, abortRequest);
2951
}
3052
});
3153

3254
// TODO: do reject here and handle this later on
3355
proxyReq.on('error', function(err) {
3456
// reject(error);
3557
if (err.code === 'ECONNRESET') {
36-
ctx.set('X-Timout-Reason', 'koa-better-http-proxy timed out your request after ' + options.timeout + 'ms.');
58+
ctx.set('X-Timout-Reason', 'koa-better-http-proxy timed out your request after ' + timeoutDuration + 'ms.');
3759
ctx.set('Content-Type', 'text/plain');
3860
ctx.status = 504;
3961
resolve(Container);

lib/requestOptions.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ function parseHost(Container) {
4444
return {
4545
host: parsed.hostname,
4646
port: parsed.port || (ishttps ? 443 : 80),
47+
isSecure: ishttps,
4748
module: ishttps ? https : http,
4849
};
4950
}

lib/resolveOptions.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ function resolveOptions(options) {
2828
https: options.https,
2929
port: options.port,
3030
reqAsBuffer: options.reqAsBuffer,
31+
connectTimeout: options.connectTimeout,
3132
timeout: options.timeout,
3233
limit: options.limit,
3334
};

test/connectTimeout.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
var Koa = require('koa');
2+
var agent = require('supertest').agent;
3+
var proxy = require('../');
4+
var proxyTarget = require('./support/proxyTarget');
5+
6+
describe('honors connectTimeout option', function() {
7+
'use strict';
8+
9+
var other, http;
10+
beforeEach(function() {
11+
http = new Koa();
12+
other = proxyTarget(8080, 1000, [{
13+
method: 'get',
14+
path: '/',
15+
fn: function(_, res) { res.sendStatus(200); }
16+
}]);
17+
});
18+
19+
afterEach(function() {
20+
other.close();
21+
});
22+
23+
function assertSuccess(server, done) {
24+
agent(server.callback())
25+
.get('/')
26+
.expect(200)
27+
.end(function(err) {
28+
if (err) {
29+
return done(err);
30+
}
31+
done();
32+
});
33+
}
34+
35+
function assertConnectionTimeout(server, time, done) {
36+
agent(server.callback())
37+
.get('/')
38+
.expect(504)
39+
.expect('X-Timout-Reason', 'koa-better-http-proxy timed out your request after ' + time + 'ms.')
40+
.end(function(err) {
41+
if (err) {
42+
return done(err);
43+
}
44+
done();
45+
});
46+
}
47+
48+
describe('when connectTimeout option is set lower than server connect time', function() {
49+
it('should fail with CONNECTION TIMEOUT', function(done) {
50+
http.use(proxy('http://127.0.0.0', {
51+
connectTimeout: 50,
52+
}));
53+
54+
assertConnectionTimeout(http, 50, done);
55+
});
56+
});
57+
58+
describe('when connectTimeout option is set higher than server connect time', function() {
59+
it('should succeed', function(done) {
60+
http.use(proxy('http://localhost:8080', {
61+
connectTimeout: 50,
62+
}));
63+
64+
assertSuccess(http, done);
65+
});
66+
});
67+
68+
describe('when timeout option is also used', function() {
69+
it('should fail with CONNECTION TIMEOUT when timeout is set lower than server response time', function(done) {
70+
http.use(proxy('http://localhost:8080', {
71+
connectTimeout: 100,
72+
timeout: 300,
73+
}));
74+
75+
assertConnectionTimeout(http, 300, done);
76+
});
77+
78+
it('should fail with CONNECTION TIMEOUT based on connectTimeout when a connection cannot be made', function(done) {
79+
http.use(proxy('http://127.0.0.0', {
80+
connectTimeout: 100,
81+
timeout: 300,
82+
}));
83+
84+
assertConnectionTimeout(http, 100, done);
85+
});
86+
87+
it('should succeed when timeout is higher than server response time', function(done) {
88+
http.use(proxy('http://localhost:8080', {
89+
connectTimeout: 100,
90+
timeout: 1200,
91+
}));
92+
93+
assertSuccess(http, done);
94+
});
95+
});
96+
97+
});

types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ declare namespace koaHttpProxy {
1414
preserveReqSession?: boolean,
1515
reqAsBuffer?: boolean,
1616
reqBodyEncoding?: string | null,
17+
connectTimeout?: number,
1718
timeout?: number,
1819
filter?(ctx: koa.Context): boolean,
1920
proxyReqBodyDecorator?(bodyContent: string, ctx: koa.Context): string | Promise<string>,

0 commit comments

Comments
 (0)