| Index: net/proxy/proxy_service_unittest.cc
|
| ===================================================================
|
| --- net/proxy/proxy_service_unittest.cc (revision 10072)
|
| +++ net/proxy/proxy_service_unittest.cc (working copy)
|
| @@ -2,6 +2,7 @@
|
| // 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_service.h"
|
| @@ -27,7 +28,8 @@
|
|
|
| class MockProxyResolver : public net::ProxyResolver {
|
| public:
|
| - MockProxyResolver() : fail_get_proxy_for_url(false) {
|
| + MockProxyResolver() : net::ProxyResolver(true),
|
| + fail_get_proxy_for_url(false) {
|
| }
|
|
|
| virtual int GetProxyForURL(const GURL& query_url,
|
| @@ -80,8 +82,272 @@
|
| scoped_refptr<net::SyncProxyServiceHelper> 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<ResultFuture> {
|
| + 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<ResultFuture> 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<ResultFuture>* 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<MockProxyScriptFetcher>::RetainCallee(
|
| + MockProxyScriptFetcher* remover) {}
|
| +template<>
|
| +void RunnableMethodTraits<MockProxyScriptFetcher>::ReleaseCallee(
|
| + MockProxyScriptFetcher* remover) {}
|
| +
|
| // Test parsing from a PAC string.
|
| TEST(ProxyListTest, SetFromPacString) {
|
| const struct {
|
| @@ -419,7 +685,7 @@
|
| 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/");
|
| @@ -537,3 +803,186 @@
|
| 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<ResultFuture> result1;
|
| + service.ResolveProxy(&result1, GURL("http://request1"));
|
| +
|
| + scoped_refptr<ResultFuture> result2;
|
| + service.ResolveProxy(&result2, GURL("http://request2"));
|
| +
|
| + scoped_refptr<ResultFuture> 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<ResultFuture> result1;
|
| + service.ResolveProxy(&result1, GURL("http://request1"));
|
| +
|
| + scoped_refptr<ResultFuture> result2;
|
| + service.ResolveProxy(&result2, GURL("http://request2"));
|
| +
|
| + scoped_refptr<ResultFuture> 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<ResultFuture> result1;
|
| + service.ResolveProxy(&result1, GURL("http://request1"));
|
| +
|
| + scoped_refptr<ResultFuture> result2;
|
| + service.ResolveProxy(&result2, GURL("http://request2"));
|
| +
|
| + scoped_refptr<ResultFuture> 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<ResultFuture> result1;
|
| + service.ResolveProxy(&result1, GURL("http://request1"));
|
| +
|
| + scoped_refptr<ResultFuture> result2;
|
| + service.ResolveProxy(&result2, GURL("http://request2"));
|
| +
|
| + scoped_refptr<ResultFuture> 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());
|
| +}
|
| +
|
|
|