@@ -81,7 +81,12 @@ class Http2Transport implements Transport {
81
81
/**
82
82
* Timer reference for timeout that indicates when to send the next ping
83
83
*/
84
- private keepaliveIntervalId : NodeJS . Timer ;
84
+ private keepaliveTimerId : NodeJS . Timer | null = null ;
85
+ /**
86
+ * Indicates that the keepalive timer ran out while there were no active
87
+ * calls, and a ping should be sent the next time a call starts.
88
+ */
89
+ private pendingSendKeepalivePing = false ;
85
90
/**
86
91
* Timer reference tracking when the most recent ping will be considered lost
87
92
*/
@@ -142,10 +147,8 @@ class Http2Transport implements Transport {
142
147
} else {
143
148
this . keepaliveWithoutCalls = false ;
144
149
}
145
- this . keepaliveIntervalId = setTimeout ( ( ) => { } , 0 ) ;
146
- clearTimeout ( this . keepaliveIntervalId ) ;
147
150
if ( this . keepaliveWithoutCalls ) {
148
- this . startKeepalivePings ( ) ;
151
+ this . maybeStartKeepalivePingTimer ( ) ;
149
152
}
150
153
151
154
this . subchannelAddressString = subchannelAddressToString ( subchannelAddress ) ;
@@ -295,6 +298,14 @@ class Http2Transport implements Transport {
295
298
this . disconnectListeners . push ( listener ) ;
296
299
}
297
300
301
+ private clearKeepaliveTimer ( ) {
302
+ if ( ! this . keepaliveTimerId ) {
303
+ return ;
304
+ }
305
+ clearTimeout ( this . keepaliveTimerId ) ;
306
+ this . keepaliveTimerId = null ;
307
+ }
308
+
298
309
private clearKeepaliveTimeout ( ) {
299
310
if ( ! this . keepaliveTimeoutId ) {
300
311
return ;
@@ -303,7 +314,16 @@ class Http2Transport implements Transport {
303
314
this . keepaliveTimeoutId = null ;
304
315
}
305
316
306
- private sendPing ( ) {
317
+ private canSendPing ( ) {
318
+ return this . keepaliveTimeMs > 0 && ( this . keepaliveWithoutCalls || this . activeCalls . size > 0 ) ;
319
+ }
320
+
321
+ private maybeSendPing ( ) {
322
+ this . clearKeepaliveTimer ( ) ;
323
+ if ( ! this . canSendPing ( ) ) {
324
+ this . pendingSendKeepalivePing = true ;
325
+ return ;
326
+ }
307
327
if ( this . channelzEnabled ) {
308
328
this . keepalivesSent += 1 ;
309
329
}
@@ -320,6 +340,7 @@ class Http2Transport implements Transport {
320
340
( err : Error | null , duration : number , payload : Buffer ) => {
321
341
this . keepaliveTrace ( 'Received ping response' ) ;
322
342
this . clearKeepaliveTimeout ( ) ;
343
+ this . maybeStartKeepalivePingTimer ( ) ;
323
344
}
324
345
) ;
325
346
} catch ( e ) {
@@ -329,46 +350,52 @@ class Http2Transport implements Transport {
329
350
}
330
351
}
331
352
332
- private startKeepalivePings ( ) {
333
- if ( this . keepaliveTimeMs < 0 ) {
353
+ /**
354
+ * Starts the keepalive ping timer if appropriate. If the timer already ran
355
+ * out while there were no active requests, instead send a ping immediately.
356
+ * If the ping timer is already running or a ping is currently in flight,
357
+ * instead do nothing and wait for them to resolve.
358
+ */
359
+ private maybeStartKeepalivePingTimer ( ) {
360
+ if ( ! this . canSendPing ( ) ) {
334
361
return ;
335
362
}
336
- this . keepaliveIntervalId = setInterval ( ( ) => {
337
- this . sendPing ( ) ;
338
- } , this . keepaliveTimeMs ) ;
339
- this . keepaliveIntervalId . unref ?.( ) ;
340
- /* Don't send a ping immediately because whatever caused us to start
341
- * sending pings should also involve some network activity. */
363
+ if ( this . pendingSendKeepalivePing ) {
364
+ this . pendingSendKeepalivePing = false ;
365
+ this . maybeSendPing ( ) ;
366
+ } else if ( ! this . keepaliveTimerId && ! this . keepaliveTimeoutId ) {
367
+ this . keepaliveTrace ( 'Starting keepalive timer for ' + this . keepaliveTimeMs + 'ms' ) ;
368
+ this . keepaliveTimerId = setTimeout ( ( ) => {
369
+ this . maybeSendPing ( ) ;
370
+ } , this . keepaliveTimeMs ) . unref ?.( ) ;
371
+ }
372
+ /* Otherwise, there is already either a keepalive timer or a ping pending,
373
+ * wait for those to resolve. */
342
374
}
343
375
344
- /**
345
- * Stop keepalive pings when terminating a connection. This discards the
346
- * outstanding ping timeout, so it should not be called if the same
347
- * connection will still be used.
348
- */
349
376
private stopKeepalivePings ( ) {
350
- clearInterval ( this . keepaliveIntervalId ) ;
377
+ if ( this . keepaliveTimerId ) {
378
+ clearTimeout ( this . keepaliveTimerId ) ;
379
+ this . keepaliveTimerId = null ;
380
+ }
351
381
this . clearKeepaliveTimeout ( ) ;
352
382
}
353
383
354
384
private removeActiveCall ( call : Http2SubchannelCall ) {
355
385
this . activeCalls . delete ( call ) ;
356
386
if ( this . activeCalls . size === 0 ) {
357
387
this . session . unref ( ) ;
358
- if ( ! this . keepaliveWithoutCalls ) {
359
- this . stopKeepalivePings ( ) ;
360
- }
361
388
}
362
389
}
363
390
364
391
private addActiveCall ( call : Http2SubchannelCall ) {
365
- if ( this . activeCalls . size === 0 ) {
392
+ this . activeCalls . add ( call ) ;
393
+ if ( this . activeCalls . size === 1 ) {
366
394
this . session . ref ( ) ;
367
395
if ( ! this . keepaliveWithoutCalls ) {
368
- this . startKeepalivePings ( ) ;
396
+ this . maybeStartKeepalivePingTimer ( ) ;
369
397
}
370
398
}
371
- this . activeCalls . add ( call ) ;
372
399
}
373
400
374
401
createCall ( metadata : Metadata , host : string , method : string , listener : SubchannelCallInterceptingListener , subchannelCallStatsTracker : Partial < CallEventTracker > ) : Http2SubchannelCall {
0 commit comments