1// Copyright (c) 2012 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_config.h"
6#include "net/proxy/proxy_config_service_common_unittest.h"
7#include "net/proxy/proxy_info.h"
8#include "testing/gtest/include/gtest/gtest.h"
9
10namespace net {
11namespace {
12
13void ExpectProxyServerEquals(const char* expectation,
14                             const ProxyList& proxy_servers) {
15  if (expectation == NULL) {
16    EXPECT_TRUE(proxy_servers.IsEmpty());
17  } else {
18    EXPECT_EQ(expectation, proxy_servers.ToPacString());
19  }
20}
21
22TEST(ProxyConfigTest, Equals) {
23  // Test |ProxyConfig::auto_detect|.
24
25  ProxyConfig config1;
26  config1.set_auto_detect(true);
27
28  ProxyConfig config2;
29  config2.set_auto_detect(false);
30
31  EXPECT_FALSE(config1.Equals(config2));
32  EXPECT_FALSE(config2.Equals(config1));
33
34  config2.set_auto_detect(true);
35
36  EXPECT_TRUE(config1.Equals(config2));
37  EXPECT_TRUE(config2.Equals(config1));
38
39  // Test |ProxyConfig::pac_url|.
40
41  config2.set_pac_url(GURL("http://wpad/wpad.dat"));
42
43  EXPECT_FALSE(config1.Equals(config2));
44  EXPECT_FALSE(config2.Equals(config1));
45
46  config1.set_pac_url(GURL("http://wpad/wpad.dat"));
47
48  EXPECT_TRUE(config1.Equals(config2));
49  EXPECT_TRUE(config2.Equals(config1));
50
51  // Test |ProxyConfig::proxy_rules|.
52
53  config2.proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
54  config2.proxy_rules().single_proxies.SetSingleProxyServer(
55      ProxyServer::FromURI("myproxy:80", ProxyServer::SCHEME_HTTP));
56
57  EXPECT_FALSE(config1.Equals(config2));
58  EXPECT_FALSE(config2.Equals(config1));
59
60  config1.proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
61  config1.proxy_rules().single_proxies.SetSingleProxyServer(
62      ProxyServer::FromURI("myproxy:100", ProxyServer::SCHEME_HTTP));
63
64  EXPECT_FALSE(config1.Equals(config2));
65  EXPECT_FALSE(config2.Equals(config1));
66
67  config1.proxy_rules().single_proxies.SetSingleProxyServer(
68      ProxyServer::FromURI("myproxy", ProxyServer::SCHEME_HTTP));
69
70  EXPECT_TRUE(config1.Equals(config2));
71  EXPECT_TRUE(config2.Equals(config1));
72
73  // Test |ProxyConfig::bypass_rules|.
74
75  config2.proxy_rules().bypass_rules.AddRuleFromString("*.google.com");
76
77  EXPECT_FALSE(config1.Equals(config2));
78  EXPECT_FALSE(config2.Equals(config1));
79
80  config1.proxy_rules().bypass_rules.AddRuleFromString("*.google.com");
81
82  EXPECT_TRUE(config1.Equals(config2));
83  EXPECT_TRUE(config2.Equals(config1));
84
85  // Test |ProxyConfig::proxy_rules.reverse_bypass|.
86
87  config2.proxy_rules().reverse_bypass = true;
88
89  EXPECT_FALSE(config1.Equals(config2));
90  EXPECT_FALSE(config2.Equals(config1));
91
92  config1.proxy_rules().reverse_bypass = true;
93
94  EXPECT_TRUE(config1.Equals(config2));
95  EXPECT_TRUE(config2.Equals(config1));
96}
97
98TEST(ProxyConfigTest, ParseProxyRules) {
99  const struct {
100    const char* proxy_rules;
101
102    ProxyConfig::ProxyRules::Type type;
103    // These will be PAC-stle strings, eg 'PROXY foo.com'
104    const char* single_proxy;
105    const char* proxy_for_http;
106    const char* proxy_for_https;
107    const char* proxy_for_ftp;
108    const char* fallback_proxy;
109  } tests[] = {
110    // One HTTP proxy for all schemes.
111    {
112      "myproxy:80",
113
114      ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY,
115      "PROXY myproxy:80",
116      NULL,
117      NULL,
118      NULL,
119      NULL,
120    },
121
122    // Multiple HTTP proxies for all schemes.
123    {
124      "myproxy:80,https://myotherproxy",
125
126      ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY,
127      "PROXY myproxy:80;HTTPS myotherproxy:443",
128      NULL,
129      NULL,
130      NULL,
131      NULL,
132    },
133
134    // Only specify a proxy server for "http://" urls.
135    {
136      "http=myproxy:80",
137
138      ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
139      NULL,
140      "PROXY myproxy:80",
141      NULL,
142      NULL,
143      NULL,
144    },
145
146    // Specify an HTTP proxy for "ftp://" and a SOCKS proxy for "https://" urls.
147    {
148      "ftp=ftp-proxy ; https=socks4://foopy",
149
150      ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
151      NULL,
152      NULL,
153      "SOCKS foopy:1080",
154      "PROXY ftp-proxy:80",
155      NULL,
156    },
157
158    // Give a scheme-specific proxy as well as a non-scheme specific.
159    // The first entry "foopy" takes precedance marking this list as
160    // TYPE_SINGLE_PROXY.
161    {
162      "foopy ; ftp=ftp-proxy",
163
164      ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY,
165      "PROXY foopy:80",
166      NULL,
167      NULL,
168      NULL,
169      NULL,
170    },
171
172    // Give a scheme-specific proxy as well as a non-scheme specific.
173    // The first entry "ftp=ftp-proxy" takes precedance marking this list as
174    // TYPE_PROXY_PER_SCHEME.
175    {
176      "ftp=ftp-proxy ; foopy",
177
178      ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
179      NULL,
180      NULL,
181      NULL,
182      "PROXY ftp-proxy:80",
183      NULL,
184    },
185
186    // Include a list of entries for a single scheme.
187    {
188      "ftp=ftp1,ftp2,ftp3",
189
190      ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
191      NULL,
192      NULL,
193      NULL,
194      "PROXY ftp1:80;PROXY ftp2:80;PROXY ftp3:80",
195      NULL,
196    },
197
198    // Include multiple entries for the same scheme -- they accumulate.
199    {
200      "http=http1,http2; http=http3",
201
202      ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
203      NULL,
204      "PROXY http1:80;PROXY http2:80;PROXY http3:80",
205      NULL,
206      NULL,
207      NULL,
208    },
209
210    // Include lists of entries for multiple schemes.
211    {
212      "ftp=ftp1,ftp2,ftp3 ; http=http1,http2; ",
213
214      ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
215      NULL,
216      "PROXY http1:80;PROXY http2:80",
217      NULL,
218      "PROXY ftp1:80;PROXY ftp2:80;PROXY ftp3:80",
219      NULL,
220    },
221
222    // Include non-default proxy schemes.
223    {
224      "http=https://secure_proxy; ftp=socks4://socks_proxy; https=socks://foo",
225
226      ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
227      NULL,
228      "HTTPS secure_proxy:443",
229      "SOCKS5 foo:1080",
230      "SOCKS socks_proxy:1080",
231      NULL,
232    },
233
234    // Only SOCKS proxy present, others being blank.
235    {
236      "socks=foopy",
237
238      ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
239      NULL,
240      NULL,
241      NULL,
242      NULL,
243      "SOCKS foopy:1080",
244      },
245
246    // SOCKS proxy present along with other proxies too
247    {
248      "http=httpproxy ; https=httpsproxy ; ftp=ftpproxy ; socks=foopy ",
249
250      ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
251      NULL,
252      "PROXY httpproxy:80",
253      "PROXY httpsproxy:80",
254      "PROXY ftpproxy:80",
255      "SOCKS foopy:1080",
256    },
257
258    // SOCKS proxy (with modifier) present along with some proxies
259    // (FTP being blank)
260    {
261      "http=httpproxy ; https=httpsproxy ; socks=socks5://foopy ",
262
263      ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
264      NULL,
265      "PROXY httpproxy:80",
266      "PROXY httpsproxy:80",
267      NULL,
268      "SOCKS5 foopy:1080",
269      },
270
271    // Include unsupported schemes -- they are discarded.
272    {
273      "crazy=foopy ; foo=bar ; https=myhttpsproxy",
274
275      ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
276      NULL,
277      NULL,
278      "PROXY myhttpsproxy:80",
279      NULL,
280      NULL,
281    },
282
283    // direct:// as first option for a scheme.
284    {
285      "http=direct://,myhttpproxy; https=direct://",
286
287      ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
288      NULL,
289      "DIRECT;PROXY myhttpproxy:80",
290      "DIRECT",
291      NULL,
292      NULL,
293    },
294
295    // direct:// as a second option for a scheme.
296    {
297      "http=myhttpproxy,direct://",
298
299      ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
300      NULL,
301      "PROXY myhttpproxy:80;DIRECT",
302      NULL,
303      NULL,
304      NULL,
305    },
306
307  };
308
309  ProxyConfig config;
310
311  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
312    config.proxy_rules().ParseFromString(tests[i].proxy_rules);
313
314    EXPECT_EQ(tests[i].type, config.proxy_rules().type);
315    ExpectProxyServerEquals(tests[i].single_proxy,
316                            config.proxy_rules().single_proxies);
317    ExpectProxyServerEquals(tests[i].proxy_for_http,
318                            config.proxy_rules().proxies_for_http);
319    ExpectProxyServerEquals(tests[i].proxy_for_https,
320                            config.proxy_rules().proxies_for_https);
321    ExpectProxyServerEquals(tests[i].proxy_for_ftp,
322                            config.proxy_rules().proxies_for_ftp);
323    ExpectProxyServerEquals(tests[i].fallback_proxy,
324                            config.proxy_rules().fallback_proxies);
325  }
326}
327
328TEST(ProxyConfigTest, ProxyRulesSetBypassFlag) {
329  // Test whether the did_bypass_proxy() flag is set in proxy info correctly.
330  ProxyConfig::ProxyRules rules;
331  ProxyInfo  result;
332
333  rules.ParseFromString("http=httpproxy:80");
334  rules.bypass_rules.AddRuleFromString(".com");
335
336  rules.Apply(GURL("http://example.com"), &result);
337  EXPECT_TRUE(result.is_direct_only());
338  EXPECT_TRUE(result.did_bypass_proxy());
339
340  rules.Apply(GURL("http://example.org"), &result);
341  EXPECT_FALSE(result.is_direct());
342  EXPECT_FALSE(result.did_bypass_proxy());
343
344  // Try with reversed bypass rules.
345  rules.reverse_bypass = true;
346
347  rules.Apply(GURL("http://example.org"), &result);
348  EXPECT_TRUE(result.is_direct_only());
349  EXPECT_TRUE(result.did_bypass_proxy());
350
351  rules.Apply(GURL("http://example.com"), &result);
352  EXPECT_FALSE(result.is_direct());
353  EXPECT_FALSE(result.did_bypass_proxy());
354}
355
356static const char kWsUrl[] = "ws://example.com/echo";
357static const char kWssUrl[] = "wss://example.com/echo";
358
359class ProxyConfigWebSocketTest : public ::testing::Test {
360 protected:
361  void ParseFromString(const std::string& rules) {
362    rules_.ParseFromString(rules);
363  }
364  void Apply(const GURL& gurl) { rules_.Apply(gurl, &info_); }
365  std::string ToPacString() const { return info_.ToPacString(); }
366
367  static GURL WsUrl() { return GURL(kWsUrl); }
368  static GURL WssUrl() { return GURL(kWssUrl); }
369
370  ProxyConfig::ProxyRules rules_;
371  ProxyInfo info_;
372};
373
374// If a single proxy is set for all protocols, WebSocket uses it.
375TEST_F(ProxyConfigWebSocketTest, UsesProxy) {
376  ParseFromString("proxy:3128");
377  Apply(WsUrl());
378  EXPECT_EQ("PROXY proxy:3128", ToPacString());
379}
380
381// See RFC6455 Section 4.1. item 3, "_Proxy Usage_".
382TEST_F(ProxyConfigWebSocketTest, PrefersSocks) {
383  ParseFromString(
384      "http=proxy:3128 ; https=sslproxy:3128 ; socks=socksproxy:1080");
385  Apply(WsUrl());
386  EXPECT_EQ("SOCKS socksproxy:1080", ToPacString());
387}
388
389TEST_F(ProxyConfigWebSocketTest, PrefersHttpsToHttp) {
390  ParseFromString("http=proxy:3128 ; https=sslproxy:3128");
391  Apply(WssUrl());
392  EXPECT_EQ("PROXY sslproxy:3128", ToPacString());
393}
394
395TEST_F(ProxyConfigWebSocketTest, PrefersHttpsEvenForWs) {
396  ParseFromString("http=proxy:3128 ; https=sslproxy:3128");
397  Apply(WsUrl());
398  EXPECT_EQ("PROXY sslproxy:3128", ToPacString());
399}
400
401TEST_F(ProxyConfigWebSocketTest, PrefersHttpToDirect) {
402  ParseFromString("http=proxy:3128");
403  Apply(WssUrl());
404  EXPECT_EQ("PROXY proxy:3128", ToPacString());
405}
406
407TEST_F(ProxyConfigWebSocketTest, IgnoresFtpProxy) {
408  ParseFromString("ftp=ftpproxy:3128");
409  Apply(WssUrl());
410  EXPECT_EQ("DIRECT", ToPacString());
411}
412
413TEST_F(ProxyConfigWebSocketTest, ObeysBypassRules) {
414  ParseFromString("http=proxy:3128 ; https=sslproxy:3128");
415  rules_.bypass_rules.AddRuleFromString(".chromium.org");
416  Apply(GURL("wss://codereview.chromium.org/feed"));
417  EXPECT_EQ("DIRECT", ToPacString());
418}
419
420TEST_F(ProxyConfigWebSocketTest, ObeysLocalBypass) {
421  ParseFromString("http=proxy:3128 ; https=sslproxy:3128");
422  rules_.bypass_rules.AddRuleFromString("<local>");
423  Apply(GURL("ws://localhost/feed"));
424  EXPECT_EQ("DIRECT", ToPacString());
425}
426
427}  // namespace
428}  // namespace net
429