| // 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 "quic/core/uber_quic_stream_id_manager.h" |
| |
| #include "quic/core/quic_utils.h" |
| #include "quic/core/quic_versions.h" |
| #include "quic/platform/api/quic_test.h" |
| #include "quic/test_tools/quic_stream_id_manager_peer.h" |
| #include "quic/test_tools/quic_test_utils.h" |
| |
| using testing::_; |
| using testing::StrictMock; |
| |
| namespace quic { |
| namespace test { |
| namespace { |
| |
| struct TestParams { |
| explicit TestParams(ParsedQuicVersion version, Perspective perspective) |
| : version(version), perspective(perspective) {} |
| |
| ParsedQuicVersion version; |
| Perspective perspective; |
| }; |
| |
| // Used by ::testing::PrintToStringParamName(). |
| std::string PrintToString(const TestParams& p) { |
| return absl::StrCat( |
| ParsedQuicVersionToString(p.version), "_", |
| (p.perspective == Perspective::IS_CLIENT ? "client" : "server")); |
| } |
| |
| std::vector<TestParams> GetTestParams() { |
| std::vector<TestParams> params; |
| for (const ParsedQuicVersion& version : AllSupportedVersions()) { |
| if (!version.HasIetfQuicFrames()) { |
| continue; |
| } |
| params.push_back(TestParams(version, Perspective::IS_CLIENT)); |
| params.push_back(TestParams(version, Perspective::IS_SERVER)); |
| } |
| return params; |
| } |
| |
| class MockDelegate : public QuicStreamIdManager::DelegateInterface { |
| public: |
| MOCK_METHOD(void, |
| SendMaxStreams, |
| (QuicStreamCount stream_count, bool unidirectional), |
| (override)); |
| }; |
| |
| class UberQuicStreamIdManagerTest : public QuicTestWithParam<TestParams> { |
| protected: |
| UberQuicStreamIdManagerTest() |
| : manager_(perspective(), |
| version(), |
| &delegate_, |
| 0, |
| 0, |
| kDefaultMaxStreamsPerConnection, |
| kDefaultMaxStreamsPerConnection) {} |
| |
| QuicStreamId GetNthClientInitiatedBidirectionalId(int n) { |
| return QuicUtils::GetFirstBidirectionalStreamId(transport_version(), |
| Perspective::IS_CLIENT) + |
| QuicUtils::StreamIdDelta(transport_version()) * n; |
| } |
| |
| QuicStreamId GetNthClientInitiatedUnidirectionalId(int n) { |
| return QuicUtils::GetFirstUnidirectionalStreamId(transport_version(), |
| Perspective::IS_CLIENT) + |
| QuicUtils::StreamIdDelta(transport_version()) * n; |
| } |
| |
| QuicStreamId GetNthServerInitiatedBidirectionalId(int n) { |
| return QuicUtils::GetFirstBidirectionalStreamId(transport_version(), |
| Perspective::IS_SERVER) + |
| QuicUtils::StreamIdDelta(transport_version()) * n; |
| } |
| |
| QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) { |
| return QuicUtils::GetFirstUnidirectionalStreamId(transport_version(), |
| Perspective::IS_SERVER) + |
| QuicUtils::StreamIdDelta(transport_version()) * n; |
| } |
| |
| QuicStreamId GetNthPeerInitiatedBidirectionalStreamId(int n) { |
| return ((perspective() == Perspective::IS_SERVER) |
| ? GetNthClientInitiatedBidirectionalId(n) |
| : GetNthServerInitiatedBidirectionalId(n)); |
| } |
| QuicStreamId GetNthPeerInitiatedUnidirectionalStreamId(int n) { |
| return ((perspective() == Perspective::IS_SERVER) |
| ? GetNthClientInitiatedUnidirectionalId(n) |
| : GetNthServerInitiatedUnidirectionalId(n)); |
| } |
| QuicStreamId GetNthSelfInitiatedBidirectionalStreamId(int n) { |
| return ((perspective() == Perspective::IS_CLIENT) |
| ? GetNthClientInitiatedBidirectionalId(n) |
| : GetNthServerInitiatedBidirectionalId(n)); |
| } |
| QuicStreamId GetNthSelfInitiatedUnidirectionalStreamId(int n) { |
| return ((perspective() == 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 GetParam().version; } |
| QuicTransportVersion transport_version() { |
| return version().transport_version; |
| } |
| |
| Perspective perspective() { return GetParam().perspective; } |
| |
| testing::StrictMock<MockDelegate> delegate_; |
| UberQuicStreamIdManager manager_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(Tests, |
| UberQuicStreamIdManagerTest, |
| ::testing::ValuesIn(GetTestParams()), |
| ::testing::PrintToStringParamName()); |
| |
| TEST_P(UberQuicStreamIdManagerTest, Initialization) { |
| EXPECT_EQ(GetNthSelfInitiatedBidirectionalStreamId(0), |
| manager_.next_outgoing_bidirectional_stream_id()); |
| EXPECT_EQ(GetNthSelfInitiatedUnidirectionalStreamId(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_TRUE(manager_.MaybeAllowNewOutgoingBidirectionalStreams( |
| kNumMaxOutgoingStream)); |
| EXPECT_TRUE(manager_.MaybeAllowNewOutgoingUnidirectionalStreams( |
| 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), nullptr)); |
| EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId( |
| GetNthPeerInitiatedBidirectionalStreamId(i), nullptr)); |
| } |
| // Should be able to open the next bidirectional stream |
| EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId( |
| GetNthPeerInitiatedBidirectionalStreamId(i), nullptr)); |
| |
| // We should have exhausted the counts, the next streams should fail |
| std::string error_details; |
| EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId( |
| GetNthPeerInitiatedUnidirectionalStreamId(i), &error_details)); |
| EXPECT_EQ(error_details, |
| absl::StrCat( |
| "Stream id ", GetNthPeerInitiatedUnidirectionalStreamId(i), |
| " would exceed stream count limit ", kNumMaxIncomingStreams)); |
| EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId( |
| GetNthPeerInitiatedBidirectionalStreamId(i + 1), &error_details)); |
| EXPECT_EQ(error_details, |
| absl::StrCat("Stream id ", |
| GetNthPeerInitiatedBidirectionalStreamId(i + 1), |
| " would exceed stream count limit ", |
| kNumMaxIncomingStreams + 1)); |
| } |
| |
| TEST_P(UberQuicStreamIdManagerTest, GetNextOutgoingStreamId) { |
| EXPECT_TRUE(manager_.MaybeAllowNewOutgoingBidirectionalStreams(10)); |
| EXPECT_TRUE(manager_.MaybeAllowNewOutgoingUnidirectionalStreams(10)); |
| EXPECT_EQ(GetNthSelfInitiatedBidirectionalStreamId(0), |
| manager_.GetNextOutgoingBidirectionalStreamId()); |
| EXPECT_EQ(GetNthSelfInitiatedBidirectionalStreamId(1), |
| manager_.GetNextOutgoingBidirectionalStreamId()); |
| EXPECT_EQ(GetNthSelfInitiatedUnidirectionalStreamId(0), |
| manager_.GetNextOutgoingUnidirectionalStreamId()); |
| EXPECT_EQ(GetNthSelfInitiatedUnidirectionalStreamId(1), |
| manager_.GetNextOutgoingUnidirectionalStreamId()); |
| } |
| |
| TEST_P(UberQuicStreamIdManagerTest, AvailableStreams) { |
| EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId( |
| GetNthPeerInitiatedBidirectionalStreamId(3), nullptr)); |
| EXPECT_TRUE( |
| manager_.IsAvailableStream(GetNthPeerInitiatedBidirectionalStreamId(1))); |
| EXPECT_TRUE( |
| manager_.IsAvailableStream(GetNthPeerInitiatedBidirectionalStreamId(2))); |
| |
| EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId( |
| GetNthPeerInitiatedUnidirectionalStreamId(3), nullptr)); |
| EXPECT_TRUE( |
| manager_.IsAvailableStream(GetNthPeerInitiatedUnidirectionalStreamId(1))); |
| EXPECT_TRUE( |
| manager_.IsAvailableStream(GetNthPeerInitiatedUnidirectionalStreamId(2))); |
| } |
| |
| TEST_P(UberQuicStreamIdManagerTest, MaybeIncreaseLargestPeerStreamId) { |
| EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId( |
| StreamCountToId(manager_.max_incoming_bidirectional_streams(), |
| QuicUtils::InvertPerspective(perspective()), |
| /* bidirectional=*/true), |
| nullptr)); |
| EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId( |
| StreamCountToId(manager_.max_incoming_bidirectional_streams(), |
| QuicUtils::InvertPerspective(perspective()), |
| /* bidirectional=*/false), |
| nullptr)); |
| |
| std::string expected_error_details = |
| perspective() == Perspective::IS_SERVER |
| ? "Stream id 400 would exceed stream count limit 100" |
| : "Stream id 401 would exceed stream count limit 100"; |
| std::string error_details; |
| |
| EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId( |
| StreamCountToId(manager_.max_incoming_bidirectional_streams() + 1, |
| QuicUtils::InvertPerspective(perspective()), |
| /* bidirectional=*/true), |
| &error_details)); |
| EXPECT_EQ(expected_error_details, error_details); |
| expected_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_FALSE(manager_.MaybeIncreaseLargestPeerStreamId( |
| StreamCountToId(manager_.max_incoming_bidirectional_streams() + 1, |
| QuicUtils::InvertPerspective(perspective()), |
| /* bidirectional=*/false), |
| &error_details)); |
| EXPECT_EQ(expected_error_details, error_details); |
| } |
| |
| TEST_P(UberQuicStreamIdManagerTest, OnStreamsBlockedFrame) { |
| 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)) |
| .Times(0); |
| EXPECT_TRUE(manager_.OnStreamsBlockedFrame(frame, nullptr)); |
| |
| 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)) |
| .Times(0); |
| EXPECT_TRUE(manager_.OnStreamsBlockedFrame(frame, nullptr)); |
| } |
| |
| 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_TRUE(manager_.MaybeAllowNewOutgoingBidirectionalStreams( |
| kNumMaxOutgoingStream)); |
| EXPECT_TRUE(manager_.MaybeAllowNewOutgoingUnidirectionalStreams( |
| 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()); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace quic |