Add support for HTTP/3 control stream. Upon initialization, QuicSpdySession will create a write_unidirectional stream and write stream type and settings when crypto handshake is completed. The peer will receive the stream type and create a read_unidirectional stream that reads the settings. gfe-relnote: version 99 only. Not in prod. PiperOrigin-RevId: 252650934 Change-Id: I708280eb94dea3d6eb7e54b96ce8ee91e2b8684f
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc index bd12a90..6bd587f 100644 --- a/quic/core/http/end_to_end_test.cc +++ b/quic/core/http/end_to_end_test.cc
@@ -1530,7 +1530,10 @@ size_t client_max_open_outgoing_unidirectional_streams = client_session->connection()->transport_version() == QUIC_VERSION_99 ? QuicSessionPeer::v99_streamid_manager(client_session) - ->max_allowed_outgoing_unidirectional_streams() + ->max_allowed_outgoing_unidirectional_streams() - + QuicSessionPeer::v99_unidirectional_stream_id_manager( + client_session) + ->outgoing_static_stream_count() : QuicSessionPeer::GetStreamIdManager(client_session) ->max_open_outgoing_streams(); EXPECT_EQ(kServerMaxIncomingDynamicStreams, @@ -1548,7 +1551,10 @@ size_t server_max_open_outgoing_unidirectional_streams = server_session->connection()->transport_version() == QUIC_VERSION_99 ? QuicSessionPeer::v99_streamid_manager(server_session) - ->max_allowed_outgoing_unidirectional_streams() + ->max_allowed_outgoing_unidirectional_streams() - + QuicSessionPeer::v99_unidirectional_stream_id_manager( + server_session) + ->outgoing_static_stream_count() : QuicSessionPeer::GetStreamIdManager(server_session) ->max_open_outgoing_streams(); EXPECT_EQ(kClientMaxIncomingDynamicStreams, @@ -2048,9 +2054,13 @@ crypto_stream->flow_controller()), kStreamIFCW); } - EXPECT_EQ(kSessionIFCW, - QuicFlowControllerPeer::SendWindowSize( - client_->client()->client_session()->flow_controller())); + // When stream type is enabled, control streams will send settings and + // contribute to flow control windows, so this expectation is no longer valid. + if (!VersionHasStreamType(transport_version)) { + EXPECT_EQ(kSessionIFCW, + QuicFlowControllerPeer::SendWindowSize( + client_->client()->client_session()->flow_controller())); + } // Send a request with no body, and verify that the connection level window // has not been affected. @@ -2092,6 +2102,40 @@ server_thread_->Pause(); QuicSpdySession* const client_session = client_->client()->client_session(); auto* server_session = static_cast<QuicSpdySession*>(GetServerSession()); + + if (VersionHasStreamType(client_->client() + ->client_session() + ->connection() + ->transport_version())) { + // Settings frame will be sent through control streams, which contribute + // to the session's flow controller. And due to the timing issue described + // below, the settings frame might not be received. + HttpEncoder encoder; + SettingsFrame settings; + settings.values[6] = kDefaultMaxUncompressedHeaderSize; + std::unique_ptr<char[]> buffer; + auto header_length = encoder.SerializeSettingsFrame(settings, &buffer); + QuicByteCount win_difference1 = QuicFlowControllerPeer::ReceiveWindowSize( + server_session->flow_controller()) - + QuicFlowControllerPeer::SendWindowSize( + client_session->flow_controller()); + QuicByteCount win_difference2 = QuicFlowControllerPeer::ReceiveWindowSize( + client_session->flow_controller()) - + QuicFlowControllerPeer::SendWindowSize( + server_session->flow_controller()); + EXPECT_TRUE(win_difference1 == 0 || + win_difference1 == + header_length + + QuicDataWriter::GetVarInt62Len(kControlStream)); + EXPECT_TRUE(win_difference2 == 0 || + win_difference2 == + header_length + + QuicDataWriter::GetVarInt62Len(kControlStream)); + // The test returns early because in this version, headers stream no longer + // sends settings. + return; + } + ExpectFlowControlsSynced(client_session->flow_controller(), server_session->flow_controller()); if (!QuicVersionUsesCryptoFrames(client_->client()
diff --git a/quic/core/http/quic_receive_control_stream.cc b/quic/core/http/quic_receive_control_stream.cc index 5941189..183b6e1 100644 --- a/quic/core/http/quic_receive_control_stream.cc +++ b/quic/core/http/quic_receive_control_stream.cc
@@ -11,9 +11,6 @@ namespace quic { -const uint16_t kSettingsMaxHeaderListSize = 6; -const uint16_t kSettingsNumPlaceholders = 8; - // Visitor of HttpDecoder that passes data frame to QuicSpdyStream and closes // the connection on unexpected frames. class QuicReceiveControlStream::HttpDecoderVisitor @@ -172,10 +169,10 @@ bool QuicReceiveControlStream::OnSettingsFrame(const SettingsFrame& settings) { QuicSpdySession* spdy_session = static_cast<QuicSpdySession*>(session()); for (auto& it : settings.values) { - uint16_t setting_id = it.first; + uint64_t setting_id = it.first; switch (setting_id) { case kSettingsMaxHeaderListSize: - spdy_session->set_max_inbound_header_list_size(it.second); + spdy_session->set_max_outbound_header_list_size(it.second); break; case kSettingsNumPlaceholders: // TODO: Support placeholder setting
diff --git a/quic/core/http/quic_receive_control_stream.h b/quic/core/http/quic_receive_control_stream.h index a977f63..4e854ed 100644 --- a/quic/core/http/quic_receive_control_stream.h +++ b/quic/core/http/quic_receive_control_stream.h
@@ -34,6 +34,8 @@ // Implementation of QuicStream. void OnDataAvailable() override; + void SetUnblocked() { sequencer()->SetUnblocked(); } + protected: // Called from HttpDecoderVisitor. bool OnSettingsFrameStart(Http3FrameLengths frame_lengths);
diff --git a/quic/core/http/quic_receive_control_stream_test.cc b/quic/core/http/quic_receive_control_stream_test.cc index 73e8fed..2a4515c 100644 --- a/quic/core/http/quic_receive_control_stream_test.cc +++ b/quic/core/http/quic_receive_control_stream_test.cc
@@ -111,19 +111,19 @@ TEST_P(QuicReceiveControlStreamTest, ReceiveSettings) { SettingsFrame settings; settings.values[3] = 2; - settings.values[6] = 5; + settings.values[kSettingsMaxHeaderListSize] = 5; std::string data = EncodeSettings(settings); QuicStreamFrame frame(receive_control_stream_->id(), false, 0, QuicStringPiece(data)); - EXPECT_NE(5u, session_.max_inbound_header_list_size()); + EXPECT_NE(5u, session_.max_outbound_header_list_size()); receive_control_stream_->OnStreamFrame(frame); - EXPECT_EQ(5u, session_.max_inbound_header_list_size()); + EXPECT_EQ(5u, session_.max_outbound_header_list_size()); } TEST_P(QuicReceiveControlStreamTest, ReceiveSettingsTwice) { SettingsFrame settings; settings.values[3] = 2; - settings.values[6] = 5; + settings.values[kSettingsMaxHeaderListSize] = 5; std::string data = EncodeSettings(settings); QuicStreamFrame frame(receive_control_stream_->id(), false, 0, QuicStringPiece(data)); @@ -139,7 +139,7 @@ TEST_P(QuicReceiveControlStreamTest, ReceiveSettingsFragments) { SettingsFrame settings; settings.values[3] = 2; - settings.values[6] = 5; + settings.values[kSettingsMaxHeaderListSize] = 5; std::string data = EncodeSettings(settings); std::string data1 = data.substr(0, 1); std::string data2 = data.substr(1, data.length() - 1); @@ -148,10 +148,10 @@ QuicStringPiece(data.data(), 1)); QuicStreamFrame frame2(receive_control_stream_->id(), false, 1, QuicStringPiece(data.data() + 1, data.length() - 1)); - EXPECT_NE(5u, session_.max_inbound_header_list_size()); + EXPECT_NE(5u, session_.max_outbound_header_list_size()); receive_control_stream_->OnStreamFrame(frame); receive_control_stream_->OnStreamFrame(frame2); - EXPECT_EQ(5u, session_.max_inbound_header_list_size()); + EXPECT_EQ(5u, session_.max_outbound_header_list_size()); } TEST_P(QuicReceiveControlStreamTest, ReceiveWrongFrame) {
diff --git a/quic/core/http/quic_send_control_stream.cc b/quic/core/http/quic_send_control_stream.cc index 40b6111..19ad0c3 100644 --- a/quic/core/http/quic_send_control_stream.cc +++ b/quic/core/http/quic_send_control_stream.cc
@@ -6,6 +6,7 @@ #include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" @@ -26,6 +27,16 @@ void QuicSendControlStream::SendSettingsFrame(const SettingsFrame& settings) { DCHECK(!settings_sent_); + + QuicConnection::ScopedPacketFlusher flusher( + session()->connection(), QuicConnection::SEND_ACK_IF_PENDING); + // Send the stream type on so the peer knows about this stream. + char data[sizeof(kControlStream)]; + QuicDataWriter writer(QUIC_ARRAYSIZE(data), data); + writer.WriteVarInt62(kControlStream); + WriteOrBufferData(QuicStringPiece(writer.data(), writer.length()), false, + nullptr); + std::unique_ptr<char[]> buffer; QuicByteCount frame_length = encoder_.SerializeSettingsFrame(settings, &buffer);
diff --git a/quic/core/http/quic_send_control_stream_test.cc b/quic/core/http/quic_send_control_stream_test.cc index 1e06e17..2051e1f 100644 --- a/quic/core/http/quic_send_control_stream_test.cc +++ b/quic/core/http/quic_send_control_stream_test.cc
@@ -104,6 +104,7 @@ QuicByteCount frame_length = encoder_.SerializeSettingsFrame(settings, &buffer); + EXPECT_CALL(session_, WritevData(_, _, 1, _, _)); EXPECT_CALL(session_, WritevData(_, _, frame_length, _, _)); send_control_stream_->SendSettingsFrame(settings); }
diff --git a/quic/core/http/quic_server_session_base_test.cc b/quic/core/http/quic_server_session_base_test.cc index e0657a1..c3fac3e 100644 --- a/quic/core/http/quic_server_session_base_test.cc +++ b/quic/core/http/quic_server_session_base_test.cc
@@ -437,9 +437,9 @@ TEST_P(QuicServerSessionBaseTest, GetEvenIncomingError) { // Incoming streams on the server session must be odd. EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _)); - EXPECT_EQ(nullptr, - QuicServerSessionBasePeer::GetOrCreateDynamicStream( - session_.get(), GetNthServerInitiatedUnidirectionalId(0))); + EXPECT_EQ(nullptr, QuicServerSessionBasePeer::GetOrCreateDynamicStream( + session_.get(), + session_->next_outgoing_unidirectional_stream_id())); } TEST_P(QuicServerSessionBaseTest, GetStreamDisconnected) {
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc index 13a9fa2..d760036 100644 --- a/quic/core/http/quic_spdy_session.cc +++ b/quic/core/http/quic_spdy_session.cc
@@ -314,6 +314,7 @@ const ParsedQuicVersionVector& supported_versions) : QuicSession(connection, visitor, config, supported_versions), max_inbound_header_list_size_(kDefaultMaxUncompressedHeaderSize), + max_outbound_header_list_size_(kDefaultMaxUncompressedHeaderSize), server_push_enabled_(true), stream_id_( QuicUtils::GetInvalidStreamId(connection->transport_version())), @@ -374,7 +375,17 @@ } else { QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 7, 17); unowned_headers_stream_ = headers_stream_.get(); - RegisterStaticStreamNew(std::move(headers_stream_)); + RegisterStaticStreamNew(std::move(headers_stream_), + /*stream_already_counted = */ false); + } + + if (VersionHasStreamType(connection()->transport_version()) && + eliminate_static_stream_map()) { + auto send_control = QuicMakeUnique<QuicSendControlStream>( + GetNextOutgoingUnidirectionalStreamId(), this); + send_control_stream_ = send_control.get(); + RegisterStaticStreamNew(std::move(send_control), + /*stream_already_counted = */ false); } set_max_uncompressed_header_bytes(max_inbound_header_list_size_); @@ -523,6 +534,12 @@ } void QuicSpdySession::SendMaxHeaderListSize(size_t value) { + if (VersionHasStreamType(connection()->transport_version())) { + SettingsFrame settings; + settings.values[kSettingsMaxHeaderListSize] = value; + send_control_stream_->SendSettingsFrame(settings); + return; + } SpdySettingsIR settings_frame; settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, value); @@ -750,9 +767,14 @@ pending->MarkConsumed(stream_type_length); switch (stream_type) { - case kControlStream: // HTTP/3 control stream. - // TODO(renjietang): Create incoming control stream. - break; + case kControlStream: { // HTTP/3 control stream. + auto receive_stream = QuicMakeUnique<QuicReceiveControlStream>(pending); + receive_control_stream_ = receive_stream.get(); + RegisterStaticStreamNew(std::move(receive_stream), + /*stream_already_counted = */ true); + receive_control_stream_->SetUnblocked(); + return true; + } case kServerPushStream: { // Push Stream. QuicSpdyStream* stream = CreateIncomingStream(pending); stream->SetUnblocked();
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h index 5af73f1..9d87c0f 100644 --- a/quic/core/http/quic_spdy_session.h +++ b/quic/core/http/quic_spdy_session.h
@@ -11,6 +11,8 @@ #include "net/third_party/quiche/src/quic/core/http/quic_header_list.h" #include "net/third_party/quiche/src/quic/core/http/quic_headers_stream.h" +#include "net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h" +#include "net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h" #include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h" @@ -28,12 +30,16 @@ class QuicSpdySessionPeer; } // namespace test -// Unidirectional stream types define by IETF HTTP/3 draft in section 3.2. +// Unidirectional stream types defined by IETF HTTP/3 draft in section 3.2. const uint64_t kControlStream = 0; const uint64_t kServerPushStream = 1; const uint64_t kQpackEncoderStream = 2; const uint64_t kQpackDecoderStream = 3; +// Supported Settings id as defined by IETF HTTP/3 draft in section 7.2.5.1. +const uint64_t kSettingsMaxHeaderListSize = 6; +const uint64_t kSettingsNumPlaceholders = 8; + // QuicHpackDebugVisitor gathers data used for understanding HPACK HoL // dynamics. Specifically, it is to help predict the compression // penalty of avoiding HoL by chagning how the dynamic table is used. @@ -172,6 +178,14 @@ max_inbound_header_list_size_ = max_inbound_header_list_size; } + void set_max_outbound_header_list_size(size_t max_outbound_header_list_size) { + max_outbound_header_list_size_ = max_outbound_header_list_size; + } + + size_t max_outbound_header_list_size() const { + return max_outbound_header_list_size_; + } + size_t max_inbound_header_list_size() const { return max_inbound_header_list_size_; } @@ -288,10 +302,20 @@ // is deprecated. QuicHeadersStream* unowned_headers_stream_; + // HTTP/3 control streams. They are owned by QuicSession inside dynamic + // stream map, and can be accessed by those unowned pointers below. + QuicSendControlStream* send_control_stream_; + QuicReceiveControlStream* receive_control_stream_; + // The maximum size of a header block that will be accepted from the peer, // defined per spec as key + value + overhead per field (uncompressed). size_t max_inbound_header_list_size_; + // The maximum size of a header block that can be sent to the peer. This field + // is informed and set by the peer via SETTINGS frame. + // TODO(renjietang): Honor this field when sending headers. + size_t max_outbound_header_list_size_; + // Set during handshake. If true, resources in x-associated-content and link // headers will be pushed. bool server_push_enabled_;
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc index 83b5e5b..d6463c0 100644 --- a/quic/core/http/quic_spdy_session_test.cc +++ b/quic/core/http/quic_spdy_session_test.cc
@@ -380,6 +380,13 @@ return QuicUtils::StreamIdDelta(connection_->transport_version()); } + std::string EncodeSettings(const SettingsFrame& settings) { + HttpEncoder encoder; + std::unique_ptr<char[]> buffer; + auto header_length = encoder.SerializeSettingsFrame(settings, &buffer); + return std::string(buffer.get(), header_length); + } + QuicStreamId StreamCountToId(QuicStreamCount stream_count, Perspective perspective, bool bidirectional) { @@ -2064,6 +2071,55 @@ session_.ProcessPendingStream(&pending); } +TEST_P(QuicSpdySessionTestServer, ReceiveControlStream) { + if (!VersionHasStreamType(transport_version()) || + !GetQuicReloadableFlag(quic_eliminate_static_stream_map_3)) { + return; + } + // Use a arbitrary stream id. + QuicStreamId stream_id = + GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3); + char type[] = {kControlStream}; + + QuicStreamFrame data1(stream_id, false, 0, QuicStringPiece(type, 1)); + session_.OnStreamFrame(data1); + EXPECT_EQ(stream_id, + QuicSpdySessionPeer::GetReceiveControlStream(&session_)->id()); + + SettingsFrame settings; + settings.values[3] = 2; + settings.values[kSettingsMaxHeaderListSize] = 5; + std::string data = EncodeSettings(settings); + QuicStreamFrame frame(stream_id, false, 1, QuicStringPiece(data)); + + EXPECT_NE(5u, session_.max_outbound_header_list_size()); + session_.OnStreamFrame(frame); + EXPECT_EQ(5u, session_.max_outbound_header_list_size()); +} + +TEST_P(QuicSpdySessionTestServer, ReceiveControlStreamOutOfOrderDelivery) { + if (!VersionHasStreamType(transport_version()) || + !GetQuicReloadableFlag(quic_eliminate_static_stream_map_3)) { + return; + } + // Use an arbitrary stream id. + QuicStreamId stream_id = + GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3); + char type[] = {kControlStream}; + SettingsFrame settings; + settings.values[3] = 2; + settings.values[kSettingsMaxHeaderListSize] = 5; + std::string data = EncodeSettings(settings); + + QuicStreamFrame data1(stream_id, false, 1, QuicStringPiece(data)); + QuicStreamFrame data2(stream_id, false, 0, QuicStringPiece(type, 1)); + + session_.OnStreamFrame(data1); + EXPECT_NE(5u, session_.max_outbound_header_list_size()); + session_.OnStreamFrame(data2); + EXPECT_EQ(5u, session_.max_outbound_header_list_size()); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc index 9707c34..33b9ddb 100644 --- a/quic/core/quic_session.cc +++ b/quic/core/quic_session.cc
@@ -118,7 +118,7 @@ QuicUtils::GetCryptoStreamId(connection_->transport_version()); largest_static_stream_id_ = std::max(id, largest_static_stream_id_); if (connection_->transport_version() == QUIC_VERSION_99) { - v99_streamid_manager_.RegisterStaticStream(id); + v99_streamid_manager_.RegisterStaticStream(id, false); } } } @@ -138,16 +138,18 @@ largest_static_stream_id_ = std::max(id, largest_static_stream_id_); if (connection_->transport_version() == QUIC_VERSION_99) { - v99_streamid_manager_.RegisterStaticStream(id); + v99_streamid_manager_.RegisterStaticStream(id, false); } } -void QuicSession::RegisterStaticStreamNew(std::unique_ptr<QuicStream> stream) { +void QuicSession::RegisterStaticStreamNew(std::unique_ptr<QuicStream> stream, + bool stream_already_counted) { DCHECK(eliminate_static_stream_map_); QuicStreamId stream_id = stream->id(); dynamic_stream_map_[stream_id] = std::move(stream); if (connection_->transport_version() == QUIC_VERSION_99) { - v99_streamid_manager_.RegisterStaticStream(stream_id); + v99_streamid_manager_.RegisterStaticStream(stream_id, + stream_already_counted); } if (IsIncomingStream(stream_id)) { ++num_incoming_static_streams_;
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h index 32dd680..ef4e804 100644 --- a/quic/core/quic_session.h +++ b/quic/core/quic_session.h
@@ -500,7 +500,8 @@ void RegisterStaticStream(QuicStreamId id, QuicStream* stream); // TODO(renjietang): Replace the original Register method with the new one // once flag is deprecated. - void RegisterStaticStreamNew(std::unique_ptr<QuicStream> stream); + void RegisterStaticStreamNew(std::unique_ptr<QuicStream> stream, + bool stream_already_counted); const StaticStreamMap& static_streams() const { return static_stream_map_; } DynamicStreamMap& dynamic_streams() { return dynamic_stream_map_; }
diff --git a/quic/core/quic_stream_id_manager.cc b/quic/core/quic_stream_id_manager.cc index 2921268..7eaa91d 100644 --- a/quic/core/quic_stream_id_manager.cc +++ b/quic/core/quic_stream_id_manager.cc
@@ -239,7 +239,8 @@ return false; } -bool QuicStreamIdManager::RegisterStaticStream(QuicStreamId stream_id) { +bool QuicStreamIdManager::RegisterStaticStream(QuicStreamId stream_id, + bool stream_already_counted) { DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_); if (IsIncomingStream(stream_id)) { // This code is predicated on static stream ids being allocated densely, in @@ -267,7 +268,10 @@ QuicUtils::GetMaxStreamCount(unidirectional_, perspective())) { incoming_advertised_max_streams_++; } - incoming_stream_count_++; + + if (!stream_already_counted) { + incoming_stream_count_++; + } incoming_static_stream_count_++; return true; }
diff --git a/quic/core/quic_stream_id_manager.h b/quic/core/quic_stream_id_manager.h index 42c7d1a..d0b0824 100644 --- a/quic/core/quic_stream_id_manager.h +++ b/quic/core/quic_stream_id_manager.h
@@ -113,9 +113,12 @@ // advertised MAX STREAMS can be calculated based on the start of the // dynamic stream space. This method will take any stream ID, one that either // this node or the peer will initiate. + // If |stream_already_counted| is true, the stream is already counted as an + // open stream else where, so no need to count it again. // Returns false if this fails because the new static stream would cause the // stream limit to be exceeded. - bool RegisterStaticStream(QuicStreamId stream_id); + bool RegisterStaticStream(QuicStreamId stream_id, + bool stream_already_counted); // Checks if the incoming stream ID exceeds the MAX_STREAMS limit. If the // limit is exceeded, closes the connection and returns false. Uses the
diff --git a/quic/core/quic_stream_id_manager_test.cc b/quic/core/quic_stream_id_manager_test.cc index 79c4845..107f472 100644 --- a/quic/core/quic_stream_id_manager_test.cc +++ b/quic/core/quic_stream_id_manager_test.cc
@@ -640,7 +640,8 @@ // First test will register the first dynamic stream id as being for a static // stream. - stream_id_manager_->RegisterStaticStream(first_dynamic); + stream_id_manager_->RegisterStaticStream(first_dynamic, + /*stream_already_counted = */ false); // Should go up by 1 stream/stream id. EXPECT_EQ(actual_max + 1u, stream_id_manager_->incoming_actual_max_streams()); }
diff --git a/quic/core/uber_quic_stream_id_manager.cc b/quic/core/uber_quic_stream_id_manager.cc index 75f7c6a..24c3b7c 100644 --- a/quic/core/uber_quic_stream_id_manager.cc +++ b/quic/core/uber_quic_stream_id_manager.cc
@@ -24,12 +24,16 @@ /*unidirectional=*/true, max_open_outgoing_unidirectional_streams, max_open_incoming_unidirectional_streams) {} -void UberQuicStreamIdManager::RegisterStaticStream(QuicStreamId id) { +void UberQuicStreamIdManager::RegisterStaticStream( + QuicStreamId id, + bool stream_already_counted) { if (QuicUtils::IsBidirectionalStreamId(id)) { - bidirectional_stream_id_manager_.RegisterStaticStream(id); + bidirectional_stream_id_manager_.RegisterStaticStream( + id, stream_already_counted); return; } - unidirectional_stream_id_manager_.RegisterStaticStream(id); + unidirectional_stream_id_manager_.RegisterStaticStream( + id, stream_already_counted); } void UberQuicStreamIdManager::AdjustMaxOpenOutgoingUnidirectionalStreams(
diff --git a/quic/core/uber_quic_stream_id_manager.h b/quic/core/uber_quic_stream_id_manager.h index 288f0c0..45b0d33 100644 --- a/quic/core/uber_quic_stream_id_manager.h +++ b/quic/core/uber_quic_stream_id_manager.h
@@ -28,7 +28,9 @@ QuicStreamCount max_open_incoming_unidirectional_streams); // Called when a stream with |stream_id| is registered as a static stream. - void RegisterStaticStream(QuicStreamId id); + // If |stream_already_counted| is true, the static stream is already counted + // as an open stream earlier, so no need to count it again. + void RegisterStaticStream(QuicStreamId id, bool stream_already_counted); // Sets the maximum outgoing stream count as a result of doing the transport // configuration negotiation. Forces the limit to max_streams, regardless of
diff --git a/quic/core/uber_quic_stream_id_manager_test.cc b/quic/core/uber_quic_stream_id_manager_test.cc index 8b1bace..a2498b0 100644 --- a/quic/core/uber_quic_stream_id_manager_test.cc +++ b/quic/core/uber_quic_stream_id_manager_test.cc
@@ -133,7 +133,8 @@ manager_->actual_max_allowed_incoming_bidirectional_streams(); QuicStreamCount actual_max_allowed_incoming_unidirectional_streams = manager_->actual_max_allowed_incoming_unidirectional_streams(); - manager_->RegisterStaticStream(first_incoming_bidirectional_stream_id); + manager_->RegisterStaticStream(first_incoming_bidirectional_stream_id, + /*stream_already_counted = */ false); // Verify actual_max_allowed_incoming_bidirectional_streams increases. EXPECT_EQ(actual_max_allowed_incoming_bidirectional_streams + 1u, manager_->actual_max_allowed_incoming_bidirectional_streams()); @@ -142,7 +143,8 @@ EXPECT_EQ(actual_max_allowed_incoming_unidirectional_streams, manager_->actual_max_allowed_incoming_unidirectional_streams()); - manager_->RegisterStaticStream(first_incoming_unidirectional_stream_id); + manager_->RegisterStaticStream(first_incoming_unidirectional_stream_id, + /*stream_already_counted = */ false); EXPECT_EQ(actual_max_allowed_incoming_bidirectional_streams + 1u, manager_->actual_max_allowed_incoming_bidirectional_streams()); EXPECT_EQ(actual_max_allowed_incoming_unidirectional_streams + 1u,
diff --git a/quic/test_tools/quic_session_peer.cc b/quic/test_tools/quic_session_peer.cc index 9eee494..60133cf 100644 --- a/quic/test_tools/quic_session_peer.cc +++ b/quic/test_tools/quic_session_peer.cc
@@ -176,7 +176,8 @@ void QuicSessionPeer::RegisterStaticStreamNew( QuicSession* session, std::unique_ptr<QuicStream> stream) { - return session->RegisterStaticStreamNew(std::move(stream)); + return session->RegisterStaticStreamNew(std::move(stream), + /*stream_already_counted = */ false); } // static
diff --git a/quic/test_tools/quic_spdy_session_peer.cc b/quic/test_tools/quic_spdy_session_peer.cc index 956cd88..6a49bb7 100644 --- a/quic/test_tools/quic_spdy_session_peer.cc +++ b/quic/test_tools/quic_spdy_session_peer.cc
@@ -81,5 +81,11 @@ return session->GetNextOutgoingUnidirectionalStreamId(); } +// static +QuicReceiveControlStream* QuicSpdySessionPeer::GetReceiveControlStream( + QuicSpdySession* session) { + return session->receive_control_stream_; +} + } // namespace test } // namespace quic
diff --git a/quic/test_tools/quic_spdy_session_peer.h b/quic/test_tools/quic_spdy_session_peer.h index 47b55e1..47ff021 100644 --- a/quic/test_tools/quic_spdy_session_peer.h +++ b/quic/test_tools/quic_spdy_session_peer.h
@@ -5,6 +5,7 @@ #ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_SESSION_PEER_H_ #define QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_SESSION_PEER_H_ +#include "net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h" #include "net/third_party/quiche/src/spdy/core/spdy_framer.h" @@ -46,6 +47,8 @@ // |session| can't be nullptr. static QuicStreamId GetNextOutgoingUnidirectionalStreamId( QuicSpdySession* session); + static QuicReceiveControlStream* GetReceiveControlStream( + QuicSpdySession* session); }; } // namespace test
diff --git a/quic/test_tools/quic_test_utils.cc b/quic/test_tools/quic_test_utils.cc index 5021068..38873dd 100644 --- a/quic/test_tools/quic_test_utils.cc +++ b/quic/test_tools/quic_test_utils.cc
@@ -1169,6 +1169,14 @@ QuicUtils::StreamIdDelta(version) * n; } +QuicStreamId GetNthClientInitiatedUnidirectionalStreamId( + QuicTransportVersion version, + int n) { + return QuicUtils::GetFirstUnidirectionalStreamId(version, + Perspective::IS_CLIENT) + + QuicUtils::StreamIdDelta(version) * n; +} + StreamType DetermineStreamType(QuicStreamId id, QuicTransportVersion version, Perspective perspective,
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h index 8f1cb7a..cb51a3b 100644 --- a/quic/test_tools/quic_test_utils.h +++ b/quic/test_tools/quic_test_utils.h
@@ -1170,6 +1170,9 @@ QuicStreamId GetNthServerInitiatedUnidirectionalStreamId( QuicTransportVersion version, int n); +QuicStreamId GetNthClientInitiatedUnidirectionalStreamId( + QuicTransportVersion version, + int n); StreamType DetermineStreamType(QuicStreamId id, QuicTransportVersion version,
diff --git a/quic/tools/quic_simple_server_session.cc b/quic/tools/quic_simple_server_session.cc index 93a8490..d2945e3 100644 --- a/quic/tools/quic_simple_server_session.cc +++ b/quic/tools/quic_simple_server_session.cc
@@ -32,7 +32,11 @@ crypto_config, compressed_certs_cache), highest_promised_stream_id_( - QuicUtils::GetInvalidStreamId(connection->transport_version())), + VersionHasStreamType(connection->transport_version()) + ? QuicUtils::GetFirstUnidirectionalStreamId( + connection->transport_version(), + Perspective::IS_SERVER) + : QuicUtils::GetInvalidStreamId(connection->transport_version())), quic_simple_server_backend_(quic_simple_server_backend) { DCHECK(quic_simple_server_backend_); }
diff --git a/quic/tools/quic_simple_server_session_test.cc b/quic/tools/quic_simple_server_session_test.cc index aa2defa..9071e05 100644 --- a/quic/tools/quic_simple_server_session_test.cc +++ b/quic/tools/quic_simple_server_session_test.cc
@@ -502,7 +502,12 @@ QuicSpdyStream* created_stream = QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream( session_.get()); - EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(i), created_stream->id()); + if (VersionHasStreamType(connection_->transport_version())) { + EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(i + 1), + created_stream->id()); + } else { + EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(i), created_stream->id()); + } EXPECT_EQ(i + 1, session_->GetNumOpenOutgoingStreams()); } @@ -617,7 +622,12 @@ std::string scheme = "http"; QuicByteCount data_frame_header_length = 0; for (unsigned int i = 1; i <= num_resources; ++i) { - QuicStreamId stream_id = GetNthServerInitiatedUnidirectionalId(i - 1); + QuicStreamId stream_id; + if (VersionHasStreamType(connection_->transport_version())) { + stream_id = GetNthServerInitiatedUnidirectionalId(i); + } else { + stream_id = GetNthServerInitiatedUnidirectionalId(i - 1); + } std::string path = partial_push_resource_path + QuicTextUtils::Uint64ToString(i); std::string url = scheme + "://" + resource_host + path; @@ -714,8 +724,14 @@ MaybeConsumeHeadersStreamData(); size_t num_resources = kMaxStreamsForTest + 1; QuicByteCount data_frame_header_length = PromisePushResources(num_resources); - QuicStreamId next_out_going_stream_id = - GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest); + QuicStreamId next_out_going_stream_id; + if (VersionHasStreamType(connection_->transport_version())) { + next_out_going_stream_id = + GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 1); + } else { + next_out_going_stream_id = + GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest); + } // After an open stream is marked draining, a new stream is expected to be // created and a response sent on the stream. @@ -753,11 +769,17 @@ // a MAX_STREAMS frame is received. This emulates the reception of one. // For pre-v-99, the node monitors its own stream usage and makes streams // available as it closes/etc them. + // Version 99 also has unidirectional static streams, so we need to send + // MaxStreamFrame of the number of resources + number of static streams. session_->OnMaxStreamsFrame( - QuicMaxStreamsFrame(0, num_resources, /*unidirectional=*/true)); + QuicMaxStreamsFrame(0, num_resources + 1, /*unidirectional=*/true)); } - session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(0)); + if (VersionHasStreamType(connection_->transport_version())) { + session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(1)); + } else { + session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(0)); + } // Number of open outgoing streams should still be the same, because a new // stream is opened. And the queue should be empty. EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams()); @@ -783,8 +805,14 @@ QuicByteCount data_frame_header_length = PromisePushResources(num_resources); // Reset the last stream in the queue. It should be marked cancelled. - QuicStreamId stream_got_reset = - GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 1); + QuicStreamId stream_got_reset; + if (VersionHasStreamType(connection_->transport_version())) { + stream_got_reset = + GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 2); + } else { + stream_got_reset = + GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 1); + } QuicRstStreamFrame rst(kInvalidControlFrameId, stream_got_reset, QUIC_STREAM_CANCELLED, 0); EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1); @@ -798,8 +826,14 @@ // When the first 2 streams becomes draining, the two queued up stream could // be created. But since one of them was marked cancelled due to RST frame, // only one queued resource will be sent out. - QuicStreamId stream_not_reset = - GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest); + QuicStreamId stream_not_reset; + if (VersionHasStreamType(connection_->transport_version())) { + stream_not_reset = + GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 1); + } else { + stream_not_reset = + GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest); + } InSequence s; QuicStreamOffset offset = 0; if (VersionHasStreamType(connection_->transport_version())) { @@ -835,10 +869,10 @@ // For pre-v-99, the node monitors its own stream usage and makes streams // available as it closes/etc them. session_->OnMaxStreamsFrame( - QuicMaxStreamsFrame(0, num_resources, /*unidirectional=*/true)); + QuicMaxStreamsFrame(0, num_resources + 1, /*unidirectional=*/true)); } - session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(0)); session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(1)); + session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(2)); } // Tests that closing a open outgoing stream can trigger a promised resource in @@ -856,12 +890,17 @@ this, &QuicSimpleServerSessionServerPushTest::ClearControlFrame)); } QuicByteCount data_frame_header_length = PromisePushResources(num_resources); - QuicStreamId stream_to_open = - GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest); + QuicStreamId stream_to_open; + if (VersionHasStreamType(connection_->transport_version())) { + stream_to_open = + GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 1); + } else { + stream_to_open = GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest); + } - // Resetting 1st open stream will close the stream and give space for extra + // Resetting an open stream will close the stream and give space for extra // stream to be opened. - QuicStreamId stream_got_reset = GetNthServerInitiatedUnidirectionalId(0); + QuicStreamId stream_got_reset = GetNthServerInitiatedUnidirectionalId(1); EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1); EXPECT_CALL(*connection_, SendControlFrame(_)); if (!IsVersion99()) { @@ -905,7 +944,7 @@ // For pre-v-99, the node monitors its own stream usage and makes streams // available as it closes/etc them. session_->OnMaxStreamsFrame( - QuicMaxStreamsFrame(0, num_resources, /*unidirectional=*/true)); + QuicMaxStreamsFrame(0, num_resources + 1, /*unidirectional=*/true)); } visitor_->OnRstStream(rst); // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a