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