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", ¤t_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