• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2006-2008 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/proxy_resolution/proxy_list.h"
6 
7 #include <vector>
8 
9 #include "base/logging.h"
10 #include "build/buildflag.h"
11 #include "net/base/net_errors.h"
12 #include "net/base/proxy_server.h"
13 #include "net/base/proxy_string_util.h"
14 #include "net/log/net_log_with_source.h"
15 #include "net/net_buildflags.h"
16 #include "net/proxy_resolution/proxy_retry_info.h"
17 #include "net/test/gtest_util.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 
21 using net::test::IsOk;
22 
23 namespace net {
24 
25 namespace {
26 
27 // Test parsing from a PAC string.
TEST(ProxyListTest,SetFromPacString)28 TEST(ProxyListTest, SetFromPacString) {
29   const struct {
30     const char* pac_input;
31     const char* debug_output;
32   } tests[] = {
33       // Valid inputs:
34       {
35           "PROXY foopy:10",
36           "PROXY foopy:10",
37       },
38       {
39           " DIRECT",  // leading space.
40           "DIRECT",
41       },
42       {
43           "PROXY foopy1 ; proxy foopy2;\t DIRECT",
44           "PROXY foopy1:80;PROXY foopy2:80;DIRECT",
45       },
46       {
47           "proxy foopy1 ; SOCKS foopy2",
48           "PROXY foopy1:80;SOCKS foopy2:1080",
49       },
50       // Try putting DIRECT first.
51       {
52           "DIRECT ; proxy foopy1 ; DIRECT ; SOCKS5 foopy2;DIRECT ",
53           "DIRECT;PROXY foopy1:80;DIRECT;SOCKS5 foopy2:1080;DIRECT",
54       },
55       // Try putting DIRECT consecutively.
56       {
57           "DIRECT ; proxy foopy1:80; DIRECT ; DIRECT",
58           "DIRECT;PROXY foopy1:80;DIRECT;DIRECT",
59       },
60 
61       // Invalid inputs (parts which aren't understood get
62       // silently discarded):
63       //
64       // If the proxy list string parsed to empty, automatically fall-back to
65       // DIRECT.
66       {
67           "PROXY-foopy:10",
68           "DIRECT",
69       },
70       {
71           "PROXY",
72           "DIRECT",
73       },
74       {
75           "PROXY foopy1 ; JUNK ; JUNK ; SOCKS5 foopy2 ; ;",
76           "PROXY foopy1:80;SOCKS5 foopy2:1080",
77       },
78   };
79 
80   for (const auto& test : tests) {
81     ProxyList list;
82     list.SetFromPacString(test.pac_input);
83     EXPECT_EQ(test.debug_output, list.ToDebugString());
84     EXPECT_FALSE(list.IsEmpty());
85   }
86 }
87 
TEST(ProxyListTest,RemoveProxiesWithoutScheme)88 TEST(ProxyListTest, RemoveProxiesWithoutScheme) {
89   const struct {
90     const char* pac_input;
91     int filter;
92     const char* filtered_debug_output;
93   } tests[] = {
94       {
95           "PROXY foopy:10 ; SOCKS5 foopy2 ; SOCKS foopy11 ; PROXY foopy3 ; "
96           "DIRECT",
97           // Remove anything that isn't HTTP.
98           ProxyServer::SCHEME_HTTP,
99           "PROXY foopy:10;PROXY foopy3:80;DIRECT",
100       },
101       {
102           "PROXY foopy:10 ; SOCKS5 foopy2",
103           // Remove anything that isn't HTTP or SOCKS5.
104           ProxyServer::SCHEME_SOCKS4,
105           "",
106       },
107   };
108 
109   for (const auto& test : tests) {
110     ProxyList list;
111     list.SetFromPacString(test.pac_input);
112     list.RemoveProxiesWithoutScheme(test.filter);
113     EXPECT_EQ(test.filtered_debug_output, list.ToDebugString());
114   }
115 }
116 
TEST(ProxyListTest,RemoveProxiesWithoutSchemeWithProxyChains)117 TEST(ProxyListTest, RemoveProxiesWithoutSchemeWithProxyChains) {
118   const auto kProxyChainFooHttps = ProxyChain::ForIpProtection({
119       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
120                                          "foo-a", 443),
121       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
122                                          "foo-b", 443),
123   });
124   const auto kProxyChainBarMixed = ProxyChain::ForIpProtection({
125       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_QUIC,
126                                          "bar-a", 443),
127       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
128                                          "bar-b", 443),
129   });
130   const ProxyChain kProxyChainGraultSocks = ProxyChain::FromSchemeHostAndPort(
131       ProxyServer::Scheme::SCHEME_SOCKS4, "grault", 443);
132 
133   ProxyList list;
134   list.AddProxyChain(kProxyChainFooHttps);
135   list.AddProxyChain(kProxyChainBarMixed);
136   list.AddProxyChain(kProxyChainGraultSocks);
137   list.AddProxyChain(ProxyChain::Direct());
138 
139   // Remove anything that isn't entirely HTTPS.
140   list.RemoveProxiesWithoutScheme(ProxyServer::SCHEME_HTTPS);
141 
142   std::vector<net::ProxyChain> expected = {
143       kProxyChainFooHttps,
144       ProxyChain::Direct(),
145   };
146   EXPECT_EQ(list.AllChains(), expected);
147 }
148 
TEST(ProxyListTest,DeprioritizeBadProxyChains)149 TEST(ProxyListTest, DeprioritizeBadProxyChains) {
150   // Retry info that marks a proxy as being bad for a *very* long time (to avoid
151   // the test depending on the current time.)
152   ProxyRetryInfo proxy_retry_info;
153   proxy_retry_info.bad_until = base::TimeTicks::Now() + base::Days(1);
154 
155   // Call DeprioritizeBadProxyChains with an empty map -- should have no effect.
156   {
157     ProxyList list;
158     list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
159 
160     ProxyRetryInfoMap retry_info_map;
161     list.DeprioritizeBadProxyChains(retry_info_map);
162     EXPECT_EQ("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80",
163               list.ToDebugString());
164   }
165 
166   // Call DeprioritizeBadProxyChains with 2 of the three chains marked as bad.
167   // These proxies should be retried last.
168   {
169     ProxyList list;
170     list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
171 
172     ProxyRetryInfoMap retry_info_map;
173     retry_info_map[ProxyUriToProxyChain(
174         "foopy1:80", ProxyServer::SCHEME_HTTP)] = proxy_retry_info;
175     retry_info_map[ProxyUriToProxyChain(
176         "foopy3:80", ProxyServer::SCHEME_HTTP)] = proxy_retry_info;
177     retry_info_map[ProxyUriToProxyChain("socks5://localhost:1080",
178                                         ProxyServer::SCHEME_HTTP)] =
179         proxy_retry_info;
180 
181     list.DeprioritizeBadProxyChains(retry_info_map);
182 
183     EXPECT_EQ("PROXY foopy2:80;PROXY foopy1:80;PROXY foopy3:80",
184               list.ToDebugString());
185   }
186 
187   // Call DeprioritizeBadProxyChains where ALL of the chains are marked as bad.
188   // This should have no effect on the order.
189   {
190     ProxyList list;
191     list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
192 
193     ProxyRetryInfoMap retry_info_map;
194     retry_info_map[ProxyUriToProxyChain(
195         "foopy1:80", ProxyServer::SCHEME_HTTP)] = proxy_retry_info;
196     retry_info_map[ProxyUriToProxyChain(
197         "foopy2:80", ProxyServer::SCHEME_HTTP)] = proxy_retry_info;
198     retry_info_map[ProxyUriToProxyChain(
199         "foopy3:80", ProxyServer::SCHEME_HTTP)] = proxy_retry_info;
200 
201     list.DeprioritizeBadProxyChains(retry_info_map);
202 
203     EXPECT_EQ("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80",
204               list.ToDebugString());
205   }
206 
207   // Call DeprioritizeBadProxyChains with 2 of the three chains marked as bad.
208   // Of the 2 bad proxies, one is to be reconsidered and should be retried last.
209   // The other is not to be reconsidered and should be removed from the list.
210   {
211     ProxyList list;
212     list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
213 
214     ProxyRetryInfoMap retry_info_map;
215     // |proxy_retry_info.reconsider defaults to true.
216     retry_info_map[ProxyUriToProxyChain(
217         "foopy1:80", ProxyServer::SCHEME_HTTP)] = proxy_retry_info;
218     proxy_retry_info.try_while_bad = false;
219     retry_info_map[ProxyUriToProxyChain(
220         "foopy3:80", ProxyServer::SCHEME_HTTP)] = proxy_retry_info;
221     proxy_retry_info.try_while_bad = true;
222     retry_info_map[ProxyUriToProxyChain("socks5://localhost:1080",
223                                         ProxyServer::SCHEME_SOCKS5)] =
224         proxy_retry_info;
225 
226     list.DeprioritizeBadProxyChains(retry_info_map);
227 
228     EXPECT_EQ("PROXY foopy2:80;PROXY foopy1:80", list.ToDebugString());
229   }
230 }
231 
TEST(ProxyListTest,UpdateRetryInfoOnFallback)232 TEST(ProxyListTest, UpdateRetryInfoOnFallback) {
233   // Retrying should put the first proxy on the retry list.
234   {
235     ProxyList list;
236     ProxyRetryInfoMap retry_info_map;
237     NetLogWithSource net_log;
238     ProxyChain proxy_chain(
239         ProxyUriToProxyChain("foopy1:80", ProxyServer::SCHEME_HTTP));
240     std::vector<ProxyChain> bad_proxies;
241     bad_proxies.push_back(proxy_chain);
242     list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
243     list.UpdateRetryInfoOnFallback(&retry_info_map, base::Seconds(60), true,
244                                    bad_proxies, ERR_PROXY_CONNECTION_FAILED,
245                                    net_log);
246     EXPECT_TRUE(retry_info_map.end() != retry_info_map.find(proxy_chain));
247     EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED,
248               retry_info_map[proxy_chain].net_error);
249     EXPECT_TRUE(retry_info_map.end() ==
250                 retry_info_map.find(ProxyUriToProxyChain(
251                     "foopy2:80", ProxyServer::SCHEME_HTTP)));
252     EXPECT_TRUE(retry_info_map.end() ==
253                 retry_info_map.find(ProxyUriToProxyChain(
254                     "foopy3:80", ProxyServer::SCHEME_HTTP)));
255   }
256   // Retrying should put the first proxy on the retry list, even if there
257   // was no network error.
258   {
259     ProxyList list;
260     ProxyRetryInfoMap retry_info_map;
261     NetLogWithSource net_log;
262     ProxyChain proxy_chain(
263         ProxyUriToProxyChain("foopy1:80", ProxyServer::SCHEME_HTTP));
264     std::vector<ProxyChain> bad_proxies;
265     bad_proxies.push_back(proxy_chain);
266     list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
267     list.UpdateRetryInfoOnFallback(&retry_info_map, base::Seconds(60), true,
268                                    bad_proxies, OK, net_log);
269     EXPECT_TRUE(retry_info_map.end() != retry_info_map.find(proxy_chain));
270     EXPECT_THAT(retry_info_map[proxy_chain].net_error, IsOk());
271     EXPECT_TRUE(retry_info_map.end() ==
272                 retry_info_map.find(ProxyUriToProxyChain(
273                     "foopy2:80", ProxyServer::SCHEME_HTTP)));
274     EXPECT_TRUE(retry_info_map.end() ==
275                 retry_info_map.find(ProxyUriToProxyChain(
276                     "foopy3:80", ProxyServer::SCHEME_HTTP)));
277   }
278   // Including another bad proxy should put both the first and the specified
279   // proxy on the retry list.
280   {
281     ProxyList list;
282     ProxyRetryInfoMap retry_info_map;
283     NetLogWithSource net_log;
284     ProxyChain proxy_chain(
285         ProxyUriToProxyChain("foopy3:80", ProxyServer::SCHEME_HTTP));
286     std::vector<ProxyChain> bad_proxies;
287     bad_proxies.push_back(proxy_chain);
288     list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
289     list.UpdateRetryInfoOnFallback(&retry_info_map, base::Seconds(60), true,
290                                    bad_proxies, ERR_NAME_RESOLUTION_FAILED,
291                                    net_log);
292     EXPECT_TRUE(retry_info_map.end() !=
293                 retry_info_map.find(ProxyUriToProxyChain(
294                     "foopy1:80", ProxyServer::SCHEME_HTTP)));
295     EXPECT_EQ(ERR_NAME_RESOLUTION_FAILED,
296               retry_info_map[proxy_chain].net_error);
297     EXPECT_TRUE(retry_info_map.end() ==
298                 retry_info_map.find(ProxyUriToProxyChain(
299                     "foopy2:80", ProxyServer::SCHEME_HTTP)));
300     EXPECT_TRUE(retry_info_map.end() != retry_info_map.find(proxy_chain));
301   }
302   // If the first proxy is DIRECT, nothing is added to the retry list, even
303   // if another bad proxy is specified.
304   {
305     ProxyList list;
306     ProxyRetryInfoMap retry_info_map;
307     NetLogWithSource net_log;
308     ProxyChain proxy_chain(
309         ProxyUriToProxyChain("foopy2:80", ProxyServer::SCHEME_HTTP));
310     std::vector<ProxyChain> bad_proxies;
311     bad_proxies.push_back(proxy_chain);
312     list.SetFromPacString("DIRECT;PROXY foopy2:80;PROXY foopy3:80");
313     list.UpdateRetryInfoOnFallback(&retry_info_map, base::Seconds(60), true,
314                                    bad_proxies, OK, net_log);
315     EXPECT_TRUE(retry_info_map.end() == retry_info_map.find(proxy_chain));
316     EXPECT_TRUE(retry_info_map.end() ==
317                 retry_info_map.find(ProxyUriToProxyChain(
318                     "foopy3:80", ProxyServer::SCHEME_HTTP)));
319   }
320   // If the bad proxy is already on the retry list, and the old retry info would
321   // cause the proxy to be retried later than the newly specified retry info,
322   // then the old retry info should be kept.
323   {
324     ProxyList list;
325     ProxyRetryInfoMap retry_info_map;
326     NetLogWithSource net_log;
327     list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
328 
329     // First, mark the proxy as bad for 60 seconds.
330     list.UpdateRetryInfoOnFallback(&retry_info_map, base::Seconds(60), true,
331                                    std::vector<ProxyChain>(),
332                                    ERR_PROXY_CONNECTION_FAILED, net_log);
333     // Next, mark the same proxy as bad for 1 second. This call should have no
334     // effect, since this would cause the bad proxy to be retried sooner than
335     // the existing retry info.
336     list.UpdateRetryInfoOnFallback(&retry_info_map, base::Seconds(1), false,
337                                    std::vector<ProxyChain>(), OK, net_log);
338     ProxyChain proxy_chain(
339         ProxyUriToProxyChain("foopy1:80", ProxyServer::SCHEME_HTTP));
340     EXPECT_TRUE(retry_info_map.end() != retry_info_map.find(proxy_chain));
341     EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED,
342               retry_info_map[proxy_chain].net_error);
343     EXPECT_TRUE(retry_info_map[proxy_chain].try_while_bad);
344     EXPECT_EQ(base::Seconds(60), retry_info_map[proxy_chain].current_delay);
345     EXPECT_GT(retry_info_map[proxy_chain].bad_until,
346               base::TimeTicks::Now() + base::Seconds(30));
347     EXPECT_TRUE(retry_info_map.end() ==
348                 retry_info_map.find(ProxyUriToProxyChain(
349                     "foopy2:80", ProxyServer::SCHEME_HTTP)));
350     EXPECT_TRUE(retry_info_map.end() ==
351                 retry_info_map.find(ProxyUriToProxyChain(
352                     "foopy3:80", ProxyServer::SCHEME_HTTP)));
353   }
354   // If the bad proxy is already on the retry list, and the newly specified
355   // retry info would cause the proxy to be retried later than the old retry
356   // info, then the old retry info should be replaced with the new retry info.
357   {
358     ProxyList list;
359     ProxyRetryInfoMap retry_info_map;
360     NetLogWithSource net_log;
361     list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
362 
363     // First, mark the proxy as bad for 1 second.
364     list.UpdateRetryInfoOnFallback(&retry_info_map, base::Seconds(1), false,
365                                    std::vector<ProxyChain>(), OK, net_log);
366     // Next, mark the same proxy as bad for 60 seconds. This call should replace
367     // the existing retry info with the new 60 second retry info.
368     list.UpdateRetryInfoOnFallback(&retry_info_map, base::Seconds(60), true,
369                                    std::vector<ProxyChain>(),
370                                    ERR_PROXY_CONNECTION_FAILED, net_log);
371     ProxyChain proxy_chain(
372         ProxyUriToProxyChain("foopy1:80", ProxyServer::SCHEME_HTTP));
373     EXPECT_TRUE(retry_info_map.end() != retry_info_map.find(proxy_chain));
374     EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED,
375               retry_info_map[proxy_chain].net_error);
376     EXPECT_TRUE(retry_info_map[proxy_chain].try_while_bad);
377     EXPECT_EQ(base::Seconds(60), retry_info_map[proxy_chain].current_delay);
378     EXPECT_GT(retry_info_map[proxy_chain].bad_until,
379               base::TimeTicks::Now() + base::Seconds(30));
380     EXPECT_TRUE(retry_info_map.end() ==
381                 retry_info_map.find(ProxyUriToProxyChain(
382                     "foopy2:80", ProxyServer::SCHEME_HTTP)));
383     EXPECT_TRUE(retry_info_map.end() ==
384                 retry_info_map.find(ProxyUriToProxyChain(
385                     "foopy3:80", ProxyServer::SCHEME_HTTP)));
386   }
387 }
388 
TEST(ProxyListTest,ToPacString)389 TEST(ProxyListTest, ToPacString) {
390   ProxyList list;
391   list.AddProxyChain(ProxyChain::FromSchemeHostAndPort(
392       ProxyServer::Scheme::SCHEME_HTTPS, "foo", 443));
393   EXPECT_EQ(list.ToPacString(), "HTTPS foo:443");
394   // ToPacString should fail for proxy chains.
395   list.AddProxyChain(ProxyChain::ForIpProtection({
396       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
397                                          "foo-a", 443),
398       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
399                                          "foo-b", 443),
400   }));
401   EXPECT_DEATH_IF_SUPPORTED(list.ToPacString(), "");
402 }
403 
TEST(ProxyListTest,ToDebugString)404 TEST(ProxyListTest, ToDebugString) {
405   ProxyList list;
406   list.AddProxyChain(ProxyChain::FromSchemeHostAndPort(
407       ProxyServer::Scheme::SCHEME_HTTPS, "foo", 443));
408   list.AddProxyChain(ProxyChain::ForIpProtection({
409       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
410                                          "foo-a", 443),
411       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
412                                          "foo-b", 443),
413   }));
414 
415   EXPECT_EQ(
416       list.ToDebugString(),
417       "HTTPS foo:443;[https://foo-a:443, https://foo-b:443] (IP Protection)");
418 }
419 
TEST(ProxyListTest,ToValue)420 TEST(ProxyListTest, ToValue) {
421   ProxyList list;
422   list.AddProxyChain(ProxyChain::FromSchemeHostAndPort(
423       ProxyServer::Scheme::SCHEME_HTTPS, "foo", 443));
424   list.AddProxyChain(ProxyChain::ForIpProtection({
425       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
426                                          "foo-a", 443),
427       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
428                                          "foo-b", 443),
429   }));
430 
431   base::Value expected(base::Value::Type::LIST);
432   base::Value::List& exp_list = expected.GetList();
433   exp_list.Append("[https://foo:443]");
434   exp_list.Append("[https://foo-a:443, https://foo-b:443] (IP Protection)");
435 
436   EXPECT_EQ(list.ToValue(), expected);
437 }
438 
439 #if BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
440 // The following tests are for non-release builds where multi-proxy chains are
441 // permitted outside of Ip Protection.
442 
TEST(ProxyListTest,NonIpProtectionMultiProxyChainRemoveProxiesWithoutSchemeWithProxyChains)443 TEST(ProxyListTest,
444      NonIpProtectionMultiProxyChainRemoveProxiesWithoutSchemeWithProxyChains) {
445   const ProxyChain kProxyChainFooHttps({
446       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
447                                          "foo-a", 443),
448       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
449                                          "foo-b", 443),
450   });
451   const ProxyChain kProxyChainBarMixed({
452       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_QUIC,
453                                          "bar-a", 443),
454       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
455                                          "bar-b", 443),
456   });
457   const ProxyChain kProxyChainGraultSocks = ProxyChain::FromSchemeHostAndPort(
458       ProxyServer::Scheme::SCHEME_SOCKS4, "grault", 443);
459 
460   ProxyList list;
461   list.AddProxyChain(kProxyChainFooHttps);
462   list.AddProxyChain(kProxyChainBarMixed);
463   list.AddProxyChain(kProxyChainGraultSocks);
464   list.AddProxyChain(ProxyChain::Direct());
465 
466   // Remove anything that isn't entirely HTTPS.
467   list.RemoveProxiesWithoutScheme(ProxyServer::SCHEME_HTTPS);
468 
469   std::vector<net::ProxyChain> expected = {
470       kProxyChainFooHttps,
471       ProxyChain::Direct(),
472   };
473   EXPECT_EQ(list.AllChains(), expected);
474 }
475 
476 // `ToPacString` should only be called if the list contains no multi-proxy
477 // chains, as those cannot be represented in PAC syntax. This is not an issue in
478 // release builds because a `ProxyChain` constructed with multiple proxy servers
479 // would automatically default to an empty, invalid
480 // `ProxyChain` (unless for Ip Protection); however, in non-release builds,
481 // multi-proxy chains are permitted which means they must be CHECKED when this
482 // function is called.
TEST(ProxyListTest,NonIpProtectionMultiProxyChainToPacString)483 TEST(ProxyListTest, NonIpProtectionMultiProxyChainToPacString) {
484   ProxyList list;
485   list.AddProxyChain(ProxyChain::FromSchemeHostAndPort(
486       ProxyServer::Scheme::SCHEME_HTTPS, "foo", 443));
487   EXPECT_EQ(list.ToPacString(), "HTTPS foo:443");
488   // ToPacString should fail for proxy chains.
489   list.AddProxyChain(ProxyChain({
490       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
491                                          "foo-a", 443),
492       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
493                                          "foo-b", 443),
494   }));
495   EXPECT_DEATH_IF_SUPPORTED(list.ToPacString(), "");
496 }
497 
TEST(ProxyListTest,NonIpProtectionMultiProxyChainToDebugString)498 TEST(ProxyListTest, NonIpProtectionMultiProxyChainToDebugString) {
499   ProxyList list;
500   list.AddProxyChain(ProxyChain::FromSchemeHostAndPort(
501       ProxyServer::Scheme::SCHEME_HTTPS, "foo", 443));
502   list.AddProxyChain(ProxyChain({
503       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
504                                          "foo-a", 443),
505       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
506                                          "foo-b", 443),
507   }));
508 
509   EXPECT_EQ(list.ToDebugString(),
510             "HTTPS foo:443;[https://foo-a:443, https://foo-b:443]");
511 }
512 
TEST(ProxyListTest,NonIpProtectionMultiProxyChainToValue)513 TEST(ProxyListTest, NonIpProtectionMultiProxyChainToValue) {
514   ProxyList list;
515   list.AddProxyChain(ProxyChain::FromSchemeHostAndPort(
516       ProxyServer::Scheme::SCHEME_HTTPS, "foo", 443));
517   list.AddProxyChain(ProxyChain({
518       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
519                                          "foo-a", 443),
520       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
521                                          "foo-b", 443),
522   }));
523 
524   base::Value expected(base::Value::Type::LIST);
525   base::Value::List& exp_list = expected.GetList();
526   exp_list.Append("[https://foo:443]");
527   exp_list.Append("[https://foo-a:443, https://foo-b:443]");
528 
529   EXPECT_EQ(list.ToValue(), expected);
530 }
531 #endif  // BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
532 
533 }  // anonymous namespace
534 
535 }  // namespace net
536