Skip to content

Commit

Permalink
support both req and res as arguments
Browse files Browse the repository at this point in the history
closes #5
  • Loading branch information
dougwilson committed Aug 16, 2014
1 parent d795cc6 commit 1d3872e
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 12 deletions.
1 change: 1 addition & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ unreleased
==========

* remove support for non-`res` argument
* support both `req` and `res` as arguments
* deps: ee-first@1.0.5

1.2.2 / 2014-06-10
Expand Down
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ $ npm install finished

## API

### finished(response, callback)
### finished(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
to to an error, the first argument will contain the error.

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')
Expand All @@ -25,6 +32,29 @@ onFinished(res, function (err) {
})
```

### finished(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
to to an error, the first argument will contain the error.

Listening to the end of a request would be used to know when to continue
after reading the data.

```js
var data = ''
var onFinished = require('finished')

req.setEncoding('utf8')
res.on('data', function (str) {
data += str
})

onFinished(req, function (err) {
// if err, data is probably incomplete
})
```

### Example

The following code ensures that file descriptors are always closed
Expand Down
18 changes: 9 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,20 @@ var defer = typeof setImmediate === 'function'
* @api public
*/

module.exports = function finished(res, callback) {
var socket = res.socket
module.exports = function finished(msg, callback) {
var socket = msg.socket

if (res.finished || !socket.writable) {
if (msg.finished || !socket.writable) {
defer(callback)
return res
return msg
}

var listener = res.__onFinished
var listener = msg.__onFinished

// create a private single listener with queue
if (!listener || !listener.queue) {
listener = res.__onFinished = function onFinished(err) {
if (res.__onFinished === listener) res.__onFinished = null
listener = msg.__onFinished = function onFinished(err) {
if (msg.__onFinished === listener) msg.__onFinished = null
var queue = listener.queue || []
while (queue.length) queue.shift()(err)
}
Expand All @@ -51,11 +51,11 @@ module.exports = function finished(res, callback) {
// finished on first event
first([
[socket, 'error', 'close'],
[res, 'finish'],
[msg, 'end', 'finish'],
], listener)
}

listener.queue.push(callback)

return res
return msg
}
140 changes: 138 additions & 2 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var http = require('http')
var net = require('net')
var onFinished = require('..')

describe('finished', function () {
describe('finished(res, listener)', function () {
describe('when the response finishes', function () {
it('should fire the callback', function (done) {
var server = http.createServer(function (req, res) {
Expand Down Expand Up @@ -127,6 +127,143 @@ describe('finished', function () {
})
})

describe('finished(req, listener)', function () {
describe('when the request finishes', function () {
it('should fire the callback', function (done) {
var server = http.createServer(function (req, res) {
onFinished(req, done)
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))
})
})
})

it('should fire when called after finish', function (done) {
var server = http.createServer(function (req, res) {
onFinished(req, function () {
onFinished(req, done)
})
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))
})
})
})
})

describe('when using keep-alive', function () {
it('should fire for each request', function (done) {
var called = false
var server = http.createServer(function (req, res) {
var data = ''

onFinished(req, function (err) {
assert.ifError(err)
assert.equal(data, 'A')

if (called) {
socket.end()
server.close()
done(called !== req ? null : new Error('fired twice on same req'))
return
}

called = req

res.end()
writerequest(socket, true)
})

req.setEncoding('utf8')
req.on('data', function (str) {
data += str
})

socket.write('1\r\nA\r\n')
socket.write('0\r\n\r\n')
})
var socket

server.listen(function () {
socket = net.connect(this.address().port, function () {
writerequest(this, true)
})
})
})
})

describe('when request errors', function () {
it('should fire with error', function (done) {
var server = http.createServer(function (req, res) {
onFinished(req, function (err) {
assert.ok(err)
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 execute the callback', function (done) {
var client
var server = http.createServer(function (req, res) {
onFinished(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)
})
})
})

describe('when calling many times on same request', function () {
it('should not print warnings', function (done) {
var server = http.createServer(function (req, res) {
var stderr = captureStderr(function () {
for (var i = 0; i < 400; i++) {
onFinished(req, noop)
}
})

onFinished(req, done)
assert.equal(stderr, '')
res.end()
})

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))
})
})
})
})
})

function captureStderr(fn) {
var chunks = []
var write = process.stderr.write
Expand Down Expand Up @@ -156,5 +293,4 @@ function writerequest(socket, chunked) {
}

socket.write('\r\n')
socket.write('\r\n')
}

0 comments on commit 1d3872e

Please sign in to comment.