@@ -435,6 +435,14 @@ export class Server {
435
435
) ;
436
436
}
437
437
438
+ private keepaliveTrace ( text : string ) : void {
439
+ logging . trace (
440
+ LogVerbosity . DEBUG ,
441
+ 'keepalive' ,
442
+ '(' + this . channelzRef . id + ') ' + text
443
+ ) ;
444
+ }
445
+
438
446
addProtoService ( ) : never {
439
447
throw new Error ( 'Not implemented. Use addService() instead' ) ;
440
448
}
@@ -1376,8 +1384,7 @@ export class Server {
1376
1384
1377
1385
let connectionAgeTimer : NodeJS . Timeout | null = null ;
1378
1386
let connectionAgeGraceTimer : NodeJS . Timeout | null = null ;
1379
- let keeapliveTimeTimer : NodeJS . Timeout | null = null ;
1380
- let keepaliveTimeoutTimer : NodeJS . Timeout | null = null ;
1387
+ let keepaliveTimer : NodeJS . Timeout | null = null ;
1381
1388
let sessionClosedByServer = false ;
1382
1389
1383
1390
const idleTimeoutObj = this . enableIdleTimeout ( session ) ;
@@ -1420,41 +1427,90 @@ export class Server {
1420
1427
connectionAgeTimer . unref ?.( ) ;
1421
1428
}
1422
1429
1423
- if ( this . keepaliveTimeMs < KEEPALIVE_MAX_TIME_MS ) {
1424
- keeapliveTimeTimer = setInterval ( ( ) => {
1425
- keepaliveTimeoutTimer = setTimeout ( ( ) => {
1426
- sessionClosedByServer = true ;
1427
- session . close ( ) ;
1428
- } , this . keepaliveTimeoutMs ) ;
1429
- keepaliveTimeoutTimer . unref ?.( ) ;
1430
+ const clearKeepaliveTimeout = ( ) => {
1431
+ if ( keepaliveTimer ) {
1432
+ clearTimeout ( keepaliveTimer ) ;
1433
+ keepaliveTimer = null ;
1434
+ }
1435
+ } ;
1430
1436
1431
- try {
1432
- session . ping (
1433
- ( err : Error | null , duration : number , payload : Buffer ) => {
1434
- if ( keepaliveTimeoutTimer ) {
1435
- clearTimeout ( keepaliveTimeoutTimer ) ;
1436
- }
1437
-
1438
- if ( err ) {
1439
- sessionClosedByServer = true ;
1440
- this . trace (
1441
- 'Connection dropped due to error of a ping frame ' +
1442
- err . message +
1443
- ' return in ' +
1444
- duration
1445
- ) ;
1446
- session . close ( ) ;
1447
- }
1437
+ const canSendPing = ( ) => {
1438
+ return (
1439
+ ! session . destroyed &&
1440
+ this . keepaliveTimeMs < KEEPALIVE_MAX_TIME_MS &&
1441
+ this . keepaliveTimeMs > 0
1442
+ ) ;
1443
+ } ;
1444
+
1445
+ /* eslint-disable-next-line prefer-const */
1446
+ let sendPing : ( ) => void ; // hoisted for use in maybeStartKeepalivePingTimer
1447
+
1448
+ const maybeStartKeepalivePingTimer = ( ) => {
1449
+ if ( ! canSendPing ( ) ) {
1450
+ return ;
1451
+ }
1452
+ this . keepaliveTrace (
1453
+ 'Starting keepalive timer for ' + this . keepaliveTimeMs + 'ms'
1454
+ ) ;
1455
+ keepaliveTimer = setTimeout ( ( ) => {
1456
+ clearKeepaliveTimeout ( ) ;
1457
+ sendPing ( ) ;
1458
+ } , this . keepaliveTimeMs ) ;
1459
+ keepaliveTimer . unref ?.( ) ;
1460
+ } ;
1461
+
1462
+ sendPing = ( ) => {
1463
+ if ( ! canSendPing ( ) ) {
1464
+ return ;
1465
+ }
1466
+ this . keepaliveTrace (
1467
+ 'Sending ping with timeout ' + this . keepaliveTimeoutMs + 'ms'
1468
+ ) ;
1469
+ let pingSendError = '' ;
1470
+ try {
1471
+ const pingSentSuccessfully = session . ping (
1472
+ ( err : Error | null , duration : number , payload : Buffer ) => {
1473
+ clearKeepaliveTimeout ( ) ;
1474
+ if ( err ) {
1475
+ this . keepaliveTrace ( 'Ping failed with error: ' + err . message ) ;
1476
+ sessionClosedByServer = true ;
1477
+ session . close ( ) ;
1478
+ } else {
1479
+ this . keepaliveTrace ( 'Received ping response' ) ;
1480
+ maybeStartKeepalivePingTimer ( ) ;
1448
1481
}
1449
- ) ;
1450
- } catch ( e ) {
1451
- clearTimeout ( keepaliveTimeoutTimer ) ;
1452
- // The ping can't be sent because the session is already closed
1453
- session . destroy ( ) ;
1482
+ }
1483
+ ) ;
1484
+ if ( ! pingSentSuccessfully ) {
1485
+ pingSendError = 'Ping returned false' ;
1454
1486
}
1455
- } , this . keepaliveTimeMs ) ;
1456
- keeapliveTimeTimer . unref ?.( ) ;
1457
- }
1487
+ } catch ( e ) {
1488
+ // grpc/grpc-node#2139
1489
+ pingSendError =
1490
+ ( e instanceof Error ? e . message : '' ) || 'Unknown error' ;
1491
+ }
1492
+
1493
+ if ( pingSendError ) {
1494
+ this . keepaliveTrace ( 'Ping send failed: ' + pingSendError ) ;
1495
+ this . trace (
1496
+ 'Connection dropped due to ping send error: ' + pingSendError
1497
+ ) ;
1498
+ sessionClosedByServer = true ;
1499
+ session . close ( ) ;
1500
+ return ;
1501
+ }
1502
+
1503
+ keepaliveTimer = setTimeout ( ( ) => {
1504
+ clearKeepaliveTimeout ( ) ;
1505
+ this . keepaliveTrace ( 'Ping timeout passed without response' ) ;
1506
+ this . trace ( 'Connection dropped by keepalive timeout' ) ;
1507
+ sessionClosedByServer = true ;
1508
+ session . close ( ) ;
1509
+ } , this . keepaliveTimeoutMs ) ;
1510
+ keepaliveTimer . unref ?.( ) ;
1511
+ } ;
1512
+
1513
+ maybeStartKeepalivePingTimer ( ) ;
1458
1514
1459
1515
session . on ( 'close' , ( ) => {
1460
1516
if ( ! sessionClosedByServer ) {
@@ -1471,12 +1527,7 @@ export class Server {
1471
1527
clearTimeout ( connectionAgeGraceTimer ) ;
1472
1528
}
1473
1529
1474
- if ( keeapliveTimeTimer ) {
1475
- clearInterval ( keeapliveTimeTimer ) ;
1476
- if ( keepaliveTimeoutTimer ) {
1477
- clearTimeout ( keepaliveTimeoutTimer ) ;
1478
- }
1479
- }
1530
+ clearKeepaliveTimeout ( ) ;
1480
1531
1481
1532
if ( idleTimeoutObj !== null ) {
1482
1533
clearTimeout ( idleTimeoutObj . timeout ) ;
@@ -1521,8 +1572,7 @@ export class Server {
1521
1572
1522
1573
let connectionAgeTimer : NodeJS . Timeout | null = null ;
1523
1574
let connectionAgeGraceTimer : NodeJS . Timeout | null = null ;
1524
- let keeapliveTimeTimer : NodeJS . Timeout | null = null ;
1525
- let keepaliveTimeoutTimer : NodeJS . Timeout | null = null ;
1575
+ let keepaliveTimeout : NodeJS . Timeout | null = null ;
1526
1576
let sessionClosedByServer = false ;
1527
1577
1528
1578
const idleTimeoutObj = this . enableIdleTimeout ( session ) ;
@@ -1564,49 +1614,103 @@ export class Server {
1564
1614
connectionAgeTimer . unref ?.( ) ;
1565
1615
}
1566
1616
1567
- if ( this . keepaliveTimeMs < KEEPALIVE_MAX_TIME_MS ) {
1568
- keeapliveTimeTimer = setInterval ( ( ) => {
1569
- keepaliveTimeoutTimer = setTimeout ( ( ) => {
1570
- sessionClosedByServer = true ;
1571
- this . channelzTrace . addTrace (
1572
- 'CT_INFO' ,
1573
- 'Connection dropped by keepalive timeout from ' + clientAddress
1574
- ) ;
1617
+ const clearKeepaliveTimeout = ( ) => {
1618
+ if ( keepaliveTimeout ) {
1619
+ clearTimeout ( keepaliveTimeout ) ;
1620
+ keepaliveTimeout = null ;
1621
+ }
1622
+ } ;
1623
+
1624
+ const canSendPing = ( ) => {
1625
+ return (
1626
+ ! session . destroyed &&
1627
+ this . keepaliveTimeMs < KEEPALIVE_MAX_TIME_MS &&
1628
+ this . keepaliveTimeMs > 0
1629
+ ) ;
1630
+ } ;
1575
1631
1576
- session . close ( ) ;
1577
- } , this . keepaliveTimeoutMs ) ;
1578
- keepaliveTimeoutTimer . unref ?.( ) ;
1632
+ /* eslint-disable-next-line prefer-const */
1633
+ let sendPing : ( ) => void ; // hoisted for use in maybeStartKeepalivePingTimer
1579
1634
1580
- try {
1581
- session . ping (
1582
- ( err : Error | null , duration : number , payload : Buffer ) => {
1583
- if ( keepaliveTimeoutTimer ) {
1584
- clearTimeout ( keepaliveTimeoutTimer ) ;
1585
- }
1586
-
1587
- if ( err ) {
1588
- sessionClosedByServer = true ;
1589
- this . channelzTrace . addTrace (
1590
- 'CT_INFO' ,
1591
- 'Connection dropped due to error of a ping frame ' +
1592
- err . message +
1593
- ' return in ' +
1594
- duration
1595
- ) ;
1596
-
1597
- session . close ( ) ;
1598
- }
1635
+ const maybeStartKeepalivePingTimer = ( ) => {
1636
+ if ( ! canSendPing ( ) ) {
1637
+ return ;
1638
+ }
1639
+ this . keepaliveTrace (
1640
+ 'Starting keepalive timer for ' + this . keepaliveTimeMs + 'ms'
1641
+ ) ;
1642
+ keepaliveTimeout = setTimeout ( ( ) => {
1643
+ clearKeepaliveTimeout ( ) ;
1644
+ sendPing ( ) ;
1645
+ } , this . keepaliveTimeMs ) ;
1646
+ keepaliveTimeout . unref ?.( ) ;
1647
+ } ;
1648
+
1649
+ sendPing = ( ) => {
1650
+ if ( ! canSendPing ( ) ) {
1651
+ return ;
1652
+ }
1653
+ this . keepaliveTrace (
1654
+ 'Sending ping with timeout ' + this . keepaliveTimeoutMs + 'ms'
1655
+ ) ;
1656
+ let pingSendError = '' ;
1657
+ try {
1658
+ const pingSentSuccessfully = session . ping (
1659
+ ( err : Error | null , duration : number , payload : Buffer ) => {
1660
+ clearKeepaliveTimeout ( ) ;
1661
+ if ( err ) {
1662
+ this . keepaliveTrace ( 'Ping failed with error: ' + err . message ) ;
1663
+ this . channelzTrace . addTrace (
1664
+ 'CT_INFO' ,
1665
+ 'Connection dropped due to error of a ping frame ' +
1666
+ err . message +
1667
+ ' return in ' +
1668
+ duration
1669
+ ) ;
1670
+ sessionClosedByServer = true ;
1671
+ session . close ( ) ;
1672
+ } else {
1673
+ this . keepaliveTrace ( 'Received ping response' ) ;
1674
+ maybeStartKeepalivePingTimer ( ) ;
1599
1675
}
1600
- ) ;
1601
- channelzSessionInfo . keepAlivesSent += 1 ;
1602
- } catch ( e ) {
1603
- clearTimeout ( keepaliveTimeoutTimer ) ;
1604
- // The ping can't be sent because the session is already closed
1605
- session . destroy ( ) ;
1676
+ }
1677
+ ) ;
1678
+ if ( ! pingSentSuccessfully ) {
1679
+ pingSendError = 'Ping returned false' ;
1606
1680
}
1607
- } , this . keepaliveTimeMs ) ;
1608
- keeapliveTimeTimer . unref ?.( ) ;
1609
- }
1681
+ } catch ( e ) {
1682
+ // grpc/grpc-node#2139
1683
+ pingSendError =
1684
+ ( e instanceof Error ? e . message : '' ) || 'Unknown error' ;
1685
+ }
1686
+
1687
+ if ( pingSendError ) {
1688
+ this . keepaliveTrace ( 'Ping send failed: ' + pingSendError ) ;
1689
+ this . channelzTrace . addTrace (
1690
+ 'CT_INFO' ,
1691
+ 'Connection dropped due to ping send error: ' + pingSendError
1692
+ ) ;
1693
+ sessionClosedByServer = true ;
1694
+ session . close ( ) ;
1695
+ return ;
1696
+ }
1697
+
1698
+ channelzSessionInfo . keepAlivesSent += 1 ;
1699
+
1700
+ keepaliveTimeout = setTimeout ( ( ) => {
1701
+ clearKeepaliveTimeout ( ) ;
1702
+ this . keepaliveTrace ( 'Ping timeout passed without response' ) ;
1703
+ this . channelzTrace . addTrace (
1704
+ 'CT_INFO' ,
1705
+ 'Connection dropped by keepalive timeout from ' + clientAddress
1706
+ ) ;
1707
+ sessionClosedByServer = true ;
1708
+ session . close ( ) ;
1709
+ } , this . keepaliveTimeoutMs ) ;
1710
+ keepaliveTimeout . unref ?.( ) ;
1711
+ } ;
1712
+
1713
+ maybeStartKeepalivePingTimer ( ) ;
1610
1714
1611
1715
session . on ( 'close' , ( ) => {
1612
1716
if ( ! sessionClosedByServer ) {
@@ -1627,12 +1731,7 @@ export class Server {
1627
1731
clearTimeout ( connectionAgeGraceTimer ) ;
1628
1732
}
1629
1733
1630
- if ( keeapliveTimeTimer ) {
1631
- clearInterval ( keeapliveTimeTimer ) ;
1632
- if ( keepaliveTimeoutTimer ) {
1633
- clearTimeout ( keepaliveTimeoutTimer ) ;
1634
- }
1635
- }
1734
+ clearKeepaliveTimeout ( ) ;
1636
1735
1637
1736
if ( idleTimeoutObj !== null ) {
1638
1737
clearTimeout ( idleTimeoutObj . timeout ) ;
0 commit comments