diff --git a/src/quic/node_quic_session.cc b/src/quic/node_quic_session.cc index 825f81632f..610c19e2e1 100644 --- a/src/quic/node_quic_session.cc +++ b/src/quic/node_quic_session.cc @@ -1997,6 +1997,22 @@ bool QuicSession::SendConnectionClose() { // serialize the connection close once but may end up transmitting // it multiple times; whereas for clients, we will serialize it // once and send once only. + QuicError error = last_error(); + + // If initial keys have not yet been installed, use the alternative + // ImmediateConnectionClose to send a stateless connection close to + // the peer. + if (crypto_context()->write_crypto_level() == + NGTCP2_CRYPTO_LEVEL_INITIAL) { + socket()->ImmediateConnectionClose( + dcid(), + scid_, + local_address_, + remote_address_, + error.code); + return true; + } + switch (crypto_context_->side()) { case NGTCP2_CRYPTO_SIDE_SERVER: { // If we're not already in the closing period, @@ -2005,7 +2021,7 @@ bool QuicSession::SendConnectionClose() { // already started, skip this. if (!is_in_closing_period() && (!WritePackets("server connection close - write packets") || - !StartClosingPeriod())) { + !StartClosingPeriod())) { return false; } @@ -2016,18 +2032,16 @@ bool QuicSession::SendConnectionClose() { } case NGTCP2_CRYPTO_SIDE_CLIENT: { UpdateIdleTimer(); - auto packet = QuicPacket::Create( - "client connection close"); - QuicError error = last_error(); + auto packet = QuicPacket::Create("client connection close"); // If we're not already in the closing period, // first attempt to write any pending packets, then // start the closing period. Note that the behavior // here is different than the server if (!is_in_closing_period() && - !WritePackets("client connection close - write packets")) + !WritePackets("client connection close - write packets")) { return false; - + } ssize_t nwrite = SelectCloseFn(error.family)( connection(), diff --git a/src/quic/node_quic_socket.cc b/src/quic/node_quic_socket.cc index aa25214745..e61ad8f4b9 100644 --- a/src/quic/node_quic_socket.cc +++ b/src/quic/node_quic_socket.cc @@ -693,16 +693,18 @@ bool QuicSocket::SendRetry( // Shutdown a connection prematurely, before a QuicSession is created. void QuicSocket::ImmediateConnectionClose( - const ngtcp2_pkt_hd& hd, + const QuicCID& scid, + const QuicCID& dcid, const SocketAddress& local_addr, const SocketAddress& remote_addr, int32_t reason) { + Debug(this, "Sending stateless connection close to %s", scid); auto packet = QuicPacket::Create("immediate connection close"); ssize_t nwrite = ngtcp2_crypto_write_connection_close( packet->data(), packet->length(), - &hd.scid, - &hd.dcid, + scid.cid(), + dcid.cid(), reason); if (nwrite > 0) { packet->set_length(nwrite); @@ -760,7 +762,12 @@ BaseObjectPtr QuicSocket::AcceptInitialPacket( max_connections_per_host_) { Debug(this, "QuicSocket is busy or connection count exceeded"); IncrementStat(&QuicSocketStats::server_busy_count); - ImmediateConnectionClose(hd, local_addr, remote_addr, NGTCP2_SERVER_BUSY); + ImmediateConnectionClose( + QuicCID(hd.scid), + QuicCID(hd.dcid), + local_addr, + remote_addr, + NGTCP2_SERVER_BUSY); return {}; } @@ -789,7 +796,11 @@ BaseObjectPtr QuicSocket::AcceptInitialPacket( token_secret_, retry_token_expiration_)) { Debug(this, "Invalid retry token was detected. Failing."); - ImmediateConnectionClose(hd, local_addr, remote_addr); + ImmediateConnectionClose( + QuicCID(hd.scid), + QuicCID(hd.dcid), + local_addr, + remote_addr); return {}; } } diff --git a/src/quic/node_quic_socket.h b/src/quic/node_quic_socket.h index 6f4a59093f..756da24a02 100644 --- a/src/quic/node_quic_socket.h +++ b/src/quic/node_quic_socket.h @@ -386,6 +386,13 @@ class QuicSocket : public AsyncWrap, inline void AddEndpoint(QuicEndpoint* endpoint, bool preferred = false); + void ImmediateConnectionClose( + const QuicCID& scid, + const QuicCID& dcid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + int32_t reason = NGTCP2_INVALID_TOKEN); + private: static void OnAlloc( uv_handle_t* handle, @@ -405,11 +412,6 @@ class QuicSocket : public AsyncWrap, const SocketAddress& local_addr, const SocketAddress& remote_addr, unsigned int flags); - void ImmediateConnectionClose( - const ngtcp2_pkt_hd& hd, - const SocketAddress& local_addr, - const SocketAddress& remote_addr, - int32_t reason = NGTCP2_INVALID_TOKEN); BaseObjectPtr AcceptInitialPacket( uint32_t version, diff --git a/test/parallel/test-quic-session-destroy.js b/test/parallel/test-quic-quicsession-server-destroy-early.js similarity index 100% rename from test/parallel/test-quic-session-destroy.js rename to test/parallel/test-quic-quicsession-server-destroy-early.js diff --git a/test/parallel/test-quic-stream-close-early.js b/test/parallel/test-quic-quicstream-close-early.js similarity index 100% rename from test/parallel/test-quic-stream-close-early.js rename to test/parallel/test-quic-quicstream-close-early.js