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
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index d85f32c..e0f2458 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -575,6 +575,10 @@
StreamHandler GetOrCreateStreamImpl(QuicStreamId stream_id, bool may_buffer);
+ // Processes the stream type information of |pending| depending on
+ // different kinds of sessions's own rules.
+ virtual void ProcessPendingStreamType(PendingStream* pending) {}
+
bool eliminate_static_stream_map() const {
return eliminate_static_stream_map_;
}
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index f9b19b1..bad8a660 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -58,8 +58,8 @@
sequencer_(this) {}
void PendingStream::OnDataAvailable() {
- QUIC_BUG << "OnDataAvailable should not be called.";
- CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, "Unexpected data available");
+ // It will be called when pending stream receives its first byte. But this
+ // call should simply be ignored so that data remains in sequencer.
}
void PendingStream::OnFinRead() {
@@ -92,7 +92,6 @@
void PendingStream::OnStreamFrame(const QuicStreamFrame& frame) {
DCHECK_EQ(frame.stream_id, id_);
- DCHECK_NE(0u, frame.offset);
bool is_stream_too_long =
(frame.offset > kMaxStreamLength) ||
diff --git a/quic/core/quic_stream.h b/quic/core/quic_stream.h
index 0cacbe3..efe46a6 100644
--- a/quic/core/quic_stream.h
+++ b/quic/core/quic_stream.h
@@ -73,6 +73,8 @@
// Returns the number of bytes read on this stream.
uint64_t stream_bytes_read() { return stream_bytes_read_; }
+ const QuicStreamSequencer* sequencer() const { return &sequencer_; }
+
private:
friend class QuicStream;