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