Add a handler for Capsules with an unknown type to Http3DatagramVisitor. According to RFC 9297, Capsules with an unknown type can be either forwarded (by intermediaries) or dropped (by endpoints). Existing QuicSpdyStream, however, always drops such Capsules. I added OnUnknownCapsule method to QuicSpdyStream::Http3DatagramVisitor so that a user of the visitor can implement appropriate behavior. QuicSpdyStream calls the method when it receives unknown Capsules. This CL does not change the behavior of existing code. I added an empty OnUnknownCapsule method to each existing class that extends Http3DatagramVisitor, so they discard parsed unknown Capsules and eventually drop them. This CL doesn't require flag protection because datagram support is never enabled on production servers today. PiperOrigin-RevId: 515751073
diff --git a/quiche/quic/core/http/quic_spdy_stream.cc b/quiche/quic/core/http/quic_spdy_stream.cc index 8cf5b99..93546ec 100644 --- a/quiche/quic/core/http/quic_spdy_stream.cc +++ b/quiche/quic/core/http/quic_spdy_stream.cc
@@ -1350,18 +1350,18 @@ return false; } switch (capsule.capsule_type()) { - case CapsuleType::DATAGRAM: { + case CapsuleType::DATAGRAM: HandleReceivedDatagram(capsule.datagram_capsule().http_datagram_payload); - } break; - case CapsuleType::LEGACY_DATAGRAM: { + return true; + case CapsuleType::LEGACY_DATAGRAM: HandleReceivedDatagram( capsule.legacy_datagram_capsule().http_datagram_payload); - } break; - case CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT: { + return true; + case CapsuleType::LEGACY_DATAGRAM_WITHOUT_CONTEXT: HandleReceivedDatagram(capsule.legacy_datagram_without_context_capsule() .http_datagram_payload); - } break; - case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: { + return true; + case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: if (web_transport_ == nullptr) { QUIC_DLOG(ERROR) << ENDPOINT << "Received capsule " << capsule << " for a non-WebTransport stream."; @@ -1370,7 +1370,7 @@ web_transport_->OnCloseReceived( capsule.close_web_transport_session_capsule().error_code, capsule.close_web_transport_session_capsule().error_message); - } break; + return true; case CapsuleType::ADDRESS_ASSIGN: if (connect_ip_visitor_ == nullptr) { return true; @@ -1400,6 +1400,9 @@ case CapsuleType::WT_MAX_STREAMS_UNIDI: return true; } + if (datagram_visitor_) { + datagram_visitor_->OnUnknownCapsule(id(), capsule.unknown_capsule()); + } return true; }
diff --git a/quiche/quic/core/http/quic_spdy_stream.h b/quiche/quic/core/http/quic_spdy_stream.h index 1267f4c..301df24 100644 --- a/quiche/quic/core/http/quic_spdy_stream.h +++ b/quiche/quic/core/http/quic_spdy_stream.h
@@ -272,10 +272,15 @@ // the stream ID. virtual void OnHttp3Datagram(QuicStreamId stream_id, absl::string_view payload) = 0; + + // Called when a Capsule with an unknown type is received. + virtual void OnUnknownCapsule(QuicStreamId stream_id, + const quiche::UnknownCapsule& capsule) = 0; }; - // Registers |visitor| to receive HTTP/3 datagrams. |visitor| must be - // valid until a corresponding call to UnregisterHttp3DatagramVisitor. + // Registers |visitor| to receive HTTP/3 datagrams and enables Capsule + // Protocol by registering a CapsuleParser. |visitor| must be valid until a + // corresponding call to UnregisterHttp3DatagramVisitor. void RegisterHttp3DatagramVisitor(Http3DatagramVisitor* visitor); // Unregisters an HTTP/3 datagram visitor. Must only be called after a call to
diff --git a/quiche/quic/core/http/quic_spdy_stream_test.cc b/quiche/quic/core/http/quic_spdy_stream_test.cc index dc15654..90b58df 100644 --- a/quiche/quic/core/http/quic_spdy_stream_test.cc +++ b/quiche/quic/core/http/quic_spdy_stream_test.cc
@@ -3228,6 +3228,14 @@ EXPECT_THAT( connect_ip_visitor.received_route_advertisement_capsules(), ElementsAre(route_advertisement_capsule.route_advertisement_capsule())); + // Unknown capsule. + uint64_t capsule_type = 0x17u; + std::string capsule_payload = {1, 2, 3, 4}; + Capsule unknown_capsule = Capsule::Unknown(capsule_type, capsule_payload); + stream_->OnCapsule(unknown_capsule); + EXPECT_THAT(h3_datagram_visitor.received_unknown_capsules(), + ElementsAre(SavingHttp3DatagramVisitor::SavedUnknownCapsule{ + stream_->id(), capsule_type, capsule_payload})); // Cleanup. stream_->UnregisterHttp3DatagramVisitor(); stream_->UnregisterConnectIpVisitor();
diff --git a/quiche/quic/core/http/web_transport_http3.h b/quiche/quic/core/http/web_transport_http3.h index 6eb7239..8a6f35a 100644 --- a/quiche/quic/core/http/web_transport_http3.h +++ b/quiche/quic/core/http/web_transport_http3.h
@@ -92,6 +92,8 @@ // From QuicSpdyStream::Http3DatagramVisitor. void OnHttp3Datagram(QuicStreamId stream_id, absl::string_view payload) override; + void OnUnknownCapsule(QuicStreamId /*stream_id*/, + const quiche::UnknownCapsule& /*capsule*/) override {} bool close_received() const { return close_received_; } WebTransportHttp3RejectionReason rejection_reason() const {
diff --git a/quiche/quic/masque/masque_client_session.h b/quiche/quic/masque/masque_client_session.h index 43703b4..ce79c22 100644 --- a/quiche/quic/masque/masque_client_session.h +++ b/quiche/quic/masque/masque_client_session.h
@@ -148,6 +148,8 @@ // From QuicSpdyStream::Http3DatagramVisitor. void OnHttp3Datagram(QuicStreamId stream_id, absl::string_view payload) override; + void OnUnknownCapsule(QuicStreamId /*stream_id*/, + const quiche::UnknownCapsule& /*capsule*/) override {} private: QuicSpdyClientStream* stream_; // Unowned. @@ -184,6 +186,8 @@ // From QuicSpdyStream::Http3DatagramVisitor. void OnHttp3Datagram(QuicStreamId stream_id, absl::string_view payload) override; + void OnUnknownCapsule(QuicStreamId /*stream_id*/, + const quiche::UnknownCapsule& /*capsule*/) override {} // From QuicSpdyStream::ConnectIpVisitor. bool OnAddressAssignCapsule(
diff --git a/quiche/quic/masque/masque_server_session.h b/quiche/quic/masque/masque_server_session.h index 082616d..2dbed18 100644 --- a/quiche/quic/masque/masque_server_session.h +++ b/quiche/quic/masque/masque_server_session.h
@@ -82,6 +82,8 @@ // From QuicSpdyStream::Http3DatagramVisitor. void OnHttp3Datagram(QuicStreamId stream_id, absl::string_view payload) override; + void OnUnknownCapsule(QuicStreamId /*stream_id*/, + const quiche::UnknownCapsule& /*capsule*/) override {} private: QuicSpdyStream* stream_; @@ -115,6 +117,8 @@ // From QuicSpdyStream::Http3DatagramVisitor. void OnHttp3Datagram(QuicStreamId stream_id, absl::string_view payload) override; + void OnUnknownCapsule(QuicStreamId /*stream_id*/, + const quiche::UnknownCapsule& /*capsule*/) override {} // From QuicSpdyStream::ConnectIpVisitor. bool OnAddressAssignCapsule(
diff --git a/quiche/quic/test_tools/quic_test_utils.h b/quiche/quic/test_tools/quic_test_utils.h index 9d717df..2eac913 100644 --- a/quiche/quic/test_tools/quic_test_utils.h +++ b/quiche/quic/test_tools/quic_test_utils.h
@@ -41,6 +41,7 @@ #include "quiche/quic/test_tools/mock_random.h" #include "quiche/quic/test_tools/quic_framer_peer.h" #include "quiche/quic/test_tools/simple_quic_framer.h" +#include "quiche/common/capsule.h" #include "quiche/common/simple_buffer_allocator.h" #include "quiche/spdy/core/http2_header_block.h" @@ -2087,9 +2088,20 @@ return stream_id == o.stream_id && payload == o.payload; } }; + struct SavedUnknownCapsule { + QuicStreamId stream_id; + uint64_t type; + std::string payload; + bool operator==(const SavedUnknownCapsule& o) const { + return stream_id == o.stream_id && type == o.type && payload == o.payload; + } + }; const std::vector<SavedHttp3Datagram>& received_h3_datagrams() const { return received_h3_datagrams_; } + const std::vector<SavedUnknownCapsule>& received_unknown_capsules() const { + return received_unknown_capsules_; + } // Override from QuicSpdyStream::Http3DatagramVisitor. void OnHttp3Datagram(QuicStreamId stream_id, @@ -2097,9 +2109,15 @@ received_h3_datagrams_.push_back( SavedHttp3Datagram{stream_id, std::string(payload)}); } + void OnUnknownCapsule(QuicStreamId stream_id, + const quiche::UnknownCapsule& capsule) override { + received_unknown_capsules_.push_back(SavedUnknownCapsule{ + stream_id, capsule.type, std::string(capsule.payload)}); + } private: std::vector<SavedHttp3Datagram> received_h3_datagrams_; + std::vector<SavedUnknownCapsule> received_unknown_capsules_; }; // Implementation of ConnectIpVisitor which saves all received capsules.
diff --git a/quiche/quic/tools/connect_udp_tunnel.h b/quiche/quic/tools/connect_udp_tunnel.h index 71819df..1e800a7 100644 --- a/quiche/quic/tools/connect_udp_tunnel.h +++ b/quiche/quic/tools/connect_udp_tunnel.h
@@ -64,6 +64,8 @@ // QuicSpdyStream::Http3DatagramVisitor: void OnHttp3Datagram(QuicStreamId stream_id, absl::string_view payload) override; + void OnUnknownCapsule(QuicStreamId /*stream_id*/, + const quiche::UnknownCapsule& /*capsule*/) override {} private: void BeginAsyncReadFromTarget();