- add sources.
[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_response_headers.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 namespace {
16
17 struct TestData {
18   const char* raw_headers;
19   const char* expected_headers;
20   int expected_response_code;
21   net::HttpVersion expected_parsed_version;
22   net::HttpVersion expected_version;
23 };
24
25 struct ContentTypeTestData {
26   const std::string raw_headers;
27   const std::string mime_type;
28   const bool has_mimetype;
29   const std::string charset;
30   const bool has_charset;
31   const std::string all_content_type;
32 };
33
34 class HttpResponseHeadersTest : public testing::Test {
35 };
36
37 // Transform "normal"-looking headers (\n-separated) to the appropriate
38 // input format for ParseRawHeaders (\0-separated).
39 void HeadersToRaw(std::string* headers) {
40   std::replace(headers->begin(), headers->end(), '\n', '\0');
41   if (!headers->empty())
42     *headers += '\0';
43 }
44
45 void TestCommon(const TestData& test) {
46   std::string raw_headers(test.raw_headers);
47   HeadersToRaw(&raw_headers);
48   std::string expected_headers(test.expected_headers);
49
50   std::string headers;
51   scoped_refptr<net::HttpResponseHeaders> parsed(
52       new net::HttpResponseHeaders(raw_headers));
53   parsed->GetNormalizedHeaders(&headers);
54
55   // Transform to readable output format (so it's easier to see diffs).
56   std::replace(headers.begin(), headers.end(), ' ', '_');
57   std::replace(headers.begin(), headers.end(), '\n', '\\');
58   std::replace(expected_headers.begin(), expected_headers.end(), ' ', '_');
59   std::replace(expected_headers.begin(), expected_headers.end(), '\n', '\\');
60
61   EXPECT_EQ(expected_headers, headers);
62
63   EXPECT_EQ(test.expected_response_code, parsed->response_code());
64
65   EXPECT_TRUE(test.expected_parsed_version == parsed->GetParsedHttpVersion());
66   EXPECT_TRUE(test.expected_version == parsed->GetHttpVersion());
67 }
68
69 } // end namespace
70
71 // Check that we normalize headers properly.
72 TEST(HttpResponseHeadersTest, NormalizeHeadersWhitespace) {
73   TestData test = {
74     "HTTP/1.1    202   Accepted  \n"
75     "Content-TYPE  : text/html; charset=utf-8  \n"
76     "Set-Cookie: a \n"
77     "Set-Cookie:   b \n",
78
79     "HTTP/1.1 202 Accepted\n"
80     "Content-TYPE: text/html; charset=utf-8\n"
81     "Set-Cookie: a, b\n",
82
83     202,
84     net::HttpVersion(1,1),
85     net::HttpVersion(1,1)
86   };
87   TestCommon(test);
88 }
89
90 // Check that we normalize headers properly (header name is invalid if starts
91 // with LWS).
92 TEST(HttpResponseHeadersTest, NormalizeHeadersLeadingWhitespace) {
93   TestData test = {
94     "HTTP/1.1    202   Accepted  \n"
95     // Starts with space -- will be skipped as invalid.
96     "  Content-TYPE  : text/html; charset=utf-8  \n"
97     "Set-Cookie: a \n"
98     "Set-Cookie:   b \n",
99
100     "HTTP/1.1 202 Accepted\n"
101     "Set-Cookie: a, b\n",
102
103     202,
104     net::HttpVersion(1,1),
105     net::HttpVersion(1,1)
106   };
107   TestCommon(test);
108 }
109
110 TEST(HttpResponseHeadersTest, BlankHeaders) {
111   TestData test = {
112     "HTTP/1.1 200 OK\n"
113     "Header1 :          \n"
114     "Header2: \n"
115     "Header3:\n"
116     "Header4\n"
117     "Header5    :\n",
118
119     "HTTP/1.1 200 OK\n"
120     "Header1: \n"
121     "Header2: \n"
122     "Header3: \n"
123     "Header5: \n",
124
125     200,
126     net::HttpVersion(1,1),
127     net::HttpVersion(1,1)
128   };
129   TestCommon(test);
130 }
131
132 TEST(HttpResponseHeadersTest, NormalizeHeadersVersion) {
133   // Don't believe the http/0.9 version if there are headers!
134   TestData test = {
135     "hTtP/0.9 201\n"
136     "Content-TYPE: text/html; charset=utf-8\n",
137
138     "HTTP/1.0 201 OK\n"
139     "Content-TYPE: text/html; charset=utf-8\n",
140
141     201,
142     net::HttpVersion(0,9),
143     net::HttpVersion(1,0)
144   };
145   TestCommon(test);
146 }
147
148 TEST(HttpResponseHeadersTest, PreserveHttp09) {
149   // Accept the HTTP/0.9 version number if there are no headers.
150   // This is how HTTP/0.9 responses get constructed from HttpNetworkTransaction.
151   TestData test = {
152     "hTtP/0.9 200 OK\n",
153
154     "HTTP/0.9 200 OK\n",
155
156     200,
157     net::HttpVersion(0,9),
158     net::HttpVersion(0,9)
159   };
160   TestCommon(test);
161 }
162
163 TEST(HttpResponseHeadersTest, NormalizeHeadersMissingOK) {
164   TestData test = {
165     "HTTP/1.1 201\n"
166     "Content-TYPE: text/html; charset=utf-8\n",
167
168     "HTTP/1.1 201 OK\n"
169     "Content-TYPE: text/html; charset=utf-8\n",
170
171     201,
172     net::HttpVersion(1,1),
173     net::HttpVersion(1,1)
174   };
175   TestCommon(test);
176 }
177
178 TEST(HttpResponseHeadersTest, NormalizeHeadersBadStatus) {
179   TestData test = {
180     "SCREWED_UP_STATUS_LINE\n"
181     "Content-TYPE: text/html; charset=utf-8\n",
182
183     "HTTP/1.0 200 OK\n"
184     "Content-TYPE: text/html; charset=utf-8\n",
185
186     200,
187     net::HttpVersion(0,0), // Parse error
188     net::HttpVersion(1,0)
189   };
190   TestCommon(test);
191 }
192
193 TEST(HttpResponseHeadersTest, NormalizeHeadersInvalidStatusCode) {
194   TestData test = {
195     "HTTP/1.1 -1  Unknown\n",
196
197     "HTTP/1.1 200 OK\n",
198
199     200,
200     net::HttpVersion(1,1),
201     net::HttpVersion(1,1)
202   };
203   TestCommon(test);
204 }
205
206 TEST(HttpResponseHeadersTest, NormalizeHeadersEmpty) {
207   TestData test = {
208     "",
209
210     "HTTP/1.0 200 OK\n",
211
212     200,
213     net::HttpVersion(0,0), // Parse Error
214     net::HttpVersion(1,0)
215   };
216   TestCommon(test);
217 }
218
219 TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColon) {
220   TestData test = {
221     "HTTP/1.1    202   Accepted  \n"
222     "foo: bar\n"
223     ": a \n"
224     " : b\n"
225     "baz: blat \n",
226
227     "HTTP/1.1 202 Accepted\n"
228     "foo: bar\n"
229     "baz: blat\n",
230
231     202,
232     net::HttpVersion(1,1),
233     net::HttpVersion(1,1)
234   };
235   TestCommon(test);
236 }
237
238 TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColonAtEOL) {
239   TestData test = {
240     "HTTP/1.1    202   Accepted  \n"
241     "foo:   \n"
242     "bar:\n"
243     "baz: blat \n"
244     "zip:\n",
245
246     "HTTP/1.1 202 Accepted\n"
247     "foo: \n"
248     "bar: \n"
249     "baz: blat\n"
250     "zip: \n",
251
252     202,
253     net::HttpVersion(1,1),
254     net::HttpVersion(1,1)
255   };
256   TestCommon(test);
257 }
258
259 TEST(HttpResponseHeadersTest, NormalizeHeadersOfWhitepace) {
260   TestData test = {
261     "\n   \n",
262
263     "HTTP/1.0 200 OK\n",
264
265     200,
266     net::HttpVersion(0,0),  // Parse error
267     net::HttpVersion(1,0)
268   };
269   TestCommon(test);
270 }
271
272 TEST(HttpResponseHeadersTest, RepeatedSetCookie) {
273   TestData test = {
274     "HTTP/1.1 200 OK\n"
275     "Set-Cookie: x=1\n"
276     "Set-Cookie: y=2\n",
277
278     "HTTP/1.1 200 OK\n"
279     "Set-Cookie: x=1, y=2\n",
280
281     200,
282     net::HttpVersion(1,1),
283     net::HttpVersion(1,1)
284   };
285   TestCommon(test);
286 }
287
288 TEST(HttpResponseHeadersTest, GetNormalizedHeader) {
289   std::string headers =
290       "HTTP/1.1 200 OK\n"
291       "Cache-control: private\n"
292       "cache-Control: no-store\n";
293   HeadersToRaw(&headers);
294   scoped_refptr<net::HttpResponseHeaders> parsed(
295       new net::HttpResponseHeaders(headers));
296
297   std::string value;
298   EXPECT_TRUE(parsed->GetNormalizedHeader("cache-control", &value));
299   EXPECT_EQ("private, no-store", value);
300 }
301
302 TEST(HttpResponseHeadersTest, Persist) {
303   const struct {
304     net::HttpResponseHeaders::PersistOptions options;
305     const char* raw_headers;
306     const char* expected_headers;
307   } tests[] = {
308     { net::HttpResponseHeaders::PERSIST_ALL,
309       "HTTP/1.1 200 OK\n"
310       "Cache-control:private\n"
311       "cache-Control:no-store\n",
312
313       "HTTP/1.1 200 OK\n"
314       "Cache-control: private, no-store\n"
315     },
316     { net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
317       "HTTP/1.1 200 OK\n"
318       "connection: keep-alive\n"
319       "server: blah\n",
320
321       "HTTP/1.1 200 OK\n"
322       "server: blah\n"
323     },
324     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE |
325       net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
326       "HTTP/1.1 200 OK\n"
327       "fOo: 1\n"
328       "Foo: 2\n"
329       "Transfer-Encoding: chunked\n"
330       "CoNnection: keep-alive\n"
331       "cache-control: private, no-cache=\"foo\"\n",
332
333       "HTTP/1.1 200 OK\n"
334       "cache-control: private, no-cache=\"foo\"\n"
335     },
336     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
337       "HTTP/1.1 200 OK\n"
338       "Foo: 2\n"
339       "Cache-Control: private,no-cache=\"foo, bar\"\n"
340       "bar",
341
342       "HTTP/1.1 200 OK\n"
343       "Cache-Control: private,no-cache=\"foo, bar\"\n"
344     },
345     // ignore bogus no-cache value
346     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
347       "HTTP/1.1 200 OK\n"
348       "Foo: 2\n"
349       "Cache-Control: private,no-cache=foo\n",
350
351       "HTTP/1.1 200 OK\n"
352       "Foo: 2\n"
353       "Cache-Control: private,no-cache=foo\n"
354     },
355     // ignore bogus no-cache value
356     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
357       "HTTP/1.1 200 OK\n"
358       "Foo: 2\n"
359       "Cache-Control: private, no-cache=\n",
360
361       "HTTP/1.1 200 OK\n"
362       "Foo: 2\n"
363       "Cache-Control: private, no-cache=\n"
364     },
365     // ignore empty no-cache value
366     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
367       "HTTP/1.1 200 OK\n"
368       "Foo: 2\n"
369       "Cache-Control: private, no-cache=\"\"\n",
370
371       "HTTP/1.1 200 OK\n"
372       "Foo: 2\n"
373       "Cache-Control: private, no-cache=\"\"\n"
374     },
375     // ignore wrong quotes no-cache value
376     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
377       "HTTP/1.1 200 OK\n"
378       "Foo: 2\n"
379       "Cache-Control: private, no-cache=\'foo\'\n",
380
381       "HTTP/1.1 200 OK\n"
382       "Foo: 2\n"
383       "Cache-Control: private, no-cache=\'foo\'\n"
384     },
385     // ignore unterminated quotes no-cache value
386     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
387       "HTTP/1.1 200 OK\n"
388       "Foo: 2\n"
389       "Cache-Control: private, no-cache=\"foo\n",
390
391       "HTTP/1.1 200 OK\n"
392       "Foo: 2\n"
393       "Cache-Control: private, no-cache=\"foo\n"
394     },
395     // accept sloppy LWS
396     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
397       "HTTP/1.1 200 OK\n"
398       "Foo: 2\n"
399       "Cache-Control: private, no-cache=\" foo\t, bar\"\n",
400
401       "HTTP/1.1 200 OK\n"
402       "Cache-Control: private, no-cache=\" foo\t, bar\"\n"
403     },
404     // header name appears twice, separated by another header
405     { net::HttpResponseHeaders::PERSIST_ALL,
406       "HTTP/1.1 200 OK\n"
407       "Foo: 1\n"
408       "Bar: 2\n"
409       "Foo: 3\n",
410
411       "HTTP/1.1 200 OK\n"
412       "Foo: 1, 3\n"
413       "Bar: 2\n"
414     },
415     // header name appears twice, separated by another header (type 2)
416     { net::HttpResponseHeaders::PERSIST_ALL,
417       "HTTP/1.1 200 OK\n"
418       "Foo: 1, 3\n"
419       "Bar: 2\n"
420       "Foo: 4\n",
421
422       "HTTP/1.1 200 OK\n"
423       "Foo: 1, 3, 4\n"
424       "Bar: 2\n"
425     },
426     // Test filtering of cookie headers.
427     { net::HttpResponseHeaders::PERSIST_SANS_COOKIES,
428       "HTTP/1.1 200 OK\n"
429       "Set-Cookie: foo=bar; httponly\n"
430       "Set-Cookie: bar=foo\n"
431       "Bar: 1\n"
432       "Set-Cookie2: bar2=foo2\n",
433
434       "HTTP/1.1 200 OK\n"
435       "Bar: 1\n"
436     },
437     // Test LWS at the end of a header.
438     { net::HttpResponseHeaders::PERSIST_ALL,
439       "HTTP/1.1 200 OK\n"
440       "Content-Length: 450   \n"
441       "Content-Encoding: gzip\n",
442
443       "HTTP/1.1 200 OK\n"
444       "Content-Length: 450\n"
445       "Content-Encoding: gzip\n"
446     },
447     // Test LWS at the end of a header.
448     { net::HttpResponseHeaders::PERSIST_RAW,
449       "HTTP/1.1 200 OK\n"
450       "Content-Length: 450   \n"
451       "Content-Encoding: gzip\n",
452
453       "HTTP/1.1 200 OK\n"
454       "Content-Length: 450\n"
455       "Content-Encoding: gzip\n"
456     },
457     // Test filtering of transport security state headers.
458     { net::HttpResponseHeaders::PERSIST_SANS_SECURITY_STATE,
459       "HTTP/1.1 200 OK\n"
460       "Strict-Transport-Security: max-age=1576800\n"
461       "Bar: 1\n"
462       "Public-Key-Pins: max-age=100000; "
463           "pin-sha1=\"ObT42aoSpAqWdY9WfRfL7i0HsVk=\";"
464           "pin-sha1=\"7kW49EVwZG0hSNx41ZO/fUPN0ek=\"",
465
466       "HTTP/1.1 200 OK\n"
467       "Bar: 1\n"
468     },
469   };
470
471   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
472     std::string headers = tests[i].raw_headers;
473     HeadersToRaw(&headers);
474     scoped_refptr<net::HttpResponseHeaders> parsed1(
475         new net::HttpResponseHeaders(headers));
476
477     Pickle pickle;
478     parsed1->Persist(&pickle, tests[i].options);
479
480     PickleIterator iter(pickle);
481     scoped_refptr<net::HttpResponseHeaders> parsed2(
482         new net::HttpResponseHeaders(pickle, &iter));
483
484     std::string h2;
485     parsed2->GetNormalizedHeaders(&h2);
486     EXPECT_EQ(std::string(tests[i].expected_headers), h2);
487   }
488 }
489
490 TEST(HttpResponseHeadersTest, EnumerateHeader_Coalesced) {
491   // Ensure that commas in quoted strings are not regarded as value separators.
492   // Ensure that whitespace following a value is trimmed properly
493   std::string headers =
494       "HTTP/1.1 200 OK\n"
495       "Cache-control:private , no-cache=\"set-cookie,server\" \n"
496       "cache-Control: no-store\n";
497   HeadersToRaw(&headers);
498   scoped_refptr<net::HttpResponseHeaders> parsed(
499       new net::HttpResponseHeaders(headers));
500
501   void* iter = NULL;
502   std::string value;
503   EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
504   EXPECT_EQ("private", value);
505   EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
506   EXPECT_EQ("no-cache=\"set-cookie,server\"", value);
507   EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
508   EXPECT_EQ("no-store", value);
509   EXPECT_FALSE(parsed->EnumerateHeader(&iter, "cache-control", &value));
510 }
511
512 TEST(HttpResponseHeadersTest, EnumerateHeader_Challenge) {
513   // Even though WWW-Authenticate has commas, it should not be treated as
514   // coalesced values.
515   std::string headers =
516       "HTTP/1.1 401 OK\n"
517       "WWW-Authenticate:Digest realm=foobar, nonce=x, domain=y\n"
518       "WWW-Authenticate:Basic realm=quatar\n";
519   HeadersToRaw(&headers);
520   scoped_refptr<net::HttpResponseHeaders> parsed(
521       new net::HttpResponseHeaders(headers));
522
523   void* iter = NULL;
524   std::string value;
525   EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
526   EXPECT_EQ("Digest realm=foobar, nonce=x, domain=y", value);
527   EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
528   EXPECT_EQ("Basic realm=quatar", value);
529   EXPECT_FALSE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
530 }
531
532 TEST(HttpResponseHeadersTest, EnumerateHeader_DateValued) {
533   // The comma in a date valued header should not be treated as a
534   // field-value separator
535   std::string headers =
536       "HTTP/1.1 200 OK\n"
537       "Date: Tue, 07 Aug 2007 23:10:55 GMT\n"
538       "Last-Modified: Wed, 01 Aug 2007 23:23:45 GMT\n";
539   HeadersToRaw(&headers);
540   scoped_refptr<net::HttpResponseHeaders> parsed(
541       new net::HttpResponseHeaders(headers));
542
543   std::string value;
544   EXPECT_TRUE(parsed->EnumerateHeader(NULL, "date", &value));
545   EXPECT_EQ("Tue, 07 Aug 2007 23:10:55 GMT", value);
546   EXPECT_TRUE(parsed->EnumerateHeader(NULL, "last-modified", &value));
547   EXPECT_EQ("Wed, 01 Aug 2007 23:23:45 GMT", value);
548 }
549
550 TEST(HttpResponseHeadersTest, DefaultDateToGMT) {
551   // Verify we make the best interpretation when parsing dates that incorrectly
552   // do not end in "GMT" as RFC2616 requires.
553   std::string headers =
554       "HTTP/1.1 200 OK\n"
555       "Date: Tue, 07 Aug 2007 23:10:55\n"
556       "Last-Modified: Tue, 07 Aug 2007 19:10:55 EDT\n"
557       "Expires: Tue, 07 Aug 2007 23:10:55 UTC\n";
558   HeadersToRaw(&headers);
559   scoped_refptr<net::HttpResponseHeaders> parsed(
560       new net::HttpResponseHeaders(headers));
561   base::Time expected_value;
562   ASSERT_TRUE(base::Time::FromString("Tue, 07 Aug 2007 23:10:55 GMT",
563                                      &expected_value));
564
565   base::Time value;
566   // When the timezone is missing, GMT is a good guess as its what RFC2616
567   // requires.
568   EXPECT_TRUE(parsed->GetDateValue(&value));
569   EXPECT_EQ(expected_value, value);
570   // If GMT is missing but an RFC822-conforming one is present, use that.
571   EXPECT_TRUE(parsed->GetLastModifiedValue(&value));
572   EXPECT_EQ(expected_value, value);
573   // If an unknown timezone is present, treat like a missing timezone and
574   // default to GMT.  The only example of a web server not specifying "GMT"
575   // used "UTC" which is equivalent to GMT.
576   if (parsed->GetExpiresValue(&value))
577     EXPECT_EQ(expected_value, value);
578 }
579
580 TEST(HttpResponseHeadersTest, GetMimeType) {
581   const ContentTypeTestData tests[] = {
582     { "HTTP/1.1 200 OK\n"
583       "Content-type: text/html\n",
584       "text/html", true,
585       "", false,
586       "text/html" },
587     // Multiple content-type headers should give us the last one.
588     { "HTTP/1.1 200 OK\n"
589       "Content-type: text/html\n"
590       "Content-type: text/html\n",
591       "text/html", true,
592       "", false,
593       "text/html, text/html" },
594     { "HTTP/1.1 200 OK\n"
595       "Content-type: text/plain\n"
596       "Content-type: text/html\n"
597       "Content-type: text/plain\n"
598       "Content-type: text/html\n",
599       "text/html", true,
600       "", false,
601       "text/plain, text/html, text/plain, text/html" },
602     // Test charset parsing.
603     { "HTTP/1.1 200 OK\n"
604       "Content-type: text/html\n"
605       "Content-type: text/html; charset=ISO-8859-1\n",
606       "text/html", true,
607       "iso-8859-1", true,
608       "text/html, text/html; charset=ISO-8859-1" },
609     // Test charset in double quotes.
610     { "HTTP/1.1 200 OK\n"
611       "Content-type: text/html\n"
612       "Content-type: text/html; charset=\"ISO-8859-1\"\n",
613       "text/html", true,
614       "iso-8859-1", true,
615       "text/html, text/html; charset=\"ISO-8859-1\"" },
616     // If there are multiple matching content-type headers, we carry
617     // over the charset value.
618     { "HTTP/1.1 200 OK\n"
619       "Content-type: text/html;charset=utf-8\n"
620       "Content-type: text/html\n",
621       "text/html", true,
622       "utf-8", true,
623       "text/html;charset=utf-8, text/html" },
624     // Test single quotes.
625     { "HTTP/1.1 200 OK\n"
626       "Content-type: text/html;charset='utf-8'\n"
627       "Content-type: text/html\n",
628       "text/html", true,
629       "utf-8", true,
630       "text/html;charset='utf-8', text/html" },
631     // Last charset wins if matching content-type.
632     { "HTTP/1.1 200 OK\n"
633       "Content-type: text/html;charset=utf-8\n"
634       "Content-type: text/html;charset=iso-8859-1\n",
635       "text/html", true,
636       "iso-8859-1", true,
637       "text/html;charset=utf-8, text/html;charset=iso-8859-1" },
638     // Charset is ignored if the content types change.
639     { "HTTP/1.1 200 OK\n"
640       "Content-type: text/plain;charset=utf-8\n"
641       "Content-type: text/html\n",
642       "text/html", true,
643       "", false,
644       "text/plain;charset=utf-8, text/html" },
645     // Empty content-type
646     { "HTTP/1.1 200 OK\n"
647       "Content-type: \n",
648       "", false,
649       "", false,
650       "" },
651     // Emtpy charset
652     { "HTTP/1.1 200 OK\n"
653       "Content-type: text/html;charset=\n",
654       "text/html", true,
655       "", false,
656       "text/html;charset=" },
657     // Multiple charsets, last one wins.
658     { "HTTP/1.1 200 OK\n"
659       "Content-type: text/html;charset=utf-8; charset=iso-8859-1\n",
660       "text/html", true,
661       "iso-8859-1", true,
662       "text/html;charset=utf-8; charset=iso-8859-1" },
663     // Multiple params.
664     { "HTTP/1.1 200 OK\n"
665       "Content-type: text/html; foo=utf-8; charset=iso-8859-1\n",
666       "text/html", true,
667       "iso-8859-1", true,
668       "text/html; foo=utf-8; charset=iso-8859-1" },
669     { "HTTP/1.1 200 OK\n"
670       "Content-type: text/html ; charset=utf-8 ; bar=iso-8859-1\n",
671       "text/html", true,
672       "utf-8", true,
673       "text/html ; charset=utf-8 ; bar=iso-8859-1" },
674     // Comma embeded in quotes.
675     { "HTTP/1.1 200 OK\n"
676       "Content-type: text/html ; charset='utf-8,text/plain' ;\n",
677       "text/html", true,
678       "utf-8,text/plain", true,
679       "text/html ; charset='utf-8,text/plain' ;" },
680     // Charset with leading spaces.
681     { "HTTP/1.1 200 OK\n"
682       "Content-type: text/html ; charset= 'utf-8' ;\n",
683       "text/html", true,
684       "utf-8", true,
685       "text/html ; charset= 'utf-8' ;" },
686     // Media type comments in mime-type.
687     { "HTTP/1.1 200 OK\n"
688       "Content-type: text/html (html)\n",
689       "text/html", true,
690       "", false,
691       "text/html (html)" },
692     // Incomplete charset= param
693     { "HTTP/1.1 200 OK\n"
694       "Content-type: text/html; char=\n",
695       "text/html", true,
696       "", false,
697       "text/html; char=" },
698     // Invalid media type: no slash
699     { "HTTP/1.1 200 OK\n"
700       "Content-type: texthtml\n",
701       "", false,
702       "", false,
703       "texthtml" },
704     // Invalid media type: */*
705     { "HTTP/1.1 200 OK\n"
706       "Content-type: */*\n",
707       "", false,
708       "", false,
709       "*/*" },
710   };
711
712   for (size_t i = 0; i < arraysize(tests); ++i) {
713     std::string headers(tests[i].raw_headers);
714     HeadersToRaw(&headers);
715     scoped_refptr<net::HttpResponseHeaders> parsed(
716         new net::HttpResponseHeaders(headers));
717
718     std::string value;
719     EXPECT_EQ(tests[i].has_mimetype, parsed->GetMimeType(&value));
720     EXPECT_EQ(tests[i].mime_type, value);
721     value.clear();
722     EXPECT_EQ(tests[i].has_charset, parsed->GetCharset(&value));
723     EXPECT_EQ(tests[i].charset, value);
724     EXPECT_TRUE(parsed->GetNormalizedHeader("content-type", &value));
725     EXPECT_EQ(tests[i].all_content_type, value);
726   }
727 }
728
729 TEST(HttpResponseHeadersTest, RequiresValidation) {
730   const struct {
731     const char* headers;
732     bool requires_validation;
733   } tests[] = {
734     // no expiry info: expires immediately
735     { "HTTP/1.1 200 OK\n"
736       "\n",
737       true
738     },
739     // valid for a little while
740     { "HTTP/1.1 200 OK\n"
741       "cache-control: max-age=10000\n"
742       "\n",
743       false
744     },
745     // expires in the future
746     { "HTTP/1.1 200 OK\n"
747       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
748       "expires: Wed, 28 Nov 2007 01:00:00 GMT\n"
749       "\n",
750       false
751     },
752     // expired already
753     { "HTTP/1.1 200 OK\n"
754       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
755       "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
756       "\n",
757       true
758     },
759     // max-age trumps expires
760     { "HTTP/1.1 200 OK\n"
761       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
762       "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
763       "cache-control: max-age=10000\n"
764       "\n",
765       false
766     },
767     // last-modified heuristic: modified a while ago
768     { "HTTP/1.1 200 OK\n"
769       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
770       "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
771       "\n",
772       false
773     },
774     { "HTTP/1.1 203 Non-Authoritative Information\n"
775       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
776       "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
777       "\n",
778       false
779     },
780     { "HTTP/1.1 206 Partial Content\n"
781       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
782       "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
783       "\n",
784       false
785     },
786     // last-modified heuristic: modified recently
787     { "HTTP/1.1 200 OK\n"
788       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
789       "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
790       "\n",
791       true
792     },
793     { "HTTP/1.1 203 Non-Authoritative Information\n"
794       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
795       "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
796       "\n",
797       true
798     },
799     { "HTTP/1.1 206 Partial Content\n"
800       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
801       "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
802       "\n",
803       true
804     },
805     // cached permanent redirect
806     { "HTTP/1.1 301 Moved Permanently\n"
807       "\n",
808       false
809     },
810     // cached redirect: not reusable even though by default it would be
811     { "HTTP/1.1 300 Multiple Choices\n"
812       "Cache-Control: no-cache\n"
813       "\n",
814       true
815     },
816     // cached forever by default
817     { "HTTP/1.1 410 Gone\n"
818       "\n",
819       false
820     },
821     // cached temporary redirect: not reusable
822     { "HTTP/1.1 302 Found\n"
823       "\n",
824       true
825     },
826     // cached temporary redirect: reusable
827     { "HTTP/1.1 302 Found\n"
828       "cache-control: max-age=10000\n"
829       "\n",
830       false
831     },
832     // cache-control: max-age=N overrides expires: date in the past
833     { "HTTP/1.1 200 OK\n"
834       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
835       "expires: Wed, 28 Nov 2007 00:20:11 GMT\n"
836       "cache-control: max-age=10000\n"
837       "\n",
838       false
839     },
840     // cache-control: no-store overrides expires: in the future
841     { "HTTP/1.1 200 OK\n"
842       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
843       "expires: Wed, 29 Nov 2007 00:40:11 GMT\n"
844       "cache-control: no-store,private,no-cache=\"foo\"\n"
845       "\n",
846       true
847     },
848     // pragma: no-cache overrides last-modified heuristic
849     { "HTTP/1.1 200 OK\n"
850       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
851       "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
852       "pragma: no-cache\n"
853       "\n",
854       true
855     },
856     // TODO(darin): add many many more tests here
857   };
858   base::Time request_time, response_time, current_time;
859   base::Time::FromString("Wed, 28 Nov 2007 00:40:09 GMT", &request_time);
860   base::Time::FromString("Wed, 28 Nov 2007 00:40:12 GMT", &response_time);
861   base::Time::FromString("Wed, 28 Nov 2007 00:45:20 GMT", &current_time);
862
863   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
864     std::string headers(tests[i].headers);
865     HeadersToRaw(&headers);
866     scoped_refptr<net::HttpResponseHeaders> parsed(
867         new net::HttpResponseHeaders(headers));
868
869     bool requires_validation =
870         parsed->RequiresValidation(request_time, response_time, current_time);
871     EXPECT_EQ(tests[i].requires_validation, requires_validation);
872   }
873 }
874
875 TEST(HttpResponseHeadersTest, Update) {
876   const struct {
877     const char* orig_headers;
878     const char* new_headers;
879     const char* expected_headers;
880   } tests[] = {
881     { "HTTP/1.1 200 OK\n",
882
883       "HTTP/1/1 304 Not Modified\n"
884       "connection: keep-alive\n"
885       "Cache-control: max-age=10000\n",
886
887       "HTTP/1.1 200 OK\n"
888       "Cache-control: max-age=10000\n"
889     },
890     { "HTTP/1.1 200 OK\n"
891       "Foo: 1\n"
892       "Cache-control: private\n",
893
894       "HTTP/1/1 304 Not Modified\n"
895       "connection: keep-alive\n"
896       "Cache-control: max-age=10000\n",
897
898       "HTTP/1.1 200 OK\n"
899       "Cache-control: max-age=10000\n"
900       "Foo: 1\n"
901     },
902     { "HTTP/1.1 200 OK\n"
903       "Foo: 1\n"
904       "Cache-control: private\n",
905
906       "HTTP/1/1 304 Not Modified\n"
907       "connection: keep-alive\n"
908       "Cache-CONTROL: max-age=10000\n",
909
910       "HTTP/1.1 200 OK\n"
911       "Cache-CONTROL: max-age=10000\n"
912       "Foo: 1\n"
913     },
914     { "HTTP/1.1 200 OK\n"
915       "Content-Length: 450\n",
916
917       "HTTP/1/1 304 Not Modified\n"
918       "connection: keep-alive\n"
919       "Cache-control:      max-age=10001   \n",
920
921       "HTTP/1.1 200 OK\n"
922       "Cache-control: max-age=10001\n"
923       "Content-Length: 450\n"
924     },
925     { "HTTP/1.1 200 OK\n"
926       "X-Frame-Options: DENY\n",
927
928       "HTTP/1/1 304 Not Modified\n"
929       "X-Frame-Options: ALLOW\n",
930
931       "HTTP/1.1 200 OK\n"
932       "X-Frame-Options: DENY\n",
933     },
934     { "HTTP/1.1 200 OK\n"
935       "X-WebKit-CSP: default-src 'none'\n",
936
937       "HTTP/1/1 304 Not Modified\n"
938       "X-WebKit-CSP: default-src *\n",
939
940       "HTTP/1.1 200 OK\n"
941       "X-WebKit-CSP: default-src 'none'\n",
942     },
943     { "HTTP/1.1 200 OK\n"
944       "X-XSS-Protection: 1\n",
945
946       "HTTP/1/1 304 Not Modified\n"
947       "X-XSS-Protection: 0\n",
948
949       "HTTP/1.1 200 OK\n"
950       "X-XSS-Protection: 1\n",
951     },
952     { "HTTP/1.1 200 OK\n",
953
954       "HTTP/1/1 304 Not Modified\n"
955       "X-Content-Type-Options: nosniff\n",
956
957       "HTTP/1.1 200 OK\n"
958     },
959   };
960
961   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
962     std::string orig_headers(tests[i].orig_headers);
963     HeadersToRaw(&orig_headers);
964     scoped_refptr<net::HttpResponseHeaders> parsed(
965         new net::HttpResponseHeaders(orig_headers));
966
967     std::string new_headers(tests[i].new_headers);
968     HeadersToRaw(&new_headers);
969     scoped_refptr<net::HttpResponseHeaders> new_parsed(
970         new net::HttpResponseHeaders(new_headers));
971
972     parsed->Update(*new_parsed.get());
973
974     std::string resulting_headers;
975     parsed->GetNormalizedHeaders(&resulting_headers);
976     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
977   }
978 }
979
980 TEST(HttpResponseHeadersTest, EnumerateHeaderLines) {
981   const struct {
982     const char* headers;
983     const char* expected_lines;
984   } tests[] = {
985     { "HTTP/1.1 200 OK\n",
986
987       ""
988     },
989     { "HTTP/1.1 200 OK\n"
990       "Foo: 1\n",
991
992       "Foo: 1\n"
993     },
994     { "HTTP/1.1 200 OK\n"
995       "Foo: 1\n"
996       "Bar: 2\n"
997       "Foo: 3\n",
998
999       "Foo: 1\nBar: 2\nFoo: 3\n"
1000     },
1001     { "HTTP/1.1 200 OK\n"
1002       "Foo: 1, 2, 3\n",
1003
1004       "Foo: 1, 2, 3\n"
1005     },
1006   };
1007   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1008     std::string headers(tests[i].headers);
1009     HeadersToRaw(&headers);
1010     scoped_refptr<net::HttpResponseHeaders> parsed(
1011         new net::HttpResponseHeaders(headers));
1012
1013     std::string name, value, lines;
1014
1015     void* iter = NULL;
1016     while (parsed->EnumerateHeaderLines(&iter, &name, &value)) {
1017       lines.append(name);
1018       lines.append(": ");
1019       lines.append(value);
1020       lines.append("\n");
1021     }
1022
1023     EXPECT_EQ(std::string(tests[i].expected_lines), lines);
1024   }
1025 }
1026
1027 TEST(HttpResponseHeadersTest, IsRedirect) {
1028   const struct {
1029     const char* headers;
1030     const char* location;
1031     bool is_redirect;
1032   } tests[] = {
1033     { "HTTP/1.1 200 OK\n",
1034       "",
1035       false
1036     },
1037     { "HTTP/1.1 301 Moved\n"
1038       "Location: http://foopy/\n",
1039       "http://foopy/",
1040       true
1041     },
1042     { "HTTP/1.1 301 Moved\n"
1043       "Location: \t \n",
1044       "",
1045       false
1046     },
1047     // we use the first location header as the target of the redirect
1048     { "HTTP/1.1 301 Moved\n"
1049       "Location: http://foo/\n"
1050       "Location: http://bar/\n",
1051       "http://foo/",
1052       true
1053     },
1054     // we use the first _valid_ location header as the target of the redirect
1055     { "HTTP/1.1 301 Moved\n"
1056       "Location: \n"
1057       "Location: http://bar/\n",
1058       "http://bar/",
1059       true
1060     },
1061     // bug 1050541 (location header w/ an unescaped comma)
1062     { "HTTP/1.1 301 Moved\n"
1063       "Location: http://foo/bar,baz.html\n",
1064       "http://foo/bar,baz.html",
1065       true
1066     },
1067     // bug 1224617 (location header w/ non-ASCII bytes)
1068     { "HTTP/1.1 301 Moved\n"
1069       "Location: http://foo/bar?key=\xE4\xF6\xFC\n",
1070       "http://foo/bar?key=%E4%F6%FC",
1071       true
1072     },
1073     // Shift_JIS, Big5, and GBK contain multibyte characters with the trailing
1074     // byte falling in the ASCII range.
1075     { "HTTP/1.1 301 Moved\n"
1076       "Location: http://foo/bar?key=\x81\x5E\xD8\xBF\n",
1077       "http://foo/bar?key=%81^%D8%BF",
1078       true
1079     },
1080     { "HTTP/1.1 301 Moved\n"
1081       "Location: http://foo/bar?key=\x82\x40\xBD\xC4\n",
1082       "http://foo/bar?key=%82@%BD%C4",
1083       true
1084     },
1085     { "HTTP/1.1 301 Moved\n"
1086       "Location: http://foo/bar?key=\x83\x5C\x82\x5D\xCB\xD7\n",
1087       "http://foo/bar?key=%83\\%82]%CB%D7",
1088       true
1089     },
1090   };
1091   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1092     std::string headers(tests[i].headers);
1093     HeadersToRaw(&headers);
1094     scoped_refptr<net::HttpResponseHeaders> parsed(
1095         new net::HttpResponseHeaders(headers));
1096
1097     std::string location;
1098     EXPECT_EQ(parsed->IsRedirect(&location), tests[i].is_redirect);
1099     EXPECT_EQ(location, tests[i].location);
1100   }
1101 }
1102
1103 TEST(HttpResponseHeadersTest, GetContentLength) {
1104   const struct {
1105     const char* headers;
1106     int64 expected_len;
1107   } tests[] = {
1108     { "HTTP/1.1 200 OK\n",
1109       -1
1110     },
1111     { "HTTP/1.1 200 OK\n"
1112       "Content-Length: 10\n",
1113       10
1114     },
1115     { "HTTP/1.1 200 OK\n"
1116       "Content-Length: \n",
1117       -1
1118     },
1119     { "HTTP/1.1 200 OK\n"
1120       "Content-Length: abc\n",
1121       -1
1122     },
1123     { "HTTP/1.1 200 OK\n"
1124       "Content-Length: -10\n",
1125       -1
1126     },
1127     { "HTTP/1.1 200 OK\n"
1128       "Content-Length:  +10\n",
1129       -1
1130     },
1131     { "HTTP/1.1 200 OK\n"
1132       "Content-Length: 23xb5\n",
1133       -1
1134     },
1135     { "HTTP/1.1 200 OK\n"
1136       "Content-Length: 0xA\n",
1137       -1
1138     },
1139     { "HTTP/1.1 200 OK\n"
1140       "Content-Length: 010\n",
1141       10
1142     },
1143     // Content-Length too big, will overflow an int64
1144     { "HTTP/1.1 200 OK\n"
1145       "Content-Length: 40000000000000000000\n",
1146       -1
1147     },
1148     { "HTTP/1.1 200 OK\n"
1149       "Content-Length:       10\n",
1150       10
1151     },
1152     { "HTTP/1.1 200 OK\n"
1153       "Content-Length: 10  \n",
1154       10
1155     },
1156     { "HTTP/1.1 200 OK\n"
1157       "Content-Length: \t10\n",
1158       10
1159     },
1160     { "HTTP/1.1 200 OK\n"
1161       "Content-Length: \v10\n",
1162       -1
1163     },
1164     { "HTTP/1.1 200 OK\n"
1165       "Content-Length: \f10\n",
1166       -1
1167     },
1168     { "HTTP/1.1 200 OK\n"
1169       "cOnTeNt-LENgth: 33\n",
1170       33
1171     },
1172     { "HTTP/1.1 200 OK\n"
1173       "Content-Length: 34\r\n",
1174       -1
1175     },
1176   };
1177   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1178     std::string headers(tests[i].headers);
1179     HeadersToRaw(&headers);
1180     scoped_refptr<net::HttpResponseHeaders> parsed(
1181         new net::HttpResponseHeaders(headers));
1182
1183     EXPECT_EQ(tests[i].expected_len, parsed->GetContentLength());
1184   }
1185 }
1186
1187 TEST(HttpResponseHeaders, GetContentRange) {
1188   const struct {
1189     const char* headers;
1190     bool expected_return_value;
1191     int64 expected_first_byte_position;
1192     int64 expected_last_byte_position;
1193     int64 expected_instance_size;
1194   }  tests[] = {
1195     { "HTTP/1.1 206 Partial Content",
1196       false,
1197       -1,
1198       -1,
1199       -1
1200     },
1201     { "HTTP/1.1 206 Partial Content\n"
1202       "Content-Range:",
1203       false,
1204       -1,
1205       -1,
1206       -1
1207     },
1208     { "HTTP/1.1 206 Partial Content\n"
1209       "Content-Range: megabytes 0-10/50",
1210       false,
1211       -1,
1212       -1,
1213       -1
1214     },
1215     { "HTTP/1.1 206 Partial Content\n"
1216       "Content-Range: 0-10/50",
1217       false,
1218       -1,
1219       -1,
1220       -1
1221     },
1222     { "HTTP/1.1 206 Partial Content\n"
1223       "Content-Range: Bytes 0-50/51",
1224       true,
1225       0,
1226       50,
1227       51
1228     },
1229     { "HTTP/1.1 206 Partial Content\n"
1230       "Content-Range: bytes 0-50/51",
1231       true,
1232       0,
1233       50,
1234       51
1235     },
1236     { "HTTP/1.1 206 Partial Content\n"
1237       "Content-Range: bytes\t0-50/51",
1238       false,
1239       -1,
1240       -1,
1241       -1
1242     },
1243     { "HTTP/1.1 206 Partial Content\n"
1244       "Content-Range:     bytes 0-50/51",
1245       true,
1246       0,
1247       50,
1248       51
1249     },
1250     { "HTTP/1.1 206 Partial Content\n"
1251       "Content-Range:     bytes    0    -   50  \t / \t51",
1252       true,
1253       0,
1254       50,
1255       51
1256     },
1257     { "HTTP/1.1 206 Partial Content\n"
1258       "Content-Range: bytes 0\t-\t50\t/\t51\t",
1259       true,
1260       0,
1261       50,
1262       51
1263     },
1264     { "HTTP/1.1 206 Partial Content\n"
1265       "Content-Range:   \tbytes\t\t\t 0\t-\t50\t/\t51\t",
1266       true,
1267       0,
1268       50,
1269       51
1270     },
1271     { "HTTP/1.1 206 Partial Content\n"
1272       "Content-Range: \t   bytes \t  0    -   50   /   5   1",
1273       false,
1274       0,
1275       50,
1276       -1
1277     },
1278     { "HTTP/1.1 206 Partial Content\n"
1279       "Content-Range: \t   bytes \t  0    -   5 0   /   51",
1280       false,
1281       -1,
1282       -1,
1283       -1
1284     },
1285     { "HTTP/1.1 206 Partial Content\n"
1286       "Content-Range: bytes 50-0/51",
1287       false,
1288       50,
1289       0,
1290       -1
1291     },
1292     { "HTTP/1.1 416 Requested range not satisfiable\n"
1293       "Content-Range: bytes * /*",
1294       false,
1295       -1,
1296       -1,
1297       -1
1298     },
1299     { "HTTP/1.1 416 Requested range not satisfiable\n"
1300       "Content-Range: bytes *   /    *   ",
1301       false,
1302       -1,
1303       -1,
1304       -1
1305     },
1306     { "HTTP/1.1 206 Partial Content\n"
1307       "Content-Range: bytes 0-50/*",
1308       false,
1309       0,
1310       50,
1311       -1
1312     },
1313     { "HTTP/1.1 206 Partial Content\n"
1314       "Content-Range: bytes 0-50  /    * ",
1315       false,
1316       0,
1317       50,
1318       -1
1319     },
1320     { "HTTP/1.1 206 Partial Content\n"
1321       "Content-Range: bytes 0-10000000000/10000000001",
1322       true,
1323       0,
1324       10000000000ll,
1325       10000000001ll
1326     },
1327     { "HTTP/1.1 206 Partial Content\n"
1328       "Content-Range: bytes 0-10000000000/10000000000",
1329       false,
1330       0,
1331       10000000000ll,
1332       10000000000ll
1333     },
1334     // 64 bits wraparound.
1335     { "HTTP/1.1 206 Partial Content\n"
1336       "Content-Range: bytes 0 - 9223372036854775807 / 100",
1337       false,
1338       0,
1339       kint64max,
1340       100
1341     },
1342     // 64 bits wraparound.
1343     { "HTTP/1.1 206 Partial Content\n"
1344       "Content-Range: bytes 0 - 100 / -9223372036854775808",
1345       false,
1346       0,
1347       100,
1348       kint64min
1349     },
1350     { "HTTP/1.1 206 Partial Content\n"
1351       "Content-Range: bytes */50",
1352       false,
1353       -1,
1354       -1,
1355       50
1356     },
1357     { "HTTP/1.1 206 Partial Content\n"
1358       "Content-Range: bytes 0-50/10",
1359       false,
1360       0,
1361       50,
1362       10
1363     },
1364     { "HTTP/1.1 206 Partial Content\n"
1365       "Content-Range: bytes 40-50/45",
1366       false,
1367       40,
1368       50,
1369       45
1370     },
1371     { "HTTP/1.1 206 Partial Content\n"
1372       "Content-Range: bytes 0-50/-10",
1373       false,
1374       0,
1375       50,
1376       -10
1377     },
1378     { "HTTP/1.1 206 Partial Content\n"
1379       "Content-Range: bytes 0-0/1",
1380       true,
1381       0,
1382       0,
1383       1
1384     },
1385     { "HTTP/1.1 206 Partial Content\n"
1386       "Content-Range: bytes 0-40000000000000000000/40000000000000000001",
1387       false,
1388       -1,
1389       -1,
1390       -1
1391     },
1392     { "HTTP/1.1 206 Partial Content\n"
1393       "Content-Range: bytes 1-/100",
1394       false,
1395       -1,
1396       -1,
1397       -1
1398     },
1399     { "HTTP/1.1 206 Partial Content\n"
1400       "Content-Range: bytes -/100",
1401       false,
1402       -1,
1403       -1,
1404       -1
1405     },
1406     { "HTTP/1.1 206 Partial Content\n"
1407       "Content-Range: bytes -1/100",
1408       false,
1409       -1,
1410       -1,
1411       -1
1412     },
1413     { "HTTP/1.1 206 Partial Content\n"
1414       "Content-Range: bytes 0-1233/*",
1415       false,
1416       0,
1417       1233,
1418       -1
1419     },
1420     { "HTTP/1.1 206 Partial Content\n"
1421       "Content-Range: bytes -123 - -1/100",
1422       false,
1423       -1,
1424       -1,
1425       -1
1426     },
1427   };
1428   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1429     std::string headers(tests[i].headers);
1430     HeadersToRaw(&headers);
1431     scoped_refptr<net::HttpResponseHeaders> parsed(
1432         new net::HttpResponseHeaders(headers));
1433
1434     int64 first_byte_position;
1435     int64 last_byte_position;
1436     int64 instance_size;
1437     bool return_value = parsed->GetContentRange(&first_byte_position,
1438                                                 &last_byte_position,
1439                                                 &instance_size);
1440     EXPECT_EQ(tests[i].expected_return_value, return_value);
1441     EXPECT_EQ(tests[i].expected_first_byte_position, first_byte_position);
1442     EXPECT_EQ(tests[i].expected_last_byte_position, last_byte_position);
1443     EXPECT_EQ(tests[i].expected_instance_size, instance_size);
1444   }
1445 }
1446
1447 TEST(HttpResponseHeadersTest, IsKeepAlive) {
1448   const struct {
1449     const char* headers;
1450     bool expected_keep_alive;
1451   } tests[] = {
1452     // The status line fabricated by HttpNetworkTransaction for a 0.9 response.
1453     // Treated as 0.9.
1454     { "HTTP/0.9 200 OK",
1455       false
1456     },
1457     // This could come from a broken server.  Treated as 1.0 because it has a
1458     // header.
1459     { "HTTP/0.9 200 OK\n"
1460       "connection: keep-alive\n",
1461       true
1462     },
1463     { "HTTP/1.1 200 OK\n",
1464       true
1465     },
1466     { "HTTP/1.0 200 OK\n",
1467       false
1468     },
1469     { "HTTP/1.0 200 OK\n"
1470       "connection: close\n",
1471       false
1472     },
1473     { "HTTP/1.0 200 OK\n"
1474       "connection: keep-alive\n",
1475       true
1476     },
1477     { "HTTP/1.0 200 OK\n"
1478       "connection: kEeP-AliVe\n",
1479       true
1480     },
1481     { "HTTP/1.0 200 OK\n"
1482       "connection: keep-aliveX\n",
1483       false
1484     },
1485     { "HTTP/1.1 200 OK\n"
1486       "connection: close\n",
1487       false
1488     },
1489     { "HTTP/1.1 200 OK\n"
1490       "connection: keep-alive\n",
1491       true
1492     },
1493     { "HTTP/1.0 200 OK\n"
1494       "proxy-connection: close\n",
1495       false
1496     },
1497     { "HTTP/1.0 200 OK\n"
1498       "proxy-connection: keep-alive\n",
1499       true
1500     },
1501     { "HTTP/1.1 200 OK\n"
1502       "proxy-connection: close\n",
1503       false
1504     },
1505     { "HTTP/1.1 200 OK\n"
1506       "proxy-connection: keep-alive\n",
1507       true
1508     },
1509   };
1510   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1511     std::string headers(tests[i].headers);
1512     HeadersToRaw(&headers);
1513     scoped_refptr<net::HttpResponseHeaders> parsed(
1514         new net::HttpResponseHeaders(headers));
1515
1516     EXPECT_EQ(tests[i].expected_keep_alive, parsed->IsKeepAlive());
1517   }
1518 }
1519
1520 TEST(HttpResponseHeadersTest, HasStrongValidators) {
1521   const struct {
1522     const char* headers;
1523     bool expected_result;
1524   } tests[] = {
1525     { "HTTP/0.9 200 OK",
1526       false
1527     },
1528     { "HTTP/1.0 200 OK\n"
1529       "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
1530       "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
1531       "ETag: \"foo\"\n",
1532       false
1533     },
1534     { "HTTP/1.1 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       true
1539     },
1540     { "HTTP/1.1 200 OK\n"
1541       "Date: Wed, 28 Nov 2007 00:41:10 GMT\n"
1542       "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1543       true
1544     },
1545     { "HTTP/1.1 200 OK\n"
1546       "Date: Wed, 28 Nov 2007 00:41:09 GMT\n"
1547       "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1548       false
1549     },
1550     { "HTTP/1.1 200 OK\n"
1551       "ETag: \"foo\"\n",
1552       true
1553     },
1554     // This is not really a weak etag:
1555     { "HTTP/1.1 200 OK\n"
1556       "etag: \"w/foo\"\n",
1557       true
1558     },
1559     // This is a weak etag:
1560     { "HTTP/1.1 200 OK\n"
1561       "etag: w/\"foo\"\n",
1562       false
1563     },
1564     { "HTTP/1.1 200 OK\n"
1565       "etag:    W  /   \"foo\"\n",
1566       false
1567     }
1568   };
1569   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1570     std::string headers(tests[i].headers);
1571     HeadersToRaw(&headers);
1572     scoped_refptr<net::HttpResponseHeaders> parsed(
1573         new net::HttpResponseHeaders(headers));
1574
1575     EXPECT_EQ(tests[i].expected_result, parsed->HasStrongValidators()) <<
1576         "Failed test case " << i;
1577   }
1578 }
1579
1580 TEST(HttpResponseHeadersTest, GetStatusText) {
1581   std::string headers("HTTP/1.1 404 Not Found");
1582   HeadersToRaw(&headers);
1583     scoped_refptr<net::HttpResponseHeaders> parsed(
1584         new net::HttpResponseHeaders(headers));
1585   EXPECT_EQ(std::string("Not Found"), parsed->GetStatusText());
1586 }
1587
1588 TEST(HttpResponseHeadersTest, GetStatusTextMissing) {
1589   std::string headers("HTTP/1.1 404");
1590   HeadersToRaw(&headers);
1591     scoped_refptr<net::HttpResponseHeaders> parsed(
1592         new net::HttpResponseHeaders(headers));
1593   // Since the status line gets normalized, we have OK
1594   EXPECT_EQ(std::string("OK"), parsed->GetStatusText());
1595 }
1596
1597 TEST(HttpResponseHeadersTest, GetStatusTextMultiSpace) {
1598   std::string headers("HTTP/1.0     404     Not   Found");
1599   HeadersToRaw(&headers);
1600     scoped_refptr<net::HttpResponseHeaders> parsed(
1601         new net::HttpResponseHeaders(headers));
1602   EXPECT_EQ(std::string("Not   Found"), parsed->GetStatusText());
1603 }
1604
1605 TEST(HttpResponseHeadersTest, GetStatusBadStatusLine) {
1606   std::string headers("Foo bar.");
1607   HeadersToRaw(&headers);
1608     scoped_refptr<net::HttpResponseHeaders> parsed(
1609         new net::HttpResponseHeaders(headers));
1610   // The bad status line would have gotten rewritten as
1611   // HTTP/1.0 200 OK.
1612   EXPECT_EQ(std::string("OK"), parsed->GetStatusText());
1613 }
1614
1615 TEST(HttpResponseHeadersTest, AddHeader) {
1616   const struct {
1617     const char* orig_headers;
1618     const char* new_header;
1619     const char* expected_headers;
1620   } tests[] = {
1621     { "HTTP/1.1 200 OK\n"
1622       "connection: keep-alive\n"
1623       "Cache-control: max-age=10000\n",
1624
1625       "Content-Length: 450",
1626
1627       "HTTP/1.1 200 OK\n"
1628       "connection: keep-alive\n"
1629       "Cache-control: max-age=10000\n"
1630       "Content-Length: 450\n"
1631     },
1632     { "HTTP/1.1 200 OK\n"
1633       "connection: keep-alive\n"
1634       "Cache-control: max-age=10000    \n",
1635
1636       "Content-Length: 450  ",
1637
1638       "HTTP/1.1 200 OK\n"
1639       "connection: keep-alive\n"
1640       "Cache-control: max-age=10000\n"
1641       "Content-Length: 450\n"
1642     },
1643   };
1644
1645   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1646     std::string orig_headers(tests[i].orig_headers);
1647     HeadersToRaw(&orig_headers);
1648     scoped_refptr<net::HttpResponseHeaders> parsed(
1649         new net::HttpResponseHeaders(orig_headers));
1650
1651     std::string new_header(tests[i].new_header);
1652     parsed->AddHeader(new_header);
1653
1654     std::string resulting_headers;
1655     parsed->GetNormalizedHeaders(&resulting_headers);
1656     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1657   }
1658 }
1659
1660 TEST(HttpResponseHeadersTest, RemoveHeader) {
1661   const struct {
1662     const char* orig_headers;
1663     const char* to_remove;
1664     const char* expected_headers;
1665   } tests[] = {
1666     { "HTTP/1.1 200 OK\n"
1667       "connection: keep-alive\n"
1668       "Cache-control: max-age=10000\n"
1669       "Content-Length: 450\n",
1670
1671       "Content-Length",
1672
1673       "HTTP/1.1 200 OK\n"
1674       "connection: keep-alive\n"
1675       "Cache-control: max-age=10000\n"
1676     },
1677     { "HTTP/1.1 200 OK\n"
1678       "connection: keep-alive  \n"
1679       "Content-Length  : 450  \n"
1680       "Cache-control: max-age=10000\n",
1681
1682       "Content-Length",
1683
1684       "HTTP/1.1 200 OK\n"
1685       "connection: keep-alive\n"
1686       "Cache-control: max-age=10000\n"
1687     },
1688   };
1689
1690   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1691     std::string orig_headers(tests[i].orig_headers);
1692     HeadersToRaw(&orig_headers);
1693     scoped_refptr<net::HttpResponseHeaders> parsed(
1694         new net::HttpResponseHeaders(orig_headers));
1695
1696     std::string name(tests[i].to_remove);
1697     parsed->RemoveHeader(name);
1698
1699     std::string resulting_headers;
1700     parsed->GetNormalizedHeaders(&resulting_headers);
1701     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1702   }
1703 }
1704
1705 TEST(HttpResponseHeadersTest, RemoveIndividualHeader) {
1706   const struct {
1707     const char* orig_headers;
1708     const char* to_remove_name;
1709     const char* to_remove_value;
1710     const char* expected_headers;
1711   } tests[] = {
1712     { "HTTP/1.1 200 OK\n"
1713       "connection: keep-alive\n"
1714       "Cache-control: max-age=10000\n"
1715       "Content-Length: 450\n",
1716
1717       "Content-Length",
1718
1719       "450",
1720
1721       "HTTP/1.1 200 OK\n"
1722       "connection: keep-alive\n"
1723       "Cache-control: max-age=10000\n"
1724     },
1725     { "HTTP/1.1 200 OK\n"
1726       "connection: keep-alive  \n"
1727       "Content-Length  : 450  \n"
1728       "Cache-control: max-age=10000\n",
1729
1730       "Content-Length",
1731
1732       "450",
1733
1734       "HTTP/1.1 200 OK\n"
1735       "connection: keep-alive\n"
1736       "Cache-control: max-age=10000\n"
1737     },
1738     { "HTTP/1.1 200 OK\n"
1739       "connection: keep-alive  \n"
1740       "Content-Length: 450\n"
1741       "Cache-control: max-age=10000\n",
1742
1743       "Content-Length",  // Matching name.
1744
1745       "999",  // Mismatching value.
1746
1747       "HTTP/1.1 200 OK\n"
1748       "connection: keep-alive\n"
1749       "Content-Length: 450\n"
1750       "Cache-control: max-age=10000\n"
1751     },
1752     { "HTTP/1.1 200 OK\n"
1753       "connection: keep-alive  \n"
1754       "Foo: bar, baz\n"
1755       "Foo: bar\n"
1756       "Cache-control: max-age=10000\n",
1757
1758       "Foo",
1759
1760       "bar, baz",  // Space in value.
1761
1762       "HTTP/1.1 200 OK\n"
1763       "connection: keep-alive\n"
1764       "Foo: bar\n"
1765       "Cache-control: max-age=10000\n"
1766     },
1767     { "HTTP/1.1 200 OK\n"
1768       "connection: keep-alive  \n"
1769       "Foo: bar, baz\n"
1770       "Cache-control: max-age=10000\n",
1771
1772       "Foo",
1773
1774       "baz",  // Only partial match -> ignored.
1775
1776       "HTTP/1.1 200 OK\n"
1777       "connection: keep-alive\n"
1778       "Foo: bar, baz\n"
1779       "Cache-control: max-age=10000\n"
1780     },
1781   };
1782
1783   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1784     std::string orig_headers(tests[i].orig_headers);
1785     HeadersToRaw(&orig_headers);
1786     scoped_refptr<net::HttpResponseHeaders> parsed(
1787         new net::HttpResponseHeaders(orig_headers));
1788
1789     std::string name(tests[i].to_remove_name);
1790     std::string value(tests[i].to_remove_value);
1791     parsed->RemoveHeaderLine(name, value);
1792
1793     std::string resulting_headers;
1794     parsed->GetNormalizedHeaders(&resulting_headers);
1795     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1796   }
1797 }
1798
1799 TEST(HttpResponseHeadersTest, ReplaceStatus) {
1800   const struct {
1801     const char* orig_headers;
1802     const char* new_status;
1803     const char* expected_headers;
1804   } tests[] = {
1805     { "HTTP/1.1 206 Partial Content\n"
1806       "connection: keep-alive\n"
1807       "Cache-control: max-age=10000\n"
1808       "Content-Length: 450\n",
1809
1810       "HTTP/1.1 200 OK",
1811
1812       "HTTP/1.1 200 OK\n"
1813       "connection: keep-alive\n"
1814       "Cache-control: max-age=10000\n"
1815       "Content-Length: 450\n"
1816     },
1817     { "HTTP/1.1 200 OK\n"
1818       "connection: keep-alive\n",
1819
1820       "HTTP/1.1 304 Not Modified",
1821
1822       "HTTP/1.1 304 Not Modified\n"
1823       "connection: keep-alive\n"
1824     },
1825     { "HTTP/1.1 200 OK\n"
1826       "connection: keep-alive  \n"
1827       "Content-Length  : 450   \n"
1828       "Cache-control: max-age=10000\n",
1829
1830       "HTTP/1//1 304 Not Modified",
1831
1832       "HTTP/1.0 304 Not Modified\n"
1833       "connection: keep-alive\n"
1834       "Content-Length: 450\n"
1835       "Cache-control: max-age=10000\n"
1836     },
1837   };
1838
1839   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1840     std::string orig_headers(tests[i].orig_headers);
1841     HeadersToRaw(&orig_headers);
1842     scoped_refptr<net::HttpResponseHeaders> parsed(
1843         new net::HttpResponseHeaders(orig_headers));
1844
1845     std::string name(tests[i].new_status);
1846     parsed->ReplaceStatusLine(name);
1847
1848     std::string resulting_headers;
1849     parsed->GetNormalizedHeaders(&resulting_headers);
1850     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1851   }
1852 }
1853
1854 TEST(HttpResponseHeadersTest, ToNetLogParamAndBackAgain) {
1855   std::string headers("HTTP/1.1 404\n"
1856                       "Content-Length: 450\n"
1857                       "Connection: keep-alive\n");
1858   HeadersToRaw(&headers);
1859   scoped_refptr<net::HttpResponseHeaders> parsed(
1860       new net::HttpResponseHeaders(headers));
1861
1862   scoped_ptr<base::Value> event_param(
1863       parsed->NetLogCallback(net::NetLog::LOG_ALL_BUT_BYTES));
1864   scoped_refptr<net::HttpResponseHeaders> recreated;
1865
1866   ASSERT_TRUE(net::HttpResponseHeaders::FromNetLogParam(event_param.get(),
1867                                                         &recreated));
1868   ASSERT_TRUE(recreated.get());
1869   EXPECT_EQ(parsed->GetHttpVersion(), recreated->GetHttpVersion());
1870   EXPECT_EQ(parsed->response_code(), recreated->response_code());
1871   EXPECT_EQ(parsed->GetContentLength(), recreated->GetContentLength());
1872   EXPECT_EQ(parsed->IsKeepAlive(), recreated->IsKeepAlive());
1873
1874   std::string normalized_parsed;
1875   parsed->GetNormalizedHeaders(&normalized_parsed);
1876   std::string normalized_recreated;
1877   parsed->GetNormalizedHeaders(&normalized_recreated);
1878   EXPECT_EQ(normalized_parsed, normalized_recreated);
1879 }
1880
1881 #if defined(SPDY_PROXY_AUTH_ORIGIN)
1882 TEST(HttpResponseHeadersTest, GetProxyBypassInfo) {
1883   const struct {
1884      const char* headers;
1885      bool expected_result;
1886      int64 expected_retry_delay;
1887   } tests[] = {
1888     { "HTTP/1.1 200 OK\n"
1889       "Content-Length: 999\n",
1890       false,
1891       0,
1892     },
1893     { "HTTP/1.1 200 OK\n"
1894       "connection: keep-alive\n"
1895       "Content-Length: 999\n",
1896       false,
1897       0,
1898     },
1899     { "HTTP/1.1 200 OK\n"
1900       "connection: proxy-bypass\n"
1901       "Content-Length: 999\n",
1902       true,
1903       0,
1904     },
1905     { "HTTP/1.1 200 OK\n"
1906       "connection: proxy-bypass\n"
1907       "Chrome-Proxy: bypass=86400\n"
1908       "Content-Length: 999\n",
1909       true,
1910       86400
1911     },
1912     { "HTTP/1.1 200 OK\n"
1913       "connection: keep-alive\n"
1914       "Chrome-Proxy: bypass=86400\n"
1915       "Content-Length: 999\n",
1916       true,
1917       86400
1918     },
1919     { "HTTP/1.1 200 OK\n"
1920       "connection: keep-alive\n"
1921       "Chrome-Proxy: bypass=0\n"
1922       "Content-Length: 999\n",
1923       true,
1924       0
1925     },
1926     { "HTTP/1.1 200 OK\n"
1927       "connection: keep-alive\n"
1928       "Chrome-Proxy: bypass=-1\n"
1929       "Content-Length: 999\n",
1930       false,
1931       0
1932     },
1933     { "HTTP/1.1 200 OK\n"
1934       "connection: keep-alive\n"
1935       "Chrome-Proxy: bypass=xyz\n"
1936       "Content-Length: 999\n",
1937       false,
1938       0
1939     },
1940     { "HTTP/1.1 200 OK\n"
1941       "connection: keep-alive\n"
1942       "Chrome-Proxy: bypass\n"
1943       "Content-Length: 999\n",
1944       false,
1945       0
1946     },
1947     { "HTTP/1.1 200 OK\n"
1948       "connection: keep-alive\n"
1949       "Chrome-Proxy: foo=abc, bypass=86400\n"
1950       "Content-Length: 999\n",
1951       true,
1952       86400
1953     },
1954     { "HTTP/1.1 200 OK\n"
1955       "connection: keep-alive\n"
1956       "Chrome-Proxy: bypass=86400, bar=abc\n"
1957       "Content-Length: 999\n",
1958       true,
1959       86400
1960     },
1961     { "HTTP/1.1 200 OK\n"
1962       "connection: keep-alive\n"
1963       "Chrome-Proxy: bypass=3600\n"
1964       "Chrome-Proxy: bypass=86400\n"
1965       "Content-Length: 999\n",
1966       true,
1967       3600
1968     },
1969     { "HTTP/1.1 200 OK\n"
1970       "connection: keep-alive\n"
1971       "Chrome-Proxy: bypass=3600, bypass=86400\n"
1972       "Content-Length: 999\n",
1973       true,
1974       3600
1975     },
1976     { "HTTP/1.1 200 OK\n"
1977       "connection: keep-alive\n"
1978       "Chrome-Proxy: bypass=, bypass=86400\n"
1979       "Content-Length: 999\n",
1980       true,
1981       86400
1982     },
1983     { "HTTP/1.1 200 OK\n"
1984       "connection: keep-alive\n"
1985       "Chrome-Proxy: bypass\n"
1986       "Chrome-Proxy: bypass=86400\n"
1987       "Content-Length: 999\n",
1988       true,
1989       86400
1990     },
1991   };
1992   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1993     std::string headers(tests[i].headers);
1994     HeadersToRaw(&headers);
1995     scoped_refptr<net::HttpResponseHeaders> parsed(
1996         new net::HttpResponseHeaders(headers));
1997
1998     base::TimeDelta duration;
1999     EXPECT_EQ(tests[i].expected_result,
2000               parsed->GetChromeProxyInfo(&duration));
2001     EXPECT_EQ(tests[i].expected_retry_delay, duration.InSeconds());
2002   }
2003 }
2004 #endif  // defined(SPDY_PROXY_AUTH_ORIGIN)