Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / net / http / http_response_headers_unittest.cc
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.
4
5 #include <algorithm>
6
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"
15
16 namespace {
17
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;
24 };
25
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;
33 };
34
35 class HttpResponseHeadersTest : public testing::Test {
36 };
37
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';
44 }
45
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);
50
51   std::string headers;
52   scoped_refptr<net::HttpResponseHeaders> parsed(
53       new net::HttpResponseHeaders(raw_headers));
54   parsed->GetNormalizedHeaders(&headers);
55
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', '\\');
61
62   EXPECT_EQ(expected_headers, headers);
63
64   EXPECT_EQ(test.expected_response_code, parsed->response_code());
65
66   EXPECT_TRUE(test.expected_parsed_version == parsed->GetParsedHttpVersion());
67   EXPECT_TRUE(test.expected_version == parsed->GetHttpVersion());
68 }
69
70 } // end namespace
71
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",
79
80     "HTTP/1.1 202 Accepted\n"
81     "Content-TYPE: text/html; charset=utf-8\n"
82     "Set-Cookie: a, b\n",
83
84     202,
85     net::HttpVersion(1,1),
86     net::HttpVersion(1,1)
87   };
88   TestCommon(test);
89 }
90
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",
100
101     "HTTP/1.1 202 Accepted\n"
102     "Set-Cookie: a, b\n",
103
104     202,
105     net::HttpVersion(1,1),
106     net::HttpVersion(1,1)
107   };
108   TestCommon(test);
109 }
110
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",
119
120     "HTTP/1.1 200 OK\n"
121     "Header1: \n"
122     "Header2: \n"
123     "Header3: \n"
124     "Header5: \n",
125
126     200,
127     net::HttpVersion(1,1),
128     net::HttpVersion(1,1)
129   };
130   TestCommon(test);
131 }
132
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",
138
139     "HTTP/1.0 201 OK\n"
140     "Content-TYPE: text/html; charset=utf-8\n",
141
142     201,
143     net::HttpVersion(0,9),
144     net::HttpVersion(1,0)
145   };
146   TestCommon(test);
147 }
148
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",
154
155     "HTTP/0.9 200 OK\n",
156
157     200,
158     net::HttpVersion(0,9),
159     net::HttpVersion(0,9)
160   };
161   TestCommon(test);
162 }
163
164 TEST(HttpResponseHeadersTest, NormalizeHeadersMissingOK) {
165   TestData test = {
166     "HTTP/1.1 201\n"
167     "Content-TYPE: text/html; charset=utf-8\n",
168
169     "HTTP/1.1 201 OK\n"
170     "Content-TYPE: text/html; charset=utf-8\n",
171
172     201,
173     net::HttpVersion(1,1),
174     net::HttpVersion(1,1)
175   };
176   TestCommon(test);
177 }
178
179 TEST(HttpResponseHeadersTest, NormalizeHeadersBadStatus) {
180   TestData test = {
181     "SCREWED_UP_STATUS_LINE\n"
182     "Content-TYPE: text/html; charset=utf-8\n",
183
184     "HTTP/1.0 200 OK\n"
185     "Content-TYPE: text/html; charset=utf-8\n",
186
187     200,
188     net::HttpVersion(0,0), // Parse error
189     net::HttpVersion(1,0)
190   };
191   TestCommon(test);
192 }
193
194 TEST(HttpResponseHeadersTest, NormalizeHeadersInvalidStatusCode) {
195   TestData test = {
196     "HTTP/1.1 -1  Unknown\n",
197
198     "HTTP/1.1 200 OK\n",
199
200     200,
201     net::HttpVersion(1,1),
202     net::HttpVersion(1,1)
203   };
204   TestCommon(test);
205 }
206
207 TEST(HttpResponseHeadersTest, NormalizeHeadersEmpty) {
208   TestData test = {
209     "",
210
211     "HTTP/1.0 200 OK\n",
212
213     200,
214     net::HttpVersion(0,0), // Parse Error
215     net::HttpVersion(1,0)
216   };
217   TestCommon(test);
218 }
219
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",
227
228     "HTTP/1.1 202 Accepted\n"
229     "foo: bar\n"
230     "baz: blat\n",
231
232     202,
233     net::HttpVersion(1,1),
234     net::HttpVersion(1,1)
235   };
236   TestCommon(test);
237 }
238
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",
246
247     "HTTP/1.1 202 Accepted\n"
248     "foo: \n"
249     "bar: \n"
250     "baz: blat\n"
251     "zip: \n",
252
253     202,
254     net::HttpVersion(1,1),
255     net::HttpVersion(1,1)
256   };
257   TestCommon(test);
258 }
259
260 TEST(HttpResponseHeadersTest, NormalizeHeadersOfWhitepace) {
261   TestData test = {
262     "\n   \n",
263
264     "HTTP/1.0 200 OK\n",
265
266     200,
267     net::HttpVersion(0,0),  // Parse error
268     net::HttpVersion(1,0)
269   };
270   TestCommon(test);
271 }
272
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",
278
279     "HTTP/1.1 200 OK\n"
280     "Set-Cookie: x=1, y=2\n",
281
282     200,
283     net::HttpVersion(1,1),
284     net::HttpVersion(1,1)
285   };
286   TestCommon(test);
287 }
288
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));
297
298   std::string value;
299   EXPECT_TRUE(parsed->GetNormalizedHeader("cache-control", &value));
300   EXPECT_EQ("private, no-store", value);
301 }
302
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",
313
314       "HTTP/1.1 200 OK\n"
315       "Cache-control: private, no-store\n"
316     },
317     { net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
318       "HTTP/1.1 200 OK\n"
319       "connection: keep-alive\n"
320       "server: blah\n",
321
322       "HTTP/1.1 200 OK\n"
323       "server: blah\n"
324     },
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",
333
334       "HTTP/1.1 200 OK\n"
335       "cache-control: private, no-cache=\"foo\"\n"
336     },
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",
342
343       "HTTP/1.1 200 OK\n"
344       "Cache-Control: private,no-cache=\"foo, bar\"\n"
345     },
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",
351
352       "HTTP/1.1 200 OK\n"
353       "Foo: 2\n"
354       "Cache-Control: private,no-cache=foo\n"
355     },
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",
361
362       "HTTP/1.1 200 OK\n"
363       "Foo: 2\n"
364       "Cache-Control: private, no-cache=\n"
365     },
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",
371
372       "HTTP/1.1 200 OK\n"
373       "Foo: 2\n"
374       "Cache-Control: private, no-cache=\"\"\n"
375     },
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",
381
382       "HTTP/1.1 200 OK\n"
383       "Foo: 2\n"
384       "Cache-Control: private, no-cache=\'foo\'\n"
385     },
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",
391
392       "HTTP/1.1 200 OK\n"
393       "Foo: 2\n"
394       "Cache-Control: private, no-cache=\"foo\n"
395     },
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",
401
402       "HTTP/1.1 200 OK\n"
403       "Cache-Control: private, no-cache=\" foo\t, bar\"\n"
404     },
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",
411
412       "HTTP/1.1 200 OK\n"
413       "Foo: 1, 3\n"
414       "Bar: 2\n"
415     },
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",
422
423       "HTTP/1.1 200 OK\n"
424       "Foo: 1, 3, 4\n"
425       "Bar: 2\n"
426     },
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",
434
435       "HTTP/1.1 200 OK\n"
436       "Bar: 1\n"
437     },
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",
443
444       "HTTP/1.1 200 OK\n"
445       "Content-Length: 450\n"
446       "Content-Encoding: gzip\n"
447     },
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",
453
454       "HTTP/1.1 200 OK\n"
455       "Content-Length: 450\n"
456       "Content-Encoding: gzip\n"
457     },
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=\"",
466
467       "HTTP/1.1 200 OK\n"
468       "Bar: 1\n"
469     },
470   };
471
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));
477
478     Pickle pickle;
479     parsed1->Persist(&pickle, tests[i].options);
480
481     PickleIterator iter(pickle);
482     scoped_refptr<net::HttpResponseHeaders> parsed2(
483         new net::HttpResponseHeaders(pickle, &iter));
484
485     std::string h2;
486     parsed2->GetNormalizedHeaders(&h2);
487     EXPECT_EQ(std::string(tests[i].expected_headers), h2);
488   }
489 }
490
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));
501
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));
511 }
512
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));
523
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));
531 }
532
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));
543
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);
549 }
550
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));
565
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);
579 }
580
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       "*/*" },
711   };
712
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));
718
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);
727   }
728 }
729
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
739     },
740     // valid for a little while
741     { "HTTP/1.1 200 OK\n"
742       "cache-control: max-age=10000\n"
743       "\n",
744       false
745     },
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
752     },
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
759     },
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
767     },
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
774     },
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
780     },
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
786     },
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
793     },
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
799     },
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
805     },
806     // cached permanent redirect
807     { "HTTP/1.1 301 Moved Permanently\n"
808       "\n",
809       false
810     },
811     // another cached permanent redirect
812     { "HTTP/1.1 308 Permanent Redirect\n"
813       "\n",
814       false
815     },
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
821     },
822     // cached forever by default
823     { "HTTP/1.1 410 Gone\n"
824       "\n",
825       false
826     },
827     // cached temporary redirect: not reusable
828     { "HTTP/1.1 302 Found\n"
829       "\n",
830       true
831     },
832     // cached temporary redirect: reusable
833     { "HTTP/1.1 302 Found\n"
834       "cache-control: max-age=10000\n"
835       "\n",
836       false
837     },
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
845     },
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
853     },
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
861     },
862     // TODO(darin): add many many more tests here
863   };
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);
868
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));
874
875     bool requires_validation =
876         parsed->RequiresValidation(request_time, response_time, current_time);
877     EXPECT_EQ(tests[i].requires_validation, requires_validation);
878   }
879 }
880
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",
888
889       "HTTP/1/1 304 Not Modified\n"
890       "connection: keep-alive\n"
891       "Cache-control: max-age=10000\n",
892
893       "HTTP/1.1 200 OK\n"
894       "Cache-control: max-age=10000\n"
895     },
896     { "HTTP/1.1 200 OK\n"
897       "Foo: 1\n"
898       "Cache-control: private\n",
899
900       "HTTP/1/1 304 Not Modified\n"
901       "connection: keep-alive\n"
902       "Cache-control: max-age=10000\n",
903
904       "HTTP/1.1 200 OK\n"
905       "Cache-control: max-age=10000\n"
906       "Foo: 1\n"
907     },
908     { "HTTP/1.1 200 OK\n"
909       "Foo: 1\n"
910       "Cache-control: private\n",
911
912       "HTTP/1/1 304 Not Modified\n"
913       "connection: keep-alive\n"
914       "Cache-CONTROL: max-age=10000\n",
915
916       "HTTP/1.1 200 OK\n"
917       "Cache-CONTROL: max-age=10000\n"
918       "Foo: 1\n"
919     },
920     { "HTTP/1.1 200 OK\n"
921       "Content-Length: 450\n",
922
923       "HTTP/1/1 304 Not Modified\n"
924       "connection: keep-alive\n"
925       "Cache-control:      max-age=10001   \n",
926
927       "HTTP/1.1 200 OK\n"
928       "Cache-control: max-age=10001\n"
929       "Content-Length: 450\n"
930     },
931     { "HTTP/1.1 200 OK\n"
932       "X-Frame-Options: DENY\n",
933
934       "HTTP/1/1 304 Not Modified\n"
935       "X-Frame-Options: ALLOW\n",
936
937       "HTTP/1.1 200 OK\n"
938       "X-Frame-Options: DENY\n",
939     },
940     { "HTTP/1.1 200 OK\n"
941       "X-WebKit-CSP: default-src 'none'\n",
942
943       "HTTP/1/1 304 Not Modified\n"
944       "X-WebKit-CSP: default-src *\n",
945
946       "HTTP/1.1 200 OK\n"
947       "X-WebKit-CSP: default-src 'none'\n",
948     },
949     { "HTTP/1.1 200 OK\n"
950       "X-XSS-Protection: 1\n",
951
952       "HTTP/1/1 304 Not Modified\n"
953       "X-XSS-Protection: 0\n",
954
955       "HTTP/1.1 200 OK\n"
956       "X-XSS-Protection: 1\n",
957     },
958     { "HTTP/1.1 200 OK\n",
959
960       "HTTP/1/1 304 Not Modified\n"
961       "X-Content-Type-Options: nosniff\n",
962
963       "HTTP/1.1 200 OK\n"
964     },
965   };
966
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));
972
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));
977
978     parsed->Update(*new_parsed.get());
979
980     std::string resulting_headers;
981     parsed->GetNormalizedHeaders(&resulting_headers);
982     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
983   }
984 }
985
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",
992
993       ""
994     },
995     { "HTTP/1.1 200 OK\n"
996       "Foo: 1\n",
997
998       "Foo: 1\n"
999     },
1000     { "HTTP/1.1 200 OK\n"
1001       "Foo: 1\n"
1002       "Bar: 2\n"
1003       "Foo: 3\n",
1004
1005       "Foo: 1\nBar: 2\nFoo: 3\n"
1006     },
1007     { "HTTP/1.1 200 OK\n"
1008       "Foo: 1, 2, 3\n",
1009
1010       "Foo: 1, 2, 3\n"
1011     },
1012   };
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));
1018
1019     std::string name, value, lines;
1020
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");
1027     }
1028
1029     EXPECT_EQ(std::string(tests[i].expected_lines), lines);
1030   }
1031 }
1032
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",
1040       "",
1041       false
1042     },
1043     { "HTTP/1.1 301 Moved\n"
1044       "Location: http://foopy/\n",
1045       "http://foopy/",
1046       true
1047     },
1048     { "HTTP/1.1 301 Moved\n"
1049       "Location: \t \n",
1050       "",
1051       false
1052     },
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
1059     },
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
1066     },
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
1072     },
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
1078     },
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
1085     },
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
1090     },
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
1095     },
1096   };
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));
1102
1103     std::string location;
1104     EXPECT_EQ(parsed->IsRedirect(&location), tests[i].is_redirect);
1105     EXPECT_EQ(location, tests[i].location);
1106   }
1107 }
1108
1109 TEST(HttpResponseHeadersTest, GetContentLength) {
1110   const struct {
1111     const char* headers;
1112     int64 expected_len;
1113   } tests[] = {
1114     { "HTTP/1.1 200 OK\n",
1115       -1
1116     },
1117     { "HTTP/1.1 200 OK\n"
1118       "Content-Length: 10\n",
1119       10
1120     },
1121     { "HTTP/1.1 200 OK\n"
1122       "Content-Length: \n",
1123       -1
1124     },
1125     { "HTTP/1.1 200 OK\n"
1126       "Content-Length: abc\n",
1127       -1
1128     },
1129     { "HTTP/1.1 200 OK\n"
1130       "Content-Length: -10\n",
1131       -1
1132     },
1133     { "HTTP/1.1 200 OK\n"
1134       "Content-Length:  +10\n",
1135       -1
1136     },
1137     { "HTTP/1.1 200 OK\n"
1138       "Content-Length: 23xb5\n",
1139       -1
1140     },
1141     { "HTTP/1.1 200 OK\n"
1142       "Content-Length: 0xA\n",
1143       -1
1144     },
1145     { "HTTP/1.1 200 OK\n"
1146       "Content-Length: 010\n",
1147       10
1148     },
1149     // Content-Length too big, will overflow an int64
1150     { "HTTP/1.1 200 OK\n"
1151       "Content-Length: 40000000000000000000\n",
1152       -1
1153     },
1154     { "HTTP/1.1 200 OK\n"
1155       "Content-Length:       10\n",
1156       10
1157     },
1158     { "HTTP/1.1 200 OK\n"
1159       "Content-Length: 10  \n",
1160       10
1161     },
1162     { "HTTP/1.1 200 OK\n"
1163       "Content-Length: \t10\n",
1164       10
1165     },
1166     { "HTTP/1.1 200 OK\n"
1167       "Content-Length: \v10\n",
1168       -1
1169     },
1170     { "HTTP/1.1 200 OK\n"
1171       "Content-Length: \f10\n",
1172       -1
1173     },
1174     { "HTTP/1.1 200 OK\n"
1175       "cOnTeNt-LENgth: 33\n",
1176       33
1177     },
1178     { "HTTP/1.1 200 OK\n"
1179       "Content-Length: 34\r\n",
1180       -1
1181     },
1182   };
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));
1188
1189     EXPECT_EQ(tests[i].expected_len, parsed->GetContentLength());
1190   }
1191 }
1192
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,
1203       -1,
1204       -1,
1205       -1
1206     },
1207     { "HTTP/1.1 206 Partial Content\n"
1208       "Content-Range:",
1209       false,
1210       -1,
1211       -1,
1212       -1
1213     },
1214     { "HTTP/1.1 206 Partial Content\n"
1215       "Content-Range: megabytes 0-10/50",
1216       false,
1217       -1,
1218       -1,
1219       -1
1220     },
1221     { "HTTP/1.1 206 Partial Content\n"
1222       "Content-Range: 0-10/50",
1223       false,
1224       -1,
1225       -1,
1226       -1
1227     },
1228     { "HTTP/1.1 206 Partial Content\n"
1229       "Content-Range: Bytes 0-50/51",
1230       true,
1231       0,
1232       50,
1233       51
1234     },
1235     { "HTTP/1.1 206 Partial Content\n"
1236       "Content-Range: bytes 0-50/51",
1237       true,
1238       0,
1239       50,
1240       51
1241     },
1242     { "HTTP/1.1 206 Partial Content\n"
1243       "Content-Range: bytes\t0-50/51",
1244       false,
1245       -1,
1246       -1,
1247       -1
1248     },
1249     { "HTTP/1.1 206 Partial Content\n"
1250       "Content-Range:     bytes 0-50/51",
1251       true,
1252       0,
1253       50,
1254       51
1255     },
1256     { "HTTP/1.1 206 Partial Content\n"
1257       "Content-Range:     bytes    0    -   50  \t / \t51",
1258       true,
1259       0,
1260       50,
1261       51
1262     },
1263     { "HTTP/1.1 206 Partial Content\n"
1264       "Content-Range: bytes 0\t-\t50\t/\t51\t",
1265       true,
1266       0,
1267       50,
1268       51
1269     },
1270     { "HTTP/1.1 206 Partial Content\n"
1271       "Content-Range:   \tbytes\t\t\t 0\t-\t50\t/\t51\t",
1272       true,
1273       0,
1274       50,
1275       51
1276     },
1277     { "HTTP/1.1 206 Partial Content\n"
1278       "Content-Range: \t   bytes \t  0    -   50   /   5   1",
1279       false,
1280       0,
1281       50,
1282       -1
1283     },
1284     { "HTTP/1.1 206 Partial Content\n"
1285       "Content-Range: \t   bytes \t  0    -   5 0   /   51",
1286       false,
1287       -1,
1288       -1,
1289       -1
1290     },
1291     { "HTTP/1.1 206 Partial Content\n"
1292       "Content-Range: bytes 50-0/51",
1293       false,
1294       50,
1295       0,
1296       -1
1297     },
1298     { "HTTP/1.1 416 Requested range not satisfiable\n"
1299       "Content-Range: bytes * /*",
1300       false,
1301       -1,
1302       -1,
1303       -1
1304     },
1305     { "HTTP/1.1 416 Requested range not satisfiable\n"
1306       "Content-Range: bytes *   /    *   ",
1307       false,
1308       -1,
1309       -1,
1310       -1
1311     },
1312     { "HTTP/1.1 206 Partial Content\n"
1313       "Content-Range: bytes 0-50/*",
1314       false,
1315       0,
1316       50,
1317       -1
1318     },
1319     { "HTTP/1.1 206 Partial Content\n"
1320       "Content-Range: bytes 0-50  /    * ",
1321       false,
1322       0,
1323       50,
1324       -1
1325     },
1326     { "HTTP/1.1 206 Partial Content\n"
1327       "Content-Range: bytes 0-10000000000/10000000001",
1328       true,
1329       0,
1330       10000000000ll,
1331       10000000001ll
1332     },
1333     { "HTTP/1.1 206 Partial Content\n"
1334       "Content-Range: bytes 0-10000000000/10000000000",
1335       false,
1336       0,
1337       10000000000ll,
1338       10000000000ll
1339     },
1340     // 64 bits wraparound.
1341     { "HTTP/1.1 206 Partial Content\n"
1342       "Content-Range: bytes 0 - 9223372036854775807 / 100",
1343       false,
1344       0,
1345       kint64max,
1346       100
1347     },
1348     // 64 bits wraparound.
1349     { "HTTP/1.1 206 Partial Content\n"
1350       "Content-Range: bytes 0 - 100 / -9223372036854775808",
1351       false,
1352       0,
1353       100,
1354       kint64min
1355     },
1356     { "HTTP/1.1 206 Partial Content\n"
1357       "Content-Range: bytes */50",
1358       false,
1359       -1,
1360       -1,
1361       50
1362     },
1363     { "HTTP/1.1 206 Partial Content\n"
1364       "Content-Range: bytes 0-50/10",
1365       false,
1366       0,
1367       50,
1368       10
1369     },
1370     { "HTTP/1.1 206 Partial Content\n"
1371       "Content-Range: bytes 40-50/45",
1372       false,
1373       40,
1374       50,
1375       45
1376     },
1377     { "HTTP/1.1 206 Partial Content\n"
1378       "Content-Range: bytes 0-50/-10",
1379       false,
1380       0,
1381       50,
1382       -10
1383     },
1384     { "HTTP/1.1 206 Partial Content\n"
1385       "Content-Range: bytes 0-0/1",
1386       true,
1387       0,
1388       0,
1389       1
1390     },
1391     { "HTTP/1.1 206 Partial Content\n"
1392       "Content-Range: bytes 0-40000000000000000000/40000000000000000001",
1393       false,
1394       -1,
1395       -1,
1396       -1
1397     },
1398     { "HTTP/1.1 206 Partial Content\n"
1399       "Content-Range: bytes 1-/100",
1400       false,
1401       -1,
1402       -1,
1403       -1
1404     },
1405     { "HTTP/1.1 206 Partial Content\n"
1406       "Content-Range: bytes -/100",
1407       false,
1408       -1,
1409       -1,
1410       -1
1411     },
1412     { "HTTP/1.1 206 Partial Content\n"
1413       "Content-Range: bytes -1/100",
1414       false,
1415       -1,
1416       -1,
1417       -1
1418     },
1419     { "HTTP/1.1 206 Partial Content\n"
1420       "Content-Range: bytes 0-1233/*",
1421       false,
1422       0,
1423       1233,
1424       -1
1425     },
1426     { "HTTP/1.1 206 Partial Content\n"
1427       "Content-Range: bytes -123 - -1/100",
1428       false,
1429       -1,
1430       -1,
1431       -1
1432     },
1433   };
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));
1439
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);
1450   }
1451 }
1452
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
1462     },
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
1468     },
1469     { "HTTP/1.1 200 OK\n",
1470       true
1471     },
1472     { "HTTP/1.0 200 OK\n",
1473       false
1474     },
1475     { "HTTP/1.0 200 OK\n"
1476       "connection: close\n",
1477       false
1478     },
1479     { "HTTP/1.0 200 OK\n"
1480       "connection: keep-alive\n",
1481       true
1482     },
1483     { "HTTP/1.0 200 OK\n"
1484       "connection: kEeP-AliVe\n",
1485       true
1486     },
1487     { "HTTP/1.0 200 OK\n"
1488       "connection: keep-aliveX\n",
1489       false
1490     },
1491     { "HTTP/1.1 200 OK\n"
1492       "connection: close\n",
1493       false
1494     },
1495     { "HTTP/1.1 200 OK\n"
1496       "connection: keep-alive\n",
1497       true
1498     },
1499     { "HTTP/1.0 200 OK\n"
1500       "proxy-connection: close\n",
1501       false
1502     },
1503     { "HTTP/1.0 200 OK\n"
1504       "proxy-connection: keep-alive\n",
1505       true
1506     },
1507     { "HTTP/1.1 200 OK\n"
1508       "proxy-connection: close\n",
1509       false
1510     },
1511     { "HTTP/1.1 200 OK\n"
1512       "proxy-connection: keep-alive\n",
1513       true
1514     },
1515   };
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));
1521
1522     EXPECT_EQ(tests[i].expected_keep_alive, parsed->IsKeepAlive());
1523   }
1524 }
1525
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
1533     },
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
1539     },
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
1545     },
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
1550     },
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
1555     },
1556     { "HTTP/1.1 200 OK\n"
1557       "ETag: \"foo\"\n",
1558       true
1559     },
1560     // This is not really a weak etag:
1561     { "HTTP/1.1 200 OK\n"
1562       "etag: \"w/foo\"\n",
1563       true
1564     },
1565     // This is a weak etag:
1566     { "HTTP/1.1 200 OK\n"
1567       "etag: w/\"foo\"\n",
1568       false
1569     },
1570     { "HTTP/1.1 200 OK\n"
1571       "etag:    W  /   \"foo\"\n",
1572       false
1573     }
1574   };
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));
1580
1581     EXPECT_EQ(tests[i].expected_result, parsed->HasStrongValidators()) <<
1582         "Failed test case " << i;
1583   }
1584 }
1585
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());
1592 }
1593
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());
1601 }
1602
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());
1609 }
1610
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());
1619 }
1620
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",
1630
1631       "Content-Length: 450",
1632
1633       "HTTP/1.1 200 OK\n"
1634       "connection: keep-alive\n"
1635       "Cache-control: max-age=10000\n"
1636       "Content-Length: 450\n"
1637     },
1638     { "HTTP/1.1 200 OK\n"
1639       "connection: keep-alive\n"
1640       "Cache-control: max-age=10000    \n",
1641
1642       "Content-Length: 450  ",
1643
1644       "HTTP/1.1 200 OK\n"
1645       "connection: keep-alive\n"
1646       "Cache-control: max-age=10000\n"
1647       "Content-Length: 450\n"
1648     },
1649   };
1650
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));
1656
1657     std::string new_header(tests[i].new_header);
1658     parsed->AddHeader(new_header);
1659
1660     std::string resulting_headers;
1661     parsed->GetNormalizedHeaders(&resulting_headers);
1662     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1663   }
1664 }
1665
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",
1676
1677       "Content-Length",
1678
1679       "HTTP/1.1 200 OK\n"
1680       "connection: keep-alive\n"
1681       "Cache-control: max-age=10000\n"
1682     },
1683     { "HTTP/1.1 200 OK\n"
1684       "connection: keep-alive  \n"
1685       "Content-Length  : 450  \n"
1686       "Cache-control: max-age=10000\n",
1687
1688       "Content-Length",
1689
1690       "HTTP/1.1 200 OK\n"
1691       "connection: keep-alive\n"
1692       "Cache-control: max-age=10000\n"
1693     },
1694   };
1695
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));
1701
1702     std::string name(tests[i].to_remove);
1703     parsed->RemoveHeader(name);
1704
1705     std::string resulting_headers;
1706     parsed->GetNormalizedHeaders(&resulting_headers);
1707     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1708   }
1709 }
1710
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",
1722
1723       "Content-Length",
1724
1725       "450",
1726
1727       "HTTP/1.1 200 OK\n"
1728       "connection: keep-alive\n"
1729       "Cache-control: max-age=10000\n"
1730     },
1731     { "HTTP/1.1 200 OK\n"
1732       "connection: keep-alive  \n"
1733       "Content-Length  : 450  \n"
1734       "Cache-control: max-age=10000\n",
1735
1736       "Content-Length",
1737
1738       "450",
1739
1740       "HTTP/1.1 200 OK\n"
1741       "connection: keep-alive\n"
1742       "Cache-control: max-age=10000\n"
1743     },
1744     { "HTTP/1.1 200 OK\n"
1745       "connection: keep-alive  \n"
1746       "Content-Length: 450\n"
1747       "Cache-control: max-age=10000\n",
1748
1749       "Content-Length",  // Matching name.
1750
1751       "999",  // Mismatching value.
1752
1753       "HTTP/1.1 200 OK\n"
1754       "connection: keep-alive\n"
1755       "Content-Length: 450\n"
1756       "Cache-control: max-age=10000\n"
1757     },
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",
1763
1764       "Foo",
1765
1766       "bar, baz",  // Space in value.
1767
1768       "HTTP/1.1 200 OK\n"
1769       "connection: keep-alive\n"
1770       "Foo: bar\n"
1771       "Cache-control: max-age=10000\n"
1772     },
1773     { "HTTP/1.1 200 OK\n"
1774       "connection: keep-alive  \n"
1775       "Foo: bar, baz\n"
1776       "Cache-control: max-age=10000\n",
1777
1778       "Foo",
1779
1780       "baz",  // Only partial match -> ignored.
1781
1782       "HTTP/1.1 200 OK\n"
1783       "connection: keep-alive\n"
1784       "Foo: bar, baz\n"
1785       "Cache-control: max-age=10000\n"
1786     },
1787   };
1788
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));
1794
1795     std::string name(tests[i].to_remove_name);
1796     std::string value(tests[i].to_remove_value);
1797     parsed->RemoveHeaderLine(name, value);
1798
1799     std::string resulting_headers;
1800     parsed->GetNormalizedHeaders(&resulting_headers);
1801     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1802   }
1803 }
1804
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",
1815
1816       "HTTP/1.1 200 OK",
1817
1818       "HTTP/1.1 200 OK\n"
1819       "connection: keep-alive\n"
1820       "Cache-control: max-age=10000\n"
1821       "Content-Length: 450\n"
1822     },
1823     { "HTTP/1.1 200 OK\n"
1824       "connection: keep-alive\n",
1825
1826       "HTTP/1.1 304 Not Modified",
1827
1828       "HTTP/1.1 304 Not Modified\n"
1829       "connection: keep-alive\n"
1830     },
1831     { "HTTP/1.1 200 OK\n"
1832       "connection: keep-alive  \n"
1833       "Content-Length  : 450   \n"
1834       "Cache-control: max-age=10000\n",
1835
1836       "HTTP/1//1 304 Not Modified",
1837
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"
1842     },
1843   };
1844
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));
1850
1851     std::string name(tests[i].new_status);
1852     parsed->ReplaceStatusLine(name);
1853
1854     std::string resulting_headers;
1855     parsed->GetNormalizedHeaders(&resulting_headers);
1856     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1857   }
1858 }
1859
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",
1868
1869       "HTTP/1.1 200 OK\n"
1870       "Content-Range: bytes 3-5/450\n"
1871       "Content-Length: 3\n",
1872
1873       "HTTP/1.1 206 Partial Content\n"
1874       "Content-Range: bytes 3-5/450\n"
1875       "Content-Length: 3\n",
1876     },
1877     { "HTTP/1.1 200 OK\n"
1878       "Content-Length: 5\n",
1879
1880       "HTTP/1.1 200 OK\n"
1881       "Content-Range: bytes 3-5/5\n"
1882       "Content-Length: 3\n",
1883
1884       "HTTP/1.1 206 Partial Content\n"
1885       "Content-Range: bytes 3-5/5\n"
1886       "Content-Length: 3\n",
1887     },
1888   };
1889   const net::HttpByteRange range = net::HttpByteRange::Bounded(3, 5);
1890
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;
1898
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);
1903
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);
1909   }
1910 }
1911
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));
1919
1920   scoped_ptr<base::Value> event_param(
1921       parsed->NetLogCallback(net::NetLog::LOG_ALL_BUT_BYTES));
1922   scoped_refptr<net::HttpResponseHeaders> recreated;
1923
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());
1931
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);
1937 }