@@ -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,19 @@ class Http2Transport implements Transport {
383
394
this . keepaliveTimeoutId = null ;
384
395
}
385
396
386
- private sendPing ( ) {
397
+ private canSendPing ( ) {
398
+ return (
399
+ this . keepaliveTimeMs > 0 &&
400
+ ( this . keepaliveWithoutCalls || this . activeCalls . size > 0 )
401
+ ) ;
402
+ }
403
+
404
+ private maybeSendPing ( ) {
405
+ this . clearKeepaliveTimer ( ) ;
406
+ if ( ! this . canSendPing ( ) ) {
407
+ this . pendingSendKeepalivePing = true ;
408
+ return ;
409
+ }
387
410
if ( this . channelzEnabled ) {
388
411
this . keepalivesSent += 1 ;
389
412
}
@@ -402,6 +425,7 @@ class Http2Transport implements Transport {
402
425
( err : Error | null , duration : number , payload : Buffer ) => {
403
426
this . keepaliveTrace ( 'Received ping response' ) ;
404
427
this . clearKeepaliveTimeout ( ) ;
428
+ this . maybeStartKeepalivePingTimer ( ) ;
405
429
}
406
430
) ;
407
431
} catch ( e ) {
@@ -411,46 +435,54 @@ class Http2Transport implements Transport {
411
435
}
412
436
}
413
437
414
- private startKeepalivePings ( ) {
415
- if ( this . keepaliveTimeMs < 0 ) {
438
+ /**
439
+ * Starts the keepalive ping timer if appropriate. If the timer already ran
440
+ * out while there were no active requests, instead send a ping immediately.
441
+ * If the ping timer is already running or a ping is currently in flight,
442
+ * instead do nothing and wait for them to resolve.
443
+ */
444
+ private maybeStartKeepalivePingTimer ( ) {
445
+ if ( ! this . canSendPing ( ) ) {
416
446
return ;
417
447
}
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. */
448
+ if ( this . pendingSendKeepalivePing ) {
449
+ this . pendingSendKeepalivePing = false ;
450
+ this . maybeSendPing ( ) ;
451
+ } else if ( ! this . keepaliveTimerId && ! this . keepaliveTimeoutId ) {
452
+ this . keepaliveTrace (
453
+ 'Starting keepalive timer for ' + this . keepaliveTimeMs + 'ms'
454
+ ) ;
455
+ this . keepaliveTimerId = setTimeout ( ( ) => {
456
+ this . maybeSendPing ( ) ;
457
+ } , this . keepaliveTimeMs ) . unref ?.( ) ;
458
+ }
459
+ /* Otherwise, there is already either a keepalive timer or a ping pending,
460
+ * wait for those to resolve. */
424
461
}
425
462
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
463
private stopKeepalivePings ( ) {
432
- clearInterval ( this . keepaliveIntervalId ) ;
464
+ if ( this . keepaliveTimerId ) {
465
+ clearTimeout ( this . keepaliveTimerId ) ;
466
+ this . keepaliveTimerId = null ;
467
+ }
433
468
this . clearKeepaliveTimeout ( ) ;
434
469
}
435
470
436
471
private removeActiveCall ( call : Http2SubchannelCall ) {
437
472
this . activeCalls . delete ( call ) ;
438
473
if ( this . activeCalls . size === 0 ) {
439
474
this . session . unref ( ) ;
440
- if ( ! this . keepaliveWithoutCalls ) {
441
- this . stopKeepalivePings ( ) ;
442
- }
443
475
}
444
476
}
445
477
446
478
private addActiveCall ( call : Http2SubchannelCall ) {
447
- if ( this . activeCalls . size === 0 ) {
479
+ this . activeCalls . add ( call ) ;
480
+ if ( this . activeCalls . size === 1 ) {
448
481
this . session . ref ( ) ;
449
482
if ( ! this . keepaliveWithoutCalls ) {
450
- this . startKeepalivePings ( ) ;
483
+ this . maybeStartKeepalivePingTimer ( ) ;
451
484
}
452
485
}
453
- this . activeCalls . add ( call ) ;
454
486
}
455
487
456
488
createCall (
0 commit comments