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