xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/http/quic_send_control_stream_test.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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