index.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const defer_to_connect_1 = require("defer-to-connect");
  4. const util_1 = require("util");
  5. const nodejsMajorVersion = Number(process.versions.node.split('.')[0]);
  6. const timer = (request) => {
  7. if (request.timings) {
  8. return request.timings;
  9. }
  10. const timings = {
  11. start: Date.now(),
  12. socket: undefined,
  13. lookup: undefined,
  14. connect: undefined,
  15. secureConnect: undefined,
  16. upload: undefined,
  17. response: undefined,
  18. end: undefined,
  19. error: undefined,
  20. abort: undefined,
  21. phases: {
  22. wait: undefined,
  23. dns: undefined,
  24. tcp: undefined,
  25. tls: undefined,
  26. request: undefined,
  27. firstByte: undefined,
  28. download: undefined,
  29. total: undefined
  30. }
  31. };
  32. request.timings = timings;
  33. const handleError = (origin) => {
  34. const emit = origin.emit.bind(origin);
  35. origin.emit = (event, ...args) => {
  36. // Catches the `error` event
  37. if (event === 'error') {
  38. timings.error = Date.now();
  39. timings.phases.total = timings.error - timings.start;
  40. origin.emit = emit;
  41. }
  42. // Saves the original behavior
  43. return emit(event, ...args);
  44. };
  45. };
  46. handleError(request);
  47. const onAbort = () => {
  48. timings.abort = Date.now();
  49. // Let the `end` response event be responsible for setting the total phase,
  50. // unless the Node.js major version is >= 13.
  51. if (!timings.response || nodejsMajorVersion >= 13) {
  52. timings.phases.total = Date.now() - timings.start;
  53. }
  54. };
  55. request.prependOnceListener('abort', onAbort);
  56. const onSocket = (socket) => {
  57. timings.socket = Date.now();
  58. timings.phases.wait = timings.socket - timings.start;
  59. if (util_1.types.isProxy(socket)) {
  60. return;
  61. }
  62. const lookupListener = () => {
  63. timings.lookup = Date.now();
  64. timings.phases.dns = timings.lookup - timings.socket;
  65. };
  66. socket.prependOnceListener('lookup', lookupListener);
  67. defer_to_connect_1.default(socket, {
  68. connect: () => {
  69. timings.connect = Date.now();
  70. if (timings.lookup === undefined) {
  71. socket.removeListener('lookup', lookupListener);
  72. timings.lookup = timings.connect;
  73. timings.phases.dns = timings.lookup - timings.socket;
  74. }
  75. timings.phases.tcp = timings.connect - timings.lookup;
  76. // This callback is called before flushing any data,
  77. // so we don't need to set `timings.phases.request` here.
  78. },
  79. secureConnect: () => {
  80. timings.secureConnect = Date.now();
  81. timings.phases.tls = timings.secureConnect - timings.connect;
  82. }
  83. });
  84. };
  85. if (request.socket) {
  86. onSocket(request.socket);
  87. }
  88. else {
  89. request.prependOnceListener('socket', onSocket);
  90. }
  91. const onUpload = () => {
  92. var _a;
  93. timings.upload = Date.now();
  94. timings.phases.request = timings.upload - ((_a = timings.secureConnect) !== null && _a !== void 0 ? _a : timings.connect);
  95. };
  96. const writableFinished = () => {
  97. if (typeof request.writableFinished === 'boolean') {
  98. return request.writableFinished;
  99. }
  100. // Node.js doesn't have `request.writableFinished` property
  101. return request.finished && request.outputSize === 0 && (!request.socket || request.socket.writableLength === 0);
  102. };
  103. if (writableFinished()) {
  104. onUpload();
  105. }
  106. else {
  107. request.prependOnceListener('finish', onUpload);
  108. }
  109. request.prependOnceListener('response', (response) => {
  110. timings.response = Date.now();
  111. timings.phases.firstByte = timings.response - timings.upload;
  112. response.timings = timings;
  113. handleError(response);
  114. response.prependOnceListener('end', () => {
  115. timings.end = Date.now();
  116. timings.phases.download = timings.end - timings.response;
  117. timings.phases.total = timings.end - timings.start;
  118. });
  119. response.prependOnceListener('aborted', onAbort);
  120. });
  121. return timings;
  122. };
  123. exports.default = timer;
  124. // For CommonJS default export support
  125. module.exports = timer;
  126. module.exports.default = timer;