diff options
author | hamaji@chromium.org <hamaji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-06 12:11:05 +0000 |
---|---|---|
committer | hamaji@chromium.org <hamaji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-06 12:11:05 +0000 |
commit | a692c6f3a59e711bc38fc72fce3c938deec64da0 (patch) | |
tree | 0583e80059992b55252390a7891e1651ba1c2ab3 /net/proxy | |
parent | 4bd778d6cd4e108a38caeda9b283f01691fe7ecf (diff) | |
download | chromium_src-a692c6f3a59e711bc38fc72fce3c938deec64da0.zip chromium_src-a692c6f3a59e711bc38fc72fce3c938deec64da0.tar.gz chromium_src-a692c6f3a59e711bc38fc72fce3c938deec64da0.tar.bz2 |
Reverting r22591. It seems to be making purify fail.
r22590: http://build.chromium.org/buildbot/waterfall/builders/XP%20Unit%20(purify)/builds/5026
r22591: http://build.chromium.org/buildbot/waterfall/builders/XP%20Unit%20(purify)/builds/5030
TEST=none
BUG=none
TBR=eroman
Review URL: http://codereview.chromium.org/165048
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@22603 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/proxy')
-rw-r--r-- | net/proxy/mock_proxy_resolver.h | 1375 | ||||
-rw-r--r-- | net/proxy/proxy_service_unittest.cc | 151 |
2 files changed, 1517 insertions, 9 deletions
diff --git a/net/proxy/mock_proxy_resolver.h b/net/proxy/mock_proxy_resolver.h index 0b1e8a5..0dac40b 100644 --- a/net/proxy/mock_proxy_resolver.h +++ b/net/proxy/mock_proxy_resolver.h @@ -1,18 +1,40 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. -#ifndef NET_PROXY_MOCK_PROXY_RESOLVER_H_ -#define NET_PROXY_MOCK_PROXY_RESOLVER_H_ - #include <vector> #include "base/logging.h" +#include "base/string_util.h" #include "googleurl/src/gurl.h" #include "net/base/net_errors.h" +#include "net/base/test_completion_callback.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; +}; // Asynchronous mock proxy resolver. All requests complete asynchronously, // user must call Request::CompleteNow() on a pending request to signal it. @@ -111,7 +133,7 @@ class MockAsyncProxyResolverBase : public ProxyResolver { virtual int SetPacScript(const GURL& pac_url, const std::string& pac_bytes, CompletionCallback* callback) { - DCHECK(!pending_set_pac_script_request_.get()); + EXPECT_EQ(NULL, pending_set_pac_script_request_.get()); pending_set_pac_script_request_.reset( new SetPacScriptRequest(this, pac_url, pac_bytes, callback)); // Finished when user calls SetPacScriptRequest::CompleteNow(). @@ -138,7 +160,7 @@ class MockAsyncProxyResolverBase : public ProxyResolver { } void RemovePendingSetPacScriptRequest(SetPacScriptRequest* request) { - DCHECK_EQ(request, pending_set_pac_script_request()); + EXPECT_EQ(request, pending_set_pac_script_request()); pending_set_pac_script_request_.reset(); } @@ -164,6 +186,1343 @@ class MockAsyncProxyResolverExpectsBytes : public MockAsyncProxyResolverBase { : MockAsyncProxyResolverBase(true /*expects_pac_bytes*/) {} }; -} // namespace net +} // 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; + ProxyService service(new MockProxyConfigService, resolver); + + GURL url("http://www.google.com/"); + + ProxyInfo info; + TestCompletionCallback callback; + int rv = service.ResolveProxy(url, &info, &callback, NULL); + EXPECT_EQ(OK, rv); + EXPECT_TRUE(resolver->pending_requests().empty()); + + EXPECT_TRUE(info.is_direct()); +} + +TEST(ProxyServiceTest, PAC) { + MockProxyConfigService* config_service = + new MockProxyConfigService("http://foopy/proxy.pac"); + + MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; + + ProxyService service(config_service, resolver); + + GURL url("http://www.google.com/"); + + ProxyInfo info; + TestCompletionCallback callback; + int rv = service.ResolveProxy(url, &info, &callback, 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"); + resolver->pending_requests()[0]->CompleteNow(OK); + + EXPECT_EQ(OK, callback.WaitForResult()); + EXPECT_FALSE(info.is_direct()); + EXPECT_EQ("foopy:80", info.proxy_server().ToURI()); +} + +// 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; + + ProxyService service(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); + 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; + + ProxyService service(config_service, resolver); + + GURL url("http://www.google.com/"); + + ProxyInfo info; + TestCompletionCallback callback1; + int rv = service.ResolveProxy(url, &info, &callback1, 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); + 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; + + ProxyService service(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); + 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); + 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); + 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; + + ProxyService service(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); + 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); + 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); + 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); + 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); + 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); + 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; + + ProxyService service(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); + 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); + 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); + 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); + 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; + + ProxyService service(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); + 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); + 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); + 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); + 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); + 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; + + { + ProxyService service(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); + EXPECT_EQ(OK, rv); + EXPECT_FALSE(info.is_direct()); + } + + { + ProxyService service(new MockProxyConfigService(config), + new MockAsyncProxyResolver()); + GURL test_url("http://local"); + TestCompletionCallback callback; + int rv = service.ResolveProxy(test_url, &info, &callback, 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; + { + ProxyService service(new MockProxyConfigService(config), + new MockAsyncProxyResolver); + GURL test_url("http://www.webkit.org"); + TestCompletionCallback callback; + int rv = service.ResolveProxy(test_url, &info, &callback, 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; + { + ProxyService service(new MockProxyConfigService(config), + new MockAsyncProxyResolver); + GURL test_url("http://74.125.19.147"); + TestCompletionCallback callback; + int rv = service.ResolveProxy(test_url, &info, &callback, 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; + { + ProxyService service(new MockProxyConfigService(config), + new MockAsyncProxyResolver); + GURL test_url("http://www.msn.com"); + TestCompletionCallback callback; + int rv = service.ResolveProxy(test_url, &info, &callback, 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; + { + ProxyService service(new MockProxyConfigService(config), + new MockAsyncProxyResolver); + GURL test_url("http://www.msnbc.msn.com"); + TestCompletionCallback callback; + int rv = service.ResolveProxy(test_url, &info, &callback, 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; + { + ProxyService service(new MockProxyConfigService(config), + new MockAsyncProxyResolver); + GURL test_url("HTTP://WWW.MSNBC.MSN.COM"); + TestCompletionCallback callback; + int rv = service.ResolveProxy(test_url, &info, &callback, 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"); + { + ProxyService service(new MockProxyConfigService(config), + new MockAsyncProxyResolver); + { + GURL test_url("http://www.example.com:99"); + TestCompletionCallback callback; + int rv = service.ResolveProxy(test_url, &info, &callback, 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); + 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); + EXPECT_EQ(OK, rv); + EXPECT_FALSE(info.is_direct()); + } + } + + config.proxy_bypass.clear(); + config.proxy_bypass.push_back("*.example.com:80"); + { + ProxyService service(new MockProxyConfigService(config), + new MockAsyncProxyResolver); + GURL test_url("http://www.example.com"); + TestCompletionCallback callback; + int rv = service.ResolveProxy(test_url, &info, &callback, NULL); + EXPECT_EQ(OK, rv); + EXPECT_TRUE(info.is_direct()); + } + + config.proxy_bypass.clear(); + config.proxy_bypass.push_back("*.example.com"); + { + ProxyService service(new MockProxyConfigService(config), + new MockAsyncProxyResolver); + GURL test_url("http://www.example.com:99"); + TestCompletionCallback callback; + int rv = service.ResolveProxy(test_url, &info, &callback, 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"); + { + ProxyService service(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); + 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); + 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]"); + { + ProxyService service(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); + 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); + 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; + { + ProxyService service(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); + EXPECT_EQ(OK, rv); + EXPECT_FALSE(info.is_direct()); + EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); + } + { + ProxyService service(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); + EXPECT_EQ(OK, rv); + EXPECT_TRUE(info.is_direct()); + EXPECT_EQ("direct://", info.proxy_server().ToURI()); + } + { + ProxyService service(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); + EXPECT_EQ(OK, rv); + EXPECT_FALSE(info.is_direct()); + EXPECT_EQ("foopy2:8080", info.proxy_server().ToURI()); + } + { + config.proxy_rules.ParseFromString("foopy1:8080"); + ProxyService service(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); + 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); + + { + ProxyService service(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); + EXPECT_EQ(OK, rv); + EXPECT_FALSE(info.is_direct()); + EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); + } + { + ProxyService service(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); + EXPECT_EQ(OK, rv); + EXPECT_FALSE(info.is_direct()); + EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI()); + } + { + ProxyService service(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); + EXPECT_EQ(OK, rv); + EXPECT_FALSE(info.is_direct()); + EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI()); + } + { + ProxyService service(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); + 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; + + ProxyService service(config_service, resolver); + + // Start 3 requests. + + ProxyInfo info1; + TestCompletionCallback callback1; + int rv = service.ResolveProxy( + GURL("http://request1"), &info1, &callback1, 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); + 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); + 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; + + ProxyService service(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); + 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); + EXPECT_EQ(ERR_IO_PENDING, rv); + + ProxyInfo info3; + TestCompletionCallback callback3; + rv = service.ResolveProxy( + GURL("http://request3"), &info3, &callback3, 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; + + ProxyService service(config_service, resolver); + + MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher; + service.SetProxyScriptFetcher(fetcher); + + // Start 3 requests. + ProxyInfo info1; + TestCompletionCallback callback1; + ProxyService::PacRequest* request1; + int rv = service.ResolveProxy( + GURL("http://request1"), &info1, &callback1, &request1); + 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); + EXPECT_EQ(ERR_IO_PENDING, rv); + + ProxyInfo info3; + TestCompletionCallback callback3; + rv = service.ResolveProxy( + GURL("http://request3"), &info3, &callback3, 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); -#endif // NET_PROXY_MOCK_PROXY_RESOLVER_H_ + 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. +} + +// 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; + ProxyService service(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); + EXPECT_EQ(ERR_IO_PENDING, rv); + + ProxyInfo info2; + TestCompletionCallback callback2; + ProxyService::PacRequest* request2; + rv = service.ResolveProxy( + GURL("http://request2"), &info2, &callback2, &request2); + 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; + ProxyService service(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); + EXPECT_EQ(ERR_IO_PENDING, rv); + + ProxyInfo info2; + TestCompletionCallback callback2; + ProxyService::PacRequest* request2; + rv = service.ResolveProxy( + GURL("http://request2"), &info2, &callback2, &request2); + 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; + ProxyService service(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); + EXPECT_EQ(ERR_IO_PENDING, rv); + + ProxyInfo info2; + TestCompletionCallback callback2; + ProxyService::PacRequest* request2; + rv = service.ResolveProxy( + GURL("http://request2"), &info2, &callback2, &request2); + 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; + ProxyService service(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); + 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); + 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; + ProxyService service(new MockProxyConfigService(config1), + new MockAsyncProxyResolverExpectsBytes); + + ProxyInfo info; + TestCompletionCallback callback1; + int rv = service.ResolveProxy( + GURL("http://request1"), &info, &callback1, 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); + 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); + } +} + +} // namespace net diff --git a/net/proxy/proxy_service_unittest.cc b/net/proxy/proxy_service_unittest.cc index f4deb67..3b3eb49 100644 --- a/net/proxy/proxy_service_unittest.cc +++ b/net/proxy/proxy_service_unittest.cc @@ -9,7 +9,6 @@ #include "googleurl/src/gurl.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" @@ -37,6 +36,156 @@ class MockProxyConfigService: public ProxyConfigService { ProxyConfig config; }; +// Asynchronous mock proxy resolver. All requests complete asynchronously, +// user must call Request::CompleteNow() on a pending request to signal it. +class MockAsyncProxyResolverBase : public ProxyResolver { + public: + class Request : public base::RefCounted<Request> { + public: + Request(MockAsyncProxyResolverBase* resolver, + const GURL& url, + ProxyInfo* results, + CompletionCallback* callback) + : resolver_(resolver), + url_(url), + results_(results), + callback_(callback), + origin_loop_(MessageLoop::current()) { + } + + const GURL& url() const { return url_; } + ProxyInfo* results() const { return results_; } + CompletionCallback* callback() const { return callback_; } + + void CompleteNow(int rv) { + CompletionCallback* callback = callback_; + + // May delete |this|. + resolver_->RemovePendingRequest(this); + + callback->Run(rv); + } + + private: + MockAsyncProxyResolverBase* resolver_; + const GURL url_; + ProxyInfo* results_; + CompletionCallback* callback_; + MessageLoop* origin_loop_; + }; + + class SetPacScriptRequest { + public: + SetPacScriptRequest(MockAsyncProxyResolverBase* resolver, + const GURL& pac_url, + const std::string& pac_bytes, + CompletionCallback* callback) + : resolver_(resolver), + pac_url_(pac_url), + pac_bytes_(pac_bytes), + callback_(callback), + origin_loop_(MessageLoop::current()) { + } + + const GURL& pac_url() const { return pac_url_; } + const std::string& pac_bytes() const { return pac_bytes_; } + + void CompleteNow(int rv) { + CompletionCallback* callback = callback_; + + // Will delete |this|. + resolver_->RemovePendingSetPacScriptRequest(this); + + callback->Run(rv); + } + + private: + MockAsyncProxyResolverBase* resolver_; + const GURL pac_url_; + const std::string pac_bytes_; + CompletionCallback* callback_; + MessageLoop* origin_loop_; + }; + + typedef std::vector<scoped_refptr<Request> > RequestsList; + + // ProxyResolver implementation: + virtual int GetProxyForURL(const GURL& url, + ProxyInfo* results, + CompletionCallback* callback, + RequestHandle* request_handle) { + scoped_refptr<Request> request = new Request(this, url, results, callback); + pending_requests_.push_back(request); + + if (request_handle) + *request_handle = reinterpret_cast<RequestHandle>(request.get()); + + // Test code completes the request by calling request->CompleteNow(). + return ERR_IO_PENDING; + } + + virtual void CancelRequest(RequestHandle request_handle) { + scoped_refptr<Request> request = reinterpret_cast<Request*>(request_handle); + cancelled_requests_.push_back(request); + RemovePendingRequest(request); + } + + virtual int SetPacScript(const GURL& pac_url, + const std::string& pac_bytes, + CompletionCallback* callback) { + EXPECT_EQ(NULL, pending_set_pac_script_request_.get()); + pending_set_pac_script_request_.reset( + new SetPacScriptRequest(this, pac_url, pac_bytes, callback)); + // Finished when user calls SetPacScriptRequest::CompleteNow(). + return ERR_IO_PENDING; + } + + const RequestsList& pending_requests() const { + return pending_requests_; + } + + const RequestsList& cancelled_requests() const { + return cancelled_requests_; + } + + SetPacScriptRequest* pending_set_pac_script_request() const { + return pending_set_pac_script_request_.get(); + } + + void RemovePendingRequest(Request* request) { + RequestsList::iterator it = std::find( + pending_requests_.begin(), pending_requests_.end(), request); + DCHECK(it != pending_requests_.end()); + pending_requests_.erase(it); + } + + void RemovePendingSetPacScriptRequest(SetPacScriptRequest* request) { + EXPECT_EQ(request, pending_set_pac_script_request()); + pending_set_pac_script_request_.reset(); + } + + protected: + explicit MockAsyncProxyResolverBase(bool expects_pac_bytes) + : ProxyResolver(expects_pac_bytes) {} + + private: + RequestsList pending_requests_; + RequestsList cancelled_requests_; + scoped_ptr<SetPacScriptRequest> pending_set_pac_script_request_; +}; + +class MockAsyncProxyResolver : public MockAsyncProxyResolverBase { + public: + MockAsyncProxyResolver() + : MockAsyncProxyResolverBase(false /*expects_pac_bytes*/) {} +}; + +class MockAsyncProxyResolverExpectsBytes : public MockAsyncProxyResolverBase { + public: + MockAsyncProxyResolverExpectsBytes() + : MockAsyncProxyResolverBase(true /*expects_pac_bytes*/) {} +}; + } // namespace // A mock ProxyScriptFetcher. No result will be returned to the fetch client |