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