1 // Copyright 2019 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/http/quic_send_control_stream.h"
6
7 #include <string>
8 #include <utility>
9
10 #include "absl/strings/escaping.h"
11 #include "absl/strings/string_view.h"
12 #include "quiche/quic/core/crypto/null_encrypter.h"
13 #include "quiche/quic/platform/api/quic_flags.h"
14 #include "quiche/quic/test_tools/quic_config_peer.h"
15 #include "quiche/quic/test_tools/quic_spdy_session_peer.h"
16 #include "quiche/quic/test_tools/quic_test_utils.h"
17 #include "quiche/common/test_tools/quiche_test_utils.h"
18
19 namespace quic {
20 namespace test {
21
22 namespace {
23
24 using ::testing::_;
25 using ::testing::AnyNumber;
26 using ::testing::Invoke;
27 using ::testing::StrictMock;
28
29 struct TestParams {
TestParamsquic::test::__anon210919c40111::TestParams30 TestParams(const ParsedQuicVersion& version, Perspective perspective)
31 : version(version), perspective(perspective) {
32 QUIC_LOG(INFO) << "TestParams: " << *this;
33 }
34
TestParamsquic::test::__anon210919c40111::TestParams35 TestParams(const TestParams& other)
36 : version(other.version), perspective(other.perspective) {}
37
operator <<(std::ostream & os,const TestParams & tp)38 friend std::ostream& operator<<(std::ostream& os, const TestParams& tp) {
39 os << "{ version: " << ParsedQuicVersionToString(tp.version)
40 << ", perspective: "
41 << (tp.perspective == Perspective::IS_CLIENT ? "client" : "server")
42 << "}";
43 return os;
44 }
45
46 ParsedQuicVersion version;
47 Perspective perspective;
48 };
49
50 // Used by ::testing::PrintToStringParamName().
PrintToString(const TestParams & tp)51 std::string PrintToString(const TestParams& tp) {
52 return absl::StrCat(
53 ParsedQuicVersionToString(tp.version), "_",
54 (tp.perspective == Perspective::IS_CLIENT ? "client" : "server"));
55 }
56
GetTestParams()57 std::vector<TestParams> GetTestParams() {
58 std::vector<TestParams> params;
59 ParsedQuicVersionVector all_supported_versions = AllSupportedVersions();
60 for (const auto& version : AllSupportedVersions()) {
61 if (!VersionUsesHttp3(version.transport_version)) {
62 continue;
63 }
64 for (Perspective p : {Perspective::IS_SERVER, Perspective::IS_CLIENT}) {
65 params.emplace_back(version, p);
66 }
67 }
68 return params;
69 }
70
71 class QuicSendControlStreamTest : public QuicTestWithParam<TestParams> {
72 public:
QuicSendControlStreamTest()73 QuicSendControlStreamTest()
74 : connection_(new StrictMock<MockQuicConnection>(
75 &helper_, &alarm_factory_, perspective(),
76 SupportedVersions(GetParam().version))),
77 session_(connection_) {
78 ON_CALL(session_, WritevData(_, _, _, _, _, _))
79 .WillByDefault(Invoke(&session_, &MockQuicSpdySession::ConsumeData));
80 }
81
Initialize()82 void Initialize() {
83 EXPECT_CALL(session_, OnCongestionWindowChange(_)).Times(AnyNumber());
84 session_.Initialize();
85 connection_->SetEncrypter(
86 ENCRYPTION_FORWARD_SECURE,
87 std::make_unique<NullEncrypter>(connection_->perspective()));
88 send_control_stream_ = QuicSpdySessionPeer::GetSendControlStream(&session_);
89 QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(
90 session_.config(), kMinimumFlowControlSendWindow);
91 QuicConfigPeer::SetReceivedInitialMaxStreamDataBytesUnidirectional(
92 session_.config(), kMinimumFlowControlSendWindow);
93 QuicConfigPeer::SetReceivedMaxUnidirectionalStreams(session_.config(), 3);
94 session_.OnConfigNegotiated();
95 }
96
perspective() const97 Perspective perspective() const { return GetParam().perspective; }
98
99 MockQuicConnectionHelper helper_;
100 MockAlarmFactory alarm_factory_;
101 StrictMock<MockQuicConnection>* connection_;
102 StrictMock<MockQuicSpdySession> session_;
103 QuicSendControlStream* send_control_stream_;
104 };
105
106 INSTANTIATE_TEST_SUITE_P(Tests, QuicSendControlStreamTest,
107 ::testing::ValuesIn(GetTestParams()),
108 ::testing::PrintToStringParamName());
109
TEST_P(QuicSendControlStreamTest,WriteSettings)110 TEST_P(QuicSendControlStreamTest, WriteSettings) {
111 SetQuicFlag(quic_enable_http3_grease_randomness, false);
112 session_.set_qpack_maximum_dynamic_table_capacity(255);
113 session_.set_qpack_maximum_blocked_streams(16);
114 session_.set_max_inbound_header_list_size(1024);
115
116 Initialize();
117 testing::InSequence s;
118
119 std::string expected_write_data;
120 ASSERT_TRUE(
121 absl::HexStringToBytes("00" // stream type: control stream
122 "04" // frame type: SETTINGS frame
123 "0b" // frame length
124 "01" // SETTINGS_QPACK_MAX_TABLE_CAPACITY
125 "40ff" // 255
126 "06" // SETTINGS_MAX_HEADER_LIST_SIZE
127 "4400" // 1024
128 "07" // SETTINGS_QPACK_BLOCKED_STREAMS
129 "10" // 16
130 "4040" // 0x40 as the reserved settings id
131 "14" // 20
132 "4040" // 0x40 as the reserved frame type
133 "01" // 1 byte frame length
134 "61", // payload "a"
135 &expected_write_data));
136 if (perspective() == Perspective::IS_CLIENT &&
137 QuicSpdySessionPeer::LocalHttpDatagramSupport(&session_) !=
138 HttpDatagramSupport::kNone) {
139 ASSERT_TRUE(
140 absl::HexStringToBytes("00" // stream type: control stream
141 "04" // frame type: SETTINGS frame
142 "0d" // frame length
143 "01" // SETTINGS_QPACK_MAX_TABLE_CAPACITY
144 "40ff" // 255
145 "06" // SETTINGS_MAX_HEADER_LIST_SIZE
146 "4400" // 1024
147 "07" // SETTINGS_QPACK_BLOCKED_STREAMS
148 "10" // 16
149 "33" // SETTINGS_H3_DATAGRAM
150 "01" // 1
151 "4040" // 0x40 as the reserved settings id
152 "14" // 20
153 "4040" // 0x40 as the reserved frame type
154 "01" // 1 byte frame length
155 "61", // payload "a"
156 &expected_write_data));
157 }
158 if (perspective() == Perspective::IS_SERVER &&
159 QuicSpdySessionPeer::LocalHttpDatagramSupport(&session_) ==
160 HttpDatagramSupport::kNone) {
161 ASSERT_TRUE(
162 absl::HexStringToBytes("00" // stream type: control stream
163 "04" // frame type: SETTINGS frame
164 "0d" // frame length
165 "01" // SETTINGS_QPACK_MAX_TABLE_CAPACITY
166 "40ff" // 255
167 "06" // SETTINGS_MAX_HEADER_LIST_SIZE
168 "4400" // 1024
169 "07" // SETTINGS_QPACK_BLOCKED_STREAMS
170 "10" // 16
171 "08" // SETTINGS_ENABLE_CONNECT_PROTOCOL
172 "01" // 1
173 "4040" // 0x40 as the reserved settings id
174 "14" // 20
175 "4040" // 0x40 as the reserved frame type
176 "01" // 1 byte frame length
177 "61", // payload "a"
178 &expected_write_data));
179 }
180 if (perspective() == Perspective::IS_SERVER &&
181 QuicSpdySessionPeer::LocalHttpDatagramSupport(&session_) !=
182 HttpDatagramSupport::kNone) {
183 ASSERT_TRUE(
184 absl::HexStringToBytes("00" // stream type: control stream
185 "04" // frame type: SETTINGS frame
186 "0f" // frame length
187 "01" // SETTINGS_QPACK_MAX_TABLE_CAPACITY
188 "40ff" // 255
189 "06" // SETTINGS_MAX_HEADER_LIST_SIZE
190 "4400" // 1024
191 "07" // SETTINGS_QPACK_BLOCKED_STREAMS
192 "10" // 16
193 "08" // SETTINGS_ENABLE_CONNECT_PROTOCOL
194 "01" // 1
195 "33" // SETTINGS_H3_DATAGRAM
196 "01" // 1
197 "4040" // 0x40 as the reserved settings id
198 "14" // 20
199 "4040" // 0x40 as the reserved frame type
200 "01" // 1 byte frame length
201 "61", // payload "a"
202 &expected_write_data));
203 }
204
205 char buffer[1000] = {};
206 QuicDataWriter writer(sizeof(buffer), buffer);
207 ASSERT_GE(sizeof(buffer), expected_write_data.size());
208
209 // A lambda to save and consume stream data when QuicSession::WritevData() is
210 // called.
211 auto save_write_data =
212 [&writer, this](QuicStreamId /*id*/, size_t write_length,
213 QuicStreamOffset offset, StreamSendingState /*state*/,
214 TransmissionType /*type*/,
215 std::optional<EncryptionLevel> /*level*/) {
216 send_control_stream_->WriteStreamData(offset, write_length, &writer);
217 return QuicConsumedData(/* bytes_consumed = */ write_length,
218 /* fin_consumed = */ false);
219 };
220
221 EXPECT_CALL(session_, WritevData(send_control_stream_->id(), _, _, _, _, _))
222 .WillRepeatedly(Invoke(save_write_data));
223
224 send_control_stream_->MaybeSendSettingsFrame();
225 quiche::test::CompareCharArraysWithHexError(
226 "settings", writer.data(), writer.length(), expected_write_data.data(),
227 expected_write_data.length());
228 }
229
TEST_P(QuicSendControlStreamTest,WriteSettingsOnlyOnce)230 TEST_P(QuicSendControlStreamTest, WriteSettingsOnlyOnce) {
231 Initialize();
232 testing::InSequence s;
233
234 EXPECT_CALL(session_, WritevData(send_control_stream_->id(), 1, _, _, _, _));
235 EXPECT_CALL(session_, WritevData(send_control_stream_->id(), _, _, _, _, _))
236 .Times(2);
237 send_control_stream_->MaybeSendSettingsFrame();
238
239 // No data should be written the second time MaybeSendSettingsFrame() is
240 // called.
241 send_control_stream_->MaybeSendSettingsFrame();
242 }
243
244 // Send stream type and SETTINGS frame if WritePriorityUpdate() is called first.
TEST_P(QuicSendControlStreamTest,WritePriorityBeforeSettings)245 TEST_P(QuicSendControlStreamTest, WritePriorityBeforeSettings) {
246 Initialize();
247 testing::InSequence s;
248
249 // The first write will trigger the control stream to write stream type, a
250 // SETTINGS frame, and a greased frame before the PRIORITY_UPDATE frame.
251 EXPECT_CALL(session_, WritevData(send_control_stream_->id(), _, _, _, _, _))
252 .Times(4);
253 send_control_stream_->WritePriorityUpdate(
254 /* stream_id = */ 0,
255 HttpStreamPriority{/* urgency = */ 3, /* incremental = */ false});
256
257 EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(&session_));
258
259 EXPECT_CALL(session_, WritevData(send_control_stream_->id(), _, _, _, _, _));
260 send_control_stream_->WritePriorityUpdate(
261 /* stream_id = */ 0,
262 HttpStreamPriority{/* urgency = */ 3, /* incremental = */ false});
263 }
264
TEST_P(QuicSendControlStreamTest,CloseControlStream)265 TEST_P(QuicSendControlStreamTest, CloseControlStream) {
266 Initialize();
267 EXPECT_CALL(*connection_,
268 CloseConnection(QUIC_HTTP_CLOSED_CRITICAL_STREAM, _, _));
269 send_control_stream_->OnStopSending(
270 QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED));
271 }
272
TEST_P(QuicSendControlStreamTest,ReceiveDataOnSendControlStream)273 TEST_P(QuicSendControlStreamTest, ReceiveDataOnSendControlStream) {
274 Initialize();
275 QuicStreamFrame frame(send_control_stream_->id(), false, 0, "test");
276 EXPECT_CALL(
277 *connection_,
278 CloseConnection(QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM, _, _));
279 send_control_stream_->OnStreamFrame(frame);
280 }
281
TEST_P(QuicSendControlStreamTest,SendGoAway)282 TEST_P(QuicSendControlStreamTest, SendGoAway) {
283 Initialize();
284
285 StrictMock<MockHttp3DebugVisitor> debug_visitor;
286 session_.set_debug_visitor(&debug_visitor);
287
288 QuicStreamId stream_id = 4;
289
290 EXPECT_CALL(session_, WritevData(send_control_stream_->id(), _, _, _, _, _))
291 .Times(AnyNumber());
292 EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_));
293 EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(stream_id));
294
295 send_control_stream_->SendGoAway(stream_id);
296 }
297
298 } // namespace
299 } // namespace test
300 } // namespace quic
301