From d1a713c8d9456ae1d652a96711490b9c59a76353 Mon Sep 17 00:00:00 2001 From: Eran Hammer Date: Mon, 18 Feb 2013 19:12:41 -0800 Subject: [PATCH] Socket timeout support --- lib/defaults.js | 5 +- lib/schema.js | 1 + lib/server.js | 8 +++ test/integration/serverTimeout.js | 91 ++++++++++++++++++++++--------- 4 files changed, 76 insertions(+), 29 deletions(-) diff --git a/lib/defaults.js b/lib/defaults.js index 01a93921b..f6b238195 100755 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -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 diff --git a/lib/schema.js b/lib/schema.js index 24216cac3..d1112fef4 100755 --- a/lib/schema.js +++ b/lib/schema.js @@ -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), diff --git a/lib/server.js b/lib/server.js index 2491dff67..9a37c5394 100755 --- a/lib/server.js +++ b/lib/server.js @@ -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 = { @@ -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) diff --git a/test/integration/serverTimeout.js b/test/integration/serverTimeout.js index 8b6289fa5..5c8a386be 100755 --- a/test/integration/serverTimeout.js +++ b/test/integration/serverTimeout.js @@ -3,6 +3,7 @@ var Chai = require('chai'); var Http = require('http'); var Stream = require('stream'); +var Request = require('request'); var Hapi = require('../helpers'); @@ -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' } }); @@ -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(); + }); }); -}); \ No newline at end of file + + 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(); + }); + }); +});