@@ -108,7 +108,12 @@ class Http2Transport implements Transport {
108
108
/**
109
109
* Timer reference for timeout that indicates when to send the next ping
110
110
*/
111
- private keepaliveIntervalId : NodeJS . Timer ;
111
+ private keepaliveTimerId : NodeJS . Timer | null = null ;
112
+ /**
113
+ * Indicates that the keepalive timer ran out while there were no active
114
+ * calls, and a ping should be sent the next time a call starts.
115
+ */
116
+ private pendingSendKeepalivePing = false ;
112
117
/**
113
118
* Timer reference tracking when the most recent ping will be considered lost
114
119
*/
@@ -169,10 +174,8 @@ class Http2Transport implements Transport {
169
174
} else {
170
175
this . keepaliveWithoutCalls = false ;
171
176
}
172
- this . keepaliveIntervalId = setTimeout ( ( ) => { } , 0 ) ;
173
- clearTimeout ( this . keepaliveIntervalId ) ;
174
177
if ( this . keepaliveWithoutCalls ) {
175
- this . startKeepalivePings ( ) ;
178
+ this . maybeStartKeepalivePingTimer ( ) ;
176
179
}
177
180
178
181
this . subchannelAddressString = subchannelAddressToString ( subchannelAddress ) ;
@@ -375,6 +378,14 @@ class Http2Transport implements Transport {
375
378
this . disconnectListeners . push ( listener ) ;
376
379
}
377
380
381
+ private clearKeepaliveTimer ( ) {
382
+ if ( ! this . keepaliveTimerId ) {
383
+ return ;
384
+ }
385
+ clearTimeout ( this . keepaliveTimerId ) ;
386
+ this . keepaliveTimerId = null ;
387
+ }
388
+
378
389
private clearKeepaliveTimeout ( ) {
379
390
if ( ! this . keepaliveTimeoutId ) {
380
391
return ;
@@ -383,7 +394,16 @@ class Http2Transport implements Transport {
383
394
this . keepaliveTimeoutId = null ;
384
395
}
385
396
386
- private sendPing ( ) {
397
+ private canSendPing ( ) {
398
+ return this . keepaliveTimeMs > 0 && ( this . keepaliveWithoutCalls || this . activeCalls . size > 0 ) ;
399
+ }
400
+
401
+ private maybeSendPing ( ) {
402
+ this . clearKeepaliveTimer ( ) ;
403
+ if ( ! this . canSendPing ( ) ) {
404
+ this . pendingSendKeepalivePing = true ;
405
+ return ;
406
+ }
387
407
if ( this . channelzEnabled ) {
388
408
this . keepalivesSent += 1 ;
389
409
}
@@ -402,6 +422,7 @@ class Http2Transport implements Transport {
402
422
( err : Error | null , duration : number , payload : Buffer ) => {
403
423
this . keepaliveTrace ( 'Received ping response' ) ;
404
424
this . clearKeepaliveTimeout ( ) ;
425
+ this . maybeStartKeepalivePingTimer ( ) ;
405
426
}
406
427
) ;
407
428
} catch ( e ) {
@@ -411,46 +432,52 @@ class Http2Transport implements Transport {
411
432
}
412
433
}
413
434
414
- private startKeepalivePings ( ) {
415
- if ( this . keepaliveTimeMs < 0 ) {
435
+ /**
436
+ * Starts the keepalive ping timer if appropriate. If the timer already ran
437
+ * out while there were no active requests, instead send a ping immediately.
438
+ * If the ping timer is already running or a ping is currently in flight,
439
+ * instead do nothing and wait for them to resolve.
440
+ */
441
+ private maybeStartKeepalivePingTimer ( ) {
442
+ if ( ! this . canSendPing ( ) ) {
416
443
return ;
417
444
}
418
- this . keepaliveIntervalId = setInterval ( ( ) => {
419
- this . sendPing ( ) ;
420
- } , this . keepaliveTimeMs ) ;
421
- this . keepaliveIntervalId . unref ?.( ) ;
422
- /* Don't send a ping immediately because whatever caused us to start
423
- * sending pings should also involve some network activity. */
445
+ if ( this . pendingSendKeepalivePing ) {
446
+ this . pendingSendKeepalivePing = false ;
447
+ this . maybeSendPing ( ) ;
448
+ } else if ( ! this . keepaliveTimerId && ! this . keepaliveTimeoutId ) {
449
+ this . keepaliveTrace ( 'Starting keepalive timer for ' + this . keepaliveTimeMs + 'ms' ) ;
450
+ this . keepaliveTimerId = setTimeout ( ( ) => {
451
+ this . maybeSendPing ( ) ;
452
+ } , this . keepaliveTimeMs ) . unref ?.( ) ;
453
+ }
454
+ /* Otherwise, there is already either a keepalive timer or a ping pending,
455
+ * wait for those to resolve. */
424
456
}
425
457
426
- /**
427
- * Stop keepalive pings when terminating a connection. This discards the
428
- * outstanding ping timeout, so it should not be called if the same
429
- * connection will still be used.
430
- */
431
458
private stopKeepalivePings ( ) {
432
- clearInterval ( this . keepaliveIntervalId ) ;
459
+ if ( this . keepaliveTimerId ) {
460
+ clearTimeout ( this . keepaliveTimerId ) ;
461
+ this . keepaliveTimerId = null ;
462
+ }
433
463
this . clearKeepaliveTimeout ( ) ;
434
464
}
435
465
436
466
private removeActiveCall ( call : Http2SubchannelCall ) {
437
467
this . activeCalls . delete ( call ) ;
438
468
if ( this . activeCalls . size === 0 ) {
439
469
this . session . unref ( ) ;
440
- if ( ! this . keepaliveWithoutCalls ) {
441
- this . stopKeepalivePings ( ) ;
442
- }
443
470
}
444
471
}
445
472
446
473
private addActiveCall ( call : Http2SubchannelCall ) {
447
- if ( this . activeCalls . size === 0 ) {
474
+ this . activeCalls . add ( call ) ;
475
+ if ( this . activeCalls . size === 1 ) {
448
476
this . session . ref ( ) ;
449
477
if ( ! this . keepaliveWithoutCalls ) {
450
- this . startKeepalivePings ( ) ;
478
+ this . maybeStartKeepalivePingTimer ( ) ;
451
479
}
452
480
}
453
- this . activeCalls . add ( call ) ;
454
481
}
455
482
456
483
createCall (
0 commit comments