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