Skip to content

Commit

Permalink
Merge 388f15e into 24b555e
Browse files Browse the repository at this point in the history
  • Loading branch information
szmarczak committed Apr 20, 2019
2 parents 24b555e + 388f15e commit f847efd
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 176 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,3 +1,4 @@
.nyc_output
node_modules
package-lock.json
dist
1 change: 1 addition & 0 deletions .travis.yml
@@ -1,5 +1,6 @@
language: node_js
node_js:
- '11'
- '10'
- '8'
after_success: npm run coveralls
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -21,7 +21,7 @@ Yarn:
```js
'use strict';
const https = require('https');
const timer = require('@szmarczak/http-timer');
const timer = require('@szmarczak/http-timer').default;

const request = https.get('https://httpbin.org/anything');
const timings = timer(request);
Expand Down Expand Up @@ -73,6 +73,8 @@ Returns: `Object`
- `download` - `timings.end - timings.response`
- `total` - `timings.end - timings.start` or `timings.error - timings.start`

If something is not measured yet, it will be `undefined`.

**Note**: The time is a `number` representing the milliseconds elapsed since the UNIX epoch.

## License
Expand Down
13 changes: 0 additions & 13 deletions example.js

This file was deleted.

53 changes: 41 additions & 12 deletions package.json
Expand Up @@ -2,16 +2,18 @@
"name": "@szmarczak/http-timer",
"version": "1.1.2",
"description": "Timings for HTTP requests",
"main": "source",
"main": "dist",
"engines": {
"node": ">=6"
"node": ">=8"
},
"scripts": {
"test": "xo && nyc ava",
"build": "del-cli dist && tsc",
"prepublishOnly": "npm run build",
"coveralls": "nyc report --reporter=text-lcov | coveralls"
},
"files": [
"source"
"dist"
],
"keywords": [
"http",
Expand All @@ -29,19 +31,46 @@
"url": "https://github.com/szmarczak/http-timer/issues"
},
"homepage": "https://github.com/szmarczak/http-timer#readme",
"dependencies": {
"defer-to-connect": "^1.0.2"
},
"devDependencies": {
"@sindresorhus/tsconfig": "^0.3.0",
"@types/node": "^11.13.6",
"@typescript-eslint/eslint-plugin": "^1.6.0",
"ava": "^1.4.1",
"coveralls": "^3.0.3",
"del-cli": "^1.1.0",
"eslint-config-xo-typescript": "^0.9.0",
"nyc": "^14.0.0",
"p-event": "^4.1.0",
"ts-node": "^8.1.0",
"typescript": "^3.4.4",
"xo": "^0.24.0"
},
"types": "dist",
"xo": {
"extends": "xo-typescript",
"extensions": [
"ts"
],
"rules": {
"unicorn/filename-case": "camelCase"
"ava/no-ignored-test-files": "off"
}
},
"devDependencies": {
"ava": "^0.25.0",
"coveralls": "^3.0.2",
"p-event": "^2.1.0",
"nyc": "^12.0.2",
"xo": "^0.22.0"
"nyc": {
"extension": [
".ts"
]
},
"dependencies": {
"defer-to-connect": "^1.0.1"
"ava": {
"babel": false,
"compileEnhancements": false,
"extensions": [
"ts"
],
"require": [
"ts-node/register"
]
}
}
99 changes: 0 additions & 99 deletions source/index.js

This file was deleted.

122 changes: 122 additions & 0 deletions source/index.ts
@@ -0,0 +1,122 @@
import {EventEmitter} from 'events';
import {Socket} from 'net';
import {ClientRequest, IncomingMessage} from 'http';
// @ts-ignore
import deferToConnect from 'defer-to-connect';

export interface Timings {
start: number;
socket?: number;
lookup?: number;
connect?: number;
upload?: number;
response?: number;
end?: number;
error?: number;
phases: {
wait?: number;
dns?: number;
tcp?: number;
request?: number;
firstByte?: number;
download?: number;
total?: number;
};
}

export default (request: ClientRequest): Timings => {
const timings: Timings = {
start: Date.now(),
socket: undefined,
lookup: undefined,
connect: undefined,
upload: undefined,
response: undefined,
end: undefined,
error: undefined,
phases: {
wait: undefined,
dns: undefined,
tcp: undefined,
request: undefined,
firstByte: undefined,
download: undefined,
total: undefined
}
};

const handleError = (origin: EventEmitter): void => {
const emit = origin.emit.bind(origin);
origin.emit = (event, ...args) => {
// Catches the `error` event
if (event === 'error') {
timings.error = Date.now();
timings.phases.total = timings.error - timings.start;

origin.emit = emit;
}

// Saves the original behavior
return emit(event, ...args);
};
};

let uploadFinished = false;
const onUpload = (): void => {
timings.upload = Date.now();
timings.phases.request = timings.upload - timings.connect!;
};

handleError(request);

request.once('socket', (socket: Socket): void => {
timings.socket = Date.now();
timings.phases.wait = timings.socket - timings.start;

const lookupListener = (): void => {
timings.lookup = Date.now();
timings.phases.dns = timings.lookup - timings.socket!;
};

socket.once('lookup', lookupListener);

deferToConnect(socket, () => {
timings.connect = Date.now();

if (timings.lookup === undefined) {
socket.removeListener('lookup', lookupListener);
timings.lookup = timings.connect;
timings.phases.dns = timings.lookup - timings.socket!;
}

timings.phases.tcp = timings.connect - timings.lookup;

if (uploadFinished && !timings.upload) {
onUpload();
}
});
});

request.once('finish', () => {
uploadFinished = true;

if (timings.connect) {
onUpload();
}
});

request.once('response', (response: IncomingMessage): void => {
timings.response = Date.now();
timings.phases.firstByte = timings.response - timings.upload!;

handleError(response);

response.once('end', () => {
timings.end = Date.now();
timings.phases.download = timings.end - timings.response!;
timings.phases.total = timings.end - timings.start;
});
});

return timings;
};

0 comments on commit f847efd

Please sign in to comment.