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