No public description

PiperOrigin-RevId: 661382575
diff --git a/quiche/quic/core/http/http_frames.h b/quiche/quic/core/http/http_frames.h
index 645f5c0..3aac2ef 100644
--- a/quiche/quic/core/http/http_frames.h
+++ b/quiche/quic/core/http/http_frames.h
@@ -112,6 +112,21 @@
   bool operator==(const OriginFrame& rhs) const {
     return origins == rhs.origins;
   }
+
+  std::string ToString() const {
+    std::string result = "Origin Frame: {origins: ";
+    for (const std::string& origin : origins) {
+      absl::StrAppend(&result, "\n", origin);
+    }
+    result += "}";
+    return result;
+  }
+
+  friend QUICHE_EXPORT std::ostream& operator<<(std::ostream& os,
+                                                const OriginFrame& s) {
+    os << s.ToString();
+    return os;
+  }
 };
 
 // https://httpwg.org/http-extensions/draft-ietf-httpbis-priority.html
diff --git a/quiche/quic/core/http/quic_send_control_stream.cc b/quiche/quic/core/http/quic_send_control_stream.cc
index 9c328aa..76c4442 100644
--- a/quiche/quic/core/http/quic_send_control_stream.cc
+++ b/quiche/quic/core/http/quic_send_control_stream.cc
@@ -28,6 +28,7 @@
                                              const SettingsFrame& settings)
     : QuicStream(id, spdy_session, /*is_static = */ true, WRITE_UNIDIRECTIONAL),
       settings_sent_(false),
+      origin_frame_sent_(false),
       settings_(settings),
       spdy_session_(spdy_session) {}
 
@@ -86,6 +87,20 @@
                     nullptr);
 }
 
+void QuicSendControlStream::MaybeSendOriginFrame(
+    std::vector<std::string> origins) {
+  if (origins.empty() || origin_frame_sent_) {
+    return;
+  }
+  OriginFrame frame;
+  frame.origins = std::move(origins);
+  QUIC_DVLOG(1) << "Control stream " << id() << " is writing origin frame "
+                << frame;
+  WriteOrBufferData(HttpEncoder::SerializeOriginFrame(frame), /*fin =*/false,
+                    nullptr);
+  origin_frame_sent_ = true;
+}
+
 void QuicSendControlStream::WritePriorityUpdate(QuicStreamId stream_id,
                                                 HttpStreamPriority priority) {
   QuicConnection::ScopedPacketFlusher flusher(session()->connection());
diff --git a/quiche/quic/core/http/quic_send_control_stream.h b/quiche/quic/core/http/quic_send_control_stream.h
index ea6b0f8..7c2f45e 100644
--- a/quiche/quic/core/http/quic_send_control_stream.h
+++ b/quiche/quic/core/http/quic_send_control_stream.h
@@ -38,6 +38,9 @@
   // first frame sent on this stream.
   void MaybeSendSettingsFrame();
 
+  // Send ORIGIN frame if |origins| is not empty.
+  void MaybeSendOriginFrame(std::vector<std::string> origins);
+
   // Send a PRIORITY_UPDATE frame on this stream, and a SETTINGS frame
   // beforehand if one has not been already sent.
   void WritePriorityUpdate(QuicStreamId stream_id, HttpStreamPriority priority);
@@ -54,6 +57,9 @@
   // Track if a settings frame is already sent.
   bool settings_sent_;
 
+  // Track if an origin frame is already sent.
+  bool origin_frame_sent_;
+
   // SETTINGS values to send.
   const SettingsFrame settings_;
 
diff --git a/quiche/quic/core/http/quic_send_control_stream_test.cc b/quiche/quic/core/http/quic_send_control_stream_test.cc
index 02b7cc2..fabeb1b 100644
--- a/quiche/quic/core/http/quic_send_control_stream_test.cc
+++ b/quiche/quic/core/http/quic_send_control_stream_test.cc
@@ -245,6 +245,16 @@
   send_control_stream_->MaybeSendSettingsFrame();
 }
 
+TEST_P(QuicSendControlStreamTest, SendOriginFrameOnce) {
+  Initialize();
+  std::vector<std::string> origins = {"a", "b", "c"};
+
+  EXPECT_CALL(session_, WritevData(send_control_stream_->id(), _, _, _, _, _))
+      .Times(1);
+  send_control_stream_->MaybeSendOriginFrame(origins);
+  send_control_stream_->MaybeSendOriginFrame(origins);
+}
+
 // Send stream type and SETTINGS frame if WritePriorityUpdate() is called first.
 TEST_P(QuicSendControlStreamTest, WritePriorityBeforeSettings) {
   Initialize();
diff --git a/quiche/quic/core/http/quic_spdy_session.cc b/quiche/quic/core/http/quic_spdy_session.cc
index f84b30b..8c81b3c 100644
--- a/quiche/quic/core/http/quic_spdy_session.cc
+++ b/quiche/quic/core/http/quic_spdy_session.cc
@@ -869,6 +869,7 @@
   }
   QuicConnection::ScopedPacketFlusher flusher(connection());
   send_control_stream_->MaybeSendSettingsFrame();
+  SendInitialDataAfterSettings();
 }
 
 bool QuicSpdySession::CheckStreamWriteBlocked(QuicStream* stream) const {
diff --git a/quiche/quic/core/http/quic_spdy_session.h b/quiche/quic/core/http/quic_spdy_session.h
index a6e4e1a..d56c99d 100644
--- a/quiche/quic/core/http/quic_spdy_session.h
+++ b/quiche/quic/core/http/quic_spdy_session.h
@@ -561,6 +561,9 @@
   // available.
   void SendInitialData();
 
+  // Sends any data which should be sent after the initial SETTINGS frame.
+  virtual void SendInitialDataAfterSettings() {}
+
   // Override to skip checking for qpack_decoder_send_stream_ given decoder data
   // is always bundled opportunistically.
   bool CheckStreamWriteBlocked(QuicStream* stream) const override;
@@ -575,6 +578,8 @@
     cookie_crumbling_ = CookieCrumbling::kDisabled;
   }
 
+  QuicSendControlStream* send_control_stream() { return send_control_stream_; }
+
  private:
   friend class test::QuicSpdySessionPeer;