1 // Copyright 2012 The Chromium Authors
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 "net/http/http_response_headers.h"
6
7 #include <stdint.h>
8
9 #include <iostream>
10 #include <limits>
11 #include <memory>
12 #include <unordered_set>
13
14 #include "base/pickle.h"
15 #include "base/ranges/algorithm.h"
16 #include "base/time/time.h"
17 #include "base/values.h"
18 #include "net/base/cronet_buildflags.h"
19 #include "net/base/tracing.h"
20 #include "net/http/http_byte_range.h"
21 #include "net/http/http_util.h"
22 #include "net/log/net_log_capture_mode.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 #if !BUILDFLAG(CRONET_BUILD)
26 #include "third_party/perfetto/include/perfetto/test/traced_value_test_support.h"
27 #endif
28
29 namespace net {
30
31 namespace {
32
33 struct TestData {
34 const char* raw_headers;
35 const char* expected_headers;
36 HttpVersion expected_version;
37 int expected_response_code;
38 const char* expected_status_text;
39 };
40
41 class HttpResponseHeadersTest : public testing::Test {
42 };
43
44 // Transform "normal"-looking headers (\n-separated) to the appropriate
45 // input format for ParseRawHeaders (\0-separated).
HeadersToRaw(std::string * headers)46 void HeadersToRaw(std::string* headers) {
47 std::replace(headers->begin(), headers->end(), '\n', '\0');
48 if (!headers->empty())
49 *headers += '\0';
50 }
51
52 class HttpResponseHeadersCacheControlTest : public HttpResponseHeadersTest {
53 protected:
54 // Make tests less verbose.
55 typedef base::TimeDelta TimeDelta;
56
57 // Initilise the headers() value with a Cache-Control header set to
58 // |cache_control|. |cache_control| is copied and so can safely be a
59 // temporary.
InitializeHeadersWithCacheControl(const char * cache_control)60 void InitializeHeadersWithCacheControl(const char* cache_control) {
61 std::string raw_headers("HTTP/1.1 200 OK\n");
62 raw_headers += "Cache-Control: ";
63 raw_headers += cache_control;
64 raw_headers += "\n";
65 HeadersToRaw(&raw_headers);
66 headers_ = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
67 }
68
headers()69 const scoped_refptr<HttpResponseHeaders>& headers() { return headers_; }
70
71 // Return a pointer to a TimeDelta object. For use when the value doesn't
72 // matter.
TimeDeltaPointer()73 TimeDelta* TimeDeltaPointer() { return &delta_; }
74
75 // Get the max-age value. This should only be used in tests where a valid
76 // max-age parameter is expected to be present.
GetMaxAgeValue()77 TimeDelta GetMaxAgeValue() {
78 DCHECK(headers_.get()) << "Call InitializeHeadersWithCacheControl() first";
79 TimeDelta max_age_value;
80 EXPECT_TRUE(headers()->GetMaxAgeValue(&max_age_value));
81 return max_age_value;
82 }
83
84 // Get the stale-while-revalidate value. This should only be used in tests
85 // where a valid max-age parameter is expected to be present.
GetStaleWhileRevalidateValue()86 TimeDelta GetStaleWhileRevalidateValue() {
87 DCHECK(headers_.get()) << "Call InitializeHeadersWithCacheControl() first";
88 TimeDelta stale_while_revalidate_value;
89 EXPECT_TRUE(
90 headers()->GetStaleWhileRevalidateValue(&stale_while_revalidate_value));
91 return stale_while_revalidate_value;
92 }
93
94 private:
95 scoped_refptr<HttpResponseHeaders> headers_;
96 TimeDelta delta_;
97 };
98
99 class CommonHttpResponseHeadersTest
100 : public HttpResponseHeadersTest,
101 public ::testing::WithParamInterface<TestData> {
102 };
103
104 // Returns a simple text serialization of the given
105 // |HttpResponseHeaders|. This is used by tests to verify that an
106 // |HttpResponseHeaders| matches an expectation string.
107 //
108 // * One line per header, written as:
109 // HEADER_NAME: HEADER_VALUE\n
110 // * The original case of header names is preserved.
111 // * Whitespace around head names/values is stripped.
112 // * Repeated headers are not aggregated.
113 // * Headers are listed in their original order.
ToSimpleString(const scoped_refptr<HttpResponseHeaders> & parsed)114 std::string ToSimpleString(const scoped_refptr<HttpResponseHeaders>& parsed) {
115 std::string result = parsed->GetStatusLine() + "\n";
116
117 size_t iter = 0;
118 std::string name;
119 std::string value;
120 while (parsed->EnumerateHeaderLines(&iter, &name, &value)) {
121 std::string new_line = name + ": " + value + "\n";
122
123 // Verify that |name| and |value| do not contain ':' or '\n' (if they did
124 // it would make this serialized format ambiguous).
125 if (base::ranges::count(new_line, '\n') != 1 ||
126 base::ranges::count(new_line, ':') != 1) {
127 ADD_FAILURE() << "Unexpected characters in the header name or value: "
128 << new_line;
129 return result;
130 }
131
132 result += new_line;
133 }
134
135 return result;
136 }
137
TEST_P(CommonHttpResponseHeadersTest,TestCommon)138 TEST_P(CommonHttpResponseHeadersTest, TestCommon) {
139 const TestData test = GetParam();
140
141 std::string raw_headers(test.raw_headers);
142 HeadersToRaw(&raw_headers);
143 std::string expected_headers(test.expected_headers);
144
145 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
146 std::string headers = ToSimpleString(parsed);
147
148 // Transform to readable output format (so it's easier to see diffs).
149 std::replace(headers.begin(), headers.end(), ' ', '_');
150 std::replace(headers.begin(), headers.end(), '\n', '\\');
151 std::replace(expected_headers.begin(), expected_headers.end(), ' ', '_');
152 std::replace(expected_headers.begin(), expected_headers.end(), '\n', '\\');
153
154 EXPECT_EQ(expected_headers, headers);
155
156 EXPECT_TRUE(test.expected_version == parsed->GetHttpVersion());
157 EXPECT_EQ(test.expected_response_code, parsed->response_code());
158 EXPECT_EQ(test.expected_status_text, parsed->GetStatusText());
159 }
160
161 TestData response_headers_tests[] = {
162 {// Normalize whitespace.
163 "HTTP/1.1 202 Accepted \n"
164 "Content-TYPE : text/html; charset=utf-8 \n"
165 "Set-Cookie: a \n"
166 "Set-Cookie: b \n",
167
168 "HTTP/1.1 202 Accepted\n"
169 "Content-TYPE: text/html; charset=utf-8\n"
170 "Set-Cookie: a\n"
171 "Set-Cookie: b\n",
172
173 HttpVersion(1, 1), 202, "Accepted"},
174 {// Normalize leading whitespace.
175 "HTTP/1.1 202 Accepted \n"
176 // Starts with space -- will be skipped as invalid.
177 " Content-TYPE : text/html; charset=utf-8 \n"
178 "Set-Cookie: a \n"
179 "Set-Cookie: b \n",
180
181 "HTTP/1.1 202 Accepted\n"
182 "Set-Cookie: a\n"
183 "Set-Cookie: b\n",
184
185 HttpVersion(1, 1), 202, "Accepted"},
186 {// Keep whitespace within status text.
187 "HTTP/1.0 404 Not found \n",
188
189 "HTTP/1.0 404 Not found\n",
190
191 HttpVersion(1, 0), 404, "Not found"},
192 {// Normalize blank headers.
193 "HTTP/1.1 200 OK\n"
194 "Header1 : \n"
195 "Header2: \n"
196 "Header3:\n"
197 "Header4\n"
198 "Header5 :\n",
199
200 "HTTP/1.1 200 OK\n"
201 "Header1: \n"
202 "Header2: \n"
203 "Header3: \n"
204 "Header5: \n",
205
206 HttpVersion(1, 1), 200, "OK"},
207 {// Don't believe the http/0.9 version if there are headers!
208 "hTtP/0.9 201\n"
209 "Content-TYPE: text/html; charset=utf-8\n",
210
211 "HTTP/1.0 201\n"
212 "Content-TYPE: text/html; charset=utf-8\n",
213
214 HttpVersion(1, 0), 201, ""},
215 {// Accept the HTTP/0.9 version number if there are no headers.
216 // This is how HTTP/0.9 responses get constructed from
217 // HttpNetworkTransaction.
218 "hTtP/0.9 200 OK\n",
219
220 "HTTP/0.9 200 OK\n",
221
222 HttpVersion(0, 9), 200, "OK"},
223 {// Do not add missing status text.
224 "HTTP/1.1 201\n"
225 "Content-TYPE: text/html; charset=utf-8\n",
226
227 "HTTP/1.1 201\n"
228 "Content-TYPE: text/html; charset=utf-8\n",
229
230 HttpVersion(1, 1), 201, ""},
231 {// Normalize bad status line.
232 "SCREWED_UP_STATUS_LINE\n"
233 "Content-TYPE: text/html; charset=utf-8\n",
234
235 "HTTP/1.0 200 OK\n"
236 "Content-TYPE: text/html; charset=utf-8\n",
237
238 HttpVersion(1, 0), 200, "OK"},
239 {// Normalize bad status line.
240 "Foo bar.",
241
242 "HTTP/1.0 200\n",
243
244 HttpVersion(1, 0), 200, ""},
245 {// Normalize invalid status code.
246 "HTTP/1.1 -1 Unknown\n",
247
248 "HTTP/1.1 200\n",
249
250 HttpVersion(1, 1), 200, ""},
251 {// Normalize empty header.
252 "",
253
254 "HTTP/1.0 200 OK\n",
255
256 HttpVersion(1, 0), 200, "OK"},
257 {// Normalize headers that start with a colon.
258 "HTTP/1.1 202 Accepted \n"
259 "foo: bar\n"
260 ": a \n"
261 " : b\n"
262 "baz: blat \n",
263
264 "HTTP/1.1 202 Accepted\n"
265 "foo: bar\n"
266 "baz: blat\n",
267
268 HttpVersion(1, 1), 202, "Accepted"},
269 {// Normalize headers that end with a colon.
270 "HTTP/1.1 202 Accepted \n"
271 "foo: \n"
272 "bar:\n"
273 "baz: blat \n"
274 "zip:\n",
275
276 "HTTP/1.1 202 Accepted\n"
277 "foo: \n"
278 "bar: \n"
279 "baz: blat\n"
280 "zip: \n",
281
282 HttpVersion(1, 1), 202, "Accepted"},
283 {// Normalize whitespace headers.
284 "\n \n",
285
286 "HTTP/1.0 200 OK\n",
287
288 HttpVersion(1, 0), 200, "OK"},
289 {// Has multiple Set-Cookie headers.
290 "HTTP/1.1 200 OK\n"
291 "Set-Cookie: x=1\n"
292 "Set-Cookie: y=2\n",
293
294 "HTTP/1.1 200 OK\n"
295 "Set-Cookie: x=1\n"
296 "Set-Cookie: y=2\n",
297
298 HttpVersion(1, 1), 200, "OK"},
299 {// Has multiple cache-control headers.
300 "HTTP/1.1 200 OK\n"
301 "Cache-control: private\n"
302 "cache-Control: no-store\n",
303
304 "HTTP/1.1 200 OK\n"
305 "Cache-control: private\n"
306 "cache-Control: no-store\n",
307
308 HttpVersion(1, 1), 200, "OK"},
309 };
310
311 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
312 CommonHttpResponseHeadersTest,
313 testing::ValuesIn(response_headers_tests));
314
315 struct PersistData {
316 HttpResponseHeaders::PersistOptions options;
317 const char* raw_headers;
318 const char* expected_headers;
319 };
320
321 class PersistenceTest
322 : public HttpResponseHeadersTest,
323 public ::testing::WithParamInterface<PersistData> {
324 };
325
TEST_P(PersistenceTest,Persist)326 TEST_P(PersistenceTest, Persist) {
327 const PersistData test = GetParam();
328
329 std::string headers = test.raw_headers;
330 HeadersToRaw(&headers);
331 auto parsed1 = base::MakeRefCounted<HttpResponseHeaders>(headers);
332
333 base::Pickle pickle;
334 parsed1->Persist(&pickle, test.options);
335
336 base::PickleIterator iter(pickle);
337 auto parsed2 = base::MakeRefCounted<HttpResponseHeaders>(&iter);
338
339 EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed2));
340 }
341
342 const struct PersistData persistence_tests[] = {
343 {HttpResponseHeaders::PERSIST_ALL,
344 "HTTP/1.1 200 OK\n"
345 "Cache-control:private\n"
346 "cache-Control:no-store\n",
347
348 "HTTP/1.1 200 OK\n"
349 "Cache-control: private\n"
350 "cache-Control: no-store\n"},
351 {HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
352 "HTTP/1.1 200 OK\n"
353 "connection: keep-alive\n"
354 "server: blah\n",
355
356 "HTTP/1.1 200 OK\n"
357 "server: blah\n"},
358 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE |
359 HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
360 "HTTP/1.1 200 OK\n"
361 "fOo: 1\n"
362 "Foo: 2\n"
363 "Transfer-Encoding: chunked\n"
364 "CoNnection: keep-alive\n"
365 "cache-control: private, no-cache=\"foo\"\n",
366
367 "HTTP/1.1 200 OK\n"
368 "cache-control: private, no-cache=\"foo\"\n"},
369 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
370 "HTTP/1.1 200 OK\n"
371 "Foo: 2\n"
372 "Cache-Control: private,no-cache=\"foo, bar\"\n"
373 "bar",
374
375 "HTTP/1.1 200 OK\n"
376 "Cache-Control: private,no-cache=\"foo, bar\"\n"},
377 // Ignore bogus no-cache value.
378 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
379 "HTTP/1.1 200 OK\n"
380 "Foo: 2\n"
381 "Cache-Control: private,no-cache=foo\n",
382
383 "HTTP/1.1 200 OK\n"
384 "Foo: 2\n"
385 "Cache-Control: private,no-cache=foo\n"},
386 // Ignore bogus no-cache value.
387 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
388 "HTTP/1.1 200 OK\n"
389 "Foo: 2\n"
390 "Cache-Control: private, no-cache=\n",
391
392 "HTTP/1.1 200 OK\n"
393 "Foo: 2\n"
394 "Cache-Control: private, no-cache=\n"},
395 // Ignore empty no-cache value.
396 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
397 "HTTP/1.1 200 OK\n"
398 "Foo: 2\n"
399 "Cache-Control: private, no-cache=\"\"\n",
400
401 "HTTP/1.1 200 OK\n"
402 "Foo: 2\n"
403 "Cache-Control: private, no-cache=\"\"\n"},
404 // Ignore wrong quotes no-cache value.
405 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
406 "HTTP/1.1 200 OK\n"
407 "Foo: 2\n"
408 "Cache-Control: private, no-cache=\'foo\'\n",
409
410 "HTTP/1.1 200 OK\n"
411 "Foo: 2\n"
412 "Cache-Control: private, no-cache=\'foo\'\n"},
413 // Ignore unterminated quotes no-cache value.
414 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
415 "HTTP/1.1 200 OK\n"
416 "Foo: 2\n"
417 "Cache-Control: private, no-cache=\"foo\n",
418
419 "HTTP/1.1 200 OK\n"
420 "Foo: 2\n"
421 "Cache-Control: private, no-cache=\"foo\n"},
422 // Accept sloppy LWS.
423 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
424 "HTTP/1.1 200 OK\n"
425 "Foo: 2\n"
426 "Cache-Control: private, no-cache=\" foo\t, bar\"\n",
427
428 "HTTP/1.1 200 OK\n"
429 "Cache-Control: private, no-cache=\" foo\t, bar\"\n"},
430 // Header name appears twice, separated by another header.
431 {HttpResponseHeaders::PERSIST_ALL,
432 "HTTP/1.1 200 OK\n"
433 "Foo: 1\n"
434 "Bar: 2\n"
435 "Foo: 3\n",
436
437 "HTTP/1.1 200 OK\n"
438 "Foo: 1\n"
439 "Bar: 2\n"
440 "Foo: 3\n"},
441 // Header name appears twice, separated by another header (type 2).
442 {HttpResponseHeaders::PERSIST_ALL,
443 "HTTP/1.1 200 OK\n"
444 "Foo: 1, 3\n"
445 "Bar: 2\n"
446 "Foo: 4\n",
447
448 "HTTP/1.1 200 OK\n"
449 "Foo: 1, 3\n"
450 "Bar: 2\n"
451 "Foo: 4\n"},
452 // Test filtering of cookie headers.
453 {HttpResponseHeaders::PERSIST_SANS_COOKIES,
454 "HTTP/1.1 200 OK\n"
455 "Set-Cookie: foo=bar; httponly\n"
456 "Set-Cookie: bar=foo\n"
457 "Bar: 1\n"
458 "Set-Cookie2: bar2=foo2\n",
459
460 "HTTP/1.1 200 OK\n"
461 "Bar: 1\n"},
462 {HttpResponseHeaders::PERSIST_SANS_COOKIES,
463 "HTTP/1.1 200 OK\n"
464 "Set-Cookie: foo=bar\n"
465 "Foo: 2\n"
466 "Clear-Site-Data: { \"types\" : [ \"cookies\" ] }\n"
467 "Bar: 3\n",
468
469 "HTTP/1.1 200 OK\n"
470 "Foo: 2\n"
471 "Bar: 3\n"},
472 // Test LWS at the end of a header.
473 {HttpResponseHeaders::PERSIST_ALL,
474 "HTTP/1.1 200 OK\n"
475 "Content-Length: 450 \n"
476 "Content-Encoding: gzip\n",
477
478 "HTTP/1.1 200 OK\n"
479 "Content-Length: 450\n"
480 "Content-Encoding: gzip\n"},
481 // Test LWS at the end of a header.
482 {HttpResponseHeaders::PERSIST_RAW,
483 "HTTP/1.1 200 OK\n"
484 "Content-Length: 450 \n"
485 "Content-Encoding: gzip\n",
486
487 "HTTP/1.1 200 OK\n"
488 "Content-Length: 450\n"
489 "Content-Encoding: gzip\n"},
490 // Test filtering of transport security state headers.
491 {HttpResponseHeaders::PERSIST_SANS_SECURITY_STATE,
492 "HTTP/1.1 200 OK\n"
493 "Strict-Transport-Security: max-age=1576800\n"
494 "Bar: 1\n",
495
496 "HTTP/1.1 200 OK\n"
497 "Bar: 1\n"},
498 };
499
500 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
501 PersistenceTest,
502 testing::ValuesIn(persistence_tests));
503
TEST(HttpResponseHeadersTest,EnumerateHeader_Coalesced)504 TEST(HttpResponseHeadersTest, EnumerateHeader_Coalesced) {
505 // Ensure that commas in quoted strings are not regarded as value separators.
506 // Ensure that whitespace following a value is trimmed properly.
507 std::string headers =
508 "HTTP/1.1 200 OK\n"
509 "Cache-control:,,private , no-cache=\"set-cookie,server\",\n"
510 "cache-Control: no-store\n"
511 "cache-Control:\n";
512 HeadersToRaw(&headers);
513 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
514
515 size_t iter = 0;
516 std::string value;
517 ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
518 EXPECT_EQ("", value);
519 ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
520 EXPECT_EQ("", value);
521 ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
522 EXPECT_EQ("private", value);
523 ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
524 EXPECT_EQ("no-cache=\"set-cookie,server\"", value);
525 ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
526 EXPECT_EQ("", value);
527 ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
528 EXPECT_EQ("no-store", value);
529 ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
530 EXPECT_EQ("", value);
531 EXPECT_FALSE(parsed->EnumerateHeader(&iter, "cache-control", &value));
532 }
533
TEST(HttpResponseHeadersTest,EnumerateHeader_Challenge)534 TEST(HttpResponseHeadersTest, EnumerateHeader_Challenge) {
535 // Even though WWW-Authenticate has commas, it should not be treated as
536 // coalesced values.
537 std::string headers =
538 "HTTP/1.1 401 OK\n"
539 "WWW-Authenticate:Digest realm=foobar, nonce=x, domain=y\n"
540 "WWW-Authenticate:Basic realm=quatar\n";
541 HeadersToRaw(&headers);
542 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
543
544 size_t iter = 0;
545 std::string value;
546 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
547 EXPECT_EQ("Digest realm=foobar, nonce=x, domain=y", value);
548 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
549 EXPECT_EQ("Basic realm=quatar", value);
550 EXPECT_FALSE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
551 }
552
TEST(HttpResponseHeadersTest,EnumerateHeader_DateValued)553 TEST(HttpResponseHeadersTest, EnumerateHeader_DateValued) {
554 // The comma in a date valued header should not be treated as a
555 // field-value separator.
556 std::string headers =
557 "HTTP/1.1 200 OK\n"
558 "Date: Tue, 07 Aug 2007 23:10:55 GMT\n"
559 "Last-Modified: Wed, 01 Aug 2007 23:23:45 GMT\n";
560 HeadersToRaw(&headers);
561 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
562
563 std::string value;
564 EXPECT_TRUE(parsed->EnumerateHeader(nullptr, "date", &value));
565 EXPECT_EQ("Tue, 07 Aug 2007 23:10:55 GMT", value);
566 EXPECT_TRUE(parsed->EnumerateHeader(nullptr, "last-modified", &value));
567 EXPECT_EQ("Wed, 01 Aug 2007 23:23:45 GMT", value);
568 }
569
TEST(HttpResponseHeadersTest,DefaultDateToGMT)570 TEST(HttpResponseHeadersTest, DefaultDateToGMT) {
571 // Verify we make the best interpretation when parsing dates that incorrectly
572 // do not end in "GMT" as RFC2616 requires.
573 std::string headers =
574 "HTTP/1.1 200 OK\n"
575 "Date: Tue, 07 Aug 2007 23:10:55\n"
576 "Last-Modified: Tue, 07 Aug 2007 19:10:55 EDT\n"
577 "Expires: Tue, 07 Aug 2007 23:10:55 UTC\n";
578 HeadersToRaw(&headers);
579 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
580 base::Time expected_value;
581 ASSERT_TRUE(base::Time::FromString("Tue, 07 Aug 2007 23:10:55 GMT",
582 &expected_value));
583
584 base::Time value;
585 // When the timezone is missing, GMT is a good guess as its what RFC2616
586 // requires.
587 EXPECT_TRUE(parsed->GetDateValue(&value));
588 EXPECT_EQ(expected_value, value);
589 // If GMT is missing but an RFC822-conforming one is present, use that.
590 EXPECT_TRUE(parsed->GetLastModifiedValue(&value));
591 EXPECT_EQ(expected_value, value);
592 // If an unknown timezone is present, treat like a missing timezone and
593 // default to GMT. The only example of a web server not specifying "GMT"
594 // used "UTC" which is equivalent to GMT.
595 if (parsed->GetExpiresValue(&value))
596 EXPECT_EQ(expected_value, value);
597 }
598
TEST(HttpResponseHeadersTest,GetAgeValue10)599 TEST(HttpResponseHeadersTest, GetAgeValue10) {
600 std::string headers =
601 "HTTP/1.1 200 OK\n"
602 "Age: 10\n";
603 HeadersToRaw(&headers);
604 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
605 base::TimeDelta age;
606 ASSERT_TRUE(parsed->GetAgeValue(&age));
607 EXPECT_EQ(10, age.InSeconds());
608 }
609
TEST(HttpResponseHeadersTest,GetAgeValue0)610 TEST(HttpResponseHeadersTest, GetAgeValue0) {
611 std::string headers =
612 "HTTP/1.1 200 OK\n"
613 "Age: 0\n";
614 HeadersToRaw(&headers);
615 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
616 base::TimeDelta age;
617 ASSERT_TRUE(parsed->GetAgeValue(&age));
618 EXPECT_EQ(0, age.InSeconds());
619 }
620
TEST(HttpResponseHeadersTest,GetAgeValueBogus)621 TEST(HttpResponseHeadersTest, GetAgeValueBogus) {
622 std::string headers =
623 "HTTP/1.1 200 OK\n"
624 "Age: donkey\n";
625 HeadersToRaw(&headers);
626 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
627 base::TimeDelta age;
628 ASSERT_FALSE(parsed->GetAgeValue(&age));
629 }
630
TEST(HttpResponseHeadersTest,GetAgeValueNegative)631 TEST(HttpResponseHeadersTest, GetAgeValueNegative) {
632 std::string headers =
633 "HTTP/1.1 200 OK\n"
634 "Age: -10\n";
635 HeadersToRaw(&headers);
636 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
637 base::TimeDelta age;
638 ASSERT_FALSE(parsed->GetAgeValue(&age));
639 }
640
TEST(HttpResponseHeadersTest,GetAgeValueLeadingPlus)641 TEST(HttpResponseHeadersTest, GetAgeValueLeadingPlus) {
642 std::string headers =
643 "HTTP/1.1 200 OK\n"
644 "Age: +10\n";
645 HeadersToRaw(&headers);
646 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
647 base::TimeDelta age;
648 ASSERT_FALSE(parsed->GetAgeValue(&age));
649 }
650
TEST(HttpResponseHeadersTest,GetAgeValueOverflow)651 TEST(HttpResponseHeadersTest, GetAgeValueOverflow) {
652 std::string headers =
653 "HTTP/1.1 200 OK\n"
654 "Age: 999999999999999999999999999999999999999999\n";
655 HeadersToRaw(&headers);
656 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
657 base::TimeDelta age;
658 ASSERT_TRUE(parsed->GetAgeValue(&age));
659
660 // Should have saturated to 2^32 - 1.
661 EXPECT_EQ(static_cast<int64_t>(0xFFFFFFFFL), age.InSeconds());
662 }
663
664 struct ContentTypeTestData {
665 const std::string raw_headers;
666 const std::string mime_type;
667 const bool has_mimetype;
668 const std::string charset;
669 const bool has_charset;
670 const std::string all_content_type;
671 };
672
673 class ContentTypeTest
674 : public HttpResponseHeadersTest,
675 public ::testing::WithParamInterface<ContentTypeTestData> {
676 };
677
TEST_P(ContentTypeTest,GetMimeType)678 TEST_P(ContentTypeTest, GetMimeType) {
679 const ContentTypeTestData test = GetParam();
680
681 std::string headers(test.raw_headers);
682 HeadersToRaw(&headers);
683 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
684
685 std::string value;
686 EXPECT_EQ(test.has_mimetype, parsed->GetMimeType(&value));
687 EXPECT_EQ(test.mime_type, value);
688 value.clear();
689 EXPECT_EQ(test.has_charset, parsed->GetCharset(&value));
690 EXPECT_EQ(test.charset, value);
691 EXPECT_TRUE(parsed->GetNormalizedHeader("content-type", &value));
692 EXPECT_EQ(test.all_content_type, value);
693 }
694
695 // clang-format off
696 const ContentTypeTestData mimetype_tests[] = {
697 { "HTTP/1.1 200 OK\n"
698 "Content-type: text/html\n",
699 "text/html", true,
700 "", false,
701 "text/html" },
702 // Multiple content-type headers should give us the last one.
703 { "HTTP/1.1 200 OK\n"
704 "Content-type: text/html\n"
705 "Content-type: text/html\n",
706 "text/html", true,
707 "", false,
708 "text/html, text/html" },
709 { "HTTP/1.1 200 OK\n"
710 "Content-type: text/plain\n"
711 "Content-type: text/html\n"
712 "Content-type: text/plain\n"
713 "Content-type: text/html\n",
714 "text/html", true,
715 "", false,
716 "text/plain, text/html, text/plain, text/html" },
717 // Test charset parsing.
718 { "HTTP/1.1 200 OK\n"
719 "Content-type: text/html\n"
720 "Content-type: text/html; charset=ISO-8859-1\n",
721 "text/html", true,
722 "iso-8859-1", true,
723 "text/html, text/html; charset=ISO-8859-1" },
724 // Test charset in double quotes.
725 { "HTTP/1.1 200 OK\n"
726 "Content-type: text/html\n"
727 "Content-type: text/html; charset=\"ISO-8859-1\"\n",
728 "text/html", true,
729 "iso-8859-1", true,
730 "text/html, text/html; charset=\"ISO-8859-1\"" },
731 // If there are multiple matching content-type headers, we carry
732 // over the charset value.
733 { "HTTP/1.1 200 OK\n"
734 "Content-type: text/html;charset=utf-8\n"
735 "Content-type: text/html\n",
736 "text/html", true,
737 "utf-8", true,
738 "text/html;charset=utf-8, text/html" },
739 // Regression test for https://crbug.com/772350:
740 // Single quotes are not delimiters but must be treated as part of charset.
741 { "HTTP/1.1 200 OK\n"
742 "Content-type: text/html;charset='utf-8'\n"
743 "Content-type: text/html\n",
744 "text/html", true,
745 "'utf-8'", true,
746 "text/html;charset='utf-8', text/html" },
747 // First charset wins if matching content-type.
748 { "HTTP/1.1 200 OK\n"
749 "Content-type: text/html;charset=utf-8\n"
750 "Content-type: text/html;charset=iso-8859-1\n",
751 "text/html", true,
752 "iso-8859-1", true,
753 "text/html;charset=utf-8, text/html;charset=iso-8859-1" },
754 // Charset is ignored if the content types change.
755 { "HTTP/1.1 200 OK\n"
756 "Content-type: text/plain;charset=utf-8\n"
757 "Content-type: text/html\n",
758 "text/html", true,
759 "", false,
760 "text/plain;charset=utf-8, text/html" },
761 // Empty content-type.
762 { "HTTP/1.1 200 OK\n"
763 "Content-type: \n",
764 "", false,
765 "", false,
766 "" },
767 // Emtpy charset.
768 { "HTTP/1.1 200 OK\n"
769 "Content-type: text/html;charset=\n",
770 "text/html", true,
771 "", false,
772 "text/html;charset=" },
773 // Multiple charsets, first one wins.
774 { "HTTP/1.1 200 OK\n"
775 "Content-type: text/html;charset=utf-8; charset=iso-8859-1\n",
776 "text/html", true,
777 "utf-8", true,
778 "text/html;charset=utf-8; charset=iso-8859-1" },
779 // Multiple params.
780 { "HTTP/1.1 200 OK\n"
781 "Content-type: text/html; foo=utf-8; charset=iso-8859-1\n",
782 "text/html", true,
783 "iso-8859-1", true,
784 "text/html; foo=utf-8; charset=iso-8859-1" },
785 { "HTTP/1.1 200 OK\n"
786 "Content-type: text/html ; charset=utf-8 ; bar=iso-8859-1\n",
787 "text/html", true,
788 "utf-8", true,
789 "text/html ; charset=utf-8 ; bar=iso-8859-1" },
790 // Comma embeded in quotes.
791 { "HTTP/1.1 200 OK\n"
792 "Content-type: text/html ; charset=\"utf-8,text/plain\" ;\n",
793 "text/html", true,
794 "utf-8,text/plain", true,
795 "text/html ; charset=\"utf-8,text/plain\" ;" },
796 // Charset with leading spaces.
797 { "HTTP/1.1 200 OK\n"
798 "Content-type: text/html ; charset= \"utf-8\" ;\n",
799 "text/html", true,
800 "utf-8", true,
801 "text/html ; charset= \"utf-8\" ;" },
802 // Media type comments in mime-type.
803 { "HTTP/1.1 200 OK\n"
804 "Content-type: text/html (html)\n",
805 "text/html", true,
806 "", false,
807 "text/html (html)" },
808 // Incomplete charset= param.
809 { "HTTP/1.1 200 OK\n"
810 "Content-type: text/html; char=\n",
811 "text/html", true,
812 "", false,
813 "text/html; char=" },
814 // Invalid media type: no slash.
815 { "HTTP/1.1 200 OK\n"
816 "Content-type: texthtml\n",
817 "", false,
818 "", false,
819 "texthtml" },
820 // Invalid media type: "*/*".
821 { "HTTP/1.1 200 OK\n"
822 "Content-type: */*\n",
823 "", false,
824 "", false,
825 "*/*" },
826 };
827 // clang-format on
828
829 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
830 ContentTypeTest,
831 testing::ValuesIn(mimetype_tests));
832
833 struct RequiresValidationTestData {
834 const char* headers;
835 ValidationType validation_type;
836 };
837
838 class RequiresValidationTest
839 : public HttpResponseHeadersTest,
840 public ::testing::WithParamInterface<RequiresValidationTestData> {
841 };
842
TEST_P(RequiresValidationTest,RequiresValidation)843 TEST_P(RequiresValidationTest, RequiresValidation) {
844 const RequiresValidationTestData test = GetParam();
845
846 base::Time request_time, response_time, current_time;
847 ASSERT_TRUE(
848 base::Time::FromString("Wed, 28 Nov 2007 00:40:09 GMT", &request_time));
849 ASSERT_TRUE(
850 base::Time::FromString("Wed, 28 Nov 2007 00:40:12 GMT", &response_time));
851 ASSERT_TRUE(
852 base::Time::FromString("Wed, 28 Nov 2007 00:45:20 GMT", ¤t_time));
853
854 std::string headers(test.headers);
855 HeadersToRaw(&headers);
856 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
857
858 ValidationType validation_type =
859 parsed->RequiresValidation(request_time, response_time, current_time);
860 EXPECT_EQ(test.validation_type, validation_type);
861 }
862
863 const struct RequiresValidationTestData requires_validation_tests[] = {
864 // No expiry info: expires immediately.
865 {"HTTP/1.1 200 OK\n"
866 "\n",
867 VALIDATION_SYNCHRONOUS},
868 // No expiry info: expires immediately.
869 {"HTTP/1.1 200 OK\n"
870 "\n",
871 VALIDATION_SYNCHRONOUS},
872 // Valid for a little while.
873 {"HTTP/1.1 200 OK\n"
874 "cache-control: max-age=10000\n"
875 "\n",
876 VALIDATION_NONE},
877 // Expires in the future.
878 {"HTTP/1.1 200 OK\n"
879 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
880 "expires: Wed, 28 Nov 2007 01:00:00 GMT\n"
881 "\n",
882 VALIDATION_NONE},
883 // Already expired.
884 {"HTTP/1.1 200 OK\n"
885 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
886 "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
887 "\n",
888 VALIDATION_SYNCHRONOUS},
889 // Max-age trumps expires.
890 {"HTTP/1.1 200 OK\n"
891 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
892 "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
893 "cache-control: max-age=10000\n"
894 "\n",
895 VALIDATION_NONE},
896 // Last-modified heuristic: modified a while ago.
897 {"HTTP/1.1 200 OK\n"
898 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
899 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
900 "\n",
901 VALIDATION_NONE},
902 {"HTTP/1.1 203 Non-Authoritative Information\n"
903 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
904 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
905 "\n",
906 VALIDATION_NONE},
907 {"HTTP/1.1 206 Partial Content\n"
908 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
909 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
910 "\n",
911 VALIDATION_NONE},
912 // Last-modified heuristic: modified recently.
913 {"HTTP/1.1 200 OK\n"
914 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
915 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
916 "\n",
917 VALIDATION_SYNCHRONOUS},
918 {"HTTP/1.1 203 Non-Authoritative Information\n"
919 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
920 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
921 "\n",
922 VALIDATION_SYNCHRONOUS},
923 {"HTTP/1.1 206 Partial Content\n"
924 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
925 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
926 "\n",
927 VALIDATION_SYNCHRONOUS},
928 // Cached permanent redirect.
929 {"HTTP/1.1 301 Moved Permanently\n"
930 "\n",
931 VALIDATION_NONE},
932 // Another cached permanent redirect.
933 {"HTTP/1.1 308 Permanent Redirect\n"
934 "\n",
935 VALIDATION_NONE},
936 // Cached redirect: not reusable even though by default it would be.
937 {"HTTP/1.1 300 Multiple Choices\n"
938 "Cache-Control: no-cache\n"
939 "\n",
940 VALIDATION_SYNCHRONOUS},
941 // Cached forever by default.
942 {"HTTP/1.1 410 Gone\n"
943 "\n",
944 VALIDATION_NONE},
945 // Cached temporary redirect: not reusable.
946 {"HTTP/1.1 302 Found\n"
947 "\n",
948 VALIDATION_SYNCHRONOUS},
949 // Cached temporary redirect: reusable.
950 {"HTTP/1.1 302 Found\n"
951 "cache-control: max-age=10000\n"
952 "\n",
953 VALIDATION_NONE},
954 // Cache-control: max-age=N overrides expires: date in the past.
955 {"HTTP/1.1 200 OK\n"
956 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
957 "expires: Wed, 28 Nov 2007 00:20:11 GMT\n"
958 "cache-control: max-age=10000\n"
959 "\n",
960 VALIDATION_NONE},
961 // Cache-control: no-store overrides expires: in the future.
962 {"HTTP/1.1 200 OK\n"
963 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
964 "expires: Wed, 29 Nov 2007 00:40:11 GMT\n"
965 "cache-control: no-store,private,no-cache=\"foo\"\n"
966 "\n",
967 VALIDATION_SYNCHRONOUS},
968 // Pragma: no-cache overrides last-modified heuristic.
969 {"HTTP/1.1 200 OK\n"
970 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
971 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
972 "pragma: no-cache\n"
973 "\n",
974 VALIDATION_SYNCHRONOUS},
975 // max-age has expired, needs synchronous revalidation
976 {"HTTP/1.1 200 OK\n"
977 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
978 "cache-control: max-age=300\n"
979 "\n",
980 VALIDATION_SYNCHRONOUS},
981 // max-age has expired, stale-while-revalidate has not, eligible for
982 // asynchronous revalidation
983 {"HTTP/1.1 200 OK\n"
984 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
985 "cache-control: max-age=300, stale-while-revalidate=3600\n"
986 "\n",
987 VALIDATION_ASYNCHRONOUS},
988 // max-age and stale-while-revalidate have expired, needs synchronous
989 // revalidation
990 {"HTTP/1.1 200 OK\n"
991 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
992 "cache-control: max-age=300, stale-while-revalidate=5\n"
993 "\n",
994 VALIDATION_SYNCHRONOUS},
995 // max-age is 0, stale-while-revalidate is large enough to permit
996 // asynchronous revalidation
997 {"HTTP/1.1 200 OK\n"
998 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
999 "cache-control: max-age=0, stale-while-revalidate=360\n"
1000 "\n",
1001 VALIDATION_ASYNCHRONOUS},
1002 // stale-while-revalidate must not override no-cache or similar directives.
1003 {"HTTP/1.1 200 OK\n"
1004 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
1005 "cache-control: no-cache, stale-while-revalidate=360\n"
1006 "\n",
1007 VALIDATION_SYNCHRONOUS},
1008 // max-age has not expired, so no revalidation is needed.
1009 {"HTTP/1.1 200 OK\n"
1010 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
1011 "cache-control: max-age=3600, stale-while-revalidate=3600\n"
1012 "\n",
1013 VALIDATION_NONE},
1014 // must-revalidate overrides stale-while-revalidate, so synchronous
1015 // validation
1016 // is needed.
1017 {"HTTP/1.1 200 OK\n"
1018 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
1019 "cache-control: must-revalidate, max-age=300, "
1020 "stale-while-revalidate=3600\n"
1021 "\n",
1022 VALIDATION_SYNCHRONOUS},
1023
1024 // TODO(darin): Add many many more tests here.
1025 };
1026
1027 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1028 RequiresValidationTest,
1029 testing::ValuesIn(requires_validation_tests));
1030
1031 struct UpdateTestData {
1032 const char* orig_headers;
1033 const char* new_headers;
1034 const char* expected_headers;
1035 };
1036
1037 class UpdateTest
1038 : public HttpResponseHeadersTest,
1039 public ::testing::WithParamInterface<UpdateTestData> {
1040 };
1041
TEST_P(UpdateTest,Update)1042 TEST_P(UpdateTest, Update) {
1043 const UpdateTestData test = GetParam();
1044
1045 std::string orig_headers(test.orig_headers);
1046 HeadersToRaw(&orig_headers);
1047 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
1048
1049 std::string new_headers(test.new_headers);
1050 HeadersToRaw(&new_headers);
1051 auto new_parsed = base::MakeRefCounted<HttpResponseHeaders>(new_headers);
1052
1053 parsed->Update(*new_parsed.get());
1054
1055 EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
1056 }
1057
1058 const UpdateTestData update_tests[] = {
1059 {"HTTP/1.1 200 OK\n",
1060
1061 "HTTP/1/1 304 Not Modified\n"
1062 "connection: keep-alive\n"
1063 "Cache-control: max-age=10000\n",
1064
1065 "HTTP/1.1 200 OK\n"
1066 "Cache-control: max-age=10000\n"},
1067 {"HTTP/1.1 200 OK\n"
1068 "Foo: 1\n"
1069 "Cache-control: private\n",
1070
1071 "HTTP/1/1 304 Not Modified\n"
1072 "connection: keep-alive\n"
1073 "Cache-control: max-age=10000\n",
1074
1075 "HTTP/1.1 200 OK\n"
1076 "Cache-control: max-age=10000\n"
1077 "Foo: 1\n"},
1078 {"HTTP/1.1 200 OK\n"
1079 "Foo: 1\n"
1080 "Cache-control: private\n",
1081
1082 "HTTP/1/1 304 Not Modified\n"
1083 "connection: keep-alive\n"
1084 "Cache-CONTROL: max-age=10000\n",
1085
1086 "HTTP/1.1 200 OK\n"
1087 "Cache-CONTROL: max-age=10000\n"
1088 "Foo: 1\n"},
1089 {"HTTP/1.1 200 OK\n"
1090 "Content-Length: 450\n",
1091
1092 "HTTP/1/1 304 Not Modified\n"
1093 "connection: keep-alive\n"
1094 "Cache-control: max-age=10001 \n",
1095
1096 "HTTP/1.1 200 OK\n"
1097 "Cache-control: max-age=10001\n"
1098 "Content-Length: 450\n"},
1099 {
1100 "HTTP/1.1 200 OK\n"
1101 "X-Frame-Options: DENY\n",
1102
1103 "HTTP/1/1 304 Not Modified\n"
1104 "X-Frame-Options: ALLOW\n",
1105
1106 "HTTP/1.1 200 OK\n"
1107 "X-Frame-Options: DENY\n",
1108 },
1109 {
1110 "HTTP/1.1 200 OK\n"
1111 "X-WebKit-CSP: default-src 'none'\n",
1112
1113 "HTTP/1/1 304 Not Modified\n"
1114 "X-WebKit-CSP: default-src *\n",
1115
1116 "HTTP/1.1 200 OK\n"
1117 "X-WebKit-CSP: default-src 'none'\n",
1118 },
1119 {
1120 "HTTP/1.1 200 OK\n"
1121 "X-XSS-Protection: 1\n",
1122
1123 "HTTP/1/1 304 Not Modified\n"
1124 "X-XSS-Protection: 0\n",
1125
1126 "HTTP/1.1 200 OK\n"
1127 "X-XSS-Protection: 1\n",
1128 },
1129 {"HTTP/1.1 200 OK\n",
1130
1131 "HTTP/1/1 304 Not Modified\n"
1132 "X-Content-Type-Options: nosniff\n",
1133
1134 "HTTP/1.1 200 OK\n"},
1135 {"HTTP/1.1 200 OK\n"
1136 "Content-Encoding: identity\n"
1137 "Content-Length: 100\n"
1138 "Content-Type: text/html\n"
1139 "Content-Security-Policy: default-src 'none'\n",
1140
1141 "HTTP/1/1 304 Not Modified\n"
1142 "Content-Encoding: gzip\n"
1143 "Content-Length: 200\n"
1144 "Content-Type: text/xml\n"
1145 "Content-Security-Policy: default-src 'self'\n",
1146
1147 "HTTP/1.1 200 OK\n"
1148 "Content-Security-Policy: default-src 'self'\n"
1149 "Content-Encoding: identity\n"
1150 "Content-Length: 100\n"
1151 "Content-Type: text/html\n"},
1152 {"HTTP/1.1 200 OK\n"
1153 "Content-Location: /example_page.html\n",
1154
1155 "HTTP/1/1 304 Not Modified\n"
1156 "Content-Location: /not_example_page.html\n",
1157
1158 "HTTP/1.1 200 OK\n"
1159 "Content-Location: /example_page.html\n"},
1160 };
1161
1162 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1163 UpdateTest,
1164 testing::ValuesIn(update_tests));
1165
1166 struct EnumerateHeaderTestData {
1167 const char* headers;
1168 const char* expected_lines;
1169 };
1170
1171 class EnumerateHeaderLinesTest
1172 : public HttpResponseHeadersTest,
1173 public ::testing::WithParamInterface<EnumerateHeaderTestData> {
1174 };
1175
TEST_P(EnumerateHeaderLinesTest,EnumerateHeaderLines)1176 TEST_P(EnumerateHeaderLinesTest, EnumerateHeaderLines) {
1177 const EnumerateHeaderTestData test = GetParam();
1178
1179 std::string headers(test.headers);
1180 HeadersToRaw(&headers);
1181 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1182
1183 std::string name, value, lines;
1184
1185 size_t iter = 0;
1186 while (parsed->EnumerateHeaderLines(&iter, &name, &value)) {
1187 lines.append(name);
1188 lines.append(": ");
1189 lines.append(value);
1190 lines.append("\n");
1191 }
1192
1193 EXPECT_EQ(std::string(test.expected_lines), lines);
1194 }
1195
1196 const EnumerateHeaderTestData enumerate_header_tests[] = {
1197 {"HTTP/1.1 200 OK\n",
1198
1199 ""},
1200 {"HTTP/1.1 200 OK\n"
1201 "Foo: 1\n",
1202
1203 "Foo: 1\n"},
1204 {"HTTP/1.1 200 OK\n"
1205 "Foo: 1\n"
1206 "Bar: 2\n"
1207 "Foo: 3\n",
1208
1209 "Foo: 1\nBar: 2\nFoo: 3\n"},
1210 {"HTTP/1.1 200 OK\n"
1211 "Foo: 1, 2, 3\n",
1212
1213 "Foo: 1, 2, 3\n"},
1214 {"HTTP/1.1 200 OK\n"
1215 "Foo: ,, 1,, 2, 3,, \n",
1216
1217 "Foo: ,, 1,, 2, 3,,\n"},
1218 };
1219
1220 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1221 EnumerateHeaderLinesTest,
1222 testing::ValuesIn(enumerate_header_tests));
1223
1224 struct IsRedirectTestData {
1225 const char* headers;
1226 const char* location;
1227 bool is_redirect;
1228 };
1229
1230 class IsRedirectTest
1231 : public HttpResponseHeadersTest,
1232 public ::testing::WithParamInterface<IsRedirectTestData> {
1233 };
1234
TEST_P(IsRedirectTest,IsRedirect)1235 TEST_P(IsRedirectTest, IsRedirect) {
1236 const IsRedirectTestData test = GetParam();
1237
1238 std::string headers(test.headers);
1239 HeadersToRaw(&headers);
1240 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1241
1242 std::string location;
1243 EXPECT_EQ(parsed->IsRedirect(&location), test.is_redirect);
1244 EXPECT_EQ(location, test.location);
1245 }
1246
1247 const IsRedirectTestData is_redirect_tests[] = {
1248 { "HTTP/1.1 200 OK\n",
1249 "",
1250 false
1251 },
1252 { "HTTP/1.1 301 Moved\n"
1253 "Location: http://foopy/\n",
1254 "http://foopy/",
1255 true
1256 },
1257 { "HTTP/1.1 301 Moved\n"
1258 "Location: \t \n",
1259 "",
1260 false
1261 },
1262 // We use the first location header as the target of the redirect.
1263 { "HTTP/1.1 301 Moved\n"
1264 "Location: http://foo/\n"
1265 "Location: http://bar/\n",
1266 "http://foo/",
1267 true
1268 },
1269 // We use the first _valid_ location header as the target of the redirect.
1270 { "HTTP/1.1 301 Moved\n"
1271 "Location: \n"
1272 "Location: http://bar/\n",
1273 "http://bar/",
1274 true
1275 },
1276 // Bug 1050541 (location header with an unescaped comma).
1277 { "HTTP/1.1 301 Moved\n"
1278 "Location: http://foo/bar,baz.html\n",
1279 "http://foo/bar,baz.html",
1280 true
1281 },
1282 // Bug 1224617 (location header with non-ASCII bytes).
1283 { "HTTP/1.1 301 Moved\n"
1284 "Location: http://foo/bar?key=\xE4\xF6\xFC\n",
1285 "http://foo/bar?key=%E4%F6%FC",
1286 true
1287 },
1288 // Shift_JIS, Big5, and GBK contain multibyte characters with the trailing
1289 // byte falling in the ASCII range.
1290 { "HTTP/1.1 301 Moved\n"
1291 "Location: http://foo/bar?key=\x81\x5E\xD8\xBF\n",
1292 "http://foo/bar?key=%81^%D8%BF",
1293 true
1294 },
1295 { "HTTP/1.1 301 Moved\n"
1296 "Location: http://foo/bar?key=\x82\x40\xBD\xC4\n",
1297 "http://foo/bar?key=%82@%BD%C4",
1298 true
1299 },
1300 { "HTTP/1.1 301 Moved\n"
1301 "Location: http://foo/bar?key=\x83\x5C\x82\x5D\xCB\xD7\n",
1302 "http://foo/bar?key=%83\\%82]%CB%D7",
1303 true
1304 },
1305 };
1306
1307 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1308 IsRedirectTest,
1309 testing::ValuesIn(is_redirect_tests));
1310
1311 struct ContentLengthTestData {
1312 const char* headers;
1313 int64_t expected_len;
1314 };
1315
1316 class GetContentLengthTest
1317 : public HttpResponseHeadersTest,
1318 public ::testing::WithParamInterface<ContentLengthTestData> {
1319 };
1320
TEST_P(GetContentLengthTest,GetContentLength)1321 TEST_P(GetContentLengthTest, GetContentLength) {
1322 const ContentLengthTestData test = GetParam();
1323
1324 std::string headers(test.headers);
1325 HeadersToRaw(&headers);
1326 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1327
1328 EXPECT_EQ(test.expected_len, parsed->GetContentLength());
1329 }
1330
1331 const ContentLengthTestData content_length_tests[] = {
1332 {"HTTP/1.1 200 OK\n", -1},
1333 {"HTTP/1.1 200 OK\n"
1334 "Content-Length: 10\n",
1335 10},
1336 {"HTTP/1.1 200 OK\n"
1337 "Content-Length: \n",
1338 -1},
1339 {"HTTP/1.1 200 OK\n"
1340 "Content-Length: abc\n",
1341 -1},
1342 {"HTTP/1.1 200 OK\n"
1343 "Content-Length: -10\n",
1344 -1},
1345 {"HTTP/1.1 200 OK\n"
1346 "Content-Length: +10\n",
1347 -1},
1348 {"HTTP/1.1 200 OK\n"
1349 "Content-Length: 23xb5\n",
1350 -1},
1351 {"HTTP/1.1 200 OK\n"
1352 "Content-Length: 0xA\n",
1353 -1},
1354 {"HTTP/1.1 200 OK\n"
1355 "Content-Length: 010\n",
1356 10},
1357 // Content-Length too big, will overflow an int64_t.
1358 {"HTTP/1.1 200 OK\n"
1359 "Content-Length: 40000000000000000000\n",
1360 -1},
1361 {"HTTP/1.1 200 OK\n"
1362 "Content-Length: 10\n",
1363 10},
1364 {"HTTP/1.1 200 OK\n"
1365 "Content-Length: 10 \n",
1366 10},
1367 {"HTTP/1.1 200 OK\n"
1368 "Content-Length: \t10\n",
1369 10},
1370 {"HTTP/1.1 200 OK\n"
1371 "Content-Length: \v10\n",
1372 -1},
1373 {"HTTP/1.1 200 OK\n"
1374 "Content-Length: \f10\n",
1375 -1},
1376 {"HTTP/1.1 200 OK\n"
1377 "cOnTeNt-LENgth: 33\n",
1378 33},
1379 {"HTTP/1.1 200 OK\n"
1380 "Content-Length: 34\r\n",
1381 -1},
1382 };
1383
1384 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1385 GetContentLengthTest,
1386 testing::ValuesIn(content_length_tests));
1387
1388 struct ContentRangeTestData {
1389 const char* headers;
1390 bool expected_return_value;
1391 int64_t expected_first_byte_position;
1392 int64_t expected_last_byte_position;
1393 int64_t expected_instance_size;
1394 };
1395
1396 class ContentRangeTest
1397 : public HttpResponseHeadersTest,
1398 public ::testing::WithParamInterface<ContentRangeTestData> {
1399 };
1400
TEST_P(ContentRangeTest,GetContentRangeFor206)1401 TEST_P(ContentRangeTest, GetContentRangeFor206) {
1402 const ContentRangeTestData test = GetParam();
1403
1404 std::string headers(test.headers);
1405 HeadersToRaw(&headers);
1406 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1407
1408 int64_t first_byte_position;
1409 int64_t last_byte_position;
1410 int64_t instance_size;
1411 bool return_value = parsed->GetContentRangeFor206(
1412 &first_byte_position, &last_byte_position, &instance_size);
1413 EXPECT_EQ(test.expected_return_value, return_value);
1414 EXPECT_EQ(test.expected_first_byte_position, first_byte_position);
1415 EXPECT_EQ(test.expected_last_byte_position, last_byte_position);
1416 EXPECT_EQ(test.expected_instance_size, instance_size);
1417 }
1418
1419 const ContentRangeTestData content_range_tests[] = {
1420 {"HTTP/1.1 206 Partial Content", false, -1, -1, -1},
1421 {"HTTP/1.1 206 Partial Content\n"
1422 "Content-Range:",
1423 false, -1, -1, -1},
1424 {"HTTP/1.1 206 Partial Content\n"
1425 "Content-Range: bytes 0-50/51",
1426 true, 0, 50, 51},
1427 {"HTTP/1.1 206 Partial Content\n"
1428 "Content-Range: bytes 50-0/51",
1429 false, -1, -1, -1},
1430 {"HTTP/1.1 416 Requested range not satisfiable\n"
1431 "Content-Range: bytes */*",
1432 false, -1, -1, -1},
1433 {"HTTP/1.1 206 Partial Content\n"
1434 "Content-Range: bytes 0-50/*",
1435 false, -1, -1, -1},
1436 };
1437
1438 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1439 ContentRangeTest,
1440 testing::ValuesIn(content_range_tests));
1441
1442 struct KeepAliveTestData {
1443 const char* headers;
1444 bool expected_keep_alive;
1445 };
1446
1447 // Enable GTest to print KeepAliveTestData in an intelligible way if the test
1448 // fails.
PrintTo(const KeepAliveTestData & keep_alive_test_data,std::ostream * os)1449 void PrintTo(const KeepAliveTestData& keep_alive_test_data,
1450 std::ostream* os) {
1451 *os << "{\"" << keep_alive_test_data.headers << "\", " << std::boolalpha
1452 << keep_alive_test_data.expected_keep_alive << "}";
1453 }
1454
1455 class IsKeepAliveTest
1456 : public HttpResponseHeadersTest,
1457 public ::testing::WithParamInterface<KeepAliveTestData> {
1458 };
1459
TEST_P(IsKeepAliveTest,IsKeepAlive)1460 TEST_P(IsKeepAliveTest, IsKeepAlive) {
1461 const KeepAliveTestData test = GetParam();
1462
1463 std::string headers(test.headers);
1464 HeadersToRaw(&headers);
1465 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1466
1467 EXPECT_EQ(test.expected_keep_alive, parsed->IsKeepAlive());
1468 }
1469
1470 const KeepAliveTestData keepalive_tests[] = {
1471 // The status line fabricated by HttpNetworkTransaction for a 0.9 response.
1472 // Treated as 0.9.
1473 { "HTTP/0.9 200 OK",
1474 false
1475 },
1476 // This could come from a broken server. Treated as 1.0 because it has a
1477 // header.
1478 { "HTTP/0.9 200 OK\n"
1479 "connection: keep-alive\n",
1480 true
1481 },
1482 { "HTTP/1.1 200 OK\n",
1483 true
1484 },
1485 { "HTTP/1.0 200 OK\n",
1486 false
1487 },
1488 { "HTTP/1.0 200 OK\n"
1489 "connection: close\n",
1490 false
1491 },
1492 { "HTTP/1.0 200 OK\n"
1493 "connection: keep-alive\n",
1494 true
1495 },
1496 { "HTTP/1.0 200 OK\n"
1497 "connection: kEeP-AliVe\n",
1498 true
1499 },
1500 { "HTTP/1.0 200 OK\n"
1501 "connection: keep-aliveX\n",
1502 false
1503 },
1504 { "HTTP/1.1 200 OK\n"
1505 "connection: close\n",
1506 false
1507 },
1508 { "HTTP/1.1 200 OK\n"
1509 "connection: keep-alive\n",
1510 true
1511 },
1512 { "HTTP/1.0 200 OK\n"
1513 "proxy-connection: close\n",
1514 false
1515 },
1516 { "HTTP/1.0 200 OK\n"
1517 "proxy-connection: keep-alive\n",
1518 true
1519 },
1520 { "HTTP/1.1 200 OK\n"
1521 "proxy-connection: close\n",
1522 false
1523 },
1524 { "HTTP/1.1 200 OK\n"
1525 "proxy-connection: keep-alive\n",
1526 true
1527 },
1528 { "HTTP/1.1 200 OK\n"
1529 "Connection: Upgrade, close\n",
1530 false
1531 },
1532 { "HTTP/1.1 200 OK\n"
1533 "Connection: Upgrade, keep-alive\n",
1534 true
1535 },
1536 { "HTTP/1.1 200 OK\n"
1537 "Connection: Upgrade\n"
1538 "Connection: close\n",
1539 false
1540 },
1541 { "HTTP/1.1 200 OK\n"
1542 "Connection: Upgrade\n"
1543 "Connection: keep-alive\n",
1544 true
1545 },
1546 { "HTTP/1.1 200 OK\n"
1547 "Connection: close, Upgrade\n",
1548 false
1549 },
1550 { "HTTP/1.1 200 OK\n"
1551 "Connection: keep-alive, Upgrade\n",
1552 true
1553 },
1554 { "HTTP/1.1 200 OK\n"
1555 "Connection: Upgrade\n"
1556 "Proxy-Connection: close\n",
1557 false
1558 },
1559 { "HTTP/1.1 200 OK\n"
1560 "Connection: Upgrade\n"
1561 "Proxy-Connection: keep-alive\n",
1562 true
1563 },
1564 // In situations where the response headers conflict with themselves, use the
1565 // first one for backwards-compatibility.
1566 { "HTTP/1.1 200 OK\n"
1567 "Connection: close\n"
1568 "Connection: keep-alive\n",
1569 false
1570 },
1571 { "HTTP/1.1 200 OK\n"
1572 "Connection: keep-alive\n"
1573 "Connection: close\n",
1574 true
1575 },
1576 { "HTTP/1.0 200 OK\n"
1577 "Connection: close\n"
1578 "Connection: keep-alive\n",
1579 false
1580 },
1581 { "HTTP/1.0 200 OK\n"
1582 "Connection: keep-alive\n"
1583 "Connection: close\n",
1584 true
1585 },
1586 // Ignore the Proxy-Connection header if at all possible.
1587 { "HTTP/1.0 200 OK\n"
1588 "Proxy-Connection: keep-alive\n"
1589 "Connection: close\n",
1590 false
1591 },
1592 { "HTTP/1.1 200 OK\n"
1593 "Proxy-Connection: close\n"
1594 "Connection: keep-alive\n",
1595 true
1596 },
1597 // Older versions of Chrome would have ignored Proxy-Connection in this case,
1598 // but it doesn't seem safe.
1599 { "HTTP/1.1 200 OK\n"
1600 "Proxy-Connection: close\n"
1601 "Connection: Transfer-Encoding\n",
1602 false
1603 },
1604 };
1605
1606 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1607 IsKeepAliveTest,
1608 testing::ValuesIn(keepalive_tests));
1609
1610 struct HasStrongValidatorsTestData {
1611 const char* headers;
1612 bool expected_result;
1613 };
1614
1615 class HasStrongValidatorsTest
1616 : public HttpResponseHeadersTest,
1617 public ::testing::WithParamInterface<HasStrongValidatorsTestData> {
1618 };
1619
TEST_P(HasStrongValidatorsTest,HasStrongValidators)1620 TEST_P(HasStrongValidatorsTest, HasStrongValidators) {
1621 const HasStrongValidatorsTestData test = GetParam();
1622
1623 std::string headers(test.headers);
1624 HeadersToRaw(&headers);
1625 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1626
1627 EXPECT_EQ(test.expected_result, parsed->HasStrongValidators());
1628 }
1629
1630 const HasStrongValidatorsTestData strong_validators_tests[] = {
1631 { "HTTP/0.9 200 OK",
1632 false
1633 },
1634 { "HTTP/1.0 200 OK\n"
1635 "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
1636 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
1637 "ETag: \"foo\"\n",
1638 false
1639 },
1640 { "HTTP/1.1 200 OK\n"
1641 "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
1642 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
1643 "ETag: \"foo\"\n",
1644 true
1645 },
1646 { "HTTP/1.1 200 OK\n"
1647 "Date: Wed, 28 Nov 2007 00:41:10 GMT\n"
1648 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1649 true
1650 },
1651 { "HTTP/1.1 200 OK\n"
1652 "Date: Wed, 28 Nov 2007 00:41:09 GMT\n"
1653 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1654 false
1655 },
1656 { "HTTP/1.1 200 OK\n"
1657 "ETag: \"foo\"\n",
1658 true
1659 },
1660 // This is not really a weak etag:
1661 { "HTTP/1.1 200 OK\n"
1662 "etag: \"w/foo\"\n",
1663 true
1664 },
1665 // This is a weak etag:
1666 { "HTTP/1.1 200 OK\n"
1667 "etag: w/\"foo\"\n",
1668 false
1669 },
1670 { "HTTP/1.1 200 OK\n"
1671 "etag: W / \"foo\"\n",
1672 false
1673 }
1674 };
1675
1676 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1677 HasStrongValidatorsTest,
1678 testing::ValuesIn(strong_validators_tests));
1679
TEST(HttpResponseHeadersTest,HasValidatorsNone)1680 TEST(HttpResponseHeadersTest, HasValidatorsNone) {
1681 std::string headers("HTTP/1.1 200 OK");
1682 HeadersToRaw(&headers);
1683 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1684 EXPECT_FALSE(parsed->HasValidators());
1685 }
1686
TEST(HttpResponseHeadersTest,HasValidatorsEtag)1687 TEST(HttpResponseHeadersTest, HasValidatorsEtag) {
1688 std::string headers(
1689 "HTTP/1.1 200 OK\n"
1690 "etag: \"anything\"");
1691 HeadersToRaw(&headers);
1692 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1693 EXPECT_TRUE(parsed->HasValidators());
1694 }
1695
TEST(HttpResponseHeadersTest,HasValidatorsLastModified)1696 TEST(HttpResponseHeadersTest, HasValidatorsLastModified) {
1697 std::string headers(
1698 "HTTP/1.1 200 OK\n"
1699 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT");
1700 HeadersToRaw(&headers);
1701 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1702 EXPECT_TRUE(parsed->HasValidators());
1703 }
1704
TEST(HttpResponseHeadersTest,HasValidatorsWeakEtag)1705 TEST(HttpResponseHeadersTest, HasValidatorsWeakEtag) {
1706 std::string headers(
1707 "HTTP/1.1 200 OK\n"
1708 "etag: W/\"anything\"");
1709 HeadersToRaw(&headers);
1710 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1711 EXPECT_TRUE(parsed->HasValidators());
1712 }
1713
TEST(HttpResponseHeadersTest,GetNormalizedHeaderWithEmptyValues)1714 TEST(HttpResponseHeadersTest, GetNormalizedHeaderWithEmptyValues) {
1715 std::string headers(
1716 "HTTP/1.1 200 OK\n"
1717 "a:\n"
1718 "b: \n"
1719 "c:*\n"
1720 "d: *\n"
1721 "e: \n"
1722 "a: \n"
1723 "b:*\n"
1724 "c:\n"
1725 "d:*\n"
1726 "a:\n");
1727 HeadersToRaw(&headers);
1728 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1729 std::string value;
1730
1731 EXPECT_TRUE(parsed->GetNormalizedHeader("a", &value));
1732 EXPECT_EQ(value, ", , ");
1733 EXPECT_TRUE(parsed->GetNormalizedHeader("b", &value));
1734 EXPECT_EQ(value, ", *");
1735 EXPECT_TRUE(parsed->GetNormalizedHeader("c", &value));
1736 EXPECT_EQ(value, "*, ");
1737 EXPECT_TRUE(parsed->GetNormalizedHeader("d", &value));
1738 EXPECT_EQ(value, "*, *");
1739 EXPECT_TRUE(parsed->GetNormalizedHeader("e", &value));
1740 EXPECT_EQ(value, "");
1741 EXPECT_FALSE(parsed->GetNormalizedHeader("f", &value));
1742 }
1743
TEST(HttpResponseHeadersTest,GetNormalizedHeaderWithCommas)1744 TEST(HttpResponseHeadersTest, GetNormalizedHeaderWithCommas) {
1745 std::string headers(
1746 "HTTP/1.1 200 OK\n"
1747 "a: foo, bar\n"
1748 "b: , foo, bar,\n"
1749 "c: ,,,\n"
1750 "d: , , , \n"
1751 "e:\t,\t,\t,\t\n"
1752 "a: ,");
1753 HeadersToRaw(&headers);
1754 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1755 std::string value;
1756
1757 // TODO(mmenke): "Normalized" headers probably should preserve the
1758 // leading/trailing whitespace from the original headers.
1759 ASSERT_TRUE(parsed->GetNormalizedHeader("a", &value));
1760 EXPECT_EQ("foo, bar, ,", value);
1761 ASSERT_TRUE(parsed->GetNormalizedHeader("b", &value));
1762 EXPECT_EQ(", foo, bar,", value);
1763 ASSERT_TRUE(parsed->GetNormalizedHeader("c", &value));
1764 EXPECT_EQ(",,,", value);
1765 ASSERT_TRUE(parsed->GetNormalizedHeader("d", &value));
1766 EXPECT_EQ(", , ,", value);
1767 ASSERT_TRUE(parsed->GetNormalizedHeader("e", &value));
1768 EXPECT_EQ(",\t,\t,", value);
1769 EXPECT_FALSE(parsed->GetNormalizedHeader("f", &value));
1770 }
1771
TEST(HttpResponseHeadersTest,AddHeader)1772 TEST(HttpResponseHeadersTest, AddHeader) {
1773 scoped_refptr<HttpResponseHeaders> headers = HttpResponseHeaders::TryToCreate(
1774 "HTTP/1.1 200 OK\n"
1775 "connection: keep-alive\n"
1776 "Cache-control: max-age=10000\n");
1777 ASSERT_TRUE(headers);
1778
1779 headers->AddHeader("Content-Length", "450");
1780 EXPECT_EQ(
1781 "HTTP/1.1 200 OK\n"
1782 "connection: keep-alive\n"
1783 "Cache-control: max-age=10000\n"
1784 "Content-Length: 450\n",
1785 ToSimpleString(headers));
1786
1787 // Add a second Content-Length header with extra spaces in the value. It
1788 // should be added to the end, and the extra spaces removed.
1789 headers->AddHeader("Content-Length", " 42 ");
1790 EXPECT_EQ(
1791 "HTTP/1.1 200 OK\n"
1792 "connection: keep-alive\n"
1793 "Cache-control: max-age=10000\n"
1794 "Content-Length: 450\n"
1795 "Content-Length: 42\n",
1796 ToSimpleString(headers));
1797 }
1798
TEST(HttpResponseHeadersTest,SetHeader)1799 TEST(HttpResponseHeadersTest, SetHeader) {
1800 scoped_refptr<HttpResponseHeaders> headers = HttpResponseHeaders::TryToCreate(
1801 "HTTP/1.1 200 OK\n"
1802 "connection: keep-alive\n"
1803 "Cache-control: max-age=10000\n");
1804 ASSERT_TRUE(headers);
1805
1806 headers->SetHeader("Content-Length", "450");
1807 EXPECT_EQ(
1808 "HTTP/1.1 200 OK\n"
1809 "connection: keep-alive\n"
1810 "Cache-control: max-age=10000\n"
1811 "Content-Length: 450\n",
1812 ToSimpleString(headers));
1813
1814 headers->SetHeader("Content-Length", " 42 ");
1815 EXPECT_EQ(
1816 "HTTP/1.1 200 OK\n"
1817 "connection: keep-alive\n"
1818 "Cache-control: max-age=10000\n"
1819 "Content-Length: 42\n",
1820 ToSimpleString(headers));
1821
1822 headers->SetHeader("connection", "close");
1823 EXPECT_EQ(
1824 "HTTP/1.1 200 OK\n"
1825 "Cache-control: max-age=10000\n"
1826 "Content-Length: 42\n"
1827 "connection: close\n",
1828 ToSimpleString(headers));
1829 }
1830
1831 #if !BUILDFLAG(CRONET_BUILD)
1832 // Cronet disables tracing so this test would fail.
TEST(HttpResponseHeadersTest,TracingSupport)1833 TEST(HttpResponseHeadersTest, TracingSupport) {
1834 scoped_refptr<HttpResponseHeaders> headers = HttpResponseHeaders::TryToCreate(
1835 "HTTP/1.1 200 OK\n"
1836 "connection: keep-alive\n");
1837 ASSERT_TRUE(headers);
1838
1839 EXPECT_EQ(perfetto::TracedValueToString(headers),
1840 "{response_code:200,headers:[{name:connection,value:keep-alive}]}");
1841 }
1842 #endif
1843
1844 struct RemoveHeaderTestData {
1845 const char* orig_headers;
1846 const char* to_remove;
1847 const char* expected_headers;
1848 };
1849
1850 class RemoveHeaderTest
1851 : public HttpResponseHeadersTest,
1852 public ::testing::WithParamInterface<RemoveHeaderTestData> {
1853 };
1854
TEST_P(RemoveHeaderTest,RemoveHeader)1855 TEST_P(RemoveHeaderTest, RemoveHeader) {
1856 const RemoveHeaderTestData test = GetParam();
1857
1858 std::string orig_headers(test.orig_headers);
1859 HeadersToRaw(&orig_headers);
1860 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
1861
1862 std::string name(test.to_remove);
1863 parsed->RemoveHeader(name);
1864
1865 EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
1866 }
1867
1868 const RemoveHeaderTestData remove_header_tests[] = {
1869 { "HTTP/1.1 200 OK\n"
1870 "connection: keep-alive\n"
1871 "Cache-control: max-age=10000\n"
1872 "Content-Length: 450\n",
1873
1874 "Content-Length",
1875
1876 "HTTP/1.1 200 OK\n"
1877 "connection: keep-alive\n"
1878 "Cache-control: max-age=10000\n"
1879 },
1880 { "HTTP/1.1 200 OK\n"
1881 "connection: keep-alive \n"
1882 "Content-Length : 450 \n"
1883 "Cache-control: max-age=10000\n",
1884
1885 "Content-Length",
1886
1887 "HTTP/1.1 200 OK\n"
1888 "connection: keep-alive\n"
1889 "Cache-control: max-age=10000\n"
1890 },
1891 };
1892
1893 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1894 RemoveHeaderTest,
1895 testing::ValuesIn(remove_header_tests));
1896
1897 struct RemoveHeadersTestData {
1898 const char* orig_headers;
1899 const char* to_remove[2];
1900 const char* expected_headers;
1901 };
1902
1903 class RemoveHeadersTest
1904 : public HttpResponseHeadersTest,
1905 public ::testing::WithParamInterface<RemoveHeadersTestData> {};
1906
TEST_P(RemoveHeadersTest,RemoveHeaders)1907 TEST_P(RemoveHeadersTest, RemoveHeaders) {
1908 const RemoveHeadersTestData test = GetParam();
1909
1910 std::string orig_headers(test.orig_headers);
1911 HeadersToRaw(&orig_headers);
1912 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
1913
1914 std::unordered_set<std::string> to_remove;
1915 for (auto* header : test.to_remove) {
1916 if (header)
1917 to_remove.insert(header);
1918 }
1919 parsed->RemoveHeaders(to_remove);
1920
1921 EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
1922 }
1923
1924 const RemoveHeadersTestData remove_headers_tests[] = {
1925 {"HTTP/1.1 200 OK\n"
1926 "connection: keep-alive\n"
1927 "Cache-control: max-age=10000\n"
1928 "Content-Length: 450\n",
1929
1930 {"Content-Length", "CACHE-control"},
1931
1932 "HTTP/1.1 200 OK\n"
1933 "connection: keep-alive\n"},
1934
1935 {"HTTP/1.1 200 OK\n"
1936 "connection: keep-alive\n"
1937 "Content-Length: 450\n",
1938
1939 {"foo", "bar"},
1940
1941 "HTTP/1.1 200 OK\n"
1942 "connection: keep-alive\n"
1943 "Content-Length: 450\n"},
1944
1945 {"HTTP/1.1 404 Kinda not OK\n"
1946 "connection: keep-alive \n",
1947
1948 {},
1949
1950 "HTTP/1.1 404 Kinda not OK\n"
1951 "connection: keep-alive\n"},
1952 };
1953
1954 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1955 RemoveHeadersTest,
1956 testing::ValuesIn(remove_headers_tests));
1957
1958 struct RemoveIndividualHeaderTestData {
1959 const char* orig_headers;
1960 const char* to_remove_name;
1961 const char* to_remove_value;
1962 const char* expected_headers;
1963 };
1964
1965 class RemoveIndividualHeaderTest
1966 : public HttpResponseHeadersTest,
1967 public ::testing::WithParamInterface<RemoveIndividualHeaderTestData> {
1968 };
1969
TEST_P(RemoveIndividualHeaderTest,RemoveIndividualHeader)1970 TEST_P(RemoveIndividualHeaderTest, RemoveIndividualHeader) {
1971 const RemoveIndividualHeaderTestData test = GetParam();
1972
1973 std::string orig_headers(test.orig_headers);
1974 HeadersToRaw(&orig_headers);
1975 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
1976
1977 std::string name(test.to_remove_name);
1978 std::string value(test.to_remove_value);
1979 parsed->RemoveHeaderLine(name, value);
1980
1981 EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
1982 }
1983
1984 const RemoveIndividualHeaderTestData remove_individual_header_tests[] = {
1985 { "HTTP/1.1 200 OK\n"
1986 "connection: keep-alive\n"
1987 "Cache-control: max-age=10000\n"
1988 "Content-Length: 450\n",
1989
1990 "Content-Length",
1991
1992 "450",
1993
1994 "HTTP/1.1 200 OK\n"
1995 "connection: keep-alive\n"
1996 "Cache-control: max-age=10000\n"
1997 },
1998 { "HTTP/1.1 200 OK\n"
1999 "connection: keep-alive \n"
2000 "Content-Length : 450 \n"
2001 "Cache-control: max-age=10000\n",
2002
2003 "Content-Length",
2004
2005 "450",
2006
2007 "HTTP/1.1 200 OK\n"
2008 "connection: keep-alive\n"
2009 "Cache-control: max-age=10000\n"
2010 },
2011 { "HTTP/1.1 200 OK\n"
2012 "connection: keep-alive \n"
2013 "Content-Length: 450\n"
2014 "Cache-control: max-age=10000\n",
2015
2016 "Content-Length", // Matching name.
2017
2018 "999", // Mismatching value.
2019
2020 "HTTP/1.1 200 OK\n"
2021 "connection: keep-alive\n"
2022 "Content-Length: 450\n"
2023 "Cache-control: max-age=10000\n"
2024 },
2025 { "HTTP/1.1 200 OK\n"
2026 "connection: keep-alive \n"
2027 "Foo: bar, baz\n"
2028 "Foo: bar\n"
2029 "Cache-control: max-age=10000\n",
2030
2031 "Foo",
2032
2033 "bar, baz", // Space in value.
2034
2035 "HTTP/1.1 200 OK\n"
2036 "connection: keep-alive\n"
2037 "Foo: bar\n"
2038 "Cache-control: max-age=10000\n"
2039 },
2040 { "HTTP/1.1 200 OK\n"
2041 "connection: keep-alive \n"
2042 "Foo: bar, baz\n"
2043 "Cache-control: max-age=10000\n",
2044
2045 "Foo",
2046
2047 "baz", // Only partial match -> ignored.
2048
2049 "HTTP/1.1 200 OK\n"
2050 "connection: keep-alive\n"
2051 "Foo: bar, baz\n"
2052 "Cache-control: max-age=10000\n"
2053 },
2054 };
2055
2056 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
2057 RemoveIndividualHeaderTest,
2058 testing::ValuesIn(remove_individual_header_tests));
2059
2060 struct ReplaceStatusTestData {
2061 const char* orig_headers;
2062 const char* new_status;
2063 const char* expected_headers;
2064 };
2065
2066 class ReplaceStatusTest
2067 : public HttpResponseHeadersTest,
2068 public ::testing::WithParamInterface<ReplaceStatusTestData> {
2069 };
2070
TEST_P(ReplaceStatusTest,ReplaceStatus)2071 TEST_P(ReplaceStatusTest, ReplaceStatus) {
2072 const ReplaceStatusTestData test = GetParam();
2073
2074 std::string orig_headers(test.orig_headers);
2075 HeadersToRaw(&orig_headers);
2076 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
2077
2078 std::string name(test.new_status);
2079 parsed->ReplaceStatusLine(name);
2080
2081 EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
2082 }
2083
2084 const ReplaceStatusTestData replace_status_tests[] = {
2085 { "HTTP/1.1 206 Partial Content\n"
2086 "connection: keep-alive\n"
2087 "Cache-control: max-age=10000\n"
2088 "Content-Length: 450\n",
2089
2090 "HTTP/1.1 200 OK",
2091
2092 "HTTP/1.1 200 OK\n"
2093 "connection: keep-alive\n"
2094 "Cache-control: max-age=10000\n"
2095 "Content-Length: 450\n"
2096 },
2097 { "HTTP/1.1 200 OK\n"
2098 "connection: keep-alive\n",
2099
2100 "HTTP/1.1 304 Not Modified",
2101
2102 "HTTP/1.1 304 Not Modified\n"
2103 "connection: keep-alive\n"
2104 },
2105 { "HTTP/1.1 200 OK\n"
2106 "connection: keep-alive \n"
2107 "Content-Length : 450 \n"
2108 "Cache-control: max-age=10000\n",
2109
2110 "HTTP/1//1 304 Not Modified",
2111
2112 "HTTP/1.0 304 Not Modified\n"
2113 "connection: keep-alive\n"
2114 "Content-Length: 450\n"
2115 "Cache-control: max-age=10000\n"
2116 },
2117 };
2118
2119 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
2120 ReplaceStatusTest,
2121 testing::ValuesIn(replace_status_tests));
2122
2123 struct UpdateWithNewRangeTestData {
2124 const char* orig_headers;
2125 const char* expected_headers;
2126 const char* expected_headers_with_replaced_status;
2127 };
2128
2129 class UpdateWithNewRangeTest
2130 : public HttpResponseHeadersTest,
2131 public ::testing::WithParamInterface<UpdateWithNewRangeTestData> {
2132 };
2133
TEST_P(UpdateWithNewRangeTest,UpdateWithNewRange)2134 TEST_P(UpdateWithNewRangeTest, UpdateWithNewRange) {
2135 const UpdateWithNewRangeTestData test = GetParam();
2136
2137 const HttpByteRange range = HttpByteRange::Bounded(3, 5);
2138
2139 std::string orig_headers(test.orig_headers);
2140 std::replace(orig_headers.begin(), orig_headers.end(), '\n', '\0');
2141 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers + '\0');
2142 int64_t content_size = parsed->GetContentLength();
2143
2144 // Update headers without replacing status line.
2145 parsed->UpdateWithNewRange(range, content_size, false);
2146 EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
2147
2148 // Replace status line too.
2149 parsed->UpdateWithNewRange(range, content_size, true);
2150 EXPECT_EQ(std::string(test.expected_headers_with_replaced_status),
2151 ToSimpleString(parsed));
2152 }
2153
2154 const UpdateWithNewRangeTestData update_range_tests[] = {
2155 { "HTTP/1.1 200 OK\n"
2156 "Content-Length: 450\n",
2157
2158 "HTTP/1.1 200 OK\n"
2159 "Content-Range: bytes 3-5/450\n"
2160 "Content-Length: 3\n",
2161
2162 "HTTP/1.1 206 Partial Content\n"
2163 "Content-Range: bytes 3-5/450\n"
2164 "Content-Length: 3\n",
2165 },
2166 { "HTTP/1.1 200 OK\n"
2167 "Content-Length: 5\n",
2168
2169 "HTTP/1.1 200 OK\n"
2170 "Content-Range: bytes 3-5/5\n"
2171 "Content-Length: 3\n",
2172
2173 "HTTP/1.1 206 Partial Content\n"
2174 "Content-Range: bytes 3-5/5\n"
2175 "Content-Length: 3\n",
2176 },
2177 };
2178
2179 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
2180 UpdateWithNewRangeTest,
2181 testing::ValuesIn(update_range_tests));
2182
TEST_F(HttpResponseHeadersCacheControlTest,AbsentMaxAgeReturnsFalse)2183 TEST_F(HttpResponseHeadersCacheControlTest, AbsentMaxAgeReturnsFalse) {
2184 InitializeHeadersWithCacheControl("nocache");
2185 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2186 }
2187
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeWithNoParameterRejected)2188 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithNoParameterRejected) {
2189 InitializeHeadersWithCacheControl("max-age=,private");
2190 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2191 }
2192
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeWithSpaceParameterRejected)2193 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithSpaceParameterRejected) {
2194 InitializeHeadersWithCacheControl("max-age= ,private");
2195 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2196 }
2197
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeWithInterimSpaceIsRejected)2198 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithInterimSpaceIsRejected) {
2199 InitializeHeadersWithCacheControl("max-age=1 2");
2200 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2201 }
2202
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeWithMinusSignIsRejected)2203 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithMinusSignIsRejected) {
2204 InitializeHeadersWithCacheControl("max-age=-7");
2205 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2206 }
2207
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeWithSpaceBeforeEqualsIsRejected)2208 TEST_F(HttpResponseHeadersCacheControlTest,
2209 MaxAgeWithSpaceBeforeEqualsIsRejected) {
2210 InitializeHeadersWithCacheControl("max-age = 7");
2211 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2212 }
2213
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeWithLeadingandTrailingSpaces)2214 TEST_F(HttpResponseHeadersCacheControlTest,
2215 MaxAgeWithLeadingandTrailingSpaces) {
2216 InitializeHeadersWithCacheControl("max-age= 7 ");
2217 EXPECT_EQ(base::Seconds(7), GetMaxAgeValue());
2218 }
2219
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeFirstMatchUsed)2220 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeFirstMatchUsed) {
2221 InitializeHeadersWithCacheControl("max-age=10, max-age=20");
2222 EXPECT_EQ(base::Seconds(10), GetMaxAgeValue());
2223 }
2224
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeBogusFirstMatchUsed)2225 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeBogusFirstMatchUsed) {
2226 // "max-age10" isn't parsed as "max-age"; "max-age=now" is bogus and
2227 // ignored and so "max-age=20" is used.
2228 InitializeHeadersWithCacheControl(
2229 "max-age10, max-age=now, max-age=20, max-age=30");
2230 EXPECT_EQ(base::Seconds(20), GetMaxAgeValue());
2231 }
2232
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeCaseInsensitive)2233 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeCaseInsensitive) {
2234 InitializeHeadersWithCacheControl("Max-aGe=15");
2235 EXPECT_EQ(base::Seconds(15), GetMaxAgeValue());
2236 }
2237
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeOverflow)2238 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeOverflow) {
2239 InitializeHeadersWithCacheControl("max-age=99999999999999999999");
2240 EXPECT_EQ(base::TimeDelta::FiniteMax().InSeconds(),
2241 GetMaxAgeValue().InSeconds());
2242 }
2243
2244 struct MaxAgeTestData {
2245 const char* max_age_string;
2246 const absl::optional<int64_t> expected_seconds;
2247 };
2248
2249 class MaxAgeEdgeCasesTest
2250 : public HttpResponseHeadersCacheControlTest,
2251 public ::testing::WithParamInterface<MaxAgeTestData> {
2252 };
2253
TEST_P(MaxAgeEdgeCasesTest,MaxAgeEdgeCases)2254 TEST_P(MaxAgeEdgeCasesTest, MaxAgeEdgeCases) {
2255 const MaxAgeTestData test = GetParam();
2256
2257 std::string max_age = "max-age=";
2258 InitializeHeadersWithCacheControl(
2259 (max_age + test.max_age_string).c_str());
2260 if (test.expected_seconds.has_value()) {
2261 EXPECT_EQ(test.expected_seconds.value(), GetMaxAgeValue().InSeconds())
2262 << " for max-age=" << test.max_age_string;
2263 } else {
2264 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2265 }
2266 }
2267
2268 const MaxAgeTestData max_age_tests[] = {
2269 {" 1 ", 1}, // Spaces are ignored.
2270 {"-1", absl::nullopt},
2271 {"--1", absl::nullopt},
2272 {"2s", absl::nullopt},
2273 {"3 days", absl::nullopt},
2274 {"'4'", absl::nullopt},
2275 {"\"5\"", absl::nullopt},
2276 {"0x6", absl::nullopt}, // Hex not parsed as hex.
2277 {"7F", absl::nullopt}, // Hex without 0x still not parsed as hex.
2278 {"010", 10}, // Octal not parsed as octal.
2279 {"9223372036853", 9223372036853},
2280 {"9223372036854", 9223372036854},
2281 {"9223372036855", 9223372036854},
2282 {"9223372036854775806", 9223372036854},
2283 {"9223372036854775807", 9223372036854},
2284 {"20000000000000000000", 9223372036854}, // Overflow int64_t.
2285 };
2286
2287 INSTANTIATE_TEST_SUITE_P(HttpResponseHeadersCacheControl,
2288 MaxAgeEdgeCasesTest,
2289 testing::ValuesIn(max_age_tests));
2290
TEST_F(HttpResponseHeadersCacheControlTest,AbsentStaleWhileRevalidateReturnsFalse)2291 TEST_F(HttpResponseHeadersCacheControlTest,
2292 AbsentStaleWhileRevalidateReturnsFalse) {
2293 InitializeHeadersWithCacheControl("max-age=3600");
2294 EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
2295 }
2296
TEST_F(HttpResponseHeadersCacheControlTest,StaleWhileRevalidateWithoutValueRejected)2297 TEST_F(HttpResponseHeadersCacheControlTest,
2298 StaleWhileRevalidateWithoutValueRejected) {
2299 InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=");
2300 EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
2301 }
2302
TEST_F(HttpResponseHeadersCacheControlTest,StaleWhileRevalidateWithInvalidValueIgnored)2303 TEST_F(HttpResponseHeadersCacheControlTest,
2304 StaleWhileRevalidateWithInvalidValueIgnored) {
2305 InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=true");
2306 EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
2307 }
2308
TEST_F(HttpResponseHeadersCacheControlTest,StaleWhileRevalidateValueReturned)2309 TEST_F(HttpResponseHeadersCacheControlTest, StaleWhileRevalidateValueReturned) {
2310 InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=7200");
2311 EXPECT_EQ(base::Seconds(7200), GetStaleWhileRevalidateValue());
2312 }
2313
TEST_F(HttpResponseHeadersCacheControlTest,FirstStaleWhileRevalidateValueUsed)2314 TEST_F(HttpResponseHeadersCacheControlTest,
2315 FirstStaleWhileRevalidateValueUsed) {
2316 InitializeHeadersWithCacheControl(
2317 "stale-while-revalidate=1,stale-while-revalidate=7200");
2318 EXPECT_EQ(base::Seconds(1), GetStaleWhileRevalidateValue());
2319 }
2320
2321 struct GetCurrentAgeTestData {
2322 const char* headers;
2323 const char* request_time;
2324 const char* response_time;
2325 const char* current_time;
2326 const int expected_age;
2327 };
2328
2329 class GetCurrentAgeTest
2330 : public HttpResponseHeadersTest,
2331 public ::testing::WithParamInterface<GetCurrentAgeTestData> {
2332 };
2333
TEST_P(GetCurrentAgeTest,GetCurrentAge)2334 TEST_P(GetCurrentAgeTest, GetCurrentAge) {
2335 const GetCurrentAgeTestData test = GetParam();
2336
2337 base::Time request_time, response_time, current_time;
2338 ASSERT_TRUE(base::Time::FromString(test.request_time, &request_time));
2339 ASSERT_TRUE(base::Time::FromString(test.response_time, &response_time));
2340 ASSERT_TRUE(base::Time::FromString(test.current_time, ¤t_time));
2341
2342 std::string headers(test.headers);
2343 HeadersToRaw(&headers);
2344 auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
2345
2346 base::TimeDelta age =
2347 parsed->GetCurrentAge(request_time, response_time, current_time);
2348 EXPECT_EQ(test.expected_age, age.InSeconds());
2349 }
2350
2351 const struct GetCurrentAgeTestData get_current_age_tests[] = {
2352 // Without Date header.
2353 {"HTTP/1.1 200 OK\n"
2354 "Age: 2",
2355 "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
2356 "Fri, 20 Jan 2011 10:40:14 GMT", 8},
2357 // Without Age header.
2358 {"HTTP/1.1 200 OK\n"
2359 "Date: Fri, 20 Jan 2011 10:40:10 GMT\n",
2360 "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
2361 "Fri, 20 Jan 2011 10:40:14 GMT", 6},
2362 // date_value > response_time with Age header.
2363 {"HTTP/1.1 200 OK\n"
2364 "Date: Fri, 20 Jan 2011 10:40:14 GMT\n"
2365 "Age: 2\n",
2366 "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
2367 "Fri, 20 Jan 2011 10:40:14 GMT", 8},
2368 // date_value > response_time without Age header.
2369 {"HTTP/1.1 200 OK\n"
2370 "Date: Fri, 20 Jan 2011 10:40:14 GMT\n",
2371 "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
2372 "Fri, 20 Jan 2011 10:40:14 GMT", 6},
2373 // apparent_age > corrected_age_value
2374 {"HTTP/1.1 200 OK\n"
2375 "Date: Fri, 20 Jan 2011 10:40:07 GMT\n"
2376 "Age: 0\n",
2377 "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
2378 "Fri, 20 Jan 2011 10:40:14 GMT", 7}};
2379
2380 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
2381 GetCurrentAgeTest,
2382 testing::ValuesIn(get_current_age_tests));
2383
2384 } // namespace
2385
2386 } // namespace net
2387