1 // Copyright (c) 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/quic/core/uber_quic_stream_id_manager.h"
6 
7 #include "quiche/quic/core/quic_utils.h"
8 #include "quiche/quic/core/quic_versions.h"
9 #include "quiche/quic/platform/api/quic_test.h"
10 #include "quiche/quic/test_tools/quic_stream_id_manager_peer.h"
11 #include "quiche/quic/test_tools/quic_test_utils.h"
12 
13 using testing::_;
14 using testing::StrictMock;
15 
16 namespace quic {
17 namespace test {
18 namespace {
19 
20 struct TestParams {
TestParamsquic::test::__anonc70af87f0111::TestParams21   explicit TestParams(ParsedQuicVersion version, Perspective perspective)
22       : version(version), perspective(perspective) {}
23 
24   ParsedQuicVersion version;
25   Perspective perspective;
26 };
27 
28 // Used by ::testing::PrintToStringParamName().
PrintToString(const TestParams & p)29 std::string PrintToString(const TestParams& p) {
30   return absl::StrCat(
31       ParsedQuicVersionToString(p.version), "_",
32       (p.perspective == Perspective::IS_CLIENT ? "client" : "server"));
33 }
34 
GetTestParams()35 std::vector<TestParams> GetTestParams() {
36   std::vector<TestParams> params;
37   for (const ParsedQuicVersion& version : AllSupportedVersions()) {
38     if (!version.HasIetfQuicFrames()) {
39       continue;
40     }
41     params.push_back(TestParams(version, Perspective::IS_CLIENT));
42     params.push_back(TestParams(version, Perspective::IS_SERVER));
43   }
44   return params;
45 }
46 
47 class MockDelegate : public QuicStreamIdManager::DelegateInterface {
48  public:
49   MOCK_METHOD(bool, CanSendMaxStreams, (), (override));
50   MOCK_METHOD(void, SendMaxStreams,
51               (QuicStreamCount stream_count, bool unidirectional), (override));
52 };
53 
54 class UberQuicStreamIdManagerTest : public QuicTestWithParam<TestParams> {
55  protected:
UberQuicStreamIdManagerTest()56   UberQuicStreamIdManagerTest()
57       : manager_(perspective(), version(), &delegate_, 0, 0,
58                  kDefaultMaxStreamsPerConnection,
59                  kDefaultMaxStreamsPerConnection) {}
60 
GetNthClientInitiatedBidirectionalId(int n)61   QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
62     return QuicUtils::GetFirstBidirectionalStreamId(transport_version(),
63                                                     Perspective::IS_CLIENT) +
64            QuicUtils::StreamIdDelta(transport_version()) * n;
65   }
66 
GetNthClientInitiatedUnidirectionalId(int n)67   QuicStreamId GetNthClientInitiatedUnidirectionalId(int n) {
68     return QuicUtils::GetFirstUnidirectionalStreamId(transport_version(),
69                                                      Perspective::IS_CLIENT) +
70            QuicUtils::StreamIdDelta(transport_version()) * n;
71   }
72 
GetNthServerInitiatedBidirectionalId(int n)73   QuicStreamId GetNthServerInitiatedBidirectionalId(int n) {
74     return QuicUtils::GetFirstBidirectionalStreamId(transport_version(),
75                                                     Perspective::IS_SERVER) +
76            QuicUtils::StreamIdDelta(transport_version()) * n;
77   }
78 
GetNthServerInitiatedUnidirectionalId(int n)79   QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) {
80     return QuicUtils::GetFirstUnidirectionalStreamId(transport_version(),
81                                                      Perspective::IS_SERVER) +
82            QuicUtils::StreamIdDelta(transport_version()) * n;
83   }
84 
GetNthPeerInitiatedBidirectionalStreamId(int n)85   QuicStreamId GetNthPeerInitiatedBidirectionalStreamId(int n) {
86     return ((perspective() == Perspective::IS_SERVER)
87                 ? GetNthClientInitiatedBidirectionalId(n)
88                 : GetNthServerInitiatedBidirectionalId(n));
89   }
GetNthPeerInitiatedUnidirectionalStreamId(int n)90   QuicStreamId GetNthPeerInitiatedUnidirectionalStreamId(int n) {
91     return ((perspective() == Perspective::IS_SERVER)
92                 ? GetNthClientInitiatedUnidirectionalId(n)
93                 : GetNthServerInitiatedUnidirectionalId(n));
94   }
GetNthSelfInitiatedBidirectionalStreamId(int n)95   QuicStreamId GetNthSelfInitiatedBidirectionalStreamId(int n) {
96     return ((perspective() == Perspective::IS_CLIENT)
97                 ? GetNthClientInitiatedBidirectionalId(n)
98                 : GetNthServerInitiatedBidirectionalId(n));
99   }
GetNthSelfInitiatedUnidirectionalStreamId(int n)100   QuicStreamId GetNthSelfInitiatedUnidirectionalStreamId(int n) {
101     return ((perspective() == Perspective::IS_CLIENT)
102                 ? GetNthClientInitiatedUnidirectionalId(n)
103                 : GetNthServerInitiatedUnidirectionalId(n));
104   }
105 
StreamCountToId(QuicStreamCount stream_count,Perspective perspective,bool bidirectional)106   QuicStreamId StreamCountToId(QuicStreamCount stream_count,
107                                Perspective perspective, bool bidirectional) {
108     return ((bidirectional) ? QuicUtils::GetFirstBidirectionalStreamId(
109                                   transport_version(), perspective)
110                             : QuicUtils::GetFirstUnidirectionalStreamId(
111                                   transport_version(), perspective)) +
112            ((stream_count - 1) * QuicUtils::StreamIdDelta(transport_version()));
113   }
114 
version()115   ParsedQuicVersion version() { return GetParam().version; }
transport_version()116   QuicTransportVersion transport_version() {
117     return version().transport_version;
118   }
119 
perspective()120   Perspective perspective() { return GetParam().perspective; }
121 
122   testing::StrictMock<MockDelegate> delegate_;
123   UberQuicStreamIdManager manager_;
124 };
125 
126 INSTANTIATE_TEST_SUITE_P(Tests, UberQuicStreamIdManagerTest,
127                          ::testing::ValuesIn(GetTestParams()),
128                          ::testing::PrintToStringParamName());
129 
TEST_P(UberQuicStreamIdManagerTest,Initialization)130 TEST_P(UberQuicStreamIdManagerTest, Initialization) {
131   EXPECT_EQ(GetNthSelfInitiatedBidirectionalStreamId(0),
132             manager_.next_outgoing_bidirectional_stream_id());
133   EXPECT_EQ(GetNthSelfInitiatedUnidirectionalStreamId(0),
134             manager_.next_outgoing_unidirectional_stream_id());
135 }
136 
TEST_P(UberQuicStreamIdManagerTest,SetMaxOpenOutgoingStreams)137 TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenOutgoingStreams) {
138   const size_t kNumMaxOutgoingStream = 123;
139   // Set the uni- and bi- directional limits to different values to ensure
140   // that they are managed separately.
141   EXPECT_TRUE(manager_.MaybeAllowNewOutgoingBidirectionalStreams(
142       kNumMaxOutgoingStream));
143   EXPECT_TRUE(manager_.MaybeAllowNewOutgoingUnidirectionalStreams(
144       kNumMaxOutgoingStream + 1));
145   EXPECT_EQ(kNumMaxOutgoingStream,
146             manager_.max_outgoing_bidirectional_streams());
147   EXPECT_EQ(kNumMaxOutgoingStream + 1,
148             manager_.max_outgoing_unidirectional_streams());
149   // Check that, for each directionality, we can open the correct number of
150   // streams.
151   int i = kNumMaxOutgoingStream;
152   while (i) {
153     EXPECT_TRUE(manager_.CanOpenNextOutgoingBidirectionalStream());
154     manager_.GetNextOutgoingBidirectionalStreamId();
155     EXPECT_TRUE(manager_.CanOpenNextOutgoingUnidirectionalStream());
156     manager_.GetNextOutgoingUnidirectionalStreamId();
157     i--;
158   }
159   // One more unidirectional
160   EXPECT_TRUE(manager_.CanOpenNextOutgoingUnidirectionalStream());
161   manager_.GetNextOutgoingUnidirectionalStreamId();
162 
163   // Both should be exhausted...
164   EXPECT_FALSE(manager_.CanOpenNextOutgoingUnidirectionalStream());
165   EXPECT_FALSE(manager_.CanOpenNextOutgoingBidirectionalStream());
166 }
167 
TEST_P(UberQuicStreamIdManagerTest,SetMaxOpenIncomingStreams)168 TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenIncomingStreams) {
169   const size_t kNumMaxIncomingStreams = 456;
170   manager_.SetMaxOpenIncomingUnidirectionalStreams(kNumMaxIncomingStreams);
171   // Do +1 for bidirectional to ensure that uni- and bi- get properly set.
172   manager_.SetMaxOpenIncomingBidirectionalStreams(kNumMaxIncomingStreams + 1);
173   EXPECT_EQ(kNumMaxIncomingStreams + 1,
174             manager_.GetMaxAllowdIncomingBidirectionalStreams());
175   EXPECT_EQ(kNumMaxIncomingStreams,
176             manager_.GetMaxAllowdIncomingUnidirectionalStreams());
177   EXPECT_EQ(manager_.max_incoming_bidirectional_streams(),
178             manager_.advertised_max_incoming_bidirectional_streams());
179   EXPECT_EQ(manager_.max_incoming_unidirectional_streams(),
180             manager_.advertised_max_incoming_unidirectional_streams());
181   // Make sure that we can create kNumMaxIncomingStreams incoming unidirectional
182   // streams and kNumMaxIncomingStreams+1 incoming bidirectional streams.
183   size_t i;
184   for (i = 0; i < kNumMaxIncomingStreams; i++) {
185     EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
186         GetNthPeerInitiatedUnidirectionalStreamId(i), nullptr));
187     EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
188         GetNthPeerInitiatedBidirectionalStreamId(i), nullptr));
189   }
190   // Should be able to open the next bidirectional stream
191   EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
192       GetNthPeerInitiatedBidirectionalStreamId(i), nullptr));
193 
194   // We should have exhausted the counts, the next streams should fail
195   std::string error_details;
196   EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId(
197       GetNthPeerInitiatedUnidirectionalStreamId(i), &error_details));
198   EXPECT_EQ(error_details,
199             absl::StrCat(
200                 "Stream id ", GetNthPeerInitiatedUnidirectionalStreamId(i),
201                 " would exceed stream count limit ", kNumMaxIncomingStreams));
202   EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId(
203       GetNthPeerInitiatedBidirectionalStreamId(i + 1), &error_details));
204   EXPECT_EQ(error_details,
205             absl::StrCat("Stream id ",
206                          GetNthPeerInitiatedBidirectionalStreamId(i + 1),
207                          " would exceed stream count limit ",
208                          kNumMaxIncomingStreams + 1));
209 }
210 
TEST_P(UberQuicStreamIdManagerTest,GetNextOutgoingStreamId)211 TEST_P(UberQuicStreamIdManagerTest, GetNextOutgoingStreamId) {
212   EXPECT_TRUE(manager_.MaybeAllowNewOutgoingBidirectionalStreams(10));
213   EXPECT_TRUE(manager_.MaybeAllowNewOutgoingUnidirectionalStreams(10));
214   EXPECT_EQ(GetNthSelfInitiatedBidirectionalStreamId(0),
215             manager_.GetNextOutgoingBidirectionalStreamId());
216   EXPECT_EQ(GetNthSelfInitiatedBidirectionalStreamId(1),
217             manager_.GetNextOutgoingBidirectionalStreamId());
218   EXPECT_EQ(GetNthSelfInitiatedUnidirectionalStreamId(0),
219             manager_.GetNextOutgoingUnidirectionalStreamId());
220   EXPECT_EQ(GetNthSelfInitiatedUnidirectionalStreamId(1),
221             manager_.GetNextOutgoingUnidirectionalStreamId());
222 }
223 
TEST_P(UberQuicStreamIdManagerTest,AvailableStreams)224 TEST_P(UberQuicStreamIdManagerTest, AvailableStreams) {
225   EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
226       GetNthPeerInitiatedBidirectionalStreamId(3), nullptr));
227   EXPECT_TRUE(
228       manager_.IsAvailableStream(GetNthPeerInitiatedBidirectionalStreamId(1)));
229   EXPECT_TRUE(
230       manager_.IsAvailableStream(GetNthPeerInitiatedBidirectionalStreamId(2)));
231 
232   EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
233       GetNthPeerInitiatedUnidirectionalStreamId(3), nullptr));
234   EXPECT_TRUE(
235       manager_.IsAvailableStream(GetNthPeerInitiatedUnidirectionalStreamId(1)));
236   EXPECT_TRUE(
237       manager_.IsAvailableStream(GetNthPeerInitiatedUnidirectionalStreamId(2)));
238 }
239 
TEST_P(UberQuicStreamIdManagerTest,MaybeIncreaseLargestPeerStreamId)240 TEST_P(UberQuicStreamIdManagerTest, MaybeIncreaseLargestPeerStreamId) {
241   EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
242       StreamCountToId(manager_.max_incoming_bidirectional_streams(),
243                       QuicUtils::InvertPerspective(perspective()),
244                       /* bidirectional=*/true),
245       nullptr));
246   EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
247       StreamCountToId(manager_.max_incoming_bidirectional_streams(),
248                       QuicUtils::InvertPerspective(perspective()),
249                       /* bidirectional=*/false),
250       nullptr));
251 
252   std::string expected_error_details =
253       perspective() == Perspective::IS_SERVER
254           ? "Stream id 400 would exceed stream count limit 100"
255           : "Stream id 401 would exceed stream count limit 100";
256   std::string error_details;
257 
258   EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId(
259       StreamCountToId(manager_.max_incoming_bidirectional_streams() + 1,
260                       QuicUtils::InvertPerspective(perspective()),
261                       /* bidirectional=*/true),
262       &error_details));
263   EXPECT_EQ(expected_error_details, error_details);
264   expected_error_details =
265       perspective() == Perspective::IS_SERVER
266           ? "Stream id 402 would exceed stream count limit 100"
267           : "Stream id 403 would exceed stream count limit 100";
268 
269   EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId(
270       StreamCountToId(manager_.max_incoming_bidirectional_streams() + 1,
271                       QuicUtils::InvertPerspective(perspective()),
272                       /* bidirectional=*/false),
273       &error_details));
274   EXPECT_EQ(expected_error_details, error_details);
275 }
276 
TEST_P(UberQuicStreamIdManagerTest,OnStreamsBlockedFrame)277 TEST_P(UberQuicStreamIdManagerTest, OnStreamsBlockedFrame) {
278   QuicStreamCount stream_count =
279       manager_.advertised_max_incoming_bidirectional_streams() - 1;
280 
281   QuicStreamsBlockedFrame frame(kInvalidControlFrameId, stream_count,
282                                 /*unidirectional=*/false);
283   EXPECT_CALL(delegate_,
284               SendMaxStreams(manager_.max_incoming_bidirectional_streams(),
285                              frame.unidirectional))
286       .Times(0);
287   EXPECT_TRUE(manager_.OnStreamsBlockedFrame(frame, nullptr));
288 
289   stream_count = manager_.advertised_max_incoming_unidirectional_streams() - 1;
290   frame.stream_count = stream_count;
291   frame.unidirectional = true;
292 
293   EXPECT_CALL(delegate_,
294               SendMaxStreams(manager_.max_incoming_unidirectional_streams(),
295                              frame.unidirectional))
296       .Times(0);
297   EXPECT_TRUE(manager_.OnStreamsBlockedFrame(frame, nullptr));
298 }
299 
TEST_P(UberQuicStreamIdManagerTest,SetMaxOpenOutgoingStreamsPlusFrame)300 TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenOutgoingStreamsPlusFrame) {
301   const size_t kNumMaxOutgoingStream = 123;
302   // Set the uni- and bi- directional limits to different values to ensure
303   // that they are managed separately.
304   EXPECT_TRUE(manager_.MaybeAllowNewOutgoingBidirectionalStreams(
305       kNumMaxOutgoingStream));
306   EXPECT_TRUE(manager_.MaybeAllowNewOutgoingUnidirectionalStreams(
307       kNumMaxOutgoingStream + 1));
308   EXPECT_EQ(kNumMaxOutgoingStream,
309             manager_.max_outgoing_bidirectional_streams());
310   EXPECT_EQ(kNumMaxOutgoingStream + 1,
311             manager_.max_outgoing_unidirectional_streams());
312   // Check that, for each directionality, we can open the correct number of
313   // streams.
314   int i = kNumMaxOutgoingStream;
315   while (i) {
316     EXPECT_TRUE(manager_.CanOpenNextOutgoingBidirectionalStream());
317     manager_.GetNextOutgoingBidirectionalStreamId();
318     EXPECT_TRUE(manager_.CanOpenNextOutgoingUnidirectionalStream());
319     manager_.GetNextOutgoingUnidirectionalStreamId();
320     i--;
321   }
322   // One more unidirectional
323   EXPECT_TRUE(manager_.CanOpenNextOutgoingUnidirectionalStream());
324   manager_.GetNextOutgoingUnidirectionalStreamId();
325 
326   // Both should be exhausted...
327   EXPECT_FALSE(manager_.CanOpenNextOutgoingUnidirectionalStream());
328   EXPECT_FALSE(manager_.CanOpenNextOutgoingBidirectionalStream());
329 }
330 
331 }  // namespace
332 }  // namespace test
333 }  // namespace quic
334