Disable capsule parsing for failed requests Otherwise we were incorrectly parsing the body as capsules for rejected MASQUE requests. PiperOrigin-RevId: 640970762
diff --git a/quiche/quic/core/http/quic_spdy_client_stream.cc b/quiche/quic/core/http/quic_spdy_client_stream.cc index d173277..323fa9b 100644 --- a/quiche/quic/core/http/quic_spdy_client_stream.cc +++ b/quiche/quic/core/http/quic_spdy_client_stream.cc
@@ -107,10 +107,7 @@ if (!web_transport()->ready()) { // The request was rejected by WebTransport, typically due to not having a // 2xx status. The reason we're using Reset() here rather than closing - // cleanly is that even if the server attempts to send us any form of body - // with a 4xx request, we've already set up the capsule parser, and we - // don't have any way to process anything from the response body in - // question. + // cleanly is to avoid having to process the response body. Reset(QUIC_STREAM_CANCELLED); return; } @@ -120,6 +117,10 @@ return; } + if (uses_capsules() && (response_code_ < 200 || response_code_ >= 300)) { + capsules_failed_ = true; + } + ConsumeHeaderList(); QUIC_DVLOG(1) << "headers complete for stream " << id(); }
diff --git a/quiche/quic/core/http/quic_spdy_client_stream.h b/quiche/quic/core/http/quic_spdy_client_stream.h index 001f312..bbe922a 100644 --- a/quiche/quic/core/http/quic_spdy_client_stream.h +++ b/quiche/quic/core/http/quic_spdy_client_stream.h
@@ -90,6 +90,10 @@ // response_header_. Returns false on error. virtual bool ParseAndValidateStatusCode(); + bool uses_capsules() const override { + return QuicSpdyStream::uses_capsules() && !capsules_failed_; + } + private: // The parsed headers received from the server. spdy::Http2HeaderBlock response_headers_; @@ -97,6 +101,7 @@ // The parsed content-length, or -1 if none is specified. int64_t content_length_; int response_code_; + bool capsules_failed_ = false; std::string data_; size_t header_bytes_read_; size_t header_bytes_written_;
diff --git a/quiche/quic/core/http/quic_spdy_client_stream_test.cc b/quiche/quic/core/http/quic_spdy_client_stream_test.cc index ed03be2..ce39e5b 100644 --- a/quiche/quic/core/http/quic_spdy_client_stream_test.cc +++ b/quiche/quic/core/http/quic_spdy_client_stream_test.cc
@@ -24,6 +24,7 @@ using spdy::Http2HeaderBlock; using testing::_; +using testing::ElementsAre; using testing::StrictMock; namespace quic { @@ -390,6 +391,65 @@ EXPECT_TRUE(stream_->reading_stopped()); } +TEST_P(QuicSpdyClientStreamTest, Capsules) { + if (!VersionUsesHttp3(connection_->transport_version())) { + return; + } + SavingHttp3DatagramVisitor h3_datagram_visitor; + stream_->RegisterHttp3DatagramVisitor(&h3_datagram_visitor); + headers_.erase("content-length"); + auto headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + std::string capsule_data = {0, 6, 1, 2, 3, 4, 5, 6, 0x17, 4, 1, 2, 3, 4}; + quiche::QuicheBuffer data_frame_header = + HttpEncoder::SerializeDataFrameHeader( + capsule_data.length(), quiche::SimpleBufferAllocator::Get()); + std::string stream_data = + absl::StrCat(data_frame_header.AsStringView(), capsule_data); + stream_->OnStreamFrame( + QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, stream_data)); + // Datagram capsule. + std::string http_datagram_payload = {1, 2, 3, 4, 5, 6}; + EXPECT_THAT(h3_datagram_visitor.received_h3_datagrams(), + ElementsAre(SavingHttp3DatagramVisitor::SavedHttp3Datagram{ + stream_->id(), http_datagram_payload})); + // Unknown capsule. + uint64_t capsule_type = 0x17u; + std::string unknown_capsule_payload = {1, 2, 3, 4}; + EXPECT_THAT(h3_datagram_visitor.received_unknown_capsules(), + ElementsAre(SavingHttp3DatagramVisitor::SavedUnknownCapsule{ + stream_->id(), capsule_type, unknown_capsule_payload})); + // Cleanup. + stream_->UnregisterHttp3DatagramVisitor(); +} + +TEST_P(QuicSpdyClientStreamTest, CapsulesOnUnsuccessfulResponse) { + if (!VersionUsesHttp3(connection_->transport_version())) { + return; + } + SavingHttp3DatagramVisitor h3_datagram_visitor; + stream_->RegisterHttp3DatagramVisitor(&h3_datagram_visitor); + headers_[":status"] = "401"; + headers_.erase("content-length"); + auto headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + std::string capsule_data = {0, 6, 1, 2, 3, 4, 5, 6, 0x17, 4, 1, 2, 3, 4}; + quiche::QuicheBuffer data_frame_header = + HttpEncoder::SerializeDataFrameHeader( + capsule_data.length(), quiche::SimpleBufferAllocator::Get()); + std::string stream_data = + absl::StrCat(data_frame_header.AsStringView(), capsule_data); + stream_->OnStreamFrame( + QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, stream_data)); + // Ensure received capsules were ignored. + EXPECT_TRUE(h3_datagram_visitor.received_h3_datagrams().empty()); + EXPECT_TRUE(h3_datagram_visitor.received_unknown_capsules().empty()); + // Cleanup. + stream_->UnregisterHttp3DatagramVisitor(); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quiche/quic/core/http/quic_spdy_stream.cc b/quiche/quic/core/http/quic_spdy_stream.cc index 59405a6..1634202 100644 --- a/quiche/quic/core/http/quic_spdy_stream.cc +++ b/quiche/quic/core/http/quic_spdy_stream.cc
@@ -1708,7 +1708,7 @@ } void QuicSpdyStream::HandleBodyAvailable() { - if (!capsule_parser_) { + if (!capsule_parser_ || !uses_capsules()) { OnBodyAvailable(); return; }
diff --git a/quiche/quic/core/http/quic_spdy_stream.h b/quiche/quic/core/http/quic_spdy_stream.h index c0b7588..4de6fe2 100644 --- a/quiche/quic/core/http/quic_spdy_stream.h +++ b/quiche/quic/core/http/quic_spdy_stream.h
@@ -373,6 +373,8 @@ void set_headers_decompressed(bool val) { headers_decompressed_ = val; } + virtual bool uses_capsules() const { return capsule_parser_ != nullptr; } + void set_ack_listener( quiche::QuicheReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {