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