1- import { EventEmitter } from 'events' ;
2- import { Socket } from 'net' ;
3- import { ClientRequest , IncomingMessage } from 'http' ;
1+ import { errorMonitor } from 'node:events' ;
2+ import { types } from 'node:util' ;
3+ import type { EventEmitter } from 'node:events' ;
4+ import type { Socket } from 'node:net' ;
5+ import type { ClientRequest , IncomingMessage } from 'node:http' ;
46import deferToConnect from 'defer-to-connect' ;
5- import { types } from 'util' ;
67
78export interface Timings {
89 start : number ;
@@ -35,8 +36,6 @@ export interface IncomingMessageWithTimings extends IncomingMessage {
3536 timings ?: Timings ;
3637}
3738
38- const nodejsMajorVersion = Number ( process . versions . node . split ( '.' ) [ 0 ] ) ;
39-
4039const timer = ( request : ClientRequestWithTimings ) : Timings => {
4140 if ( request . timings ) {
4241 return request . timings ;
@@ -61,38 +60,24 @@ const timer = (request: ClientRequestWithTimings): Timings => {
6160 request : undefined ,
6261 firstByte : undefined ,
6362 download : undefined ,
64- total : undefined
65- }
63+ total : undefined ,
64+ } ,
6665 } ;
6766
6867 request . timings = timings ;
6968
7069 const handleError = ( origin : EventEmitter ) : void => {
71- const emit = origin . emit . bind ( origin ) ;
72- origin . emit = ( event , ...args : unknown [ ] ) => {
73- // Catches the `error` event
74- if ( event === 'error' ) {
75- timings . error = Date . now ( ) ;
76- timings . phases . total = timings . error - timings . start ;
77-
78- origin . emit = emit ;
79- }
80-
81- // Saves the original behavior
82- return emit ( event , ...args ) ;
83- } ;
70+ origin . once ( errorMonitor , ( ) => {
71+ timings . error = Date . now ( ) ;
72+ timings . phases . total = timings . error - timings . start ;
73+ } ) ;
8474 } ;
8575
8676 handleError ( request ) ;
8777
8878 const onAbort = ( ) : void => {
8979 timings . abort = Date . now ( ) ;
90-
91- // Let the `end` response event be responsible for setting the total phase,
92- // unless the Node.js major version is >= 13.
93- if ( ! timings . response || nodejsMajorVersion >= 13 ) {
94- timings . phases . total = Date . now ( ) - timings . start ;
95- }
80+ timings . phases . total = timings . abort - timings . start ;
9681 } ;
9782
9883 request . prependOnceListener ( 'abort' , onAbort ) ;
@@ -123,14 +108,11 @@ const timer = (request: ClientRequestWithTimings): Timings => {
123108 }
124109
125110 timings . phases . tcp = timings . connect - timings . lookup ;
126-
127- // This callback is called before flushing any data,
128- // so we don't need to set `timings.phases.request` here.
129111 } ,
130112 secureConnect : ( ) => {
131113 timings . secureConnect = Date . now ( ) ;
132114 timings . phases . tls = timings . secureConnect - timings . connect ! ;
133- }
115+ } ,
134116 } ) ;
135117 } ;
136118
@@ -145,16 +127,7 @@ const timer = (request: ClientRequestWithTimings): Timings => {
145127 timings . phases . request = timings . upload - ( timings . secureConnect ?? timings . connect ! ) ;
146128 } ;
147129
148- const writableFinished = ( ) : boolean => {
149- if ( typeof request . writableFinished === 'boolean' ) {
150- return request . writableFinished ;
151- }
152-
153- // Node.js doesn't have `request.writableFinished` property
154- return request . finished && ( request as any ) . outputSize === 0 && ( ! request . socket || request . socket . writableLength === 0 ) ;
155- } ;
156-
157- if ( writableFinished ( ) ) {
130+ if ( request . writableFinished ) {
158131 onUpload ( ) ;
159132 } else {
160133 request . prependOnceListener ( 'finish' , onUpload ) ;
@@ -169,6 +142,14 @@ const timer = (request: ClientRequestWithTimings): Timings => {
169142 handleError ( response ) ;
170143
171144 response . prependOnceListener ( 'end' , ( ) => {
145+ request . off ( 'abort' , onAbort ) ;
146+ response . off ( 'aborted' , onAbort ) ;
147+
148+ if ( timings . phases . total ) {
149+ // Aborted or errored
150+ return ;
151+ }
152+
172153 timings . end = Date . now ( ) ;
173154 timings . phases . download = timings . end - timings . response ! ;
174155 timings . phases . total = timings . end - timings . start ;
@@ -181,7 +162,3 @@ const timer = (request: ClientRequestWithTimings): Timings => {
181162} ;
182163
183164export default timer ;
184-
185- // For CommonJS default export support
186- module . exports = timer ;
187- module . exports . default = timer ;
0 commit comments