• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "net/http/http_no_vary_search_data.h"
11 
12 #include <string>
13 #include <string_view>
14 
15 #include "base/containers/flat_map.h"
16 #include "base/containers/flat_set.h"
17 #include "base/memory/scoped_refptr.h"
18 #include "base/strings/string_util.h"
19 #include "base/test/gmock_expected_support.h"
20 #include "base/test/metrics/histogram_tester.h"
21 #include "base/types/expected.h"
22 #include "net/http/http_response_headers.h"
23 #include "net/http/http_util.h"
24 #include "testing/gmock/include/gmock/gmock.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "url/gurl.h"
27 
28 namespace net {
29 
30 namespace {
31 
32 using testing::IsEmpty;
33 using testing::UnorderedElementsAreArray;
34 
TEST(HttpNoVarySearchCreateTest,CreateFromNoVaryParamsNonEmptyVaryOnKeyOrder)35 TEST(HttpNoVarySearchCreateTest, CreateFromNoVaryParamsNonEmptyVaryOnKeyOrder) {
36   const auto no_vary_search =
37       HttpNoVarySearchData::CreateFromNoVaryParams({"a"}, true);
38   EXPECT_THAT(no_vary_search.no_vary_params(),
39               UnorderedElementsAreArray({"a"}));
40   EXPECT_THAT(no_vary_search.vary_params(), IsEmpty());
41   EXPECT_TRUE(no_vary_search.vary_on_key_order());
42   EXPECT_TRUE(no_vary_search.vary_by_default());
43 }
44 
TEST(HttpNoVarySearchCreateTest,CreateFromNoVaryParamsNonEmptyNoVaryOnKeyOrder)45 TEST(HttpNoVarySearchCreateTest,
46      CreateFromNoVaryParamsNonEmptyNoVaryOnKeyOrder) {
47   const auto no_vary_search =
48       HttpNoVarySearchData::CreateFromNoVaryParams({"a"}, false);
49   EXPECT_THAT(no_vary_search.no_vary_params(),
50               UnorderedElementsAreArray({"a"}));
51   EXPECT_THAT(no_vary_search.vary_params(), IsEmpty());
52   EXPECT_FALSE(no_vary_search.vary_on_key_order());
53   EXPECT_TRUE(no_vary_search.vary_by_default());
54 }
55 
TEST(HttpNoVarySearchCreateTest,CreateFromNoVaryParamsEmptyNoVaryOnKeyOrder)56 TEST(HttpNoVarySearchCreateTest, CreateFromNoVaryParamsEmptyNoVaryOnKeyOrder) {
57   const auto no_vary_search =
58       HttpNoVarySearchData::CreateFromNoVaryParams({}, false);
59   EXPECT_THAT(no_vary_search.no_vary_params(), IsEmpty());
60   EXPECT_THAT(no_vary_search.vary_params(), IsEmpty());
61   EXPECT_FALSE(no_vary_search.vary_on_key_order());
62   EXPECT_TRUE(no_vary_search.vary_by_default());
63 }
64 
TEST(HttpNoVarySearchCreateTest,CreateFromNoVaryParamsEmptyVaryOnKeyOrder)65 TEST(HttpNoVarySearchCreateTest, CreateFromNoVaryParamsEmptyVaryOnKeyOrder) {
66   const auto no_vary_search =
67       HttpNoVarySearchData::CreateFromNoVaryParams({}, true);
68   EXPECT_THAT(no_vary_search.no_vary_params(), IsEmpty());
69   EXPECT_THAT(no_vary_search.vary_params(), IsEmpty());
70   EXPECT_TRUE(no_vary_search.vary_on_key_order());
71   EXPECT_TRUE(no_vary_search.vary_by_default());
72 }
73 
TEST(HttpNoVarySearchCreateTest,CreateFromVaryParamsNonEmptyVaryOnKeyOrder)74 TEST(HttpNoVarySearchCreateTest, CreateFromVaryParamsNonEmptyVaryOnKeyOrder) {
75   const auto no_vary_search =
76       HttpNoVarySearchData::CreateFromVaryParams({"a"}, true);
77   EXPECT_THAT(no_vary_search.no_vary_params(), IsEmpty());
78   EXPECT_THAT(no_vary_search.vary_params(), UnorderedElementsAreArray({"a"}));
79   EXPECT_TRUE(no_vary_search.vary_on_key_order());
80   EXPECT_FALSE(no_vary_search.vary_by_default());
81 }
82 
TEST(HttpNoVarySearchCreateTest,CreateFromVaryParamsNonEmptyNoVaryOnKeyOrder)83 TEST(HttpNoVarySearchCreateTest, CreateFromVaryParamsNonEmptyNoVaryOnKeyOrder) {
84   const auto no_vary_search =
85       HttpNoVarySearchData::CreateFromVaryParams({"a"}, false);
86   EXPECT_THAT(no_vary_search.no_vary_params(), IsEmpty());
87   EXPECT_THAT(no_vary_search.vary_params(), UnorderedElementsAreArray({"a"}));
88   EXPECT_FALSE(no_vary_search.vary_on_key_order());
89   EXPECT_FALSE(no_vary_search.vary_by_default());
90 }
91 
TEST(HttpNoVarySearchCreateTest,CreateFromVaryParamsEmptyNoVaryOnKeyOrder)92 TEST(HttpNoVarySearchCreateTest, CreateFromVaryParamsEmptyNoVaryOnKeyOrder) {
93   const auto no_vary_search =
94       HttpNoVarySearchData::CreateFromVaryParams({}, false);
95   EXPECT_THAT(no_vary_search.no_vary_params(), IsEmpty());
96   EXPECT_THAT(no_vary_search.vary_params(), IsEmpty());
97   EXPECT_FALSE(no_vary_search.vary_on_key_order());
98   EXPECT_FALSE(no_vary_search.vary_by_default());
99 }
100 
TEST(HttpNoVarySearchCreateTest,CreateFromVaryParamsEmptyVaryOnKeyOrder)101 TEST(HttpNoVarySearchCreateTest, CreateFromVaryParamsEmptyVaryOnKeyOrder) {
102   const auto no_vary_search =
103       HttpNoVarySearchData::CreateFromVaryParams({}, true);
104   EXPECT_THAT(no_vary_search.no_vary_params(), IsEmpty());
105   EXPECT_THAT(no_vary_search.vary_params(), IsEmpty());
106   EXPECT_TRUE(no_vary_search.vary_on_key_order());
107   EXPECT_FALSE(no_vary_search.vary_by_default());
108 }
109 
110 struct TestData {
111   const char* raw_headers;
112   const base::flat_set<std::string> expected_no_vary_params;
113   const base::flat_set<std::string> expected_vary_params;
114   const bool expected_vary_on_key_order;
115   const bool expected_vary_by_default;
116 };
117 
118 class HttpNoVarySearchResponseHeadersTest
119     : public ::testing::Test,
120       public ::testing::WithParamInterface<TestData> {};
121 
TEST_P(HttpNoVarySearchResponseHeadersTest,ParsingSuccess)122 TEST_P(HttpNoVarySearchResponseHeadersTest, ParsingSuccess) {
123   const TestData test = GetParam();
124 
125   const std::string raw_headers =
126       HttpUtil::AssembleRawHeaders(test.raw_headers);
127 
128   const auto parsed = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
129   ASSERT_OK_AND_ASSIGN(const auto no_vary_search_data,
130                        HttpNoVarySearchData::ParseFromHeaders(*parsed));
131 
132   EXPECT_EQ(no_vary_search_data.vary_on_key_order(),
133             test.expected_vary_on_key_order);
134   EXPECT_EQ(no_vary_search_data.vary_by_default(),
135             test.expected_vary_by_default);
136 
137   EXPECT_EQ(no_vary_search_data.no_vary_params(), test.expected_no_vary_params);
138   EXPECT_EQ(no_vary_search_data.vary_params(), test.expected_vary_params);
139 }
140 
141 struct FailureData {
142   const char* raw_headers;
143   const HttpNoVarySearchData::ParseErrorEnum expected_error;
144 };
145 
146 class HttpNoVarySearchResponseHeadersParseFailureTest
147     : public ::testing::Test,
148       public ::testing::WithParamInterface<FailureData> {};
149 
TEST_P(HttpNoVarySearchResponseHeadersParseFailureTest,ParsingFailureOrDefaultValue)150 TEST_P(HttpNoVarySearchResponseHeadersParseFailureTest,
151        ParsingFailureOrDefaultValue) {
152   const std::string raw_headers =
153       HttpUtil::AssembleRawHeaders(GetParam().raw_headers);
154 
155   const auto parsed = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
156   const auto no_vary_search_data =
157       HttpNoVarySearchData::ParseFromHeaders(*parsed);
158 
159   EXPECT_THAT(no_vary_search_data,
160               base::test::ErrorIs(GetParam().expected_error))
161       << "Headers = " << GetParam().raw_headers;
162 }
163 
164 FailureData response_header_failed[] = {
165     {// No No-Vary-Search Header case
166      "HTTP/1.1 200 OK\r\n"
167      "Set-Cookie: a\r\n"
168      "Set-Cookie: b\r\n\r\n",
169      HttpNoVarySearchData::ParseErrorEnum::kOk},
170 
171     {// No-Vary-Search Header doesn't parse as a dictionary.
172      "HTTP/1.1 200 OK\r\n"
173      R"(No-Vary-Search: "a")"
174      "\r\n\r\n",
175      HttpNoVarySearchData::ParseErrorEnum::kNotDictionary},
176 
177     {// No-Vary-Search Header doesn't parse as a dictionary.
178      "HTTP/1.1 200 OK\r\n"
179      "No-Vary-Search: (a)\r\n\r\n",
180      HttpNoVarySearchData::ParseErrorEnum::kNotDictionary},
181 
182     {// When except is specified, params cannot be a list of strings.
183      "HTTP/1.1 200 OK\r\n"
184      R"(No-Vary-Search: params=("b"),except=("a"))"
185      "\r\n\r\n",
186      HttpNoVarySearchData::ParseErrorEnum::kExceptWithoutTrueParams},
187 
188     {// An unknown dictionary key should behave as if the key was not
189      // specified.
190      "HTTP/1.1 200 OK\r\n"
191      "No-Vary-Search: unknown-key\r\n\r\n",
192      HttpNoVarySearchData::ParseErrorEnum::kDefaultValue},
193 
194     {// params not a boolean or a list of strings.
195      "HTTP/1.1 200 OK\r\n"
196      R"(No-Vary-Search: params="a")"
197      "\r\n\r\n",
198      HttpNoVarySearchData::ParseErrorEnum::kParamsNotStringList},
199 
200     {// params not a boolean or a list of strings.
201      "HTTP/1.1 200 OK\r\n"
202      "No-Vary-Search: params=a\r\n\r\n",
203      HttpNoVarySearchData::ParseErrorEnum::kParamsNotStringList},
204 
205     {// params as an empty list of strings should behave as if the header was
206      // not specified.
207      "HTTP/1.1 200 OK\r\n"
208      "No-Vary-Search: params=()\r\n\r\n",
209      HttpNoVarySearchData::ParseErrorEnum::kDefaultValue},
210 
211     {// params not a boolean or a list of strings.
212      "HTTP/1.1 200 OK\r\n"
213      R"(No-Vary-Search: params=("a" b))"
214      "\r\n\r\n",
215      HttpNoVarySearchData::ParseErrorEnum::kParamsNotStringList},
216 
217     {// params defaulting to ?0 which is the same as no header.
218      "HTTP/1.1 200 OK\r\n"
219      R"(No-Vary-Search: params=("a"))"
220      "\r\n"
221      "No-Vary-Search: params=?0\r\n\r\n",
222      HttpNoVarySearchData::ParseErrorEnum::kDefaultValue},
223 
224     {// except without params.
225      "HTTP/1.1 200 OK\r\n"
226      "No-Vary-Search: except=()\r\n\r\n",
227      HttpNoVarySearchData::ParseErrorEnum::kExceptWithoutTrueParams},
228 
229     {// except without params.
230      "HTTP/1.1 200 OK\r\n"
231      "No-Vary-Search: except=()\r\n"
232      R"(No-Vary-Search: except=("a"))"
233      "\r\n\r\n",
234      HttpNoVarySearchData::ParseErrorEnum::kExceptWithoutTrueParams},
235 
236     {// except without params.
237      "HTTP/1.1 200 OK\r\n"
238      R"(No-Vary-Search: except=("a" "b"))"
239      "\r\n\r\n",
240      HttpNoVarySearchData::ParseErrorEnum::kExceptWithoutTrueParams},
241 
242     {// except with params set to a list of strings is incorrect.
243      "HTTP/1.1 200 OK\r\n"
244      R"(No-Vary-Search: params=("a"))"
245      "\r\n"
246      "No-Vary-Search: except=()\r\n\r\n",
247      HttpNoVarySearchData::ParseErrorEnum::kExceptWithoutTrueParams},
248 
249     {// except with params set to a list of strings is incorrect.
250      "HTTP/1.1 200 OK\r\n"
251      "No-Vary-Search: params=(),except=()\r\n\r\n",
252      HttpNoVarySearchData::ParseErrorEnum::kExceptWithoutTrueParams},
253 
254     {// except with params set to a list of strings is incorrect.
255      "HTTP/1.1 200 OK\r\n"
256      R"(No-Vary-Search: params,except=(),params=())"
257      "\r\n\r\n",
258      HttpNoVarySearchData::ParseErrorEnum::kExceptWithoutTrueParams},
259 
260     {// except with params set to a list of strings is incorrect.
261      "HTTP/1.1 200 OK\r\n"
262      R"(No-Vary-Search: except=("a" "b"))"
263      "\r\n"
264      R"(No-Vary-Search: params=("a"))"
265      "\r\n\r\n",
266      HttpNoVarySearchData::ParseErrorEnum::kExceptWithoutTrueParams},
267 
268     {// except with params set to a list of strings is incorrect.
269      "HTTP/1.1 200 OK\r\n"
270      R"(No-Vary-Search: params=("a"),except=("b"))"
271      "\r\n"
272      "No-Vary-Search: except=()\r\n\r\n",
273      HttpNoVarySearchData::ParseErrorEnum::kExceptWithoutTrueParams},
274 
275     {// except with params set to false is incorrect.
276      "HTTP/1.1 200 OK\r\n"
277      R"(No-Vary-Search: params=?0,except=("a"))"
278      "\r\n\r\n",
279      HttpNoVarySearchData::ParseErrorEnum::kExceptWithoutTrueParams},
280 
281     {// except with params set to a list of strings is incorrect.
282      "HTTP/1.1 200 OK\r\n"
283      R"(No-Vary-Search: params,except=("a" "b"))"
284      "\r\n"
285      R"(No-Vary-Search: params=("a"))"
286      "\r\n\r\n",
287      HttpNoVarySearchData::ParseErrorEnum::kExceptWithoutTrueParams},
288 
289     {// key-order not a boolean
290      "HTTP/1.1 200 OK\r\n"
291      R"(No-Vary-Search: key-order="a")"
292      "\r\n\r\n",
293      HttpNoVarySearchData::ParseErrorEnum::kNonBooleanKeyOrder},
294 
295     {// key-order not a boolean
296      "HTTP/1.1 200 OK\r\n"
297      "No-Vary-Search: key-order=a\r\n\r\n",
298      HttpNoVarySearchData::ParseErrorEnum::kNonBooleanKeyOrder},
299 
300     {// key-order not a boolean
301      "HTTP/1.1 200 OK\r\n"
302      "No-Vary-Search: key-order=()\r\n\r\n",
303      HttpNoVarySearchData::ParseErrorEnum::kNonBooleanKeyOrder},
304 
305     {// key-order not a boolean
306      "HTTP/1.1 200 OK\r\n"
307      "No-Vary-Search: key-order=(a)\r\n\r\n",
308      HttpNoVarySearchData::ParseErrorEnum::kNonBooleanKeyOrder},
309 
310     {// key-order not a boolean
311      "HTTP/1.1 200 OK\r\n"
312      R"(No-Vary-Search: key-order=("a"))"
313      "\r\n\r\n",
314      HttpNoVarySearchData::ParseErrorEnum::kNonBooleanKeyOrder},
315 
316     {// key-order not a boolean
317      "HTTP/1.1 200 OK\r\n"
318      "No-Vary-Search: key-order=(?1)\r\n\r\n",
319      HttpNoVarySearchData::ParseErrorEnum::kNonBooleanKeyOrder},
320 
321     {// key-order set to false should behave as if the
322      // header was not specified at all
323      "HTTP/1.1 200 OK\r\n"
324      "No-Vary-Search: key-order=?0\r\n\r\n",
325      HttpNoVarySearchData::ParseErrorEnum::kDefaultValue},
326 
327     {// params set to false should behave as if the
328      // header was not specified at all
329      "HTTP/1.1 200 OK\r\n"
330      "No-Vary-Search: params=?0\r\n\r\n",
331      HttpNoVarySearchData::ParseErrorEnum::kDefaultValue},
332 
333     {// params set to false should behave as if the
334      // header was not specified at all. except set to
335      // a list of tokens is incorrect.
336      "HTTP/1.1 200 OK\r\n"
337      "No-Vary-Search: params=?0\r\n"
338      "No-Vary-Search: except=(\"a\")\r\n\r\n",
339      HttpNoVarySearchData::ParseErrorEnum::kExceptWithoutTrueParams},
340 
341     {// except set to a list of tokens is incorrect.
342      "HTTP/1.1 200 OK\r\n"
343      "No-Vary-Search: params=?1\r\n"
344      "No-Vary-Search: except=(a)\r\n\r\n",
345      HttpNoVarySearchData::ParseErrorEnum::kExceptNotStringList},
346 
347     {// except set to true
348      "HTTP/1.1 200 OK\r\n"
349      "No-Vary-Search: params=?1\r\n"
350      "No-Vary-Search: except\r\n\r\n",
351      HttpNoVarySearchData::ParseErrorEnum::kExceptNotStringList},
352 };
353 
354 const TestData response_headers_tests[] = {
355     // params set to a list of strings with one element.
356     {
357         "HTTP/1.1 200 OK\r\n"
358         R"(No-Vary-Search: params=("a"))"
359         "\r\n\r\n",  // raw_headers
360         {"a"},       // expected_no_vary_params
361         {},          // expected_vary_params
362         true,        // expected_vary_on_key_order
363         true,        // expected_vary_by_default
364     },
365     // params set to a list of strings with one non-ASCII character.
366     {
367         "HTTP/1.1 200 OK\r\n"
368         R"(No-Vary-Search: params=("%C2%A2"))"
369         "\r\n\r\n",  // raw_headers
370         {"¢"},       // expected_no_vary_params
371         {},          // expected_vary_params
372         true,        // expected_vary_on_key_order
373         true,        // expected_vary_by_default
374     },
375     // params set to a list of strings with one ASCII and one non-ASCII
376     // character.
377     {
378         "HTTP/1.1 200 OK\r\n"
379         R"(No-Vary-Search: params=("c%C2%A2"))"
380         "\r\n\r\n",  // raw_headers
381         {"c¢"},      // expected_no_vary_params
382         {},          // expected_vary_params
383         true,        // expected_vary_on_key_order
384         true,        // expected_vary_by_default
385     },
386     // params set to a list of strings with one space and one non-ASCII
387     // character.
388     {
389         "HTTP/1.1 200 OK\r\n"
390         R"(No-Vary-Search: params=("+%C2%A2"))"
391         "\r\n\r\n",  // raw_headers
392         {" ¢"},      // expected_no_vary_params
393         {},          // expected_vary_params
394         true,        // expected_vary_on_key_order
395         true,        // expected_vary_by_default
396     },
397     // params set to true.
398     {
399         "HTTP/1.1 200 OK\r\n"
400         "No-Vary-Search: params\r\n\r\n",  // raw_headers
401         {},                                // expected_no_vary_params
402         {},                                // expected_vary_params
403         true,                              // expected_vary_on_key_order
404         false,                             // expected_vary_by_default
405     },
406     // params set to true.
407     {
408         "HTTP/1.1 200 OK\r\n"
409         "No-Vary-Search: params=?1\r\n\r\n",  // raw_headers
410         {},                                   // expected_no_vary_params
411         {},                                   // expected_vary_params
412         true,                                 // expected_vary_on_key_order
413         false,                                // expected_vary_by_default
414     },
415     // params overridden by a list of strings.
416     {
417         "HTTP/1.1 200 OK\r\n"
418         R"(No-Vary-Search: params=("a" b))"
419         "\r\n"
420         R"(No-Vary-Search: params=("c"))"
421         "\r\n\r\n",  // raw_headers
422         {"c"},       // expected_no_vary_params
423         {},          // expected_vary_params
424         true,        // expected_vary_on_key_order
425         true,        // expected_vary_by_default
426     },
427     // Vary on all with one excepted search param.
428     {
429         "HTTP/1.1 200 OK\r\n"
430         "No-Vary-Search: params\r\n"
431         "No-Vary-Search: except=()\r\n\r\n",  // raw_headers
432         {},                                   // expected_no_vary_params
433         {},                                   // expected_vary_params
434         true,                                 // expected_vary_on_key_order
435         false,                                // expected_vary_by_default
436     },
437     // Vary on all with one excepted search param.
438     {
439         "HTTP/1.1 200 OK\r\n"
440         "No-Vary-Search: params\r\n"
441         R"(No-Vary-Search: except=("a"))"
442         "\r\n\r\n",  // raw_headers
443         {},          // expected_no_vary_params
444         {"a"},       // expected_vary_params
445         true,        // expected_vary_on_key_order
446         false,       // expected_vary_by_default
447     },
448     // Vary on all with one excepted non-ASCII search param.
449     {
450         "HTTP/1.1 200 OK\r\n"
451         "No-Vary-Search: params\r\n"
452         R"(No-Vary-Search: except=("%C2%A2"))"
453         "\r\n\r\n",  // raw_headers
454         {},          // expected_no_vary_params
455         {"¢"},       // expected_vary_params
456         true,        // expected_vary_on_key_order
457         false,       // expected_vary_by_default
458     },
459     // Vary on all with one excepted search param that includes non-ASCII
460     // character.
461     {
462         "HTTP/1.1 200 OK\r\n"
463         "No-Vary-Search: params\r\n"
464         R"(No-Vary-Search: except=("c+%C2%A2"))"
465         "\r\n\r\n",  // raw_headers
466         {},          // expected_no_vary_params
467         {"c ¢"},     // expected_vary_params
468         true,        // expected_vary_on_key_order
469         false,       // expected_vary_by_default
470     },
471     // Vary on all with one excepted search param. Set params as
472     // part of the same header line.
473     {
474         "HTTP/1.1 200 OK\r\n"
475         R"(No-Vary-Search: params,except=("a"))"
476         "\r\n\r\n",  // raw_headers
477         {},          // expected_no_vary_params
478         {"a"},       // expected_vary_params
479         true,        // expected_vary_on_key_order
480         false,       // expected_vary_by_default
481     },
482     // Vary on all with one excepted search param. Override except
483     // on different header line.
484     {
485         "HTTP/1.1 200 OK\r\n"
486         R"(No-Vary-Search: params,except=("a" b))"
487         "\r\n"
488         R"(No-Vary-Search: except=("c"))"
489         "\r\n\r\n",  // raw_headers
490         {},          // expected_no_vary_params
491         {"c"},       // expected_vary_params
492         true,        // expected_vary_on_key_order
493         false,       // expected_vary_by_default
494     },
495     // Vary on all with more than one excepted search param.
496     {
497         "HTTP/1.1 200 OK\r\n"
498         "No-Vary-Search: params\r\n"
499         R"(No-Vary-Search: except=("a" "b"))"
500         "\r\n\r\n",  // raw_headers
501         {},          // expected_no_vary_params
502         {"a", "b"},  // expected_vary_params
503         true,        // expected_vary_on_key_order
504         false,       // expected_vary_by_default
505     },
506     // Vary on all with more than one excepted search param. params appears
507     // after except in header definition.
508     {
509         "HTTP/1.1 200 OK\r\n"
510         R"(No-Vary-Search: except=("a" "b"))"
511         "\r\n"
512         "No-Vary-Search: params\r\n\r\n",  // raw_headers
513         {},                                // expected_no_vary_params
514         {"a", "b"},                        // expected_vary_params
515         true,                              // expected_vary_on_key_order
516         false,                             // expected_vary_by_default
517     },
518     // Vary on all with more than one excepted search param. Set params as
519     // part of the same header line.
520     {
521         "HTTP/1.1 200 OK\r\n"
522         R"(No-Vary-Search: params,except=("a" "b"))"
523         "\r\n\r\n",  // raw_headers
524         {},          // expected_no_vary_params
525         {"a", "b"},  // expected_vary_params
526         true,        // expected_vary_on_key_order
527         false,       // expected_vary_by_default
528     },
529     // Don't vary on two search params.
530     {
531         "HTTP/1.1 200 OK\r\n"
532         R"(No-Vary-Search: params=("a" "b"))"
533         "\r\n\r\n",  // raw_headers
534         {"a", "b"},  // expected_no_vary_params
535         {},          // expected_vary_params
536         true,        // expected_vary_on_key_order
537         true,        // expected_vary_by_default
538     },
539     // Don't vary on search params order.
540     {
541         "HTTP/1.1 200 OK\r\n"
542         "No-Vary-Search: key-order\r\n\r\n",  // raw_headers
543         {},                                   // expected_no_vary_params
544         {},                                   // expected_vary_params
545         false,                                // expected_vary_on_key_order
546         true,                                 // expected_vary_by_default
547     },
548     // Don't vary on search params order.
549     {
550         "HTTP/1.1 200 OK\r\n"
551         "No-Vary-Search: key-order=?1\r\n\r\n",  // raw_headers
552         {},                                      // expected_no_vary_params
553         {},                                      // expected_vary_params
554         false,                                   // expected_vary_on_key_order
555         true,                                    // expected_vary_by_default
556     },
557     // Don't vary on search params order and on two specific search params.
558     {
559         "HTTP/1.1 200 OK\r\n"
560         R"(No-Vary-Search: params=("a" "b"))"
561         "\r\n"
562         "No-Vary-Search: key-order\r\n\r\n",  // raw_headers
563         {"a", "b"},                           // expected_no_vary_params
564         {},                                   // expected_vary_params
565         false,                                // expected_vary_on_key_order
566         true,                                 // expected_vary_by_default
567     },
568     // Don't vary on search params order and on two specific search params.
569     {
570         "HTTP/1.1 200 OK\r\n"
571         R"(No-Vary-Search: params=("a" "b"))"
572         "\r\n"
573         "No-Vary-Search: key-order=?1\r\n\r\n",  // raw_headers
574         {"a", "b"},                              // expected_no_vary_params
575         {},                                      // expected_vary_params
576         false,                                   // expected_vary_on_key_order
577         true,                                    // expected_vary_by_default
578     },
579     // Vary on search params order and do not vary on two specific search
580     // params.
581     {
582         "HTTP/1.1 200 OK\r\n"
583         R"(No-Vary-Search: params=("a" "b"))"
584         "\r\n"
585         "No-Vary-Search: key-order=?0\r\n\r\n",  // raw_headers
586         {"a", "b"},                              // expected_no_vary_params
587         {},                                      // expected_vary_params
588         true,                                    // expected_vary_on_key_order
589         true,                                    // expected_vary_by_default
590     },
591     // Vary on all search params except one, and do not vary on search params
592     // order.
593     {
594         "HTTP/1.1 200 OK\r\n"
595         "No-Vary-Search: params\r\n"
596         R"(No-Vary-Search: except=("a"))"
597         "\r\n"
598         "No-Vary-Search: key-order\r\n\r\n",  // raw_headers
599         {},                                   // expected_no_vary_params
600         {"a"},                                // expected_vary_params
601         false,                                // expected_vary_on_key_order
602         false,                                // expected_vary_by_default
603     },
604     // Vary on all search params except one, and do not vary on search params
605     // order.
606     {
607         "HTTP/1.1 200 OK\r\n"
608         "No-Vary-Search: params=?1\r\n"
609         R"(No-Vary-Search: except=("a"))"
610         "\r\n"
611         "No-Vary-Search: key-order\r\n\r\n",  // raw_headers
612         {},                                   // expected_no_vary_params
613         {"a"},                                // expected_vary_params
614         false,                                // expected_vary_on_key_order
615         false,                                // expected_vary_by_default
616     },
617     // Vary on all search params except one, and do not vary on search params
618     // order.
619     {
620         "HTTP/1.1 200 OK\r\n"
621         "No-Vary-Search: params\r\n"
622         R"(No-Vary-Search: except=("a"))"
623         "\r\n"
624         "No-Vary-Search: key-order=?1\r\n\r\n",  // raw_headers
625         {},                                      // expected_no_vary_params
626         {"a"},                                   // expected_vary_params
627         false,                                   // expected_vary_on_key_order
628         false,                                   // expected_vary_by_default
629     },
630     // Vary on all search params except one, and vary on search params order.
631     {
632         "HTTP/1.1 200 OK\r\n"
633         "No-Vary-Search: params=?1\r\n"
634         R"(No-Vary-Search: except=("a"))"
635         "\r\n"
636         "No-Vary-Search: key-order=?0\r\n\r\n",  // raw_headers
637         {},                                      // expected_no_vary_params
638         {"a"},                                   // expected_vary_params
639         true,                                    // expected_vary_on_key_order
640         false,                                   // expected_vary_by_default
641     },
642     // Vary on all search params except two, and do not vary on search params
643     // order.
644     {
645         "HTTP/1.1 200 OK\r\n"
646         "No-Vary-Search: params\r\n"
647         R"(No-Vary-Search: except=("a" "b"))"
648         "\r\n"
649         "No-Vary-Search: key-order\r\n\r\n",  // raw_headers
650         {},                                   // expected_no_vary_params
651         {"a", "b"},                           // expected_vary_params
652         false,                                // expected_vary_on_key_order
653         false,                                // expected_vary_by_default
654     },
655     // Do not vary on one search params. Override params on a different header
656     // line.
657     {
658         "HTTP/1.1 200 OK\r\n"
659         R"(No-Vary-Search: params=("a"))"
660         "\r\n"
661         R"(No-Vary-Search: params=("b"))"
662         "\r\n\r\n",  // raw_headers
663         {"b"},       // expected_no_vary_params
664         {},          // expected_vary_params
665         true,        // expected_vary_on_key_order
666         true,        // expected_vary_by_default
667     },
668     // Do not vary on any search params. Override params on a different header
669     // line.
670     {
671         "HTTP/1.1 200 OK\r\n"
672         R"(No-Vary-Search: params=("a"))"
673         "\r\n"
674         "No-Vary-Search: params\r\n\r\n",  // raw_headers
675         {},                                // expected_no_vary_params
676         {},                                // expected_vary_params
677         true,                              // expected_vary_on_key_order
678         false,                             // expected_vary_by_default
679     },
680     // Do not vary on any search params except one. Override except on a
681     // different header line.
682     {
683         "HTTP/1.1 200 OK\r\n"
684         "No-Vary-Search: params\r\n"
685         R"(No-Vary-Search: except=("a"))"
686         "\r\n"
687         R"(No-Vary-Search: except=("b"))"
688         "\r\n\r\n",  // raw_headers
689         {},          // expected_no_vary_params
690         {"b"},       // expected_vary_params
691         true,        // expected_vary_on_key_order
692         false,       // expected_vary_by_default
693     },
694     // Allow extension via parameters.
695     {
696         "HTTP/1.1 200 OK\r\n"
697         "No-Vary-Search: params;unknown\r\n\r\n",  // raw_headers
698         {},                                        // expected_no_vary_params
699         {},                                        // expected_vary_params
700         true,                                      // expected_vary_on_key_order
701         false,                                     // expected_vary_by_default
702     },
703     // Allow extension via parameters.
704     {
705         "HTTP/1.1 200 OK\r\n"
706         R"(No-Vary-Search: params=("a");unknown)"
707         "\r\n\r\n",  // raw_headers
708         {"a"},       // expected_no_vary_params
709         {},          // expected_vary_params
710         true,        // expected_vary_on_key_order
711         true,        // expected_vary_by_default
712     },
713     // Allow extension via parameters.
714     {
715         "HTTP/1.1 200 OK\r\n"
716         R"(No-Vary-Search: params;unknown,except=("a");unknown)"
717         "\r\n\r\n",  // raw_headers
718         {},          // expected_no_vary_params
719         {"a"},       // expected_vary_params
720         true,        // expected_vary_on_key_order
721         false,       // expected_vary_by_default
722     },
723     // Allow extension via parameters.
724     {
725         "HTTP/1.1 200 OK\r\n"
726         "No-Vary-Search: key-order;unknown\r\n\r\n",  // raw_headers
727         {},                                           // expected_no_vary_params
728         {},                                           // expected_vary_params
729         false,  // expected_vary_on_key_order
730         true,   // expected_vary_by_default
731     },
732     // Allow extension via parameters.
733     {
734         "HTTP/1.1 200 OK\r\n"
735         R"(No-Vary-Search: params=("a";unknown))"
736         "\r\n\r\n",  // raw_headers
737         {"a"},       // expected_no_vary_params
738         {},          // expected_vary_params
739         true,        // expected_vary_on_key_order
740         true,        // expected_vary_by_default
741     },
742     // Allow extension via parameters.
743     {
744         "HTTP/1.1 200 OK\r\n"
745         "No-Vary-Search: params\r\n"
746         R"(No-Vary-Search: except=("a";unknown))"
747         "\r\n\r\n",  // raw_headers
748         {},          // expected_no_vary_params
749         {"a"},       // expected_vary_params
750         true,        // expected_vary_on_key_order
751         false,       // expected_vary_by_default
752     },
753     // Vary on all search params except one. Override except on a different
754     // header line.
755     {
756         "HTTP/1.1 200 OK\r\n"
757         "No-Vary-Search: params,except=(a)\r\n"
758         R"(No-Vary-Search: except=("a"))"
759         "\r\n\r\n",  // raw_headers
760         {},          // expected_no_vary_params
761         {"a"},       // expected_vary_params
762         true,        // expected_vary_on_key_order
763         false,       // expected_vary_by_default
764     },
765     // Continue parsing if an unknown key is in the dictionary.
766     {
767         "HTTP/1.1 200 OK\r\n"
768         "No-Vary-Search: params,except=(a)\r\n"
769         "No-Vary-Search: unknown-key\r\n"
770         R"(No-Vary-Search: except=("a"))"
771         "\r\n\r\n",  // raw_headers
772         {},          // expected_no_vary_params
773         {"a"},       // expected_vary_params
774         true,        // expected_vary_on_key_order
775         false,       // expected_vary_by_default
776     }};
777 
778 INSTANTIATE_TEST_SUITE_P(HttpNoVarySearchResponseHeadersTest,
779                          HttpNoVarySearchResponseHeadersTest,
780                          testing::ValuesIn(response_headers_tests));
781 
782 INSTANTIATE_TEST_SUITE_P(HttpNoVarySearchResponseHeadersParseFailureTest,
783                          HttpNoVarySearchResponseHeadersParseFailureTest,
784                          testing::ValuesIn(response_header_failed));
785 
786 struct NoVarySearchCompareTestData {
787   const GURL request_url;
788   const GURL cached_url;
789   const std::string_view raw_headers;
790   const bool expected_match;
791 };
792 
TEST(HttpNoVarySearchCompare,CheckUrlEqualityWithSpecialCharacters)793 TEST(HttpNoVarySearchCompare, CheckUrlEqualityWithSpecialCharacters) {
794   // Use special characters in both `keys` and `values`.
795   const base::flat_map<std::string, std::string> percent_encoding = {
796       {"!", "%21"},    {"#", "%23"},    {"$", "%24"},    {"%", "%25"},
797       {"&", "%26"},    {"'", "%27"},    {"(", "%28"},    {")", "%29"},
798       {"*", R"(%2A)"}, {"+", R"(%2B)"}, {",", R"(%2C)"}, {"-", R"(%2D)"},
799       {".", R"(%2E)"}, {"/", R"(%2F)"}, {":", R"(%3A)"}, {";", "%3B"},
800       {"<", R"(%3C)"}, {"=", R"(%3D)"}, {">", R"(%3E)"}, {"?", R"(%3F)"},
801       {"@", "%40"},    {"[", "%5B"},    {"]", R"(%5D)"}, {"^", R"(%5E)"},
802       {"_", R"(%5F)"}, {"`", "%60"},    {"{", "%7B"},    {"|", R"(%7C)"},
803       {"}", R"(%7D)"}, {"~", R"(%7E)"}, {"", ""}};
804   const std::string_view raw_headers =
805       "HTTP/1.1 200 OK\r\n"
806       R"(No-Vary-Search: params=("c"))"
807       "\r\n\r\n";
808   const std::string headers = HttpUtil::AssembleRawHeaders(raw_headers);
809   const auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
810 
811   const auto no_vary_search_data =
812       HttpNoVarySearchData::ParseFromHeaders(*parsed).value();
813 
814   for (const auto& [key, value] : percent_encoding) {
815     std::string request_url_template =
816         R"(https://a.test/index.html?$key=$value)";
817     std::string cached_url_template =
818         R"(https://a.test/index.html?c=3&$key=$value)";
819 
820     base::ReplaceSubstringsAfterOffset(&request_url_template, 0, "$key", value);
821     base::ReplaceSubstringsAfterOffset(&request_url_template, 0, "$value",
822                                        value);
823     base::ReplaceSubstringsAfterOffset(&cached_url_template, 0, "$key", value);
824     base::ReplaceSubstringsAfterOffset(&cached_url_template, 0, "$value",
825                                        value);
826 
827     EXPECT_TRUE(no_vary_search_data.AreEquivalent(GURL(request_url_template),
828                                                   GURL(cached_url_template)));
829 
830     std::string header_template =
831         "HTTP/1.1 200 OK\r\n"
832         R"(No-Vary-Search: params, except=("$key"))"
833         "\r\n\r\n";
834     base::ReplaceSubstringsAfterOffset(&header_template, 0, "$key", key);
835 
836     const auto parsed_header = base::MakeRefCounted<HttpResponseHeaders>(
837         HttpUtil::AssembleRawHeaders(header_template));
838     const auto no_vary_search_data_special_char =
839         HttpNoVarySearchData::ParseFromHeaders(*parsed_header).value();
840 
841     EXPECT_TRUE(no_vary_search_data_special_char.AreEquivalent(
842         GURL(request_url_template), GURL(cached_url_template)));
843   }
844 }
845 
846 constexpr std::pair<std::string_view, std::string_view>
847     kPercentEncodedNonAsciiKeys[] = {
848         {"¢", R"(%C2%A2)"},
849         {"¢ ¢", R"(%C2%A2+%C2%A2)"},
850         {"é 気", R"(%C3%A9+%E6%B0%97)"},
851         {"é", R"(%C3%A9)"},
852         {"気", R"(%E6%B0%97)"},
853         {"ぁ", R"(%E3%81%81)"},
854         {"��", R"(%F0%90%A8%80)"},
855 };
856 
TEST(HttpNoVarySearchCompare,CheckUrlEqualityWithPercentEncodedNonASCIICharactersExcept)857 TEST(HttpNoVarySearchCompare,
858      CheckUrlEqualityWithPercentEncodedNonASCIICharactersExcept) {
859   for (const auto& [key, value] : kPercentEncodedNonAsciiKeys) {
860     std::string request_url_template = R"(https://a.test/index.html?$key=c)";
861     std::string cached_url_template = R"(https://a.test/index.html?c=3&$key=c)";
862     base::ReplaceSubstringsAfterOffset(&request_url_template, 0, "$key", key);
863     base::ReplaceSubstringsAfterOffset(&cached_url_template, 0, "$key", key);
864     std::string header_template =
865         "HTTP/1.1 200 OK\r\n"
866         R"(No-Vary-Search: params, except=("$key"))"
867         "\r\n\r\n";
868     base::ReplaceSubstringsAfterOffset(&header_template, 0, "$key", value);
869 
870     const auto parsed_header = base::MakeRefCounted<HttpResponseHeaders>(
871         HttpUtil::AssembleRawHeaders(header_template));
872     const auto no_vary_search_data_special_char =
873         HttpNoVarySearchData::ParseFromHeaders(*parsed_header).value();
874 
875     EXPECT_TRUE(no_vary_search_data_special_char.AreEquivalent(
876         GURL(request_url_template), GURL(cached_url_template)))
877         << "request_url = " << request_url_template
878         << " cached_url = " << cached_url_template
879         << " headers = " << header_template;
880   }
881 }
882 
TEST(HttpNoVarySearchCompare,CheckUrlEqualityWithPercentEncodedNonASCIICharacters)883 TEST(HttpNoVarySearchCompare,
884      CheckUrlEqualityWithPercentEncodedNonASCIICharacters) {
885   for (const auto& [key, value] : kPercentEncodedNonAsciiKeys) {
886     std::string request_url_template =
887         R"(https://a.test/index.html?a=2&$key=c)";
888     std::string cached_url_template = R"(https://a.test/index.html?$key=d&a=2)";
889     base::ReplaceSubstringsAfterOffset(&request_url_template, 0, "$key", key);
890     base::ReplaceSubstringsAfterOffset(&cached_url_template, 0, "$key", key);
891     std::string header_template =
892         "HTTP/1.1 200 OK\r\n"
893         R"(No-Vary-Search: params=("$key"))"
894         "\r\n\r\n";
895     base::ReplaceSubstringsAfterOffset(&header_template, 0, "$key", value);
896 
897     const auto parsed_header = base::MakeRefCounted<HttpResponseHeaders>(
898         HttpUtil::AssembleRawHeaders(header_template));
899     const auto no_vary_search_data_special_char =
900         HttpNoVarySearchData::ParseFromHeaders(*parsed_header).value();
901 
902     EXPECT_TRUE(no_vary_search_data_special_char.AreEquivalent(
903         GURL(request_url_template), GURL(cached_url_template)))
904         << "request_url = " << request_url_template
905         << " cached_url = " << cached_url_template
906         << " headers = " << header_template;
907   }
908 }
909 
910 class HttpNoVarySearchCompare
911     : public ::testing::Test,
912       public ::testing::WithParamInterface<NoVarySearchCompareTestData> {};
913 
TEST_P(HttpNoVarySearchCompare,CheckUrlEqualityByNoVarySearch)914 TEST_P(HttpNoVarySearchCompare, CheckUrlEqualityByNoVarySearch) {
915   const auto& test_data = GetParam();
916 
917   const std::string headers =
918       HttpUtil::AssembleRawHeaders(test_data.raw_headers);
919   const auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
920   const auto no_vary_search_data =
921       HttpNoVarySearchData::ParseFromHeaders(*parsed).value();
922 
923   EXPECT_EQ(no_vary_search_data.AreEquivalent(test_data.request_url,
924                                               test_data.cached_url),
925             test_data.expected_match)
926       << "request_url = " << test_data.request_url
927       << " cached_url = " << test_data.cached_url
928       << " headers = " << test_data.raw_headers
929       << " match = " << test_data.expected_match;
930 }
931 
932 const NoVarySearchCompareTestData no_vary_search_compare_tests[] = {
933     // Url's for same page with same username but different passwords.
934     {GURL("https://owner:correct@a.test/index.html?a=2&b=3"),
935      GURL("https://owner:incorrect@a.test/index.html?a=2&b=3"),
936      "HTTP/1.1 200 OK\r\n"
937      "No-Vary-Search: params\r\n\r\n",
938      false},
939     // Url's for same page with different username.
940     {GURL("https://anonymous@a.test/index.html?a=2&b=3"),
941      GURL("https://owner@a.test/index.html?a=2&b=3"),
942      "HTTP/1.1 200 OK\r\n"
943      "No-Vary-Search: params\r\n\r\n",
944      false},
945     // Url's for same origin with different path.
946     {GURL("https://a.test/index.html?a=2&b=3"),
947      GURL("https://a.test/home.html?a=2&b=3"),
948      "HTTP/1.1 200 OK\r\n"
949      "No-Vary-Search: params\r\n\r\n",
950      false},
951     // Url's for same page with different protocol.
952     {GURL("http://a.test/index.html?a=2&b=3"),
953      GURL("https://a.test/index.html?a=2&b=3"),
954      "HTTP/1.1 200 OK\r\n"
955      "No-Vary-Search: params\r\n\r\n",
956      false},
957     // Url's for different pages without the query and reference part
958     // are not equivalent.
959     {GURL("https://a.test/index.html?a=2&b=3"),
960      GURL("https://b.test/index.html?b=4&c=5"),
961      "HTTP/1.1 200 OK\r\n"
962      "No-Vary-Search: params\r\n\r\n",
963      false},
964     // Cached page requested again with different order of query parameters with
965     // the same values.
966     {GURL("https://a.test/index.html?a=2&b=3"),
967      GURL("https://a.test/index.html?b=3&a=2"),
968      "HTTP/1.1 200 OK\r\n"
969      "No-Vary-Search: key-order\r\n\r\n",
970      true},
971     // Cached page requested again with different order of query parameters but
972     // with different values.
973     {GURL("https://a.test/index.html?a=2&c=5&b=3"),
974      GURL("https://a.test/index.html?c=4&b=3&a=2"),
975      "HTTP/1.1 200 OK\r\n"
976      "No-Vary-Search: key-order\r\n\r\n",
977      false},
978     // Cached page requested again with values in different order for the query
979     // parameters with the same name. Key order is ignored.
980     {GURL("https://a.test/index.html?d=6&a=4&b=5&b=3&c=5&a=3"),
981      GURL("https://a.test/index.html?b=5&a=3&a=4&d=6&c=5&b=3"),
982      "HTTP/1.1 200 OK\r\n"
983      "No-Vary-Search: key-order"
984      "\r\n\r\n",
985      false},
986     // Cached page requested again with values in the same order for the query
987     // parameters with the same name. Key order is ignored.
988     {GURL("https://a.test/index.html?d=6&a=3&b=5&b=3&c=5&a=4"),
989      GURL("https://a.test/index.html?b=5&a=3&a=4&d=6&c=5&b=3"),
990      "HTTP/1.1 200 OK\r\n"
991      "No-Vary-Search: key-order"
992      "\r\n\r\n",
993      true},
994     // Cached page requested again with different order of query parameters but
995     // with one of the query parameters marked to be ignored.
996     {GURL("https://a.test/index.html?a=2&c=3&b=2"),
997      GURL("https://a.test/index.html?a=2&b=2&c=5"),
998      "HTTP/1.1 200 OK\r\n"
999      R"(No-Vary-Search: params=("c"))"
1000      "\r\n\r\n",
1001      true},
1002     // Cached page requested again without any query parameters, but
1003     // the cached URL's query parameter marked to be ignored.
1004     {GURL("https://a.test/index.html"), GURL("https://a.test/index.html?a=2"),
1005      "HTTP/1.1 200 OK\r\n"
1006      R"(No-Vary-Search: params=("a"))"
1007      "\r\n\r\n",
1008      true},
1009     // Cached page requested again with different values for the query
1010     // parameters that are marked to be ignored. Same value for the query
1011     // parameter that is marked as to vary.
1012     {GURL("https://a.test/index.html?a=1&b=2&c=3"),
1013      GURL("https://a.test/index.html?b=5&a=3&d=6&c=3"),
1014      "HTTP/1.1 200 OK\r\n"
1015      R"(No-Vary-Search: params, except=("c"))"
1016      "\r\n\r\n",
1017      true},
1018     // Cached page requested again with different values for the query
1019     // parameters that are marked to be ignored. Different value for the query
1020     // parameter that is marked as to vary.
1021     {GURL("https://a.test/index.html?a=1&b=2&c=5"),
1022      GURL("https://a.test/index.html?b=5&a=3&d=6&c=3"),
1023      "HTTP/1.1 200 OK\r\n"
1024      R"(No-Vary-Search: params, except=("c"))"
1025      "\r\n\r\n",
1026      false},
1027     // Cached page requested again with different values for the query
1028     // parameters that are marked to be ignored. Same values for the query
1029     // parameters that are marked as to vary.
1030     {GURL("https://a.test/index.html?d=6&a=1&b=2&c=5"),
1031      GURL("https://a.test/index.html?b=5&a=3&d=6&c=5"),
1032      "HTTP/1.1 200 OK\r\n"
1033      R"(No-Vary-Search: params, except=("c" "d"))"
1034      "\r\n\r\n",
1035      true},
1036     // Cached page requested again with different values for the query
1037     // parameters that are marked to be ignored. Same values for the query
1038     // parameters that are marked as to vary. Some query parameters to be
1039     // ignored appear multiple times in the query.
1040     {GURL("https://a.test/index.html?d=6&a=1&a=2&b=2&b=3&c=5"),
1041      GURL("https://a.test/index.html?b=5&a=3&a=4&d=6&c=5"),
1042      "HTTP/1.1 200 OK\r\n"
1043      R"(No-Vary-Search: params, except=("c" "d"))"
1044      "\r\n\r\n",
1045      true},
1046     // Cached page requested again with query parameters. All query parameters
1047     // are marked as to be ignored.
1048     {GURL("https://a.test/index.html?a=1&b=2&c=5"),
1049      GURL("https://a.test/index.html"),
1050      "HTTP/1.1 200 OK\r\n"
1051      "No-Vary-Search: params\r\n\r\n",
1052      true},
1053     // Cached page requested again with query parameters. All query parameters
1054     // are marked as to be ignored. Both request url and cached url have query
1055     // parameters.
1056     {GURL("https://a.test/index.html?a=1&b=2&c=5"),
1057      GURL("https://a.test/index.html?a=5&b=6&c=8&d=1"),
1058      "HTTP/1.1 200 OK\r\n"
1059      "No-Vary-Search: params\r\n\r\n",
1060      true},
1061     // Add test for when the keys are percent encoded.
1062     {GURL(R"(https://a.test/index.html?c+1=3&b+%202=2&a=1&%63%201=2&a=5)"),
1063      GURL(R"(https://a.test/index.html?a=1&b%20%202=2&%63%201=3&a=5&c+1=2)"),
1064      "HTTP/1.1 200 OK\r\n"
1065      "No-Vary-Search: key-order\r\n\r\n",
1066      true},
1067     // Add test for when there are different representations of a character
1068     {GURL(R"(https://a.test/index.html?%C3%A9=f&a=2&c=4&é=b)"),
1069      GURL(R"(https://a.test/index.html?a=2&é=f&c=4&d=7&é=b)"),
1070      "HTTP/1.1 200 OK\r\n"
1071      R"(No-Vary-Search: params=("d"), key-order)"
1072      "\r\n\r\n",
1073      true},
1074     // Add test for when there are triple code point
1075     {GURL(R"(https://a.test/index.html?%E3%81%81=f&a=2&c=4&%E3%81%81=b)"),
1076      GURL(R"(https://a.test/index.html?a=2&%E3%81%81=f&c=4&d=7&%E3%81%81=b)"),
1077      "HTTP/1.1 200 OK\r\n"
1078      R"(No-Vary-Search: params=("d"), key-order)"
1079      "\r\n\r\n",
1080      true},
1081     // Add test for when there are quadruple code point
1082     {GURL(
1083          R"(https://a.test/index.html?%F0%90%A8%80=%F0%90%A8%80&a=2&c=4&%F0%90%A8%80=b)"),
1084      GURL(
1085          R"(https://a.test/index.html?a=2&%F0%90%A8%80=%F0%90%A8%80&c=4&d=7&%F0%90%A8%80=b)"),
1086      "HTTP/1.1 200 OK\r\n"
1087      R"(No-Vary-Search: params=("d"), key-order)"
1088      "\r\n\r\n",
1089      true},
1090     // Add test for when there are params with empty values / keys.
1091     {GURL("https://a.test/index.html?a&b&c&a=2&d&=5&=1&=3"),
1092      GURL("https://a.test/index.html?c&d&b&a&=5&=1&a=2&=3"),
1093      "HTTP/1.1 200 OK\r\n"
1094      "No-Vary-Search: key-order\r\n\r\n",
1095      true},
1096     // Add test for when there are params with empty values / keys, an empty
1097     // key pair missing.
1098     {GURL("https://a.test/index.html?a&b&c&a=2&d&=5&=1&=3"),
1099      GURL("https://a.test/index.html?c&d&b&a&=5&a=2&=3"),
1100      "HTTP/1.1 200 OK\r\n"
1101      "No-Vary-Search: key-order\r\n\r\n",
1102      false},
1103     // Add test when there are params with keys / values that are wrongly
1104     // escaped.
1105     {GURL(R"(https://a.test/index.html?a=%3&%3=b)"),
1106      GURL(R"(https://a.test/index.html?a=%3&c=3&%3=b)"),
1107      "HTTP/1.1 200 OK\r\n"
1108      R"(No-Vary-Search: params=("c"))"
1109      "\r\n\r\n",
1110      true},
1111     // Add test when there is a param with key starting with a percent encoded
1112     // space (+).
1113     {GURL(R"(https://a.test/index.html?+a=3)"),
1114      GURL(R"(https://a.test/index.html?+a=2)"),
1115      "HTTP/1.1 200 OK\r\n"
1116      R"(No-Vary-Search: params=("+a"))"
1117      "\r\n\r\n",
1118      true},
1119     // Add test when there is a param with key starting with a percent encoded
1120     // space (+) and gets compared with same key without the leading space.
1121     {GURL(R"(https://a.test/index.html?+a=3)"),
1122      GURL(R"(https://a.test/index.html?a=2)"),
1123      "HTTP/1.1 200 OK\r\n"
1124      R"(No-Vary-Search: params=("+a"))"
1125      "\r\n\r\n",
1126      false},
1127     // Add test for when there are different representations of the character é
1128     // and we are ignoring that key.
1129     {GURL(R"(https://a.test/index.html?%C3%A9=g&a=2&c=4&é=b)"),
1130      GURL(R"(https://a.test/index.html?a=2&é=f&c=4&d=7&é=b)"),
1131      "HTTP/1.1 200 OK\r\n"
1132      R"(No-Vary-Search: params=("d" "%C3%A9"))"
1133      "\r\n\r\n",
1134      true},
1135     // Add test for when there are different representations of the character é
1136     // and we are not ignoring that key.
1137     {GURL(R"(https://a.test/index.html?%C3%A9=f&a=2&c=4&é=b)"),
1138      GURL(R"(https://a.test/index.html?a=2&é=f&c=4&d=7&é=b)"),
1139      "HTTP/1.1 200 OK\r\n"
1140      R"(No-Vary-Search: params, except=("%C3%A9"))"
1141      "\r\n\r\n",
1142      true},
1143     // Add test for when there are different representations of the character é
1144     // and we are not ignoring that key.
1145     {GURL(R"(https://a.test/index.html?%C3%A9=g&a=2&c=4&é=b)"),
1146      GURL(R"(https://a.test/index.html?a=2&é=f&c=4&d=7&é=b)"),
1147      "HTTP/1.1 200 OK\r\n"
1148      R"(No-Vary-Search: params, except=("%C3%A9"))"
1149      "\r\n\r\n",
1150      false},
1151 };
1152 
1153 INSTANTIATE_TEST_SUITE_P(HttpNoVarySearchCompare,
1154                          HttpNoVarySearchCompare,
1155                          testing::ValuesIn(no_vary_search_compare_tests));
1156 
TEST(HttpNoVarySearchResponseHeadersParseHistogramTest,NoUnrecognizedKeys)1157 TEST(HttpNoVarySearchResponseHeadersParseHistogramTest, NoUnrecognizedKeys) {
1158   base::HistogramTester histogram_tester;
1159   const std::string raw_headers = HttpUtil::AssembleRawHeaders(
1160       "HTTP/1.1 200 OK\r\nNo-Vary-Search: params\r\n\r\n");
1161   const auto parsed = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
1162   const auto no_vary_search_data =
1163       HttpNoVarySearchData::ParseFromHeaders(*parsed);
1164   EXPECT_THAT(no_vary_search_data, base::test::HasValue());
1165   histogram_tester.ExpectUniqueSample(
1166       "Net.HttpNoVarySearch.HasUnrecognizedKeys", false, 1);
1167 }
1168 
TEST(HttpNoVarySearchResponseHeadersParseHistogramTest,UnrecognizedKeys)1169 TEST(HttpNoVarySearchResponseHeadersParseHistogramTest, UnrecognizedKeys) {
1170   base::HistogramTester histogram_tester;
1171   const std::string raw_headers = HttpUtil::AssembleRawHeaders(
1172       "HTTP/1.1 200 OK\r\nNo-Vary-Search: params, rainbows\r\n\r\n");
1173   const auto parsed = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
1174   const auto no_vary_search_data =
1175       HttpNoVarySearchData::ParseFromHeaders(*parsed);
1176   EXPECT_THAT(no_vary_search_data, base::test::HasValue());
1177   histogram_tester.ExpectUniqueSample(
1178       "Net.HttpNoVarySearch.HasUnrecognizedKeys", true, 1);
1179 }
1180 
1181 }  // namespace
1182 
1183 }  // namespace net
1184