// Copyright (c) 2006-2008 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 #include "base/logging.h" #include "base/string_util.h" #include "googleurl/src/gurl.h" #include "net/base/load_log.h" #include "net/base/load_log_unittest.h" #include "net/base/net_errors.h" #include "net/base/test_completion_callback.h" #include "net/proxy/mock_proxy_resolver.h" #include "net/proxy/proxy_config_service.h" #include "net/proxy/proxy_resolver.h" #include "net/proxy/proxy_script_fetcher.h" #include "net/proxy/proxy_service.h" #include "testing/gtest/include/gtest/gtest.h" // TODO(eroman): Write a test which exercises // ProxyService::SuspendAllPendingRequests(). namespace net { namespace { class MockProxyConfigService: public ProxyConfigService { public: MockProxyConfigService() {} // Direct connect. explicit MockProxyConfigService(const ProxyConfig& pc) : config(pc) {} explicit MockProxyConfigService(const std::string& pac_url) { config.pac_url = GURL(pac_url); } virtual int GetProxyConfig(ProxyConfig* results) { *results = config; return OK; } ProxyConfig config; }; } // namespace // A mock ProxyScriptFetcher. No result will be returned to the fetch client // until we call NotifyFetchCompletion() to set the results. class MockProxyScriptFetcher : public ProxyScriptFetcher { public: MockProxyScriptFetcher() : pending_request_callback_(NULL), pending_request_bytes_(NULL) { } // ProxyScriptFetcher implementation. virtual int Fetch(const GURL& url, std::string* bytes, CompletionCallback* callback) { DCHECK(!has_pending_request()); // Save the caller's information, and have them wait. pending_request_url_ = url; pending_request_callback_ = callback; pending_request_bytes_ = bytes; return ERR_IO_PENDING; } void NotifyFetchCompletion(int result, const std::string& bytes) { DCHECK(has_pending_request()); *pending_request_bytes_ = bytes; CompletionCallback* callback = pending_request_callback_; pending_request_callback_ = NULL; callback->Run(result); } virtual void Cancel() {} const GURL& pending_request_url() const { return pending_request_url_; } bool has_pending_request() const { return pending_request_callback_ != NULL; } private: GURL pending_request_url_; CompletionCallback* pending_request_callback_; std::string* pending_request_bytes_; }; TEST(ProxyServiceTest, Direct) { MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; scoped_refptr service( new ProxyService(new MockProxyConfigService, resolver)); GURL url("http://www.google.com/"); ProxyInfo info; TestCompletionCallback callback; scoped_refptr log(new LoadLog); int rv = service->ResolveProxy(url, &info, &callback, NULL, log); EXPECT_EQ(OK, rv); EXPECT_TRUE(resolver->pending_requests().empty()); EXPECT_TRUE(NULL == service->init_proxy_resolver_log()); EXPECT_TRUE(info.is_direct()); // Check the LoadLog was filled correctly. EXPECT_EQ(2u, log->events().size()); ExpectLogContains(log, 0, LoadLog::TYPE_PROXY_SERVICE, LoadLog::PHASE_BEGIN); ExpectLogContains(log, 1, LoadLog::TYPE_PROXY_SERVICE, LoadLog::PHASE_END); } TEST(ProxyServiceTest, PAC) { MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; scoped_refptr service( new ProxyService(config_service, resolver)); GURL url("http://www.google.com/"); ProxyInfo info; TestCompletionCallback callback; scoped_refptr log(new LoadLog); int rv = service->ResolveProxy(url, &info, &callback, NULL, log); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pending_set_pac_script_request()->pac_url()); EXPECT_FALSE(NULL == service->init_proxy_resolver_log()); resolver->pending_set_pac_script_request()->CompleteNow(OK); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(url, resolver->pending_requests()[0]->url()); // Set the result in proxy resolver. resolver->pending_requests()[0]->results()->UseNamedProxy("foopy"); resolver->pending_requests()[0]->CompleteNow(OK); EXPECT_EQ(OK, callback.WaitForResult()); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy:80", info.proxy_server().ToURI()); // Check the LoadLog was filled correctly. EXPECT_EQ(4u, log->events().size()); ExpectLogContains(log, 0, LoadLog::TYPE_PROXY_SERVICE, LoadLog::PHASE_BEGIN); ExpectLogContains(log, 1, LoadLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC, LoadLog::PHASE_BEGIN); ExpectLogContains(log, 2, LoadLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC, LoadLog::PHASE_END); ExpectLogContains(log, 3, LoadLog::TYPE_PROXY_SERVICE, LoadLog::PHASE_END); } // Test that the proxy resolver does not see the URL's username/password // or its reference section. TEST(ProxyServiceTest, PAC_NoIdentityOrHash) { MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; scoped_refptr service( new ProxyService(config_service, resolver)); GURL url("http://username:password@www.google.com/?ref#hash#hash"); ProxyInfo info; TestCompletionCallback callback; int rv = service->ResolveProxy(url, &info, &callback, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pending_set_pac_script_request()->pac_url()); resolver->pending_set_pac_script_request()->CompleteNow(OK); ASSERT_EQ(1u, resolver->pending_requests().size()); // The URL should have been simplified, stripping the username/password/hash. EXPECT_EQ(GURL("http://www.google.com/?ref"), resolver->pending_requests()[0]->url()); // We end here without ever completing the request -- destruction of // ProxyService will cancel the outstanding request. } TEST(ProxyServiceTest, PAC_FailoverToDirect) { MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; scoped_refptr service( new ProxyService(config_service, resolver)); GURL url("http://www.google.com/"); ProxyInfo info; TestCompletionCallback callback1; int rv = service->ResolveProxy(url, &info, &callback1, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pending_set_pac_script_request()->pac_url()); resolver->pending_set_pac_script_request()->CompleteNow(OK); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(url, resolver->pending_requests()[0]->url()); // Set the result in proxy resolver. resolver->pending_requests()[0]->results()->UseNamedProxy("foopy:8080"); resolver->pending_requests()[0]->CompleteNow(OK); EXPECT_EQ(OK, callback1.WaitForResult()); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy:8080", info.proxy_server().ToURI()); // Now, imagine that connecting to foopy:8080 fails. TestCompletionCallback callback2; rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); } TEST(ProxyServiceTest, ProxyResolverFails) { // Test what happens when the ProxyResolver fails (this could represent // a failure to download the PAC script in the case of ProxyResolvers which // do the fetch internally.) // TODO(eroman): change this comment. MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; scoped_refptr service( new ProxyService(config_service, resolver)); // Start first resolve request. GURL url("http://www.google.com/"); ProxyInfo info; TestCompletionCallback callback1; int rv = service->ResolveProxy(url, &info, &callback1, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pending_set_pac_script_request()->pac_url()); resolver->pending_set_pac_script_request()->CompleteNow(OK); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(url, resolver->pending_requests()[0]->url()); // Fail the first resolve request in MockAsyncProxyResolver. resolver->pending_requests()[0]->CompleteNow(ERR_FAILED); EXPECT_EQ(ERR_FAILED, callback1.WaitForResult()); // The second resolve request will automatically select direct connect, // because it has cached the configuration as being bad. TestCompletionCallback callback2; rv = service->ResolveProxy(url, &info, &callback2, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); EXPECT_TRUE(resolver->pending_requests().empty()); // But, if that fails, then we should give the proxy config another shot // since we have never tried it with this URL before. TestCompletionCallback callback3; rv = service->ReconsiderProxyAfterError(url, &info, &callback3, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(url, resolver->pending_requests()[0]->url()); // Set the result in proxy resolver. resolver->pending_requests()[0]->results()->UseNamedProxy("foopy_valid:8080"); resolver->pending_requests()[0]->CompleteNow(OK); EXPECT_EQ(OK, callback3.WaitForResult()); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy_valid:8080", info.proxy_server().ToURI()); } TEST(ProxyServiceTest, ProxyFallback) { // Test what happens when we specify multiple proxy servers and some of them // are bad. MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; scoped_refptr service( new ProxyService(config_service, resolver)); GURL url("http://www.google.com/"); // Get the proxy information. ProxyInfo info; TestCompletionCallback callback1; int rv = service->ResolveProxy(url, &info, &callback1, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pending_set_pac_script_request()->pac_url()); resolver->pending_set_pac_script_request()->CompleteNow(OK); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(url, resolver->pending_requests()[0]->url()); // Set the result in proxy resolver. resolver->pending_requests()[0]->results()->UseNamedProxy( "foopy1:8080;foopy2:9090"); resolver->pending_requests()[0]->CompleteNow(OK); // The first item is valid. EXPECT_EQ(OK, callback1.WaitForResult()); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); // Fake an error on the proxy. TestCompletionCallback callback2; rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL, NULL); EXPECT_EQ(OK, rv); // The second proxy should be specified. EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); TestCompletionCallback callback3; rv = service->ResolveProxy(url, &info, &callback3, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(url, resolver->pending_requests()[0]->url()); // Set the result in proxy resolver -- the second result is already known // to be bad. resolver->pending_requests()[0]->results()->UseNamedProxy( "foopy3:7070;foopy1:8080;foopy2:9090"); resolver->pending_requests()[0]->CompleteNow(OK); EXPECT_EQ(OK, callback3.WaitForResult()); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy3:7070", info.proxy_server().ToURI()); // We fake another error. It should now try the third one. TestCompletionCallback callback4; rv = service->ReconsiderProxyAfterError(url, &info, &callback4, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); // Fake another error, the last proxy is gone, the list should now be empty. TestCompletionCallback callback5; rv = service->ReconsiderProxyAfterError(url, &info, &callback5, NULL, NULL); EXPECT_EQ(OK, rv); // We try direct. EXPECT_TRUE(info.is_direct()); // If it fails again, we don't have anything else to try. TestCompletionCallback callback6; rv = service->ReconsiderProxyAfterError(url, &info, &callback6, NULL, NULL); EXPECT_EQ(ERR_FAILED, rv); // TODO(nsylvain): Test that the proxy can be retried after the delay. } TEST(ProxyServiceTest, ProxyFallback_NewSettings) { // Test proxy failover when new settings are available. MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; scoped_refptr service( new ProxyService(config_service, resolver)); GURL url("http://www.google.com/"); // Get the proxy information. ProxyInfo info; TestCompletionCallback callback1; int rv = service->ResolveProxy(url, &info, &callback1, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pending_set_pac_script_request()->pac_url()); resolver->pending_set_pac_script_request()->CompleteNow(OK); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(url, resolver->pending_requests()[0]->url()); // Set the result in proxy resolver. resolver->pending_requests()[0]->results()->UseNamedProxy( "foopy1:8080;foopy2:9090"); resolver->pending_requests()[0]->CompleteNow(OK); // The first item is valid. EXPECT_EQ(OK, callback1.WaitForResult()); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); // Fake an error on the proxy, and also a new configuration on the proxy. config_service->config = ProxyConfig(); config_service->config.pac_url = GURL("http://foopy-new/proxy.pac"); TestCompletionCallback callback2; rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(GURL("http://foopy-new/proxy.pac"), resolver->pending_set_pac_script_request()->pac_url()); resolver->pending_set_pac_script_request()->CompleteNow(OK); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(url, resolver->pending_requests()[0]->url()); resolver->pending_requests()[0]->results()->UseNamedProxy( "foopy1:8080;foopy2:9090"); resolver->pending_requests()[0]->CompleteNow(OK); // The first proxy is still there since the configuration changed. EXPECT_EQ(OK, callback2.WaitForResult()); EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); // We fake another error. It should now ignore the first one. TestCompletionCallback callback3; rv = service->ReconsiderProxyAfterError(url, &info, &callback3, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); // We simulate a new configuration. config_service->config = ProxyConfig(); config_service->config.pac_url = GURL("http://foopy-new2/proxy.pac"); // We fake another error. It should go back to the first proxy. TestCompletionCallback callback4; rv = service->ReconsiderProxyAfterError(url, &info, &callback4, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(GURL("http://foopy-new2/proxy.pac"), resolver->pending_set_pac_script_request()->pac_url()); resolver->pending_set_pac_script_request()->CompleteNow(OK); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(url, resolver->pending_requests()[0]->url()); resolver->pending_requests()[0]->results()->UseNamedProxy( "foopy1:8080;foopy2:9090"); resolver->pending_requests()[0]->CompleteNow(OK); EXPECT_EQ(OK, callback4.WaitForResult()); EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); } TEST(ProxyServiceTest, ProxyFallback_BadConfig) { // Test proxy failover when the configuration is bad. MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; scoped_refptr service( new ProxyService(config_service, resolver)); GURL url("http://www.google.com/"); // Get the proxy information. ProxyInfo info; TestCompletionCallback callback1; int rv = service->ResolveProxy(url, &info, &callback1, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pending_set_pac_script_request()->pac_url()); resolver->pending_set_pac_script_request()->CompleteNow(OK); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(url, resolver->pending_requests()[0]->url()); resolver->pending_requests()[0]->results()->UseNamedProxy( "foopy1:8080;foopy2:9090"); resolver->pending_requests()[0]->CompleteNow(OK); // The first item is valid. EXPECT_EQ(OK, callback1.WaitForResult()); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); // Fake a proxy error. TestCompletionCallback callback2; rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL, NULL); EXPECT_EQ(OK, rv); // The first proxy is ignored, and the second one is selected. EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); // Fake a PAC failure. ProxyInfo info2; TestCompletionCallback callback3; rv = service->ResolveProxy(url, &info2, &callback3, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(url, resolver->pending_requests()[0]->url()); resolver->pending_requests()[0]->CompleteNow(ERR_FAILED); // No proxy servers are returned. It's a direct connection. EXPECT_EQ(ERR_FAILED, callback3.WaitForResult()); EXPECT_TRUE(info2.is_direct()); // The PAC will now be fixed and will return a proxy server. // It should also clear the list of bad proxies. // Try to resolve, it will still return "direct" because we have no reason // to check the config since everything works. ProxyInfo info3; TestCompletionCallback callback4; rv = service->ResolveProxy(url, &info3, &callback4, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info3.is_direct()); // But if the direct connection fails, we check if the ProxyInfo tried to // resolve the proxy before, and if not (like in this case), we give the // PAC another try. TestCompletionCallback callback5; rv = service->ReconsiderProxyAfterError(url, &info3, &callback5, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(url, resolver->pending_requests()[0]->url()); resolver->pending_requests()[0]->results()->UseNamedProxy( "foopy1:8080;foopy2:9090"); resolver->pending_requests()[0]->CompleteNow(OK); // The first proxy is still there since the list of bad proxies got cleared. EXPECT_EQ(OK, callback5.WaitForResult()); EXPECT_FALSE(info3.is_direct()); EXPECT_EQ("foopy1:8080", info3.proxy_server().ToURI()); } TEST(ProxyServiceTest, ProxyBypassList) { // Test what happens when a proxy bypass list is specified. ProxyInfo info; ProxyConfig config; config.proxy_rules.ParseFromString("foopy1:8080;foopy2:9090"); config.auto_detect = false; config.proxy_bypass_local_names = true; { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver())); GURL url("http://www.google.com/"); // Get the proxy information. TestCompletionCallback callback; int rv = service->ResolveProxy(url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_FALSE(info.is_direct()); } { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver())); GURL test_url("http://local"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); } config.proxy_bypass.clear(); config.proxy_bypass.push_back("*.org"); config.proxy_bypass_local_names = true; { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("http://www.webkit.org"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); } config.proxy_bypass.clear(); config.proxy_bypass.push_back("*.org"); config.proxy_bypass.push_back("7*"); config.proxy_bypass_local_names = true; { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("http://74.125.19.147"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); } config.proxy_bypass.clear(); config.proxy_bypass.push_back("*.org"); config.proxy_bypass_local_names = true; { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("http://www.msn.com"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_FALSE(info.is_direct()); } config.proxy_bypass.clear(); config.proxy_bypass.push_back("*.MSN.COM"); config.proxy_bypass_local_names = true; { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("http://www.msnbc.msn.com"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); } config.proxy_bypass.clear(); config.proxy_bypass.push_back("*.msn.com"); config.proxy_bypass_local_names = true; { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("HTTP://WWW.MSNBC.MSN.COM"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); } } TEST(ProxyServiceTest, ProxyBypassListWithPorts) { // Test port specification in bypass list entries. ProxyInfo info; ProxyConfig config; config.proxy_rules.ParseFromString("foopy1:8080;foopy2:9090"); config.auto_detect = false; config.proxy_bypass_local_names = false; config.proxy_bypass.clear(); config.proxy_bypass.push_back("*.example.com:99"); { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); { GURL test_url("http://www.example.com:99"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); } { GURL test_url("http://www.example.com:100"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_FALSE(info.is_direct()); } { GURL test_url("http://www.example.com"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_FALSE(info.is_direct()); } } config.proxy_bypass.clear(); config.proxy_bypass.push_back("*.example.com:80"); { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("http://www.example.com"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); } config.proxy_bypass.clear(); config.proxy_bypass.push_back("*.example.com"); { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("http://www.example.com:99"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); } // IPv6 with port. config.proxy_bypass.clear(); config.proxy_bypass.push_back("[3ffe:2a00:100:7031::1]:99"); { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); { GURL test_url("http://[3ffe:2a00:100:7031::1]:99/"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); } { GURL test_url("http://[3ffe:2a00:100:7031::1]/"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_FALSE(info.is_direct()); } } // IPv6 without port. The bypass entry ought to work without the // brackets, but the bypass matching logic in ProxyService is // currently limited. config.proxy_bypass.clear(); config.proxy_bypass.push_back("[3ffe:2a00:100:7031::1]"); { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); { GURL test_url("http://[3ffe:2a00:100:7031::1]:99/"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); } { GURL test_url("http://[3ffe:2a00:100:7031::1]/"); TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); } } } TEST(ProxyServiceTest, PerProtocolProxyTests) { ProxyConfig config; config.proxy_rules.ParseFromString("http=foopy1:8080;https=foopy2:8080"); config.auto_detect = false; { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("http://www.msn.com"); ProxyInfo info; TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); } { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("ftp://ftp.google.com"); ProxyInfo info; TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); EXPECT_EQ("direct://", info.proxy_server().ToURI()); } { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("https://webbranch.techcu.com"); ProxyInfo info; TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy2:8080", info.proxy_server().ToURI()); } { config.proxy_rules.ParseFromString("foopy1:8080"); scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("http://www.microsoft.com"); ProxyInfo info; TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); } } // If only HTTP and a SOCKS proxy are specified, check if ftp/https queries // fall back to the SOCKS proxy. TEST(ProxyServiceTest, DefaultProxyFallbackToSOCKS) { ProxyConfig config; config.proxy_rules.ParseFromString("http=foopy1:8080;socks=foopy2:1080"); config.auto_detect = false; EXPECT_EQ(ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME, config.proxy_rules.type); { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("http://www.msn.com"); ProxyInfo info; TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); } { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("ftp://ftp.google.com"); ProxyInfo info; TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI()); } { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("https://webbranch.techcu.com"); ProxyInfo info; TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI()); } { scoped_refptr service(new ProxyService( new MockProxyConfigService(config), new MockAsyncProxyResolver)); GURL test_url("unknown://www.microsoft.com"); ProxyInfo info; TestCompletionCallback callback; int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI()); } } // Test cancellation of an in-progress request. TEST(ProxyServiceTest, CancelInProgressRequest) { MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; scoped_refptr service( new ProxyService(config_service, resolver)); // Start 3 requests. ProxyInfo info1; TestCompletionCallback callback1; int rv = service->ResolveProxy( GURL("http://request1"), &info1, &callback1, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); // Nothing has been sent to the proxy resolver yet, since the proxy // resolver has not been configured yet. ASSERT_EQ(0u, resolver->pending_requests().size()); // Successfully initialize the PAC script. EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pending_set_pac_script_request()->pac_url()); resolver->pending_set_pac_script_request()->CompleteNow(OK); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(GURL("http://request1"), resolver->pending_requests()[0]->url()); ProxyInfo info2; TestCompletionCallback callback2; ProxyService::PacRequest* request2; rv = service->ResolveProxy( GURL("http://request2"), &info2, &callback2, &request2, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); ASSERT_EQ(2u, resolver->pending_requests().size()); EXPECT_EQ(GURL("http://request2"), resolver->pending_requests()[1]->url()); ProxyInfo info3; TestCompletionCallback callback3; rv = service->ResolveProxy( GURL("http://request3"), &info3, &callback3, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); ASSERT_EQ(3u, resolver->pending_requests().size()); EXPECT_EQ(GURL("http://request3"), resolver->pending_requests()[2]->url()); // Cancel the second request service->CancelPacRequest(request2); ASSERT_EQ(2u, resolver->pending_requests().size()); EXPECT_EQ(GURL("http://request1"), resolver->pending_requests()[0]->url()); EXPECT_EQ(GURL("http://request3"), resolver->pending_requests()[1]->url()); // Complete the two un-cancelled requests. // We complete the last one first, just to mix it up a bit. resolver->pending_requests()[1]->results()->UseNamedProxy("request3:80"); resolver->pending_requests()[1]->CompleteNow(OK); resolver->pending_requests()[0]->results()->UseNamedProxy("request1:80"); resolver->pending_requests()[0]->CompleteNow(OK); // Complete and verify that requests ran as expected. EXPECT_EQ(OK, callback1.WaitForResult()); EXPECT_EQ("request1:80", info1.proxy_server().ToURI()); EXPECT_FALSE(callback2.have_result()); // Cancelled. ASSERT_EQ(1u, resolver->cancelled_requests().size()); EXPECT_EQ(GURL("http://request2"), resolver->cancelled_requests()[0]->url()); EXPECT_EQ(OK, callback3.WaitForResult()); EXPECT_EQ("request3:80", info3.proxy_server().ToURI()); } // Test the initial PAC download for ProxyResolverWithoutFetch. TEST(ProxyServiceTest, InitialPACScriptDownload) { MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockAsyncProxyResolverExpectsBytes* resolver = new MockAsyncProxyResolverExpectsBytes; scoped_refptr service( new ProxyService(config_service, resolver)); MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher; service->SetProxyScriptFetcher(fetcher); // Start 3 requests. ProxyInfo info1; TestCompletionCallback callback1; int rv = service->ResolveProxy( GURL("http://request1"), &info1, &callback1, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); // The first request should have triggered download of PAC script. EXPECT_TRUE(fetcher->has_pending_request()); EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url()); ProxyInfo info2; TestCompletionCallback callback2; rv = service->ResolveProxy( GURL("http://request2"), &info2, &callback2, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); ProxyInfo info3; TestCompletionCallback callback3; rv = service->ResolveProxy( GURL("http://request3"), &info3, &callback3, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); // Nothing has been sent to the resolver yet. EXPECT_TRUE(resolver->pending_requests().empty()); // At this point the ProxyService should be waiting for the // ProxyScriptFetcher to invoke its completion callback, notifying it of // PAC script download completion. fetcher->NotifyFetchCompletion(OK, "pac-v1"); // Now that the PAC script is downloaded, it will have been sent to the proxy // resolver. EXPECT_EQ("pac-v1", resolver->pending_set_pac_script_request()->pac_bytes()); resolver->pending_set_pac_script_request()->CompleteNow(OK); ASSERT_EQ(3u, resolver->pending_requests().size()); EXPECT_EQ(GURL("http://request1"), resolver->pending_requests()[0]->url()); EXPECT_EQ(GURL("http://request2"), resolver->pending_requests()[1]->url()); EXPECT_EQ(GURL("http://request3"), resolver->pending_requests()[2]->url()); // Complete all the requests (in some order). // Note that as we complete requests, they shift up in |pending_requests()|. resolver->pending_requests()[2]->results()->UseNamedProxy("request3:80"); resolver->pending_requests()[2]->CompleteNow(OK); resolver->pending_requests()[0]->results()->UseNamedProxy("request1:80"); resolver->pending_requests()[0]->CompleteNow(OK); resolver->pending_requests()[0]->results()->UseNamedProxy("request2:80"); resolver->pending_requests()[0]->CompleteNow(OK); // Complete and verify that requests ran as expected. EXPECT_EQ(OK, callback1.WaitForResult()); EXPECT_EQ("request1:80", info1.proxy_server().ToURI()); EXPECT_EQ(OK, callback2.WaitForResult()); EXPECT_EQ("request2:80", info2.proxy_server().ToURI()); EXPECT_EQ(OK, callback3.WaitForResult()); EXPECT_EQ("request3:80", info3.proxy_server().ToURI()); } // Test cancellation of a request, while the PAC script is being fetched. TEST(ProxyServiceTest, CancelWhilePACFetching) { MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockAsyncProxyResolverExpectsBytes* resolver = new MockAsyncProxyResolverExpectsBytes; scoped_refptr service( new ProxyService(config_service, resolver)); MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher; service->SetProxyScriptFetcher(fetcher); // Start 3 requests. ProxyInfo info1; TestCompletionCallback callback1; ProxyService::PacRequest* request1; scoped_refptr log1(new LoadLog); int rv = service->ResolveProxy( GURL("http://request1"), &info1, &callback1, &request1, log1); EXPECT_EQ(ERR_IO_PENDING, rv); // The first request should have triggered download of PAC script. EXPECT_TRUE(fetcher->has_pending_request()); EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url()); ProxyInfo info2; TestCompletionCallback callback2; ProxyService::PacRequest* request2; rv = service->ResolveProxy( GURL("http://request2"), &info2, &callback2, &request2, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); ProxyInfo info3; TestCompletionCallback callback3; rv = service->ResolveProxy( GURL("http://request3"), &info3, &callback3, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); // Nothing has been sent to the resolver yet. EXPECT_TRUE(resolver->pending_requests().empty()); // Cancel the first 2 requests. service->CancelPacRequest(request1); service->CancelPacRequest(request2); // At this point the ProxyService should be waiting for the // ProxyScriptFetcher to invoke its completion callback, notifying it of // PAC script download completion. fetcher->NotifyFetchCompletion(OK, "pac-v1"); // Now that the PAC script is downloaded, it will have been sent to the // proxy resolver. EXPECT_EQ("pac-v1", resolver->pending_set_pac_script_request()->pac_bytes()); resolver->pending_set_pac_script_request()->CompleteNow(OK); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(GURL("http://request3"), resolver->pending_requests()[0]->url()); // Complete all the requests. resolver->pending_requests()[0]->results()->UseNamedProxy("request3:80"); resolver->pending_requests()[0]->CompleteNow(OK); EXPECT_EQ(OK, callback3.WaitForResult()); EXPECT_EQ("request3:80", info3.proxy_server().ToURI()); EXPECT_TRUE(resolver->cancelled_requests().empty()); EXPECT_FALSE(callback1.have_result()); // Cancelled. EXPECT_FALSE(callback2.have_result()); // Cancelled. // Check the LoadLog for request 1 (which was cancelled) got filled properly. EXPECT_EQ(4u, log1->events().size()); ExpectLogContains(log1, 0, LoadLog::TYPE_PROXY_SERVICE, LoadLog::PHASE_BEGIN); ExpectLogContains(log1, 1, LoadLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC, LoadLog::PHASE_BEGIN); // Note that TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC is never completed before // the cancellation occured. ExpectLogContains(log1, 2, LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE); ExpectLogContains(log1, 3, LoadLog::TYPE_PROXY_SERVICE, LoadLog::PHASE_END); } // Test that if auto-detect fails, we fall-back to the custom pac. TEST(ProxyServiceTest, FallbackFromAutodetectToCustomPac) { ProxyConfig config; config.auto_detect = true; config.pac_url = GURL("http://foopy/proxy.pac"); config.proxy_rules.ParseFromString("http=foopy:80"); // Won't be used. MockProxyConfigService* config_service = new MockProxyConfigService(config); MockAsyncProxyResolverExpectsBytes* resolver = new MockAsyncProxyResolverExpectsBytes; scoped_refptr service( new ProxyService(config_service, resolver)); MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher; service->SetProxyScriptFetcher(fetcher); // Start 2 requests. ProxyInfo info1; TestCompletionCallback callback1; int rv = service->ResolveProxy( GURL("http://request1"), &info1, &callback1, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); ProxyInfo info2; TestCompletionCallback callback2; ProxyService::PacRequest* request2; rv = service->ResolveProxy( GURL("http://request2"), &info2, &callback2, &request2, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); // Check that nothing has been sent to the proxy resolver yet. ASSERT_EQ(0u, resolver->pending_requests().size()); // It should be trying to auto-detect first -- FAIL the autodetect during // the script download. EXPECT_TRUE(fetcher->has_pending_request()); EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url()); fetcher->NotifyFetchCompletion(ERR_FAILED, ""); // Next it should be trying the custom PAC url. EXPECT_TRUE(fetcher->has_pending_request()); EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url()); fetcher->NotifyFetchCompletion(OK, "custom-pac-script"); EXPECT_EQ("custom-pac-script", resolver->pending_set_pac_script_request()->pac_bytes()); resolver->pending_set_pac_script_request()->CompleteNow(OK); // Now finally, the pending requests should have been sent to the resolver // (which was initialized with custom PAC script). ASSERT_EQ(2u, resolver->pending_requests().size()); EXPECT_EQ(GURL("http://request1"), resolver->pending_requests()[0]->url()); EXPECT_EQ(GURL("http://request2"), resolver->pending_requests()[1]->url()); // Complete the pending requests. resolver->pending_requests()[1]->results()->UseNamedProxy("request2:80"); resolver->pending_requests()[1]->CompleteNow(OK); resolver->pending_requests()[0]->results()->UseNamedProxy("request1:80"); resolver->pending_requests()[0]->CompleteNow(OK); // Verify that requests ran as expected. EXPECT_EQ(OK, callback1.WaitForResult()); EXPECT_EQ("request1:80", info1.proxy_server().ToURI()); EXPECT_EQ(OK, callback2.WaitForResult()); EXPECT_EQ("request2:80", info2.proxy_server().ToURI()); } // This is the same test as FallbackFromAutodetectToCustomPac, except // the auto-detect script fails parsing rather than downloading. TEST(ProxyServiceTest, FallbackFromAutodetectToCustomPac2) { ProxyConfig config; config.auto_detect = true; config.pac_url = GURL("http://foopy/proxy.pac"); config.proxy_rules.ParseFromString("http=foopy:80"); // Won't be used. MockProxyConfigService* config_service = new MockProxyConfigService(config); MockAsyncProxyResolverExpectsBytes* resolver = new MockAsyncProxyResolverExpectsBytes; scoped_refptr service( new ProxyService(config_service, resolver)); MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher; service->SetProxyScriptFetcher(fetcher); // Start 2 requests. ProxyInfo info1; TestCompletionCallback callback1; int rv = service->ResolveProxy( GURL("http://request1"), &info1, &callback1, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); ProxyInfo info2; TestCompletionCallback callback2; ProxyService::PacRequest* request2; rv = service->ResolveProxy( GURL("http://request2"), &info2, &callback2, &request2, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); // Check that nothing has been sent to the proxy resolver yet. ASSERT_EQ(0u, resolver->pending_requests().size()); // It should be trying to auto-detect first -- succeed the download. EXPECT_TRUE(fetcher->has_pending_request()); EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url()); fetcher->NotifyFetchCompletion(OK, "invalid-script-contents"); // Simulate a parse error. EXPECT_EQ("invalid-script-contents", resolver->pending_set_pac_script_request()->pac_bytes()); resolver->pending_set_pac_script_request()->CompleteNow( ERR_PAC_SCRIPT_FAILED); // Next it should be trying the custom PAC url. EXPECT_TRUE(fetcher->has_pending_request()); EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url()); fetcher->NotifyFetchCompletion(OK, "custom-pac-script"); EXPECT_EQ("custom-pac-script", resolver->pending_set_pac_script_request()->pac_bytes()); resolver->pending_set_pac_script_request()->CompleteNow(OK); // Now finally, the pending requests should have been sent to the resolver // (which was initialized with custom PAC script). ASSERT_EQ(2u, resolver->pending_requests().size()); EXPECT_EQ(GURL("http://request1"), resolver->pending_requests()[0]->url()); EXPECT_EQ(GURL("http://request2"), resolver->pending_requests()[1]->url()); // Complete the pending requests. resolver->pending_requests()[1]->results()->UseNamedProxy("request2:80"); resolver->pending_requests()[1]->CompleteNow(OK); resolver->pending_requests()[0]->results()->UseNamedProxy("request1:80"); resolver->pending_requests()[0]->CompleteNow(OK); // Verify that requests ran as expected. EXPECT_EQ(OK, callback1.WaitForResult()); EXPECT_EQ("request1:80", info1.proxy_server().ToURI()); EXPECT_EQ(OK, callback2.WaitForResult()); EXPECT_EQ("request2:80", info2.proxy_server().ToURI()); } // Test that if all of auto-detect, a custom PAC script, and manual settings // are given, then we will try them in that order. TEST(ProxyServiceTest, FallbackFromAutodetectToCustomToManual) { ProxyConfig config; config.auto_detect = true; config.pac_url = GURL("http://foopy/proxy.pac"); config.proxy_rules.ParseFromString("http=foopy:80"); MockProxyConfigService* config_service = new MockProxyConfigService(config); MockAsyncProxyResolverExpectsBytes* resolver = new MockAsyncProxyResolverExpectsBytes; scoped_refptr service( new ProxyService(config_service, resolver)); MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher; service->SetProxyScriptFetcher(fetcher); // Start 2 requests. ProxyInfo info1; TestCompletionCallback callback1; int rv = service->ResolveProxy( GURL("http://request1"), &info1, &callback1, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); ProxyInfo info2; TestCompletionCallback callback2; ProxyService::PacRequest* request2; rv = service->ResolveProxy( GURL("http://request2"), &info2, &callback2, &request2, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); // Check that nothing has been sent to the proxy resolver yet. ASSERT_EQ(0u, resolver->pending_requests().size()); // It should be trying to auto-detect first -- fail the download. EXPECT_TRUE(fetcher->has_pending_request()); EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url()); fetcher->NotifyFetchCompletion(ERR_FAILED, ""); // Next it should be trying the custom PAC url -- fail the download. EXPECT_TRUE(fetcher->has_pending_request()); EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url()); fetcher->NotifyFetchCompletion(ERR_FAILED, ""); // Since we never managed to initialize a ProxyResolver, nothing should have // been sent to it. ASSERT_EQ(0u, resolver->pending_requests().size()); // Verify that requests ran as expected -- they should have fallen back to // the manual proxy configuration for HTTP urls. EXPECT_EQ(OK, callback1.WaitForResult()); EXPECT_EQ("foopy:80", info1.proxy_server().ToURI()); EXPECT_EQ(OK, callback2.WaitForResult()); EXPECT_EQ("foopy:80", info2.proxy_server().ToURI()); } // Test that the bypass rules are NOT applied when using autodetect. TEST(ProxyServiceTest, BypassDoesntApplyToPac) { ProxyConfig config; config.auto_detect = true; config.pac_url = GURL("http://foopy/proxy.pac"); config.proxy_rules.ParseFromString("http=foopy:80"); // Not used. config.proxy_bypass.push_back("www.google.com"); MockProxyConfigService* config_service = new MockProxyConfigService(config); MockAsyncProxyResolverExpectsBytes* resolver = new MockAsyncProxyResolverExpectsBytes; scoped_refptr service( new ProxyService(config_service, resolver)); MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher; service->SetProxyScriptFetcher(fetcher); // Start 1 requests. ProxyInfo info1; TestCompletionCallback callback1; int rv = service->ResolveProxy( GURL("http://www.google.com"), &info1, &callback1, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); // Check that nothing has been sent to the proxy resolver yet. ASSERT_EQ(0u, resolver->pending_requests().size()); // It should be trying to auto-detect first -- succeed the download. EXPECT_TRUE(fetcher->has_pending_request()); EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url()); fetcher->NotifyFetchCompletion(OK, "auto-detect"); EXPECT_EQ("auto-detect", resolver->pending_set_pac_script_request()->pac_bytes()); resolver->pending_set_pac_script_request()->CompleteNow(OK); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(GURL("http://www.google.com"), resolver->pending_requests()[0]->url()); // Complete the pending request. resolver->pending_requests()[0]->results()->UseNamedProxy("request1:80"); resolver->pending_requests()[0]->CompleteNow(OK); // Verify that request ran as expected. EXPECT_EQ(OK, callback1.WaitForResult()); EXPECT_EQ("request1:80", info1.proxy_server().ToURI()); // Start another request, it should pickup the bypass item. ProxyInfo info2; TestCompletionCallback callback2; rv = service->ResolveProxy( GURL("http://www.google.com"), &info2, &callback2, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); ASSERT_EQ(1u, resolver->pending_requests().size()); EXPECT_EQ(GURL("http://www.google.com"), resolver->pending_requests()[0]->url()); // Complete the pending request. resolver->pending_requests()[0]->results()->UseNamedProxy("request2:80"); resolver->pending_requests()[0]->CompleteNow(OK); EXPECT_EQ(OK, callback2.WaitForResult()); EXPECT_EQ("request2:80", info2.proxy_server().ToURI()); } TEST(ProxyServiceTest, ResetProxyConfigService) { ProxyConfig config1; config1.proxy_rules.ParseFromString("foopy1:8080"); config1.auto_detect = false; scoped_refptr service(new ProxyService( new MockProxyConfigService(config1), new MockAsyncProxyResolverExpectsBytes)); ProxyInfo info; TestCompletionCallback callback1; int rv = service->ResolveProxy( GURL("http://request1"), &info, &callback1, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); ProxyConfig config2; config2.proxy_rules.ParseFromString("foopy2:8080"); config2.auto_detect = false; service->ResetConfigService(new MockProxyConfigService(config2)); TestCompletionCallback callback2; rv = service->ResolveProxy( GURL("http://request2"), &info, &callback2, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_EQ("foopy2:8080", info.proxy_server().ToURI()); } TEST(ProxyServiceTest, IsLocalName) { const struct { const char* url; bool expected_is_local; } tests[] = { // Single-component hostnames are considered local. {"http://localhost/x", true}, {"http://www", true}, // IPv4 loopback interface. {"http://127.0.0.1/x", true}, {"http://127.0.0.1:80/x", true}, // IPv6 loopback interface. {"http://[::1]:80/x", true}, {"http://[0:0::1]:6233/x", true}, {"http://[0:0:0:0:0:0:0:1]/x", true}, // Non-local URLs. {"http://foo.com/", false}, {"http://localhost.i/", false}, {"http://www.google.com/", false}, {"http://192.168.0.1/", false}, // Try with different protocols. {"ftp://127.0.0.1/x", true}, {"ftp://foobar.com/x", false}, // This is a bit of a gray-area, but GURL does not strip trailing dots // in host-names, so the following are considered non-local. {"http://www./x", false}, {"http://localhost./x", false}, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { SCOPED_TRACE(StringPrintf("Test[%d]: %s", i, tests[i].url)); bool is_local = ProxyService::IsLocalName(GURL(tests[i].url)); EXPECT_EQ(tests[i].expected_is_local, is_local); } } // Check that after we have done the auto-detect test, and the configuration // is updated (with no change), we don't re-try the autodetect test. // Regression test for http://crbug.com/18526 -- the configuration was being // mutated to cancel out the automatic settings, which meant UpdateConfig() // thought it had received a new configuration. TEST(ProxyServiceTest, UpdateConfigAfterFailedAutodetect) { ProxyConfig config; config.auto_detect = true; MockProxyConfigService* config_service = new MockProxyConfigService(config); MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; scoped_refptr service( new ProxyService(config_service, resolver)); // Start 1 requests. ProxyInfo info1; TestCompletionCallback callback1; int rv = service->ResolveProxy( GURL("http://www.google.com"), &info1, &callback1, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); // Check that nothing has been sent to the proxy resolver yet. ASSERT_EQ(0u, resolver->pending_requests().size()); // Fail the setting of autodetect script. EXPECT_EQ(GURL(), resolver->pending_set_pac_script_request()->pac_url()); resolver->pending_set_pac_script_request()->CompleteNow(ERR_FAILED); // Verify that request ran as expected -- should have fallen back to direct. EXPECT_EQ(OK, callback1.WaitForResult()); EXPECT_TRUE(info1.is_direct()); // Force the ProxyService to pull down a new proxy configuration. // (Even though the configuration isn't old/bad). service->UpdateConfig(); // Start another request -- the effective configuration has not // changed, so we shouldn't re-run the autodetect step. // Rather, it should complete synchronously as direct-connect. ProxyInfo info2; TestCompletionCallback callback2; rv = service->ResolveProxy( GURL("http://www.google.com"), &info2, &callback2, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info2.is_direct()); } // Test that when going from a configuration that required PAC to one // that does NOT, we unset the variable |should_use_proxy_resolver_|. TEST(ProxyServiceTest, UpdateConfigFromPACToDirect) { ProxyConfig config; config.auto_detect = true; MockProxyConfigService* config_service = new MockProxyConfigService(config); MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; scoped_refptr service( new ProxyService(config_service, resolver)); // Start 1 request. ProxyInfo info1; TestCompletionCallback callback1; int rv = service->ResolveProxy( GURL("http://www.google.com"), &info1, &callback1, NULL, NULL); EXPECT_EQ(ERR_IO_PENDING, rv); // Check that nothing has been sent to the proxy resolver yet. ASSERT_EQ(0u, resolver->pending_requests().size()); // Successfully set the autodetect script. EXPECT_EQ(GURL(), resolver->pending_set_pac_script_request()->pac_url()); resolver->pending_set_pac_script_request()->CompleteNow(OK); // Complete the pending request. ASSERT_EQ(1u, resolver->pending_requests().size()); resolver->pending_requests()[0]->results()->UseNamedProxy("request1:80"); resolver->pending_requests()[0]->CompleteNow(OK); // Verify that request ran as expected. EXPECT_EQ(OK, callback1.WaitForResult()); EXPECT_EQ("request1:80", info1.proxy_server().ToURI()); // Force the ProxyService to pull down a new proxy configuration. // (Even though the configuration isn't old/bad). // // This new configuration no longer has auto_detect set, so // requests should complete synchronously now as direct-connect. config.auto_detect = false; config_service->config = config; service->UpdateConfig(); // Start another request -- the effective configuration has changed. ProxyInfo info2; TestCompletionCallback callback2; rv = service->ResolveProxy( GURL("http://www.google.com"), &info2, &callback2, NULL, NULL); EXPECT_EQ(OK, rv); EXPECT_TRUE(info2.is_direct()); } } // namespace net