• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
13 namespace {
14 
15 struct 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 
23 struct 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 
32 class HttpResponseHeadersTest : public testing::Test {
33 };
34 
35 // Transform "normal"-looking headers (\n-separated) to the appropriate
36 // input format for ParseRawHeaders (\0-separated).
HeadersToRaw(std::string * headers)37 void HeadersToRaw(std::string* headers) {
38   replace(headers->begin(), headers->end(), '\n', '\0');
39   if (!headers->empty())
40     *headers += '\0';
41 }
42 
TestCommon(const TestData & test)43 void 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.
TEST(HttpResponseHeadersTest,NormalizeHeadersWhitespace)70 TEST(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).
TEST(HttpResponseHeadersTest,NormalizeHeadersLeadingWhitespace)90 TEST(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 
TEST(HttpResponseHeadersTest,BlankHeaders)108 TEST(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 
TEST(HttpResponseHeadersTest,NormalizeHeadersVersion)130 TEST(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 
TEST(HttpResponseHeadersTest,PreserveHttp09)146 TEST(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 
TEST(HttpResponseHeadersTest,NormalizeHeadersMissingOK)161 TEST(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 
TEST(HttpResponseHeadersTest,NormalizeHeadersBadStatus)176 TEST(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 
TEST(HttpResponseHeadersTest,NormalizeHeadersEmpty)191 TEST(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 
TEST(HttpResponseHeadersTest,NormalizeHeadersStartWithColon)204 TEST(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 
TEST(HttpResponseHeadersTest,NormalizeHeadersStartWithColonAtEOL)223 TEST(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 
TEST(HttpResponseHeadersTest,NormalizeHeadersOfWhitepace)244 TEST(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 
TEST(HttpResponseHeadersTest,RepeatedSetCookie)257 TEST(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 
TEST(HttpResponseHeadersTest,GetNormalizedHeader)273 TEST(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 
TEST(HttpResponseHeadersTest,Persist)287 TEST(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 
TEST(HttpResponseHeadersTest,EnumerateHeader_Coalesced)463 TEST(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 
TEST(HttpResponseHeadersTest,EnumerateHeader_Challenge)485 TEST(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 
TEST(HttpResponseHeadersTest,EnumerateHeader_DateValued)505 TEST(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 
TEST(HttpResponseHeadersTest,GetMimeType)523 TEST(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 
TEST(HttpResponseHeadersTest,RequiresValidation)672 TEST(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 
TEST(HttpResponseHeadersTest,Update)818 TEST(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 
TEST(HttpResponseHeadersTest,EnumerateHeaderLines)889 TEST(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 
TEST(HttpResponseHeadersTest,IsRedirect)936 TEST(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 
TEST(HttpResponseHeadersTest,GetContentLength)1012 TEST(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 
TEST(HttpResponseHeaders,GetContentRange)1096 TEST(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 
TEST(HttpResponseHeadersTest,IsKeepAlive)1356 TEST(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 
TEST(HttpResponseHeadersTest,HasStrongValidators)1429 TEST(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 
TEST(HttpResponseHeadersTest,GetStatusText)1483 TEST(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 
TEST(HttpResponseHeadersTest,GetStatusTextMissing)1491 TEST(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 
TEST(HttpResponseHeadersTest,GetStatusTextMultiSpace)1500 TEST(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 
TEST(HttpResponseHeadersTest,GetStatusBadStatusLine)1508 TEST(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 
TEST(HttpResponseHeadersTest,AddHeader)1518 TEST(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 
TEST(HttpResponseHeadersTest,RemoveHeader)1563 TEST(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 
TEST(HttpResponseHeadersTest,ReplaceStatus)1608 TEST(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