Extract protobuf database into a new 'leveldb_proto' component
[chromium-blink-merge.git] / net / http / http_response_headers_unittest.cc
blobcc236d183e8bc80de79360ee2b2620e85a02e089
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>
7 #include "base/basictypes.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/pickle.h"
10 #include "base/time/time.h"
11 #include "base/values.h"
12 #include "net/http/http_byte_range.h"
13 #include "net/http/http_response_headers.h"
14 #include "testing/gtest/include/gtest/gtest.h"
16 namespace {
18 struct TestData {
19 const char* raw_headers;
20 const char* expected_headers;
21 int expected_response_code;
22 net::HttpVersion expected_parsed_version;
23 net::HttpVersion expected_version;
26 struct ContentTypeTestData {
27 const std::string raw_headers;
28 const std::string mime_type;
29 const bool has_mimetype;
30 const std::string charset;
31 const bool has_charset;
32 const std::string all_content_type;
35 class HttpResponseHeadersTest : public testing::Test {
38 // Transform "normal"-looking headers (\n-separated) to the appropriate
39 // input format for ParseRawHeaders (\0-separated).
40 void HeadersToRaw(std::string* headers) {
41 std::replace(headers->begin(), headers->end(), '\n', '\0');
42 if (!headers->empty())
43 *headers += '\0';
46 void TestCommon(const TestData& test) {
47 std::string raw_headers(test.raw_headers);
48 HeadersToRaw(&raw_headers);
49 std::string expected_headers(test.expected_headers);
51 std::string headers;
52 scoped_refptr<net::HttpResponseHeaders> parsed(
53 new net::HttpResponseHeaders(raw_headers));
54 parsed->GetNormalizedHeaders(&headers);
56 // Transform to readable output format (so it's easier to see diffs).
57 std::replace(headers.begin(), headers.end(), ' ', '_');
58 std::replace(headers.begin(), headers.end(), '\n', '\\');
59 std::replace(expected_headers.begin(), expected_headers.end(), ' ', '_');
60 std::replace(expected_headers.begin(), expected_headers.end(), '\n', '\\');
62 EXPECT_EQ(expected_headers, headers);
64 EXPECT_EQ(test.expected_response_code, parsed->response_code());
66 EXPECT_TRUE(test.expected_parsed_version == parsed->GetParsedHttpVersion());
67 EXPECT_TRUE(test.expected_version == parsed->GetHttpVersion());
70 } // end namespace
72 // Check that we normalize headers properly.
73 TEST(HttpResponseHeadersTest, NormalizeHeadersWhitespace) {
74 TestData test = {
75 "HTTP/1.1 202 Accepted \n"
76 "Content-TYPE : text/html; charset=utf-8 \n"
77 "Set-Cookie: a \n"
78 "Set-Cookie: b \n",
80 "HTTP/1.1 202 Accepted\n"
81 "Content-TYPE: text/html; charset=utf-8\n"
82 "Set-Cookie: a, b\n",
84 202,
85 net::HttpVersion(1,1),
86 net::HttpVersion(1,1)
88 TestCommon(test);
91 // Check that we normalize headers properly (header name is invalid if starts
92 // with LWS).
93 TEST(HttpResponseHeadersTest, NormalizeHeadersLeadingWhitespace) {
94 TestData test = {
95 "HTTP/1.1 202 Accepted \n"
96 // Starts with space -- will be skipped as invalid.
97 " Content-TYPE : text/html; charset=utf-8 \n"
98 "Set-Cookie: a \n"
99 "Set-Cookie: b \n",
101 "HTTP/1.1 202 Accepted\n"
102 "Set-Cookie: a, b\n",
104 202,
105 net::HttpVersion(1,1),
106 net::HttpVersion(1,1)
108 TestCommon(test);
111 TEST(HttpResponseHeadersTest, BlankHeaders) {
112 TestData test = {
113 "HTTP/1.1 200 OK\n"
114 "Header1 : \n"
115 "Header2: \n"
116 "Header3:\n"
117 "Header4\n"
118 "Header5 :\n",
120 "HTTP/1.1 200 OK\n"
121 "Header1: \n"
122 "Header2: \n"
123 "Header3: \n"
124 "Header5: \n",
126 200,
127 net::HttpVersion(1,1),
128 net::HttpVersion(1,1)
130 TestCommon(test);
133 TEST(HttpResponseHeadersTest, NormalizeHeadersVersion) {
134 // Don't believe the http/0.9 version if there are headers!
135 TestData test = {
136 "hTtP/0.9 201\n"
137 "Content-TYPE: text/html; charset=utf-8\n",
139 "HTTP/1.0 201 OK\n"
140 "Content-TYPE: text/html; charset=utf-8\n",
142 201,
143 net::HttpVersion(0,9),
144 net::HttpVersion(1,0)
146 TestCommon(test);
149 TEST(HttpResponseHeadersTest, PreserveHttp09) {
150 // Accept the HTTP/0.9 version number if there are no headers.
151 // This is how HTTP/0.9 responses get constructed from HttpNetworkTransaction.
152 TestData test = {
153 "hTtP/0.9 200 OK\n",
155 "HTTP/0.9 200 OK\n",
157 200,
158 net::HttpVersion(0,9),
159 net::HttpVersion(0,9)
161 TestCommon(test);
164 TEST(HttpResponseHeadersTest, NormalizeHeadersMissingOK) {
165 TestData test = {
166 "HTTP/1.1 201\n"
167 "Content-TYPE: text/html; charset=utf-8\n",
169 "HTTP/1.1 201 OK\n"
170 "Content-TYPE: text/html; charset=utf-8\n",
172 201,
173 net::HttpVersion(1,1),
174 net::HttpVersion(1,1)
176 TestCommon(test);
179 TEST(HttpResponseHeadersTest, NormalizeHeadersBadStatus) {
180 TestData test = {
181 "SCREWED_UP_STATUS_LINE\n"
182 "Content-TYPE: text/html; charset=utf-8\n",
184 "HTTP/1.0 200 OK\n"
185 "Content-TYPE: text/html; charset=utf-8\n",
187 200,
188 net::HttpVersion(0,0), // Parse error
189 net::HttpVersion(1,0)
191 TestCommon(test);
194 TEST(HttpResponseHeadersTest, NormalizeHeadersInvalidStatusCode) {
195 TestData test = {
196 "HTTP/1.1 -1 Unknown\n",
198 "HTTP/1.1 200 OK\n",
200 200,
201 net::HttpVersion(1,1),
202 net::HttpVersion(1,1)
204 TestCommon(test);
207 TEST(HttpResponseHeadersTest, NormalizeHeadersEmpty) {
208 TestData test = {
211 "HTTP/1.0 200 OK\n",
213 200,
214 net::HttpVersion(0,0), // Parse Error
215 net::HttpVersion(1,0)
217 TestCommon(test);
220 TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColon) {
221 TestData test = {
222 "HTTP/1.1 202 Accepted \n"
223 "foo: bar\n"
224 ": a \n"
225 " : b\n"
226 "baz: blat \n",
228 "HTTP/1.1 202 Accepted\n"
229 "foo: bar\n"
230 "baz: blat\n",
232 202,
233 net::HttpVersion(1,1),
234 net::HttpVersion(1,1)
236 TestCommon(test);
239 TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColonAtEOL) {
240 TestData test = {
241 "HTTP/1.1 202 Accepted \n"
242 "foo: \n"
243 "bar:\n"
244 "baz: blat \n"
245 "zip:\n",
247 "HTTP/1.1 202 Accepted\n"
248 "foo: \n"
249 "bar: \n"
250 "baz: blat\n"
251 "zip: \n",
253 202,
254 net::HttpVersion(1,1),
255 net::HttpVersion(1,1)
257 TestCommon(test);
260 TEST(HttpResponseHeadersTest, NormalizeHeadersOfWhitepace) {
261 TestData test = {
262 "\n \n",
264 "HTTP/1.0 200 OK\n",
266 200,
267 net::HttpVersion(0,0), // Parse error
268 net::HttpVersion(1,0)
270 TestCommon(test);
273 TEST(HttpResponseHeadersTest, RepeatedSetCookie) {
274 TestData test = {
275 "HTTP/1.1 200 OK\n"
276 "Set-Cookie: x=1\n"
277 "Set-Cookie: y=2\n",
279 "HTTP/1.1 200 OK\n"
280 "Set-Cookie: x=1, y=2\n",
282 200,
283 net::HttpVersion(1,1),
284 net::HttpVersion(1,1)
286 TestCommon(test);
289 TEST(HttpResponseHeadersTest, GetNormalizedHeader) {
290 std::string headers =
291 "HTTP/1.1 200 OK\n"
292 "Cache-control: private\n"
293 "cache-Control: no-store\n";
294 HeadersToRaw(&headers);
295 scoped_refptr<net::HttpResponseHeaders> parsed(
296 new net::HttpResponseHeaders(headers));
298 std::string value;
299 EXPECT_TRUE(parsed->GetNormalizedHeader("cache-control", &value));
300 EXPECT_EQ("private, no-store", value);
303 TEST(HttpResponseHeadersTest, Persist) {
304 const struct {
305 net::HttpResponseHeaders::PersistOptions options;
306 const char* raw_headers;
307 const char* expected_headers;
308 } tests[] = {
309 { net::HttpResponseHeaders::PERSIST_ALL,
310 "HTTP/1.1 200 OK\n"
311 "Cache-control:private\n"
312 "cache-Control:no-store\n",
314 "HTTP/1.1 200 OK\n"
315 "Cache-control: private, no-store\n"
317 { net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
318 "HTTP/1.1 200 OK\n"
319 "connection: keep-alive\n"
320 "server: blah\n",
322 "HTTP/1.1 200 OK\n"
323 "server: blah\n"
325 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE |
326 net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
327 "HTTP/1.1 200 OK\n"
328 "fOo: 1\n"
329 "Foo: 2\n"
330 "Transfer-Encoding: chunked\n"
331 "CoNnection: keep-alive\n"
332 "cache-control: private, no-cache=\"foo\"\n",
334 "HTTP/1.1 200 OK\n"
335 "cache-control: private, no-cache=\"foo\"\n"
337 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
338 "HTTP/1.1 200 OK\n"
339 "Foo: 2\n"
340 "Cache-Control: private,no-cache=\"foo, bar\"\n"
341 "bar",
343 "HTTP/1.1 200 OK\n"
344 "Cache-Control: private,no-cache=\"foo, bar\"\n"
346 // ignore bogus no-cache value
347 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
348 "HTTP/1.1 200 OK\n"
349 "Foo: 2\n"
350 "Cache-Control: private,no-cache=foo\n",
352 "HTTP/1.1 200 OK\n"
353 "Foo: 2\n"
354 "Cache-Control: private,no-cache=foo\n"
356 // ignore bogus no-cache value
357 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
358 "HTTP/1.1 200 OK\n"
359 "Foo: 2\n"
360 "Cache-Control: private, no-cache=\n",
362 "HTTP/1.1 200 OK\n"
363 "Foo: 2\n"
364 "Cache-Control: private, no-cache=\n"
366 // ignore empty no-cache value
367 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
368 "HTTP/1.1 200 OK\n"
369 "Foo: 2\n"
370 "Cache-Control: private, no-cache=\"\"\n",
372 "HTTP/1.1 200 OK\n"
373 "Foo: 2\n"
374 "Cache-Control: private, no-cache=\"\"\n"
376 // ignore wrong quotes no-cache value
377 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
378 "HTTP/1.1 200 OK\n"
379 "Foo: 2\n"
380 "Cache-Control: private, no-cache=\'foo\'\n",
382 "HTTP/1.1 200 OK\n"
383 "Foo: 2\n"
384 "Cache-Control: private, no-cache=\'foo\'\n"
386 // ignore unterminated quotes no-cache value
387 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
388 "HTTP/1.1 200 OK\n"
389 "Foo: 2\n"
390 "Cache-Control: private, no-cache=\"foo\n",
392 "HTTP/1.1 200 OK\n"
393 "Foo: 2\n"
394 "Cache-Control: private, no-cache=\"foo\n"
396 // accept sloppy LWS
397 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
398 "HTTP/1.1 200 OK\n"
399 "Foo: 2\n"
400 "Cache-Control: private, no-cache=\" foo\t, bar\"\n",
402 "HTTP/1.1 200 OK\n"
403 "Cache-Control: private, no-cache=\" foo\t, bar\"\n"
405 // header name appears twice, separated by another header
406 { net::HttpResponseHeaders::PERSIST_ALL,
407 "HTTP/1.1 200 OK\n"
408 "Foo: 1\n"
409 "Bar: 2\n"
410 "Foo: 3\n",
412 "HTTP/1.1 200 OK\n"
413 "Foo: 1, 3\n"
414 "Bar: 2\n"
416 // header name appears twice, separated by another header (type 2)
417 { net::HttpResponseHeaders::PERSIST_ALL,
418 "HTTP/1.1 200 OK\n"
419 "Foo: 1, 3\n"
420 "Bar: 2\n"
421 "Foo: 4\n",
423 "HTTP/1.1 200 OK\n"
424 "Foo: 1, 3, 4\n"
425 "Bar: 2\n"
427 // Test filtering of cookie headers.
428 { net::HttpResponseHeaders::PERSIST_SANS_COOKIES,
429 "HTTP/1.1 200 OK\n"
430 "Set-Cookie: foo=bar; httponly\n"
431 "Set-Cookie: bar=foo\n"
432 "Bar: 1\n"
433 "Set-Cookie2: bar2=foo2\n",
435 "HTTP/1.1 200 OK\n"
436 "Bar: 1\n"
438 // Test LWS at the end of a header.
439 { net::HttpResponseHeaders::PERSIST_ALL,
440 "HTTP/1.1 200 OK\n"
441 "Content-Length: 450 \n"
442 "Content-Encoding: gzip\n",
444 "HTTP/1.1 200 OK\n"
445 "Content-Length: 450\n"
446 "Content-Encoding: gzip\n"
448 // Test LWS at the end of a header.
449 { net::HttpResponseHeaders::PERSIST_RAW,
450 "HTTP/1.1 200 OK\n"
451 "Content-Length: 450 \n"
452 "Content-Encoding: gzip\n",
454 "HTTP/1.1 200 OK\n"
455 "Content-Length: 450\n"
456 "Content-Encoding: gzip\n"
458 // Test filtering of transport security state headers.
459 { net::HttpResponseHeaders::PERSIST_SANS_SECURITY_STATE,
460 "HTTP/1.1 200 OK\n"
461 "Strict-Transport-Security: max-age=1576800\n"
462 "Bar: 1\n"
463 "Public-Key-Pins: max-age=100000; "
464 "pin-sha1=\"ObT42aoSpAqWdY9WfRfL7i0HsVk=\";"
465 "pin-sha1=\"7kW49EVwZG0hSNx41ZO/fUPN0ek=\"",
467 "HTTP/1.1 200 OK\n"
468 "Bar: 1\n"
472 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
473 std::string headers = tests[i].raw_headers;
474 HeadersToRaw(&headers);
475 scoped_refptr<net::HttpResponseHeaders> parsed1(
476 new net::HttpResponseHeaders(headers));
478 Pickle pickle;
479 parsed1->Persist(&pickle, tests[i].options);
481 PickleIterator iter(pickle);
482 scoped_refptr<net::HttpResponseHeaders> parsed2(
483 new net::HttpResponseHeaders(pickle, &iter));
485 std::string h2;
486 parsed2->GetNormalizedHeaders(&h2);
487 EXPECT_EQ(std::string(tests[i].expected_headers), h2);
491 TEST(HttpResponseHeadersTest, EnumerateHeader_Coalesced) {
492 // Ensure that commas in quoted strings are not regarded as value separators.
493 // Ensure that whitespace following a value is trimmed properly
494 std::string headers =
495 "HTTP/1.1 200 OK\n"
496 "Cache-control:private , no-cache=\"set-cookie,server\" \n"
497 "cache-Control: no-store\n";
498 HeadersToRaw(&headers);
499 scoped_refptr<net::HttpResponseHeaders> parsed(
500 new net::HttpResponseHeaders(headers));
502 void* iter = NULL;
503 std::string value;
504 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
505 EXPECT_EQ("private", value);
506 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
507 EXPECT_EQ("no-cache=\"set-cookie,server\"", value);
508 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
509 EXPECT_EQ("no-store", value);
510 EXPECT_FALSE(parsed->EnumerateHeader(&iter, "cache-control", &value));
513 TEST(HttpResponseHeadersTest, EnumerateHeader_Challenge) {
514 // Even though WWW-Authenticate has commas, it should not be treated as
515 // coalesced values.
516 std::string headers =
517 "HTTP/1.1 401 OK\n"
518 "WWW-Authenticate:Digest realm=foobar, nonce=x, domain=y\n"
519 "WWW-Authenticate:Basic realm=quatar\n";
520 HeadersToRaw(&headers);
521 scoped_refptr<net::HttpResponseHeaders> parsed(
522 new net::HttpResponseHeaders(headers));
524 void* iter = NULL;
525 std::string value;
526 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
527 EXPECT_EQ("Digest realm=foobar, nonce=x, domain=y", value);
528 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
529 EXPECT_EQ("Basic realm=quatar", value);
530 EXPECT_FALSE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
533 TEST(HttpResponseHeadersTest, EnumerateHeader_DateValued) {
534 // The comma in a date valued header should not be treated as a
535 // field-value separator
536 std::string headers =
537 "HTTP/1.1 200 OK\n"
538 "Date: Tue, 07 Aug 2007 23:10:55 GMT\n"
539 "Last-Modified: Wed, 01 Aug 2007 23:23:45 GMT\n";
540 HeadersToRaw(&headers);
541 scoped_refptr<net::HttpResponseHeaders> parsed(
542 new net::HttpResponseHeaders(headers));
544 std::string value;
545 EXPECT_TRUE(parsed->EnumerateHeader(NULL, "date", &value));
546 EXPECT_EQ("Tue, 07 Aug 2007 23:10:55 GMT", value);
547 EXPECT_TRUE(parsed->EnumerateHeader(NULL, "last-modified", &value));
548 EXPECT_EQ("Wed, 01 Aug 2007 23:23:45 GMT", value);
551 TEST(HttpResponseHeadersTest, DefaultDateToGMT) {
552 // Verify we make the best interpretation when parsing dates that incorrectly
553 // do not end in "GMT" as RFC2616 requires.
554 std::string headers =
555 "HTTP/1.1 200 OK\n"
556 "Date: Tue, 07 Aug 2007 23:10:55\n"
557 "Last-Modified: Tue, 07 Aug 2007 19:10:55 EDT\n"
558 "Expires: Tue, 07 Aug 2007 23:10:55 UTC\n";
559 HeadersToRaw(&headers);
560 scoped_refptr<net::HttpResponseHeaders> parsed(
561 new net::HttpResponseHeaders(headers));
562 base::Time expected_value;
563 ASSERT_TRUE(base::Time::FromString("Tue, 07 Aug 2007 23:10:55 GMT",
564 &expected_value));
566 base::Time value;
567 // When the timezone is missing, GMT is a good guess as its what RFC2616
568 // requires.
569 EXPECT_TRUE(parsed->GetDateValue(&value));
570 EXPECT_EQ(expected_value, value);
571 // If GMT is missing but an RFC822-conforming one is present, use that.
572 EXPECT_TRUE(parsed->GetLastModifiedValue(&value));
573 EXPECT_EQ(expected_value, value);
574 // If an unknown timezone is present, treat like a missing timezone and
575 // default to GMT. The only example of a web server not specifying "GMT"
576 // used "UTC" which is equivalent to GMT.
577 if (parsed->GetExpiresValue(&value))
578 EXPECT_EQ(expected_value, value);
581 TEST(HttpResponseHeadersTest, GetMimeType) {
582 const ContentTypeTestData tests[] = {
583 { "HTTP/1.1 200 OK\n"
584 "Content-type: text/html\n",
585 "text/html", true,
586 "", false,
587 "text/html" },
588 // Multiple content-type headers should give us the last one.
589 { "HTTP/1.1 200 OK\n"
590 "Content-type: text/html\n"
591 "Content-type: text/html\n",
592 "text/html", true,
593 "", false,
594 "text/html, text/html" },
595 { "HTTP/1.1 200 OK\n"
596 "Content-type: text/plain\n"
597 "Content-type: text/html\n"
598 "Content-type: text/plain\n"
599 "Content-type: text/html\n",
600 "text/html", true,
601 "", false,
602 "text/plain, text/html, text/plain, text/html" },
603 // Test charset parsing.
604 { "HTTP/1.1 200 OK\n"
605 "Content-type: text/html\n"
606 "Content-type: text/html; charset=ISO-8859-1\n",
607 "text/html", true,
608 "iso-8859-1", true,
609 "text/html, text/html; charset=ISO-8859-1" },
610 // Test charset in double quotes.
611 { "HTTP/1.1 200 OK\n"
612 "Content-type: text/html\n"
613 "Content-type: text/html; charset=\"ISO-8859-1\"\n",
614 "text/html", true,
615 "iso-8859-1", true,
616 "text/html, text/html; charset=\"ISO-8859-1\"" },
617 // If there are multiple matching content-type headers, we carry
618 // over the charset value.
619 { "HTTP/1.1 200 OK\n"
620 "Content-type: text/html;charset=utf-8\n"
621 "Content-type: text/html\n",
622 "text/html", true,
623 "utf-8", true,
624 "text/html;charset=utf-8, text/html" },
625 // Test single quotes.
626 { "HTTP/1.1 200 OK\n"
627 "Content-type: text/html;charset='utf-8'\n"
628 "Content-type: text/html\n",
629 "text/html", true,
630 "utf-8", true,
631 "text/html;charset='utf-8', text/html" },
632 // Last charset wins if matching content-type.
633 { "HTTP/1.1 200 OK\n"
634 "Content-type: text/html;charset=utf-8\n"
635 "Content-type: text/html;charset=iso-8859-1\n",
636 "text/html", true,
637 "iso-8859-1", true,
638 "text/html;charset=utf-8, text/html;charset=iso-8859-1" },
639 // Charset is ignored if the content types change.
640 { "HTTP/1.1 200 OK\n"
641 "Content-type: text/plain;charset=utf-8\n"
642 "Content-type: text/html\n",
643 "text/html", true,
644 "", false,
645 "text/plain;charset=utf-8, text/html" },
646 // Empty content-type
647 { "HTTP/1.1 200 OK\n"
648 "Content-type: \n",
649 "", false,
650 "", false,
651 "" },
652 // Emtpy charset
653 { "HTTP/1.1 200 OK\n"
654 "Content-type: text/html;charset=\n",
655 "text/html", true,
656 "", false,
657 "text/html;charset=" },
658 // Multiple charsets, last one wins.
659 { "HTTP/1.1 200 OK\n"
660 "Content-type: text/html;charset=utf-8; charset=iso-8859-1\n",
661 "text/html", true,
662 "iso-8859-1", true,
663 "text/html;charset=utf-8; charset=iso-8859-1" },
664 // Multiple params.
665 { "HTTP/1.1 200 OK\n"
666 "Content-type: text/html; foo=utf-8; charset=iso-8859-1\n",
667 "text/html", true,
668 "iso-8859-1", true,
669 "text/html; foo=utf-8; charset=iso-8859-1" },
670 { "HTTP/1.1 200 OK\n"
671 "Content-type: text/html ; charset=utf-8 ; bar=iso-8859-1\n",
672 "text/html", true,
673 "utf-8", true,
674 "text/html ; charset=utf-8 ; bar=iso-8859-1" },
675 // Comma embeded in quotes.
676 { "HTTP/1.1 200 OK\n"
677 "Content-type: text/html ; charset='utf-8,text/plain' ;\n",
678 "text/html", true,
679 "utf-8,text/plain", true,
680 "text/html ; charset='utf-8,text/plain' ;" },
681 // Charset with leading spaces.
682 { "HTTP/1.1 200 OK\n"
683 "Content-type: text/html ; charset= 'utf-8' ;\n",
684 "text/html", true,
685 "utf-8", true,
686 "text/html ; charset= 'utf-8' ;" },
687 // Media type comments in mime-type.
688 { "HTTP/1.1 200 OK\n"
689 "Content-type: text/html (html)\n",
690 "text/html", true,
691 "", false,
692 "text/html (html)" },
693 // Incomplete charset= param
694 { "HTTP/1.1 200 OK\n"
695 "Content-type: text/html; char=\n",
696 "text/html", true,
697 "", false,
698 "text/html; char=" },
699 // Invalid media type: no slash
700 { "HTTP/1.1 200 OK\n"
701 "Content-type: texthtml\n",
702 "", false,
703 "", false,
704 "texthtml" },
705 // Invalid media type: */*
706 { "HTTP/1.1 200 OK\n"
707 "Content-type: */*\n",
708 "", false,
709 "", false,
710 "*/*" },
713 for (size_t i = 0; i < arraysize(tests); ++i) {
714 std::string headers(tests[i].raw_headers);
715 HeadersToRaw(&headers);
716 scoped_refptr<net::HttpResponseHeaders> parsed(
717 new net::HttpResponseHeaders(headers));
719 std::string value;
720 EXPECT_EQ(tests[i].has_mimetype, parsed->GetMimeType(&value));
721 EXPECT_EQ(tests[i].mime_type, value);
722 value.clear();
723 EXPECT_EQ(tests[i].has_charset, parsed->GetCharset(&value));
724 EXPECT_EQ(tests[i].charset, value);
725 EXPECT_TRUE(parsed->GetNormalizedHeader("content-type", &value));
726 EXPECT_EQ(tests[i].all_content_type, value);
730 TEST(HttpResponseHeadersTest, RequiresValidation) {
731 const struct {
732 const char* headers;
733 bool requires_validation;
734 } tests[] = {
735 // no expiry info: expires immediately
736 { "HTTP/1.1 200 OK\n"
737 "\n",
738 true
740 // valid for a little while
741 { "HTTP/1.1 200 OK\n"
742 "cache-control: max-age=10000\n"
743 "\n",
744 false
746 // expires in the future
747 { "HTTP/1.1 200 OK\n"
748 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
749 "expires: Wed, 28 Nov 2007 01:00:00 GMT\n"
750 "\n",
751 false
753 // expired already
754 { "HTTP/1.1 200 OK\n"
755 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
756 "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
757 "\n",
758 true
760 // max-age trumps expires
761 { "HTTP/1.1 200 OK\n"
762 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
763 "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
764 "cache-control: max-age=10000\n"
765 "\n",
766 false
768 // last-modified heuristic: modified a while ago
769 { "HTTP/1.1 200 OK\n"
770 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
771 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
772 "\n",
773 false
775 { "HTTP/1.1 203 Non-Authoritative Information\n"
776 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
777 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
778 "\n",
779 false
781 { "HTTP/1.1 206 Partial Content\n"
782 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
783 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
784 "\n",
785 false
787 // last-modified heuristic: modified recently
788 { "HTTP/1.1 200 OK\n"
789 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
790 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
791 "\n",
792 true
794 { "HTTP/1.1 203 Non-Authoritative Information\n"
795 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
796 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
797 "\n",
798 true
800 { "HTTP/1.1 206 Partial Content\n"
801 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
802 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
803 "\n",
804 true
806 // cached permanent redirect
807 { "HTTP/1.1 301 Moved Permanently\n"
808 "\n",
809 false
811 // another cached permanent redirect
812 { "HTTP/1.1 308 Permanent Redirect\n"
813 "\n",
814 false
816 // cached redirect: not reusable even though by default it would be
817 { "HTTP/1.1 300 Multiple Choices\n"
818 "Cache-Control: no-cache\n"
819 "\n",
820 true
822 // cached forever by default
823 { "HTTP/1.1 410 Gone\n"
824 "\n",
825 false
827 // cached temporary redirect: not reusable
828 { "HTTP/1.1 302 Found\n"
829 "\n",
830 true
832 // cached temporary redirect: reusable
833 { "HTTP/1.1 302 Found\n"
834 "cache-control: max-age=10000\n"
835 "\n",
836 false
838 // cache-control: max-age=N overrides expires: date in the past
839 { "HTTP/1.1 200 OK\n"
840 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
841 "expires: Wed, 28 Nov 2007 00:20:11 GMT\n"
842 "cache-control: max-age=10000\n"
843 "\n",
844 false
846 // cache-control: no-store overrides expires: in the future
847 { "HTTP/1.1 200 OK\n"
848 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
849 "expires: Wed, 29 Nov 2007 00:40:11 GMT\n"
850 "cache-control: no-store,private,no-cache=\"foo\"\n"
851 "\n",
852 true
854 // pragma: no-cache overrides last-modified heuristic
855 { "HTTP/1.1 200 OK\n"
856 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
857 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
858 "pragma: no-cache\n"
859 "\n",
860 true
862 // TODO(darin): add many many more tests here
864 base::Time request_time, response_time, current_time;
865 base::Time::FromString("Wed, 28 Nov 2007 00:40:09 GMT", &request_time);
866 base::Time::FromString("Wed, 28 Nov 2007 00:40:12 GMT", &response_time);
867 base::Time::FromString("Wed, 28 Nov 2007 00:45:20 GMT", &current_time);
869 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
870 std::string headers(tests[i].headers);
871 HeadersToRaw(&headers);
872 scoped_refptr<net::HttpResponseHeaders> parsed(
873 new net::HttpResponseHeaders(headers));
875 bool requires_validation =
876 parsed->RequiresValidation(request_time, response_time, current_time);
877 EXPECT_EQ(tests[i].requires_validation, requires_validation);
881 TEST(HttpResponseHeadersTest, Update) {
882 const struct {
883 const char* orig_headers;
884 const char* new_headers;
885 const char* expected_headers;
886 } tests[] = {
887 { "HTTP/1.1 200 OK\n",
889 "HTTP/1/1 304 Not Modified\n"
890 "connection: keep-alive\n"
891 "Cache-control: max-age=10000\n",
893 "HTTP/1.1 200 OK\n"
894 "Cache-control: max-age=10000\n"
896 { "HTTP/1.1 200 OK\n"
897 "Foo: 1\n"
898 "Cache-control: private\n",
900 "HTTP/1/1 304 Not Modified\n"
901 "connection: keep-alive\n"
902 "Cache-control: max-age=10000\n",
904 "HTTP/1.1 200 OK\n"
905 "Cache-control: max-age=10000\n"
906 "Foo: 1\n"
908 { "HTTP/1.1 200 OK\n"
909 "Foo: 1\n"
910 "Cache-control: private\n",
912 "HTTP/1/1 304 Not Modified\n"
913 "connection: keep-alive\n"
914 "Cache-CONTROL: max-age=10000\n",
916 "HTTP/1.1 200 OK\n"
917 "Cache-CONTROL: max-age=10000\n"
918 "Foo: 1\n"
920 { "HTTP/1.1 200 OK\n"
921 "Content-Length: 450\n",
923 "HTTP/1/1 304 Not Modified\n"
924 "connection: keep-alive\n"
925 "Cache-control: max-age=10001 \n",
927 "HTTP/1.1 200 OK\n"
928 "Cache-control: max-age=10001\n"
929 "Content-Length: 450\n"
931 { "HTTP/1.1 200 OK\n"
932 "X-Frame-Options: DENY\n",
934 "HTTP/1/1 304 Not Modified\n"
935 "X-Frame-Options: ALLOW\n",
937 "HTTP/1.1 200 OK\n"
938 "X-Frame-Options: DENY\n",
940 { "HTTP/1.1 200 OK\n"
941 "X-WebKit-CSP: default-src 'none'\n",
943 "HTTP/1/1 304 Not Modified\n"
944 "X-WebKit-CSP: default-src *\n",
946 "HTTP/1.1 200 OK\n"
947 "X-WebKit-CSP: default-src 'none'\n",
949 { "HTTP/1.1 200 OK\n"
950 "X-XSS-Protection: 1\n",
952 "HTTP/1/1 304 Not Modified\n"
953 "X-XSS-Protection: 0\n",
955 "HTTP/1.1 200 OK\n"
956 "X-XSS-Protection: 1\n",
958 { "HTTP/1.1 200 OK\n",
960 "HTTP/1/1 304 Not Modified\n"
961 "X-Content-Type-Options: nosniff\n",
963 "HTTP/1.1 200 OK\n"
967 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
968 std::string orig_headers(tests[i].orig_headers);
969 HeadersToRaw(&orig_headers);
970 scoped_refptr<net::HttpResponseHeaders> parsed(
971 new net::HttpResponseHeaders(orig_headers));
973 std::string new_headers(tests[i].new_headers);
974 HeadersToRaw(&new_headers);
975 scoped_refptr<net::HttpResponseHeaders> new_parsed(
976 new net::HttpResponseHeaders(new_headers));
978 parsed->Update(*new_parsed.get());
980 std::string resulting_headers;
981 parsed->GetNormalizedHeaders(&resulting_headers);
982 EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
986 TEST(HttpResponseHeadersTest, EnumerateHeaderLines) {
987 const struct {
988 const char* headers;
989 const char* expected_lines;
990 } tests[] = {
991 { "HTTP/1.1 200 OK\n",
995 { "HTTP/1.1 200 OK\n"
996 "Foo: 1\n",
998 "Foo: 1\n"
1000 { "HTTP/1.1 200 OK\n"
1001 "Foo: 1\n"
1002 "Bar: 2\n"
1003 "Foo: 3\n",
1005 "Foo: 1\nBar: 2\nFoo: 3\n"
1007 { "HTTP/1.1 200 OK\n"
1008 "Foo: 1, 2, 3\n",
1010 "Foo: 1, 2, 3\n"
1013 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1014 std::string headers(tests[i].headers);
1015 HeadersToRaw(&headers);
1016 scoped_refptr<net::HttpResponseHeaders> parsed(
1017 new net::HttpResponseHeaders(headers));
1019 std::string name, value, lines;
1021 void* iter = NULL;
1022 while (parsed->EnumerateHeaderLines(&iter, &name, &value)) {
1023 lines.append(name);
1024 lines.append(": ");
1025 lines.append(value);
1026 lines.append("\n");
1029 EXPECT_EQ(std::string(tests[i].expected_lines), lines);
1033 TEST(HttpResponseHeadersTest, IsRedirect) {
1034 const struct {
1035 const char* headers;
1036 const char* location;
1037 bool is_redirect;
1038 } tests[] = {
1039 { "HTTP/1.1 200 OK\n",
1041 false
1043 { "HTTP/1.1 301 Moved\n"
1044 "Location: http://foopy/\n",
1045 "http://foopy/",
1046 true
1048 { "HTTP/1.1 301 Moved\n"
1049 "Location: \t \n",
1051 false
1053 // we use the first location header as the target of the redirect
1054 { "HTTP/1.1 301 Moved\n"
1055 "Location: http://foo/\n"
1056 "Location: http://bar/\n",
1057 "http://foo/",
1058 true
1060 // we use the first _valid_ location header as the target of the redirect
1061 { "HTTP/1.1 301 Moved\n"
1062 "Location: \n"
1063 "Location: http://bar/\n",
1064 "http://bar/",
1065 true
1067 // bug 1050541 (location header w/ an unescaped comma)
1068 { "HTTP/1.1 301 Moved\n"
1069 "Location: http://foo/bar,baz.html\n",
1070 "http://foo/bar,baz.html",
1071 true
1073 // bug 1224617 (location header w/ non-ASCII bytes)
1074 { "HTTP/1.1 301 Moved\n"
1075 "Location: http://foo/bar?key=\xE4\xF6\xFC\n",
1076 "http://foo/bar?key=%E4%F6%FC",
1077 true
1079 // Shift_JIS, Big5, and GBK contain multibyte characters with the trailing
1080 // byte falling in the ASCII range.
1081 { "HTTP/1.1 301 Moved\n"
1082 "Location: http://foo/bar?key=\x81\x5E\xD8\xBF\n",
1083 "http://foo/bar?key=%81^%D8%BF",
1084 true
1086 { "HTTP/1.1 301 Moved\n"
1087 "Location: http://foo/bar?key=\x82\x40\xBD\xC4\n",
1088 "http://foo/bar?key=%82@%BD%C4",
1089 true
1091 { "HTTP/1.1 301 Moved\n"
1092 "Location: http://foo/bar?key=\x83\x5C\x82\x5D\xCB\xD7\n",
1093 "http://foo/bar?key=%83\\%82]%CB%D7",
1094 true
1097 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1098 std::string headers(tests[i].headers);
1099 HeadersToRaw(&headers);
1100 scoped_refptr<net::HttpResponseHeaders> parsed(
1101 new net::HttpResponseHeaders(headers));
1103 std::string location;
1104 EXPECT_EQ(parsed->IsRedirect(&location), tests[i].is_redirect);
1105 EXPECT_EQ(location, tests[i].location);
1109 TEST(HttpResponseHeadersTest, GetContentLength) {
1110 const struct {
1111 const char* headers;
1112 int64 expected_len;
1113 } tests[] = {
1114 { "HTTP/1.1 200 OK\n",
1117 { "HTTP/1.1 200 OK\n"
1118 "Content-Length: 10\n",
1121 { "HTTP/1.1 200 OK\n"
1122 "Content-Length: \n",
1125 { "HTTP/1.1 200 OK\n"
1126 "Content-Length: abc\n",
1129 { "HTTP/1.1 200 OK\n"
1130 "Content-Length: -10\n",
1133 { "HTTP/1.1 200 OK\n"
1134 "Content-Length: +10\n",
1137 { "HTTP/1.1 200 OK\n"
1138 "Content-Length: 23xb5\n",
1141 { "HTTP/1.1 200 OK\n"
1142 "Content-Length: 0xA\n",
1145 { "HTTP/1.1 200 OK\n"
1146 "Content-Length: 010\n",
1149 // Content-Length too big, will overflow an int64
1150 { "HTTP/1.1 200 OK\n"
1151 "Content-Length: 40000000000000000000\n",
1154 { "HTTP/1.1 200 OK\n"
1155 "Content-Length: 10\n",
1158 { "HTTP/1.1 200 OK\n"
1159 "Content-Length: 10 \n",
1162 { "HTTP/1.1 200 OK\n"
1163 "Content-Length: \t10\n",
1166 { "HTTP/1.1 200 OK\n"
1167 "Content-Length: \v10\n",
1170 { "HTTP/1.1 200 OK\n"
1171 "Content-Length: \f10\n",
1174 { "HTTP/1.1 200 OK\n"
1175 "cOnTeNt-LENgth: 33\n",
1178 { "HTTP/1.1 200 OK\n"
1179 "Content-Length: 34\r\n",
1183 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1184 std::string headers(tests[i].headers);
1185 HeadersToRaw(&headers);
1186 scoped_refptr<net::HttpResponseHeaders> parsed(
1187 new net::HttpResponseHeaders(headers));
1189 EXPECT_EQ(tests[i].expected_len, parsed->GetContentLength());
1193 TEST(HttpResponseHeaders, GetContentRange) {
1194 const struct {
1195 const char* headers;
1196 bool expected_return_value;
1197 int64 expected_first_byte_position;
1198 int64 expected_last_byte_position;
1199 int64 expected_instance_size;
1200 } tests[] = {
1201 { "HTTP/1.1 206 Partial Content",
1202 false,
1207 { "HTTP/1.1 206 Partial Content\n"
1208 "Content-Range:",
1209 false,
1214 { "HTTP/1.1 206 Partial Content\n"
1215 "Content-Range: megabytes 0-10/50",
1216 false,
1221 { "HTTP/1.1 206 Partial Content\n"
1222 "Content-Range: 0-10/50",
1223 false,
1228 { "HTTP/1.1 206 Partial Content\n"
1229 "Content-Range: Bytes 0-50/51",
1230 true,
1235 { "HTTP/1.1 206 Partial Content\n"
1236 "Content-Range: bytes 0-50/51",
1237 true,
1242 { "HTTP/1.1 206 Partial Content\n"
1243 "Content-Range: bytes\t0-50/51",
1244 false,
1249 { "HTTP/1.1 206 Partial Content\n"
1250 "Content-Range: bytes 0-50/51",
1251 true,
1256 { "HTTP/1.1 206 Partial Content\n"
1257 "Content-Range: bytes 0 - 50 \t / \t51",
1258 true,
1263 { "HTTP/1.1 206 Partial Content\n"
1264 "Content-Range: bytes 0\t-\t50\t/\t51\t",
1265 true,
1270 { "HTTP/1.1 206 Partial Content\n"
1271 "Content-Range: \tbytes\t\t\t 0\t-\t50\t/\t51\t",
1272 true,
1277 { "HTTP/1.1 206 Partial Content\n"
1278 "Content-Range: \t bytes \t 0 - 50 / 5 1",
1279 false,
1284 { "HTTP/1.1 206 Partial Content\n"
1285 "Content-Range: \t bytes \t 0 - 5 0 / 51",
1286 false,
1291 { "HTTP/1.1 206 Partial Content\n"
1292 "Content-Range: bytes 50-0/51",
1293 false,
1298 { "HTTP/1.1 416 Requested range not satisfiable\n"
1299 "Content-Range: bytes * /*",
1300 false,
1305 { "HTTP/1.1 416 Requested range not satisfiable\n"
1306 "Content-Range: bytes * / * ",
1307 false,
1312 { "HTTP/1.1 206 Partial Content\n"
1313 "Content-Range: bytes 0-50/*",
1314 false,
1319 { "HTTP/1.1 206 Partial Content\n"
1320 "Content-Range: bytes 0-50 / * ",
1321 false,
1326 { "HTTP/1.1 206 Partial Content\n"
1327 "Content-Range: bytes 0-10000000000/10000000001",
1328 true,
1330 10000000000ll,
1331 10000000001ll
1333 { "HTTP/1.1 206 Partial Content\n"
1334 "Content-Range: bytes 0-10000000000/10000000000",
1335 false,
1337 10000000000ll,
1338 10000000000ll
1340 // 64 bits wraparound.
1341 { "HTTP/1.1 206 Partial Content\n"
1342 "Content-Range: bytes 0 - 9223372036854775807 / 100",
1343 false,
1345 kint64max,
1348 // 64 bits wraparound.
1349 { "HTTP/1.1 206 Partial Content\n"
1350 "Content-Range: bytes 0 - 100 / -9223372036854775808",
1351 false,
1353 100,
1354 kint64min
1356 { "HTTP/1.1 206 Partial Content\n"
1357 "Content-Range: bytes */50",
1358 false,
1363 { "HTTP/1.1 206 Partial Content\n"
1364 "Content-Range: bytes 0-50/10",
1365 false,
1370 { "HTTP/1.1 206 Partial Content\n"
1371 "Content-Range: bytes 40-50/45",
1372 false,
1377 { "HTTP/1.1 206 Partial Content\n"
1378 "Content-Range: bytes 0-50/-10",
1379 false,
1384 { "HTTP/1.1 206 Partial Content\n"
1385 "Content-Range: bytes 0-0/1",
1386 true,
1391 { "HTTP/1.1 206 Partial Content\n"
1392 "Content-Range: bytes 0-40000000000000000000/40000000000000000001",
1393 false,
1398 { "HTTP/1.1 206 Partial Content\n"
1399 "Content-Range: bytes 1-/100",
1400 false,
1405 { "HTTP/1.1 206 Partial Content\n"
1406 "Content-Range: bytes -/100",
1407 false,
1412 { "HTTP/1.1 206 Partial Content\n"
1413 "Content-Range: bytes -1/100",
1414 false,
1419 { "HTTP/1.1 206 Partial Content\n"
1420 "Content-Range: bytes 0-1233/*",
1421 false,
1423 1233,
1426 { "HTTP/1.1 206 Partial Content\n"
1427 "Content-Range: bytes -123 - -1/100",
1428 false,
1434 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1435 std::string headers(tests[i].headers);
1436 HeadersToRaw(&headers);
1437 scoped_refptr<net::HttpResponseHeaders> parsed(
1438 new net::HttpResponseHeaders(headers));
1440 int64 first_byte_position;
1441 int64 last_byte_position;
1442 int64 instance_size;
1443 bool return_value = parsed->GetContentRange(&first_byte_position,
1444 &last_byte_position,
1445 &instance_size);
1446 EXPECT_EQ(tests[i].expected_return_value, return_value);
1447 EXPECT_EQ(tests[i].expected_first_byte_position, first_byte_position);
1448 EXPECT_EQ(tests[i].expected_last_byte_position, last_byte_position);
1449 EXPECT_EQ(tests[i].expected_instance_size, instance_size);
1453 TEST(HttpResponseHeadersTest, IsKeepAlive) {
1454 const struct {
1455 const char* headers;
1456 bool expected_keep_alive;
1457 } tests[] = {
1458 // The status line fabricated by HttpNetworkTransaction for a 0.9 response.
1459 // Treated as 0.9.
1460 { "HTTP/0.9 200 OK",
1461 false
1463 // This could come from a broken server. Treated as 1.0 because it has a
1464 // header.
1465 { "HTTP/0.9 200 OK\n"
1466 "connection: keep-alive\n",
1467 true
1469 { "HTTP/1.1 200 OK\n",
1470 true
1472 { "HTTP/1.0 200 OK\n",
1473 false
1475 { "HTTP/1.0 200 OK\n"
1476 "connection: close\n",
1477 false
1479 { "HTTP/1.0 200 OK\n"
1480 "connection: keep-alive\n",
1481 true
1483 { "HTTP/1.0 200 OK\n"
1484 "connection: kEeP-AliVe\n",
1485 true
1487 { "HTTP/1.0 200 OK\n"
1488 "connection: keep-aliveX\n",
1489 false
1491 { "HTTP/1.1 200 OK\n"
1492 "connection: close\n",
1493 false
1495 { "HTTP/1.1 200 OK\n"
1496 "connection: keep-alive\n",
1497 true
1499 { "HTTP/1.0 200 OK\n"
1500 "proxy-connection: close\n",
1501 false
1503 { "HTTP/1.0 200 OK\n"
1504 "proxy-connection: keep-alive\n",
1505 true
1507 { "HTTP/1.1 200 OK\n"
1508 "proxy-connection: close\n",
1509 false
1511 { "HTTP/1.1 200 OK\n"
1512 "proxy-connection: keep-alive\n",
1513 true
1516 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1517 std::string headers(tests[i].headers);
1518 HeadersToRaw(&headers);
1519 scoped_refptr<net::HttpResponseHeaders> parsed(
1520 new net::HttpResponseHeaders(headers));
1522 EXPECT_EQ(tests[i].expected_keep_alive, parsed->IsKeepAlive());
1526 TEST(HttpResponseHeadersTest, HasStrongValidators) {
1527 const struct {
1528 const char* headers;
1529 bool expected_result;
1530 } tests[] = {
1531 { "HTTP/0.9 200 OK",
1532 false
1534 { "HTTP/1.0 200 OK\n"
1535 "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
1536 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
1537 "ETag: \"foo\"\n",
1538 false
1540 { "HTTP/1.1 200 OK\n"
1541 "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
1542 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
1543 "ETag: \"foo\"\n",
1544 true
1546 { "HTTP/1.1 200 OK\n"
1547 "Date: Wed, 28 Nov 2007 00:41:10 GMT\n"
1548 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1549 true
1551 { "HTTP/1.1 200 OK\n"
1552 "Date: Wed, 28 Nov 2007 00:41:09 GMT\n"
1553 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1554 false
1556 { "HTTP/1.1 200 OK\n"
1557 "ETag: \"foo\"\n",
1558 true
1560 // This is not really a weak etag:
1561 { "HTTP/1.1 200 OK\n"
1562 "etag: \"w/foo\"\n",
1563 true
1565 // This is a weak etag:
1566 { "HTTP/1.1 200 OK\n"
1567 "etag: w/\"foo\"\n",
1568 false
1570 { "HTTP/1.1 200 OK\n"
1571 "etag: W / \"foo\"\n",
1572 false
1575 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1576 std::string headers(tests[i].headers);
1577 HeadersToRaw(&headers);
1578 scoped_refptr<net::HttpResponseHeaders> parsed(
1579 new net::HttpResponseHeaders(headers));
1581 EXPECT_EQ(tests[i].expected_result, parsed->HasStrongValidators()) <<
1582 "Failed test case " << i;
1586 TEST(HttpResponseHeadersTest, GetStatusText) {
1587 std::string headers("HTTP/1.1 404 Not Found");
1588 HeadersToRaw(&headers);
1589 scoped_refptr<net::HttpResponseHeaders> parsed(
1590 new net::HttpResponseHeaders(headers));
1591 EXPECT_EQ(std::string("Not Found"), parsed->GetStatusText());
1594 TEST(HttpResponseHeadersTest, GetStatusTextMissing) {
1595 std::string headers("HTTP/1.1 404");
1596 HeadersToRaw(&headers);
1597 scoped_refptr<net::HttpResponseHeaders> parsed(
1598 new net::HttpResponseHeaders(headers));
1599 // Since the status line gets normalized, we have OK
1600 EXPECT_EQ(std::string("OK"), parsed->GetStatusText());
1603 TEST(HttpResponseHeadersTest, GetStatusTextMultiSpace) {
1604 std::string headers("HTTP/1.0 404 Not Found");
1605 HeadersToRaw(&headers);
1606 scoped_refptr<net::HttpResponseHeaders> parsed(
1607 new net::HttpResponseHeaders(headers));
1608 EXPECT_EQ(std::string("Not Found"), parsed->GetStatusText());
1611 TEST(HttpResponseHeadersTest, GetStatusBadStatusLine) {
1612 std::string headers("Foo bar.");
1613 HeadersToRaw(&headers);
1614 scoped_refptr<net::HttpResponseHeaders> parsed(
1615 new net::HttpResponseHeaders(headers));
1616 // The bad status line would have gotten rewritten as
1617 // HTTP/1.0 200 OK.
1618 EXPECT_EQ(std::string("OK"), parsed->GetStatusText());
1621 TEST(HttpResponseHeadersTest, AddHeader) {
1622 const struct {
1623 const char* orig_headers;
1624 const char* new_header;
1625 const char* expected_headers;
1626 } tests[] = {
1627 { "HTTP/1.1 200 OK\n"
1628 "connection: keep-alive\n"
1629 "Cache-control: max-age=10000\n",
1631 "Content-Length: 450",
1633 "HTTP/1.1 200 OK\n"
1634 "connection: keep-alive\n"
1635 "Cache-control: max-age=10000\n"
1636 "Content-Length: 450\n"
1638 { "HTTP/1.1 200 OK\n"
1639 "connection: keep-alive\n"
1640 "Cache-control: max-age=10000 \n",
1642 "Content-Length: 450 ",
1644 "HTTP/1.1 200 OK\n"
1645 "connection: keep-alive\n"
1646 "Cache-control: max-age=10000\n"
1647 "Content-Length: 450\n"
1651 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1652 std::string orig_headers(tests[i].orig_headers);
1653 HeadersToRaw(&orig_headers);
1654 scoped_refptr<net::HttpResponseHeaders> parsed(
1655 new net::HttpResponseHeaders(orig_headers));
1657 std::string new_header(tests[i].new_header);
1658 parsed->AddHeader(new_header);
1660 std::string resulting_headers;
1661 parsed->GetNormalizedHeaders(&resulting_headers);
1662 EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1666 TEST(HttpResponseHeadersTest, RemoveHeader) {
1667 const struct {
1668 const char* orig_headers;
1669 const char* to_remove;
1670 const char* expected_headers;
1671 } tests[] = {
1672 { "HTTP/1.1 200 OK\n"
1673 "connection: keep-alive\n"
1674 "Cache-control: max-age=10000\n"
1675 "Content-Length: 450\n",
1677 "Content-Length",
1679 "HTTP/1.1 200 OK\n"
1680 "connection: keep-alive\n"
1681 "Cache-control: max-age=10000\n"
1683 { "HTTP/1.1 200 OK\n"
1684 "connection: keep-alive \n"
1685 "Content-Length : 450 \n"
1686 "Cache-control: max-age=10000\n",
1688 "Content-Length",
1690 "HTTP/1.1 200 OK\n"
1691 "connection: keep-alive\n"
1692 "Cache-control: max-age=10000\n"
1696 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1697 std::string orig_headers(tests[i].orig_headers);
1698 HeadersToRaw(&orig_headers);
1699 scoped_refptr<net::HttpResponseHeaders> parsed(
1700 new net::HttpResponseHeaders(orig_headers));
1702 std::string name(tests[i].to_remove);
1703 parsed->RemoveHeader(name);
1705 std::string resulting_headers;
1706 parsed->GetNormalizedHeaders(&resulting_headers);
1707 EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1711 TEST(HttpResponseHeadersTest, RemoveIndividualHeader) {
1712 const struct {
1713 const char* orig_headers;
1714 const char* to_remove_name;
1715 const char* to_remove_value;
1716 const char* expected_headers;
1717 } tests[] = {
1718 { "HTTP/1.1 200 OK\n"
1719 "connection: keep-alive\n"
1720 "Cache-control: max-age=10000\n"
1721 "Content-Length: 450\n",
1723 "Content-Length",
1725 "450",
1727 "HTTP/1.1 200 OK\n"
1728 "connection: keep-alive\n"
1729 "Cache-control: max-age=10000\n"
1731 { "HTTP/1.1 200 OK\n"
1732 "connection: keep-alive \n"
1733 "Content-Length : 450 \n"
1734 "Cache-control: max-age=10000\n",
1736 "Content-Length",
1738 "450",
1740 "HTTP/1.1 200 OK\n"
1741 "connection: keep-alive\n"
1742 "Cache-control: max-age=10000\n"
1744 { "HTTP/1.1 200 OK\n"
1745 "connection: keep-alive \n"
1746 "Content-Length: 450\n"
1747 "Cache-control: max-age=10000\n",
1749 "Content-Length", // Matching name.
1751 "999", // Mismatching value.
1753 "HTTP/1.1 200 OK\n"
1754 "connection: keep-alive\n"
1755 "Content-Length: 450\n"
1756 "Cache-control: max-age=10000\n"
1758 { "HTTP/1.1 200 OK\n"
1759 "connection: keep-alive \n"
1760 "Foo: bar, baz\n"
1761 "Foo: bar\n"
1762 "Cache-control: max-age=10000\n",
1764 "Foo",
1766 "bar, baz", // Space in value.
1768 "HTTP/1.1 200 OK\n"
1769 "connection: keep-alive\n"
1770 "Foo: bar\n"
1771 "Cache-control: max-age=10000\n"
1773 { "HTTP/1.1 200 OK\n"
1774 "connection: keep-alive \n"
1775 "Foo: bar, baz\n"
1776 "Cache-control: max-age=10000\n",
1778 "Foo",
1780 "baz", // Only partial match -> ignored.
1782 "HTTP/1.1 200 OK\n"
1783 "connection: keep-alive\n"
1784 "Foo: bar, baz\n"
1785 "Cache-control: max-age=10000\n"
1789 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1790 std::string orig_headers(tests[i].orig_headers);
1791 HeadersToRaw(&orig_headers);
1792 scoped_refptr<net::HttpResponseHeaders> parsed(
1793 new net::HttpResponseHeaders(orig_headers));
1795 std::string name(tests[i].to_remove_name);
1796 std::string value(tests[i].to_remove_value);
1797 parsed->RemoveHeaderLine(name, value);
1799 std::string resulting_headers;
1800 parsed->GetNormalizedHeaders(&resulting_headers);
1801 EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1805 TEST(HttpResponseHeadersTest, ReplaceStatus) {
1806 const struct {
1807 const char* orig_headers;
1808 const char* new_status;
1809 const char* expected_headers;
1810 } tests[] = {
1811 { "HTTP/1.1 206 Partial Content\n"
1812 "connection: keep-alive\n"
1813 "Cache-control: max-age=10000\n"
1814 "Content-Length: 450\n",
1816 "HTTP/1.1 200 OK",
1818 "HTTP/1.1 200 OK\n"
1819 "connection: keep-alive\n"
1820 "Cache-control: max-age=10000\n"
1821 "Content-Length: 450\n"
1823 { "HTTP/1.1 200 OK\n"
1824 "connection: keep-alive\n",
1826 "HTTP/1.1 304 Not Modified",
1828 "HTTP/1.1 304 Not Modified\n"
1829 "connection: keep-alive\n"
1831 { "HTTP/1.1 200 OK\n"
1832 "connection: keep-alive \n"
1833 "Content-Length : 450 \n"
1834 "Cache-control: max-age=10000\n",
1836 "HTTP/1//1 304 Not Modified",
1838 "HTTP/1.0 304 Not Modified\n"
1839 "connection: keep-alive\n"
1840 "Content-Length: 450\n"
1841 "Cache-control: max-age=10000\n"
1845 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1846 std::string orig_headers(tests[i].orig_headers);
1847 HeadersToRaw(&orig_headers);
1848 scoped_refptr<net::HttpResponseHeaders> parsed(
1849 new net::HttpResponseHeaders(orig_headers));
1851 std::string name(tests[i].new_status);
1852 parsed->ReplaceStatusLine(name);
1854 std::string resulting_headers;
1855 parsed->GetNormalizedHeaders(&resulting_headers);
1856 EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1860 TEST(HttpResponseHeadersTest, UpdateWithNewRange) {
1861 const struct {
1862 const char* orig_headers;
1863 const char* expected_headers;
1864 const char* expected_headers_with_replaced_status;
1865 } tests[] = {
1866 { "HTTP/1.1 200 OK\n"
1867 "Content-Length: 450\n",
1869 "HTTP/1.1 200 OK\n"
1870 "Content-Range: bytes 3-5/450\n"
1871 "Content-Length: 3\n",
1873 "HTTP/1.1 206 Partial Content\n"
1874 "Content-Range: bytes 3-5/450\n"
1875 "Content-Length: 3\n",
1877 { "HTTP/1.1 200 OK\n"
1878 "Content-Length: 5\n",
1880 "HTTP/1.1 200 OK\n"
1881 "Content-Range: bytes 3-5/5\n"
1882 "Content-Length: 3\n",
1884 "HTTP/1.1 206 Partial Content\n"
1885 "Content-Range: bytes 3-5/5\n"
1886 "Content-Length: 3\n",
1889 const net::HttpByteRange range = net::HttpByteRange::Bounded(3, 5);
1891 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1892 std::string orig_headers(tests[i].orig_headers);
1893 std::replace(orig_headers.begin(), orig_headers.end(), '\n', '\0');
1894 scoped_refptr<net::HttpResponseHeaders> parsed(
1895 new net::HttpResponseHeaders(orig_headers + '\0'));
1896 int64 content_size = parsed->GetContentLength();
1897 std::string resulting_headers;
1899 // Update headers without replacing status line.
1900 parsed->UpdateWithNewRange(range, content_size, false);
1901 parsed->GetNormalizedHeaders(&resulting_headers);
1902 EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1904 // Replace status line too.
1905 parsed->UpdateWithNewRange(range, content_size, true);
1906 parsed->GetNormalizedHeaders(&resulting_headers);
1907 EXPECT_EQ(std::string(tests[i].expected_headers_with_replaced_status),
1908 resulting_headers);
1912 TEST(HttpResponseHeadersTest, ToNetLogParamAndBackAgain) {
1913 std::string headers("HTTP/1.1 404\n"
1914 "Content-Length: 450\n"
1915 "Connection: keep-alive\n");
1916 HeadersToRaw(&headers);
1917 scoped_refptr<net::HttpResponseHeaders> parsed(
1918 new net::HttpResponseHeaders(headers));
1920 scoped_ptr<base::Value> event_param(
1921 parsed->NetLogCallback(net::NetLog::LOG_ALL_BUT_BYTES));
1922 scoped_refptr<net::HttpResponseHeaders> recreated;
1924 ASSERT_TRUE(net::HttpResponseHeaders::FromNetLogParam(event_param.get(),
1925 &recreated));
1926 ASSERT_TRUE(recreated.get());
1927 EXPECT_EQ(parsed->GetHttpVersion(), recreated->GetHttpVersion());
1928 EXPECT_EQ(parsed->response_code(), recreated->response_code());
1929 EXPECT_EQ(parsed->GetContentLength(), recreated->GetContentLength());
1930 EXPECT_EQ(parsed->IsKeepAlive(), recreated->IsKeepAlive());
1932 std::string normalized_parsed;
1933 parsed->GetNormalizedHeaders(&normalized_parsed);
1934 std::string normalized_recreated;
1935 parsed->GetNormalizedHeaders(&normalized_recreated);
1936 EXPECT_EQ(normalized_parsed, normalized_recreated);