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