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;