1// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
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/proxy_list.h"
6
7#include "net/base/net_errors.h"
8#include "net/base/net_log.h"
9#include "net/proxy/proxy_retry_info.h"
10#include "net/proxy/proxy_server.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13namespace net {
14
15namespace {
16
17// Test parsing from a PAC string.
18TEST(ProxyListTest, SetFromPacString) {
19  const struct {
20    const char* pac_input;
21    const char* pac_output;
22  } tests[] = {
23    // Valid inputs:
24    {  "PROXY foopy:10",
25       "PROXY foopy:10",
26    },
27    {  " DIRECT",  // leading space.
28       "DIRECT",
29    },
30    {  "PROXY foopy1 ; proxy foopy2;\t DIRECT",
31       "PROXY foopy1:80;PROXY foopy2:80;DIRECT",
32    },
33    {  "proxy foopy1 ; SOCKS foopy2",
34       "PROXY foopy1:80;SOCKS foopy2:1080",
35    },
36    // Try putting DIRECT first.
37    {  "DIRECT ; proxy foopy1 ; DIRECT ; SOCKS5 foopy2;DIRECT ",
38       "DIRECT;PROXY foopy1:80;DIRECT;SOCKS5 foopy2:1080;DIRECT",
39    },
40    // Try putting DIRECT consecutively.
41    {  "DIRECT ; proxy foopy1:80; DIRECT ; DIRECT",
42       "DIRECT;PROXY foopy1:80;DIRECT;DIRECT",
43    },
44
45    // Invalid inputs (parts which aren't understood get
46    // silently discarded):
47    //
48    // If the proxy list string parsed to empty, automatically fall-back to
49    // DIRECT.
50    {  "PROXY-foopy:10",
51       "DIRECT",
52    },
53    {  "PROXY",
54       "DIRECT",
55    },
56    {  "PROXY foopy1 ; JUNK ; JUNK ; SOCKS5 foopy2 ; ;",
57       "PROXY foopy1:80;SOCKS5 foopy2:1080",
58    },
59  };
60
61  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
62    ProxyList list;
63    list.SetFromPacString(tests[i].pac_input);
64    EXPECT_EQ(tests[i].pac_output, list.ToPacString());
65    EXPECT_FALSE(list.IsEmpty());
66  }
67}
68
69TEST(ProxyListTest, RemoveProxiesWithoutScheme) {
70  const struct {
71    const char* pac_input;
72    int filter;
73    const char* filtered_pac_output;
74  } tests[] = {
75    {  "PROXY foopy:10 ; SOCKS5 foopy2 ; SOCKS foopy11 ; PROXY foopy3 ; DIRECT",
76       // Remove anything that isn't HTTP or DIRECT.
77       ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_HTTP,
78       "PROXY foopy:10;PROXY foopy3:80;DIRECT",
79    },
80    {  "PROXY foopy:10 ; SOCKS5 foopy2",
81       // Remove anything that isn't HTTP or SOCKS5.
82       ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_SOCKS4,
83       "",
84    },
85  };
86
87  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
88    ProxyList list;
89    list.SetFromPacString(tests[i].pac_input);
90    list.RemoveProxiesWithoutScheme(tests[i].filter);
91    EXPECT_EQ(tests[i].filtered_pac_output, list.ToPacString());
92  }
93}
94
95TEST(ProxyListTest, DeprioritizeBadProxies) {
96  // Retry info that marks a proxy as being bad for a *very* long time (to avoid
97  // the test depending on the current time.)
98  ProxyRetryInfo proxy_retry_info;
99  proxy_retry_info.bad_until =
100      base::TimeTicks::Now() + base::TimeDelta::FromDays(1);
101
102  // Call DeprioritizeBadProxies with an empty map -- should have no effect.
103  {
104    ProxyList list;
105    list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
106
107    ProxyRetryInfoMap retry_info_map;
108    list.DeprioritizeBadProxies(retry_info_map);
109    EXPECT_EQ("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80",
110              list.ToPacString());
111  }
112
113  // Call DeprioritizeBadProxies with 2 of the three proxies marked as bad.
114  // These proxies should be retried last.
115  {
116    ProxyList list;
117    list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
118
119    ProxyRetryInfoMap retry_info_map;
120    retry_info_map["foopy1:80"] = proxy_retry_info;
121    retry_info_map["foopy3:80"] = proxy_retry_info;
122    retry_info_map["socks5://localhost:1080"] = proxy_retry_info;
123
124    list.DeprioritizeBadProxies(retry_info_map);
125
126    EXPECT_EQ("PROXY foopy2:80;PROXY foopy1:80;PROXY foopy3:80",
127              list.ToPacString());
128  }
129
130  // Call DeprioritizeBadProxies where ALL of the proxies are marked as bad.
131  // This should have no effect on the order.
132  {
133    ProxyList list;
134    list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
135
136    ProxyRetryInfoMap retry_info_map;
137    retry_info_map["foopy1:80"] = proxy_retry_info;
138    retry_info_map["foopy2:80"] = proxy_retry_info;
139    retry_info_map["foopy3:80"] = proxy_retry_info;
140
141    list.DeprioritizeBadProxies(retry_info_map);
142
143    EXPECT_EQ("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80",
144              list.ToPacString());
145  }
146
147  // Call DeprioritizeBadProxies with 2 of the three proxies marked as bad. Of
148  // the 2 bad proxies, one is to be reconsidered and should be retried last.
149  // The other is not to be reconsidered and should be removed from the list.
150  {
151    ProxyList list;
152    list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
153
154    ProxyRetryInfoMap retry_info_map;
155    // |proxy_retry_info.reconsider defaults to true.
156    retry_info_map["foopy1:80"] = proxy_retry_info;
157    proxy_retry_info.try_while_bad = false;
158    retry_info_map["foopy3:80"] = proxy_retry_info;
159    proxy_retry_info.try_while_bad = true;
160    retry_info_map["socks5://localhost:1080"] = proxy_retry_info;
161
162    list.DeprioritizeBadProxies(retry_info_map);
163
164    EXPECT_EQ("PROXY foopy2:80;PROXY foopy1:80",
165              list.ToPacString());
166  }
167}
168
169TEST(ProxyListTest, UpdateRetryInfoOnFallback) {
170  ProxyRetryInfo proxy_retry_info;
171  // Retrying should put the first proxy on the retry list.
172  {
173    ProxyList list;
174    ProxyRetryInfoMap retry_info_map;
175    BoundNetLog net_log;
176    ProxyServer proxy_server(
177        ProxyServer::FromURI("foopy1:80", ProxyServer::SCHEME_HTTP));
178    list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
179    list.UpdateRetryInfoOnFallback(&retry_info_map,
180                                   base::TimeDelta::FromSeconds(60),
181                                   true,
182                                   proxy_server,
183                                   ERR_PROXY_CONNECTION_FAILED,
184                                   net_log);
185    EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy1:80"));
186    EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED,
187              retry_info_map[proxy_server.ToURI()].net_error);
188    EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80"));
189    EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy3:80"));
190  }
191  // Retrying should put the first proxy on the retry list, even if there
192  // was no network error.
193  {
194    ProxyList list;
195    ProxyRetryInfoMap retry_info_map;
196    BoundNetLog net_log;
197    ProxyServer proxy_server(
198        ProxyServer::FromURI("foopy1:80", ProxyServer::SCHEME_HTTP));
199    list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
200    list.UpdateRetryInfoOnFallback(&retry_info_map,
201                                   base::TimeDelta::FromSeconds(60),
202                                   true,
203                                   proxy_server,
204                                   OK,
205                                   net_log);
206    EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy1:80"));
207    EXPECT_EQ(OK, retry_info_map[proxy_server.ToURI()].net_error);
208    EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80"));
209    EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy3:80"));
210  }
211  // Including another bad proxy should put both the first and the specified
212  // proxy on the retry list.
213  {
214    ProxyList list;
215    ProxyRetryInfoMap retry_info_map;
216    BoundNetLog net_log;
217    ProxyServer proxy_server = ProxyServer::FromURI("foopy3:80",
218                                                    ProxyServer::SCHEME_HTTP);
219    list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
220    list.UpdateRetryInfoOnFallback(&retry_info_map,
221                                   base::TimeDelta::FromSeconds(60),
222                                   true,
223                                   proxy_server,
224                                   ERR_NAME_RESOLUTION_FAILED,
225                                   net_log);
226    EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy1:80"));
227    EXPECT_EQ(ERR_NAME_RESOLUTION_FAILED,
228              retry_info_map[proxy_server.ToURI()].net_error);
229    EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80"));
230    EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy3:80"));
231  }
232  // If the first proxy is DIRECT, nothing is added to the retry list, even
233  // if another bad proxy is specified.
234  {
235    ProxyList list;
236    ProxyRetryInfoMap retry_info_map;
237    BoundNetLog net_log;
238    ProxyServer proxy_server = ProxyServer::FromURI("foopy2:80",
239                                                    ProxyServer::SCHEME_HTTP);
240    list.SetFromPacString("DIRECT;PROXY foopy2:80;PROXY foopy3:80");
241    list.UpdateRetryInfoOnFallback(&retry_info_map,
242                                   base::TimeDelta::FromSeconds(60),
243                                   true,
244                                   proxy_server,
245                                   OK,
246                                   net_log);
247    EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80"));
248    EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy3:80"));
249  }
250}
251
252}  // namesapce
253
254}  // namespace net
255