// 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 "base/compiler_specific.h" #include "googleurl/src/gurl.h" #include "net/base/net_errors.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" namespace { class MockProxyConfigService: public net::ProxyConfigService { public: MockProxyConfigService() {} // Direct connect. explicit MockProxyConfigService(const net::ProxyConfig& pc) : config(pc) {} explicit MockProxyConfigService(const std::string& pac_url) { config.pac_url = GURL(pac_url); } virtual int GetProxyConfig(net::ProxyConfig* results) { *results = config; return net::OK; } net::ProxyConfig config; }; class MockProxyResolver : public net::ProxyResolver { public: MockProxyResolver() : net::ProxyResolver(true), fail_get_proxy_for_url(false) { } virtual int GetProxyForURL(const GURL& query_url, const GURL& pac_url, net::ProxyInfo* results) { if (fail_get_proxy_for_url) return net::ERR_FAILED; if (GURL(query_url).host() == info_predicate_query_host) { results->Use(info); } else { results->UseDirect(); } return net::OK; } net::ProxyInfo info; // info is only returned if query_url in GetProxyForURL matches this: std::string info_predicate_query_host; // If true, then GetProxyForURL will fail, which simulates failure to // download or execute the PAC file. bool fail_get_proxy_for_url; }; class SyncProxyService { public: SyncProxyService(net::ProxyConfigService* config_service, net::ProxyResolver* resolver) : io_thread_("IO_Thread"), service_(config_service, resolver) { base::Thread::Options options; options.message_loop_type = MessageLoop::TYPE_IO; io_thread_.StartWithOptions(options); sync_proxy_service_ = new net::SyncProxyServiceHelper( io_thread_.message_loop(), &service_); } int ResolveProxy(const GURL& url, net::ProxyInfo* proxy_info) { return sync_proxy_service_->ResolveProxy(url, proxy_info); } int ReconsiderProxyAfterError(const GURL& url, net::ProxyInfo* proxy_info) { return sync_proxy_service_->ReconsiderProxyAfterError(url, proxy_info); } private: base::Thread io_thread_; net::ProxyService service_; scoped_refptr sync_proxy_service_; }; // ResultFuture is a handle to get at the result from // ProxyService::ResolveProxyForURL() that ran on another thread. class ResultFuture : public base::RefCountedThreadSafe { public: // |service| is the ProxyService to issue requests on, and |io_message_loop| // is the message loop where ProxyService lives. ResultFuture(MessageLoop* io_message_loop, net::ProxyService* service) : io_message_loop_(io_message_loop), service_(service), request_(NULL), ALLOW_THIS_IN_INITIALIZER_LIST( callback_(this, &ResultFuture::OnCompletion)), completion_(true, false), cancelled_(false, false), started_(false, false), did_complete_(false) { } // Block until the request has completed, then return the result. int GetResultCode() { DCHECK(MessageLoop::current() != io_message_loop_); WaitUntilCompleted(); return result_code_; } // Block until the request has completed, then return the result. const net::ProxyInfo& GetProxyInfo() { DCHECK(MessageLoop::current() != io_message_loop_); WaitUntilCompleted(); return proxy_info_; } // Cancel this request (wait until the cancel has been issued before // returning). void Cancel() { DCHECK(MessageLoop::current() != io_message_loop_); io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &ResultFuture::DoCancel)); cancelled_.Wait(); } // Return true if the request has already completed. bool IsCompleted() { DCHECK(MessageLoop::current() != io_message_loop_); return did_complete_; } // Wait until the ProxyService completes this request. void WaitUntilCompleted() { DCHECK(MessageLoop::current() != io_message_loop_); completion_.Wait(); DCHECK(did_complete_); } private: friend class ProxyServiceWithFutures; // Start the request. Return once ProxyService::GetProxyForURL() returns. void StartResolve(const GURL& url) { DCHECK(MessageLoop::current() != io_message_loop_); io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &ResultFuture::DoStartResolve, url)); started_.Wait(); } // Called on |io_message_loop_|. void DoStartResolve(const GURL& url) { DCHECK(MessageLoop::current() == io_message_loop_); int rv = service_->ResolveProxy(url, &proxy_info_, &callback_, &request_); if (rv != net::ERR_IO_PENDING) { // Completed synchronously. OnCompletion(rv); } started_.Signal(); } // Called on |io_message_loop_|. void DoCancel() { DCHECK(MessageLoop::current() == io_message_loop_); if (!did_complete_) service_->CancelPacRequest(request_); cancelled_.Signal(); } // Called on |io_message_loop_|. void OnCompletion(int result) { DCHECK(MessageLoop::current() == io_message_loop_); DCHECK(!did_complete_); did_complete_ = true; result_code_ = result; request_ = NULL; completion_.Signal(); } // The message loop where the ProxyService lives. MessageLoop* io_message_loop_; // The proxy service that started this request. net::ProxyService* service_; // The in-progress request. net::ProxyService::PacRequest* request_; net::CompletionCallbackImpl callback_; base::WaitableEvent completion_; base::WaitableEvent cancelled_; base::WaitableEvent started_; bool did_complete_; // Results from the request. int result_code_; net::ProxyInfo proxy_info_; }; // Wraps a ProxyService running on its own IO thread. class ProxyServiceWithFutures { public: ProxyServiceWithFutures(net::ProxyConfigService* config_service, net::ProxyResolver* resolver) : io_thread_("IO_Thread"), service_(config_service, resolver) { base::Thread::Options options; options.message_loop_type = MessageLoop::TYPE_IO; io_thread_.StartWithOptions(options); } // Start the request on |io_thread_|, and return a handle that can be // used to access the results. The caller is responsible for freeing // the ResultFuture. void ResolveProxy(scoped_refptr* result, const GURL& url) { (*result) = new ResultFuture(io_thread_.message_loop(), &service_); (*result)->StartResolve(url); } void SetProxyScriptFetcher(net::ProxyScriptFetcher* proxy_script_fetcher) { service_.SetProxyScriptFetcher(proxy_script_fetcher); } private: base::Thread io_thread_; net::ProxyService service_; }; // A ProxyResolver which can be set to block upon reaching GetProxyForURL. class BlockableProxyResolver : public net::ProxyResolver { public: BlockableProxyResolver() : net::ProxyResolver(true), should_block_(false), unblocked_(true, true), blocked_(true, false) { } void Block() { should_block_ = true; unblocked_.Reset(); } void Unblock() { should_block_ = false; blocked_.Reset(); unblocked_.Signal(); } void WaitUntilBlocked() { blocked_.Wait(); } // net::ProxyResolver implementation: virtual int GetProxyForURL(const GURL& query_url, const GURL& pac_url, net::ProxyInfo* results) { if (should_block_) { blocked_.Signal(); unblocked_.Wait(); } results->UseNamedProxy(query_url.host()); return net::OK; } private: bool should_block_; base::WaitableEvent unblocked_; base::WaitableEvent blocked_; }; // A mock ProxyResolverWithoutFetch which concatenates the query's host with // the last download PAC contents. This way the result describes what the last // downloaded PAC script's contents were, in addition to the query url itself. class MockProxyResolverWithoutFetch : public net::ProxyResolver { public: MockProxyResolverWithoutFetch() : net::ProxyResolver(false), last_pac_contents_("NONE") {} // net::ProxyResolver implementation: virtual int GetProxyForURL(const GURL& query_url, const GURL& pac_url, net::ProxyInfo* results) { results->UseNamedProxy(last_pac_contents_ + "." + query_url.host()); return net::OK; } virtual void SetPacScript(const std::string& bytes) { last_pac_contents_ = bytes; } private: std::string last_pac_contents_; }; } // namespace // A mock ProxyScriptFetcher. No result will be returned to the fetch client // until we call NotifyFetchCompletion() to set the results. class MockProxyScriptFetcher : public net::ProxyScriptFetcher { public: MockProxyScriptFetcher() : pending_request_loop_(NULL), pending_request_callback_(NULL), pending_request_bytes_(NULL) {} // net::ProxyScriptFetcher implementation. virtual void Fetch(const GURL& url, std::string* bytes, net::CompletionCallback* callback) { DCHECK(!HasPendingRequest()); // Save the caller's information, and have them wait. pending_request_loop_ = MessageLoop::current(); pending_request_url_ = url; pending_request_callback_ = callback; pending_request_bytes_ = bytes; } void NotifyFetchCompletion(int result, const std::string& bytes) { DCHECK(HasPendingRequest()); pending_request_loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &MockProxyScriptFetcher::DoNotifyFetchCompletion, result, bytes)); } virtual void Cancel() {} private: // Runs on |pending_request_loop_|. void DoNotifyFetchCompletion(int result, const std::string& bytes) { DCHECK(HasPendingRequest()); *pending_request_bytes_ = bytes; pending_request_callback_->Run(result); } bool HasPendingRequest() const { return pending_request_loop_ != NULL; } MessageLoop* pending_request_loop_; GURL pending_request_url_; net::CompletionCallback* pending_request_callback_; std::string* pending_request_bytes_; }; // Template specialization so MockProxyScriptFetcher does not have to be // refcounted. template<> void RunnableMethodTraits::RetainCallee( MockProxyScriptFetcher* remover) {} template<> void RunnableMethodTraits::ReleaseCallee( MockProxyScriptFetcher* remover) {} TEST(ProxyServiceTest, Direct) { SyncProxyService service(new MockProxyConfigService, new MockProxyResolver); GURL url("http://www.google.com/"); net::ProxyInfo info; int rv = service.ResolveProxy(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info.is_direct()); } TEST(ProxyServiceTest, PAC) { MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockProxyResolver* resolver = new MockProxyResolver; resolver->info.UseNamedProxy("foopy"); resolver->info_predicate_query_host = "www.google.com"; SyncProxyService service(config_service, resolver); GURL url("http://www.google.com/"); net::ProxyInfo info; int rv = service.ResolveProxy(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy:80", info.proxy_server().ToURI()); } TEST(ProxyServiceTest, PAC_FailoverToDirect) { MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockProxyResolver* resolver = new MockProxyResolver; resolver->info.UseNamedProxy("foopy:8080"); resolver->info_predicate_query_host = "www.google.com"; SyncProxyService service(config_service, resolver); GURL url("http://www.google.com/"); net::ProxyInfo info; int rv = service.ResolveProxy(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy:8080", info.proxy_server().ToURI()); // Now, imagine that connecting to foopy:8080 fails. rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info.is_direct()); } TEST(ProxyServiceTest, PAC_FailsToDownload) { // Test what happens when we fail to download the PAC URL. MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockProxyResolver* resolver = new MockProxyResolver; resolver->info.UseNamedProxy("foopy:8080"); resolver->info_predicate_query_host = "www.google.com"; resolver->fail_get_proxy_for_url = true; SyncProxyService service(config_service, resolver); // The first resolve fails in the MockProxyResolver. GURL url("http://www.google.com/"); net::ProxyInfo info; int rv = service.ResolveProxy(url, &info); EXPECT_EQ(rv, net::ERR_FAILED); // The second resolve request will automatically select direct connect, // because it has cached the configuration as being bad. rv = service.ResolveProxy(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info.is_direct()); resolver->fail_get_proxy_for_url = false; resolver->info.UseNamedProxy("foopy_valid:8080"); // But, if that fails, then we should give the proxy config another shot // since we have never tried it with this URL before. rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); 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"); MockProxyResolver* resolver = new MockProxyResolver; resolver->info.UseNamedProxy("foopy1:8080;foopy2:9090"); resolver->info_predicate_query_host = "www.google.com"; resolver->fail_get_proxy_for_url = false; SyncProxyService service(config_service, resolver); GURL url("http://www.google.com/"); // Get the proxy information. net::ProxyInfo info; int rv = service.ResolveProxy(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info.is_direct()); // The first item is valid. EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); // Fake an error on the proxy. rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); // The second proxy should be specified. EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); // Create a new resolver that returns 3 proxies. The second one is already // known to be bad. config_service->config.pac_url = GURL("http://foopy/proxy.pac"); resolver->info.UseNamedProxy("foopy3:7070;foopy1:8080;foopy2:9090"); resolver->info_predicate_query_host = "www.google.com"; resolver->fail_get_proxy_for_url = false; rv = service.ResolveProxy(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foopy3:7070", info.proxy_server().ToURI()); // We fake another error. It should now try the third one. rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); // Fake another error, the last proxy is gone, the list should now be empty. rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); // We try direct. EXPECT_TRUE(info.is_direct()); // If it fails again, we don't have anything else to try. rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::ERR_FAILED); // We try direct. // 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"); MockProxyResolver* resolver = new MockProxyResolver; resolver->info.UseNamedProxy("foopy1:8080;foopy2:9090"); resolver->info_predicate_query_host = "www.google.com"; resolver->fail_get_proxy_for_url = false; SyncProxyService service(config_service, resolver); GURL url("http://www.google.com/"); // Get the proxy information. net::ProxyInfo info; int rv = service.ResolveProxy(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info.is_direct()); // The first item is valid. 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 = net::ProxyConfig(); config_service->config.pac_url = GURL("http://foopy-new/proxy.pac"); rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); // The first proxy is still there since the configuration changed. EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); // We fake another error. It should now ignore the first one. rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); // We simulate a new configuration. config_service->config = net::ProxyConfig(); config_service->config.pac_url = GURL("http://foopy-new2/proxy.pac"); // We fake anothe error. It should go back to the first proxy. rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); 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"); MockProxyResolver* resolver = new MockProxyResolver; resolver->info.UseNamedProxy("foopy1:8080;foopy2:9090"); resolver->info_predicate_query_host = "www.google.com"; resolver->fail_get_proxy_for_url = false; SyncProxyService service(config_service, resolver); GURL url("http://www.google.com/"); // Get the proxy information. net::ProxyInfo info; int rv = service.ResolveProxy(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info.is_direct()); // The first item is valid. EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); // Fake a proxy error. rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); // 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. net::ProxyInfo info2; resolver->fail_get_proxy_for_url = true; rv = service.ResolveProxy(url, &info2); EXPECT_EQ(rv, net::ERR_FAILED); // No proxy servers are returned. It's a direct connection. EXPECT_TRUE(info2.is_direct()); // The PAC is now fixed and will return a proxy server. // It should also clear the list of bad proxies. resolver->fail_get_proxy_for_url = false; // Try to resolve, it will still return "direct" because we have no reason // to check the config since everything works. net::ProxyInfo info3; rv = service.ResolveProxy(url, &info3); EXPECT_EQ(rv, net::OK); 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. rv = service.ReconsiderProxyAfterError(url, &info3); EXPECT_EQ(rv, net::OK); // The first proxy is still there since the list of bad proxies got cleared. 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. net::ProxyConfig config; config.proxy_rules = "foopy1:8080;foopy2:9090"; config.auto_detect = false; config.proxy_bypass_local_names = true; SyncProxyService service(new MockProxyConfigService(config), new MockProxyResolver()); GURL url("http://www.google.com/"); // Get the proxy information. net::ProxyInfo info; int rv = service.ResolveProxy(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info.is_direct()); SyncProxyService service1(new MockProxyConfigService(config), new MockProxyResolver()); GURL test_url1("local"); net::ProxyInfo info1; rv = service1.ResolveProxy(test_url1, &info1); EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info1.is_direct()); config.proxy_bypass.clear(); config.proxy_bypass.push_back("*.org"); config.proxy_bypass_local_names = true; SyncProxyService service2(new MockProxyConfigService(config), new MockProxyResolver); GURL test_url2("http://www.webkit.org"); net::ProxyInfo info2; rv = service2.ResolveProxy(test_url2, &info2); EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info2.is_direct()); config.proxy_bypass.clear(); config.proxy_bypass.push_back("*.org"); config.proxy_bypass.push_back("7*"); config.proxy_bypass_local_names = true; SyncProxyService service3(new MockProxyConfigService(config), new MockProxyResolver); GURL test_url3("http://74.125.19.147"); net::ProxyInfo info3; rv = service3.ResolveProxy(test_url3, &info3); EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info3.is_direct()); config.proxy_bypass.clear(); config.proxy_bypass.push_back("*.org"); config.proxy_bypass_local_names = true; SyncProxyService service4(new MockProxyConfigService(config), new MockProxyResolver); GURL test_url4("http://www.msn.com"); net::ProxyInfo info4; rv = service4.ResolveProxy(test_url4, &info4); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info4.is_direct()); config.proxy_bypass.clear(); config.proxy_bypass.push_back("*.MSN.COM"); config.proxy_bypass_local_names = true; SyncProxyService service5(new MockProxyConfigService(config), new MockProxyResolver); GURL test_url5("http://www.msnbc.msn.com"); net::ProxyInfo info5; rv = service5.ResolveProxy(test_url5, &info5); EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info5.is_direct()); config.proxy_bypass.clear(); config.proxy_bypass.push_back("*.msn.com"); config.proxy_bypass_local_names = true; SyncProxyService service6(new MockProxyConfigService(config), new MockProxyResolver); GURL test_url6("HTTP://WWW.MSNBC.MSN.COM"); net::ProxyInfo info6; rv = service6.ResolveProxy(test_url6, &info6); EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info6.is_direct()); } TEST(ProxyServiceTest, PerProtocolProxyTests) { net::ProxyConfig config; config.proxy_rules = "http=foopy1:8080;https=foopy2:8080"; config.auto_detect = false; SyncProxyService service1(new MockProxyConfigService(config), new MockProxyResolver); GURL test_url1("http://www.msn.com"); net::ProxyInfo info1; int rv = service1.ResolveProxy(test_url1, &info1); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info1.is_direct()); EXPECT_EQ("foopy1:8080", info1.proxy_server().ToURI()); SyncProxyService service2(new MockProxyConfigService(config), new MockProxyResolver); GURL test_url2("ftp://ftp.google.com"); net::ProxyInfo info2; rv = service2.ResolveProxy(test_url2, &info2); EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info2.is_direct()); EXPECT_EQ("direct://", info2.proxy_server().ToURI()); SyncProxyService service3(new MockProxyConfigService(config), new MockProxyResolver); GURL test_url3("https://webbranch.techcu.com"); net::ProxyInfo info3; rv = service3.ResolveProxy(test_url3, &info3); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info3.is_direct()); EXPECT_EQ("foopy2:8080", info3.proxy_server().ToURI()); config.proxy_rules = "foopy1:8080"; SyncProxyService service4(new MockProxyConfigService(config), new MockProxyResolver); GURL test_url4("www.microsoft.com"); net::ProxyInfo info4; rv = service4.ResolveProxy(test_url4, &info4); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info4.is_direct()); EXPECT_EQ("foopy1:8080", info4.proxy_server().ToURI()); } // Test cancellation of a queued request. TEST(ProxyServiceTest, CancelQueuedRequest) { MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); BlockableProxyResolver* resolver = new BlockableProxyResolver; ProxyServiceWithFutures service(config_service, resolver); // Cause requests to pile up, by having them block in the PAC thread. resolver->Block(); // Start 3 requests. scoped_refptr result1; service.ResolveProxy(&result1, GURL("http://request1")); scoped_refptr result2; service.ResolveProxy(&result2, GURL("http://request2")); scoped_refptr result3; service.ResolveProxy(&result3, GURL("http://request3")); // Wait until the first request has become blocked in the PAC thread. resolver->WaitUntilBlocked(); // Cancel the second request result2->Cancel(); // Unblock the PAC thread. resolver->Unblock(); // Wait for the final request to complete. result3->WaitUntilCompleted(); // Verify that requests ran as expected. EXPECT_TRUE(result1->IsCompleted()); EXPECT_EQ(net::OK, result1->GetResultCode()); EXPECT_EQ("request1:80", result1->GetProxyInfo().proxy_server().ToURI()); EXPECT_FALSE(result2->IsCompleted()); // Cancelled. EXPECT_TRUE(result3->IsCompleted()); EXPECT_EQ(net::OK, result3->GetResultCode()); EXPECT_EQ("request3:80", result3->GetProxyInfo().proxy_server().ToURI()); } // Test cancellation of an in-progress request. TEST(ProxyServiceTest, CancelInprogressRequest) { MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); BlockableProxyResolver* resolver = new BlockableProxyResolver; ProxyServiceWithFutures service(config_service, resolver); // Cause requests to pile up, by having them block in the PAC thread. resolver->Block(); // Start 3 requests. scoped_refptr result1; service.ResolveProxy(&result1, GURL("http://request1")); scoped_refptr result2; service.ResolveProxy(&result2, GURL("http://request2")); scoped_refptr result3; service.ResolveProxy(&result3, GURL("http://request3")); // Wait until the first request has become blocked in the PAC thread. resolver->WaitUntilBlocked(); // Cancel the first request result1->Cancel(); // Unblock the PAC thread. resolver->Unblock(); // Wait for the final request to complete. result3->WaitUntilCompleted(); // Verify that requests ran as expected. EXPECT_FALSE(result1->IsCompleted()); // Cancelled. EXPECT_TRUE(result2->IsCompleted()); EXPECT_EQ(net::OK, result2->GetResultCode()); EXPECT_EQ("request2:80", result2->GetProxyInfo().proxy_server().ToURI()); EXPECT_TRUE(result3->IsCompleted()); EXPECT_EQ(net::OK, result3->GetResultCode()); EXPECT_EQ("request3:80", result3->GetProxyInfo().proxy_server().ToURI()); } // Test the initial PAC download for ProxyResolverWithoutFetch. TEST(ProxyServiceTest, InitialPACScriptDownload) { MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); MockProxyResolverWithoutFetch* resolver = new MockProxyResolverWithoutFetch; ProxyServiceWithFutures service(config_service, resolver); MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher; service.SetProxyScriptFetcher(fetcher); // Start 3 requests. scoped_refptr result1; service.ResolveProxy(&result1, GURL("http://request1")); scoped_refptr result2; service.ResolveProxy(&result2, GURL("http://request2")); scoped_refptr result3; service.ResolveProxy(&result3, GURL("http://request3")); // 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(net::OK, "pac-v1"); // Complete all the requests. result3->WaitUntilCompleted(); EXPECT_TRUE(result1->IsCompleted()); EXPECT_EQ(net::OK, result1->GetResultCode()); EXPECT_EQ("pac-v1.request1:80", result1->GetProxyInfo().proxy_server().ToURI()); EXPECT_TRUE(result2->IsCompleted()); EXPECT_EQ(net::OK, result2->GetResultCode()); EXPECT_EQ("pac-v1.request2:80", result2->GetProxyInfo().proxy_server().ToURI()); EXPECT_TRUE(result3->IsCompleted()); EXPECT_EQ(net::OK, result3->GetResultCode()); EXPECT_EQ("pac-v1.request3:80", result3->GetProxyInfo().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"); MockProxyResolverWithoutFetch* resolver = new MockProxyResolverWithoutFetch; ProxyServiceWithFutures service(config_service, resolver); MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher; service.SetProxyScriptFetcher(fetcher); // Start 3 requests. scoped_refptr result1; service.ResolveProxy(&result1, GURL("http://request1")); scoped_refptr result2; service.ResolveProxy(&result2, GURL("http://request2")); scoped_refptr result3; service.ResolveProxy(&result3, GURL("http://request3")); // Cancel the first 2 requests. result1->Cancel(); result2->Cancel(); // 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(net::OK, "pac-v1"); // Complete all the requests. result3->WaitUntilCompleted(); EXPECT_FALSE(result1->IsCompleted()); // Cancelled. EXPECT_FALSE(result2->IsCompleted()); // Cancelled. EXPECT_TRUE(result3->IsCompleted()); EXPECT_EQ(net::OK, result3->GetResultCode()); EXPECT_EQ("pac-v1.request3:80", result3->GetProxyInfo().proxy_server().ToURI()); }