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
15namespace {
16
17struct 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
25struct 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
34class HttpResponseHeadersTest : public testing::Test {
35};
36
37// Transform "normal"-looking headers (\n-separated) to the appropriate
38// input format for ParseRawHeaders (\0-separated).
39void HeadersToRaw(std::string* headers) {
40  std::replace(headers->begin(), headers->end(), '\n', '\0');
41  if (!headers->empty())
42    *headers += '\0';
43}
44
45void 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.
72TEST(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).
92TEST(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
110TEST(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
132TEST(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
148TEST(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
163TEST(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
178TEST(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
193TEST(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
206TEST(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
219TEST(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
238TEST(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
259TEST(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
272TEST(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
288TEST(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
302TEST(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
490TEST(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
512TEST(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
532TEST(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
550TEST(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
580TEST(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
729TEST(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
875TEST(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
980TEST(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
1027TEST(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
1103TEST(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
1187TEST(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
1447TEST(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
1520TEST(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
1580TEST(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
1588TEST(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
1597TEST(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
1605TEST(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
1615TEST(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
1660TEST(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
1705TEST(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
1799TEST(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
1854TEST(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