@@ -1633,7 +1633,8 @@ BaseObjectPtr<QuicStream> QuicSession::CreateStream(int64_t stream_id) {
1633
1633
1634
1634
// Initiate a shutdown of the QuicSession.
1635
1635
void QuicSession::Close (int close_flags) {
1636
- CHECK (!is_destroyed ());
1636
+ if (is_destroyed ())
1637
+ return ;
1637
1638
bool silent = close_flags & QuicSessionListener::SESSION_CLOSE_FLAG_SILENT;
1638
1639
bool stateless_reset = is_stateless_reset ();
1639
1640
@@ -1735,13 +1736,16 @@ bool QuicSession::GetNewConnectionID(
1735
1736
}
1736
1737
1737
1738
void QuicSession::HandleError () {
1738
- if (is_destroyed () || ( is_in_closing_period () && ! is_server ()) )
1739
+ if (is_destroyed ())
1739
1740
return ;
1740
1741
1741
- if (!SendConnectionClose ()) {
1742
- set_last_error (QUIC_ERROR_SESSION, NGTCP2_ERR_INTERNAL);
1743
- Close ();
1744
- }
1742
+ // If the QuicSession is a server, send a CONNECTION_CLOSE. In either
1743
+ // case, the closing timer will be set and the QuicSession will be
1744
+ // destroyed.
1745
+ if (is_server ())
1746
+ SendConnectionClose ();
1747
+ else
1748
+ UpdateClosingTimer ();
1745
1749
}
1746
1750
1747
1751
// The the retransmit libuv timer fires, it will call MaybeTimeout,
@@ -1751,20 +1755,25 @@ void QuicSession::MaybeTimeout() {
1751
1755
if (is_destroyed ())
1752
1756
return ;
1753
1757
uint64_t now = uv_hrtime ();
1754
- bool transmit = false ;
1758
+
1755
1759
if (ngtcp2_conn_loss_detection_expiry (connection ()) <= now) {
1756
1760
Debug (this , " Retransmitting due to loss detection" );
1757
- CHECK_EQ (ngtcp2_conn_on_loss_detection_timer (connection (), now), 0 );
1758
1761
IncrementStat (&QuicSessionStats::loss_retransmit_count);
1759
- transmit = true ;
1760
- } else if (ngtcp2_conn_ack_delay_expiry (connection ()) <= now) {
1762
+ }
1763
+
1764
+ if (ngtcp2_conn_ack_delay_expiry (connection ()) <= now) {
1761
1765
Debug (this , " Retransmitting due to ack delay" );
1762
- ngtcp2_conn_cancel_expired_ack_delay_timer (connection (), now);
1763
1766
IncrementStat (&QuicSessionStats::ack_delay_retransmit_count);
1764
- transmit = true ;
1765
1767
}
1766
- if (transmit)
1767
- SendPendingData ();
1768
+
1769
+ int rv = ngtcp2_conn_handle_expiry (connection (), now);
1770
+ if (rv != 0 ) {
1771
+ Debug (this , " Error handling retransmit timeout: %s" , ngtcp2_strerror (rv));
1772
+ set_last_error (QUIC_ERROR_SESSION, rv);
1773
+ HandleError ();
1774
+ }
1775
+
1776
+ SendPendingData ();
1768
1777
}
1769
1778
1770
1779
bool QuicSession::OpenBidirectionalStream (int64_t * stream_id) {
@@ -1847,16 +1856,7 @@ bool QuicSession::Receive(
1847
1856
Debug (this , " Receiving QUIC packet" );
1848
1857
IncrementStat (&QuicSessionStats::bytes_received, nread);
1849
1858
1850
- // Closing period starts once ngtcp2 has detected that the session
1851
- // is being shutdown locally. Note that this is different that the
1852
- // is_graceful_closing() function, which
1853
- // indicates a graceful shutdown that allows the session and streams
1854
- // to finish naturally. When is_in_closing_period is true, ngtcp2 is
1855
- // actively in the process of shutting down the connection and a
1856
- // CONNECTION_CLOSE has already been sent. The only thing we can do
1857
- // at this point is either ignore the packet or send another
1858
- // CONNECTION_CLOSE.
1859
- if (is_in_closing_period ()) {
1859
+ if (is_in_closing_period () && is_server ()) {
1860
1860
Debug (this , " QUIC packet received while in closing period" );
1861
1861
IncrementConnectionCloseAttempts ();
1862
1862
// For server QuicSession instances, we serialize the connection close
@@ -1866,30 +1866,13 @@ bool QuicSession::Receive(
1866
1866
// every received packet, however, so we use an exponential
1867
1867
// backoff, increasing the ratio of packets received to connection
1868
1868
// close frame sent with every one we send.
1869
- if (!ShouldAttemptConnectionClose ()) {
1870
- Debug (this , " Not sending connection close" );
1869
+ if (UNLIKELY (ShouldAttemptConnectionClose () &&
1870
+ !SendConnectionClose ())) {
1871
+ Debug (this , " Failure trying to send another connection close" );
1871
1872
return false ;
1872
1873
}
1873
- Debug (this , " Sending connection close" );
1874
- return SendConnectionClose ();
1875
- }
1876
-
1877
- // When is_in_draining_period is true, ngtcp2 has received a
1878
- // connection close and we are simply discarding received packets.
1879
- // No outbound packets may be sent. Return true here because
1880
- // the packet was correctly processed, even tho it is being
1881
- // ignored.
1882
- if (is_in_draining_period ()) {
1883
- Debug (this , " QUIC packet received while in draining period" );
1884
- return true ;
1885
1874
}
1886
1875
1887
- // It's possible for the remote address to change from one
1888
- // packet to the next so we have to look at the addr on
1889
- // every packet.
1890
- remote_address_ = remote_addr;
1891
- QuicPath path (local_addr, remote_address_);
1892
-
1893
1876
{
1894
1877
// These are within a scope to ensure that the InternalCallbackScope
1895
1878
// and HandleScope are both exited before continuing on with the
@@ -1901,38 +1884,28 @@ bool QuicSession::Receive(
1901
1884
Debug (this , " Processing received packet" );
1902
1885
HandleScope handle_scope (env ()->isolate ());
1903
1886
InternalCallbackScope callback_scope (this );
1887
+ remote_address_ = remote_addr;
1888
+ QuicPath path (local_addr, remote_address_);
1904
1889
if (!ReceivePacket (&path, data, nread)) {
1905
- Debug (this , " Failure processing received packet (code %" PRIu64 " )" ,
1906
- last_error ().code );
1907
1890
HandleError ();
1908
1891
return false ;
1909
1892
}
1910
1893
}
1911
1894
1912
- if (is_destroyed ()) {
1913
- Debug (this , " Session was destroyed while processing the received packet" );
1914
- // If the QuicSession has been destroyed but it is not
1915
- // in the closing period, a CONNECTION_CLOSE has not yet
1916
- // been sent to the peer. Let's attempt to send one.
1917
- if (!is_in_closing_period () && !is_in_draining_period ()) {
1918
- set_last_error ();
1919
- SendConnectionClose ();
1920
- }
1921
- return true ;
1922
- }
1923
-
1924
1895
// Only send pending data if we haven't entered draining mode.
1925
1896
// We enter the draining period when a CONNECTION_CLOSE has been
1926
1897
// received from the remote peer.
1927
1898
if (is_in_draining_period ()) {
1928
1899
Debug (this , " In draining period after processing packet" );
1929
1900
// If processing the packet puts us into draining period, there's
1930
1901
// absolutely nothing left for us to do except silently close
1931
- // and destroy this QuicSession.
1902
+ // and destroy this QuicSession, which we do by updating the
1903
+ // closing timer.
1932
1904
GetConnectionCloseInfo ();
1933
- Close (QuicSessionListener::SESSION_CLOSE_FLAG_SILENT );
1905
+ UpdateClosingTimer ( );
1934
1906
return true ;
1935
1907
}
1908
+
1936
1909
Debug (this , " Sending pending data after processing packet" );
1937
1910
SendPendingData ();
1938
1911
UpdateIdleTimer ();
@@ -1965,20 +1938,32 @@ bool QuicSession::ReceivePacket(
1965
1938
case NGTCP2_ERR_DRAINING:
1966
1939
case NGTCP2_ERR_RECV_VERSION_NEGOTIATION:
1967
1940
break ;
1941
+ case NGTCP2_ERR_RETRY:
1942
+ // This should only ever happen on the server
1943
+ CHECK (is_server ());
1944
+ socket ()->SendRetry (scid_, dcid_, local_address_, remote_address_);
1945
+ Close (QuicSessionListener::SESSION_CLOSE_FLAG_SILENT);
1946
+ break ;
1947
+ case NGTCP2_ERR_DROP_CONN:
1948
+ Close (QuicSessionListener::SESSION_CLOSE_FLAG_SILENT);
1949
+ break ;
1968
1950
default :
1969
- // Per ngtcp2: If NGTCP2_ERR_RETRY is returned,
1970
- // QuicSession must be a server and must perform
1971
- // address validation by sending a Retry packet
1972
- // then immediately close the connection.
1973
- if (err == NGTCP2_ERR_RETRY && is_server ()) {
1974
- socket ()->SendRetry (scid_, dcid_, local_address_, remote_address_);
1975
- Close (QuicSessionListener::SESSION_CLOSE_FLAG_SILENT);
1976
- break ;
1977
- }
1978
1951
set_last_error (QUIC_ERROR_SESSION, err);
1979
1952
return false ;
1980
1953
}
1981
1954
}
1955
+
1956
+ if (is_destroyed ()) {
1957
+ Debug (this , " Session was destroyed while processing the received packet" );
1958
+ // If the QuicSession has been destroyed but it is not
1959
+ // in the closing period, a CONNECTION_CLOSE has not yet
1960
+ // been sent to the peer. Let's attempt to send one. This
1961
+ // will have the effect of setting the idle timer to the
1962
+ // closing/draining period, after which the QuicSession
1963
+ // will be destroyed.
1964
+ return is_in_closing_period () ? true : SendConnectionClose ();
1965
+ }
1966
+
1982
1967
return true ;
1983
1968
}
1984
1969
@@ -2121,6 +2106,9 @@ bool QuicSession::SendConnectionClose() {
2121
2106
Debug (this , " Connection Close code: %" PRIu64 " (family: %s)" ,
2122
2107
error.code , error.family_name ());
2123
2108
2109
+ Debug (this , " Setting the connection/draining period timer" );
2110
+ UpdateClosingTimer ();
2111
+
2124
2112
// If initial keys have not yet been installed, use the alternative
2125
2113
// ImmediateConnectionClose to send a stateless connection close to
2126
2114
// the peer.
@@ -2135,11 +2123,12 @@ bool QuicSession::SendConnectionClose() {
2135
2123
return true ;
2136
2124
}
2137
2125
2138
- UpdateIdleTimer ();
2139
2126
switch (crypto_context_->side ()) {
2140
2127
case NGTCP2_CRYPTO_SIDE_SERVER: {
2141
- if (!is_in_closing_period () && !StartClosingPeriod ())
2128
+ if (!is_in_closing_period () && !StartClosingPeriod ()) {
2129
+ Close (QuicSessionListener::SESSION_CLOSE_FLAG_SILENT);
2142
2130
return false ;
2131
+ }
2143
2132
CHECK_GT (conn_closebuf_->length (), 0 );
2144
2133
return SendPacket (QuicPacket::Copy (conn_closebuf_));
2145
2134
}
@@ -2157,6 +2146,7 @@ bool QuicSession::SendConnectionClose() {
2157
2146
if (UNLIKELY (nwrite < 0 )) {
2158
2147
Debug (this , " Error writing connection close: %d" , nwrite);
2159
2148
set_last_error (QUIC_ERROR_SESSION, static_cast <int >(nwrite));
2149
+ Close (QuicSessionListener::SESSION_CLOSE_FLAG_SILENT);
2160
2150
return false ;
2161
2151
}
2162
2152
packet->set_length (nwrite);
@@ -2330,16 +2320,12 @@ bool QuicSession::StartClosingPeriod() {
2330
2320
if (is_in_closing_period ())
2331
2321
return true ;
2332
2322
2333
- StopRetransmitTimer ();
2334
- UpdateIdleTimer ();
2335
-
2336
2323
QuicError error = last_error ();
2337
2324
Debug (this , " Closing period has started. Error %d" , error.code );
2338
2325
2339
2326
// Once the CONNECTION_CLOSE packet is written,
2340
2327
// is_in_closing_period will return true.
2341
- conn_closebuf_ = QuicPacket::Create (
2342
- " server connection close" );
2328
+ conn_closebuf_ = QuicPacket::Create (" server connection close" );
2343
2329
ssize_t nwrite =
2344
2330
SelectCloseFn (error.family )(
2345
2331
connection (),
@@ -2349,12 +2335,7 @@ bool QuicSession::StartClosingPeriod() {
2349
2335
error.code ,
2350
2336
uv_hrtime ());
2351
2337
if (nwrite < 0 ) {
2352
- if (nwrite == NGTCP2_ERR_PKT_NUM_EXHAUSTED) {
2353
- set_last_error (QUIC_ERROR_SESSION, NGTCP2_ERR_PKT_NUM_EXHAUSTED);
2354
- Close (QuicSessionListener::SESSION_CLOSE_FLAG_SILENT);
2355
- } else {
2356
- set_last_error (QUIC_ERROR_SESSION, static_cast <int >(nwrite));
2357
- }
2338
+ set_last_error (QUIC_ERROR_SESSION, static_cast <int >(nwrite));
2358
2339
return false ;
2359
2340
}
2360
2341
conn_closebuf_->set_length (nwrite);
@@ -2450,6 +2431,8 @@ void QuicSession::UpdateConnectionID(
2450
2431
// will be silently closed. It is important to update this as activity
2451
2432
// occurs to keep the idle timer from firing.
2452
2433
void QuicSession::UpdateIdleTimer () {
2434
+ if (is_closing_timer_enabled ())
2435
+ return ;
2453
2436
uint64_t now = uv_hrtime ();
2454
2437
uint64_t expiry = ngtcp2_conn_get_idle_expiry (connection ());
2455
2438
// nano to millis
@@ -2459,6 +2442,15 @@ void QuicSession::UpdateIdleTimer() {
2459
2442
idle_.Update (timeout, timeout);
2460
2443
}
2461
2444
2445
+ void QuicSession::UpdateClosingTimer () {
2446
+ set_closing_timer_enabled (true );
2447
+ uint64_t timeout =
2448
+ is_server () ? (ngtcp2_conn_get_pto (connection ()) / 1000000ULL ) * 3 : 0 ;
2449
+ Debug (this , " Setting closing timeout to %" PRIu64, timeout);
2450
+ retransmit_.Stop ();
2451
+ idle_.Update (timeout, 0 );
2452
+ idle_.Ref ();
2453
+ }
2462
2454
2463
2455
// Write any packets current pending for the ngtcp2 connection based on
2464
2456
// the current state of the QuicSession. If the QuicSession is in the
0 commit comments