Send Stream Cancellation QPACK instruction.
According to
https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#name-abandonment-of-a-stream,
this should be done when a stream reset is received before the end of a stream
or before all header blocks are processed on that stream, or when reading of a
stream is abandoned.
gfe-relnote: n/a, change to QUIC v99-only code. Protected by existing disabled gfe2_reloadable_flag_quic_enable_version_99.
PiperOrigin-RevId: 282861038
Change-Id: Ibf6f69e29f98022e0eebf89676783fa3ce26541a
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 27717ab..b37246a 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -32,6 +32,7 @@
#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_encoder_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_header_table_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_test_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
@@ -2025,6 +2026,14 @@
}
TEST_P(QuicSpdySessionTestServer, DonotRetransmitDataOfClosedStreams) {
+ // Resetting a stream will send a QPACK Stream Cancellation instruction on the
+ // decoder stream. For simplicity, ignore writes on this stream.
+ NoopQpackStreamSenderDelegate qpack_stream_sender_delegate;
+ if (VersionUsesHttp3(transport_version())) {
+ session_.qpack_decoder()->set_qpack_stream_sender_delegate(
+ &qpack_stream_sender_delegate);
+ }
+
InSequence s;
TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index 4c5e642..d25f99d 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -680,9 +680,15 @@
void QuicSpdyStream::OnStreamReset(const QuicRstStreamFrame& frame) {
if (frame.error_code != QUIC_STREAM_NO_ERROR) {
+ if (VersionUsesHttp3(transport_version()) && !fin_received() &&
+ spdy_session_->qpack_decoder()) {
+ spdy_session_->qpack_decoder()->OnStreamReset(id());
+ }
+
QuicStream::OnStreamReset(frame);
return;
}
+
QUIC_DVLOG(1) << ENDPOINT
<< "Received QUIC_STREAM_NO_ERROR, not discarding response";
set_rst_received(true);
@@ -691,6 +697,15 @@
CloseWriteSide();
}
+void QuicSpdyStream::Reset(QuicRstStreamErrorCode error) {
+ if (VersionUsesHttp3(transport_version()) && !fin_received() &&
+ spdy_session_->qpack_decoder()) {
+ spdy_session_->qpack_decoder()->OnStreamReset(id());
+ }
+
+ QuicStream::Reset(error);
+}
+
void QuicSpdyStream::OnDataAvailable() {
if (!VersionUsesHttp3(transport_version())) {
// Sequencer must be blocked until headers are consumed.
diff --git a/quic/core/http/quic_spdy_stream.h b/quic/core/http/quic_spdy_stream.h
index e315dcc..8610dbb 100644
--- a/quic/core/http/quic_spdy_stream.h
+++ b/quic/core/http/quic_spdy_stream.h
@@ -102,6 +102,8 @@
// QUIC_STREAM_NO_ERROR.
void OnStreamReset(const QuicRstStreamFrame& frame) override;
+ void Reset(QuicRstStreamErrorCode error) override;
+
// Called by the sequencer when new data is available. Decodes the data and
// calls OnBodyAvailable() to pass to the upper layer.
void OnDataAvailable() override;
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index 73177dc..8def703 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -2522,6 +2522,43 @@
EXPECT_EQ(0u, stream_->sequencer()->NumBytesConsumed());
}
+// Stream Cancellation instruction is sent on QPACK decoder stream
+// when stream is reset.
+TEST_P(QuicSpdyStreamTest, StreamCancellationWhenStreamReset) {
+ if (!UsesHttp3()) {
+ return;
+ }
+
+ Initialize(kShouldProcessData);
+
+ auto qpack_decoder_stream =
+ QuicSpdySessionPeer::GetQpackDecoderSendStream(session_.get());
+ EXPECT_CALL(*session_, WritevData(qpack_decoder_stream,
+ qpack_decoder_stream->id(), 1, 1, _));
+ EXPECT_CALL(*session_,
+ SendRstStream(stream_->id(), QUIC_STREAM_CANCELLED, 0));
+
+ stream_->Reset(QUIC_STREAM_CANCELLED);
+}
+
+// Stream Cancellation instruction is sent on QPACK decoder stream
+// when RESET_STREAM frame is received.
+TEST_P(QuicSpdyStreamTest, StreamCancellationOnResetReceived) {
+ if (!UsesHttp3()) {
+ return;
+ }
+
+ Initialize(kShouldProcessData);
+
+ auto qpack_decoder_stream =
+ QuicSpdySessionPeer::GetQpackDecoderSendStream(session_.get());
+ EXPECT_CALL(*session_, WritevData(qpack_decoder_stream,
+ qpack_decoder_stream->id(), 1, 1, _));
+
+ stream_->OnStreamReset(QuicRstStreamFrame(
+ kInvalidControlFrameId, stream_->id(), QUIC_STREAM_CANCELLED, 0));
+}
+
} // namespace
} // namespace test
} // namespace quic