Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(475)

Unified Diff: net/proxy/proxy_service_unittest.cc

Issue 21328: Add support to ProxyService for downloading a PAC script on behalf of the Pro... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Add missing files (for constructor boolean flag fall-out) Created 11 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/proxy/proxy_service.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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());
+}
+
« no previous file with comments | « net/proxy/proxy_service.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698