Include all dupe types (event when value is zero) in scan stats.
[chromium-blink-merge.git] / net / http / http_response_headers_unittest.cc
blob0184885dcd31018cd6c4006ba811cd2d01390d2a
1 // Copyright (c) 2012 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.
5 #include <algorithm>
6 #include <iostream>
7 #include <limits>
9 #include "base/basictypes.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/pickle.h"
12 #include "base/time/time.h"
13 #include "base/values.h"
14 #include "net/http/http_byte_range.h"
15 #include "net/http/http_response_headers.h"
16 #include "testing/gtest/include/gtest/gtest.h"
18 namespace net {
20 namespace {
22 struct TestData {
23 const char* raw_headers;
24 const char* expected_headers;
25 int expected_response_code;
26 HttpVersion expected_parsed_version;
27 HttpVersion expected_version;
30 class HttpResponseHeadersTest : public testing::Test {
33 // Transform "normal"-looking headers (\n-separated) to the appropriate
34 // input format for ParseRawHeaders (\0-separated).
35 void HeadersToRaw(std::string* headers) {
36 std::replace(headers->begin(), headers->end(), '\n', '\0');
37 if (!headers->empty())
38 *headers += '\0';
41 class HttpResponseHeadersCacheControlTest : public HttpResponseHeadersTest {
42 protected:
43 // Make tests less verbose.
44 typedef base::TimeDelta TimeDelta;
46 // Initilise the headers() value with a Cache-Control header set to
47 // |cache_control|. |cache_control| is copied and so can safely be a
48 // temporary.
49 void InitializeHeadersWithCacheControl(const char* cache_control) {
50 std::string raw_headers("HTTP/1.1 200 OK\n");
51 raw_headers += "Cache-Control: ";
52 raw_headers += cache_control;
53 raw_headers += "\n";
54 HeadersToRaw(&raw_headers);
55 headers_ = new HttpResponseHeaders(raw_headers);
58 const scoped_refptr<HttpResponseHeaders>& headers() { return headers_; }
60 // Return a pointer to a TimeDelta object. For use when the value doesn't
61 // matter.
62 TimeDelta* TimeDeltaPointer() { return &delta_; }
64 // Get the max-age value. This should only be used in tests where a valid
65 // max-age parameter is expected to be present.
66 TimeDelta GetMaxAgeValue() {
67 DCHECK(headers_.get()) << "Call InitializeHeadersWithCacheControl() first";
68 TimeDelta max_age_value;
69 EXPECT_TRUE(headers()->GetMaxAgeValue(&max_age_value));
70 return max_age_value;
73 // Get the stale-while-revalidate value. This should only be used in tests
74 // where a valid max-age parameter is expected to be present.
75 TimeDelta GetStaleWhileRevalidateValue() {
76 DCHECK(headers_.get()) << "Call InitializeHeadersWithCacheControl() first";
77 TimeDelta stale_while_revalidate_value;
78 EXPECT_TRUE(
79 headers()->GetStaleWhileRevalidateValue(&stale_while_revalidate_value));
80 return stale_while_revalidate_value;
83 private:
84 scoped_refptr<HttpResponseHeaders> headers_;
85 TimeDelta delta_;
88 class CommonHttpResponseHeadersTest
89 : public HttpResponseHeadersTest,
90 public ::testing::WithParamInterface<TestData> {
93 TEST_P(CommonHttpResponseHeadersTest, TestCommon) {
94 const TestData test = GetParam();
96 std::string raw_headers(test.raw_headers);
97 HeadersToRaw(&raw_headers);
98 std::string expected_headers(test.expected_headers);
100 std::string headers;
101 scoped_refptr<HttpResponseHeaders> parsed(
102 new HttpResponseHeaders(raw_headers));
103 parsed->GetNormalizedHeaders(&headers);
105 // Transform to readable output format (so it's easier to see diffs).
106 std::replace(headers.begin(), headers.end(), ' ', '_');
107 std::replace(headers.begin(), headers.end(), '\n', '\\');
108 std::replace(expected_headers.begin(), expected_headers.end(), ' ', '_');
109 std::replace(expected_headers.begin(), expected_headers.end(), '\n', '\\');
111 EXPECT_EQ(expected_headers, headers);
113 EXPECT_EQ(test.expected_response_code, parsed->response_code());
115 EXPECT_TRUE(test.expected_parsed_version == parsed->GetParsedHttpVersion());
116 EXPECT_TRUE(test.expected_version == parsed->GetHttpVersion());
119 TestData response_headers_tests[] = {
120 {// Normalise whitespace.
122 "HTTP/1.1 202 Accepted \n"
123 "Content-TYPE : text/html; charset=utf-8 \n"
124 "Set-Cookie: a \n"
125 "Set-Cookie: b \n",
127 "HTTP/1.1 202 Accepted\n"
128 "Content-TYPE: text/html; charset=utf-8\n"
129 "Set-Cookie: a, b\n",
131 202,
132 HttpVersion(1, 1),
133 HttpVersion(1, 1)},
134 {// Normalize leading whitespace.
136 "HTTP/1.1 202 Accepted \n"
137 // Starts with space -- will be skipped as invalid.
138 " Content-TYPE : text/html; charset=utf-8 \n"
139 "Set-Cookie: a \n"
140 "Set-Cookie: b \n",
142 "HTTP/1.1 202 Accepted\n"
143 "Set-Cookie: a, b\n",
145 202,
146 HttpVersion(1, 1),
147 HttpVersion(1, 1)},
148 {// Normalize blank headers.
150 "HTTP/1.1 200 OK\n"
151 "Header1 : \n"
152 "Header2: \n"
153 "Header3:\n"
154 "Header4\n"
155 "Header5 :\n",
157 "HTTP/1.1 200 OK\n"
158 "Header1: \n"
159 "Header2: \n"
160 "Header3: \n"
161 "Header5: \n",
163 200,
164 HttpVersion(1, 1),
165 HttpVersion(1, 1)},
166 {// Don't believe the http/0.9 version if there are headers!
168 "hTtP/0.9 201\n"
169 "Content-TYPE: text/html; charset=utf-8\n",
171 "HTTP/1.0 201 OK\n"
172 "Content-TYPE: text/html; charset=utf-8\n",
174 201,
175 HttpVersion(0, 9),
176 HttpVersion(1, 0)},
177 {// Accept the HTTP/0.9 version number if there are no headers.
178 // This is how HTTP/0.9 responses get constructed from
179 // HttpNetworkTransaction.
181 "hTtP/0.9 200 OK\n",
183 "HTTP/0.9 200 OK\n",
185 200,
186 HttpVersion(0, 9),
187 HttpVersion(0, 9)},
188 {// Add missing OK.
190 "HTTP/1.1 201\n"
191 "Content-TYPE: text/html; charset=utf-8\n",
193 "HTTP/1.1 201 OK\n"
194 "Content-TYPE: text/html; charset=utf-8\n",
196 201,
197 HttpVersion(1, 1),
198 HttpVersion(1, 1)},
199 {// Normalize bad status line.
201 "SCREWED_UP_STATUS_LINE\n"
202 "Content-TYPE: text/html; charset=utf-8\n",
204 "HTTP/1.0 200 OK\n"
205 "Content-TYPE: text/html; charset=utf-8\n",
207 200,
208 HttpVersion(0, 0), // Parse error.
209 HttpVersion(1, 0)},
210 {// Normalize invalid status code.
212 "HTTP/1.1 -1 Unknown\n",
214 "HTTP/1.1 200 OK\n",
216 200,
217 HttpVersion(1, 1),
218 HttpVersion(1, 1)},
219 {// Normalize empty header.
223 "HTTP/1.0 200 OK\n",
225 200,
226 HttpVersion(0, 0), // Parse Error.
227 HttpVersion(1, 0)},
228 {// Normalize headers that start with a colon.
230 "HTTP/1.1 202 Accepted \n"
231 "foo: bar\n"
232 ": a \n"
233 " : b\n"
234 "baz: blat \n",
236 "HTTP/1.1 202 Accepted\n"
237 "foo: bar\n"
238 "baz: blat\n",
240 202,
241 HttpVersion(1, 1),
242 HttpVersion(1, 1)},
243 {// Normalize headers that end with a colon.
245 "HTTP/1.1 202 Accepted \n"
246 "foo: \n"
247 "bar:\n"
248 "baz: blat \n"
249 "zip:\n",
251 "HTTP/1.1 202 Accepted\n"
252 "foo: \n"
253 "bar: \n"
254 "baz: blat\n"
255 "zip: \n",
257 202,
258 HttpVersion(1, 1),
259 HttpVersion(1, 1)},
260 {// Normalize whitespace headers.
262 "\n \n",
264 "HTTP/1.0 200 OK\n",
266 200,
267 HttpVersion(0, 0), // Parse error.
268 HttpVersion(1, 0)},
269 {// Consolidate Set-Cookie headers.
271 "HTTP/1.1 200 OK\n"
272 "Set-Cookie: x=1\n"
273 "Set-Cookie: y=2\n",
275 "HTTP/1.1 200 OK\n"
276 "Set-Cookie: x=1, y=2\n",
278 200,
279 HttpVersion(1, 1),
280 HttpVersion(1, 1)},
283 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
284 CommonHttpResponseHeadersTest,
285 testing::ValuesIn(response_headers_tests));
287 TEST(HttpResponseHeadersTest, GetNormalizedHeader) {
288 std::string headers =
289 "HTTP/1.1 200 OK\n"
290 "Cache-control: private\n"
291 "cache-Control: no-store\n";
292 HeadersToRaw(&headers);
293 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
295 std::string value;
296 EXPECT_TRUE(parsed->GetNormalizedHeader("cache-control", &value));
297 EXPECT_EQ("private, no-store", value);
300 struct PersistData {
301 HttpResponseHeaders::PersistOptions options;
302 const char* raw_headers;
303 const char* expected_headers;
306 class PersistenceTest
307 : public HttpResponseHeadersTest,
308 public ::testing::WithParamInterface<PersistData> {
311 TEST_P(PersistenceTest, Persist) {
312 const PersistData test = GetParam();
314 std::string headers = test.raw_headers;
315 HeadersToRaw(&headers);
316 scoped_refptr<HttpResponseHeaders> parsed1(new HttpResponseHeaders(headers));
318 base::Pickle pickle;
319 parsed1->Persist(&pickle, test.options);
321 base::PickleIterator iter(pickle);
322 scoped_refptr<HttpResponseHeaders> parsed2(new HttpResponseHeaders(&iter));
324 std::string h2;
325 parsed2->GetNormalizedHeaders(&h2);
326 EXPECT_EQ(std::string(test.expected_headers), h2);
329 const struct PersistData persistence_tests[] = {
330 {HttpResponseHeaders::PERSIST_ALL,
331 "HTTP/1.1 200 OK\n"
332 "Cache-control:private\n"
333 "cache-Control:no-store\n",
335 "HTTP/1.1 200 OK\n"
336 "Cache-control: private, no-store\n"},
337 {HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
338 "HTTP/1.1 200 OK\n"
339 "connection: keep-alive\n"
340 "server: blah\n",
342 "HTTP/1.1 200 OK\n"
343 "server: blah\n"},
344 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE |
345 HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
346 "HTTP/1.1 200 OK\n"
347 "fOo: 1\n"
348 "Foo: 2\n"
349 "Transfer-Encoding: chunked\n"
350 "CoNnection: keep-alive\n"
351 "cache-control: private, no-cache=\"foo\"\n",
353 "HTTP/1.1 200 OK\n"
354 "cache-control: private, no-cache=\"foo\"\n"},
355 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
356 "HTTP/1.1 200 OK\n"
357 "Foo: 2\n"
358 "Cache-Control: private,no-cache=\"foo, bar\"\n"
359 "bar",
361 "HTTP/1.1 200 OK\n"
362 "Cache-Control: private,no-cache=\"foo, bar\"\n"},
363 // Ignore bogus no-cache value.
364 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
365 "HTTP/1.1 200 OK\n"
366 "Foo: 2\n"
367 "Cache-Control: private,no-cache=foo\n",
369 "HTTP/1.1 200 OK\n"
370 "Foo: 2\n"
371 "Cache-Control: private,no-cache=foo\n"},
372 // Ignore bogus no-cache value.
373 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
374 "HTTP/1.1 200 OK\n"
375 "Foo: 2\n"
376 "Cache-Control: private, no-cache=\n",
378 "HTTP/1.1 200 OK\n"
379 "Foo: 2\n"
380 "Cache-Control: private, no-cache=\n"},
381 // Ignore empty no-cache value.
382 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
383 "HTTP/1.1 200 OK\n"
384 "Foo: 2\n"
385 "Cache-Control: private, no-cache=\"\"\n",
387 "HTTP/1.1 200 OK\n"
388 "Foo: 2\n"
389 "Cache-Control: private, no-cache=\"\"\n"},
390 // Ignore wrong quotes no-cache value.
391 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
392 "HTTP/1.1 200 OK\n"
393 "Foo: 2\n"
394 "Cache-Control: private, no-cache=\'foo\'\n",
396 "HTTP/1.1 200 OK\n"
397 "Foo: 2\n"
398 "Cache-Control: private, no-cache=\'foo\'\n"},
399 // Ignore unterminated quotes no-cache value.
400 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
401 "HTTP/1.1 200 OK\n"
402 "Foo: 2\n"
403 "Cache-Control: private, no-cache=\"foo\n",
405 "HTTP/1.1 200 OK\n"
406 "Foo: 2\n"
407 "Cache-Control: private, no-cache=\"foo\n"},
408 // Accept sloppy LWS.
409 {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
410 "HTTP/1.1 200 OK\n"
411 "Foo: 2\n"
412 "Cache-Control: private, no-cache=\" foo\t, bar\"\n",
414 "HTTP/1.1 200 OK\n"
415 "Cache-Control: private, no-cache=\" foo\t, bar\"\n"},
416 // Header name appears twice, separated by another header.
417 {HttpResponseHeaders::PERSIST_ALL,
418 "HTTP/1.1 200 OK\n"
419 "Foo: 1\n"
420 "Bar: 2\n"
421 "Foo: 3\n",
423 "HTTP/1.1 200 OK\n"
424 "Foo: 1, 3\n"
425 "Bar: 2\n"},
426 // Header name appears twice, separated by another header (type 2).
427 {HttpResponseHeaders::PERSIST_ALL,
428 "HTTP/1.1 200 OK\n"
429 "Foo: 1, 3\n"
430 "Bar: 2\n"
431 "Foo: 4\n",
433 "HTTP/1.1 200 OK\n"
434 "Foo: 1, 3, 4\n"
435 "Bar: 2\n"},
436 // Test filtering of cookie headers.
437 {HttpResponseHeaders::PERSIST_SANS_COOKIES,
438 "HTTP/1.1 200 OK\n"
439 "Set-Cookie: foo=bar; httponly\n"
440 "Set-Cookie: bar=foo\n"
441 "Bar: 1\n"
442 "Set-Cookie2: bar2=foo2\n",
444 "HTTP/1.1 200 OK\n"
445 "Bar: 1\n"},
446 // Test LWS at the end of a header.
447 {HttpResponseHeaders::PERSIST_ALL,
448 "HTTP/1.1 200 OK\n"
449 "Content-Length: 450 \n"
450 "Content-Encoding: gzip\n",
452 "HTTP/1.1 200 OK\n"
453 "Content-Length: 450\n"
454 "Content-Encoding: gzip\n"},
455 // Test LWS at the end of a header.
456 {HttpResponseHeaders::PERSIST_RAW,
457 "HTTP/1.1 200 OK\n"
458 "Content-Length: 450 \n"
459 "Content-Encoding: gzip\n",
461 "HTTP/1.1 200 OK\n"
462 "Content-Length: 450\n"
463 "Content-Encoding: gzip\n"},
464 // Test filtering of transport security state headers.
465 {HttpResponseHeaders::PERSIST_SANS_SECURITY_STATE,
466 "HTTP/1.1 200 OK\n"
467 "Strict-Transport-Security: max-age=1576800\n"
468 "Bar: 1\n"
469 "Public-Key-Pins: max-age=100000; "
470 "pin-sha1=\"ObT42aoSpAqWdY9WfRfL7i0HsVk=\";"
471 "pin-sha1=\"7kW49EVwZG0hSNx41ZO/fUPN0ek=\"",
473 "HTTP/1.1 200 OK\n"
474 "Bar: 1\n"},
477 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
478 PersistenceTest,
479 testing::ValuesIn(persistence_tests));
481 TEST(HttpResponseHeadersTest, EnumerateHeader_Coalesced) {
482 // Ensure that commas in quoted strings are not regarded as value separators.
483 // Ensure that whitespace following a value is trimmed properly.
484 std::string headers =
485 "HTTP/1.1 200 OK\n"
486 "Cache-control:private , no-cache=\"set-cookie,server\" \n"
487 "cache-Control: no-store\n";
488 HeadersToRaw(&headers);
489 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
491 void* iter = NULL;
492 std::string value;
493 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
494 EXPECT_EQ("private", value);
495 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
496 EXPECT_EQ("no-cache=\"set-cookie,server\"", value);
497 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
498 EXPECT_EQ("no-store", value);
499 EXPECT_FALSE(parsed->EnumerateHeader(&iter, "cache-control", &value));
502 TEST(HttpResponseHeadersTest, EnumerateHeader_Challenge) {
503 // Even though WWW-Authenticate has commas, it should not be treated as
504 // coalesced values.
505 std::string headers =
506 "HTTP/1.1 401 OK\n"
507 "WWW-Authenticate:Digest realm=foobar, nonce=x, domain=y\n"
508 "WWW-Authenticate:Basic realm=quatar\n";
509 HeadersToRaw(&headers);
510 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
512 void* iter = NULL;
513 std::string value;
514 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
515 EXPECT_EQ("Digest realm=foobar, nonce=x, domain=y", value);
516 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
517 EXPECT_EQ("Basic realm=quatar", value);
518 EXPECT_FALSE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
521 TEST(HttpResponseHeadersTest, EnumerateHeader_DateValued) {
522 // The comma in a date valued header should not be treated as a
523 // field-value separator.
524 std::string headers =
525 "HTTP/1.1 200 OK\n"
526 "Date: Tue, 07 Aug 2007 23:10:55 GMT\n"
527 "Last-Modified: Wed, 01 Aug 2007 23:23:45 GMT\n";
528 HeadersToRaw(&headers);
529 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
531 std::string value;
532 EXPECT_TRUE(parsed->EnumerateHeader(NULL, "date", &value));
533 EXPECT_EQ("Tue, 07 Aug 2007 23:10:55 GMT", value);
534 EXPECT_TRUE(parsed->EnumerateHeader(NULL, "last-modified", &value));
535 EXPECT_EQ("Wed, 01 Aug 2007 23:23:45 GMT", value);
538 TEST(HttpResponseHeadersTest, DefaultDateToGMT) {
539 // Verify we make the best interpretation when parsing dates that incorrectly
540 // do not end in "GMT" as RFC2616 requires.
541 std::string headers =
542 "HTTP/1.1 200 OK\n"
543 "Date: Tue, 07 Aug 2007 23:10:55\n"
544 "Last-Modified: Tue, 07 Aug 2007 19:10:55 EDT\n"
545 "Expires: Tue, 07 Aug 2007 23:10:55 UTC\n";
546 HeadersToRaw(&headers);
547 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
548 base::Time expected_value;
549 ASSERT_TRUE(base::Time::FromString("Tue, 07 Aug 2007 23:10:55 GMT",
550 &expected_value));
552 base::Time value;
553 // When the timezone is missing, GMT is a good guess as its what RFC2616
554 // requires.
555 EXPECT_TRUE(parsed->GetDateValue(&value));
556 EXPECT_EQ(expected_value, value);
557 // If GMT is missing but an RFC822-conforming one is present, use that.
558 EXPECT_TRUE(parsed->GetLastModifiedValue(&value));
559 EXPECT_EQ(expected_value, value);
560 // If an unknown timezone is present, treat like a missing timezone and
561 // default to GMT. The only example of a web server not specifying "GMT"
562 // used "UTC" which is equivalent to GMT.
563 if (parsed->GetExpiresValue(&value))
564 EXPECT_EQ(expected_value, value);
567 struct ContentTypeTestData {
568 const std::string raw_headers;
569 const std::string mime_type;
570 const bool has_mimetype;
571 const std::string charset;
572 const bool has_charset;
573 const std::string all_content_type;
576 class ContentTypeTest
577 : public HttpResponseHeadersTest,
578 public ::testing::WithParamInterface<ContentTypeTestData> {
581 TEST_P(ContentTypeTest, GetMimeType) {
582 const ContentTypeTestData test = GetParam();
584 std::string headers(test.raw_headers);
585 HeadersToRaw(&headers);
586 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
588 std::string value;
589 EXPECT_EQ(test.has_mimetype, parsed->GetMimeType(&value));
590 EXPECT_EQ(test.mime_type, value);
591 value.clear();
592 EXPECT_EQ(test.has_charset, parsed->GetCharset(&value));
593 EXPECT_EQ(test.charset, value);
594 EXPECT_TRUE(parsed->GetNormalizedHeader("content-type", &value));
595 EXPECT_EQ(test.all_content_type, value);
598 const ContentTypeTestData mimetype_tests[] = {
599 { "HTTP/1.1 200 OK\n"
600 "Content-type: text/html\n",
601 "text/html", true,
602 "", false,
603 "text/html" },
604 // Multiple content-type headers should give us the last one.
605 { "HTTP/1.1 200 OK\n"
606 "Content-type: text/html\n"
607 "Content-type: text/html\n",
608 "text/html", true,
609 "", false,
610 "text/html, text/html" },
611 { "HTTP/1.1 200 OK\n"
612 "Content-type: text/plain\n"
613 "Content-type: text/html\n"
614 "Content-type: text/plain\n"
615 "Content-type: text/html\n",
616 "text/html", true,
617 "", false,
618 "text/plain, text/html, text/plain, text/html" },
619 // Test charset parsing.
620 { "HTTP/1.1 200 OK\n"
621 "Content-type: text/html\n"
622 "Content-type: text/html; charset=ISO-8859-1\n",
623 "text/html", true,
624 "iso-8859-1", true,
625 "text/html, text/html; charset=ISO-8859-1" },
626 // Test charset in double quotes.
627 { "HTTP/1.1 200 OK\n"
628 "Content-type: text/html\n"
629 "Content-type: text/html; charset=\"ISO-8859-1\"\n",
630 "text/html", true,
631 "iso-8859-1", true,
632 "text/html, text/html; charset=\"ISO-8859-1\"" },
633 // If there are multiple matching content-type headers, we carry
634 // over the charset value.
635 { "HTTP/1.1 200 OK\n"
636 "Content-type: text/html;charset=utf-8\n"
637 "Content-type: text/html\n",
638 "text/html", true,
639 "utf-8", true,
640 "text/html;charset=utf-8, text/html" },
641 // Test single quotes.
642 { "HTTP/1.1 200 OK\n"
643 "Content-type: text/html;charset='utf-8'\n"
644 "Content-type: text/html\n",
645 "text/html", true,
646 "utf-8", true,
647 "text/html;charset='utf-8', text/html" },
648 // Last charset wins if matching content-type.
649 { "HTTP/1.1 200 OK\n"
650 "Content-type: text/html;charset=utf-8\n"
651 "Content-type: text/html;charset=iso-8859-1\n",
652 "text/html", true,
653 "iso-8859-1", true,
654 "text/html;charset=utf-8, text/html;charset=iso-8859-1" },
655 // Charset is ignored if the content types change.
656 { "HTTP/1.1 200 OK\n"
657 "Content-type: text/plain;charset=utf-8\n"
658 "Content-type: text/html\n",
659 "text/html", true,
660 "", false,
661 "text/plain;charset=utf-8, text/html" },
662 // Empty content-type.
663 { "HTTP/1.1 200 OK\n"
664 "Content-type: \n",
665 "", false,
666 "", false,
667 "" },
668 // Emtpy charset.
669 { "HTTP/1.1 200 OK\n"
670 "Content-type: text/html;charset=\n",
671 "text/html", true,
672 "", false,
673 "text/html;charset=" },
674 // Multiple charsets, last one wins.
675 { "HTTP/1.1 200 OK\n"
676 "Content-type: text/html;charset=utf-8; charset=iso-8859-1\n",
677 "text/html", true,
678 "iso-8859-1", true,
679 "text/html;charset=utf-8; charset=iso-8859-1" },
680 // Multiple params.
681 { "HTTP/1.1 200 OK\n"
682 "Content-type: text/html; foo=utf-8; charset=iso-8859-1\n",
683 "text/html", true,
684 "iso-8859-1", true,
685 "text/html; foo=utf-8; charset=iso-8859-1" },
686 { "HTTP/1.1 200 OK\n"
687 "Content-type: text/html ; charset=utf-8 ; bar=iso-8859-1\n",
688 "text/html", true,
689 "utf-8", true,
690 "text/html ; charset=utf-8 ; bar=iso-8859-1" },
691 // Comma embeded in quotes.
692 { "HTTP/1.1 200 OK\n"
693 "Content-type: text/html ; charset='utf-8,text/plain' ;\n",
694 "text/html", true,
695 "utf-8,text/plain", true,
696 "text/html ; charset='utf-8,text/plain' ;" },
697 // Charset with leading spaces.
698 { "HTTP/1.1 200 OK\n"
699 "Content-type: text/html ; charset= 'utf-8' ;\n",
700 "text/html", true,
701 "utf-8", true,
702 "text/html ; charset= 'utf-8' ;" },
703 // Media type comments in mime-type.
704 { "HTTP/1.1 200 OK\n"
705 "Content-type: text/html (html)\n",
706 "text/html", true,
707 "", false,
708 "text/html (html)" },
709 // Incomplete charset= param.
710 { "HTTP/1.1 200 OK\n"
711 "Content-type: text/html; char=\n",
712 "text/html", true,
713 "", false,
714 "text/html; char=" },
715 // Invalid media type: no slash.
716 { "HTTP/1.1 200 OK\n"
717 "Content-type: texthtml\n",
718 "", false,
719 "", false,
720 "texthtml" },
721 // Invalid media type: "*/*".
722 { "HTTP/1.1 200 OK\n"
723 "Content-type: */*\n",
724 "", false,
725 "", false,
726 "*/*" },
729 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
730 ContentTypeTest,
731 testing::ValuesIn(mimetype_tests));
733 struct RequiresValidationTestData {
734 const char* headers;
735 ValidationType validation_type;
738 class RequiresValidationTest
739 : public HttpResponseHeadersTest,
740 public ::testing::WithParamInterface<RequiresValidationTestData> {
743 TEST_P(RequiresValidationTest, RequiresValidation) {
744 const RequiresValidationTestData test = GetParam();
746 base::Time request_time, response_time, current_time;
747 base::Time::FromString("Wed, 28 Nov 2007 00:40:09 GMT", &request_time);
748 base::Time::FromString("Wed, 28 Nov 2007 00:40:12 GMT", &response_time);
749 base::Time::FromString("Wed, 28 Nov 2007 00:45:20 GMT", &current_time);
751 std::string headers(test.headers);
752 HeadersToRaw(&headers);
753 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
755 ValidationType validation_type =
756 parsed->RequiresValidation(request_time, response_time, current_time);
757 EXPECT_EQ(test.validation_type, validation_type);
760 const struct RequiresValidationTestData requires_validation_tests[] = {
761 // No expiry info: expires immediately.
762 { "HTTP/1.1 200 OK\n"
763 "\n",
764 VALIDATION_SYNCHRONOUS
766 // No expiry info: expires immediately.
767 { "HTTP/1.1 200 OK\n"
768 "\n",
769 VALIDATION_SYNCHRONOUS
771 // Valid for a little while.
772 { "HTTP/1.1 200 OK\n"
773 "cache-control: max-age=10000\n"
774 "\n",
775 VALIDATION_NONE
777 // Expires in the future.
778 { "HTTP/1.1 200 OK\n"
779 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
780 "expires: Wed, 28 Nov 2007 01:00:00 GMT\n"
781 "\n",
782 VALIDATION_NONE
784 // Already expired.
785 { "HTTP/1.1 200 OK\n"
786 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
787 "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
788 "\n",
789 VALIDATION_SYNCHRONOUS
791 // Max-age trumps expires.
792 { "HTTP/1.1 200 OK\n"
793 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
794 "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
795 "cache-control: max-age=10000\n"
796 "\n",
797 VALIDATION_NONE
799 // Last-modified heuristic: modified a while ago.
800 { "HTTP/1.1 200 OK\n"
801 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
802 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
803 "\n",
804 VALIDATION_NONE
806 { "HTTP/1.1 203 Non-Authoritative Information\n"
807 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
808 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
809 "\n",
810 VALIDATION_NONE
812 { "HTTP/1.1 206 Partial Content\n"
813 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
814 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
815 "\n",
816 VALIDATION_NONE
818 // Last-modified heuristic: modified recently.
819 { "HTTP/1.1 200 OK\n"
820 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
821 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
822 "\n",
823 VALIDATION_SYNCHRONOUS
825 { "HTTP/1.1 203 Non-Authoritative Information\n"
826 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
827 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
828 "\n",
829 VALIDATION_SYNCHRONOUS
831 { "HTTP/1.1 206 Partial Content\n"
832 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
833 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
834 "\n",
835 VALIDATION_SYNCHRONOUS
837 // Cached permanent redirect.
838 { "HTTP/1.1 301 Moved Permanently\n"
839 "\n",
840 VALIDATION_NONE
842 // Another cached permanent redirect.
843 { "HTTP/1.1 308 Permanent Redirect\n"
844 "\n",
845 VALIDATION_NONE
847 // Cached redirect: not reusable even though by default it would be.
848 { "HTTP/1.1 300 Multiple Choices\n"
849 "Cache-Control: no-cache\n"
850 "\n",
851 VALIDATION_SYNCHRONOUS
853 // Cached forever by default.
854 { "HTTP/1.1 410 Gone\n"
855 "\n",
856 VALIDATION_NONE
858 // Cached temporary redirect: not reusable.
859 { "HTTP/1.1 302 Found\n"
860 "\n",
861 VALIDATION_SYNCHRONOUS
863 // Cached temporary redirect: reusable.
864 { "HTTP/1.1 302 Found\n"
865 "cache-control: max-age=10000\n"
866 "\n",
867 VALIDATION_NONE
869 // Cache-control: max-age=N overrides expires: date in the past.
870 { "HTTP/1.1 200 OK\n"
871 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
872 "expires: Wed, 28 Nov 2007 00:20:11 GMT\n"
873 "cache-control: max-age=10000\n"
874 "\n",
875 VALIDATION_NONE
877 // Cache-control: no-store overrides 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, 29 Nov 2007 00:40:11 GMT\n"
881 "cache-control: no-store,private,no-cache=\"foo\"\n"
882 "\n",
883 VALIDATION_SYNCHRONOUS
885 // Pragma: no-cache overrides last-modified heuristic.
886 { "HTTP/1.1 200 OK\n"
887 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
888 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
889 "pragma: no-cache\n"
890 "\n",
891 VALIDATION_SYNCHRONOUS
893 // max-age has expired, needs synchronous revalidation
894 { "HTTP/1.1 200 OK\n"
895 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
896 "cache-control: max-age=300\n"
897 "\n",
898 VALIDATION_SYNCHRONOUS
900 // max-age has expired, stale-while-revalidate has not, eligible for
901 // asynchronous revalidation
902 { "HTTP/1.1 200 OK\n"
903 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
904 "cache-control: max-age=300, stale-while-revalidate=3600\n"
905 "\n",
906 VALIDATION_ASYNCHRONOUS
908 // max-age and stale-while-revalidate have expired, needs synchronous
909 // revalidation
910 { "HTTP/1.1 200 OK\n"
911 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
912 "cache-control: max-age=300, stale-while-revalidate=5\n"
913 "\n",
914 VALIDATION_SYNCHRONOUS
916 // max-age is 0, stale-while-revalidate is large enough to permit
917 // asynchronous revalidation
918 { "HTTP/1.1 200 OK\n"
919 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
920 "cache-control: max-age=0, stale-while-revalidate=360\n"
921 "\n",
922 VALIDATION_ASYNCHRONOUS
924 // stale-while-revalidate must not override no-cache or similar directives.
925 { "HTTP/1.1 200 OK\n"
926 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
927 "cache-control: no-cache, stale-while-revalidate=360\n"
928 "\n",
929 VALIDATION_SYNCHRONOUS
931 // max-age has not expired, so no revalidation is needed.
932 { "HTTP/1.1 200 OK\n"
933 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
934 "cache-control: max-age=3600, stale-while-revalidate=3600\n"
935 "\n",
936 VALIDATION_NONE
938 // must-revalidate overrides stale-while-revalidate, so synchronous validation
939 // is needed.
940 { "HTTP/1.1 200 OK\n"
941 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
942 "cache-control: must-revalidate, max-age=300, stale-while-revalidate=3600\n"
943 "\n",
944 VALIDATION_SYNCHRONOUS
947 // TODO(darin): Add many many more tests here.
950 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
951 RequiresValidationTest,
952 testing::ValuesIn(requires_validation_tests));
954 struct UpdateTestData {
955 const char* orig_headers;
956 const char* new_headers;
957 const char* expected_headers;
960 class UpdateTest
961 : public HttpResponseHeadersTest,
962 public ::testing::WithParamInterface<UpdateTestData> {
965 TEST_P(UpdateTest, Update) {
966 const UpdateTestData test = GetParam();
968 std::string orig_headers(test.orig_headers);
969 HeadersToRaw(&orig_headers);
970 scoped_refptr<HttpResponseHeaders> parsed(
971 new HttpResponseHeaders(orig_headers));
973 std::string new_headers(test.new_headers);
974 HeadersToRaw(&new_headers);
975 scoped_refptr<HttpResponseHeaders> new_parsed(
976 new HttpResponseHeaders(new_headers));
978 parsed->Update(*new_parsed.get());
980 std::string resulting_headers;
981 parsed->GetNormalizedHeaders(&resulting_headers);
982 EXPECT_EQ(std::string(test.expected_headers), resulting_headers);
985 const UpdateTestData update_tests[] = {
986 { "HTTP/1.1 200 OK\n",
988 "HTTP/1/1 304 Not Modified\n"
989 "connection: keep-alive\n"
990 "Cache-control: max-age=10000\n",
992 "HTTP/1.1 200 OK\n"
993 "Cache-control: max-age=10000\n"
995 { "HTTP/1.1 200 OK\n"
996 "Foo: 1\n"
997 "Cache-control: private\n",
999 "HTTP/1/1 304 Not Modified\n"
1000 "connection: keep-alive\n"
1001 "Cache-control: max-age=10000\n",
1003 "HTTP/1.1 200 OK\n"
1004 "Cache-control: max-age=10000\n"
1005 "Foo: 1\n"
1007 { "HTTP/1.1 200 OK\n"
1008 "Foo: 1\n"
1009 "Cache-control: private\n",
1011 "HTTP/1/1 304 Not Modified\n"
1012 "connection: keep-alive\n"
1013 "Cache-CONTROL: max-age=10000\n",
1015 "HTTP/1.1 200 OK\n"
1016 "Cache-CONTROL: max-age=10000\n"
1017 "Foo: 1\n"
1019 { "HTTP/1.1 200 OK\n"
1020 "Content-Length: 450\n",
1022 "HTTP/1/1 304 Not Modified\n"
1023 "connection: keep-alive\n"
1024 "Cache-control: max-age=10001 \n",
1026 "HTTP/1.1 200 OK\n"
1027 "Cache-control: max-age=10001\n"
1028 "Content-Length: 450\n"
1030 { "HTTP/1.1 200 OK\n"
1031 "X-Frame-Options: DENY\n",
1033 "HTTP/1/1 304 Not Modified\n"
1034 "X-Frame-Options: ALLOW\n",
1036 "HTTP/1.1 200 OK\n"
1037 "X-Frame-Options: DENY\n",
1039 { "HTTP/1.1 200 OK\n"
1040 "X-WebKit-CSP: default-src 'none'\n",
1042 "HTTP/1/1 304 Not Modified\n"
1043 "X-WebKit-CSP: default-src *\n",
1045 "HTTP/1.1 200 OK\n"
1046 "X-WebKit-CSP: default-src 'none'\n",
1048 { "HTTP/1.1 200 OK\n"
1049 "X-XSS-Protection: 1\n",
1051 "HTTP/1/1 304 Not Modified\n"
1052 "X-XSS-Protection: 0\n",
1054 "HTTP/1.1 200 OK\n"
1055 "X-XSS-Protection: 1\n",
1057 { "HTTP/1.1 200 OK\n",
1059 "HTTP/1/1 304 Not Modified\n"
1060 "X-Content-Type-Options: nosniff\n",
1062 "HTTP/1.1 200 OK\n"
1066 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
1067 UpdateTest,
1068 testing::ValuesIn(update_tests));
1070 struct EnumerateHeaderTestData {
1071 const char* headers;
1072 const char* expected_lines;
1075 class EnumerateHeaderLinesTest
1076 : public HttpResponseHeadersTest,
1077 public ::testing::WithParamInterface<EnumerateHeaderTestData> {
1080 TEST_P(EnumerateHeaderLinesTest, EnumerateHeaderLines) {
1081 const EnumerateHeaderTestData test = GetParam();
1083 std::string headers(test.headers);
1084 HeadersToRaw(&headers);
1085 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
1087 std::string name, value, lines;
1089 void* iter = NULL;
1090 while (parsed->EnumerateHeaderLines(&iter, &name, &value)) {
1091 lines.append(name);
1092 lines.append(": ");
1093 lines.append(value);
1094 lines.append("\n");
1097 EXPECT_EQ(std::string(test.expected_lines), lines);
1100 const EnumerateHeaderTestData enumerate_header_tests[] = {
1101 { "HTTP/1.1 200 OK\n",
1105 { "HTTP/1.1 200 OK\n"
1106 "Foo: 1\n",
1108 "Foo: 1\n"
1110 { "HTTP/1.1 200 OK\n"
1111 "Foo: 1\n"
1112 "Bar: 2\n"
1113 "Foo: 3\n",
1115 "Foo: 1\nBar: 2\nFoo: 3\n"
1117 { "HTTP/1.1 200 OK\n"
1118 "Foo: 1, 2, 3\n",
1120 "Foo: 1, 2, 3\n"
1124 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
1125 EnumerateHeaderLinesTest,
1126 testing::ValuesIn(enumerate_header_tests));
1128 struct IsRedirectTestData {
1129 const char* headers;
1130 const char* location;
1131 bool is_redirect;
1134 class IsRedirectTest
1135 : public HttpResponseHeadersTest,
1136 public ::testing::WithParamInterface<IsRedirectTestData> {
1139 TEST_P(IsRedirectTest, IsRedirect) {
1140 const IsRedirectTestData test = GetParam();
1142 std::string headers(test.headers);
1143 HeadersToRaw(&headers);
1144 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
1146 std::string location;
1147 EXPECT_EQ(parsed->IsRedirect(&location), test.is_redirect);
1148 EXPECT_EQ(location, test.location);
1151 const IsRedirectTestData is_redirect_tests[] = {
1152 { "HTTP/1.1 200 OK\n",
1154 false
1156 { "HTTP/1.1 301 Moved\n"
1157 "Location: http://foopy/\n",
1158 "http://foopy/",
1159 true
1161 { "HTTP/1.1 301 Moved\n"
1162 "Location: \t \n",
1164 false
1166 // We use the first location header as the target of the redirect.
1167 { "HTTP/1.1 301 Moved\n"
1168 "Location: http://foo/\n"
1169 "Location: http://bar/\n",
1170 "http://foo/",
1171 true
1173 // We use the first _valid_ location header as the target of the redirect.
1174 { "HTTP/1.1 301 Moved\n"
1175 "Location: \n"
1176 "Location: http://bar/\n",
1177 "http://bar/",
1178 true
1180 // Bug 1050541 (location header with an unescaped comma).
1181 { "HTTP/1.1 301 Moved\n"
1182 "Location: http://foo/bar,baz.html\n",
1183 "http://foo/bar,baz.html",
1184 true
1186 // Bug 1224617 (location header with non-ASCII bytes).
1187 { "HTTP/1.1 301 Moved\n"
1188 "Location: http://foo/bar?key=\xE4\xF6\xFC\n",
1189 "http://foo/bar?key=%E4%F6%FC",
1190 true
1192 // Shift_JIS, Big5, and GBK contain multibyte characters with the trailing
1193 // byte falling in the ASCII range.
1194 { "HTTP/1.1 301 Moved\n"
1195 "Location: http://foo/bar?key=\x81\x5E\xD8\xBF\n",
1196 "http://foo/bar?key=%81^%D8%BF",
1197 true
1199 { "HTTP/1.1 301 Moved\n"
1200 "Location: http://foo/bar?key=\x82\x40\xBD\xC4\n",
1201 "http://foo/bar?key=%82@%BD%C4",
1202 true
1204 { "HTTP/1.1 301 Moved\n"
1205 "Location: http://foo/bar?key=\x83\x5C\x82\x5D\xCB\xD7\n",
1206 "http://foo/bar?key=%83\\%82]%CB%D7",
1207 true
1211 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
1212 IsRedirectTest,
1213 testing::ValuesIn(is_redirect_tests));
1215 struct ContentLengthTestData {
1216 const char* headers;
1217 int64 expected_len;
1220 class GetContentLengthTest
1221 : public HttpResponseHeadersTest,
1222 public ::testing::WithParamInterface<ContentLengthTestData> {
1225 TEST_P(GetContentLengthTest, GetContentLength) {
1226 const ContentLengthTestData test = GetParam();
1228 std::string headers(test.headers);
1229 HeadersToRaw(&headers);
1230 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
1232 EXPECT_EQ(test.expected_len, parsed->GetContentLength());
1235 const ContentLengthTestData content_length_tests[] = {
1236 { "HTTP/1.1 200 OK\n",
1239 { "HTTP/1.1 200 OK\n"
1240 "Content-Length: 10\n",
1243 { "HTTP/1.1 200 OK\n"
1244 "Content-Length: \n",
1247 { "HTTP/1.1 200 OK\n"
1248 "Content-Length: abc\n",
1251 { "HTTP/1.1 200 OK\n"
1252 "Content-Length: -10\n",
1255 { "HTTP/1.1 200 OK\n"
1256 "Content-Length: +10\n",
1259 { "HTTP/1.1 200 OK\n"
1260 "Content-Length: 23xb5\n",
1263 { "HTTP/1.1 200 OK\n"
1264 "Content-Length: 0xA\n",
1267 { "HTTP/1.1 200 OK\n"
1268 "Content-Length: 010\n",
1271 // Content-Length too big, will overflow an int64.
1272 { "HTTP/1.1 200 OK\n"
1273 "Content-Length: 40000000000000000000\n",
1276 { "HTTP/1.1 200 OK\n"
1277 "Content-Length: 10\n",
1280 { "HTTP/1.1 200 OK\n"
1281 "Content-Length: 10 \n",
1284 { "HTTP/1.1 200 OK\n"
1285 "Content-Length: \t10\n",
1288 { "HTTP/1.1 200 OK\n"
1289 "Content-Length: \v10\n",
1292 { "HTTP/1.1 200 OK\n"
1293 "Content-Length: \f10\n",
1296 { "HTTP/1.1 200 OK\n"
1297 "cOnTeNt-LENgth: 33\n",
1300 { "HTTP/1.1 200 OK\n"
1301 "Content-Length: 34\r\n",
1306 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
1307 GetContentLengthTest,
1308 testing::ValuesIn(content_length_tests));
1310 struct ContentRangeTestData {
1311 const char* headers;
1312 bool expected_return_value;
1313 int64 expected_first_byte_position;
1314 int64 expected_last_byte_position;
1315 int64 expected_instance_size;
1318 class ContentRangeTest
1319 : public HttpResponseHeadersTest,
1320 public ::testing::WithParamInterface<ContentRangeTestData> {
1323 TEST_P(ContentRangeTest, GetContentRange) {
1324 const ContentRangeTestData test = GetParam();
1326 std::string headers(test.headers);
1327 HeadersToRaw(&headers);
1328 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
1330 int64 first_byte_position;
1331 int64 last_byte_position;
1332 int64 instance_size;
1333 bool return_value = parsed->GetContentRange(&first_byte_position,
1334 &last_byte_position,
1335 &instance_size);
1336 EXPECT_EQ(test.expected_return_value, return_value);
1337 EXPECT_EQ(test.expected_first_byte_position, first_byte_position);
1338 EXPECT_EQ(test.expected_last_byte_position, last_byte_position);
1339 EXPECT_EQ(test.expected_instance_size, instance_size);
1342 const ContentRangeTestData content_range_tests[] = {
1343 { "HTTP/1.1 206 Partial Content",
1344 false,
1349 { "HTTP/1.1 206 Partial Content\n"
1350 "Content-Range:",
1351 false,
1356 { "HTTP/1.1 206 Partial Content\n"
1357 "Content-Range: megabytes 0-10/50",
1358 false,
1363 { "HTTP/1.1 206 Partial Content\n"
1364 "Content-Range: 0-10/50",
1365 false,
1370 { "HTTP/1.1 206 Partial Content\n"
1371 "Content-Range: Bytes 0-50/51",
1372 true,
1377 { "HTTP/1.1 206 Partial Content\n"
1378 "Content-Range: bytes 0-50/51",
1379 true,
1384 { "HTTP/1.1 206 Partial Content\n"
1385 "Content-Range: bytes\t0-50/51",
1386 false,
1391 { "HTTP/1.1 206 Partial Content\n"
1392 "Content-Range: bytes 0-50/51",
1393 true,
1398 { "HTTP/1.1 206 Partial Content\n"
1399 "Content-Range: bytes 0 - 50 \t / \t51",
1400 true,
1405 { "HTTP/1.1 206 Partial Content\n"
1406 "Content-Range: bytes 0\t-\t50\t/\t51\t",
1407 true,
1412 { "HTTP/1.1 206 Partial Content\n"
1413 "Content-Range: \tbytes\t\t\t 0\t-\t50\t/\t51\t",
1414 true,
1419 { "HTTP/1.1 206 Partial Content\n"
1420 "Content-Range: \t bytes \t 0 - 50 / 5 1",
1421 false,
1426 { "HTTP/1.1 206 Partial Content\n"
1427 "Content-Range: \t bytes \t 0 - 5 0 / 51",
1428 false,
1433 { "HTTP/1.1 206 Partial Content\n"
1434 "Content-Range: bytes 50-0/51",
1435 false,
1440 { "HTTP/1.1 416 Requested range not satisfiable\n"
1441 "Content-Range: bytes * /*",
1442 false,
1447 { "HTTP/1.1 416 Requested range not satisfiable\n"
1448 "Content-Range: bytes * / * ",
1449 false,
1454 { "HTTP/1.1 206 Partial Content\n"
1455 "Content-Range: bytes 0-50/*",
1456 false,
1461 { "HTTP/1.1 206 Partial Content\n"
1462 "Content-Range: bytes 0-50 / * ",
1463 false,
1468 { "HTTP/1.1 206 Partial Content\n"
1469 "Content-Range: bytes 0-10000000000/10000000001",
1470 true,
1472 10000000000ll,
1473 10000000001ll
1475 { "HTTP/1.1 206 Partial Content\n"
1476 "Content-Range: bytes 0-10000000000/10000000000",
1477 false,
1479 10000000000ll,
1480 10000000000ll
1482 // 64 bit wraparound.
1483 { "HTTP/1.1 206 Partial Content\n"
1484 "Content-Range: bytes 0 - 9223372036854775807 / 100",
1485 false,
1487 kint64max,
1490 // 64 bit wraparound.
1491 { "HTTP/1.1 206 Partial Content\n"
1492 "Content-Range: bytes 0 - 100 / -9223372036854775808",
1493 false,
1495 100,
1496 kint64min
1498 { "HTTP/1.1 206 Partial Content\n"
1499 "Content-Range: bytes */50",
1500 false,
1505 { "HTTP/1.1 206 Partial Content\n"
1506 "Content-Range: bytes 0-50/10",
1507 false,
1512 { "HTTP/1.1 206 Partial Content\n"
1513 "Content-Range: bytes 40-50/45",
1514 false,
1519 { "HTTP/1.1 206 Partial Content\n"
1520 "Content-Range: bytes 0-50/-10",
1521 false,
1526 { "HTTP/1.1 206 Partial Content\n"
1527 "Content-Range: bytes 0-0/1",
1528 true,
1533 { "HTTP/1.1 206 Partial Content\n"
1534 "Content-Range: bytes 0-40000000000000000000/40000000000000000001",
1535 false,
1540 { "HTTP/1.1 206 Partial Content\n"
1541 "Content-Range: bytes 1-/100",
1542 false,
1547 { "HTTP/1.1 206 Partial Content\n"
1548 "Content-Range: bytes -/100",
1549 false,
1554 { "HTTP/1.1 206 Partial Content\n"
1555 "Content-Range: bytes -1/100",
1556 false,
1561 { "HTTP/1.1 206 Partial Content\n"
1562 "Content-Range: bytes 0-1233/*",
1563 false,
1565 1233,
1568 { "HTTP/1.1 206 Partial Content\n"
1569 "Content-Range: bytes -123 - -1/100",
1570 false,
1577 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
1578 ContentRangeTest,
1579 testing::ValuesIn(content_range_tests));
1581 struct KeepAliveTestData {
1582 const char* headers;
1583 bool expected_keep_alive;
1586 // Enable GTest to print KeepAliveTestData in an intelligible way if the test
1587 // fails.
1588 void PrintTo(const KeepAliveTestData& keep_alive_test_data,
1589 std::ostream* os) {
1590 *os << "{\"" << keep_alive_test_data.headers << "\", " << std::boolalpha
1591 << keep_alive_test_data.expected_keep_alive << "}";
1594 class IsKeepAliveTest
1595 : public HttpResponseHeadersTest,
1596 public ::testing::WithParamInterface<KeepAliveTestData> {
1599 TEST_P(IsKeepAliveTest, IsKeepAlive) {
1600 const KeepAliveTestData test = GetParam();
1602 std::string headers(test.headers);
1603 HeadersToRaw(&headers);
1604 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
1606 EXPECT_EQ(test.expected_keep_alive, parsed->IsKeepAlive());
1609 const KeepAliveTestData keepalive_tests[] = {
1610 // The status line fabricated by HttpNetworkTransaction for a 0.9 response.
1611 // Treated as 0.9.
1612 { "HTTP/0.9 200 OK",
1613 false
1615 // This could come from a broken server. Treated as 1.0 because it has a
1616 // header.
1617 { "HTTP/0.9 200 OK\n"
1618 "connection: keep-alive\n",
1619 true
1621 { "HTTP/1.1 200 OK\n",
1622 true
1624 { "HTTP/1.0 200 OK\n",
1625 false
1627 { "HTTP/1.0 200 OK\n"
1628 "connection: close\n",
1629 false
1631 { "HTTP/1.0 200 OK\n"
1632 "connection: keep-alive\n",
1633 true
1635 { "HTTP/1.0 200 OK\n"
1636 "connection: kEeP-AliVe\n",
1637 true
1639 { "HTTP/1.0 200 OK\n"
1640 "connection: keep-aliveX\n",
1641 false
1643 { "HTTP/1.1 200 OK\n"
1644 "connection: close\n",
1645 false
1647 { "HTTP/1.1 200 OK\n"
1648 "connection: keep-alive\n",
1649 true
1651 { "HTTP/1.0 200 OK\n"
1652 "proxy-connection: close\n",
1653 false
1655 { "HTTP/1.0 200 OK\n"
1656 "proxy-connection: keep-alive\n",
1657 true
1659 { "HTTP/1.1 200 OK\n"
1660 "proxy-connection: close\n",
1661 false
1663 { "HTTP/1.1 200 OK\n"
1664 "proxy-connection: keep-alive\n",
1665 true
1667 { "HTTP/1.1 200 OK\n"
1668 "Connection: Upgrade, close\n",
1669 false
1671 { "HTTP/1.1 200 OK\n"
1672 "Connection: Upgrade, keep-alive\n",
1673 true
1675 { "HTTP/1.1 200 OK\n"
1676 "Connection: Upgrade\n"
1677 "Connection: close\n",
1678 false
1680 { "HTTP/1.1 200 OK\n"
1681 "Connection: Upgrade\n"
1682 "Connection: keep-alive\n",
1683 true
1685 { "HTTP/1.1 200 OK\n"
1686 "Connection: close, Upgrade\n",
1687 false
1689 { "HTTP/1.1 200 OK\n"
1690 "Connection: keep-alive, Upgrade\n",
1691 true
1693 { "HTTP/1.1 200 OK\n"
1694 "Connection: Upgrade\n"
1695 "Proxy-Connection: close\n",
1696 false
1698 { "HTTP/1.1 200 OK\n"
1699 "Connection: Upgrade\n"
1700 "Proxy-Connection: keep-alive\n",
1701 true
1703 // In situations where the response headers conflict with themselves, use the
1704 // first one for backwards-compatibility.
1705 { "HTTP/1.1 200 OK\n"
1706 "Connection: close\n"
1707 "Connection: keep-alive\n",
1708 false
1710 { "HTTP/1.1 200 OK\n"
1711 "Connection: keep-alive\n"
1712 "Connection: close\n",
1713 true
1715 { "HTTP/1.0 200 OK\n"
1716 "Connection: close\n"
1717 "Connection: keep-alive\n",
1718 false
1720 { "HTTP/1.0 200 OK\n"
1721 "Connection: keep-alive\n"
1722 "Connection: close\n",
1723 true
1725 // Ignore the Proxy-Connection header if at all possible.
1726 { "HTTP/1.0 200 OK\n"
1727 "Proxy-Connection: keep-alive\n"
1728 "Connection: close\n",
1729 false
1731 { "HTTP/1.1 200 OK\n"
1732 "Proxy-Connection: close\n"
1733 "Connection: keep-alive\n",
1734 true
1736 // Older versions of Chrome would have ignored Proxy-Connection in this case,
1737 // but it doesn't seem safe.
1738 { "HTTP/1.1 200 OK\n"
1739 "Proxy-Connection: close\n"
1740 "Connection: Transfer-Encoding\n",
1741 false
1745 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
1746 IsKeepAliveTest,
1747 testing::ValuesIn(keepalive_tests));
1749 struct HasStrongValidatorsTestData {
1750 const char* headers;
1751 bool expected_result;
1754 class HasStrongValidatorsTest
1755 : public HttpResponseHeadersTest,
1756 public ::testing::WithParamInterface<HasStrongValidatorsTestData> {
1759 TEST_P(HasStrongValidatorsTest, HasStrongValidators) {
1760 const HasStrongValidatorsTestData test = GetParam();
1762 std::string headers(test.headers);
1763 HeadersToRaw(&headers);
1764 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
1766 EXPECT_EQ(test.expected_result, parsed->HasStrongValidators());
1769 const HasStrongValidatorsTestData strong_validators_tests[] = {
1770 { "HTTP/0.9 200 OK",
1771 false
1773 { "HTTP/1.0 200 OK\n"
1774 "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
1775 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
1776 "ETag: \"foo\"\n",
1777 false
1779 { "HTTP/1.1 200 OK\n"
1780 "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
1781 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
1782 "ETag: \"foo\"\n",
1783 true
1785 { "HTTP/1.1 200 OK\n"
1786 "Date: Wed, 28 Nov 2007 00:41:10 GMT\n"
1787 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1788 true
1790 { "HTTP/1.1 200 OK\n"
1791 "Date: Wed, 28 Nov 2007 00:41:09 GMT\n"
1792 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1793 false
1795 { "HTTP/1.1 200 OK\n"
1796 "ETag: \"foo\"\n",
1797 true
1799 // This is not really a weak etag:
1800 { "HTTP/1.1 200 OK\n"
1801 "etag: \"w/foo\"\n",
1802 true
1804 // This is a weak etag:
1805 { "HTTP/1.1 200 OK\n"
1806 "etag: w/\"foo\"\n",
1807 false
1809 { "HTTP/1.1 200 OK\n"
1810 "etag: W / \"foo\"\n",
1811 false
1815 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
1816 HasStrongValidatorsTest,
1817 testing::ValuesIn(strong_validators_tests));
1819 TEST(HttpResponseHeadersTest, GetStatusText) {
1820 std::string headers("HTTP/1.1 404 Not Found");
1821 HeadersToRaw(&headers);
1822 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
1823 EXPECT_EQ(std::string("Not Found"), parsed->GetStatusText());
1826 TEST(HttpResponseHeadersTest, GetStatusTextMissing) {
1827 std::string headers("HTTP/1.1 404");
1828 HeadersToRaw(&headers);
1829 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
1830 // Since the status line gets normalized, we have OK.
1831 EXPECT_EQ(std::string("OK"), parsed->GetStatusText());
1834 TEST(HttpResponseHeadersTest, GetStatusTextMultiSpace) {
1835 std::string headers("HTTP/1.0 404 Not Found");
1836 HeadersToRaw(&headers);
1837 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
1838 EXPECT_EQ(std::string("Not Found"), parsed->GetStatusText());
1841 TEST(HttpResponseHeadersTest, GetStatusBadStatusLine) {
1842 std::string headers("Foo bar.");
1843 HeadersToRaw(&headers);
1844 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
1845 // The bad status line would have gotten rewritten as
1846 // HTTP/1.0 200 OK.
1847 EXPECT_EQ(std::string("OK"), parsed->GetStatusText());
1850 struct AddHeaderTestData {
1851 const char* orig_headers;
1852 const char* new_header;
1853 const char* expected_headers;
1856 class AddHeaderTest
1857 : public HttpResponseHeadersTest,
1858 public ::testing::WithParamInterface<AddHeaderTestData> {
1861 TEST_P(AddHeaderTest, AddHeader) {
1862 const AddHeaderTestData test = GetParam();
1864 std::string orig_headers(test.orig_headers);
1865 HeadersToRaw(&orig_headers);
1866 scoped_refptr<HttpResponseHeaders> parsed(
1867 new HttpResponseHeaders(orig_headers));
1869 std::string new_header(test.new_header);
1870 parsed->AddHeader(new_header);
1872 std::string resulting_headers;
1873 parsed->GetNormalizedHeaders(&resulting_headers);
1874 EXPECT_EQ(std::string(test.expected_headers), resulting_headers);
1877 const AddHeaderTestData add_header_tests[] = {
1878 { "HTTP/1.1 200 OK\n"
1879 "connection: keep-alive\n"
1880 "Cache-control: max-age=10000\n",
1882 "Content-Length: 450",
1884 "HTTP/1.1 200 OK\n"
1885 "connection: keep-alive\n"
1886 "Cache-control: max-age=10000\n"
1887 "Content-Length: 450\n"
1889 { "HTTP/1.1 200 OK\n"
1890 "connection: keep-alive\n"
1891 "Cache-control: max-age=10000 \n",
1893 "Content-Length: 450 ",
1895 "HTTP/1.1 200 OK\n"
1896 "connection: keep-alive\n"
1897 "Cache-control: max-age=10000\n"
1898 "Content-Length: 450\n"
1902 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
1903 AddHeaderTest,
1904 testing::ValuesIn(add_header_tests));
1906 struct RemoveHeaderTestData {
1907 const char* orig_headers;
1908 const char* to_remove;
1909 const char* expected_headers;
1912 class RemoveHeaderTest
1913 : public HttpResponseHeadersTest,
1914 public ::testing::WithParamInterface<RemoveHeaderTestData> {
1917 TEST_P(RemoveHeaderTest, RemoveHeader) {
1918 const RemoveHeaderTestData test = GetParam();
1920 std::string orig_headers(test.orig_headers);
1921 HeadersToRaw(&orig_headers);
1922 scoped_refptr<HttpResponseHeaders> parsed(
1923 new HttpResponseHeaders(orig_headers));
1925 std::string name(test.to_remove);
1926 parsed->RemoveHeader(name);
1928 std::string resulting_headers;
1929 parsed->GetNormalizedHeaders(&resulting_headers);
1930 EXPECT_EQ(std::string(test.expected_headers), resulting_headers);
1933 const RemoveHeaderTestData remove_header_tests[] = {
1934 { "HTTP/1.1 200 OK\n"
1935 "connection: keep-alive\n"
1936 "Cache-control: max-age=10000\n"
1937 "Content-Length: 450\n",
1939 "Content-Length",
1941 "HTTP/1.1 200 OK\n"
1942 "connection: keep-alive\n"
1943 "Cache-control: max-age=10000\n"
1945 { "HTTP/1.1 200 OK\n"
1946 "connection: keep-alive \n"
1947 "Content-Length : 450 \n"
1948 "Cache-control: max-age=10000\n",
1950 "Content-Length",
1952 "HTTP/1.1 200 OK\n"
1953 "connection: keep-alive\n"
1954 "Cache-control: max-age=10000\n"
1958 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
1959 RemoveHeaderTest,
1960 testing::ValuesIn(remove_header_tests));
1962 struct RemoveIndividualHeaderTestData {
1963 const char* orig_headers;
1964 const char* to_remove_name;
1965 const char* to_remove_value;
1966 const char* expected_headers;
1969 class RemoveIndividualHeaderTest
1970 : public HttpResponseHeadersTest,
1971 public ::testing::WithParamInterface<RemoveIndividualHeaderTestData> {
1974 TEST_P(RemoveIndividualHeaderTest, RemoveIndividualHeader) {
1975 const RemoveIndividualHeaderTestData test = GetParam();
1977 std::string orig_headers(test.orig_headers);
1978 HeadersToRaw(&orig_headers);
1979 scoped_refptr<HttpResponseHeaders> parsed(
1980 new HttpResponseHeaders(orig_headers));
1982 std::string name(test.to_remove_name);
1983 std::string value(test.to_remove_value);
1984 parsed->RemoveHeaderLine(name, value);
1986 std::string resulting_headers;
1987 parsed->GetNormalizedHeaders(&resulting_headers);
1988 EXPECT_EQ(std::string(test.expected_headers), resulting_headers);
1991 const RemoveIndividualHeaderTestData remove_individual_header_tests[] = {
1992 { "HTTP/1.1 200 OK\n"
1993 "connection: keep-alive\n"
1994 "Cache-control: max-age=10000\n"
1995 "Content-Length: 450\n",
1997 "Content-Length",
1999 "450",
2001 "HTTP/1.1 200 OK\n"
2002 "connection: keep-alive\n"
2003 "Cache-control: max-age=10000\n"
2005 { "HTTP/1.1 200 OK\n"
2006 "connection: keep-alive \n"
2007 "Content-Length : 450 \n"
2008 "Cache-control: max-age=10000\n",
2010 "Content-Length",
2012 "450",
2014 "HTTP/1.1 200 OK\n"
2015 "connection: keep-alive\n"
2016 "Cache-control: max-age=10000\n"
2018 { "HTTP/1.1 200 OK\n"
2019 "connection: keep-alive \n"
2020 "Content-Length: 450\n"
2021 "Cache-control: max-age=10000\n",
2023 "Content-Length", // Matching name.
2025 "999", // Mismatching value.
2027 "HTTP/1.1 200 OK\n"
2028 "connection: keep-alive\n"
2029 "Content-Length: 450\n"
2030 "Cache-control: max-age=10000\n"
2032 { "HTTP/1.1 200 OK\n"
2033 "connection: keep-alive \n"
2034 "Foo: bar, baz\n"
2035 "Foo: bar\n"
2036 "Cache-control: max-age=10000\n",
2038 "Foo",
2040 "bar, baz", // Space in value.
2042 "HTTP/1.1 200 OK\n"
2043 "connection: keep-alive\n"
2044 "Foo: bar\n"
2045 "Cache-control: max-age=10000\n"
2047 { "HTTP/1.1 200 OK\n"
2048 "connection: keep-alive \n"
2049 "Foo: bar, baz\n"
2050 "Cache-control: max-age=10000\n",
2052 "Foo",
2054 "baz", // Only partial match -> ignored.
2056 "HTTP/1.1 200 OK\n"
2057 "connection: keep-alive\n"
2058 "Foo: bar, baz\n"
2059 "Cache-control: max-age=10000\n"
2063 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
2064 RemoveIndividualHeaderTest,
2065 testing::ValuesIn(remove_individual_header_tests));
2067 struct ReplaceStatusTestData {
2068 const char* orig_headers;
2069 const char* new_status;
2070 const char* expected_headers;
2073 class ReplaceStatusTest
2074 : public HttpResponseHeadersTest,
2075 public ::testing::WithParamInterface<ReplaceStatusTestData> {
2078 TEST_P(ReplaceStatusTest, ReplaceStatus) {
2079 const ReplaceStatusTestData test = GetParam();
2081 std::string orig_headers(test.orig_headers);
2082 HeadersToRaw(&orig_headers);
2083 scoped_refptr<HttpResponseHeaders> parsed(
2084 new HttpResponseHeaders(orig_headers));
2086 std::string name(test.new_status);
2087 parsed->ReplaceStatusLine(name);
2089 std::string resulting_headers;
2090 parsed->GetNormalizedHeaders(&resulting_headers);
2091 EXPECT_EQ(std::string(test.expected_headers), resulting_headers);
2094 const ReplaceStatusTestData replace_status_tests[] = {
2095 { "HTTP/1.1 206 Partial Content\n"
2096 "connection: keep-alive\n"
2097 "Cache-control: max-age=10000\n"
2098 "Content-Length: 450\n",
2100 "HTTP/1.1 200 OK",
2102 "HTTP/1.1 200 OK\n"
2103 "connection: keep-alive\n"
2104 "Cache-control: max-age=10000\n"
2105 "Content-Length: 450\n"
2107 { "HTTP/1.1 200 OK\n"
2108 "connection: keep-alive\n",
2110 "HTTP/1.1 304 Not Modified",
2112 "HTTP/1.1 304 Not Modified\n"
2113 "connection: keep-alive\n"
2115 { "HTTP/1.1 200 OK\n"
2116 "connection: keep-alive \n"
2117 "Content-Length : 450 \n"
2118 "Cache-control: max-age=10000\n",
2120 "HTTP/1//1 304 Not Modified",
2122 "HTTP/1.0 304 Not Modified\n"
2123 "connection: keep-alive\n"
2124 "Content-Length: 450\n"
2125 "Cache-control: max-age=10000\n"
2129 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
2130 ReplaceStatusTest,
2131 testing::ValuesIn(replace_status_tests));
2133 struct UpdateWithNewRangeTestData {
2134 const char* orig_headers;
2135 const char* expected_headers;
2136 const char* expected_headers_with_replaced_status;
2139 class UpdateWithNewRangeTest
2140 : public HttpResponseHeadersTest,
2141 public ::testing::WithParamInterface<UpdateWithNewRangeTestData> {
2144 TEST_P(UpdateWithNewRangeTest, UpdateWithNewRange) {
2145 const UpdateWithNewRangeTestData test = GetParam();
2147 const HttpByteRange range = HttpByteRange::Bounded(3, 5);
2149 std::string orig_headers(test.orig_headers);
2150 std::replace(orig_headers.begin(), orig_headers.end(), '\n', '\0');
2151 scoped_refptr<HttpResponseHeaders> parsed(
2152 new HttpResponseHeaders(orig_headers + '\0'));
2153 int64 content_size = parsed->GetContentLength();
2154 std::string resulting_headers;
2156 // Update headers without replacing status line.
2157 parsed->UpdateWithNewRange(range, content_size, false);
2158 parsed->GetNormalizedHeaders(&resulting_headers);
2159 EXPECT_EQ(std::string(test.expected_headers), resulting_headers);
2161 // Replace status line too.
2162 parsed->UpdateWithNewRange(range, content_size, true);
2163 parsed->GetNormalizedHeaders(&resulting_headers);
2164 EXPECT_EQ(std::string(test.expected_headers_with_replaced_status),
2165 resulting_headers);
2168 const UpdateWithNewRangeTestData update_range_tests[] = {
2169 { "HTTP/1.1 200 OK\n"
2170 "Content-Length: 450\n",
2172 "HTTP/1.1 200 OK\n"
2173 "Content-Range: bytes 3-5/450\n"
2174 "Content-Length: 3\n",
2176 "HTTP/1.1 206 Partial Content\n"
2177 "Content-Range: bytes 3-5/450\n"
2178 "Content-Length: 3\n",
2180 { "HTTP/1.1 200 OK\n"
2181 "Content-Length: 5\n",
2183 "HTTP/1.1 200 OK\n"
2184 "Content-Range: bytes 3-5/5\n"
2185 "Content-Length: 3\n",
2187 "HTTP/1.1 206 Partial Content\n"
2188 "Content-Range: bytes 3-5/5\n"
2189 "Content-Length: 3\n",
2193 INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
2194 UpdateWithNewRangeTest,
2195 testing::ValuesIn(update_range_tests));
2197 TEST(HttpResponseHeadersTest, ToNetLogParamAndBackAgain) {
2198 std::string headers("HTTP/1.1 404\n"
2199 "Content-Length: 450\n"
2200 "Connection: keep-alive\n");
2201 HeadersToRaw(&headers);
2202 scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
2204 scoped_ptr<base::Value> event_param(parsed->NetLogCallback(
2205 NetLogCaptureMode::IncludeCookiesAndCredentials()));
2206 scoped_refptr<HttpResponseHeaders> recreated;
2208 ASSERT_TRUE(
2209 HttpResponseHeaders::FromNetLogParam(event_param.get(), &recreated));
2210 ASSERT_TRUE(recreated.get());
2211 EXPECT_EQ(parsed->GetHttpVersion(), recreated->GetHttpVersion());
2212 EXPECT_EQ(parsed->response_code(), recreated->response_code());
2213 EXPECT_EQ(parsed->GetContentLength(), recreated->GetContentLength());
2214 EXPECT_EQ(parsed->IsKeepAlive(), recreated->IsKeepAlive());
2216 std::string normalized_parsed;
2217 parsed->GetNormalizedHeaders(&normalized_parsed);
2218 std::string normalized_recreated;
2219 parsed->GetNormalizedHeaders(&normalized_recreated);
2220 EXPECT_EQ(normalized_parsed, normalized_recreated);
2223 TEST_F(HttpResponseHeadersCacheControlTest, AbsentMaxAgeReturnsFalse) {
2224 InitializeHeadersWithCacheControl("nocache");
2225 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2228 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithNoParameterRejected) {
2229 InitializeHeadersWithCacheControl("max-age=,private");
2230 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2233 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithSpaceParameterRejected) {
2234 InitializeHeadersWithCacheControl("max-age= ,private");
2235 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2238 TEST_F(HttpResponseHeadersCacheControlTest,
2239 MaxAgeWithSpaceBeforeEqualsIsRejected) {
2240 InitializeHeadersWithCacheControl("max-age = 7");
2241 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2244 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeFirstMatchUsed) {
2245 InitializeHeadersWithCacheControl("max-age=10, max-age=20");
2246 EXPECT_EQ(TimeDelta::FromSeconds(10), GetMaxAgeValue());
2249 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeBogusFirstMatchUsed) {
2250 // "max-age10" isn't parsed as "max-age"; "max-age=now" is parsed as
2251 // "max-age=0" and so "max-age=20" is not used.
2252 InitializeHeadersWithCacheControl("max-age10, max-age=now, max-age=20");
2253 EXPECT_EQ(TimeDelta::FromSeconds(0), GetMaxAgeValue());
2256 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeCaseInsensitive) {
2257 InitializeHeadersWithCacheControl("Max-aGe=15");
2258 EXPECT_EQ(TimeDelta::FromSeconds(15), GetMaxAgeValue());
2261 struct MaxAgeTestData {
2262 const char* max_age_string;
2263 const int64 expected_seconds;
2266 class MaxAgeEdgeCasesTest
2267 : public HttpResponseHeadersCacheControlTest,
2268 public ::testing::WithParamInterface<MaxAgeTestData> {
2271 TEST_P(MaxAgeEdgeCasesTest, MaxAgeEdgeCases) {
2272 const MaxAgeTestData test = GetParam();
2274 std::string max_age = "max-age=";
2275 InitializeHeadersWithCacheControl(
2276 (max_age + test.max_age_string).c_str());
2277 EXPECT_EQ(test.expected_seconds, GetMaxAgeValue().InSeconds())
2278 << " for max-age=" << test.max_age_string;
2281 const MaxAgeTestData max_age_tests[] = {
2282 {" 1 ", 1}, // Spaces are ignored.
2283 {"-1", -1}, // Negative numbers are passed through.
2284 {"--1", 0}, // Leading junk gives 0.
2285 {"2s", 2}, // Trailing junk is ignored.
2286 {"3 days", 3},
2287 {"'4'", 0}, // Single quotes don't work.
2288 {"\"5\"", 0}, // Double quotes don't work.
2289 {"0x6", 0}, // Hex not parsed as hex.
2290 {"7F", 7}, // Hex without 0x still not parsed as hex.
2291 {"010", 10}, // Octal not parsed as octal.
2292 {"9223372036854", 9223372036854},
2293 // {"9223372036855", -9223372036854}, // Undefined behaviour.
2294 // {"9223372036854775806", -2}, // Undefined behaviour.
2295 {"9223372036854775807", 9223372036854775807},
2296 {"20000000000000000000",
2297 std::numeric_limits<int64>::max()}, // Overflow int64.
2300 INSTANTIATE_TEST_CASE_P(HttpResponseHeadersCacheControl,
2301 MaxAgeEdgeCasesTest,
2302 testing::ValuesIn(max_age_tests));
2304 TEST_F(HttpResponseHeadersCacheControlTest,
2305 AbsentStaleWhileRevalidateReturnsFalse) {
2306 InitializeHeadersWithCacheControl("max-age=3600");
2307 EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
2310 TEST_F(HttpResponseHeadersCacheControlTest,
2311 StaleWhileRevalidateWithoutValueRejected) {
2312 InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=");
2313 EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
2316 TEST_F(HttpResponseHeadersCacheControlTest,
2317 StaleWhileRevalidateWithInvalidValueTreatedAsZero) {
2318 InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=true");
2319 EXPECT_EQ(TimeDelta(), GetStaleWhileRevalidateValue());
2322 TEST_F(HttpResponseHeadersCacheControlTest, StaleWhileRevalidateValueReturned) {
2323 InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=7200");
2324 EXPECT_EQ(TimeDelta::FromSeconds(7200), GetStaleWhileRevalidateValue());
2327 TEST_F(HttpResponseHeadersCacheControlTest,
2328 FirstStaleWhileRevalidateValueUsed) {
2329 InitializeHeadersWithCacheControl(
2330 "stale-while-revalidate=1,stale-while-revalidate=7200");
2331 EXPECT_EQ(TimeDelta::FromSeconds(1), GetStaleWhileRevalidateValue());
2334 } // namespace
2336 } // namespace net