diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js index 90727194246450..75f07c8d7e5871 100644 --- a/lib/internal/quic/core.js +++ b/lib/internal/quic/core.js @@ -287,24 +287,10 @@ function onSessionReady(handle) { process.nextTick(emit.bind(socket, 'session', session)); } -// Called when the session needs to be closed and destroyed. -// If silent is true, then the session is going to be closed -// immediately without sending any CONNECTION_CLOSE to the -// connected peer. If silent is false, a CONNECTION_CLOSE -// is going to be sent to the peer. +// Called when the C++ QuicSession::Close() method has been called. +// Synchronously cleanup and destroy the JavaScript QuicSession. function onSessionClose(code, family, silent, statelessReset) { - if (this[owner_symbol]) { - if (silent) { - this[owner_symbol][kDestroy](statelessReset, family, code); - } else { - this[owner_symbol][kClose](family, code); - } - return; - } - // When there's no owner_symbol, the session was closed - // before it could be fully set up. Just immediately - // close everything down on the native side. - this.destroy(code, family); + this[owner_symbol][kDestroy](code, family, silent, statelessReset); } // Called by the C++ internals when a QuicSession has been destroyed. @@ -1654,6 +1640,7 @@ class QuicSession extends EventEmitter { maxPacketLength: NGTCP2_DEFAULT_MAX_PKTLEN, servername: undefined, socket: undefined, + silentClose: false, statelessReset: false, stats: undefined, pendingStreams: new Set(), @@ -1736,46 +1723,15 @@ class QuicSession extends EventEmitter { // Causes the QuicSession to be immediately destroyed, but with // additional metadata set. - [kDestroy](statelessReset, family, code) { + [kDestroy](code, family, silent, statelessReset) { const state = this[kInternalState]; - state.statelessReset = statelessReset; state.closeCode = code; state.closeFamily = family; + state.silentClose = silent; + state.statelessReset = statelessReset; this.destroy(); } - // Immediate close has been initiated for the session. Any - // still open QuicStreams must be abandoned and shutdown - // with RESET_STREAM and STOP_SENDING frames transmitted - // as appropriate. Once the streams have been shutdown, a - // CONNECTION_CLOSE will be generated and sent, switching - // the session into the closing period. - [kClose](family, code) { - const state = this[kInternalState]; - // Do nothing if the QuicSession has already been destroyed. - if (state.destroyed) - return; - - // Set the close code and family so we can keep track. - state.closeCode = code; - state.closeFamily = family; - - // Shutdown all pending streams. These are Streams that - // have been created but do not yet have a handle assigned. - for (const stream of state.pendingStreams) - stream[kClose](family, code); - - // Shutdown all of the remaining streams - for (const stream of state.streams.values()) - stream[kClose](family, code); - - // By this point, all necessary RESET_STREAM and - // STOP_SENDING frames ought to have been sent, - // so now we just trigger sending of the - // CONNECTION_CLOSE frame. - this[kHandle].close(family, code); - } - // Closes the specified stream with the given code. The // QuicStream object will be destroyed. [kStreamClose](id, code) { @@ -1873,14 +1829,6 @@ class QuicSession extends EventEmitter { this[kInternalState].streams.set(id, stream); } - // The QuicSession will be destroyed if closing has been - // called and there are no remaining streams - [kMaybeDestroy]() { - const state = this[kInternalState]; - if (state.closing && state.streams.size === 0) - this.destroy(); - } - // Called when a client QuicSession has opted to use the // server provided preferred address. This is a purely // informationational notification. It is not called on @@ -1890,6 +1838,17 @@ class QuicSession extends EventEmitter { emit.bind(this, 'usePreferredAddress', address)); } + // The QuicSession will be destroyed if close() has been + // called and there are no remaining streams + [kMaybeDestroy]() { + const state = this[kInternalState]; + if (state.closing && state.streams.size === 0) { + this.destroy(); + return true; + } + return false; + } + // Closing allows any existing QuicStream's to complete // normally but disallows any new QuicStreams from being // opened. Calls to openStream() will fail, and new streams @@ -1910,27 +1869,27 @@ class QuicSession extends EventEmitter { // has been destroyed if (state.closing) return; - state.closing = true; - this[kHandle].gracefulClose(); - // See if we can close immediately. - this[kMaybeDestroy](); + // If there are no active streams, we can close immediately, + // otherwise set the graceful close flag. + if (!this[kMaybeDestroy]()) + this[kHandle].gracefulClose(); } // Destroying synchronously shuts down and frees the // QuicSession immediately, even if there are still open // streams. // - // A CONNECTION_CLOSE packet is sent to the - // connected peer and the session is immediately - // destroyed. + // Unless we're in the middle of a silent close, a + // CONNECTION_CLOSE packet will be sent to the connected + // peer and the session will be immediately destroyed. // // If destroy is called with an error argument, the // 'error' event is emitted on next tick. // // Once destroyed, and after the 'error' event (if any), - // the close event is emitted on next tick. + // the 'close' event is emitted on next tick. destroy(error) { const state = this[kInternalState]; // Destroy can only be called once. Multiple calls will be ignored @@ -1939,19 +1898,6 @@ class QuicSession extends EventEmitter { state.destroyed = true; state.closing = false; - if (typeof error === 'number' || - (error != null && - typeof error === 'object' && - !(error instanceof Error))) { - const { - closeCode, - closeFamily - } = validateCloseCode(error); - state.closeCode = closeCode; - state.closeFamily = closeFamily; - error = new ERR_QUIC_ERROR(closeCode, closeFamily); - } - // Destroy any pending streams immediately. These // are streams that have been created but have not // yet been assigned an internal handle. @@ -1965,16 +1911,20 @@ class QuicSession extends EventEmitter { this.removeListener('newListener', onNewListener); this.removeListener('removeListener', onRemoveListener); + // If we are destroying with an error, schedule the + // error to be emitted on process.nextTick. if (error) process.nextTick(emit.bind(this, 'error', error)); const handle = this[kHandle]; + this[kHandle] = undefined; + if (handle !== undefined) { // Copy the stats for use after destruction state.stats = new BigInt64Array(handle.stats); - state.idleTimeout = !!handle.state[IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT]; - // Calling destroy will cause a CONNECTION_CLOSE to be - // sent to the peer and will destroy the QuicSession - // handler immediately. + state.idleTimeout = + Boolean(handle.state[IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT]); + + // Destroy the underlying QuicSession handle handle.destroy(state.closeCode, state.closeFamily); } else { process.nextTick(emit.bind(this, 'close')); @@ -2108,7 +2058,8 @@ class QuicSession extends EventEmitter { const state = this[kInternalState]; return { code: state.closeCode, - family: state.closeFamily + family: state.closeFamily, + silent: state.silentClose, }; } diff --git a/src/quic/node_quic_session-inl.h b/src/quic/node_quic_session-inl.h index 339df8210f4a09..73db45ca56deb1 100644 --- a/src/quic/node_quic_session-inl.h +++ b/src/quic/node_quic_session-inl.h @@ -348,7 +348,7 @@ void QuicSession::OnIdleTimeout() { if (!is_destroyed()) { state_[IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT] = 1; Debug(this, "Idle timeout"); - SilentClose(); + Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); } } diff --git a/src/quic/node_quic_session.cc b/src/quic/node_quic_session.cc index 5f102eb2f9362f..8263abc3cc1cab 100644 --- a/src/quic/node_quic_session.cc +++ b/src/quic/node_quic_session.cc @@ -1258,7 +1258,7 @@ bool QuicApplication::SendPendingData() { // to be silent because we can't even send a // CONNECTION_CLOSE since even those require a // packet number. - session()->SilentClose(); + session()->Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); return false; case NGTCP2_ERR_STREAM_DATA_BLOCKED: session()->StreamDataBlocked(stream_data.id); @@ -1629,59 +1629,6 @@ void QuicSession::AddStream(BaseObjectPtr stream) { } } -// Like the silent close, the immediate close must start with -// the JavaScript side, first shutting down any existing -// streams before entering the closing period. Unlike silent -// close, however, all streams are closed using proper -// STOP_SENDING and RESET_STREAM frames and a CONNECTION_CLOSE -// frame is ultimately sent to the peer. This makes the -// naming a bit of a misnomer in that the connection is -// not immediately torn down, but is allowed to drain -// properly per the QUIC spec description of "immediate close". -void QuicSession::ImmediateClose() { - // If ImmediateClose or SilentClose has already been called, - // do not re-enter. - if (is_closing()) - return; - set_closing(); - - QuicError err = last_error(); - Debug(this, "Immediate close with code %" PRIu64 " (%s)", - err.code, - err.family_name()); - - listener()->OnSessionClose(err); -} - -// Silent Close must start with the JavaScript side, which must -// clean up state, abort any still existing QuicSessions, then -// destroy the handle when done. The most important characteristic -// of the SilentClose is that no frames are sent to the peer. -// -// When a valid stateless reset is received, the connection is -// immediately and unrecoverably closed at the ngtcp2 level. -// Specifically, it will be put into the draining_period so -// absolutely no frames can be sent. What we need to do is -// notify the JavaScript side and destroy the connection with -// a flag set that indicates stateless reset. -void QuicSession::SilentClose() { - CHECK(!is_silent_closing()); - set_silent_closing(); - set_closing(); - - QuicError err = last_error(); - Debug(this, - "Silent close with %s code %" PRIu64 " (stateless reset? %s)", - err.family_name(), - err.code, - is_stateless_reset() ? "yes" : "no"); - - int flags = QuicSessionListener::SESSION_CLOSE_FLAG_SILENT; - if (is_stateless_reset()) - flags |= QuicSessionListener::SESSION_CLOSE_FLAG_STATELESS_RESET; - listener()->OnSessionClose(err, flags); -} - // Creates a new stream object and passes it off to the javascript side. // This has to be called from within a handlescope/contextscope. BaseObjectPtr QuicSession::CreateStream(int64_t stream_id) { @@ -1695,32 +1642,80 @@ BaseObjectPtr QuicSession::CreateStream(int64_t stream_id) { return stream; } -// Mark the QuicSession instance destroyed. After this is called, -// the QuicSession instance will be generally unusable but most -// likely will not be immediately freed. -void QuicSession::Destroy() { - if (is_destroyed()) +// Initiate a shutdown of the QuicSession. +void QuicSession::Close(int close_flags) { + CHECK(!is_destroyed()); + bool silent = close_flags & QuicSessionListener::SESSION_CLOSE_FLAG_SILENT; + bool stateless_reset = is_stateless_reset(); + + // If we're not running within a ngtcp2 callback scope, schedule + // a CONNECTION_CLOSE to be sent when Close exits. If we are + // within a ngtcp2 callback scope, sending the CONNECTION_CLOSE + // will be deferred. + ConnectionCloseScope close_scope(this, silent); + + // Once Close has been called, we cannot re-enter + if (UNLIKELY(is_closing())) return; - // If we're not in the closing or draining periods, - // then we should at least attempt to send a connection - // close to the peer. - if (!Ngtcp2CallbackScope::InNgtcp2CallbackScope(this) && - !is_in_closing_period() && - !is_in_draining_period()) { - Debug(this, "Making attempt to send a connection close"); - set_last_error(); - SendConnectionClose(); - } + set_closing(); + set_silent_closing(silent); - // Streams should have already been destroyed by this point. - CHECK(streams_.empty()); + if (stateless_reset && silent) + close_flags |= QuicSessionListener::SESSION_CLOSE_FLAG_STATELESS_RESET; + + QuicError error = last_error(); + Debug(this, "Closing with code %" PRIu64 + " (family: %s, silent: %s, stateless reset: %s)", + error.code, + error.family_name(), + silent ? "Y" : "N", + stateless_reset ? "Y" : "N"); + + // Ensure that the QuicSession is not freed at least until after we + // exit this scope. + BaseObjectPtr ptr(this); + + // If the QuicSession has been wrapped by a JS object, we have to + // notify the JavaScript side that the session is being closed. + // If it hasn't yet been wrapped, we can skip the call and and + // go straight to destroy. + if (is_wrapped()) + listener()->OnSessionClose(error, close_flags); + else + Destroy(); + + // At this point, the QuicSession should have been destroyed, indicating + // that all cleanup on the JavaScript side has completed and the + // QuicSession::Destroy() method has been called. + CHECK(is_destroyed()); +} + +// Mark the QuicSession instance destroyed. This will either be invoked +// synchronously within the callstack of the QuicSession::Close() method +// or not. If it is invoked within QuicSession::Close(), the +// QuicSession::Close() will handle sending the CONNECTION_CLOSE +// frame. +void QuicSession::Destroy() { + if (is_destroyed()) + return; // Mark the session destroyed. set_destroyed(); set_closing(false); set_graceful_closing(false); + // TODO(@jasnell): Allow overriding the close code + + // If we're not already in a ConnectionCloseScope, schedule + // sending a CONNECTION_CLOSE when destroy exits. If we are + // running within an ngtcp2 callback scope, sending the + // CONNECTION_CLOSE will be deferred. + ConnectionCloseScope close_scope(this, is_silent_closing()); + + // All existing streams should have already been destroyed + CHECK(streams_.empty()); + // Stop and free the idle and retransmission timers if they are active. StopIdleTimer(); StopRetransmitTimer(); @@ -1731,7 +1726,8 @@ void QuicSession::Destroy() { // the QuicSession from the QuicSocket will free // that pointer, allowing the QuicSession to be // deconstructed once the stack unwinds and any - // remaining shared_ptr instances fall out of scope. + // remaining BaseObjectPtr instances + // fall out of scope. RemoveFromSocket(); } @@ -1758,7 +1754,7 @@ void QuicSession::HandleError() { if (!SendConnectionClose()) { set_last_error(QUIC_ERROR_SESSION, NGTCP2_ERR_INTERNAL); - ImmediateClose(); + Close(); } } @@ -1933,7 +1929,6 @@ bool QuicSession::Receive( // in the closing period, a CONNECTION_CLOSE has not yet // been sent to the peer. Let's attempt to send one. if (!is_in_closing_period() && !is_in_draining_period()) { - Debug(this, "Attempting to send connection close"); set_last_error(); SendConnectionClose(); } @@ -1949,7 +1944,7 @@ bool QuicSession::Receive( // absolutely nothing left for us to do except silently close // and destroy this QuicSession. GetConnectionCloseInfo(); - SilentClose(); + Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); return true; } Debug(this, "Sending pending data after processing packet"); @@ -1991,7 +1986,7 @@ bool QuicSession::ReceivePacket( // then immediately close the connection. if (err == NGTCP2_ERR_RETRY && is_server()) { socket()->SendRetry(scid_, dcid_, local_address_, remote_address_); - SilentClose(); + Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); break; } set_last_error(QUIC_ERROR_SESSION, err); @@ -2154,35 +2149,17 @@ bool QuicSession::SendConnectionClose() { return true; } + UpdateIdleTimer(); switch (crypto_context_->side()) { case NGTCP2_CRYPTO_SIDE_SERVER: { - // If we're not already in the closing period, - // first attempt to write any pending packets, then - // start the closing period. If closing period has - // already started, skip this. - if (!is_in_closing_period() && - (!WritePackets("server connection close - write packets") || - !StartClosingPeriod())) { - return false; - } - - UpdateIdleTimer(); + if (!is_in_closing_period() && !StartClosingPeriod()) + return false; CHECK_GT(conn_closebuf_->length(), 0); - return SendPacket(QuicPacket::Copy(conn_closebuf_)); } case NGTCP2_CRYPTO_SIDE_CLIENT: { - UpdateIdleTimer(); 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")) { - return false; - } ssize_t nwrite = SelectCloseFn(error.family)( connection(), @@ -2191,7 +2168,7 @@ bool QuicSession::SendConnectionClose() { max_pktlen_, error.code, uv_hrtime()); - if (nwrite < 0) { + if (UNLIKELY(nwrite < 0)) { Debug(this, "Error writing connection close: %d", nwrite); set_last_error(QUIC_ERROR_SESSION, static_cast(nwrite)); return false; @@ -2233,7 +2210,6 @@ void QuicSession::UsePreferredAddressStrategy( // Passes a serialized packet to the associated QuicSocket. bool QuicSession::SendPacket(std::unique_ptr packet) { - CHECK(!is_destroyed()); CHECK(!is_in_draining_period()); // There's nothing to send. @@ -2391,7 +2367,7 @@ bool QuicSession::StartClosingPeriod() { if (nwrite < 0) { if (nwrite == NGTCP2_ERR_PKT_NUM_EXHAUSTED) { set_last_error(QUIC_ERROR_SESSION, NGTCP2_ERR_PKT_NUM_EXHAUSTED); - SilentClose(); + Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); } else { set_last_error(QUIC_ERROR_SESSION, static_cast(nwrite)); } @@ -2516,16 +2492,11 @@ void QuicSession::UpdateIdleTimer() { bool QuicSession::WritePackets(const char* diagnostic_label) { CHECK(!Ngtcp2CallbackScope::InNgtcp2CallbackScope(this)); - // During the draining period, we must not send any frames at all. - if (is_in_draining_period()) + // During either the draining or closing period, + // we are not permitted to send any additional packets. + if (is_in_draining_period() || is_in_closing_period()) return true; - // During the closing period, we are only permitted to send - // CONNECTION_CLOSE frames. - if (is_in_closing_period()) { - return SendConnectionClose(); - } - // Otherwise, serialize and send pending frames QuicPathStorage path; for (;;) { @@ -2541,9 +2512,11 @@ bool QuicSession::WritePackets(const char* diagnostic_label) { packet->data(), max_pktlen_, uv_hrtime()); + if (nwrite <= 0) { switch (nwrite) { case 0: + // There was nothing to write. return true; case NGTCP2_ERR_PKT_NUM_EXHAUSTED: // There is a finite number of packets that can be sent @@ -2553,7 +2526,7 @@ bool QuicSession::WritePackets(const char* diagnostic_label) { // to be silent because we can't even send a // CONNECTION_CLOSE since even those require a // packet number. - SilentClose(); + Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); return false; default: set_last_error(QUIC_ERROR_SESSION, static_cast(nwrite)); @@ -3359,23 +3332,6 @@ void QuicSessionSetSocket(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(session->set_socket(socket)); } -// Perform an immediate close on the QuicSession, causing a -// CONNECTION_CLOSE frame to be scheduled and sent and starting -// the closing period for this session. The name "ImmediateClose" -// is a bit of an unfortunate misnomer as the session will not -// be immediately shutdown. The naming is pulled from the QUIC -// spec to indicate a state where the session immediately enters -// the closing period, but the session will not be destroyed -// until either the idle timeout fires or destroy is explicitly -// called. -void QuicSessionClose(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - QuicSession* session; - ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - session->set_last_error(QuicError(env, args[0], args[1])); - session->SendConnectionClose(); -} - // GracefulClose flips a flag that prevents new local streams // from being opened and new remote streams from being received. It is // important to note that this does *NOT* send a CONNECTION_CLOSE packet @@ -3445,7 +3401,7 @@ void QuicSessionSilentClose(const FunctionCallbackInfo& args) { ProcessEmitWarning( session->env(), "Forcing silent close of QuicSession for testing purposes only"); - session->SilentClose(); + session->Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); } // TODO(addaleax): This is a temporary solution for testing and should be @@ -3592,7 +3548,6 @@ void NewQuicClientSession(const FunctionCallbackInfo& args) { // Add methods that are shared by both client and server QuicSessions void AddMethods(Environment* env, Local session) { - env->SetProtoMethod(session, "close", QuicSessionClose); env->SetProtoMethod(session, "destroy", QuicSessionDestroy); env->SetProtoMethod(session, "getRemoteAddress", QuicSessionGetRemoteAddress); env->SetProtoMethod(session, "getCertificate", QuicSessionGetCertificate); diff --git a/src/quic/node_quic_session.h b/src/quic/node_quic_session.h index 3087ecf12c947b..c68291aed67858 100644 --- a/src/quic/node_quic_session.h +++ b/src/quic/node_quic_session.h @@ -691,11 +691,13 @@ class QuicApplication : public MemoryRetainer, // QUICSESSION_FLAGS are converted into is_{name}() and set_{name}(bool on) // accessors on the QuicSession class. #define QUICSESSION_FLAGS(V) \ + V(WRAPPED, wrapped) \ V(CLOSING, closing) \ V(GRACEFUL_CLOSING, graceful_closing) \ V(DESTROYED, destroyed) \ V(TRANSPORT_PARAMS_SET, transport_params_set) \ V(NGTCP2_CALLBACK, in_ngtcp2_callback) \ + V(CONNECTION_CLOSE_SCOPE, in_connection_close_scope) \ V(SILENT_CLOSE, silent_closing) \ V(STATELESS_RESET, stateless_reset) @@ -847,7 +849,7 @@ class QuicSession : public AsyncWrap, #undef V // Returns true if the QuicSession has entered the - // closing period following a call to ImmediateClose. + // closing period after sending a CONNECTION_CLOSE. // While true, the QuicSession is only permitted to // transmit CONNECTION_CLOSE frames until either the // idle timeout period elapses or until the QuicSession @@ -871,7 +873,7 @@ class QuicSession : public AsyncWrap, // be immediately closed once there are no remaining streams. Note // that no notification is given to the connecting peer that we're // in a graceful closing state. A CONNECTION_CLOSE will be sent only - // once ImmediateClose() is called. + // once Close() is called. inline void StartGracefulClose(); QuicError last_error() const { return last_error_; } @@ -1040,17 +1042,16 @@ class QuicSession : public AsyncWrap, inline void DecreaseAllocatedSize(size_t size); - // Triggers an "immediate close" on the QuicSession. - // This will round trip through JavaScript, causing - // all currently open streams to be closed and ultimately - // send a CONNECTION_CLOSE to the connected peer before - // terminating the connection. - void ImmediateClose(); - - // Silently and immediately close the QuicSession. This is - // typically only done during an idle timeout or when sending - // a retry packet. - void SilentClose(); + // Initiate closing of the QuicSession. This will round trip + // through JavaScript, causing all currently opened streams + // to be closed. If the SESSION_CLOSE_FLAG_SILENT flag is + // set, the connected peer will not be notified, otherwise + // an attempt will be made to send a CONNECTION_CLOSE frame + // to the peer. If Close is called while within the ngtcp2 + // callback scope, sending the CONNECTION_CLOSE will be + // deferred until the ngtcp2 callback scope exits. + inline void Close( + int close_flags = QuicSessionListener::SESSION_CLOSE_FLAG_NONE); void PushListener(QuicSessionListener* listener); @@ -1087,12 +1088,48 @@ class QuicSession : public AsyncWrap, } ~SendSessionScope() { - if (!Ngtcp2CallbackScope::InNgtcp2CallbackScope(session_.get())) - session_->SendPendingData(); + if (Ngtcp2CallbackScope::InNgtcp2CallbackScope(session_.get()) || + session_->is_in_closing_period() || + session_->is_in_draining_period()) { + return; + } + session_->SendPendingData(); + } + + private: + BaseObjectPtr session_; + }; + + // ConnectionCloseScope triggers sending a CONNECTION_CLOSE + // when not executing within the context of an ngtcp2 callback + // and the session is in the correct state. + class ConnectionCloseScope { + public: + ConnectionCloseScope(QuicSession* session, bool silent = false) + : session_(session), + silent_(silent) { + CHECK(session_); + // If we are already in a ConnectionCloseScope, ignore. + if (session_->is_in_connection_close_scope()) + silent_ = true; + else + session_->set_in_connection_close_scope(); + } + + ~ConnectionCloseScope() { + if (silent_ || + Ngtcp2CallbackScope::InNgtcp2CallbackScope(session_.get()) || + session_->is_in_closing_period() || + session_->is_in_draining_period()) { + return; + } + session_->set_in_connection_close_scope(false); + bool ret = session_->SendConnectionClose(); } private: BaseObjectPtr session_; + bool silent_ = false; }; // Tracks whether or not we are currently within an ngtcp2 callback diff --git a/src/quic/node_quic_socket.cc b/src/quic/node_quic_socket.cc index 6ebdb30ef8a250..b51edee6c9be9a 100644 --- a/src/quic/node_quic_socket.cc +++ b/src/quic/node_quic_socket.cc @@ -843,6 +843,8 @@ BaseObjectPtr QuicSocket::AcceptInitialPacket( local_addr, remote_addr, NGTCP2_CONNECTION_REFUSED); + } else { + session->set_wrapped(); } return session;