Allow QuicSimpleServerStream to send CONNECT response

This will allow us to support MASQUE CONNECT-UDP in a subsequent change. This class is not used in production and therefore does not require flag protection.

PiperOrigin-RevId: 344288735
Change-Id: I4e5d75943b36b928e0db55ad5de435b19cb81355
diff --git a/quic/tools/quic_simple_server_stream.cc b/quic/tools/quic_simple_server_stream.cc
index 93b4356..8fee04a 100644
--- a/quic/tools/quic_simple_server_stream.cc
+++ b/quic/tools/quic_simple_server_stream.cc
@@ -7,6 +7,7 @@
 #include <list>
 #include <utility>
 
+#include "absl/strings/match.h"
 #include "absl/strings/numbers.h"
 #include "absl/strings/string_view.h"
 #include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
@@ -63,6 +64,16 @@
     SendErrorResponse();
   }
   ConsumeHeaderList();
+  if (!fin) {
+    // CONNECT and other CONNECT-like methods (such as CONNECT-UDP) require
+    // sending the response right after parsing the headers even though the FIN
+    // bit has not been received on the request stream.
+    auto it = request_headers_.find(":method");
+    if (it != request_headers_.end() &&
+        absl::StartsWith(it->second, "CONNECT")) {
+      SendResponse();
+    }
+  }
 }
 
 void QuicSimpleServerStream::OnTrailingHeadersComplete(
diff --git a/quic/tools/quic_simple_server_stream_test.cc b/quic/tools/quic_simple_server_stream_test.cc
index 4b21cca..c21088a 100644
--- a/quic/tools/quic_simple_server_stream_test.cc
+++ b/quic/tools/quic_simple_server_stream_test.cc
@@ -76,12 +76,22 @@
   void set_body(std::string body) { body_ = std::move(body); }
   const std::string& body() const { return body_; }
   int content_length() const { return content_length_; }
+  bool send_response_was_called() const { return send_response_was_called_; }
 
   absl::string_view GetHeader(absl::string_view key) const {
     auto it = request_headers_.find(key);
     DCHECK(it != request_headers_.end());
     return it->second;
   }
+
+ protected:
+  void SendResponse() override {
+    send_response_was_called_ = true;
+    QuicSimpleServerStream::SendResponse();
+  }
+
+ private:
+  bool send_response_was_called_ = false;
 };
 
 namespace {
@@ -782,6 +792,29 @@
   stream_->OnStreamFrame(frame);
 }
 
+TEST_P(QuicSimpleServerStreamTest, ConnectSendsResponseBeforeFinReceived) {
+  EXPECT_CALL(session_, WritevData(_, _, _, _, _, _))
+      .WillRepeatedly(
+          Invoke(&session_, &MockQuicSimpleServerSession::ConsumeData));
+  QuicHeaderList header_list;
+  header_list.OnHeaderBlockStart();
+  header_list.OnHeader(":authority", "www.google.com:4433");
+  header_list.OnHeader(":method", "CONNECT-SILLY");
+  header_list.OnHeaderBlockEnd(128, 128);
+  EXPECT_CALL(*stream_, WriteHeadersMock(/*fin=*/false));
+  stream_->OnStreamHeaderList(/*fin=*/false, kFakeFrameLen, header_list);
+  std::unique_ptr<char[]> buffer;
+  QuicByteCount header_length =
+      HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer);
+  std::string header = std::string(buffer.get(), header_length);
+  std::string data = UsesHttp3() ? header + body_ : body_;
+  stream_->OnStreamFrame(
+      QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
+  EXPECT_EQ("CONNECT-SILLY", StreamHeadersValue(":method"));
+  EXPECT_EQ(body_, StreamBody());
+  EXPECT_TRUE(stream_->send_response_was_called());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic