summaryrefslogtreecommitdiffstats
path: root/chrome/browser/net
diff options
context:
space:
mode:
authorericroman@google.com <ericroman@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-31 01:34:20 +0000
committerericroman@google.com <ericroman@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-31 01:34:20 +0000
commit41b2780f0135d23196958816d3adcfd606b80f1e (patch)
tree306a00be7404dfc6d3ff9ef667d35827f87281a2 /chrome/browser/net
parent18a12354e6c3e1edf2f85e11cd7359127d2d3ce2 (diff)
downloadchromium_src-41b2780f0135d23196958816d3adcfd606b80f1e.zip
chromium_src-41b2780f0135d23196958816d3adcfd606b80f1e.tar.gz
chromium_src-41b2780f0135d23196958816d3adcfd606b80f1e.tar.bz2
Move proxy resolve requests out of plugin/renderer process, and into the browser.
Review URL: http://codereview.chromium.org/14142 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@9006 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/net')
-rw-r--r--chrome/browser/net/resolve_proxy_msg_helper.cc89
-rw-r--r--chrome/browser/net/resolve_proxy_msg_helper.h104
-rw-r--r--chrome/browser/net/resolve_proxy_msg_helper_unittest.cc349
3 files changed, 542 insertions, 0 deletions
diff --git a/chrome/browser/net/resolve_proxy_msg_helper.cc b/chrome/browser/net/resolve_proxy_msg_helper.cc
new file mode 100644
index 0000000..dca5ad6
--- /dev/null
+++ b/chrome/browser/net/resolve_proxy_msg_helper.cc
@@ -0,0 +1,89 @@
+// Copyright (c) 2009 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 "chrome/browser/net/resolve_proxy_msg_helper.h"
+
+#include "base/compiler_specific.h"
+#include "chrome/browser/profile.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request_context.h"
+
+ResolveProxyMsgHelper::ResolveProxyMsgHelper(Delegate* delegate,
+ net::ProxyService* proxy_service)
+ : proxy_service_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(callback_(
+ this, &ResolveProxyMsgHelper::OnResolveProxyCompleted)),
+ delegate_(delegate),
+ proxy_service_override_(proxy_service) {
+}
+
+void ResolveProxyMsgHelper::Start(const GURL& url, IPC::Message* reply_msg) {
+ // Enqueue the pending request.
+ pending_requests_.push_back(PendingRequest(url, reply_msg));
+
+ // If nothing is in progress, start.
+ if (pending_requests_.size() == 1)
+ StartPendingRequest();
+}
+
+void ResolveProxyMsgHelper::OnResolveProxyCompleted(int result) {
+ CHECK(!pending_requests_.empty());
+
+ // Notify the delegate of completion.
+ const PendingRequest& completed_req = pending_requests_.front();
+ delegate_->OnResolveProxyCompleted(completed_req.reply_msg,
+ result,
+ proxy_info_.GetAnnotatedProxyList());
+
+ // Clear the current (completed) request.
+ pending_requests_.pop_front();
+ proxy_service_ = NULL;
+
+ // Start the next request.
+ if (!pending_requests_.empty())
+ StartPendingRequest();
+}
+
+void ResolveProxyMsgHelper::StartPendingRequest() {
+ PendingRequest& req = pending_requests_.front();
+
+ // Verify the request wasn't started yet.
+ DCHECK(NULL == req.pac_req);
+ DCHECK(NULL == proxy_service_);
+
+ // Start the request.
+ proxy_service_ = GetProxyService();
+ int result = proxy_service_->ResolveProxy(
+ req.url, &proxy_info_, &callback_, &req.pac_req);
+
+ // Completed synchronously.
+ if (result != net::ERR_IO_PENDING)
+ OnResolveProxyCompleted(result);
+}
+
+net::ProxyService* ResolveProxyMsgHelper::GetProxyService() const {
+ // Unit-tests specify their own proxy service to use.
+ if (proxy_service_override_)
+ return proxy_service_override_;
+
+ // Otherwise use the browser's global proxy service.
+ return Profile::GetDefaultRequestContext()->proxy_service();
+}
+
+ResolveProxyMsgHelper::~ResolveProxyMsgHelper() {
+ // Clear all pending requests.
+ if (!pending_requests_.empty()) {
+ PendingRequest req = pending_requests_.front();
+ proxy_service_->CancelPacRequest(req.pac_req);
+ }
+
+ for (PendingRequestList::iterator it = pending_requests_.begin();
+ it != pending_requests_.end();
+ ++it) {
+ delete it->reply_msg;
+ }
+
+ proxy_service_ = NULL;
+ pending_requests_.clear();
+}
diff --git a/chrome/browser/net/resolve_proxy_msg_helper.h b/chrome/browser/net/resolve_proxy_msg_helper.h
new file mode 100644
index 0000000..bf4c2f8
--- /dev/null
+++ b/chrome/browser/net/resolve_proxy_msg_helper.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2009 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 CHROME_BROWSER_NET_RESOLVE_PROXY_MSG_HELPER_
+#define CHROME_BROWSER_NET_RESOLVE_PROXY_MSG_HELPER_
+
+#include <deque>
+#include <string>
+
+#include "chrome/common/ipc_message.h"
+#include "net/base/completion_callback.h"
+#include "googleurl/src/gurl.h"
+#include "net/proxy/proxy_service.h"
+
+// This class holds the common logic used to respond to the messages:
+// {PluginProcessHostMsg_ResolveProxy, ViewHostMsg_ResolveProxy}.
+//
+// This involves kicking off a ProxyResolve request on the IO thread using
+// the specified proxy service.
+//
+// When the request completes, it calls the delegate's OnProxyResolveCompleted()
+// method, passing it the result (error code + proxy list), as well as the
+// IPC::Message pointer that had been stored.
+//
+// When an instance of ResolveProxyMsgHelper is destroyed, it cancels any
+// outstanding proxy resolve requests with the proxy service. It also deletes
+// the stored IPC::Message pointers for pending requests.
+//
+// This object is expected to live on the IO thread.
+class ResolveProxyMsgHelper {
+ public:
+ class Delegate {
+ public:
+ // Callback for when the proxy resolve request has completed.
+ // |reply_msg| -- The same pointer that the request was started with.
+ // |result| -- The network error code from ProxyService.
+ // |proxy_list| -- The PAC string result from ProxyService.
+ virtual void OnResolveProxyCompleted(IPC::Message* reply_msg,
+ int result,
+ const std::string& proxy_list) = 0;
+ virtual ~Delegate() {}
+ };
+
+ // Construct a ResolveProxyMsgHelper instance that notifies request
+ // completion to |delegate|. Note that |delegate| must be live throughout
+ // our lifespan. If |proxy_service| is NULL, then the current profile's
+ // proxy service will be used.
+ explicit ResolveProxyMsgHelper(Delegate* delegate,
+ net::ProxyService* proxy_service);
+
+ // Resolve proxies for |url|. Completion is notified through the delegate.
+ // If multiple requests are started at the same time, they will run in
+ // FIFO order, with only 1 being outstanding at a time.
+ void Start(const GURL& url, IPC::Message* reply_msg);
+
+ // Destruction cancels the current outstanding request, and clears the
+ // pending queue.
+ ~ResolveProxyMsgHelper();
+
+ private:
+ // Callback for the ProxyService (bound to |callback_|).
+ void OnResolveProxyCompleted(int result);
+
+ // Start the first pending request.
+ void StartPendingRequest();
+
+ // Get the proxy service instance to use.
+ net::ProxyService* GetProxyService() const;
+
+ // A PendingRequest is a resolve request that is in progress, or queued.
+ struct PendingRequest {
+ public:
+ PendingRequest(const GURL& url, IPC::Message* reply_msg) :
+ url(url), reply_msg(reply_msg), pac_req(NULL) { }
+
+ // The URL of the request.
+ GURL url;
+
+ // Data to pass back to the delegate on completion (we own it until then).
+ IPC::Message* reply_msg;
+
+ // Handle for cancelling the current request if it has started (else NULL).
+ net::ProxyService::PacRequest* pac_req;
+ };
+
+ // Members for the current outstanding proxy request.
+ net::ProxyService* proxy_service_;
+ net::CompletionCallbackImpl<ResolveProxyMsgHelper> callback_;
+ net::ProxyInfo proxy_info_;
+
+ // FIFO queue of pending requests. The first entry is always the current one.
+ typedef std::deque<PendingRequest> PendingRequestList;
+ PendingRequestList pending_requests_;
+
+ Delegate* delegate_;
+
+ // Specified by unit-tests, to use this proxy service in place of the
+ // global one.
+ net::ProxyService* proxy_service_override_;
+};
+
+#endif // CHROME_BROWSER_NET_RESOLVE_PROXY_MSG_HELPER_
+
diff --git a/chrome/browser/net/resolve_proxy_msg_helper_unittest.cc b/chrome/browser/net/resolve_proxy_msg_helper_unittest.cc
new file mode 100644
index 0000000..dd70713
--- /dev/null
+++ b/chrome/browser/net/resolve_proxy_msg_helper_unittest.cc
@@ -0,0 +1,349 @@
+// Copyright (c) 2009 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 "chrome/browser/net/resolve_proxy_msg_helper.h"
+
+#include "base/waitable_event.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// This ProxyConfigService always returns "http://pac" as the PAC url to use.
+class MockProxyConfigService: public net::ProxyConfigService {
+ public:
+ virtual int GetProxyConfig(net::ProxyConfig* results) {
+ results->pac_url = GURL("http://pac");
+ return net::OK;
+ }
+};
+
+// This PAC resolver always returns the hostname of the query URL as the
+// proxy to use. The Block() method will make GetProxyForURL() hang until
+// Unblock() is called.
+class MockProxyResolver : public net::ProxyResolver {
+ public:
+ explicit MockProxyResolver() : event_(false, false), is_blocked_(false) {
+ }
+
+ virtual int GetProxyForURL(const GURL& query_url,
+ const GURL& /*pac_url*/,
+ net::ProxyInfo* results) {
+ if (is_blocked_)
+ event_.Wait();
+ results->UseNamedProxy(query_url.host());
+ return net::OK;
+ }
+
+ void Block() {
+ is_blocked_ = true;
+ event_.Reset();
+ }
+
+ void Unblock() {
+ is_blocked_ = false;
+ event_.Signal();
+ }
+
+ private:
+ base::WaitableEvent event_;
+ bool is_blocked_;
+};
+
+// This struct holds the values that were passed to
+// Delegate::OnResolveProxyCompleted(). The caller should use WaitUntilDone()
+// to block until the result has been populated.
+struct ResultFuture {
+ public:
+ ResultFuture()
+ : reply_msg(NULL),
+ error_code(0),
+ started_(false, false),
+ completed_(false, false) {
+ }
+
+ // Wait until the request has completed. In other words we have invoked:
+ // ResolveProxyMsgHelper::Delegate::OnResolveProxyCompleted.
+ void WaitUntilDone() {
+ completed_.Wait();
+ }
+
+ // Wait until the request has been sent to ResolveProxyMsgHelper.
+ void WaitUntilStarted() {
+ started_.Wait();
+ }
+
+ bool TimedWaitUntilDone(const base::TimeDelta& max_time) {
+ return completed_.TimedWait(max_time);
+ }
+
+ // These fields are only valid after returning from WaitUntilDone().
+ IPC::Message* reply_msg;
+ int error_code;
+ std::string proxy_list;
+
+ private:
+ friend class AsyncRequestRunner;
+ base::WaitableEvent started_;
+ base::WaitableEvent completed_;
+};
+
+// This class lives on the io thread. It starts async requests using the
+// class under test (ResolveProxyMsgHelper), and signals the result future on
+// completion.
+class AsyncRequestRunner : public ResolveProxyMsgHelper::Delegate {
+ public:
+ AsyncRequestRunner(net::ProxyService* proxy_service) {
+ resolve_proxy_msg_helper_.reset(
+ new ResolveProxyMsgHelper(this, proxy_service));
+ }
+
+ void Start(ResultFuture* future, const GURL& url, IPC::Message* reply_msg) {
+ futures_.push_back(future);
+ resolve_proxy_msg_helper_->Start(url, reply_msg);
+
+ // Notify of request start.
+ future->started_.Signal();
+ }
+
+ virtual void OnResolveProxyCompleted(IPC::Message* reply_msg,
+ int error_code,
+ const std::string& proxy_list) {
+ // Update the result future for this request (top of queue), and signal it.
+ ResultFuture* future = futures_.front();
+ futures_.pop_front();
+
+ future->reply_msg = reply_msg;
+ future->error_code = error_code;
+ future->proxy_list = proxy_list;
+
+ // Notify of request completion.
+ future->completed_.Signal();
+ }
+
+ private:
+ std::deque<ResultFuture*> futures_;
+ scoped_ptr<ResolveProxyMsgHelper> resolve_proxy_msg_helper_;
+};
+
+// Helper class to start async requests on an io thread, and return a
+// result future. The caller then uses ResultFuture::WaitUntilDone() to
+// get at the results. It "bridges" the originating thread with the helper
+// io thread.
+class RunnerBridge {
+ public:
+ RunnerBridge() : io_thread_("io_thread"), done_(true, false) {
+ // Start an io thread where we will run the async requests.
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ io_thread_.StartWithOptions(options);
+
+ // Construct the state that lives on io thread.
+ io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RunnerBridge::DoConstruct));
+ done_.Wait();
+ }
+
+ // Start an async request on the io thread.
+ ResultFuture* Start(const GURL& url, IPC::Message* reply_msg) {
+ ResultFuture* future = new ResultFuture();
+
+ io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ async_runner_, &AsyncRequestRunner::Start, future, url, reply_msg));
+
+ return future;
+ }
+
+ void DestroyAsyncRunner() {
+ io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RunnerBridge::DoDestroyAsyncRunner));
+ done_.Wait();
+ }
+
+ ~RunnerBridge() {
+ io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RunnerBridge::DoDestroy));
+ done_.Wait();
+ }
+
+ MockProxyResolver* proxy_resolver() {
+ return proxy_resolver_;
+ }
+
+ // Called from io thread.
+ void DoConstruct() {
+ proxy_resolver_ = new MockProxyResolver();
+ proxy_service_ = new net::ProxyService(new MockProxyConfigService(),
+ proxy_resolver_);
+ async_runner_ = new AsyncRequestRunner(proxy_service_);
+ done_.Signal();
+ }
+
+ // Called from io thread.
+ void DoDestroy() {
+ delete async_runner_;
+ delete proxy_service_;
+ done_.Signal();
+ }
+
+ // Called from io thread.
+ void DoDestroyAsyncRunner() {
+ delete async_runner_;
+ async_runner_ = NULL;
+ done_.Signal();
+ }
+
+ private:
+ base::Thread io_thread_;
+ base::WaitableEvent done_;
+
+ net::ProxyService* proxy_service_;
+ MockProxyResolver* proxy_resolver_; // Owned by proxy_service_.
+
+ AsyncRequestRunner* async_runner_;
+};
+
+// Avoid the need to have an AddRef / Release
+template<>
+void RunnableMethodTraits<RunnerBridge>::RetainCallee(RunnerBridge*) {}
+template<>
+void RunnableMethodTraits<RunnerBridge>::ReleaseCallee(RunnerBridge*) {}
+
+template<>
+void RunnableMethodTraits<AsyncRequestRunner>::RetainCallee(AsyncRequestRunner*) {}
+template<>
+void RunnableMethodTraits<AsyncRequestRunner>::ReleaseCallee(AsyncRequestRunner*) {}
+
+
+// Issue three sequential requests -- each should succeed.
+TEST(ResolveProxyMsgHelperTest, Sequential) {
+ RunnerBridge runner;
+
+ GURL url1("http://www.google1.com/");
+ GURL url2("http://www.google2.com/");
+ GURL url3("http://www.google3.com/");
+
+ scoped_ptr<IPC::Message> msg1(new IPC::Message());
+ scoped_ptr<IPC::Message> msg2(new IPC::Message());
+ scoped_ptr<IPC::Message> msg3(new IPC::Message());
+
+ // Execute each request sequentially (so there are never 2 requests
+ // outstanding at the same time).
+
+ scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1.get()));
+ result1->WaitUntilDone();
+
+ scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2.get()));
+ result2->WaitUntilDone();
+
+ scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3.get()));
+ result3->WaitUntilDone();
+
+ // Check that each request gave the expected result.
+
+ EXPECT_EQ(msg1.get(), result1->reply_msg);
+ EXPECT_EQ(net::OK, result1->error_code);
+ EXPECT_EQ("PROXY www.google1.com", result1->proxy_list);
+
+ EXPECT_EQ(msg2.get(), result2->reply_msg);
+ EXPECT_EQ(net::OK, result2->error_code);
+ EXPECT_EQ("PROXY www.google2.com", result2->proxy_list);
+
+ EXPECT_EQ(msg3.get(), result3->reply_msg);
+ EXPECT_EQ(net::OK, result3->error_code);
+ EXPECT_EQ("PROXY www.google3.com", result3->proxy_list);
+}
+
+// Issue a request while one is already in progress -- should be queued.
+TEST(ResolveProxyMsgHelperTest, QueueRequests) {
+ RunnerBridge runner;
+
+ GURL url1("http://www.google1.com/");
+ GURL url2("http://www.google2.com/");
+ GURL url3("http://www.google3.com/");
+
+ scoped_ptr<IPC::Message> msg1(new IPC::Message());
+ scoped_ptr<IPC::Message> msg2(new IPC::Message());
+ scoped_ptr<IPC::Message> msg3(new IPC::Message());
+
+ // Make the proxy resolver hang on the next request.
+ runner.proxy_resolver()->Block();
+
+ // Start three requests. Since the proxy resolver is hung, the second two
+ // will be pending.
+
+ scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1.get()));
+ scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2.get()));
+ scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3.get()));
+
+ // Wait for the final request to have been scheduled. Otherwise we may rush
+ // to calling Unblock() without actually having blocked anything.
+ result3->WaitUntilStarted();
+
+ // Unblock the proxy service so requests 1-3 can complete.
+ runner.proxy_resolver()->Unblock();
+
+ // Wait for all the requests to finish (they run in FIFO order).
+ result3->WaitUntilDone();
+
+ // Check that each call invoked the callback with the right parameters.
+
+ EXPECT_EQ(msg1.get(), result1->reply_msg);
+ EXPECT_EQ(net::OK, result1->error_code);
+ EXPECT_EQ("PROXY www.google1.com", result1->proxy_list);
+
+ EXPECT_EQ(msg2.get(), result2->reply_msg);
+ EXPECT_EQ(net::OK, result2->error_code);
+ EXPECT_EQ("PROXY www.google2.com", result2->proxy_list);
+
+ EXPECT_EQ(msg3.get(), result3->reply_msg);
+ EXPECT_EQ(net::OK, result3->error_code);
+ EXPECT_EQ("PROXY www.google3.com", result3->proxy_list);
+}
+
+// Delete the helper while a request is in progress, and others are pending.
+TEST(ResolveProxyMsgHelperTest, CancelPendingRequests) {
+ RunnerBridge runner;
+
+ GURL url1("http://www.google1.com/");
+ GURL url2("http://www.google2.com/");
+ GURL url3("http://www.google3.com/");
+
+ // NOTE: these are not scoped ptr, since they will be deleted by the
+ // request's cancellation.
+ IPC::Message* msg1 = new IPC::Message();
+ IPC::Message* msg2 = new IPC::Message();
+ IPC::Message* msg3 = new IPC::Message();
+
+ // Make the next request block.
+ runner.proxy_resolver()->Block();
+
+ // Start three requests; since the first one blocked, the other two should
+ // be pending.
+
+ scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1));
+ scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2));
+ scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3));
+
+ result3->WaitUntilStarted();
+
+ // Delete the underlying ResolveProxyMsgHelper -- this should cancel all
+ // the requests which are outstanding.
+ runner.DestroyAsyncRunner();
+
+ // Unblocking the proxy resolver means the three requests can complete --
+ // however they should not try to notify the delegate since we have already
+ // deleted the helper.
+ runner.proxy_resolver()->Unblock();
+
+ // Check that none of the requests were sent to the delegate.
+ EXPECT_FALSE(
+ result1->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2)));
+ EXPECT_FALSE(
+ result2->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2)));
+ EXPECT_FALSE(
+ result3->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2)));
+
+ // It should also be the case that msg1, msg2, msg3 were deleted by the
+ // cancellation. (Else will show up as a leak in Purify).
+}
+