Skip to content

Commit 9220fa1

Browse files
committed
quic: correct http3 callback and fix revealed errs
The http3 application had misinterpreted some of nghttp3 callbacks regarding stopSending and ResetStream. Actually, these callbacks asks the application to do the action and not informs about an event from the peer. The fixes lead to some failures of the automated tests, uncovering some problems: First headers, and pendingTrailers were reset, when the internal object went away, though the test wanted to read them. Second, during a graceful session shutdown, the implemented did not waited for all stream to be removed, but only one. Fixes: #63657 Signed-off-by: Marten Richter <marten.richter@freenet.de>
1 parent ba65cd6 commit 9220fa1

5 files changed

Lines changed: 62 additions & 39 deletions

File tree

lib/internal/quic/quic.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2705,8 +2705,8 @@ class QuicStream {
27052705
inner.ontrailers = undefined;
27062706
inner.oninfo = undefined;
27072707
inner.onwanttrailers = undefined;
2708-
inner.headers = undefined;
2709-
inner.pendingTrailers = undefined;
2708+
// do not reset headers here, this is still important information
2709+
// the same applies for pendingTrailers
27102710
this.#handle = undefined;
27112711
if (inner.fileHandle !== undefined) {
27122712
// Close the FileHandle that was used as a body source. The close

src/quic/http3.cc

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -999,24 +999,24 @@ class Http3ApplicationImpl final : public Session::Application {
999999
stream->ReceiveData(nullptr, 0, flags);
10001000
}
10011001

1002-
void OnStopSending(stream_id id, error_code app_error_code) {
1002+
void OnSendStopSending(stream_id id, error_code app_error_code) {
10031003
auto stream = session().FindStream(id);
10041004
if (!stream) [[unlikely]]
10051005
return;
10061006
Debug(&session(),
1007-
"HTTP/3 application received stop sending for stream %" PRIi64,
1007+
"HTTP/3 application should send stop sending for stream %" PRIi64,
10081008
id);
1009-
stream->ReceiveStopSending(QuicError::ForApplication(app_error_code));
1009+
stream->SendStopSending(app_error_code);
10101010
}
10111011

1012-
void OnResetStream(stream_id id, error_code app_error_code) {
1012+
void OnDoResetStream(stream_id id, error_code app_error_code) {
10131013
auto stream = session().FindStream(id);
10141014
if (!stream) [[unlikely]]
10151015
return;
10161016
Debug(&session(),
1017-
"HTTP/3 application received reset stream for stream %" PRIi64,
1017+
"HTTP/3 application received a request to reset stream for stream %" PRIi64,
10181018
id);
1019-
stream->ReceiveStreamReset(0, QuicError::ForApplication(app_error_code));
1019+
stream->DoStreamReset(app_error_code);
10201020
}
10211021

10221022
void OnShutdown(stream_id id) {
@@ -1447,29 +1447,31 @@ class Http3ApplicationImpl final : public Session::Application {
14471447
return NGTCP2_SUCCESS;
14481448
}
14491449

1450-
static int on_stop_sending(nghttp3_conn* conn,
1450+
static int on_send_stop_sending(nghttp3_conn* conn,
14511451
stream_id id,
14521452
error_code app_error_code,
14531453
void* conn_user_data,
14541454
void* stream_user_data) {
1455+
// this callback asks the app side to send a stop sending
14551456
NGHTTP3_CALLBACK_SCOPE(app);
14561457
if (app.is_control_stream(id)) [[unlikely]] {
14571458
return NGHTTP3_ERR_CALLBACK_FAILURE;
14581459
}
1459-
app.OnStopSending(id, app_error_code);
1460+
app.OnSendStopSending(id, app_error_code);
14601461
return NGTCP2_SUCCESS;
14611462
}
14621463

1463-
static int on_reset_stream(nghttp3_conn* conn,
1464+
static int on_do_reset_stream(nghttp3_conn* conn,
14641465
stream_id id,
14651466
error_code app_error_code,
14661467
void* conn_user_data,
14671468
void* stream_user_data) {
1469+
// this callback ask the app side to do a reset stream
14681470
NGHTTP3_CALLBACK_SCOPE(app);
14691471
if (app.is_control_stream(id)) [[unlikely]] {
14701472
return NGHTTP3_ERR_CALLBACK_FAILURE;
14711473
}
1472-
app.OnResetStream(id, app_error_code);
1474+
app.OnDoResetStream(id, app_error_code);
14731475
return NGTCP2_SUCCESS;
14741476
}
14751477

@@ -1523,9 +1525,9 @@ class Http3ApplicationImpl final : public Session::Application {
15231525
on_begin_trailers,
15241526
on_receive_trailer,
15251527
on_end_trailers,
1526-
on_stop_sending,
1528+
on_send_stop_sending,
15271529
on_end_stream,
1528-
on_reset_stream,
1530+
on_do_reset_stream,
15291531
on_shutdown,
15301532
nullptr, // recv_settings (deprecated)
15311533
on_receive_origin,

src/quic/session.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2797,7 +2797,8 @@ void Session::RemoveStream(stream_id id) {
27972797
// then we can proceed to finishing the close now. Note that the
27982798
// expectation is that the session will be destroyed once FinishClose
27992799
// returns.
2800-
if (impl_->state()->closing && impl_->state()->graceful_close) {
2800+
if (impl_->state()->closing && impl_->state()->graceful_close
2801+
&& impl_->streams_.size() == 0) {
28012802
FinishClose();
28022803
CHECK(is_destroyed());
28032804
}

src/quic/streams.cc

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -548,15 +548,7 @@ struct Stream::Impl {
548548
code = args[0].As<BigInt>()->Uint64Value(&unused);
549549
}
550550

551-
stream->EndReadable();
552-
553-
if (!stream->is_pending()) {
554-
// If the stream is a local unidirectional there's nothing to do here.
555-
if (stream->is_local_unidirectional()) return;
556-
stream->NotifyReadableEnded(code);
557-
} else {
558-
stream->pending_close_read_code_ = code;
559-
}
551+
stream->SendStopSending(code);
560552
}
561553

562554
// Sends a reset stream to the peer to tell it we will not be sending any
@@ -573,21 +565,7 @@ struct Stream::Impl {
573565
code = args[0].As<BigInt>()->Uint64Value(&lossless);
574566
}
575567

576-
if (stream->state()->reset == 1) return;
577-
578-
stream->EndWritable();
579-
// We can release our outbound here now. Since the stream is being reset
580-
// on the ngtcp2 side, we do not need to keep any of the data around
581-
// waiting for acknowledgement that will never come.
582-
stream->outbound_.reset();
583-
stream->state()->reset = 1;
584-
585-
if (!stream->is_pending()) {
586-
if (stream->is_remote_unidirectional()) return;
587-
stream->NotifyWritableEnded(code);
588-
} else {
589-
stream->pending_close_write_code_ = code;
590-
}
568+
stream->DoStreamReset(code);
591569
}
592570

593571
JS_METHOD(SetPriority) {
@@ -1917,6 +1895,36 @@ void Stream::ReceiveStreamReset(uint64_t final_size, QuicError error) {
19171895
EmitReset(error);
19181896
}
19191897

1898+
void Stream::DoStreamReset(error_code code) {
1899+
if (state()->reset == 1) return;
1900+
1901+
EndWritable();
1902+
// We can release our outbound here now. Since the stream is being reset
1903+
// on the ngtcp2 side, we do not need to keep any of the data around
1904+
// waiting for acknowledgement that will never come.
1905+
outbound_.reset();
1906+
state()->reset = 1;
1907+
1908+
if (!is_pending()) {
1909+
if (is_remote_unidirectional()) return;
1910+
NotifyWritableEnded(code);
1911+
} else {
1912+
pending_close_write_code_ = code;
1913+
}
1914+
}
1915+
1916+
void Stream::SendStopSending(error_code code) {
1917+
EndReadable();
1918+
1919+
if (!is_pending()) {
1920+
// If the stream is a local unidirectional there's nothing to do here.
1921+
if (is_local_unidirectional()) return;
1922+
NotifyReadableEnded(code);
1923+
} else {
1924+
pending_close_read_code_ = code;
1925+
}
1926+
}
1927+
19201928
// ============================================================================
19211929

19221930
void Stream::EmitBlocked() {

src/quic/streams.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,18 @@ class Stream final : public AsyncWrap,
346346
void ReceiveStopSending(QuicError error);
347347
void ReceiveStreamReset(uint64_t final_size, QuicError error);
348348

349+
// Sends a reset stream to the peer to tell it we will not be sending any
350+
// more data for this stream. This has the effect of shutting down the
351+
// writable side of the stream for this peer. Any data that is held in the
352+
// outbound queue will be dropped. The stream may still be readable.
353+
void DoStreamReset(error_code code);
354+
355+
// Tells the peer to stop sending data for this stream. This has the effect
356+
// of shutting down the readable side of the stream for this peer. Any data
357+
// that has already been received is still readable.
358+
void SendStopSending(error_code code);
359+
360+
349361
// Currently, only HTTP/3 streams support headers. These methods are here
350362
// to support that. They are not used when using any other QUIC application.
351363

0 commit comments

Comments
 (0)