Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added connect.timeout() middleware #499

Closed
wants to merge 14 commits into from
Closed
51 changes: 51 additions & 0 deletions lib/middleware/timeout.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,51 @@
/*!
* Connect - timeout
* Copyright(c) 2012 Hunter Loftis
* MIT Licensed
*/

/**
* Timeout:
*
* Invokes `socket.setTimeout` to timeout idle requests
* Adds req.clearTimeout() for long-running requests
*
* - `throwError`: throw an error instead of writing a status code
* - `time`: timeout length in ms (default: 10000)
*
* @param {Object} options
* @return {Function}
* @api public
*/

module.exports = function timeout(options) {
options = options || {};
options.time = options.time || 8000;
options.code = options.code || 500;
options.throwError = options.throwError || false;

return function(req, res, next) {

req.socket.removeAllListeners('timeout'); // http socket auto-destroys on timeout
req.socket.setTimeout(options.time, timed_out);

function timed_out() {
if (options.throwError) {
return next(new Error('Timeout ' + req.method + ' ' + req.url));
}
else {
res.writeHead(options.code);
res.end();
}
}

if (!req.socket.clearTimeout) {
req.socket.clearTimeout = function() {
req.socket.setTimeout(0);
};
req.socket.once('drain', req.socket.clearTimeout); // Clear whenever we send anything
}

return next();
};
};
Binary file added test/fixtures/favicon.ico
Binary file not shown.
79 changes: 79 additions & 0 deletions test/timeout.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,79 @@

var connect = require('../');

var fixtures = __dirname + '/fixtures';

var app = connect();

app.use(connect.timeout({
code: 503,
time: 500
}));

app.use(connect['static'](fixtures));

var timeouts;
app.use(function(err, req, res, next) {
timeouts++;
});

app.use(function(req, res, next) {
if (req.url === '/should/timeout') {
// chill and wait for timeout
}
else if (req.url === '/should/not/timeout') {
res.writeHead(200);
res.end();
}
else if (req.url === '/should/interrupt/timeout') {
req.clearTimeout();
setTimeout(function() {
res.writeHead(200);
res.end();
}, 1000); // Wait until after timeout `time`
}
});

// Tests

describe('connect.timeout()', function() {
it('should timeout', function(done) {
app.request()
.get('/should/timeout')
.end(function(res) {
res.statusCode.should.equal(503);
done();
});
});

it('should not timeout', function(done) {
app.request()
.get('/should/not/timeout')
.end(function(res) {
res.statusCode.should.equal(200);
done();
});
});

it('should interrupt timeout', function(done) {
app.request()
.get('/should/interrupt/timeout')
.end(function(res) {
res.statusCode.should.equal(200);
done();
});
});

it('should serve static files without timeout', function(done){
timeouts = 0;
app.request()
.get('/favicon.ico')
.end(function(res) {
setTimeout(function() {
timeouts.should.equal(0);
done();
}, 1000);
});
});

});