From cafe7cb4bb131c7118f4ded238740f3b273d6543 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 15 Aug 2014 21:11:55 -0400 Subject: [PATCH] add isFinished function --- HISTORY.md | 1 + README.md | 21 ++++-- index.js | 29 +++++++- test/test.js | 182 +++++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 197 insertions(+), 36 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 4ed26bc..71f08a1 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,7 @@ unreleased ========== + * add `isFinished` function * remove support for non-`res` argument * support both `req` and `res` as arguments * deps: ee-first@1.0.5 diff --git a/README.md b/README.md index 17c659e..2847d86 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,11 @@ $ npm install finished ## API -### finished(res, listener) +```js +var onFinished = require('finished') +``` + +### onFinished(res, listener) Attach a listener to listen for the response to finish. The listener will be invoked only once when the response finished. If the response finished @@ -25,14 +29,12 @@ Listening to the end of a response would be used to close things associated with the response, like open files. ```js -var onFinished = require('finished') - onFinished(res, function (err) { // do something maybe }) ``` -### finished(req, listener) +### onFinished(req, listener) Attach a listener to listen for the request to finish. The listener will be invoked only once when the request finished. If the request finished @@ -43,7 +45,6 @@ after reading the data. ```js var data = '' -var onFinished = require('finished') req.setEncoding('utf8') res.on('data', function (str) { @@ -55,6 +56,16 @@ onFinished(req, function (err) { }) ``` +### onFinished.isFinished(res) + +Determine if `res` is already finished. This would be useful to check and +not even start certain operations if the response has already finished. + +### onFinished.isFinished(req) + +Determine if `req` is already finished. This would be useful to check and +not even start certain operations if the request has already finished. + ### Example The following code ensures that file descriptors are always closed diff --git a/index.js b/index.js index d765482..8418f9c 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,14 @@ * MIT Licensed */ +/** + * Module exports. + */ + +module.exports = finished; +module.exports.isFinished = isFinished; + + /** * Module dependencies. */ @@ -23,16 +31,16 @@ var defer = typeof setImmediate === 'function' * Invoke callback when the response has finished, useful for * cleaning up resources afterwards. * - * @param {object} thingie + * @param {object} msg * @param {function} callback * @return {object} * @api public */ -module.exports = function finished(msg, callback) { +function finished(msg, callback) { var socket = msg.socket - if (msg.finished || !socket.writable) { + if (isFinished(msg)) { defer(callback) return msg } @@ -59,3 +67,18 @@ module.exports = function finished(msg, callback) { return msg } + +/** + * Determine is message is already finished. + * + * @param {object} msg + * @return {boolean} + * @api public + */ + +function isFinished(msg) { + // finished for OutgoingMessage, complete for IncomingMessage + return msg.finished === true + || msg.complete === true + || msg.socket.writable === false +} diff --git a/test/test.js b/test/test.js index 12fd1b6..696c281 100644 --- a/test/test.js +++ b/test/test.js @@ -11,13 +11,8 @@ describe('finished(res, listener)', function () { onFinished(res, done) setTimeout(res.end.bind(res), 0) }) - server.listen(function () { - var port = this.address().port - http.get('http://127.0.0.1:' + port, function (res) { - res.resume() - res.on('close', server.close.bind(server)) - }) - }) + + sendget(server) }) it('should fire when called after finish', function (done) { @@ -27,13 +22,8 @@ describe('finished(res, listener)', function () { }) setTimeout(res.end.bind(res), 0) }) - server.listen(function () { - var port = this.address().port - http.get('http://127.0.0.1:' + port, function (res) { - res.resume() - res.on('close', server.close.bind(server)) - }) - }) + + sendget(server) }) }) @@ -127,6 +117,73 @@ describe('finished(res, listener)', function () { }) }) +describe('isFinished(res)', function () { + it('should be false before response finishes', function (done) { + var server = http.createServer(function (req, res) { + assert.ok(!onFinished.isFinished(res)) + res.end() + done() + }) + + sendget(server) + }) + + it('should be true after response finishes', function (done) { + var server = http.createServer(function (req, res) { + onFinished(res, function (err) { + assert.ifError(err) + assert.ok(onFinished.isFinished(res)) + done() + }) + + res.end() + }) + + sendget(server) + }) + + describe('when response errors', function () { + it('should return true', function (done) { + var server = http.createServer(function (req, res) { + onFinished(res, function (err) { + assert.ok(err) + assert.ok(onFinished.isFinished(res)) + done() + }) + + socket.on('error', noop) + socket.write('W') + }) + var socket + + server.listen(function () { + socket = net.connect(this.address().port, function () { + writerequest(this, true) + }) + }) + }) + }) + + describe('when the response aborts', function () { + it('should return true', function (done) { + var client + var server = http.createServer(function (req, res) { + onFinished(res, function (err) { + assert.ifError(err) + assert.ok(onFinished.isFinished(res)) + done() + }) + setTimeout(client.abort.bind(client), 0) + }) + server.listen(function () { + var port = this.address().port + client = http.get('http://127.0.0.1:' + port) + client.on('error', noop) + }) + }) + }) +}) + describe('finished(req, listener)', function () { describe('when the request finishes', function () { it('should fire the callback', function (done) { @@ -135,13 +192,8 @@ describe('finished(req, listener)', function () { req.resume() setTimeout(res.end.bind(res), 0) }) - server.listen(function () { - var port = this.address().port - http.get('http://127.0.0.1:' + port, function (res) { - res.resume() - res.on('close', server.close.bind(server)) - }) - }) + + sendget(server) }) it('should fire when called after finish', function (done) { @@ -152,13 +204,8 @@ describe('finished(req, listener)', function () { req.resume() setTimeout(res.end.bind(res), 0) }) - server.listen(function () { - var port = this.address().port - http.get('http://127.0.0.1:' + port, function (res) { - res.resume() - res.on('close', server.close.bind(server)) - }) - }) + + sendget(server) }) }) @@ -264,6 +311,75 @@ describe('finished(req, listener)', function () { }) }) +describe('isFinished(req)', function () { + it('should be false before request finishes', function (done) { + var server = http.createServer(function (req, res) { + assert.ok(!onFinished.isFinished(req)) + req.resume() + res.end() + done() + }) + + sendget(server) + }) + + it('should be true after request finishes', function (done) { + var server = http.createServer(function (req, res) { + onFinished(req, function (err) { + assert.ifError(err) + assert.ok(onFinished.isFinished(req)) + done() + }) + + req.resume() + res.end() + }) + + sendget(server) + }) + + describe('when request errors', function () { + it('should return true', function (done) { + var server = http.createServer(function (req, res) { + onFinished(req, function (err) { + assert.ok(err) + assert.ok(onFinished.isFinished(req)) + done() + }) + + socket.on('error', noop) + socket.write('W') + }) + var socket + + server.listen(function () { + socket = net.connect(this.address().port, function () { + writerequest(this, true) + }) + }) + }) + }) + + describe('when the request aborts', function () { + it('should return true', function (done) { + var client + var server = http.createServer(function (req, res) { + onFinished(res, function (err) { + assert.ifError(err) + assert.ok(onFinished.isFinished(req)) + done() + }) + setTimeout(client.abort.bind(client), 0) + }) + server.listen(function () { + var port = this.address().port + client = http.get('http://127.0.0.1:' + port) + client.on('error', noop) + }) + }) + }) +}) + function captureStderr(fn) { var chunks = [] var write = process.stderr.write @@ -283,6 +399,16 @@ function captureStderr(fn) { function noop() {} +function sendget(server) { + server.listen(function onListening() { + var port = this.address().port + http.get('http://127.0.0.1:' + port, function onResponse(res) { + res.resume() + res.on('close', server.close.bind(server)) + }) + }) +} + function writerequest(socket, chunked) { socket.write('GET / HTTP/1.1\r\n') socket.write('Host: localhost\r\n')