blob: 265756f2b2c5c5f6e1b481e01858a5cbce89854f [file] [log] [blame]
// Copyright (c) 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h"
#include "net/third_party/quiche/src/quic/core/quic_utils.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.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_test_utils.h"
using testing::_;
using testing::StrictMock;
namespace quic {
namespace test {
namespace {
class MockDelegate : public QuicStreamIdManager::DelegateInterface {
public:
MOCK_METHOD1(OnCanCreateNewOutgoingStream, void(bool unidirectional));
MOCK_METHOD2(OnError,
void(QuicErrorCode error_code, std::string error_details));
MOCK_METHOD2(SendMaxStreams,
void(QuicStreamCount stream_count, bool unidirectional));
MOCK_METHOD2(SendStreamsBlocked,
void(QuicStreamCount stream_count, bool unidirectional));
};
class UberQuicStreamIdManagerTest : public QuicTestWithParam<Perspective> {
protected:
UberQuicStreamIdManagerTest()
: manager_(perspective(),
version(),
&delegate_,
/*num_expected_unidirectional_static_streams=*/0,
kDefaultMaxStreamsPerConnection,
kDefaultMaxStreamsPerConnection,
kDefaultMaxStreamsPerConnection,
kDefaultMaxStreamsPerConnection) {}
QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
return QuicUtils::GetFirstBidirectionalStreamId(transport_version(),
Perspective::IS_CLIENT) +
kV99StreamIdIncrement * n;
}
QuicStreamId GetNthClientInitiatedUnidirectionalId(int n) {
return QuicUtils::GetFirstUnidirectionalStreamId(transport_version(),
Perspective::IS_CLIENT) +
kV99StreamIdIncrement * n;
}
QuicStreamId GetNthServerInitiatedBidirectionalId(int n) {
return QuicUtils::GetFirstBidirectionalStreamId(transport_version(),
Perspective::IS_SERVER) +
kV99StreamIdIncrement * n;
}
QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) {
return QuicUtils::GetFirstUnidirectionalStreamId(transport_version(),
Perspective::IS_SERVER) +
kV99StreamIdIncrement * n;
}
// TODO(fkastenholz): Existing tests can use these helper functions.
QuicStreamId GetNthPeerInitiatedBidirectionalStreamId(int n) {
return ((GetParam() == Perspective::IS_SERVER)
? GetNthClientInitiatedBidirectionalId(n)
: GetNthServerInitiatedBidirectionalId(n));
}
QuicStreamId GetNthPeerInitiatedUnidirectionalStreamId(int n) {
return ((GetParam() == Perspective::IS_SERVER)
? GetNthClientInitiatedUnidirectionalId(n)
: GetNthServerInitiatedUnidirectionalId(n));
}
QuicStreamId GetNthSelfInitiatedBidirectionalStreamId(int n) {
return ((GetParam() == Perspective::IS_CLIENT)
? GetNthClientInitiatedBidirectionalId(n)
: GetNthServerInitiatedBidirectionalId(n));
}
QuicStreamId GetNthSelfInitiatedUnidirectionalStreamId(int n) {
return ((GetParam() == Perspective::IS_CLIENT)
? GetNthClientInitiatedUnidirectionalId(n)
: GetNthServerInitiatedUnidirectionalId(n));
}
QuicStreamId StreamCountToId(QuicStreamCount stream_count,
Perspective perspective,
bool bidirectional) {
return ((bidirectional) ? QuicUtils::GetFirstBidirectionalStreamId(
transport_version(), perspective)
: QuicUtils::GetFirstUnidirectionalStreamId(
transport_version(), perspective)) +
((stream_count - 1) * QuicUtils::StreamIdDelta(transport_version()));
}
ParsedQuicVersion version() { return {PROTOCOL_TLS1_3, transport_version()}; }
QuicTransportVersion transport_version() { return QUIC_VERSION_99; }
Perspective perspective() { return GetParam(); }
testing::StrictMock<MockDelegate> delegate_;
UberQuicStreamIdManager manager_;
};
INSTANTIATE_TEST_SUITE_P(Tests,
UberQuicStreamIdManagerTest,
::testing::ValuesIn({Perspective::IS_CLIENT,
Perspective::IS_SERVER}),
::testing::PrintToStringParamName());
TEST_P(UberQuicStreamIdManagerTest, Initialization) {
if (perspective() == Perspective::IS_SERVER) {
EXPECT_EQ(GetNthServerInitiatedBidirectionalId(0),
manager_.next_outgoing_bidirectional_stream_id());
EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(0),
manager_.next_outgoing_unidirectional_stream_id());
} else {
EXPECT_EQ(GetNthClientInitiatedBidirectionalId(0),
manager_.next_outgoing_bidirectional_stream_id());
EXPECT_EQ(GetNthClientInitiatedUnidirectionalId(0),
manager_.next_outgoing_unidirectional_stream_id());
}
}
TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenOutgoingStreams) {
const size_t kNumMaxOutgoingStream = 123;
// Set the uni- and bi- directional limits to different values to ensure
// that they are managed separately.
EXPECT_CALL(delegate_, OnCanCreateNewOutgoingStream(false));
manager_.SetMaxOpenOutgoingBidirectionalStreams(kNumMaxOutgoingStream);
EXPECT_CALL(delegate_, OnCanCreateNewOutgoingStream(true));
manager_.SetMaxOpenOutgoingUnidirectionalStreams(kNumMaxOutgoingStream + 1);
EXPECT_EQ(kNumMaxOutgoingStream,
manager_.max_outgoing_bidirectional_streams());
EXPECT_EQ(kNumMaxOutgoingStream + 1,
manager_.max_outgoing_unidirectional_streams());
// Check that, for each directionality, we can open the correct number of
// streams.
int i = kNumMaxOutgoingStream;
while (i) {
EXPECT_TRUE(manager_.CanOpenNextOutgoingBidirectionalStream());
manager_.GetNextOutgoingBidirectionalStreamId();
EXPECT_TRUE(manager_.CanOpenNextOutgoingUnidirectionalStream());
manager_.GetNextOutgoingUnidirectionalStreamId();
i--;
}
// One more unidirectional
EXPECT_TRUE(manager_.CanOpenNextOutgoingUnidirectionalStream());
manager_.GetNextOutgoingUnidirectionalStreamId();
// Both should be exhausted...
EXPECT_FALSE(manager_.CanOpenNextOutgoingUnidirectionalStream());
EXPECT_FALSE(manager_.CanOpenNextOutgoingBidirectionalStream());
}
TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenIncomingStreams) {
const size_t kNumMaxIncomingStreams = 456;
manager_.SetMaxOpenIncomingUnidirectionalStreams(kNumMaxIncomingStreams);
// Do +1 for bidirectional to ensure that uni- and bi- get properly set.
manager_.SetMaxOpenIncomingBidirectionalStreams(kNumMaxIncomingStreams + 1);
EXPECT_EQ(kNumMaxIncomingStreams + 1,
manager_.GetMaxAllowdIncomingBidirectionalStreams());
EXPECT_EQ(kNumMaxIncomingStreams,
manager_.GetMaxAllowdIncomingUnidirectionalStreams());
EXPECT_EQ(manager_.max_incoming_bidirectional_streams(),
manager_.advertised_max_incoming_bidirectional_streams());
EXPECT_EQ(manager_.max_incoming_unidirectional_streams(),
manager_.advertised_max_incoming_unidirectional_streams());
// Make sure that we can create kNumMaxIncomingStreams incoming unidirectional
// streams and kNumMaxIncomingStreams+1 incoming bidirectional streams.
size_t i;
for (i = 0; i < kNumMaxIncomingStreams; i++) {
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthPeerInitiatedUnidirectionalStreamId(i)));
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthPeerInitiatedBidirectionalStreamId(i)));
}
// Should be able to open the next bidirectional stream
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthPeerInitiatedBidirectionalStreamId(i)));
// We should have exhausted the counts, the next streams should fail
EXPECT_CALL(delegate_, OnError(QUIC_INVALID_STREAM_ID, _));
EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthPeerInitiatedUnidirectionalStreamId(i)));
EXPECT_CALL(delegate_, OnError(QUIC_INVALID_STREAM_ID, _));
EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthPeerInitiatedBidirectionalStreamId(i + 1)));
}
TEST_P(UberQuicStreamIdManagerTest, GetNextOutgoingStreamId) {
if (perspective() == Perspective::IS_SERVER) {
EXPECT_EQ(GetNthServerInitiatedBidirectionalId(0),
manager_.GetNextOutgoingBidirectionalStreamId());
EXPECT_EQ(GetNthServerInitiatedBidirectionalId(1),
manager_.GetNextOutgoingBidirectionalStreamId());
EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(0),
manager_.GetNextOutgoingUnidirectionalStreamId());
EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(1),
manager_.GetNextOutgoingUnidirectionalStreamId());
} else {
EXPECT_EQ(GetNthClientInitiatedBidirectionalId(0),
manager_.GetNextOutgoingBidirectionalStreamId());
EXPECT_EQ(GetNthClientInitiatedBidirectionalId(1),
manager_.GetNextOutgoingBidirectionalStreamId());
EXPECT_EQ(GetNthClientInitiatedUnidirectionalId(0),
manager_.GetNextOutgoingUnidirectionalStreamId());
EXPECT_EQ(GetNthClientInitiatedUnidirectionalId(1),
manager_.GetNextOutgoingUnidirectionalStreamId());
}
}
TEST_P(UberQuicStreamIdManagerTest, AvailableStreams) {
if (perspective() == Perspective::IS_SERVER) {
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthClientInitiatedBidirectionalId(3)));
EXPECT_TRUE(
manager_.IsAvailableStream(GetNthClientInitiatedBidirectionalId(1)));
EXPECT_TRUE(
manager_.IsAvailableStream(GetNthClientInitiatedBidirectionalId(2)));
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthClientInitiatedUnidirectionalId(3)));
EXPECT_TRUE(
manager_.IsAvailableStream(GetNthClientInitiatedUnidirectionalId(1)));
EXPECT_TRUE(
manager_.IsAvailableStream(GetNthClientInitiatedUnidirectionalId(2)));
} else {
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthServerInitiatedBidirectionalId(3)));
EXPECT_TRUE(
manager_.IsAvailableStream(GetNthServerInitiatedBidirectionalId(1)));
EXPECT_TRUE(
manager_.IsAvailableStream(GetNthServerInitiatedBidirectionalId(2)));
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthServerInitiatedUnidirectionalId(3)));
EXPECT_TRUE(
manager_.IsAvailableStream(GetNthServerInitiatedUnidirectionalId(1)));
EXPECT_TRUE(
manager_.IsAvailableStream(GetNthServerInitiatedUnidirectionalId(2)));
}
}
TEST_P(UberQuicStreamIdManagerTest, MaybeIncreaseLargestPeerStreamId) {
EXPECT_CALL(delegate_, OnError(_, _)).Times(0);
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(StreamCountToId(
manager_.max_incoming_bidirectional_streams(),
QuicUtils::InvertPerspective(perspective()), /* bidirectional=*/true)));
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(StreamCountToId(
manager_.max_incoming_bidirectional_streams(),
QuicUtils::InvertPerspective(perspective()), /* bidirectional=*/false)));
std::string error_details =
perspective() == Perspective::IS_SERVER
? "Stream id 400 would exceed stream count limit 100"
: "Stream id 401 would exceed stream count limit 100";
EXPECT_CALL(delegate_, OnError(QUIC_INVALID_STREAM_ID, error_details));
EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId(StreamCountToId(
manager_.max_incoming_bidirectional_streams() + 1,
QuicUtils::InvertPerspective(perspective()), /* bidirectional=*/true)));
error_details = perspective() == Perspective::IS_SERVER
? "Stream id 402 would exceed stream count limit 100"
: "Stream id 403 would exceed stream count limit 100";
EXPECT_CALL(delegate_, OnError(QUIC_INVALID_STREAM_ID, error_details));
EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId(StreamCountToId(
manager_.max_incoming_bidirectional_streams() + 1,
QuicUtils::InvertPerspective(perspective()), /* bidirectional=*/false)));
}
TEST_P(UberQuicStreamIdManagerTest, OnMaxStreamsFrame) {
QuicStreamCount max_outgoing_bidirectional_stream_count =
manager_.max_outgoing_bidirectional_streams();
QuicStreamCount max_outgoing_unidirectional_stream_count =
manager_.max_outgoing_unidirectional_streams();
// Inject a MAX_STREAMS frame that does not increase the limit and then
// check that there are no changes. First try the bidirectional manager.
QuicMaxStreamsFrame frame(kInvalidControlFrameId,
max_outgoing_bidirectional_stream_count,
/*unidirectional=*/false);
EXPECT_CALL(delegate_, OnCanCreateNewOutgoingStream(frame.unidirectional));
EXPECT_TRUE(manager_.OnMaxStreamsFrame(frame));
EXPECT_EQ(max_outgoing_bidirectional_stream_count,
manager_.max_outgoing_bidirectional_streams());
// Now try the unidirectioanl manager
frame.stream_count = max_outgoing_unidirectional_stream_count;
frame.unidirectional = true;
EXPECT_CALL(delegate_, OnCanCreateNewOutgoingStream(frame.unidirectional));
EXPECT_TRUE(manager_.OnMaxStreamsFrame(frame));
EXPECT_EQ(max_outgoing_unidirectional_stream_count,
manager_.max_outgoing_unidirectional_streams());
// Now try to increase the bidirectional stream count.
frame.stream_count = max_outgoing_bidirectional_stream_count + 1;
frame.unidirectional = false;
EXPECT_CALL(delegate_, OnCanCreateNewOutgoingStream(frame.unidirectional));
EXPECT_TRUE(manager_.OnMaxStreamsFrame(frame));
EXPECT_EQ(max_outgoing_bidirectional_stream_count + 1,
manager_.max_outgoing_bidirectional_streams());
// Make sure that the unidirectional state does not change.
EXPECT_EQ(max_outgoing_unidirectional_stream_count,
manager_.max_outgoing_unidirectional_streams());
// Now check that a MAX_STREAMS for the unidirectional manager increases
// just the unidirectiomal manager's state.
frame.stream_count = max_outgoing_unidirectional_stream_count + 1;
frame.unidirectional = true;
EXPECT_CALL(delegate_, OnCanCreateNewOutgoingStream(frame.unidirectional));
EXPECT_TRUE(manager_.OnMaxStreamsFrame(frame));
EXPECT_EQ(max_outgoing_bidirectional_stream_count + 1,
manager_.max_outgoing_bidirectional_streams());
EXPECT_EQ(max_outgoing_unidirectional_stream_count + 1,
manager_.max_outgoing_unidirectional_streams());
}
TEST_P(UberQuicStreamIdManagerTest, OnStreamsBlockedFrame) {
// Allow MAX_STREAMS frame transmission
manager_.OnConfigNegotiated();
QuicStreamCount stream_count =
manager_.advertised_max_incoming_bidirectional_streams() - 1;
QuicStreamsBlockedFrame frame(kInvalidControlFrameId, stream_count,
/*unidirectional=*/false);
EXPECT_CALL(delegate_,
SendMaxStreams(manager_.max_incoming_bidirectional_streams(),
frame.unidirectional));
manager_.OnStreamsBlockedFrame(frame);
stream_count = manager_.advertised_max_incoming_unidirectional_streams() - 1;
frame.stream_count = stream_count;
frame.unidirectional = true;
EXPECT_CALL(delegate_,
SendMaxStreams(manager_.max_incoming_unidirectional_streams(),
frame.unidirectional));
manager_.OnStreamsBlockedFrame(frame);
}
TEST_P(UberQuicStreamIdManagerTest, IsIncomingStream) {
if (perspective() == Perspective::IS_SERVER) {
EXPECT_TRUE(
manager_.IsIncomingStream(GetNthClientInitiatedBidirectionalId(0)));
EXPECT_TRUE(
manager_.IsIncomingStream(GetNthClientInitiatedUnidirectionalId(0)));
EXPECT_FALSE(
manager_.IsIncomingStream(GetNthServerInitiatedBidirectionalId(0)));
EXPECT_FALSE(
manager_.IsIncomingStream(GetNthServerInitiatedUnidirectionalId(0)));
} else {
EXPECT_FALSE(
manager_.IsIncomingStream(GetNthClientInitiatedBidirectionalId(0)));
EXPECT_FALSE(
manager_.IsIncomingStream(GetNthClientInitiatedUnidirectionalId(0)));
EXPECT_TRUE(
manager_.IsIncomingStream(GetNthServerInitiatedBidirectionalId(0)));
EXPECT_TRUE(
manager_.IsIncomingStream(GetNthServerInitiatedUnidirectionalId(0)));
}
}
TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenOutgoingStreamsPlusFrame) {
const size_t kNumMaxOutgoingStream = 123;
// Set the uni- and bi- directional limits to different values to ensure
// that they are managed separately.
EXPECT_CALL(delegate_, OnCanCreateNewOutgoingStream(false));
manager_.SetMaxOpenOutgoingBidirectionalStreams(kNumMaxOutgoingStream);
EXPECT_CALL(delegate_, OnCanCreateNewOutgoingStream(true));
manager_.SetMaxOpenOutgoingUnidirectionalStreams(kNumMaxOutgoingStream + 1);
EXPECT_EQ(kNumMaxOutgoingStream,
manager_.max_outgoing_bidirectional_streams());
EXPECT_EQ(kNumMaxOutgoingStream + 1,
manager_.max_outgoing_unidirectional_streams());
// Check that, for each directionality, we can open the correct number of
// streams.
int i = kNumMaxOutgoingStream;
while (i) {
EXPECT_TRUE(manager_.CanOpenNextOutgoingBidirectionalStream());
manager_.GetNextOutgoingBidirectionalStreamId();
EXPECT_TRUE(manager_.CanOpenNextOutgoingUnidirectionalStream());
manager_.GetNextOutgoingUnidirectionalStreamId();
i--;
}
// One more unidirectional
EXPECT_TRUE(manager_.CanOpenNextOutgoingUnidirectionalStream());
manager_.GetNextOutgoingUnidirectionalStreamId();
// Both should be exhausted...
EXPECT_FALSE(manager_.CanOpenNextOutgoingUnidirectionalStream());
EXPECT_FALSE(manager_.CanOpenNextOutgoingBidirectionalStream());
// Now cons a MAX STREAMS frame for unidirectional streams to raise
// the limit.
QuicMaxStreamsFrame frame(1, kNumMaxOutgoingStream + 10,
/*unidirectional=*/true);
EXPECT_CALL(delegate_, OnCanCreateNewOutgoingStream(frame.unidirectional));
manager_.OnMaxStreamsFrame(frame);
// We now should be able to get another uni- stream, but not a bi.
EXPECT_TRUE(manager_.CanOpenNextOutgoingUnidirectionalStream());
EXPECT_FALSE(manager_.CanOpenNextOutgoingBidirectionalStream());
}
} // namespace
} // namespace test
} // namespace quic