Add APIs to allow sessions to read stream types from pending stream.

Pending stream will now allow receiving stream frame with zero offset.

gfe-relnote: version 99 only. Only used in test.
PiperOrigin-RevId: 247277571
Change-Id: Ib7986a27ce0106efd3102b50bfe82f0eac4b9fe3
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index de34250..163b890 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -724,4 +724,40 @@
   return false;
 }
 
+void QuicSpdySession::ProcessPendingStreamType(PendingStream* pending) {
+  DCHECK(VersionHasControlStreams(connection()->transport_version()));
+  struct iovec iov;
+  if (!pending->sequencer()->GetReadableRegion(&iov)) {
+    // We don't have the first byte yet.
+    return;
+  }
+
+  QuicDataReader reader(static_cast<char*>(iov.iov_base), iov.iov_len);
+  uint64_t stream_type = 0;
+  if (!reader.ReadVarInt62(&stream_type)) {
+    return;
+  }
+  CreateIncomingStreamFromPending(pending->id(), stream_type);
+}
+
+void QuicSpdySession::CreateIncomingStreamFromPending(QuicStreamId id,
+                                                      uint64_t stream_type) {
+  switch (stream_type) {
+    case 0x00:  // HTTP/3 control stream.
+      // TODO(renjietang): Create incoming control stream.
+      break;
+    case 0x01:  // Push Stream.
+      break;
+    case 0x02:  // QPACK encoder stream.
+      // TODO(bnc): Create QPACK encoder stream.
+      break;
+    case 0x03:  // QPACK decoder stream.
+      // TODO(bnc): Create QPACK decoder stream.
+      break;
+    default:
+      SendStopSending(0x0D, id);
+      return;
+  }
+}
+
 }  // namespace quic
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index 23e57c3..46f2b8a 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -198,6 +198,10 @@
   // Overridden to buffer incoming streams for version 99.
   bool ShouldBufferIncomingStream(QuicStreamId id) const override;
 
+  // Overridden to Process HTTP/3 stream types. No action will be taken if
+  // stream type cannot be read.
+  void ProcessPendingStreamType(PendingStream* pending) override;
+
   size_t WriteHeadersOnHeadersStreamImpl(
       QuicStreamId id,
       spdy::SpdyHeaderBlock headers,
@@ -237,6 +241,10 @@
   void set_max_uncompressed_header_bytes(
       size_t set_max_uncompressed_header_bytes);
 
+  // Creates HTTP/3 unidirectional stream of |id| and |type|. Sends
+  // STOP_SENDING frame if |type| is not supported.
+  void CreateIncomingStreamFromPending(QuicStreamId id, uint64_t type);
+
  private:
   friend class test::QuicSpdySessionPeer;
 
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 6724e69..da920dc 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -267,6 +267,7 @@
 
   using QuicSession::closed_streams;
   using QuicSession::zombie_streams;
+  using QuicSpdySession::ProcessPendingStreamType;
   using QuicSpdySession::ShouldBufferIncomingStream;
 
  private:
@@ -1855,6 +1856,75 @@
   EXPECT_EQ(kV3HighestPriority, stream->priority());
 }
 
+TEST_P(QuicSpdySessionTestServer, SimplePendingStreamType) {
+  if (!VersionHasControlStreams(transport_version())) {
+    return;
+  }
+  PendingStream pending(QuicUtils::GetFirstUnidirectionalStreamId(
+                            transport_version(), Perspective::IS_CLIENT),
+                        &session_);
+  char input[] = {// type
+                  0x04,
+                  // data
+                  'a', 'b', 'c'};
+  QuicStreamFrame data(pending.id(), true, 0, QuicStringPiece(input, 4));
+  pending.OnStreamFrame(data);
+
+  // A stop sending frame will be sent to indicate unknown type.
+  EXPECT_CALL(*connection_, SendControlFrame(_));
+  session_.ProcessPendingStreamType(&pending);
+}
+
+TEST_P(QuicSpdySessionTestServer, SimplePendingStreamTypeOutOfOrderDelivery) {
+  if (!VersionHasControlStreams(transport_version())) {
+    return;
+  }
+  PendingStream pending(QuicUtils::GetFirstUnidirectionalStreamId(
+                            transport_version(), Perspective::IS_CLIENT),
+                        &session_);
+  char input[] = {// type
+                  0x04,
+                  // data
+                  'a', 'b', 'c'};
+  QuicStreamFrame data1(pending.id(), true, 1, QuicStringPiece(&input[1], 3));
+  pending.OnStreamFrame(data1);
+  session_.ProcessPendingStreamType(&pending);
+
+  QuicStreamFrame data2(pending.id(), false, 0, QuicStringPiece(input, 1));
+  pending.OnStreamFrame(data2);
+
+  EXPECT_CALL(*connection_, SendControlFrame(_));
+  session_.ProcessPendingStreamType(&pending);
+}
+
+TEST_P(QuicSpdySessionTestServer,
+       MultipleBytesPendingStreamTypeOutOfOrderDelivery) {
+  if (!VersionHasControlStreams(transport_version())) {
+    return;
+  }
+  PendingStream pending(QuicUtils::GetFirstUnidirectionalStreamId(
+                            transport_version(), Perspective::IS_CLIENT),
+                        &session_);
+  char input[] = {// type (256)
+                  0x40 + 0x01, 0x00,
+                  // data
+                  'a', 'b', 'c'};
+
+  QuicStreamFrame data1(pending.id(), true, 2, QuicStringPiece(&input[2], 3));
+  pending.OnStreamFrame(data1);
+  session_.ProcessPendingStreamType(&pending);
+
+  QuicStreamFrame data2(pending.id(), false, 0, QuicStringPiece(input, 1));
+  pending.OnStreamFrame(data2);
+  session_.ProcessPendingStreamType(&pending);
+
+  QuicStreamFrame data3(pending.id(), false, 1, QuicStringPiece(&input[1], 1));
+  pending.OnStreamFrame(data3);
+
+  EXPECT_CALL(*connection_, SendControlFrame(_));
+  session_.ProcessPendingStreamType(&pending);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic