Skip to content

Commit

Permalink
Socket timeout support
Browse files Browse the repository at this point in the history
  • Loading branch information
Eran Hammer committed Feb 19, 2013
1 parent 8d455c1 commit d1a713c
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 29 deletions.
5 changes: 3 additions & 2 deletions lib/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ exports.server = {
// timeout limits

timeout: {
client: 10000, // Determines how long to wait for a client connection to end before erroring out
server: null // server timeout disabled by default
socket: null, // Determines how long before closing request socket. Defaults to node (2 minutes)
client: 10 * 1000, // Determines how long to wait for receiving client payload. Defaults to 10 seconds
server: null // Determines how long to wait for server request processing. Disabled by default
},

// Optional components
Expand Down
1 change: 1 addition & 0 deletions lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ internals.serverSchema = {
relativeTo: T.String()
}).nullOk().allow(false).allow(true),
timeout: T.Object({
socket: T.Number().nullOk().allow(false).allow(true),
client: T.Number().nullOk().allow(false).allow(true),
server: T.Number().nullOk().allow(false).allow(true)
}).nullOk().allow(false).allow(true),
Expand Down
8 changes: 8 additions & 0 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ module.exports = internals.Server = function (/* host, port, options */) {
this.settings.uri = (this.settings.tls ? 'https://' : 'http://') + this.settings.host + ':' + this.settings.port;
}

Utils.assert(this.settings.timeout.server === null || this.settings.timeout.socket === null || this.settings.timeout.server < this.settings.timeout.socket, 'Server timeout must be shorter than socket timeout');
Utils.assert(this.settings.timeout.client === null || this.settings.timeout.socket === null || this.settings.timeout.client < this.settings.timeout.socket, 'Client timeout must be shorter than socket timeout');

// Extensions

this._ext = {
Expand Down Expand Up @@ -210,6 +213,11 @@ internals.Server.prototype._dispatch = function (options) {
// Create request object

var request = new Request(self, req, res, options);
if (req.socket &&
self.settings.timeout.socket !== null) {

req.socket.setTimeout(self.settings.timeout.socket);
}

// Execute onRequest extensions (can change request method and url)

Expand Down
91 changes: 64 additions & 27 deletions test/integration/serverTimeout.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
var Chai = require('chai');
var Http = require('http');
var Stream = require('stream');
var Request = require('request');
var Hapi = require('../helpers');


Expand Down Expand Up @@ -223,19 +224,19 @@ describe('Server Timeout', function () {
});
});

describe('Server and Client timeouts', function () {
describe('Server and Client timeouts', function () {

var timeoutHandler = function (request) {
var timeoutHandler = function (request) {

};

var cachedTimeoutHandler = function (request) {
var cachedTimeoutHandler = function (request) {

var reply = request.reply;
setTimeout(function () {
setTimeout(function () {

reply.bind(request, new Hapi.Response.Text('Cached'));
}, 70);
reply.bind(request, new Hapi.Response.Text('Cached'));
}, 70);
};

var _server = new Hapi.Server('127.0.0.1', 0, { timeout: { server: 50, client: 50 }, cache: { engine: 'memory' } });
Expand All @@ -244,64 +245,100 @@ describe('Server and Client timeouts', function () {
{ method: 'POST', path: '/timeoutcache', config: { handler: cachedTimeoutHandler } }
]);

before(function (done) {
before(function (done) {

_server.start(done);
_server.start(done);
});

it('are returned when both client and server timeouts are the same and the client times out', function (done) {
it('are returned when both client and server timeouts are the same and the client times out', function (done) {

var timer = new Hapi.utils.Timer();
var options = {
var options = {
hostname: '127.0.0.1',
port: _server.settings.port,
path: '/timeout',
method: 'POST'
method: 'POST'
};


var req = Http.request(options, function (res) {
var req = Http.request(options, function (res) {

expect([503, 408]).to.contain(res.statusCode);
expect(timer.elapsed()).to.be.at.least(49);
done();
done();
});

req.write('\n');
setTimeout(function() {
setTimeout(function () {

req.end();
}, 100);
req.end();
}, 100);
});

it('initial long running requests don\'t prevent server timeouts from occuring on future requests', function (done) {
it('initial long running requests don\'t prevent server timeouts from occuring on future requests', function (done) {

var timer = new Hapi.utils.Timer();
var options = {
var options = {
hostname: '127.0.0.1',
port: _server.settings.port,
path: '/timeoutcache',
method: 'POST'
method: 'POST'
};

var req1 = Http.request(options, function (res1) {
var req1 = Http.request(options, function (res1) {

expect([503, 408]).to.contain(res1.statusCode);
expect(timer.elapsed()).to.be.at.least(49);

var req2 = Http.request(options, function (res2) {
var req2 = Http.request(options, function (res2) {

expect(res2.statusCode).to.equal(503);
done();
done();
});

req2.end();
req2.end();
});

req1.write('\n');
setTimeout(function() {
setTimeout(function () {

req1.end();
}, 100);
});
});

describe('Socket timeout', function () {

var server = new Hapi.Server('0.0.0.0', 0, { timeout: { client: 45, socket: 50 } });
server.route({
method: 'GET', path: '/', config: {
handler: function () {

setTimeout(function (request) {

request.reply('too late');
}, 70);
}
}
});

req1.end();
}, 100);
var port = 0;
before(function (done) {

server.start(function () {

port = server.settings.port;
done();
});
});
});

it('closes connection on socket timeout', function (done) {

Request('http://localhost:' + port + '/', function (err, response, body) {

expect(err).to.exist;
expect(err.message).to.equal('socket hang up');
done();
});
});
});

0 comments on commit d1a713c

Please sign in to comment.