xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/http/quic_spdy_client_stream_test.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2013 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_spdy_client_stream.h"
6 
7 #include <memory>
8 #include <string>
9 #include <utility>
10 
11 #include "absl/strings/str_cat.h"
12 #include "quiche/quic/core/crypto/null_encrypter.h"
13 #include "quiche/quic/core/http/quic_spdy_client_session.h"
14 #include "quiche/quic/core/http/spdy_utils.h"
15 #include "quiche/quic/core/quic_error_codes.h"
16 #include "quiche/quic/core/quic_utils.h"
17 #include "quiche/quic/platform/api/quic_logging.h"
18 #include "quiche/quic/platform/api/quic_socket_address.h"
19 #include "quiche/quic/platform/api/quic_test.h"
20 #include "quiche/quic/test_tools/crypto_test_utils.h"
21 #include "quiche/quic/test_tools/quic_spdy_session_peer.h"
22 #include "quiche/quic/test_tools/quic_test_utils.h"
23 #include "quiche/common/simple_buffer_allocator.h"
24 
25 using spdy::Http2HeaderBlock;
26 using testing::_;
27 using testing::StrictMock;
28 
29 namespace quic {
30 namespace test {
31 
32 namespace {
33 
34 class MockQuicSpdyClientSession : public QuicSpdyClientSession {
35  public:
MockQuicSpdyClientSession(const ParsedQuicVersionVector & supported_versions,QuicConnection * connection)36   explicit MockQuicSpdyClientSession(
37       const ParsedQuicVersionVector& supported_versions,
38       QuicConnection* connection)
39       : QuicSpdyClientSession(
40             DefaultQuicConfig(), supported_versions, connection,
41             QuicServerId("example.com", 443, false), &crypto_config_),
42         crypto_config_(crypto_test_utils::ProofVerifierForTesting()) {}
43   MockQuicSpdyClientSession(const MockQuicSpdyClientSession&) = delete;
44   MockQuicSpdyClientSession& operator=(const MockQuicSpdyClientSession&) =
45       delete;
46   ~MockQuicSpdyClientSession() override = default;
47 
48   MOCK_METHOD(bool, WriteControlFrame,
49               (const QuicFrame& frame, TransmissionType type), (override));
50 
51   using QuicSession::ActivateStream;
52 
53  private:
54   QuicCryptoClientConfig crypto_config_;
55 };
56 
57 class QuicSpdyClientStreamTest : public QuicTestWithParam<ParsedQuicVersion> {
58  public:
59   class StreamVisitor;
60 
QuicSpdyClientStreamTest()61   QuicSpdyClientStreamTest()
62       : connection_(new StrictMock<MockQuicConnection>(
63             &helper_, &alarm_factory_, Perspective::IS_CLIENT,
64             SupportedVersions(GetParam()))),
65         session_(connection_->supported_versions(), connection_),
66         body_("hello world") {
67     session_.Initialize();
68     connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
69     connection_->SetEncrypter(
70         ENCRYPTION_FORWARD_SECURE,
71         std::make_unique<NullEncrypter>(connection_->perspective()));
72     headers_[":status"] = "200";
73     headers_["content-length"] = "11";
74 
75     auto stream = std::make_unique<QuicSpdyClientStream>(
76         GetNthClientInitiatedBidirectionalStreamId(
77             connection_->transport_version(), 0),
78         &session_, BIDIRECTIONAL);
79     stream_ = stream.get();
80     session_.ActivateStream(std::move(stream));
81 
82     stream_visitor_ = std::make_unique<StreamVisitor>();
83     stream_->set_visitor(stream_visitor_.get());
84   }
85 
86   class StreamVisitor : public QuicSpdyClientStream::Visitor {
OnClose(QuicSpdyStream * stream)87     void OnClose(QuicSpdyStream* stream) override {
88       QUIC_DVLOG(1) << "stream " << stream->id();
89     }
90   };
91 
92   MockQuicConnectionHelper helper_;
93   MockAlarmFactory alarm_factory_;
94   StrictMock<MockQuicConnection>* connection_;
95 
96   MockQuicSpdyClientSession session_;
97   QuicSpdyClientStream* stream_;
98   std::unique_ptr<StreamVisitor> stream_visitor_;
99   Http2HeaderBlock headers_;
100   std::string body_;
101 };
102 
103 INSTANTIATE_TEST_SUITE_P(Tests, QuicSpdyClientStreamTest,
104                          ::testing::ValuesIn(AllSupportedVersions()),
105                          ::testing::PrintToStringParamName());
106 
TEST_P(QuicSpdyClientStreamTest,TestReceivingIllegalResponseStatusCode)107 TEST_P(QuicSpdyClientStreamTest, TestReceivingIllegalResponseStatusCode) {
108   headers_[":status"] = "200 ok";
109 
110   EXPECT_CALL(session_, WriteControlFrame(_, _));
111   EXPECT_CALL(*connection_,
112               OnStreamReset(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD));
113   auto headers = AsHeaderList(headers_);
114   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
115                               headers);
116   EXPECT_THAT(stream_->stream_error(),
117               IsStreamError(QUIC_BAD_APPLICATION_PAYLOAD));
118   EXPECT_EQ(stream_->ietf_application_error(),
119             static_cast<uint64_t>(QuicHttp3ErrorCode::GENERAL_PROTOCOL_ERROR));
120 }
121 
TEST_P(QuicSpdyClientStreamTest,InvalidResponseHeader)122 TEST_P(QuicSpdyClientStreamTest, InvalidResponseHeader) {
123   SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
124   auto headers = AsHeaderList(std::vector<std::pair<std::string, std::string>>{
125       {":status", "200"}, {":path", "/foo"}});
126   EXPECT_CALL(*connection_,
127               OnStreamReset(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD));
128   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
129                               headers);
130   EXPECT_THAT(stream_->stream_error(),
131               IsStreamError(QUIC_BAD_APPLICATION_PAYLOAD));
132   EXPECT_EQ(stream_->ietf_application_error(),
133             static_cast<uint64_t>(QuicHttp3ErrorCode::GENERAL_PROTOCOL_ERROR));
134 }
135 
TEST_P(QuicSpdyClientStreamTest,MissingStatusCode)136 TEST_P(QuicSpdyClientStreamTest, MissingStatusCode) {
137   SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
138   auto headers = AsHeaderList(
139       std::vector<std::pair<std::string, std::string>>{{"key", "value"}});
140   EXPECT_CALL(*connection_,
141               OnStreamReset(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD));
142   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
143                               headers);
144   EXPECT_THAT(stream_->stream_error(),
145               IsStreamError(QUIC_BAD_APPLICATION_PAYLOAD));
146   EXPECT_EQ(stream_->ietf_application_error(),
147             static_cast<uint64_t>(QuicHttp3ErrorCode::GENERAL_PROTOCOL_ERROR));
148 }
149 
TEST_P(QuicSpdyClientStreamTest,TestFraming)150 TEST_P(QuicSpdyClientStreamTest, TestFraming) {
151   auto headers = AsHeaderList(headers_);
152   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
153                               headers);
154   quiche::QuicheBuffer header = HttpEncoder::SerializeDataFrameHeader(
155       body_.length(), quiche::SimpleBufferAllocator::Get());
156   std::string data = VersionUsesHttp3(connection_->transport_version())
157                          ? absl::StrCat(header.AsStringView(), body_)
158                          : body_;
159   stream_->OnStreamFrame(
160       QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
161   EXPECT_EQ("200", stream_->response_headers().find(":status")->second);
162   EXPECT_EQ(200, stream_->response_code());
163   EXPECT_EQ(body_, stream_->data());
164 }
165 
TEST_P(QuicSpdyClientStreamTest,HostAllowedInResponseHeader)166 TEST_P(QuicSpdyClientStreamTest, HostAllowedInResponseHeader) {
167   SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
168   auto headers = AsHeaderList(std::vector<std::pair<std::string, std::string>>{
169       {":status", "200"}, {"host", "example.com"}});
170   EXPECT_CALL(*connection_, OnStreamReset(stream_->id(), _)).Times(0u);
171   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
172                               headers);
173   EXPECT_THAT(stream_->stream_error(), IsStreamError(QUIC_STREAM_NO_ERROR));
174   EXPECT_EQ(stream_->ietf_application_error(),
175             static_cast<uint64_t>(QuicHttp3ErrorCode::HTTP3_NO_ERROR));
176 }
177 
TEST_P(QuicSpdyClientStreamTest,Test100ContinueBeforeSuccessful)178 TEST_P(QuicSpdyClientStreamTest, Test100ContinueBeforeSuccessful) {
179   // First send 100 Continue.
180   headers_[":status"] = "100";
181   auto headers = AsHeaderList(headers_);
182   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
183                               headers);
184   ASSERT_EQ(stream_->preliminary_headers().size(), 1);
185   EXPECT_EQ("100",
186             stream_->preliminary_headers().front().find(":status")->second);
187   EXPECT_EQ(0u, stream_->response_headers().size());
188   EXPECT_EQ(100, stream_->response_code());
189   EXPECT_EQ("", stream_->data());
190   // Then send 200 OK.
191   headers_[":status"] = "200";
192   headers = AsHeaderList(headers_);
193   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
194                               headers);
195   quiche::QuicheBuffer header = HttpEncoder::SerializeDataFrameHeader(
196       body_.length(), quiche::SimpleBufferAllocator::Get());
197   std::string data = VersionUsesHttp3(connection_->transport_version())
198                          ? absl::StrCat(header.AsStringView(), body_)
199                          : body_;
200   stream_->OnStreamFrame(
201       QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
202   // Make sure the 200 response got parsed correctly.
203   EXPECT_EQ("200", stream_->response_headers().find(":status")->second);
204   EXPECT_EQ(200, stream_->response_code());
205   EXPECT_EQ(body_, stream_->data());
206   // Make sure the 100 response is still available.
207   ASSERT_EQ(stream_->preliminary_headers().size(), 1);
208   EXPECT_EQ("100",
209             stream_->preliminary_headers().front().find(":status")->second);
210 }
211 
TEST_P(QuicSpdyClientStreamTest,TestUnknownInformationalBeforeSuccessful)212 TEST_P(QuicSpdyClientStreamTest, TestUnknownInformationalBeforeSuccessful) {
213   // First send 199, an unknown Informational (1XX).
214   headers_[":status"] = "199";
215   auto headers = AsHeaderList(headers_);
216   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
217                               headers);
218   ASSERT_EQ(stream_->preliminary_headers().size(), 1);
219   EXPECT_EQ("199",
220             stream_->preliminary_headers().front().find(":status")->second);
221   EXPECT_EQ(0u, stream_->response_headers().size());
222   EXPECT_EQ(199, stream_->response_code());
223   EXPECT_EQ("", stream_->data());
224   // Then send 200 OK.
225   headers_[":status"] = "200";
226   headers = AsHeaderList(headers_);
227   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
228                               headers);
229   quiche::QuicheBuffer header = HttpEncoder::SerializeDataFrameHeader(
230       body_.length(), quiche::SimpleBufferAllocator::Get());
231   std::string data = VersionUsesHttp3(connection_->transport_version())
232                          ? absl::StrCat(header.AsStringView(), body_)
233                          : body_;
234   stream_->OnStreamFrame(
235       QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
236   // Make sure the 200 response got parsed correctly.
237   EXPECT_EQ("200", stream_->response_headers().find(":status")->second);
238   EXPECT_EQ(200, stream_->response_code());
239   EXPECT_EQ(body_, stream_->data());
240   // Make sure the 199 response is still available.
241   ASSERT_EQ(stream_->preliminary_headers().size(), 1);
242   EXPECT_EQ("199",
243             stream_->preliminary_headers().front().find(":status")->second);
244 }
245 
TEST_P(QuicSpdyClientStreamTest,TestMultipleInformationalBeforeSuccessful)246 TEST_P(QuicSpdyClientStreamTest, TestMultipleInformationalBeforeSuccessful) {
247   // First send 100 Continue.
248   headers_[":status"] = "100";
249   auto headers = AsHeaderList(headers_);
250   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
251                               headers);
252   ASSERT_EQ(stream_->preliminary_headers().size(), 1);
253   EXPECT_EQ("100",
254             stream_->preliminary_headers().front().find(":status")->second);
255   EXPECT_EQ(0u, stream_->response_headers().size());
256   EXPECT_EQ(100, stream_->response_code());
257   EXPECT_EQ("", stream_->data());
258 
259   // Then send 199, an unknown Informational (1XX).
260   headers_[":status"] = "199";
261   headers = AsHeaderList(headers_);
262   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
263                               headers);
264   ASSERT_EQ(stream_->preliminary_headers().size(), 2);
265   EXPECT_EQ("100",
266             stream_->preliminary_headers().front().find(":status")->second);
267   EXPECT_EQ("199",
268             stream_->preliminary_headers().back().find(":status")->second);
269   EXPECT_EQ(0u, stream_->response_headers().size());
270   EXPECT_EQ(199, stream_->response_code());
271   EXPECT_EQ("", stream_->data());
272 
273   // Then send 200 OK.
274   headers_[":status"] = "200";
275   headers = AsHeaderList(headers_);
276   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
277                               headers);
278   quiche::QuicheBuffer header = HttpEncoder::SerializeDataFrameHeader(
279       body_.length(), quiche::SimpleBufferAllocator::Get());
280   std::string data = VersionUsesHttp3(connection_->transport_version())
281                          ? absl::StrCat(header.AsStringView(), body_)
282                          : body_;
283   stream_->OnStreamFrame(
284       QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
285 
286   // Make sure the 200 response got parsed correctly.
287   EXPECT_EQ("200", stream_->response_headers().find(":status")->second);
288   EXPECT_EQ(200, stream_->response_code());
289   EXPECT_EQ(body_, stream_->data());
290 
291   // Make sure the informational responses are still available.
292   ASSERT_EQ(stream_->preliminary_headers().size(), 2);
293   EXPECT_EQ("100",
294             stream_->preliminary_headers().front().find(":status")->second);
295   EXPECT_EQ("199",
296             stream_->preliminary_headers().back().find(":status")->second);
297 }
298 
TEST_P(QuicSpdyClientStreamTest,TestReceiving101)299 TEST_P(QuicSpdyClientStreamTest, TestReceiving101) {
300   // 101 "Switching Protocols" is forbidden in HTTP/3 as per the
301   // "HTTP Upgrade" section of draft-ietf-quic-http.
302   headers_[":status"] = "101";
303   EXPECT_CALL(session_, WriteControlFrame(_, _));
304   EXPECT_CALL(*connection_,
305               OnStreamReset(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD));
306   auto headers = AsHeaderList(headers_);
307   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
308                               headers);
309   EXPECT_THAT(stream_->stream_error(),
310               IsStreamError(QUIC_BAD_APPLICATION_PAYLOAD));
311 }
312 
TEST_P(QuicSpdyClientStreamTest,TestFramingOnePacket)313 TEST_P(QuicSpdyClientStreamTest, TestFramingOnePacket) {
314   auto headers = AsHeaderList(headers_);
315   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
316                               headers);
317   quiche::QuicheBuffer header = HttpEncoder::SerializeDataFrameHeader(
318       body_.length(), quiche::SimpleBufferAllocator::Get());
319   std::string data = VersionUsesHttp3(connection_->transport_version())
320                          ? absl::StrCat(header.AsStringView(), body_)
321                          : body_;
322   stream_->OnStreamFrame(
323       QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
324   EXPECT_EQ("200", stream_->response_headers().find(":status")->second);
325   EXPECT_EQ(200, stream_->response_code());
326   EXPECT_EQ(body_, stream_->data());
327 }
328 
TEST_P(QuicSpdyClientStreamTest,QUIC_TEST_DISABLED_IN_CHROME (TestFramingExtraData))329 TEST_P(QuicSpdyClientStreamTest,
330        QUIC_TEST_DISABLED_IN_CHROME(TestFramingExtraData)) {
331   std::string large_body = "hello world!!!!!!";
332 
333   auto headers = AsHeaderList(headers_);
334   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
335                               headers);
336   // The headers should parse successfully.
337   EXPECT_THAT(stream_->stream_error(), IsQuicStreamNoError());
338   EXPECT_EQ("200", stream_->response_headers().find(":status")->second);
339   EXPECT_EQ(200, stream_->response_code());
340   quiche::QuicheBuffer header = HttpEncoder::SerializeDataFrameHeader(
341       large_body.length(), quiche::SimpleBufferAllocator::Get());
342   std::string data = VersionUsesHttp3(connection_->transport_version())
343                          ? absl::StrCat(header.AsStringView(), large_body)
344                          : large_body;
345   EXPECT_CALL(session_, WriteControlFrame(_, _));
346   EXPECT_CALL(*connection_,
347               OnStreamReset(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD));
348 
349   stream_->OnStreamFrame(
350       QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
351 
352   EXPECT_NE(QUIC_STREAM_NO_ERROR, stream_->stream_error());
353   EXPECT_EQ(stream_->ietf_application_error(),
354             static_cast<uint64_t>(QuicHttp3ErrorCode::GENERAL_PROTOCOL_ERROR));
355 }
356 
357 // Test that receiving trailing headers (on the headers stream), containing a
358 // final offset, results in the stream being closed at that byte offset.
TEST_P(QuicSpdyClientStreamTest,ReceivingTrailers)359 TEST_P(QuicSpdyClientStreamTest, ReceivingTrailers) {
360   // There is no kFinalOffsetHeaderKey if trailers are sent on the
361   // request/response stream.
362   if (VersionUsesHttp3(connection_->transport_version())) {
363     return;
364   }
365 
366   // Send headers as usual.
367   auto headers = AsHeaderList(headers_);
368   stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
369                               headers);
370 
371   // Send trailers before sending the body. Even though a FIN has been received
372   // the stream should not be closed, as it does not yet have all the data bytes
373   // promised by the final offset field.
374   Http2HeaderBlock trailer_block;
375   trailer_block["trailer key"] = "trailer value";
376   trailer_block[kFinalOffsetHeaderKey] = absl::StrCat(body_.size());
377   auto trailers = AsHeaderList(trailer_block);
378   stream_->OnStreamHeaderList(true, trailers.uncompressed_header_bytes(),
379                               trailers);
380 
381   // Now send the body, which should close the stream as the FIN has been
382   // received, as well as all data.
383   quiche::QuicheBuffer header = HttpEncoder::SerializeDataFrameHeader(
384       body_.length(), quiche::SimpleBufferAllocator::Get());
385   std::string data = VersionUsesHttp3(connection_->transport_version())
386                          ? absl::StrCat(header.AsStringView(), body_)
387                          : body_;
388   stream_->OnStreamFrame(
389       QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
390   EXPECT_TRUE(stream_->reading_stopped());
391 }
392 
393 }  // namespace
394 }  // namespace test
395 }  // namespace quic
396