Skip to content

Commit

Permalink
Add function argument to support recording of response time
Browse files Browse the repository at this point in the history
closes #6
closes #7
  • Loading branch information
dougwilson committed Feb 16, 2015
1 parent c48e4f0 commit 831dc1f
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 25 deletions.
5 changes: 5 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
unreleased
==========

* Add function argument to support recording of response time

2.2.0 / 2014-09-22
==================

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(The MIT License)

Copyright (c) 2014 Jonathan Ong <[email protected]>
Copyright (c) 2014 Douglas Christopher Wilson <[email protected]>
Copyright (c) 2014-2015 Douglas Christopher Wilson <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Expand Down
41 changes: 38 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ $ npm install response-time
var responseTime = require('response-time')
```

### responseTime(options)
### responseTime([options])

Returns middleware that adds a `X-Response-Time` header to responses.
Create a middleware that adds a `X-Response-Time` header to responses. If
you don't want to use this module to automatically set a header, please
see the section about [`responseTime(fn)`](#responsetimeoptions).

#### Options

`responseTime` accepts these properties in the options object.
The `responseTime` function accepts an optional `options` object that may
contain any of the following keys:

##### digits

Expand All @@ -47,6 +50,12 @@ The name of the header to set, defaults to `X-Response-Time`.
Boolean to indicate if units of measurement suffix should be added to
the output, defaults to `true` (ex: `2.300ms` vs `2.300`).

### responseTime(fn)

Create a new middleware that records the response time of a request and
makes this available to your own function `fn`. The `fn` argument will be
invoked as `fn(req, res, time)`, where `time` is a number in milliseconds.

## Examples

### express/connect
Expand Down Expand Up @@ -86,6 +95,32 @@ http.createServer(function (req, res) {
})
```

### response time metrics

```js
var express = require('express')
var responseTime = require('response-time')
var StatsD = requrie('node-statsd')

var app = express()
var stats = new StatsD()

stats.socket.on('error', function (error) {
console.error(error.stack)
})

app.use(responseTime(function (req, res, time) {
var stat = (req.method + req.url).toLowerCase()
.replace(/[:\.]/g, '')
.replace(/\//g, '_')
stats.timing(stat, time)
}))

app.get('/', function (req, res) {
res.send('hello, world!')
})
```

## License

[MIT](LICENSE)
Expand Down
66 changes: 45 additions & 21 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@
* response-time
* Copyright(c) 2011 TJ Holowaychuk
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2014 Douglas Christopher Wilson
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/

/**
* Module dependencies
* @api private
*/

var deprecate = require('depd')('response-time')
var onHeaders = require('on-headers')

/**
* Module exports
*/

module.exports = responseTime

/**
* Reponse time:
*
Expand All @@ -25,15 +32,40 @@ var onHeaders = require('on-headers')
* @api public
*/

module.exports = function responseTime(options) {
function responseTime(options) {
var opts = options || {}

if (typeof options === 'number') {
// back-compat single number argument
deprecate('number argument: use {digits: ' + JSON.stringify(options) + '} instead')
options = { digits: options }
opts = { digits: options }
}

// get the function to invoke
var fn = typeof opts !== 'function'
? createSetHeader(opts)
: opts

return function responseTime(req, res, next) {
var startAt = process.hrtime()

onHeaders(res, function onHeaders() {
var diff = process.hrtime(startAt)
var time = diff[0] * 1e3 + diff[1] * 1e-6

fn(req, res, time)
})

next()
}
}

options = options || {}
/**
* Create function to set respoonse time header.
* @api private
*/

function createSetHeader(options) {
// response time digits
var digits = options.digits !== undefined
? options.digits
Expand All @@ -47,25 +79,17 @@ module.exports = function responseTime(options) {
? Boolean(options.suffix)
: true

return function responseTime(req, res, next) {
var startAt = process.hrtime()

onHeaders(res, function () {
if (this.getHeader(header)) {
return
}

var diff = process.hrtime(startAt)
var ms = diff[0] * 1e3 + diff[1] * 1e-6
var val = ms.toFixed(digits)
return function setResponseHeader(req, res, time) {
if (res.getHeader(header)) {
return
}

if (suffix) {
val += 'ms'
}
var val = time.toFixed(digits)

this.setHeader(header, val)
})
if (suffix) {
val += 'ms'
}

next()
res.setHeader(header, val)
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"on-headers": "~1.0.0"
},
"devDependencies": {
"after": "0.8.1",
"istanbul": "0.3.5",
"mocha": "~2.1.0",
"supertest": "0.15.0"
Expand Down
40 changes: 40 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

process.env.NO_DEPRECATION = 'response-time'

var after = require('after');
var assert = require('assert');
var http = require('http');
var request = require('supertest');
var responseTime = require('..')
Expand Down Expand Up @@ -47,6 +49,38 @@ describe('responseTime()', function () {
})
})

describe('responseTime(fn)', function () {
it('should call fn with response time', function (done) {
var cb = after(2, done)
var start = process.hrtime()
var server = createServer(function (req, res, time) {
var diff = process.hrtime(start)
var max = diff[0] * 1e3 + diff[1] * 1e-6
assert.equal(req.url, '/')
assert.equal(res.statusCode, 200)
assert.ok(time >= 0)
assert.ok(time <= max)
cb()
})

request(server)
.get('/')
.expect(200, cb)
})

it('should not send X-Response-Time header', function (done) {
var cb = after(2, done)
var server = createServer(function () {
cb()
})

request(server)
.get('/')
.expect(shouldNotHaveHeader('X-Response-Time'))
.expect(200, cb)
})
})

describe('responseTime(options)', function () {
describe('with "digits" option', function () {
it('should default to 3', function (done) {
Expand Down Expand Up @@ -116,3 +150,9 @@ function createServer(opts, fn) {
})
})
}

function shouldNotHaveHeader(header) {
return function (res) {
assert.ok(!(header.toLowerCase() in res.headers), 'should not have header ' + header)
}
}

0 comments on commit 831dc1f

Please sign in to comment.