Skip to content

Commit b24119a

Browse files
committed
feat: Add timing for Socket Close
* Stop using the request module. Use the standard http and https modules. This also fixed the DNS Lookup timing, which was set, although no DNS lookup took place.
1 parent 705987b commit b24119a

File tree

4 files changed

+67
-42
lines changed

4 files changed

+67
-42
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![NPM Downloads](https://nodei.co/npm/nettime.png?downloads=true&stars=true)](https://www.npmjs.com/package/nettime)
44

5-
Prints timings of a HTTP/S request, including DNS lookup, TLS handshake etc.
5+
Prints time duration of various stages of a HTTP/S request, like DNS lookup, TLS handshake, Time to First Byte etc. You can find more information in [Understanding & Measuring HTTP Timings with Node.js](https://blog.risingstack.com/measuring-http-timings-node-js/).
66

77
## Command-line usage
88

@@ -40,8 +40,8 @@ const nettime = require('nettime')
4040
nettime('http://www.google.com').then(result => {
4141
if (result.statusCode === 200) {
4242
let timings = result.timings
43-
let duration = nettime.getDuration(timings.responseBegin,
44-
timings.responseEnd)
43+
let duration = nettime.getDuration(timings.firstByte,
44+
timings.contentTransfer)
4545
console.log(nettime.getMilliseconds(duration) + 'ms')
4646
}
4747
})
@@ -64,6 +64,7 @@ your code using Grunt.
6464

6565
## Release History
6666

67+
* 2017-10-22 v0.2.0 Add timing for Socket Close
6768
* 2017-10-21 v0.1.0 Initial release
6869

6970
## License

lib/nettime.js

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
'use strict'
22

3-
const request = require('request')
3+
const http = require('http')
4+
const https = require('https')
5+
const url = require('url')
46

57
function nettime (options) {
68
if (typeof options === 'string') {
@@ -9,33 +11,54 @@ function nettime (options) {
911
url: url
1012
}
1113
}
14+
const connection = url.parse(options.url)
15+
const protocol = connection.protocol === 'http:' ? http : https
16+
const timings = {}
17+
var start, response
18+
connection.agent = false
1219
return new Promise((resolve, reject) => {
13-
const req = request({
14-
url: options.url,
15-
time: true
16-
}, (error, response) => {
17-
if (error) {
18-
reject(error)
19-
} else {
20-
let timings = req.timings
20+
start = getTime(process.hrtime())
21+
protocol.get(connection, localResponse => {
22+
response = localResponse
23+
response.once('readable', () => {
24+
timings.firstByte = getDuration()
25+
})
26+
.on('data', () => {
27+
})
28+
.on('end', () => {
29+
timings.contentTransfer = getDuration()
30+
})
31+
})
32+
.on('socket', socket => {
33+
timings.socketOpen = getDuration()
34+
socket.on('lookup', () => {
35+
timings.dnsLookup = getDuration()
36+
})
37+
.on('connect', () => {
38+
timings.tcpConnection = getDuration()
39+
})
40+
.on('secureConnect', () => {
41+
timings.tlsHandshake = getDuration()
42+
})
43+
.on('close', () => {
44+
timings.socketClose = getDuration()
2145
resolve({
22-
statusCode: response.statusCode,
23-
timings: {
24-
socketOpen: getTiming(timings.socket),
25-
dnsLookup: getTiming(timings.lookup),
26-
tcpConnection: getTiming(timings.connect),
27-
responseBegin: getTiming(timings.response),
28-
responseEnd: getTiming(timings.end)
29-
}
46+
statusCode: response ? response.statusCode : 0,
47+
timings: timings
3048
})
31-
}
49+
})
3250
})
51+
.on('error', reject)
3352
})
34-
}
3553

36-
function getTiming (duration) {
37-
const seconds = parseInt(duration / 1000)
38-
return [seconds, parseInt(duration * 1e6 - seconds * 1e9)]
54+
function getDuration () {
55+
const duration = getTime(process.hrtime()) - start
56+
return [parseInt(duration / 1e9), duration % 1e9]
57+
}
58+
59+
function getTime (timing) {
60+
return timing[0] * 1e9 + timing[1]
61+
}
3962
}
4063

4164
function getDuration (start, end) {

lib/printer.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ const labels = {
66
socketOpen: 'Socket Open',
77
dnsLookup: 'DNS Lookup',
88
tcpConnection: 'TCP Connection',
9-
responseBegin: 'Response Start',
10-
responseEnd: 'Response Finish',
9+
tlsHandshake: 'TLS Handshake',
10+
firstByte: 'First Byte',
11+
contentTransfer: 'Content Transfer',
1112
socketClose: 'Socket Close'
1213
}
1314

@@ -18,13 +19,13 @@ module.exports = function (timings, unit) {
1819

1920
function printMilliseconds (timings) {
2021
var lastTiming = [0, 0]
21-
console.log('Phase Finished Duration')
22-
console.log('---------------------------------')
22+
console.log('Phase Finished Duration')
23+
console.log('-----------------------------------')
2324
for (let part in labels) {
2425
let timing = timings[part]
2526
if (timing) {
2627
let duration = getDuration(lastTiming, timing)
27-
console.log(sprintf('%-15s %3d.%03ds %3d.%03ds',
28+
console.log(sprintf('%-17s %3d.%03ds %3d.%03ds',
2829
labels[part], timing[0], parseInt(timing[1] / 1e6),
2930
duration[0], parseInt(duration[1] / 1e6)))
3031
lastTiming = timing
@@ -34,13 +35,13 @@ function printMilliseconds (timings) {
3435

3536
function printNanoseconds (timings) {
3637
var lastTiming = [0, 0]
37-
console.log('Phase Finished Duration')
38-
console.log('---------------------------------------------')
38+
console.log('Phase Finished Duration')
39+
console.log('-----------------------------------------------')
3940
for (let part in labels) {
4041
let timing = timings[part]
4142
if (timing) {
4243
let duration = getDuration(lastTiming, timing)
43-
console.log(sprintf('%-15s %3ds %7.3fms %3ds %7.3fms',
44+
console.log(sprintf('%-17s %3ds %7.3fms %3ds %7.3fms',
4445
labels[part], timing[0], parseInt(timing[1] / 1000) / 1000,
4546
duration[0], parseInt(duration[1] / 1000) / 1000))
4647
lastTiming = timing

tests/test.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ function makeRequest (protocol, host, port, path) {
4848
function checkRequest (result) {
4949
const timings = result.timings
5050
const tcpConnection = timings.tcpConnection
51-
const responseBegin = timings.responseBegin
51+
const firstByte = timings.firstByte
5252
assert.equal(typeof result, 'object')
5353
assert.equal(Object.keys(result).length, 2)
5454
assert.equal(typeof result.timings, 'object')
5555
checkTiming(timings.socketOpen)
5656
checkTiming(tcpConnection)
57-
checkTiming(responseBegin)
58-
checkTiming(timings.responseEnd)
59-
checkNull(timings.socketClose)
60-
assert.ok(getDuration(tcpConnection, responseBegin) >= 100 * 1e6)
57+
checkTiming(firstByte)
58+
checkTiming(timings.contentTransfer)
59+
checkTiming(timings.socketClose)
60+
assert.ok(getDuration(tcpConnection, firstByte) >= 100 * 1e6)
6161
return result
6262
}
6363

@@ -85,7 +85,7 @@ function testHostname () {
8585
.then(result => {
8686
const timings = result.timings
8787
assert.equal(result.statusCode, 204)
88-
assert.equal(Object.keys(timings).length, 5)
88+
assert.equal(Object.keys(timings).length, 6)
8989
checkTiming(timings.dnsLookup)
9090
})
9191
}
@@ -96,7 +96,7 @@ function testIPAddress () {
9696
const timings = result.timings
9797
assert.equal(result.statusCode, 204)
9898
assert.equal(Object.keys(timings).length, 5)
99-
checkTiming(timings.dnsLookup)
99+
checkNull(timings.dnsLookup)
100100
})
101101
}
102102

@@ -106,7 +106,7 @@ function testMissingPage () {
106106
const timings = result.timings
107107
assert.equal(result.statusCode, 404)
108108
assert.equal(Object.keys(timings).length, 5)
109-
checkTiming(timings.dnsLookup)
109+
checkNull(timings.dnsLookup)
110110
})
111111
}
112112

@@ -124,7 +124,7 @@ function testInvalidURL () {
124124
.then(assert.fail)
125125
.catch(error => {
126126
assert.ok(error instanceof Error)
127-
assert.equal(error.message, 'Invalid protocol: dummy:')
127+
assert.ok(error.message.indexOf('dummy:') > 0)
128128
})
129129
}
130130

0 commit comments

Comments
 (0)