|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/proxy_resolution/proxy_config.h" | 
|  | #include "net/proxy_resolution/proxy_config_service_common_unittest.h" | 
|  | #include "net/proxy_resolution/proxy_info.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace net { | 
|  | namespace { | 
|  |  | 
|  | void ExpectProxyServerEquals(const char* expectation, | 
|  | const ProxyList& proxy_servers) { | 
|  | if (expectation == NULL) { | 
|  | EXPECT_TRUE(proxy_servers.IsEmpty()); | 
|  | } else { | 
|  | EXPECT_EQ(expectation, proxy_servers.ToPacString()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(ProxyConfigTest, Equals) { | 
|  | // Test |ProxyConfig::auto_detect|. | 
|  |  | 
|  | ProxyConfig config1; | 
|  | config1.set_auto_detect(true); | 
|  |  | 
|  | ProxyConfig config2; | 
|  | config2.set_auto_detect(false); | 
|  |  | 
|  | EXPECT_FALSE(config1.Equals(config2)); | 
|  | EXPECT_FALSE(config2.Equals(config1)); | 
|  |  | 
|  | config2.set_auto_detect(true); | 
|  |  | 
|  | EXPECT_TRUE(config1.Equals(config2)); | 
|  | EXPECT_TRUE(config2.Equals(config1)); | 
|  |  | 
|  | // Test |ProxyConfig::pac_url|. | 
|  |  | 
|  | config2.set_pac_url(GURL("http://wpad/wpad.dat")); | 
|  |  | 
|  | EXPECT_FALSE(config1.Equals(config2)); | 
|  | EXPECT_FALSE(config2.Equals(config1)); | 
|  |  | 
|  | config1.set_pac_url(GURL("http://wpad/wpad.dat")); | 
|  |  | 
|  | EXPECT_TRUE(config1.Equals(config2)); | 
|  | EXPECT_TRUE(config2.Equals(config1)); | 
|  |  | 
|  | // Test |ProxyConfig::proxy_rules|. | 
|  |  | 
|  | config2.proxy_rules().type = ProxyConfig::ProxyRules::Type::PROXY_LIST; | 
|  | config2.proxy_rules().single_proxies.SetSingleProxyServer( | 
|  | ProxyServer::FromURI("myproxy:80", ProxyServer::SCHEME_HTTP)); | 
|  |  | 
|  | EXPECT_FALSE(config1.Equals(config2)); | 
|  | EXPECT_FALSE(config2.Equals(config1)); | 
|  |  | 
|  | config1.proxy_rules().type = ProxyConfig::ProxyRules::Type::PROXY_LIST; | 
|  | config1.proxy_rules().single_proxies.SetSingleProxyServer( | 
|  | ProxyServer::FromURI("myproxy:100", ProxyServer::SCHEME_HTTP)); | 
|  |  | 
|  | EXPECT_FALSE(config1.Equals(config2)); | 
|  | EXPECT_FALSE(config2.Equals(config1)); | 
|  |  | 
|  | config1.proxy_rules().single_proxies.SetSingleProxyServer( | 
|  | ProxyServer::FromURI("myproxy", ProxyServer::SCHEME_HTTP)); | 
|  |  | 
|  | EXPECT_TRUE(config1.Equals(config2)); | 
|  | EXPECT_TRUE(config2.Equals(config1)); | 
|  |  | 
|  | // Test |ProxyConfig::bypass_rules|. | 
|  |  | 
|  | config2.proxy_rules().bypass_rules.AddRuleFromString("*.google.com"); | 
|  |  | 
|  | EXPECT_FALSE(config1.Equals(config2)); | 
|  | EXPECT_FALSE(config2.Equals(config1)); | 
|  |  | 
|  | config1.proxy_rules().bypass_rules.AddRuleFromString("*.google.com"); | 
|  |  | 
|  | EXPECT_TRUE(config1.Equals(config2)); | 
|  | EXPECT_TRUE(config2.Equals(config1)); | 
|  |  | 
|  | // Test |ProxyConfig::proxy_rules.reverse_bypass|. | 
|  |  | 
|  | config2.proxy_rules().reverse_bypass = true; | 
|  |  | 
|  | EXPECT_FALSE(config1.Equals(config2)); | 
|  | EXPECT_FALSE(config2.Equals(config1)); | 
|  |  | 
|  | config1.proxy_rules().reverse_bypass = true; | 
|  |  | 
|  | EXPECT_TRUE(config1.Equals(config2)); | 
|  | EXPECT_TRUE(config2.Equals(config1)); | 
|  | } | 
|  |  | 
|  | TEST(ProxyConfigTest, ParseProxyRules) { | 
|  | const struct { | 
|  | const char* proxy_rules; | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type type; | 
|  | // These will be PAC-stle strings, eg 'PROXY foo.com' | 
|  | const char* single_proxy; | 
|  | const char* proxy_for_http; | 
|  | const char* proxy_for_https; | 
|  | const char* proxy_for_ftp; | 
|  | const char* fallback_proxy; | 
|  | } tests[] = { | 
|  | // One HTTP proxy for all schemes. | 
|  | { | 
|  | "myproxy:80", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST, | 
|  | "PROXY myproxy:80", | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | }, | 
|  |  | 
|  | // Multiple HTTP proxies for all schemes. | 
|  | { | 
|  | "myproxy:80,https://myotherproxy", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST, | 
|  | "PROXY myproxy:80;HTTPS myotherproxy:443", | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | }, | 
|  |  | 
|  | // Only specify a proxy server for "http://" urls. | 
|  | { | 
|  | "http=myproxy:80", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, | 
|  | NULL, | 
|  | "PROXY myproxy:80", | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | }, | 
|  |  | 
|  | // Specify an HTTP proxy for "ftp://" and a SOCKS proxy for "https://" urls. | 
|  | { | 
|  | "ftp=ftp-proxy ; https=socks4://foopy", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, | 
|  | NULL, | 
|  | NULL, | 
|  | "SOCKS foopy:1080", | 
|  | "PROXY ftp-proxy:80", | 
|  | NULL, | 
|  | }, | 
|  |  | 
|  | // Give a scheme-specific proxy as well as a non-scheme specific. | 
|  | // The first entry "foopy" takes precedance marking this list as | 
|  | // Type::PROXY_LIST. | 
|  | { | 
|  | "foopy ; ftp=ftp-proxy", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST, | 
|  | "PROXY foopy:80", | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | }, | 
|  |  | 
|  | // Give a scheme-specific proxy as well as a non-scheme specific. | 
|  | // The first entry "ftp=ftp-proxy" takes precedance marking this list as | 
|  | // Type::PROXY_LIST_PER_SCHEME. | 
|  | { | 
|  | "ftp=ftp-proxy ; foopy", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | "PROXY ftp-proxy:80", | 
|  | NULL, | 
|  | }, | 
|  |  | 
|  | // Include a list of entries for a single scheme. | 
|  | { | 
|  | "ftp=ftp1,ftp2,ftp3", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | "PROXY ftp1:80;PROXY ftp2:80;PROXY ftp3:80", | 
|  | NULL, | 
|  | }, | 
|  |  | 
|  | // Include multiple entries for the same scheme -- they accumulate. | 
|  | { | 
|  | "http=http1,http2; http=http3", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, | 
|  | NULL, | 
|  | "PROXY http1:80;PROXY http2:80;PROXY http3:80", | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | }, | 
|  |  | 
|  | // Include lists of entries for multiple schemes. | 
|  | { | 
|  | "ftp=ftp1,ftp2,ftp3 ; http=http1,http2; ", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, | 
|  | NULL, | 
|  | "PROXY http1:80;PROXY http2:80", | 
|  | NULL, | 
|  | "PROXY ftp1:80;PROXY ftp2:80;PROXY ftp3:80", | 
|  | NULL, | 
|  | }, | 
|  |  | 
|  | // Include non-default proxy schemes. | 
|  | { | 
|  | "http=https://secure_proxy; ftp=socks4://socks_proxy; https=socks://foo", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, | 
|  | NULL, | 
|  | "HTTPS secure_proxy:443", | 
|  | "SOCKS5 foo:1080", | 
|  | "SOCKS socks_proxy:1080", | 
|  | NULL, | 
|  | }, | 
|  |  | 
|  | // Only SOCKS proxy present, others being blank. | 
|  | { | 
|  | "socks=foopy", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | "SOCKS foopy:1080", | 
|  | }, | 
|  |  | 
|  | // SOCKS proxy present along with other proxies too | 
|  | { | 
|  | "http=httpproxy ; https=httpsproxy ; ftp=ftpproxy ; socks=foopy ", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, | 
|  | NULL, | 
|  | "PROXY httpproxy:80", | 
|  | "PROXY httpsproxy:80", | 
|  | "PROXY ftpproxy:80", | 
|  | "SOCKS foopy:1080", | 
|  | }, | 
|  |  | 
|  | // SOCKS proxy (with modifier) present along with some proxies | 
|  | // (FTP being blank) | 
|  | { | 
|  | "http=httpproxy ; https=httpsproxy ; socks=socks5://foopy ", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, | 
|  | NULL, | 
|  | "PROXY httpproxy:80", | 
|  | "PROXY httpsproxy:80", | 
|  | NULL, | 
|  | "SOCKS5 foopy:1080", | 
|  | }, | 
|  |  | 
|  | // Include unsupported schemes -- they are discarded. | 
|  | { | 
|  | "crazy=foopy ; foo=bar ; https=myhttpsproxy", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, | 
|  | NULL, | 
|  | NULL, | 
|  | "PROXY myhttpsproxy:80", | 
|  | NULL, | 
|  | NULL, | 
|  | }, | 
|  |  | 
|  | // direct:// as first option for a scheme. | 
|  | { | 
|  | "http=direct://,myhttpproxy; https=direct://", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, | 
|  | NULL, | 
|  | "DIRECT;PROXY myhttpproxy:80", | 
|  | "DIRECT", | 
|  | NULL, | 
|  | NULL, | 
|  | }, | 
|  |  | 
|  | // direct:// as a second option for a scheme. | 
|  | { | 
|  | "http=myhttpproxy,direct://", | 
|  |  | 
|  | ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME, | 
|  | NULL, | 
|  | "PROXY myhttpproxy:80;DIRECT", | 
|  | NULL, | 
|  | NULL, | 
|  | NULL, | 
|  | }, | 
|  |  | 
|  | }; | 
|  |  | 
|  | ProxyConfig config; | 
|  |  | 
|  | for (size_t i = 0; i < arraysize(tests); ++i) { | 
|  | config.proxy_rules().ParseFromString(tests[i].proxy_rules); | 
|  |  | 
|  | EXPECT_EQ(tests[i].type, config.proxy_rules().type); | 
|  | ExpectProxyServerEquals(tests[i].single_proxy, | 
|  | config.proxy_rules().single_proxies); | 
|  | ExpectProxyServerEquals(tests[i].proxy_for_http, | 
|  | config.proxy_rules().proxies_for_http); | 
|  | ExpectProxyServerEquals(tests[i].proxy_for_https, | 
|  | config.proxy_rules().proxies_for_https); | 
|  | ExpectProxyServerEquals(tests[i].proxy_for_ftp, | 
|  | config.proxy_rules().proxies_for_ftp); | 
|  | ExpectProxyServerEquals(tests[i].fallback_proxy, | 
|  | config.proxy_rules().fallback_proxies); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(ProxyConfigTest, ProxyRulesSetBypassFlag) { | 
|  | // Test whether the did_bypass_proxy() flag is set in proxy info correctly. | 
|  | ProxyConfig::ProxyRules rules; | 
|  | ProxyInfo  result; | 
|  |  | 
|  | rules.ParseFromString("http=httpproxy:80"); | 
|  | rules.bypass_rules.AddRuleFromString(".com"); | 
|  |  | 
|  | rules.Apply(GURL("http://example.com"), &result); | 
|  | EXPECT_TRUE(result.is_direct_only()); | 
|  | EXPECT_TRUE(result.did_bypass_proxy()); | 
|  |  | 
|  | rules.Apply(GURL("http://example.org"), &result); | 
|  | EXPECT_FALSE(result.is_direct()); | 
|  | EXPECT_FALSE(result.did_bypass_proxy()); | 
|  |  | 
|  | // Try with reversed bypass rules. | 
|  | rules.reverse_bypass = true; | 
|  |  | 
|  | rules.Apply(GURL("http://example.org"), &result); | 
|  | EXPECT_TRUE(result.is_direct_only()); | 
|  | EXPECT_TRUE(result.did_bypass_proxy()); | 
|  |  | 
|  | rules.Apply(GURL("http://example.com"), &result); | 
|  | EXPECT_FALSE(result.is_direct()); | 
|  | EXPECT_FALSE(result.did_bypass_proxy()); | 
|  | } | 
|  |  | 
|  | static const char kWsUrl[] = "ws://example.com/echo"; | 
|  | static const char kWssUrl[] = "wss://example.com/echo"; | 
|  |  | 
|  | class ProxyConfigWebSocketTest : public ::testing::Test { | 
|  | protected: | 
|  | void ParseFromString(const std::string& rules) { | 
|  | rules_.ParseFromString(rules); | 
|  | } | 
|  | void Apply(const GURL& gurl) { rules_.Apply(gurl, &info_); } | 
|  | std::string ToPacString() const { return info_.ToPacString(); } | 
|  |  | 
|  | static GURL WsUrl() { return GURL(kWsUrl); } | 
|  | static GURL WssUrl() { return GURL(kWssUrl); } | 
|  |  | 
|  | ProxyConfig::ProxyRules rules_; | 
|  | ProxyInfo info_; | 
|  | }; | 
|  |  | 
|  | // If a single proxy is set for all protocols, WebSocket uses it. | 
|  | TEST_F(ProxyConfigWebSocketTest, UsesProxy) { | 
|  | ParseFromString("proxy:3128"); | 
|  | Apply(WsUrl()); | 
|  | EXPECT_EQ("PROXY proxy:3128", ToPacString()); | 
|  | } | 
|  |  | 
|  | // See RFC6455 Section 4.1. item 3, "_Proxy Usage_". | 
|  | TEST_F(ProxyConfigWebSocketTest, PrefersSocks) { | 
|  | ParseFromString( | 
|  | "http=proxy:3128 ; https=sslproxy:3128 ; socks=socksproxy:1080"); | 
|  | Apply(WsUrl()); | 
|  | EXPECT_EQ("SOCKS socksproxy:1080", ToPacString()); | 
|  | } | 
|  |  | 
|  | TEST_F(ProxyConfigWebSocketTest, PrefersHttpsToHttp) { | 
|  | ParseFromString("http=proxy:3128 ; https=sslproxy:3128"); | 
|  | Apply(WssUrl()); | 
|  | EXPECT_EQ("PROXY sslproxy:3128", ToPacString()); | 
|  | } | 
|  |  | 
|  | TEST_F(ProxyConfigWebSocketTest, PrefersHttpsEvenForWs) { | 
|  | ParseFromString("http=proxy:3128 ; https=sslproxy:3128"); | 
|  | Apply(WsUrl()); | 
|  | EXPECT_EQ("PROXY sslproxy:3128", ToPacString()); | 
|  | } | 
|  |  | 
|  | TEST_F(ProxyConfigWebSocketTest, PrefersHttpToDirect) { | 
|  | ParseFromString("http=proxy:3128"); | 
|  | Apply(WssUrl()); | 
|  | EXPECT_EQ("PROXY proxy:3128", ToPacString()); | 
|  | } | 
|  |  | 
|  | TEST_F(ProxyConfigWebSocketTest, IgnoresFtpProxy) { | 
|  | ParseFromString("ftp=ftpproxy:3128"); | 
|  | Apply(WssUrl()); | 
|  | EXPECT_EQ("DIRECT", ToPacString()); | 
|  | } | 
|  |  | 
|  | TEST_F(ProxyConfigWebSocketTest, ObeysBypassRules) { | 
|  | ParseFromString("http=proxy:3128 ; https=sslproxy:3128"); | 
|  | rules_.bypass_rules.AddRuleFromString(".chromium.org"); | 
|  | Apply(GURL("wss://codereview.chromium.org/feed")); | 
|  | EXPECT_EQ("DIRECT", ToPacString()); | 
|  | } | 
|  |  | 
|  | TEST_F(ProxyConfigWebSocketTest, ObeysLocalBypass) { | 
|  | ParseFromString("http=proxy:3128 ; https=sslproxy:3128"); | 
|  | rules_.bypass_rules.AddRuleFromString("<local>"); | 
|  | Apply(GURL("ws://localhost/feed")); | 
|  | EXPECT_EQ("DIRECT", ToPacString()); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace net |