xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/http/spdy_utils_test.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2016 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/spdy_utils.h"
6 
7 #include <memory>
8 #include <string>
9 
10 #include "absl/base/macros.h"
11 #include "absl/strings/string_view.h"
12 #include "quiche/quic/core/quic_versions.h"
13 #include "quiche/quic/platform/api/quic_test.h"
14 
15 using spdy::Http2HeaderBlock;
16 using testing::Pair;
17 using testing::UnorderedElementsAre;
18 
19 namespace quic {
20 namespace test {
21 namespace {
22 
23 const bool kExpectFinalByteOffset = true;
24 const bool kDoNotExpectFinalByteOffset = false;
25 
FromList(const QuicHeaderList::ListType & src)26 static std::unique_ptr<QuicHeaderList> FromList(
27     const QuicHeaderList::ListType& src) {
28   std::unique_ptr<QuicHeaderList> headers(new QuicHeaderList);
29   headers->OnHeaderBlockStart();
30   for (const auto& p : src) {
31     headers->OnHeader(p.first, p.second);
32   }
33   headers->OnHeaderBlockEnd(0, 0);
34   return headers;
35 }
36 
37 }  // anonymous namespace
38 
39 using CopyAndValidateHeaders = QuicTest;
40 
TEST_F(CopyAndValidateHeaders,NormalUsage)41 TEST_F(CopyAndValidateHeaders, NormalUsage) {
42   auto headers = FromList({// All cookie crumbs are joined.
43                            {"cookie", " part 1"},
44                            {"cookie", "part 2 "},
45                            {"cookie", "part3"},
46 
47                            // Already-delimited headers are passed through.
48                            {"passed-through", std::string("foo\0baz", 7)},
49 
50                            // Other headers are joined on \0.
51                            {"joined", "value 1"},
52                            {"joined", "value 2"},
53 
54                            // Empty headers remain empty.
55                            {"empty", ""},
56 
57                            // Joined empty headers work as expected.
58                            {"empty-joined", ""},
59                            {"empty-joined", "foo"},
60                            {"empty-joined", ""},
61                            {"empty-joined", ""},
62 
63                            // Non-continguous cookie crumb.
64                            {"cookie", " fin!"}});
65 
66   int64_t content_length = -1;
67   Http2HeaderBlock block;
68   ASSERT_TRUE(
69       SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
70   EXPECT_THAT(block,
71               UnorderedElementsAre(
72                   Pair("cookie", " part 1; part 2 ; part3;  fin!"),
73                   Pair("passed-through", absl::string_view("foo\0baz", 7)),
74                   Pair("joined", absl::string_view("value 1\0value 2", 15)),
75                   Pair("empty", ""),
76                   Pair("empty-joined", absl::string_view("\0foo\0\0", 6))));
77   EXPECT_EQ(-1, content_length);
78 }
79 
TEST_F(CopyAndValidateHeaders,EmptyName)80 TEST_F(CopyAndValidateHeaders, EmptyName) {
81   auto headers = FromList({{"foo", "foovalue"}, {"", "barvalue"}, {"baz", ""}});
82   int64_t content_length = -1;
83   Http2HeaderBlock block;
84   ASSERT_FALSE(
85       SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
86 }
87 
TEST_F(CopyAndValidateHeaders,UpperCaseName)88 TEST_F(CopyAndValidateHeaders, UpperCaseName) {
89   auto headers =
90       FromList({{"foo", "foovalue"}, {"bar", "barvalue"}, {"bAz", ""}});
91   int64_t content_length = -1;
92   Http2HeaderBlock block;
93   ASSERT_FALSE(
94       SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
95 }
96 
TEST_F(CopyAndValidateHeaders,MultipleContentLengths)97 TEST_F(CopyAndValidateHeaders, MultipleContentLengths) {
98   auto headers = FromList({{"content-length", "9"},
99                            {"foo", "foovalue"},
100                            {"content-length", "9"},
101                            {"bar", "barvalue"},
102                            {"baz", ""}});
103   int64_t content_length = -1;
104   Http2HeaderBlock block;
105   ASSERT_TRUE(
106       SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
107   EXPECT_THAT(block, UnorderedElementsAre(
108                          Pair("foo", "foovalue"), Pair("bar", "barvalue"),
109                          Pair("content-length", absl::string_view("9\09", 3)),
110                          Pair("baz", "")));
111   EXPECT_EQ(9, content_length);
112 }
113 
TEST_F(CopyAndValidateHeaders,InconsistentContentLengths)114 TEST_F(CopyAndValidateHeaders, InconsistentContentLengths) {
115   auto headers = FromList({{"content-length", "9"},
116                            {"foo", "foovalue"},
117                            {"content-length", "8"},
118                            {"bar", "barvalue"},
119                            {"baz", ""}});
120   int64_t content_length = -1;
121   Http2HeaderBlock block;
122   ASSERT_FALSE(
123       SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
124 }
125 
TEST_F(CopyAndValidateHeaders,LargeContentLength)126 TEST_F(CopyAndValidateHeaders, LargeContentLength) {
127   auto headers = FromList({{"content-length", "9000000000"},
128                            {"foo", "foovalue"},
129                            {"bar", "barvalue"},
130                            {"baz", ""}});
131   int64_t content_length = -1;
132   Http2HeaderBlock block;
133   ASSERT_TRUE(
134       SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
135   EXPECT_THAT(block,
136               UnorderedElementsAre(
137                   Pair("foo", "foovalue"), Pair("bar", "barvalue"),
138                   Pair("content-length", absl::string_view("9000000000")),
139                   Pair("baz", "")));
140   EXPECT_EQ(9000000000, content_length);
141 }
142 
TEST_F(CopyAndValidateHeaders,NonDigitContentLength)143 TEST_F(CopyAndValidateHeaders, NonDigitContentLength) {
144   // Section 3.3.2 of RFC 7230 defines content-length as being only digits.
145   // Number parsers might accept symbols like a leading plus; test that this
146   // fails to parse.
147   auto headers = FromList({{"content-length", "+123"},
148                            {"foo", "foovalue"},
149                            {"bar", "barvalue"},
150                            {"baz", ""}});
151   int64_t content_length = -1;
152   Http2HeaderBlock block;
153   EXPECT_FALSE(
154       SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
155 }
156 
TEST_F(CopyAndValidateHeaders,MultipleValues)157 TEST_F(CopyAndValidateHeaders, MultipleValues) {
158   auto headers = FromList({{"foo", "foovalue"},
159                            {"bar", "barvalue"},
160                            {"baz", ""},
161                            {"foo", "boo"},
162                            {"baz", "buzz"}});
163   int64_t content_length = -1;
164   Http2HeaderBlock block;
165   ASSERT_TRUE(
166       SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
167   EXPECT_THAT(block, UnorderedElementsAre(
168                          Pair("foo", absl::string_view("foovalue\0boo", 12)),
169                          Pair("bar", "barvalue"),
170                          Pair("baz", absl::string_view("\0buzz", 5))));
171   EXPECT_EQ(-1, content_length);
172 }
173 
TEST_F(CopyAndValidateHeaders,MoreThanTwoValues)174 TEST_F(CopyAndValidateHeaders, MoreThanTwoValues) {
175   auto headers = FromList({{"set-cookie", "value1"},
176                            {"set-cookie", "value2"},
177                            {"set-cookie", "value3"}});
178   int64_t content_length = -1;
179   Http2HeaderBlock block;
180   ASSERT_TRUE(
181       SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
182   EXPECT_THAT(block, UnorderedElementsAre(Pair(
183                          "set-cookie",
184                          absl::string_view("value1\0value2\0value3", 20))));
185   EXPECT_EQ(-1, content_length);
186 }
187 
TEST_F(CopyAndValidateHeaders,Cookie)188 TEST_F(CopyAndValidateHeaders, Cookie) {
189   auto headers = FromList({{"foo", "foovalue"},
190                            {"bar", "barvalue"},
191                            {"cookie", "value1"},
192                            {"baz", ""}});
193   int64_t content_length = -1;
194   Http2HeaderBlock block;
195   ASSERT_TRUE(
196       SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
197   EXPECT_THAT(block, UnorderedElementsAre(
198                          Pair("foo", "foovalue"), Pair("bar", "barvalue"),
199                          Pair("cookie", "value1"), Pair("baz", "")));
200   EXPECT_EQ(-1, content_length);
201 }
202 
TEST_F(CopyAndValidateHeaders,MultipleCookies)203 TEST_F(CopyAndValidateHeaders, MultipleCookies) {
204   auto headers = FromList({{"foo", "foovalue"},
205                            {"bar", "barvalue"},
206                            {"cookie", "value1"},
207                            {"baz", ""},
208                            {"cookie", "value2"}});
209   int64_t content_length = -1;
210   Http2HeaderBlock block;
211   ASSERT_TRUE(
212       SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
213   EXPECT_THAT(block, UnorderedElementsAre(
214                          Pair("foo", "foovalue"), Pair("bar", "barvalue"),
215                          Pair("cookie", "value1; value2"), Pair("baz", "")));
216   EXPECT_EQ(-1, content_length);
217 }
218 
219 using CopyAndValidateTrailers = QuicTest;
220 
TEST_F(CopyAndValidateTrailers,SimplestValidList)221 TEST_F(CopyAndValidateTrailers, SimplestValidList) {
222   // Verify that the simplest trailers are valid: just a final byte offset that
223   // gets parsed successfully.
224   auto trailers = FromList({{kFinalOffsetHeaderKey, "1234"}});
225   size_t final_byte_offset = 0;
226   Http2HeaderBlock block;
227   EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
228       *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
229   EXPECT_EQ(1234u, final_byte_offset);
230 }
231 
TEST_F(CopyAndValidateTrailers,EmptyTrailerListWithFinalByteOffsetExpected)232 TEST_F(CopyAndValidateTrailers, EmptyTrailerListWithFinalByteOffsetExpected) {
233   // An empty trailer list will fail as expected key kFinalOffsetHeaderKey is
234   // not present.
235   QuicHeaderList trailers;
236   size_t final_byte_offset = 0;
237   Http2HeaderBlock block;
238   EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
239       trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
240 }
241 
TEST_F(CopyAndValidateTrailers,EmptyTrailerListWithFinalByteOffsetNotExpected)242 TEST_F(CopyAndValidateTrailers,
243        EmptyTrailerListWithFinalByteOffsetNotExpected) {
244   // An empty trailer list will pass successfully if kFinalOffsetHeaderKey is
245   // not expected.
246   QuicHeaderList trailers;
247   size_t final_byte_offset = 0;
248   Http2HeaderBlock block;
249   EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
250       trailers, kDoNotExpectFinalByteOffset, &final_byte_offset, &block));
251   EXPECT_TRUE(block.empty());
252 }
253 
TEST_F(CopyAndValidateTrailers,FinalByteOffsetExpectedButNotPresent)254 TEST_F(CopyAndValidateTrailers, FinalByteOffsetExpectedButNotPresent) {
255   // Validation fails if expected kFinalOffsetHeaderKey is not present, even if
256   // the rest of the header block is valid.
257   auto trailers = FromList({{"key", "value"}});
258   size_t final_byte_offset = 0;
259   Http2HeaderBlock block;
260   EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
261       *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
262 }
263 
TEST_F(CopyAndValidateTrailers,FinalByteOffsetNotExpectedButPresent)264 TEST_F(CopyAndValidateTrailers, FinalByteOffsetNotExpectedButPresent) {
265   // Validation fails if kFinalOffsetHeaderKey is present but should not be,
266   // even if the rest of the header block is valid.
267   auto trailers = FromList({{"key", "value"}, {kFinalOffsetHeaderKey, "1234"}});
268   size_t final_byte_offset = 0;
269   Http2HeaderBlock block;
270   EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
271       *trailers, kDoNotExpectFinalByteOffset, &final_byte_offset, &block));
272 }
273 
TEST_F(CopyAndValidateTrailers,FinalByteOffsetNotExpectedAndNotPresent)274 TEST_F(CopyAndValidateTrailers, FinalByteOffsetNotExpectedAndNotPresent) {
275   // Validation succeeds if kFinalOffsetHeaderKey is not expected and not
276   // present.
277   auto trailers = FromList({{"key", "value"}});
278   size_t final_byte_offset = 0;
279   Http2HeaderBlock block;
280   EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
281       *trailers, kDoNotExpectFinalByteOffset, &final_byte_offset, &block));
282   EXPECT_THAT(block, UnorderedElementsAre(Pair("key", "value")));
283 }
284 
TEST_F(CopyAndValidateTrailers,EmptyName)285 TEST_F(CopyAndValidateTrailers, EmptyName) {
286   // Trailer validation will fail with an empty header key, in an otherwise
287   // valid block of trailers.
288   auto trailers = FromList({{"", "value"}, {kFinalOffsetHeaderKey, "1234"}});
289   size_t final_byte_offset = 0;
290   Http2HeaderBlock block;
291   EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
292       *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
293 }
294 
TEST_F(CopyAndValidateTrailers,PseudoHeaderInTrailers)295 TEST_F(CopyAndValidateTrailers, PseudoHeaderInTrailers) {
296   // Pseudo headers are illegal in trailers.
297   auto trailers =
298       FromList({{":pseudo_key", "value"}, {kFinalOffsetHeaderKey, "1234"}});
299   size_t final_byte_offset = 0;
300   Http2HeaderBlock block;
301   EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
302       *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
303 }
304 
TEST_F(CopyAndValidateTrailers,DuplicateTrailers)305 TEST_F(CopyAndValidateTrailers, DuplicateTrailers) {
306   // Duplicate trailers are allowed, and their values are concatenated into a
307   // single string delimted with '\0'. Some of the duplicate headers
308   // deliberately have an empty value.
309   auto trailers = FromList({{"key", "value0"},
310                             {"key", "value1"},
311                             {"key", ""},
312                             {"key", ""},
313                             {"key", "value2"},
314                             {"key", ""},
315                             {kFinalOffsetHeaderKey, "1234"},
316                             {"other_key", "value"},
317                             {"key", "non_contiguous_duplicate"}});
318   size_t final_byte_offset = 0;
319   Http2HeaderBlock block;
320   EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
321       *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
322   EXPECT_THAT(
323       block,
324       UnorderedElementsAre(
325           Pair("key",
326                absl::string_view(
327                    "value0\0value1\0\0\0value2\0\0non_contiguous_duplicate",
328                    48)),
329           Pair("other_key", "value")));
330 }
331 
TEST_F(CopyAndValidateTrailers,DuplicateCookies)332 TEST_F(CopyAndValidateTrailers, DuplicateCookies) {
333   // Duplicate cookie headers in trailers should be concatenated into a single
334   //  "; " delimted string.
335   auto headers = FromList({{"cookie", " part 1"},
336                            {"cookie", "part 2 "},
337                            {"cookie", "part3"},
338                            {"key", "value"},
339                            {kFinalOffsetHeaderKey, "1234"},
340                            {"cookie", " non_contiguous_cookie!"}});
341 
342   size_t final_byte_offset = 0;
343   Http2HeaderBlock block;
344   EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
345       *headers, kExpectFinalByteOffset, &final_byte_offset, &block));
346   EXPECT_THAT(
347       block,
348       UnorderedElementsAre(
349           Pair("cookie", " part 1; part 2 ; part3;  non_contiguous_cookie!"),
350           Pair("key", "value")));
351 }
352 
353 using PopulateHeaderBlockFromUrl = QuicTest;
354 
TEST_F(PopulateHeaderBlockFromUrl,NormalUsage)355 TEST_F(PopulateHeaderBlockFromUrl, NormalUsage) {
356   std::string url = "https://www.google.com/index.html";
357   Http2HeaderBlock headers;
358   EXPECT_TRUE(SpdyUtils::PopulateHeaderBlockFromUrl(url, &headers));
359   EXPECT_EQ("https", headers[":scheme"].as_string());
360   EXPECT_EQ("www.google.com", headers[":authority"].as_string());
361   EXPECT_EQ("/index.html", headers[":path"].as_string());
362 }
363 
TEST_F(PopulateHeaderBlockFromUrl,UrlWithNoPath)364 TEST_F(PopulateHeaderBlockFromUrl, UrlWithNoPath) {
365   std::string url = "https://www.google.com";
366   Http2HeaderBlock headers;
367   EXPECT_TRUE(SpdyUtils::PopulateHeaderBlockFromUrl(url, &headers));
368   EXPECT_EQ("https", headers[":scheme"].as_string());
369   EXPECT_EQ("www.google.com", headers[":authority"].as_string());
370   EXPECT_EQ("/", headers[":path"].as_string());
371 }
372 
TEST_F(PopulateHeaderBlockFromUrl,Failure)373 TEST_F(PopulateHeaderBlockFromUrl, Failure) {
374   Http2HeaderBlock headers;
375   EXPECT_FALSE(SpdyUtils::PopulateHeaderBlockFromUrl("/", &headers));
376   EXPECT_FALSE(SpdyUtils::PopulateHeaderBlockFromUrl("/index.html", &headers));
377   EXPECT_FALSE(
378       SpdyUtils::PopulateHeaderBlockFromUrl("www.google.com/", &headers));
379 }
380 
381 using ExtractQuicVersionFromAltSvcEntry = QuicTest;
382 
TEST_F(ExtractQuicVersionFromAltSvcEntry,SupportedVersion)383 TEST_F(ExtractQuicVersionFromAltSvcEntry, SupportedVersion) {
384   ParsedQuicVersionVector supported_versions = AllSupportedVersions();
385   spdy::SpdyAltSvcWireFormat::AlternativeService entry;
386   for (const ParsedQuicVersion& version : supported_versions) {
387     entry.protocol_id = AlpnForVersion(version);
388     ParsedQuicVersion expected_version = version;
389     // Versions with share an ALPN with v1 are currently unable to be
390     // advertised with Alt-Svc.
391     if (entry.protocol_id == AlpnForVersion(ParsedQuicVersion::RFCv1()) &&
392         version != ParsedQuicVersion::RFCv1()) {
393       expected_version = ParsedQuicVersion::RFCv1();
394     }
395     EXPECT_EQ(expected_version, SpdyUtils::ExtractQuicVersionFromAltSvcEntry(
396                                     entry, supported_versions))
397         << "version: " << version;
398   }
399 }
400 
TEST_F(ExtractQuicVersionFromAltSvcEntry,UnsupportedVersion)401 TEST_F(ExtractQuicVersionFromAltSvcEntry, UnsupportedVersion) {
402   spdy::SpdyAltSvcWireFormat::AlternativeService entry;
403   entry.protocol_id = "quic";
404   EXPECT_EQ(ParsedQuicVersion::Unsupported(),
405             SpdyUtils::ExtractQuicVersionFromAltSvcEntry(
406                 entry, AllSupportedVersions()));
407 }
408 
409 }  // namespace test
410 }  // namespace quic
411