Prevent IETF QUIC Frame transmission prior to config
A number of IETF QUIC fames, including MAX_STREAMS and STREAMS_BLOCKED, should not be sent prior to having the session configured.
gfe-relnote: N/A IETF QUIC, protected via V99 flag.
PiperOrigin-RevId: 269611696
Change-Id: Ibed580f9bff8f716f3f78481022b5c929f5634ef
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 310081c..8ea04f8 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -1645,6 +1645,9 @@
// it) does not count against the open quota (because it is closed from the
// protocol point of view).
if (VersionHasIetfQuicFrames(transport_version())) {
+ // Simulate receiving a config. so that MAX_STREAMS/etc frames may
+ // be transmitted
+ QuicSessionPeer::set_is_configured(&session_, true);
// Version 99 will result in a MAX_STREAMS frame as streams are consumed
// (via the OnStreamFrame call) and then released (via
// StreamDraining). Eventually this node will believe that the peer is
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 9ede3f8..23873d0 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -1044,6 +1044,13 @@
config_.ReceivedInitialSessionFlowControlWindowBytes());
}
is_configured_ = true;
+
+ // Inform stream ID manager so that it can reevaluate any deferred
+ // STREAMS_BLOCKED or MAX_STREAMS frames against the config and either send
+ // the frames or discard them.
+ if (VersionHasIetfQuicFrames(connection_->transport_version())) {
+ v99_streamid_manager_.OnConfigNegotiated();
+ }
}
void QuicSession::AdjustInitialFlowControlWindows(size_t stream_window) {
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index 004af59..60178fe 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -32,6 +32,7 @@
#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
@@ -155,7 +156,6 @@
}
~TestSession() override {
- EXPECT_TRUE(is_configured());
delete connection();
}
@@ -284,14 +284,14 @@
return consumed;
}
+ const QuicFrame& save_frame() { return save_frame_; }
+
bool SaveFrame(const QuicFrame& frame) {
save_frame_ = frame;
DeleteFrame(&const_cast<QuicFrame&>(frame));
return true;
}
- const QuicFrame& save_frame() { return save_frame_; }
-
QuicConsumedData SendLargeFakeData(QuicStream* stream, int bytes) {
DCHECK(writev_consumes_all_data_);
return WritevData(stream, stream->id(), bytes, 0, FIN);
@@ -308,6 +308,7 @@
}
using QuicSession::ActivateStream;
+ using QuicSession::CanOpenNextOutgoingUnidirectionalStream;
using QuicSession::closed_streams;
using QuicSession::zombie_streams;
@@ -322,13 +323,14 @@
class QuicSessionTestBase : public QuicTestWithParam<ParsedQuicVersion> {
protected:
- explicit QuicSessionTestBase(Perspective perspective)
+ QuicSessionTestBase(Perspective perspective, bool configure_session)
: connection_(
new StrictMock<MockQuicConnection>(&helper_,
&alarm_factory_,
perspective,
SupportedVersions(GetParam()))),
- session_(connection_, &session_visitor_) {
+ session_(connection_, &session_visitor_),
+ configure_session_(configure_session) {
session_.config()->SetInitialStreamFlowControlWindowToSend(
kInitialStreamFlowControlWindowForTest);
session_.config()->SetInitialSessionFlowControlWindowToSend(
@@ -341,12 +343,20 @@
QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(
session_.config(), kMinimumFlowControlSendWindow);
connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
- session_.OnConfigNegotiated();
+ if (configure_session) {
+ session_.OnConfigNegotiated();
+ }
TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
.Times(testing::AnyNumber());
}
+ ~QuicSessionTestBase() {
+ if (configure_session_) {
+ EXPECT_TRUE(session_.is_configured());
+ }
+ }
+
void CheckClosedStreams() {
QuicStreamId first_stream_id = QuicUtils::GetFirstBidirectionalStreamId(
connection_->transport_version(), Perspective::IS_CLIENT);
@@ -440,6 +450,7 @@
StrictMock<MockQuicConnection>* connection_;
TestSession session_;
std::set<QuicStreamId> closed_streams_;
+ bool configure_session_;
};
class QuicSessionTestServer : public QuicSessionTestBase {
@@ -479,7 +490,7 @@
protected:
QuicSessionTestServer()
- : QuicSessionTestBase(Perspective::IS_SERVER),
+ : QuicSessionTestBase(Perspective::IS_SERVER, /*configure_session=*/true),
path_frame_buffer1_({0, 1, 2, 3, 4, 5, 6, 7}),
path_frame_buffer2_({8, 9, 10, 11, 12, 13, 14, 15}),
client_framer_(SupportedVersions(GetParam()),
@@ -1943,7 +1954,9 @@
class QuicSessionTestClient : public QuicSessionTestBase {
protected:
- QuicSessionTestClient() : QuicSessionTestBase(Perspective::IS_CLIENT) {}
+ QuicSessionTestClient()
+ : QuicSessionTestBase(Perspective::IS_CLIENT,
+ /*configure_session=*/true) {}
};
INSTANTIATE_TEST_SUITE_P(Tests,
@@ -2553,6 +2566,7 @@
// Applicable only to IETF QUIC
return;
}
+
QuicStreamId bidirectional_stream_id = StreamCountToId(
QuicSessionPeer::v99_streamid_manager(&session_)
->advertised_max_allowed_incoming_bidirectional_streams() +
@@ -2729,6 +2743,113 @@
}
}
+// A client test class that can be used when the automatic configuration is not
+// desired.
+class QuicSessionTestClientUnconfigured : public QuicSessionTestBase {
+ protected:
+ QuicSessionTestClientUnconfigured()
+ : QuicSessionTestBase(Perspective::IS_CLIENT,
+ /*configure_session=*/false) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSessionTestClientUnconfigured,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+TEST_P(QuicSessionTestClientUnconfigured, HoldMaxStreamsFrame) {
+ if (!VersionHasIetfQuicFrames(transport_version())) {
+ return;
+ }
+ QuicStreamIdManager* stream_id_manager =
+ QuicSessionPeer::v99_unidirectional_stream_id_manager(&session_);
+
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ QuicStreamsBlockedFrame frame(1u, 0u, /*unidirectional=*/true);
+ session_.OnStreamsBlockedFrame(frame);
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(1)
+ .WillRepeatedly(Invoke(&session_, &TestSession::SaveFrame));
+ session_.OnConfigNegotiated();
+ EXPECT_EQ(MAX_STREAMS_FRAME, session_.save_frame().type);
+ EXPECT_EQ(stream_id_manager->incoming_actual_max_streams(),
+ session_.save_frame().max_streams_frame.stream_count);
+ EXPECT_EQ(1u, session_.save_frame().max_streams_frame.control_frame_id);
+}
+
+TEST_P(QuicSessionTestClientUnconfigured, HoldStreamsBlockedFrameXmit) {
+ if (!VersionHasIetfQuicFrames(transport_version())) {
+ // Applicable only to IETF QUIC
+ return;
+ }
+ QuicStreamIdManager* stream_id_manager =
+ QuicSessionPeer::v99_unidirectional_stream_id_manager(&session_);
+
+ // Set the stream limit to 0 which will cause
+ // CanOpenNextOutgoingUnidirectionalStream()
+ // to generated a STREAMS_BLOCKED frame.
+ QuicStreamIdManagerPeer::set_outgoing_max_streams(stream_id_manager, 0);
+
+ // Since the stream limit is 0 and no sreams can be created this should return
+ // false and have forced a streams-blocked to be queued up, with the
+ // blocked stream id == 0.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ EXPECT_FALSE(session_.CanOpenNextOutgoingUnidirectionalStream());
+
+ // We will expect two calls to SendControlFrame: The first is because
+ // OnConfigNegotiated does not increase the limit, so the app still can not
+ // create new streams (and therefore needs the STREAMS-BLOCKED to go out). The
+ // second is because the ensuing CanOpenNext.. call will fail (this test not
+ // actually increasing the limit) and that will send another STREAMS-BLOCKED.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(2)
+ .WillRepeatedly(Invoke(&session_, &TestSession::SaveFrame));
+ // Set configuration data so that when the config happens, the stream limit is
+ // not increased and another STREAMS-BLOCKED will be needed..
+ QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams(session_.config(),
+ 0);
+
+ session_.OnConfigNegotiated();
+
+ EXPECT_EQ(STREAMS_BLOCKED_FRAME, session_.save_frame().type);
+ EXPECT_EQ(0u, session_.save_frame().streams_blocked_frame.stream_count);
+ EXPECT_EQ(1u, session_.save_frame().streams_blocked_frame.control_frame_id);
+
+ EXPECT_FALSE(session_.CanOpenNextOutgoingUnidirectionalStream());
+ EXPECT_EQ(STREAMS_BLOCKED_FRAME, session_.save_frame().type);
+ EXPECT_EQ(0u, session_.save_frame().streams_blocked_frame.stream_count);
+ EXPECT_EQ(2u, session_.save_frame().streams_blocked_frame.control_frame_id);
+}
+
+TEST_P(QuicSessionTestClientUnconfigured, HoldStreamsBlockedFrameNoXmit) {
+ if (!VersionHasIetfQuicFrames(transport_version())) {
+ return;
+ }
+ QuicStreamIdManager* stream_id_manager =
+ QuicSessionPeer::v99_unidirectional_stream_id_manager(&session_);
+
+ // Set the stream limit to 0 which will cause
+ // CanOpenNextOutgoingUnidirectionalStream()
+ // to generated a STREAMS_BLOCKED frame.
+ QuicStreamIdManagerPeer::set_outgoing_max_streams(stream_id_manager, 0);
+
+ // Since the stream limit is 0 and no streams can be created this should
+ // return false and have forced a streams-blocked to be queued up, with the
+ // blocked stream id == 0.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ EXPECT_FALSE(session_.CanOpenNextOutgoingUnidirectionalStream());
+
+ // Set configuration data so that when the config happens, the stream limit is
+ // increased.
+ QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams(session_.config(),
+ 10);
+
+ // STREAMS_BLOCKED frame should not be sent because streams can now be
+ // created.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ session_.OnConfigNegotiated();
+ EXPECT_TRUE(session_.CanOpenNextOutgoingUnidirectionalStream());
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_stream_id_manager.cc b/quic/core/quic_stream_id_manager.cc
index c400d77..9d94157 100644
--- a/quic/core/quic_stream_id_manager.cc
+++ b/quic/core/quic_stream_id_manager.cc
@@ -39,7 +39,10 @@
incoming_stream_count_(0),
largest_peer_created_stream_id_(
QuicUtils::GetInvalidStreamId(transport_version())),
- max_streams_window_(0) {
+ max_streams_window_(0),
+ pending_max_streams_(false),
+ pending_streams_blocked_(
+ QuicUtils::GetInvalidStreamId(transport_version())) {
CalculateIncomingMaxStreamsWindow();
}
@@ -50,20 +53,11 @@
// Ensure that the frame has the correct directionality.
DCHECK_EQ(frame.unidirectional, unidirectional_);
QUIC_CODE_COUNT_N(quic_max_streams_received, 2, 2);
- const QuicStreamCount current_outgoing_max_streams = outgoing_max_streams_;
// Set the limit to be exactly the stream count in the frame.
- if (!SetMaxOpenOutgoingStreams(frame.stream_count)) {
- return false;
- }
- // If we were at the previous limit and this MAX_STREAMS frame
- // increased the limit, inform the application that new streams are
- // available.
- if (outgoing_stream_count_ == current_outgoing_max_streams &&
- current_outgoing_max_streams < outgoing_max_streams_) {
- session_->OnCanCreateNewOutgoingStream(unidirectional_);
- }
- return true;
+ // Also informs the higher layers that they can create more
+ // streams if the limit is increased.
+ return SetMaxOpenOutgoingStreams(frame.stream_count);
}
// The peer sends a streams blocked frame when it can not open any more
@@ -140,6 +134,11 @@
outgoing_max_streams_ = std::min<size_t>(
max_open_streams,
QuicUtils::GetMaxStreamCount(unidirectional_, session_->perspective()));
+
+ // Inform the higher layers that the stream limit has increased and that
+ // new streams may be created.
+ session_->OnCanCreateNewOutgoingStream(unidirectional_);
+
return true;
}
@@ -171,6 +170,13 @@
}
void QuicStreamIdManager::SendMaxStreamsFrame() {
+ if (!session_->is_configured()) {
+ // Session not configured, so we can not send the MAX STREAMS frame yet.
+ // Remember that we would have sent one and then return. A new frame will
+ // be generated once the configuration is received.
+ pending_max_streams_ = true;
+ return;
+ }
incoming_advertised_max_streams_ = incoming_actual_max_streams_;
session_->SendMaxStreams(incoming_advertised_max_streams_, unidirectional_);
}
@@ -213,6 +219,15 @@
return true;
}
// Next stream ID would exceed the limit, need to inform the peer.
+
+ if (!session_->is_configured()) {
+ // Session not configured, so we can not send the STREAMS_BLOCKED frame yet.
+ // Remember that we would have sent one, and what the limit was when we were
+ // blocked, and return.
+ pending_streams_blocked_ = outgoing_max_streams_;
+ return false;
+ }
+
session_->SendStreamsBlocked(outgoing_max_streams_, unidirectional_);
QUIC_CODE_COUNT(quic_reached_outgoing_stream_id_limit);
return false;
@@ -342,4 +357,25 @@
}
}
+void QuicStreamIdManager::OnConfigNegotiated() {
+ QuicConnection::ScopedPacketFlusher flusher(session_->connection());
+ // If a STREAMS_BLOCKED or MAX_STREAMS is pending, send it and clear
+ // the pending state.
+ if (pending_streams_blocked_ !=
+ QuicUtils::GetInvalidStreamId(transport_version())) {
+ if (pending_streams_blocked_ >= outgoing_max_streams_) {
+ // There is a pending STREAMS_BLOCKED frame and the current limit does not
+ // let new streams be formed. Regenerate and send the frame.
+ session_->SendStreamsBlocked(outgoing_max_streams_, unidirectional_);
+ }
+ pending_streams_blocked_ =
+ QuicUtils::GetInvalidStreamId(transport_version());
+ }
+ if (pending_max_streams_) {
+ // Generate a MAX_STREAMS using the current stream limits.
+ SendMaxStreamsFrame();
+ pending_max_streams_ = false;
+ }
+}
+
} // namespace quic
diff --git a/quic/core/quic_stream_id_manager.h b/quic/core/quic_stream_id_manager.h
index baba5ac..51524ba 100644
--- a/quic/core/quic_stream_id_manager.h
+++ b/quic/core/quic_stream_id_manager.h
@@ -149,6 +149,10 @@
QuicTransportVersion transport_version() const;
+ // Called when session has been configured. Causes the Stream ID manager to
+ // send out any pending MAX_STREAMS and STREAMS_BLOCKED frames.
+ void OnConfigNegotiated();
+
private:
friend class test::QuicSessionPeer;
friend class test::QuicStreamIdManagerPeer;
@@ -226,6 +230,13 @@
// max_streams_window_ is set to 1/2 of the initial number of incoming streams
// that are allowed (as set in the constructor).
QuicStreamId max_streams_window_;
+
+ // MAX_STREAMS and STREAMS_BLOCKED frames are not sent before the session has
+ // been configured. Instead, the relevant information is stored in
+ // |pending_max_streams_| and |pending_streams_blocked_| and sent when
+ // OnConfigNegotiated() is invoked.
+ bool pending_max_streams_;
+ QuicStreamId pending_streams_blocked_;
};
} // namespace quic
diff --git a/quic/core/quic_stream_id_manager_test.cc b/quic/core/quic_stream_id_manager_test.cc
index 06eb141..67f20f4 100644
--- a/quic/core/quic_stream_id_manager_test.cc
+++ b/quic/core/quic_stream_id_manager_test.cc
@@ -56,6 +56,9 @@
public:
TestQuicSession(QuicConnection* connection)
: MockQuicSession(connection, /*create_mock_crypto_stream=*/true) {
+ // Initialize to an invalid frame type to detect cases where the frame type
+ // is not set subsequently.
+ save_frame_.type = static_cast<QuicFrameType>(-1);
Initialize();
}
@@ -277,6 +280,10 @@
QuicStreamCount stream_count =
stream_id_manager_->incoming_initial_max_open_streams() - 1;
QuicStreamsBlockedFrame frame(0, stream_count, /*unidirectional=*/false);
+
+ // Simulate being configured so that the MAX_STREAMS is transmitted.
+ session_->OnConfigNegotiated();
+
session_->OnStreamsBlockedFrame(frame);
// We should see a MAX_STREAMS frame.
@@ -395,6 +402,9 @@
// Get the current maximum allowed incoming stream count.
QuicStreamCount advertised_stream_count =
stream_id_manager_->incoming_advertised_max_streams();
+ // Simulate receiving a config to allow frame transmission
+ session_->OnConfigNegotiated();
+
QuicStreamsBlockedFrame frame;
frame.unidirectional = IsUnidi();
@@ -457,6 +467,15 @@
// Number of streams we can open and the first one we should get when
// opening...
size_t number_of_streams = kDefaultMaxStreamsPerConnection;
+
+ // Set up config to allow the default stream limit and then
+ // simulate receiving a config to allow frame transmission
+ QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams(
+ session_->config(), kDefaultMaxStreamsPerConnection);
+ QuicConfigPeer::SetReceivedMaxIncomingBidirectionalStreams(
+ session_->config(), kDefaultMaxStreamsPerConnection);
+ session_->OnConfigNegotiated();
+
QuicStreamId stream_id =
IsUnidi() ? session_->next_outgoing_unidirectional_stream_id()
: session_->next_outgoing_bidirectional_stream_id();
@@ -510,6 +529,9 @@
// Test the MAX STREAMS Window functionality.
TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerServerMaxStreams) {
+ // Simulate completed config to allow frame transmission
+ session_->OnConfigNegotiated();
+
// Test that a MAX_STREAMS frame is generated when the peer has less than
// |max_streams_window_| streams left that it can initiate.
@@ -585,6 +607,9 @@
// Check that edge conditions of the stream count in a STREAMS_BLOCKED frame
// are. properly handled.
TEST_P(QuicStreamIdManagerTestClient, StreamsBlockedEdgeConditions) {
+ // Simulate completed config to allow frame transmission
+ session_->OnConfigNegotiated();
+
QuicStreamsBlockedFrame frame;
frame.unidirectional = IsUnidi();
@@ -607,6 +632,28 @@
EXPECT_EQ(123u, session_->save_frame().max_streams_frame.stream_count);
}
+TEST_P(QuicStreamIdManagerTestClient, HoldMaxStreamsFrame) {
+ if (!VersionHasIetfQuicFrames(transport_version())) {
+ return;
+ }
+
+ // The session has not been configured so frame transmission will not be
+ // allowed.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+
+ QuicStreamsBlockedFrame frame(
+ 1u, 0u, QuicStreamIdManagerPeer::get_unidirectional(stream_id_manager_));
+ // Should cause change in pending_max_streams.
+ stream_id_manager_->OnStreamsBlockedFrame(frame);
+ // Will do OnConfig. We should see a control frame pop out now.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+
+ // Now allow frame transmission -- which happens when the QuicSession
+ // receives the configuration and calls
+ // QuicStreamIdManager::OnConfigNegotiated()
+ session_->OnConfigNegotiated();
+}
+
// Following tests all are server-specific. They depend, in some way, on
// server-specific attributes, such as the initial stream ID.
@@ -616,6 +663,64 @@
: QuicStreamIdManagerTestBase(Perspective::IS_SERVER) {}
};
+TEST_P(QuicStreamIdManagerTestClient, HoldStreamsBlockedFrameXmit) {
+ if (!VersionHasIetfQuicFrames(transport_version())) {
+ return;
+ }
+
+ // set outgoing limit to 0, will cause the CanOpenNext... to fail
+ // leading to a STREAMS_BLOCKED.
+ QuicStreamIdManagerPeer::set_outgoing_max_streams(stream_id_manager_, 0);
+
+ // We should not see a STREAMS-BLOCKED frame because we're not configured..
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+
+ // Since the stream limit is 0 and no sreams can be created this should return
+ // false and have forced a streams-blocked to be queued up, with the
+ // blocked stream id == 0.
+ EXPECT_FALSE(stream_id_manager_->CanOpenNextOutgoingStream());
+
+ // Simulate receipt of the configuration; This case does not update the
+ // outgoing stream limit, so the on-config should result in a streams-blocked
+ // being sent.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+ QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams(
+ session_->config(), 0);
+ QuicConfigPeer::SetReceivedMaxIncomingBidirectionalStreams(session_->config(),
+ 0);
+ session_->OnConfigNegotiated();
+}
+
+TEST_P(QuicStreamIdManagerTestClient, HoldStreamsBlockedFrameNoXmit) {
+ if (!VersionHasIetfQuicFrames(transport_version())) {
+ return;
+ }
+ // Set outgoing limit to 0, will cause the CanOpenNext... to fail
+ // leading to a STREAMS_BLOCKED.
+ QuicStreamIdManagerPeer::set_outgoing_max_streams(stream_id_manager_, 0);
+
+ // We should not see a STREAMS-BLOCKED frame because we're not configured..
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+
+ // Since the stream limit is 0 and no sreams can be created this should return
+ // false and have forced a streams-blocked to be queued up, with the
+ // blocked stream id == 0.
+ EXPECT_FALSE(stream_id_manager_->CanOpenNextOutgoingStream());
+
+ // Since the config gives some streams to create, we should not see
+ // a STREAMS-BLOCKED frame.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+
+ // Do the configuration. The stream limits are increased, allowing this
+ // node to create more streams, so we should not see the pending
+ // STREAMS-BLOCKED frame get transmitted.
+ QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams(
+ session_->config(), 10);
+ QuicConfigPeer::SetReceivedMaxIncomingBidirectionalStreams(session_->config(),
+ 10);
+ session_->OnConfigNegotiated();
+}
+
INSTANTIATE_TEST_SUITE_P(Tests, QuicStreamIdManagerTestServer, testing::Bool());
// This test checks that the initialization for the maximum allowed outgoing
@@ -677,6 +782,13 @@
// Tast that an attempt to create an outgoing stream does not exceed the limit
// and that it generates an appropriate STREAMS_BLOCKED frame.
TEST_P(QuicStreamIdManagerTestServer, NewStreamDoesNotExceedLimit) {
+ // Configure with some number of streams, and allow frame transmission
+ QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams(
+ session_->config(), 100);
+ QuicConfigPeer::SetReceivedMaxIncomingBidirectionalStreams(session_->config(),
+ 100);
+ session_->OnConfigNegotiated();
+
size_t stream_count = stream_id_manager_->outgoing_max_streams();
EXPECT_NE(0u, stream_count);
TestQuicStream* stream;
diff --git a/quic/core/uber_quic_stream_id_manager.h b/quic/core/uber_quic_stream_id_manager.h
index 61eaf62..4db221a 100644
--- a/quic/core/uber_quic_stream_id_manager.h
+++ b/quic/core/uber_quic_stream_id_manager.h
@@ -83,6 +83,11 @@
QuicStreamCount advertised_max_allowed_incoming_unidirectional_streams()
const;
+ void OnConfigNegotiated() {
+ bidirectional_stream_id_manager_.OnConfigNegotiated();
+ unidirectional_stream_id_manager_.OnConfigNegotiated();
+ }
+
private:
friend class test::QuicSessionPeer;
friend class test::UberQuicStreamIdManagerPeer;
diff --git a/quic/core/uber_quic_stream_id_manager_test.cc b/quic/core/uber_quic_stream_id_manager_test.cc
index 6d4990e..3362139 100644
--- a/quic/core/uber_quic_stream_id_manager_test.cc
+++ b/quic/core/uber_quic_stream_id_manager_test.cc
@@ -313,6 +313,8 @@
}
TEST_P(UberQuicStreamIdManagerTest, OnStreamsBlockedFrame) {
+ // Allow MAX_STREAMS frame transmission
+ QuicSessionPeer::set_is_configured(session_.get(), true);
// Set up to capture calls to SendControlFrame - when a STREAMS_BLOCKED
// frame is received, it will result in a a new MAX_STREAMS frame being
// sent (if new streams can be made available).
diff --git a/quic/test_tools/quic_session_peer.cc b/quic/test_tools/quic_session_peer.cc
index 22ccb28..001f1ca 100644
--- a/quic/test_tools/quic_session_peer.cc
+++ b/quic/test_tools/quic_session_peer.cc
@@ -241,5 +241,10 @@
return it == session->pending_stream_map_.end() ? nullptr : it->second.get();
}
+// static
+void QuicSessionPeer::set_is_configured(QuicSession* session, bool value) {
+ session->is_configured_ = value;
+}
+
} // namespace test
} // namespace quic
diff --git a/quic/test_tools/quic_session_peer.h b/quic/test_tools/quic_session_peer.h
index 79338fc..eed3bdd 100644
--- a/quic/test_tools/quic_session_peer.h
+++ b/quic/test_tools/quic_session_peer.h
@@ -86,6 +86,7 @@
bool close_write_side_only);
static PendingStream* GetPendingStream(QuicSession* session,
QuicStreamId stream_id);
+ static void set_is_configured(QuicSession* session, bool value);
};
} // namespace test
diff --git a/quic/test_tools/quic_stream_id_manager_peer.cc b/quic/test_tools/quic_stream_id_manager_peer.cc
index 3ce5f1f..6c08ece 100644
--- a/quic/test_tools/quic_stream_id_manager_peer.cc
+++ b/quic/test_tools/quic_stream_id_manager_peer.cc
@@ -20,10 +20,23 @@
}
// static
+void QuicStreamIdManagerPeer::set_outgoing_max_streams(
+ QuicStreamIdManager* stream_id_manager,
+ QuicStreamCount count) {
+ stream_id_manager->outgoing_max_streams_ = count;
+}
+
+// static
QuicStreamId QuicStreamIdManagerPeer::GetFirstIncomingStreamId(
QuicStreamIdManager* stream_id_manager) {
return stream_id_manager->GetFirstIncomingStreamId();
}
+// static
+bool QuicStreamIdManagerPeer::get_unidirectional(
+ QuicStreamIdManager* stream_id_manager) {
+ return stream_id_manager->unidirectional_;
+}
+
} // namespace test
} // namespace quic
diff --git a/quic/test_tools/quic_stream_id_manager_peer.h b/quic/test_tools/quic_stream_id_manager_peer.h
index cc78aee..4c97512 100644
--- a/quic/test_tools/quic_stream_id_manager_peer.h
+++ b/quic/test_tools/quic_stream_id_manager_peer.h
@@ -22,9 +22,13 @@
static void set_incoming_actual_max_streams(
QuicStreamIdManager* stream_id_manager,
QuicStreamCount count);
+ static void set_outgoing_max_streams(QuicStreamIdManager* stream_id_manager,
+ QuicStreamCount count);
static QuicStreamId GetFirstIncomingStreamId(
QuicStreamIdManager* stream_id_manager);
+
+ static bool get_unidirectional(QuicStreamIdManager* stream_id_manager);
};
} // namespace test
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 8ba9615..ba5e1cd 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -632,6 +632,12 @@
MOCK_CONST_METHOD0(IsCryptoHandshakeConfirmed, bool());
MOCK_CONST_METHOD0(ShouldKeepConnectionAlive, bool());
MOCK_METHOD2(SendStopSending, void(uint16_t code, QuicStreamId stream_id));
+#ifdef undefined
+ MOCK_METHOD2(SendMaxStreams,
+ void(QuicStreamCount stream_count, bool unidirectional));
+ MOCK_METHOD2(SendStreamsBlocked,
+ void(QuicStreamCount stream_count, bool unidirectional));
+#endif
MOCK_METHOD1(OnCryptoHandshakeEvent, void(QuicSession::CryptoHandshakeEvent));
MOCK_CONST_METHOD0(GetAlpnsToOffer, std::vector<std::string>());
MOCK_CONST_METHOD1(SelectAlpn,