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