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