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));