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