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