• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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