index.js 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. 'use strict';
  2. const deferToConnect = require('defer-to-connect');
  3. module.exports = request => {
  4. const timings = {
  5. start: Date.now(),
  6. socket: null,
  7. lookup: null,
  8. connect: null,
  9. upload: null,
  10. response: null,
  11. end: null,
  12. error: null,
  13. phases: {
  14. wait: null,
  15. dns: null,
  16. tcp: null,
  17. request: null,
  18. firstByte: null,
  19. download: null,
  20. total: null
  21. }
  22. };
  23. const handleError = origin => {
  24. const emit = origin.emit.bind(origin);
  25. origin.emit = (event, ...args) => {
  26. // Catches the `error` event
  27. if (event === 'error') {
  28. timings.error = Date.now();
  29. timings.phases.total = timings.error - timings.start;
  30. origin.emit = emit;
  31. }
  32. // Saves the original behavior
  33. return emit(event, ...args);
  34. };
  35. };
  36. let uploadFinished = false;
  37. const onUpload = () => {
  38. timings.upload = Date.now();
  39. timings.phases.request = timings.upload - timings.connect;
  40. };
  41. handleError(request);
  42. request.once('socket', socket => {
  43. timings.socket = Date.now();
  44. timings.phases.wait = timings.socket - timings.start;
  45. const lookupListener = () => {
  46. timings.lookup = Date.now();
  47. timings.phases.dns = timings.lookup - timings.socket;
  48. };
  49. socket.once('lookup', lookupListener);
  50. deferToConnect(socket, () => {
  51. timings.connect = Date.now();
  52. if (timings.lookup === null) {
  53. socket.removeListener('lookup', lookupListener);
  54. timings.lookup = timings.connect;
  55. timings.phases.dns = timings.lookup - timings.socket;
  56. }
  57. timings.phases.tcp = timings.connect - timings.lookup;
  58. if (uploadFinished && !timings.upload) {
  59. onUpload();
  60. }
  61. });
  62. });
  63. request.once('finish', () => {
  64. uploadFinished = true;
  65. if (timings.connect) {
  66. onUpload();
  67. }
  68. });
  69. request.once('response', response => {
  70. timings.response = Date.now();
  71. timings.phases.firstByte = timings.response - timings.upload;
  72. handleError(response);
  73. response.once('end', () => {
  74. timings.end = Date.now();
  75. timings.phases.download = timings.end - timings.response;
  76. timings.phases.total = timings.end - timings.start;
  77. });
  78. });
  79. return timings;
  80. };