Parse received ALPS data in QuicSpdySession. PiperOrigin-RevId: 352994081 Change-Id: I5f4e69cee1b1784103c0f0629e74f151b921c5a6
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc index 3596a90..e35d622 100644 --- a/quic/core/http/quic_spdy_session.cc +++ b/quic/core/http/quic_spdy_session.cc
@@ -15,6 +15,8 @@ #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "quic/core/http/http_constants.h" +#include "quic/core/http/http_decoder.h" +#include "quic/core/http/http_frames.h" #include "quic/core/http/quic_headers_stream.h" #include "quic/core/quic_error_codes.h" #include "quic/core/quic_types.h" @@ -86,6 +88,84 @@ std::unique_ptr<QuicHpackDebugVisitor> headers_stream_hpack_visitor_; }; +// Class to forward ACCEPT_CH frame to QuicSpdySession, +// and ignore every other frame. +class AlpsFrameDecoder : public HttpDecoder::Visitor { + public: + explicit AlpsFrameDecoder(QuicSpdySession* session) : session_(session) {} + ~AlpsFrameDecoder() override = default; + + // HttpDecoder::Visitor implementation. + void OnError(HttpDecoder* /*decoder*/) override {} + bool OnCancelPushFrame(const CancelPushFrame& /*frame*/) override { + return true; + } + bool OnMaxPushIdFrame(const MaxPushIdFrame& /*frame*/) override { + return true; + } + bool OnGoAwayFrame(const GoAwayFrame& /*frame*/) override { return true; } + bool OnSettingsFrameStart(QuicByteCount /*header_length*/) override { + return true; + } + bool OnSettingsFrame(const SettingsFrame& /*frame*/) override { return true; } + bool OnDataFrameStart(QuicByteCount /*header_length*/, QuicByteCount + /*payload_length*/) override { + return true; + } + bool OnDataFramePayload(absl::string_view /*payload*/) override { + return true; + } + bool OnDataFrameEnd() override { return true; } + bool OnHeadersFrameStart(QuicByteCount /*header_length*/, + QuicByteCount /*payload_length*/) override { + return true; + } + bool OnHeadersFramePayload(absl::string_view /*payload*/) override { + return true; + } + bool OnHeadersFrameEnd() override { return true; } + bool OnPushPromiseFrameStart(QuicByteCount /*header_length*/) override { + return true; + } + bool OnPushPromiseFramePushId( + PushId /*push_id*/, + QuicByteCount + /*push_id_length*/, + QuicByteCount /*header_block_length*/) override { + return true; + } + bool OnPushPromiseFramePayload(absl::string_view /*payload*/) override { + return true; + } + bool OnPushPromiseFrameEnd() override { return true; } + bool OnPriorityUpdateFrameStart(QuicByteCount /*header_length*/) override { + return true; + } + bool OnPriorityUpdateFrame(const PriorityUpdateFrame& /*frame*/) override { + return true; + } + bool OnAcceptChFrameStart(QuicByteCount /*header_length*/) override { + return true; + } + bool OnAcceptChFrame(const AcceptChFrame& frame) override { + session_->OnAcceptChFrameReceivedViaAlps(frame); + return true; + } + bool OnUnknownFrameStart(uint64_t /*frame_type*/, + QuicByteCount + /*header_length*/, + QuicByteCount /*payload_length*/) override { + return true; + } + bool OnUnknownFramePayload(absl::string_view /*payload*/) override { + return true; + } + bool OnUnknownFrameEnd() override { return true; } + + private: + QuicSpdySession* const session_; +}; + } // namespace // A SpdyFramerVisitor that passes HEADERS frames to the QuicSpdyStream, and @@ -978,6 +1058,26 @@ return true; } +absl::optional<std::string> QuicSpdySession::OnAlpsData( + const uint8_t* alps_data, + size_t alps_length) { + AlpsFrameDecoder alps_frame_decoder(this); + HttpDecoder decoder(&alps_frame_decoder); + decoder.ProcessInput(reinterpret_cast<const char*>(alps_data), alps_length); + if (decoder.error() != QUIC_NO_ERROR) { + return decoder.error_detail(); + } + + return absl::nullopt; +} + +void QuicSpdySession::OnAcceptChFrameReceivedViaAlps( + const AcceptChFrame& frame) { + if (debug_visitor_) { + debug_visitor_->OnAcceptChFrameReceivedViaAlps(frame); + } +} + bool QuicSpdySession::OnSettingsFrame(const SettingsFrame& frame) { DCHECK(VersionUsesHttp3(transport_version())); if (debug_visitor_ != nullptr) {
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h index 9b8d398..cda619b 100644 --- a/quic/core/http/quic_spdy_session.h +++ b/quic/core/http/quic_spdy_session.h
@@ -83,6 +83,9 @@ // Called when peer's QPACK decoder stream type is received. virtual void OnPeerQpackDecoderStreamCreated(QuicStreamId /*stream_id*/) = 0; + // Incoming HTTP/3 frames in ALPS TLS extension. + virtual void OnAcceptChFrameReceivedViaAlps(const AcceptChFrame& /*frame*/) {} + // Incoming HTTP/3 frames on the control stream. virtual void OnCancelPushFrameReceived(const CancelPushFrame& /*frame*/) {} virtual void OnSettingsFrameReceived(const SettingsFrame& /*frame*/) = 0; @@ -406,6 +409,13 @@ // Decode SETTINGS from |cached_state| and apply it to the session. bool ResumeApplicationState(ApplicationState* cached_state) override; + absl::optional<std::string> OnAlpsData(const uint8_t* alps_data, + size_t alps_length) override; + + // Called when ACCEPT_CH frame is parsed out of data received in TLS ALPS + // extension. + virtual void OnAcceptChFrameReceivedViaAlps(const AcceptChFrame& /*frame*/); + protected: // Override CreateIncomingStream(), CreateOutgoingBidirectionalStream() and // CreateOutgoingUnidirectionalStream() with QuicSpdyStream return type to
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc index 767a881..2e8b80f 100644 --- a/quic/core/http/quic_spdy_session_test.cc +++ b/quic/core/http/quic_spdy_session_test.cc
@@ -3319,6 +3319,28 @@ session_.OnStreamFrame(data3); } +TEST_P(QuicSpdySessionTestClient, OnAlpsData) { + StrictMock<MockHttp3DebugVisitor> debug_visitor; + session_.set_debug_visitor(&debug_visitor); + + AcceptChFrame accept_ch_frame{{{"foo", "bar"}}}; + std::string serialized_accept_ch_frame = absl::HexStringToBytes( + "4089" // type (ACCEPT_CH) + "08" // length + "03" // length of origin + "666f6f" // origin "foo" + "03" // length of value + "626172"); // value "bar" + + if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { + EXPECT_CALL(debug_visitor, OnAcceptChFrameReceivedViaAlps(accept_ch_frame)); + } + + session_.OnAlpsData( + reinterpret_cast<const uint8_t*>(serialized_accept_ch_frame.data()), + serialized_accept_ch_frame.size()); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc index 26c6ea7..a50d7a6 100644 --- a/quic/core/quic_session.cc +++ b/quic/core/quic_session.cc
@@ -1364,6 +1364,12 @@ } } +absl::optional<std::string> QuicSession::OnAlpsData( + const uint8_t* /*alps_data*/, + size_t /*alps_length*/) { + return absl::nullopt; +} + void QuicSession::AdjustInitialFlowControlWindows(size_t stream_window) { const float session_window_multiplier = config_.GetInitialStreamFlowControlWindowToSend()
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h index f3662f0..2101c28 100644 --- a/quic/core/quic_session.h +++ b/quic/core/quic_session.h
@@ -279,6 +279,11 @@ // Called by the QuicCryptoStream when a new QuicConfig has been negotiated. virtual void OnConfigNegotiated(); + // Called by the TLS handshaker when ALPS data is received. + // Returns an error message if an error has occurred, or nullopt otherwise. + virtual absl::optional<std::string> OnAlpsData(const uint8_t* alps_data, + size_t alps_length); + // From HandshakerDelegateInterface bool OnNewDecryptionKeyAvailable(EncryptionLevel level, std::unique_ptr<QuicDecrypter> decrypter,
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h index 8231c62..893b18f 100644 --- a/quic/test_tools/quic_test_utils.h +++ b/quic/test_tools/quic_test_utils.h
@@ -1110,6 +1110,11 @@ (override)); MOCK_METHOD(void, + OnAcceptChFrameReceivedViaAlps, + (const AcceptChFrame&), + (override)); + + MOCK_METHOD(void, OnCancelPushFrameReceived, (const CancelPushFrame&), (override));