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,