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