From a3a5377818df85825d474704b11ab8fd544cc0ce Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Sat, 18 Jul 2026 10:30:21 +0200 Subject: [PATCH 1/6] quic: changes for nghttp3_conn_close_stream2 nghttp2 will introduce a version 2 callback and function for closing streams. This prepares node.js for the change. Other changes may be required for its full potential. Signed-off-by: Marten Richter --- src/quic/http3.cc | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/quic/http3.cc b/src/quic/http3.cc index b6d876af60f6..cbcbb2f16a72 100644 --- a/src/quic/http3.cc +++ b/src/quic/http3.cc @@ -503,7 +503,8 @@ class Http3ApplicationImpl final : public Session::Application { code = error.code(); } - int rv = nghttp3_conn_close_stream(*this, stream->id(), code); + int rv = nghttp3_conn_close_stream2(*this, NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET, + stream->id(), code, 0); // If the call is successful, Http3Application::OnStreamClose callback will // be invoked when the stream is ready to be closed. We'll handle destroying // the actual Stream object there. @@ -797,16 +798,31 @@ class Http3ApplicationImpl final : public Session::Application { return Http3ConnectionPointer(conn); } - void OnStreamClose(Stream* stream, error_code app_error_code) { - if (app_error_code != NGHTTP3_H3_NO_ERROR) { + void OnStreamClose(Stream* stream, uint32_t flags, + error_code rx_app_error_code, + error_code tx_app_error_code) { + if (flags & NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET) { Debug(&session(), "HTTP/3 application received stream close for stream %" PRIi64 - " with code %" PRIu64, + " with remote error code %" PRIu64, stream->id(), - app_error_code); + rx_app_error_code); + } + if (flags & NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET) { + Debug(&session(), + "HTTP/3 application send stream close for stream %" PRIi64 + " with error code %" PRIu64, + stream->id(), + tx_app_error_code); } auto direction = stream->direction(); - stream->Destroy(QuicError::ForApplication(app_error_code)); + if (flags & NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET) { + stream->Destroy(QuicError::ForApplication(rx_app_error_code)); + } else if (flags & NGHTTP3_STREAM_CLOSE_FLAG_TX_APP_ERROR_CODE_SET) { + stream->Destroy(QuicError::ForApplication(tx_app_error_code)); + } else { + stream->Destroy(); + } ExtendMaxStreams(EndpointLabel::REMOTE, direction, 1); } @@ -1168,13 +1184,16 @@ class Http3ApplicationImpl final : public Session::Application { } static int on_stream_close(nghttp3_conn* conn, + uint32_t flags, stream_id id, - error_code app_error_code, + error_code rx_app_error_code, + error_code tx_app_error_code, void* conn_user_data, void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); if (auto stream = app.session().FindStream(id)) { - app.OnStreamClose(stream.get(), app_error_code); + app.OnStreamClose(stream.get(), flags, rx_app_error_code, + tx_app_error_code); } return NGTCP2_SUCCESS; } @@ -1382,7 +1401,7 @@ class Http3ApplicationImpl final : public Session::Application { static constexpr nghttp3_callbacks kCallbacks = { on_acked_stream_data, - on_stream_close, + nullptr, //nghttp3_stream_close (deprecated) on_receive_data, on_deferred_consume, on_begin_headers, @@ -1400,9 +1419,7 @@ class Http3ApplicationImpl final : public Session::Application { on_end_origin, on_rand, on_receive_settings, -#ifdef NGHTTP3_CALLBACKS_V4 - nullptr, -#endif // NGHTTP3_CALLBACKS_V4 + on_stream_close }; }; From 0dc69ddc1c52e4d5f1150ad1666f206a51a5f87c Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Sat, 18 Jul 2026 10:48:25 +0200 Subject: [PATCH 2/6] quic: fix lint --- src/quic/http3.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/quic/http3.cc b/src/quic/http3.cc index cbcbb2f16a72..65c6fc56439e 100644 --- a/src/quic/http3.cc +++ b/src/quic/http3.cc @@ -503,8 +503,8 @@ class Http3ApplicationImpl final : public Session::Application { code = error.code(); } - int rv = nghttp3_conn_close_stream2(*this, NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET, - stream->id(), code, 0); + int rv = nghttp3_conn_close_stream2(*this, + NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET, stream->id(), code, 0); // If the call is successful, Http3Application::OnStreamClose callback will // be invoked when the stream is ready to be closed. We'll handle destroying // the actual Stream object there. @@ -798,7 +798,7 @@ class Http3ApplicationImpl final : public Session::Application { return Http3ConnectionPointer(conn); } - void OnStreamClose(Stream* stream, uint32_t flags, + void OnStreamClose(Stream* stream, uint32_t flags, error_code rx_app_error_code, error_code tx_app_error_code) { if (flags & NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET) { @@ -1401,7 +1401,7 @@ class Http3ApplicationImpl final : public Session::Application { static constexpr nghttp3_callbacks kCallbacks = { on_acked_stream_data, - nullptr, //nghttp3_stream_close (deprecated) + nullptr, // nghttp3_stream_close (deprecated) on_receive_data, on_deferred_consume, on_begin_headers, From 0d8acbee8306411a735fee2926466f2dea9037ad Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Mon, 20 Jul 2026 06:43:34 +0200 Subject: [PATCH 3/6] quic: Fix lint in src/quic/http3.cc Co-authored-by: James M Snell --- src/quic/http3.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quic/http3.cc b/src/quic/http3.cc index 65c6fc56439e..c6beb15f35bb 100644 --- a/src/quic/http3.cc +++ b/src/quic/http3.cc @@ -821,7 +821,7 @@ class Http3ApplicationImpl final : public Session::Application { } else if (flags & NGHTTP3_STREAM_CLOSE_FLAG_TX_APP_ERROR_CODE_SET) { stream->Destroy(QuicError::ForApplication(tx_app_error_code)); } else { - stream->Destroy(); + stream->Destroy(); } ExtendMaxStreams(EndpointLabel::REMOTE, direction, 1); } From 1a8bf056cfbb5a46959559d88da7cf6be2152c00 Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Sat, 8 Aug 2026 14:43:03 +0200 Subject: [PATCH 4/6] quic: Fix OnStreamClose --- src/quic/http3.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quic/http3.cc b/src/quic/http3.cc index c6beb15f35bb..d7425be63a08 100644 --- a/src/quic/http3.cc +++ b/src/quic/http3.cc @@ -808,7 +808,7 @@ class Http3ApplicationImpl final : public Session::Application { stream->id(), rx_app_error_code); } - if (flags & NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET) { + if (flags & NGHTTP3_STREAM_CLOSE_FLAG_TX_APP_ERROR_CODE_SET) { Debug(&session(), "HTTP/3 application send stream close for stream %" PRIi64 " with error code %" PRIu64, From d1288af813c5e7eb728927f9f3908ffaaf4c28b8 Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Sun, 9 Aug 2026 11:05:44 +0200 Subject: [PATCH 5/6] quic: fix lint --- src/quic/http3.cc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/quic/http3.cc b/src/quic/http3.cc index d7425be63a08..43279d35655e 100644 --- a/src/quic/http3.cc +++ b/src/quic/http3.cc @@ -503,8 +503,11 @@ class Http3ApplicationImpl final : public Session::Application { code = error.code(); } - int rv = nghttp3_conn_close_stream2(*this, - NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET, stream->id(), code, 0); + int rv = nghttp3_conn_close_stream2(*this, + NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET, + stream->id(), + code, + 0); // If the call is successful, Http3Application::OnStreamClose callback will // be invoked when the stream is ready to be closed. We'll handle destroying // the actual Stream object there. @@ -798,7 +801,8 @@ class Http3ApplicationImpl final : public Session::Application { return Http3ConnectionPointer(conn); } - void OnStreamClose(Stream* stream, uint32_t flags, + void OnStreamClose(Stream* stream, + uint32_t flags, error_code rx_app_error_code, error_code tx_app_error_code) { if (flags & NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET) { @@ -1192,8 +1196,8 @@ class Http3ApplicationImpl final : public Session::Application { void* stream_user_data) { NGHTTP3_CALLBACK_SCOPE(app); if (auto stream = app.session().FindStream(id)) { - app.OnStreamClose(stream.get(), flags, rx_app_error_code, - tx_app_error_code); + app.OnStreamClose( + stream.get(), flags, rx_app_error_code, tx_app_error_code); } return NGTCP2_SUCCESS; } @@ -1419,8 +1423,7 @@ class Http3ApplicationImpl final : public Session::Application { on_end_origin, on_rand, on_receive_settings, - on_stream_close - }; + on_stream_close}; }; std::optional ParseHttp3TicketData(const uv_buf_t& data) { From 68750950bcc54045dec8e1b8bbb5121fd7e1b3de Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Sun, 9 Aug 2026 11:21:27 +0200 Subject: [PATCH 6/6] quic: fix lint --- src/quic/http3.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/quic/http3.cc b/src/quic/http3.cc index 43279d35655e..54668a89c90b 100644 --- a/src/quic/http3.cc +++ b/src/quic/http3.cc @@ -503,11 +503,12 @@ class Http3ApplicationImpl final : public Session::Application { code = error.code(); } - int rv = nghttp3_conn_close_stream2(*this, - NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET, - stream->id(), - code, - 0); + int rv = nghttp3_conn_close_stream2( + *this, + NGHTTP3_STREAM_CLOSE_FLAG_RX_APP_ERROR_CODE_SET, + stream->id(), + code, + 0); // If the call is successful, Http3Application::OnStreamClose callback will // be invoked when the stream is ready to be closed. We'll handle destroying // the actual Stream object there.