Commit 07ff1408 authored by amistry's avatar amistry Committed by Commit bot

Implementation of ProxyResolver that uses a Mojo service.

BUG=11746

Review URL: https://codereview.chromium.org/917863005

Cr-Commit-Position: refs/heads/master@{#319848}
parent 9731c54d
...@@ -798,12 +798,16 @@ if (use_v8_in_net && !is_android) { ...@@ -798,12 +798,16 @@ if (use_v8_in_net && !is_android) {
sources = [ sources = [
"dns/mojo_host_resolver_impl.cc", "dns/mojo_host_resolver_impl.cc",
"dns/mojo_host_resolver_impl.h", "dns/mojo_host_resolver_impl.h",
"proxy/mojo_proxy_resolver_factory.h",
"proxy/proxy_resolver_mojo.cc",
"proxy/proxy_resolver_mojo.h",
] ]
public_deps = [ public_deps = [
":mojo_type_converters", ":mojo_type_converters",
":net", ":net",
"//base", "//base",
"//mojo/common",
"//net/interfaces", "//net/interfaces",
"//third_party/mojo/src/mojo/public/cpp/bindings", "//third_party/mojo/src/mojo/public/cpp/bindings",
] ]
...@@ -1405,6 +1409,7 @@ if (!is_android && !is_win && !is_mac) { ...@@ -1405,6 +1409,7 @@ if (!is_android && !is_win && !is_mac) {
"dns/mojo_host_resolver_impl_unittest.cc", "dns/mojo_host_resolver_impl_unittest.cc",
"proxy/mojo_proxy_resolver_factory_impl_unittest.cc", "proxy/mojo_proxy_resolver_factory_impl_unittest.cc",
"proxy/mojo_proxy_resolver_impl_unittest.cc", "proxy/mojo_proxy_resolver_impl_unittest.cc",
"proxy/proxy_resolver_mojo_unittest.cc",
] ]
} }
......
...@@ -627,6 +627,9 @@ NET_ERROR(HTTP_1_1_REQUIRED, -365) ...@@ -627,6 +627,9 @@ NET_ERROR(HTTP_1_1_REQUIRED, -365)
// HTTP_1_1_REQUIRED error code received on HTTP/2 session to proxy. // HTTP_1_1_REQUIRED error code received on HTTP/2 session to proxy.
NET_ERROR(PROXY_HTTP_1_1_REQUIRED, -366) NET_ERROR(PROXY_HTTP_1_1_REQUIRED, -366)
// The PAC script terminated fatally and must be reloaded.
NET_ERROR(PAC_SCRIPT_TERMINATED, -367)
// The cache does not have the requested entry. // The cache does not have the requested entry.
NET_ERROR(CACHE_MISS, -400) NET_ERROR(CACHE_MISS, -400)
......
...@@ -722,6 +722,7 @@ ...@@ -722,6 +722,7 @@
'dns/mojo_host_resolver_impl_unittest.cc', 'dns/mojo_host_resolver_impl_unittest.cc',
'proxy/mojo_proxy_resolver_factory_impl_unittest.cc', 'proxy/mojo_proxy_resolver_factory_impl_unittest.cc',
'proxy/mojo_proxy_resolver_impl_unittest.cc', 'proxy/mojo_proxy_resolver_impl_unittest.cc',
'proxy/proxy_resolver_mojo_unittest.cc',
], ],
}, },
], ],
...@@ -1277,12 +1278,17 @@ ...@@ -1277,12 +1278,17 @@
'sources': [ 'sources': [
'dns/mojo_host_resolver_impl.cc', 'dns/mojo_host_resolver_impl.cc',
'dns/mojo_host_resolver_impl.h', 'dns/mojo_host_resolver_impl.h',
'proxy/mojo_proxy_resolver_factory.h',
'proxy/proxy_resolver_mojo.cc',
'proxy/proxy_resolver_mojo.h',
], ],
'dependencies': [ 'dependencies': [
'mojo_type_converters', 'mojo_type_converters',
'net', 'net',
'net_interfaces', 'net_interfaces',
'../mojo/mojo_base.gyp:mojo_common_lib',
'../mojo/mojo_base.gyp:mojo_environment_chromium', '../mojo/mojo_base.gyp:mojo_environment_chromium',
'../mojo/mojo_base.gyp:mojo_url_type_converters',
'../third_party/mojo/mojo_public.gyp:mojo_cpp_bindings', '../third_party/mojo/mojo_public.gyp:mojo_cpp_bindings',
], ],
}, },
......
...@@ -1449,6 +1449,7 @@ ...@@ -1449,6 +1449,7 @@
'proxy/proxy_config_unittest.cc', 'proxy/proxy_config_unittest.cc',
'proxy/proxy_info_unittest.cc', 'proxy/proxy_info_unittest.cc',
'proxy/proxy_list_unittest.cc', 'proxy/proxy_list_unittest.cc',
'proxy/proxy_resolver_mojo_unittest.cc',
'proxy/proxy_resolver_v8_tracing_unittest.cc', 'proxy/proxy_resolver_v8_tracing_unittest.cc',
'proxy/proxy_resolver_v8_unittest.cc', 'proxy/proxy_resolver_v8_unittest.cc',
'proxy/proxy_script_decider_unittest.cc', 'proxy/proxy_script_decider_unittest.cc',
......
// Copyright 2015 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_MOJO_PROXY_RESOLVER_FACTORY_H_
#define NET_PROXY_MOJO_PROXY_RESOLVER_FACTORY_H_
#include "net/interfaces/host_resolver_service.mojom.h"
#include "net/interfaces/proxy_resolver_service.mojom.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h"
namespace net {
// Factory for connecting to Mojo ProxyResolver services.
class MojoProxyResolverFactory {
public:
// Connect to a new ProxyResolver service using request |req|, using
// |host_resolver| as the DNS resolver.
// Note: The connection request |req| may be resolved asynchronously.
virtual void Create(mojo::InterfaceRequest<interfaces::ProxyResolver> req,
interfaces::HostResolverPtr host_resolver) = 0;
};
} // namespace net
#endif // NET_PROXY_MOJO_PROXY_RESOLVER_FACTORY_H_
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "net/base/host_port_pair.h" #include "net/base/host_port_pair.h"
#include "net/proxy/proxy_info.h"
#include "net/proxy/proxy_server.h" #include "net/proxy/proxy_server.h"
namespace net { namespace net {
...@@ -80,4 +81,17 @@ TypeConverter<net::ProxyServer, net::interfaces::ProxyServerPtr>::Convert( ...@@ -80,4 +81,17 @@ TypeConverter<net::ProxyServer, net::interfaces::ProxyServerPtr>::Convert(
net::HostPortPair(obj->host, obj->port)); net::HostPortPair(obj->host, obj->port));
} }
// static
net::ProxyInfo
TypeConverter<net::ProxyInfo, mojo::Array<net::interfaces::ProxyServerPtr>>::
Convert(const mojo::Array<net::interfaces::ProxyServerPtr>& obj) {
net::ProxyList proxy_list;
for (size_t i = 0; i < obj.size(); i++) {
proxy_list.AddProxyServer(obj[i].To<net::ProxyServer>());
}
net::ProxyInfo info;
info.UseProxyList(proxy_list);
return info;
}
} // namespace mojo } // namespace mojo
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "third_party/mojo/src/mojo/public/cpp/bindings/type_converter.h" #include "third_party/mojo/src/mojo/public/cpp/bindings/type_converter.h"
namespace net { namespace net {
class ProxyInfo;
class ProxyServer; class ProxyServer;
} }
...@@ -24,6 +25,13 @@ struct TypeConverter<net::ProxyServer, net::interfaces::ProxyServerPtr> { ...@@ -24,6 +25,13 @@ struct TypeConverter<net::ProxyServer, net::interfaces::ProxyServerPtr> {
static net::ProxyServer Convert(const net::interfaces::ProxyServerPtr& obj); static net::ProxyServer Convert(const net::interfaces::ProxyServerPtr& obj);
}; };
template <>
struct TypeConverter<net::ProxyInfo,
mojo::Array<net::interfaces::ProxyServerPtr>> {
static net::ProxyInfo Convert(
const mojo::Array<net::interfaces::ProxyServerPtr>& obj);
};
} // namespace mojo } // namespace mojo
#endif // NET_PROXY_MOJO_PROXY_TYPE_CONVERTERS_H_ #endif // NET_PROXY_MOJO_PROXY_TYPE_CONVERTERS_H_
// Copyright 2015 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 "net/proxy/proxy_resolver_mojo.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "mojo/common/common_type_converters.h"
#include "mojo/common/url_type_converters.h"
#include "net/base/net_errors.h"
#include "net/dns/mojo_host_resolver_impl.h"
#include "net/proxy/mojo_proxy_resolver_factory.h"
#include "net/proxy/mojo_proxy_type_converters.h"
#include "net/proxy/proxy_info.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/error_handler.h"
namespace net {
class ProxyResolverMojo::Job : public interfaces::ProxyResolverRequestClient,
public mojo::ErrorHandler {
public:
Job(ProxyResolverMojo* resolver,
const GURL& url,
ProxyInfo* results,
const net::CompletionCallback& callback);
~Job() override;
// Cancels the job and prevents the callback from being run.
void Cancel();
private:
// Overridden from mojo::ErrorHandler:
void OnConnectionError() override;
// Overridden from interfaces::ProxyResolverRequestClient:
void ReportResult(
int32_t error,
mojo::Array<interfaces::ProxyServerPtr> proxy_servers) override;
ProxyResolverMojo* resolver_;
const GURL url_;
ProxyInfo* results_;
net::CompletionCallback callback_;
base::ThreadChecker thread_checker_;
mojo::Binding<interfaces::ProxyResolverRequestClient> binding_;
};
ProxyResolverMojo::Job::Job(ProxyResolverMojo* resolver,
const GURL& url,
ProxyInfo* results,
const net::CompletionCallback& callback)
: resolver_(resolver),
url_(url),
results_(results),
callback_(callback),
binding_(this) {
binding_.set_error_handler(this);
interfaces::ProxyResolverRequestClientPtr client_ptr;
binding_.Bind(mojo::GetProxy(&client_ptr));
resolver_->mojo_proxy_resolver_ptr_->GetProxyForUrl(mojo::String::From(url_),
client_ptr.Pass());
}
ProxyResolverMojo::Job::~Job() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!callback_.is_null())
callback_.Run(ERR_PAC_SCRIPT_TERMINATED);
}
void ProxyResolverMojo::Job::Cancel() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!callback_.is_null());
callback_.Reset();
}
void ProxyResolverMojo::Job::OnConnectionError() {
DCHECK(thread_checker_.CalledOnValidThread());
DVLOG(1) << "ProxyResolverMojo::Job::OnConnectionError";
resolver_->RemoveJob(this);
}
void ProxyResolverMojo::Job::ReportResult(
int32_t error,
mojo::Array<interfaces::ProxyServerPtr> proxy_servers) {
DCHECK(thread_checker_.CalledOnValidThread());
DVLOG(1) << "ProxyResolverMojo::Job::ReportResult: " << error;
if (error == OK) {
*results_ = proxy_servers.To<ProxyInfo>();
DVLOG(1) << "Servers: " << results_->ToPacString();
}
callback_.Run(error);
callback_.Reset();
resolver_->RemoveJob(this);
}
ProxyResolverMojo::ProxyResolverMojo(
MojoProxyResolverFactory* mojo_proxy_resolver_factory,
HostResolver* host_resolver)
: ProxyResolver(true /* |expects_pac_bytes| */),
mojo_proxy_resolver_factory_(mojo_proxy_resolver_factory),
host_resolver_(host_resolver) {
}
ProxyResolverMojo::~ProxyResolverMojo() {
DCHECK(thread_checker_.CalledOnValidThread());
// All pending requests should have been cancelled.
DCHECK(pending_jobs_.empty());
DCHECK(set_pac_script_callback_.IsCancelled());
}
void ProxyResolverMojo::CancelSetPacScript() {
DCHECK(thread_checker_.CalledOnValidThread());
set_pac_script_callback_.Cancel();
}
int ProxyResolverMojo::SetPacScript(
const scoped_refptr<ProxyResolverScriptData>& pac_script,
const net::CompletionCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(set_pac_script_callback_.IsCancelled());
DCHECK(!callback.is_null());
if (pac_script->type() != ProxyResolverScriptData::TYPE_SCRIPT_CONTENTS ||
pac_script->utf16().empty()) {
return ERR_PAC_SCRIPT_FAILED;
}
DVLOG(1) << "ProxyResolverMojo::SetPacScript: " << pac_script->utf16();
set_pac_script_callback_.Reset(
base::Bind(&ProxyResolverMojo::OnSetPacScriptDone, base::Unretained(this),
pac_script, callback));
if (!mojo_proxy_resolver_ptr_)
SetUpServices();
mojo_proxy_resolver_ptr_->SetPacScript(
mojo::String::From(pac_script->utf16()),
set_pac_script_callback_.callback());
return ERR_IO_PENDING;
}
void ProxyResolverMojo::OnSetPacScriptDone(
const scoped_refptr<ProxyResolverScriptData>& pac_script,
const net::CompletionCallback& callback,
int32_t result) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!set_pac_script_callback_.IsCancelled());
DVLOG(1) << "ProxyResolverMojo::OnSetPacScriptDone: " << result;
callback.Run(result);
set_pac_script_callback_.Cancel();
}
void ProxyResolverMojo::SetUpServices() {
DCHECK(thread_checker_.CalledOnValidThread());
// A Mojo service implementation must outlive its binding.
mojo_host_resolver_binding_.reset();
interfaces::HostResolverPtr mojo_host_resolver_ptr;
mojo_host_resolver_.reset(new MojoHostResolverImpl(host_resolver_));
mojo_host_resolver_binding_.reset(new mojo::Binding<interfaces::HostResolver>(
mojo_host_resolver_.get(), mojo::GetProxy(&mojo_host_resolver_ptr)));
mojo_proxy_resolver_ptr_.reset();
mojo_proxy_resolver_factory_->Create(
mojo::GetProxy(&mojo_proxy_resolver_ptr_), mojo_host_resolver_ptr.Pass());
mojo_proxy_resolver_ptr_.set_error_handler(this);
}
void ProxyResolverMojo::AbortPendingRequests() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!set_pac_script_callback_.IsCancelled()) {
set_pac_script_callback_.callback().Run(ERR_PAC_SCRIPT_TERMINATED);
set_pac_script_callback_.Cancel();
}
// Need to use this loop because deleting a Job will cause its callback to be
// run with a failure error code, which may cause other Jobs to be deleted.
while (!pending_jobs_.empty()) {
auto it = pending_jobs_.begin();
Job* job = *it;
pending_jobs_.erase(it);
// Deleting the job will cause its completion callback to be run with an
// ERR_PAC_SCRIPT_TERMINATED error.
delete job;
}
}
void ProxyResolverMojo::OnConnectionError() {
DCHECK(thread_checker_.CalledOnValidThread());
DVLOG(1) << "ProxyResolverMojo::OnConnectionError";
// Disconnect from the Mojo proxy resolver service. An attempt to reconnect
// will happen on the next |SetPacScript()| request.
mojo_proxy_resolver_ptr_.reset();
// Aborting requests will invoke their callbacks, which may call
// |SetPacScript()| and re-create the connection. So disconnect from the Mojo
// service (above) before aborting the pending requests.
AbortPendingRequests();
}
void ProxyResolverMojo::RemoveJob(Job* job) {
DCHECK(thread_checker_.CalledOnValidThread());
size_t num_erased = pending_jobs_.erase(job);
DCHECK(num_erased);
delete job;
}
int ProxyResolverMojo::GetProxyForURL(const GURL& url,
ProxyInfo* results,
const net::CompletionCallback& callback,
RequestHandle* request,
const BoundNetLog& net_log) {
DCHECK(thread_checker_.CalledOnValidThread());
// If the Mojo service is not connected, fail. The Mojo service is connected
// when the script is set, which must be done after construction and after a
// previous request returns ERR_PAC_SCRIPT_TERMINATED due to the Mojo proxy
// resolver process crashing.
if (!mojo_proxy_resolver_ptr_) {
DVLOG(1) << "ProxyResolverMojo::GetProxyForURL: Mojo not connected";
return ERR_PAC_SCRIPT_TERMINATED;
}
Job* job = new Job(this, url, results, callback);
bool inserted = pending_jobs_.insert(job).second;
DCHECK(inserted);
*request = job;
return ERR_IO_PENDING;
}
void ProxyResolverMojo::CancelRequest(RequestHandle request) {
DCHECK(thread_checker_.CalledOnValidThread());
Job* job = static_cast<Job*>(request);
DCHECK(job);
job->Cancel();
RemoveJob(job);
}
LoadState ProxyResolverMojo::GetLoadState(RequestHandle request) const {
// TODO(amistry): Implement real LoadState.
return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
}
} // namespace net
// Copyright 2015 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_PROXY_RESOLVER_MOJO_H_
#define NET_PROXY_PROXY_RESOLVER_MOJO_H_
#include <set>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/thread_checker.h"
#include "net/base/completion_callback.h"
#include "net/base/load_states.h"
#include "net/interfaces/host_resolver_service.mojom.h"
#include "net/interfaces/proxy_resolver_service.mojom.h"
#include "net/proxy/proxy_resolver.h"
#include "net/proxy/proxy_resolver_script_data.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h"
class GURL;
namespace net {
class BoundNetLog;
class HostResolver;
class ProxyInfo;
class MojoProxyResolverFactory;
// Implementation of ProxyResolver that connects to a Mojo service to evaluate
// PAC scripts. This implementation only knows about Mojo services, and
// therefore that service may live in or out of process.
//
// This implementation handles disconnections from the Mojo service (i.e. if the
// service is out-of-process and that process crashes) and transparently
// re-connects to a new service.
class ProxyResolverMojo : public ProxyResolver, public mojo::ErrorHandler {
public:
// Constructs a ProxyResolverMojo and connects to a new Mojo proxy resolver
// service using |mojo_proxy_resolver_factory|. The new Mojo proxy resolver
// uses |host_resolver| as the DNS resolver. |mojo_proxy_resolver_factory|
// and |host_resolver| are not owned and must outlive this.
// TODO(amistry): Add ProxyResolverErrorObserver and NetLog.
ProxyResolverMojo(MojoProxyResolverFactory* mojo_proxy_resolver_factory,
HostResolver* host_resolver);
~ProxyResolverMojo() override;
// ProxyResolver implementation:
int GetProxyForURL(const GURL& url,
ProxyInfo* results,
const net::CompletionCallback& callback,
RequestHandle* request,
const BoundNetLog& net_log) override;
void CancelRequest(RequestHandle request) override;
LoadState GetLoadState(RequestHandle request) const override;
void CancelSetPacScript() override;
int SetPacScript(const scoped_refptr<ProxyResolverScriptData>& pac_script,
const net::CompletionCallback& callback) override;
private:
class Job;
// Overridden from mojo::ErrorHandler:
void OnConnectionError() override;
// Callback for ProxyResolverService::SetPacScript.
void OnSetPacScriptDone(
const scoped_refptr<ProxyResolverScriptData>& pac_script,
const net::CompletionCallback& callback,
int32_t result);
void SetUpServices();
void AbortPendingRequests();
void RemoveJob(Job* job);
// Connection to the Mojo proxy resolver.
interfaces::ProxyResolverPtr mojo_proxy_resolver_ptr_;
// Mojo host resolver service and binding.
scoped_ptr<interfaces::HostResolver> mojo_host_resolver_;
scoped_ptr<mojo::Binding<interfaces::HostResolver>>
mojo_host_resolver_binding_;
// Factory for connecting to new Mojo proxy resolvers.
// Not owned.
MojoProxyResolverFactory* mojo_proxy_resolver_factory_;
// DNS resolver, saved for creating a new Mojo proxy resolver when the
// existing one disconnects (i.e. when utility process crashes).
HostResolver* host_resolver_;
std::set<Job*> pending_jobs_;
net::CancelableCompletionCallback set_pac_script_callback_;
base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(ProxyResolverMojo);
};
} // namespace net
#endif // NET_PROXY_PROXY_RESOLVER_MOJO_H_
// Copyright 2015 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 "net/proxy/proxy_resolver_mojo.h"
#include <list>
#include <map>
#include <queue>
#include <string>
#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "mojo/common/common_type_converters.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/base/test_completion_callback.h"
#include "net/dns/mock_host_resolver.h"
#include "net/proxy/mojo_proxy_resolver_factory.h"
#include "net/proxy/mojo_proxy_type_converters.h"
#include "net/proxy/proxy_info.h"
#include "net/proxy/proxy_resolver_script_data.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/error_handler.h"
#include "url/gurl.h"
namespace net {
namespace {
const char kScriptData[] = "FooBarBaz";
const char kScriptData2[] = "BlahBlahBlah";
const char kExampleUrl[] = "http://www.example.com";
struct SetPacScriptAction {
enum Action {
COMPLETE,
DISCONNECT,
};
static SetPacScriptAction ReturnResult(Error error) {
SetPacScriptAction result;
result.error = error;
return result;
}
static SetPacScriptAction Disconnect() {
SetPacScriptAction result;
result.action = DISCONNECT;
return result;
}
Action action = COMPLETE;
Error error = OK;
};
struct GetProxyForUrlAction {
enum Action {
COMPLETE,
// Drop the request by closing the reply channel.
DROP,
// Disconnect the service.
DISCONNECT,
// Wait for the client pipe to be disconnected.
WAIT_FOR_CLIENT_DISCONNECT,
};
GetProxyForUrlAction() {}
GetProxyForUrlAction(const GetProxyForUrlAction& old) {
action = old.action;
error = old.error;
expected_url = old.expected_url;
proxy_servers = old.proxy_servers.Clone();
}
static GetProxyForUrlAction ReturnError(const GURL& url, Error error) {
GetProxyForUrlAction result;
result.expected_url = url;
result.error = error;
return result;
}
static GetProxyForUrlAction ReturnServers(
const GURL& url,
const mojo::Array<interfaces::ProxyServerPtr>& proxy_servers) {
GetProxyForUrlAction result;
result.expected_url = url;
result.proxy_servers = proxy_servers.Clone();
return result;
}
static GetProxyForUrlAction DropRequest(const GURL& url) {
GetProxyForUrlAction result;
result.expected_url = url;
result.action = DROP;
return result;
}
static GetProxyForUrlAction Disconnect(const GURL& url) {
GetProxyForUrlAction result;
result.expected_url = url;
result.action = DISCONNECT;
return result;
}
static GetProxyForUrlAction WaitForClientDisconnect(const GURL& url) {
GetProxyForUrlAction result;
result.expected_url = url;
result.action = WAIT_FOR_CLIENT_DISCONNECT;
return result;
}
Action action = COMPLETE;
Error error = OK;
mojo::Array<interfaces::ProxyServerPtr> proxy_servers;
GURL expected_url;
};
class MockMojoProxyResolver : public interfaces::ProxyResolver {
public:
explicit MockMojoProxyResolver(
mojo::InterfaceRequest<interfaces::ProxyResolver> req);
~MockMojoProxyResolver() override;
void AddPacScriptAction(SetPacScriptAction action);
// Returned script data is UTF8.
std::string pac_script_data() { return pac_script_data_; }
void AddGetProxyAction(GetProxyForUrlAction action);
void WaitForNextRequest();
private:
// Overridden from interfaces::ProxyResolver:
void SetPacScript(const mojo::String& data,
const mojo::Callback<void(int32_t)>& callback) override;
void GetProxyForUrl(
const mojo::String& url,
interfaces::ProxyResolverRequestClientPtr client) override;
void WakeWaiter();
std::string pac_script_data_;
std::queue<SetPacScriptAction> pac_script_actions_;
std::queue<GetProxyForUrlAction> get_proxy_actions_;
base::Closure quit_closure_;
mojo::Binding<interfaces::ProxyResolver> binding_;
};
MockMojoProxyResolver::MockMojoProxyResolver(
mojo::InterfaceRequest<interfaces::ProxyResolver> req)
: binding_(this, req.Pass()) {
}
MockMojoProxyResolver::~MockMojoProxyResolver() {
EXPECT_TRUE(pac_script_actions_.empty())
<< "Actions remaining: " << pac_script_actions_.size();
EXPECT_TRUE(get_proxy_actions_.empty())
<< "Actions remaining: " << get_proxy_actions_.size();
}
void MockMojoProxyResolver::AddPacScriptAction(SetPacScriptAction action) {
pac_script_actions_.push(action);
}
void MockMojoProxyResolver::AddGetProxyAction(GetProxyForUrlAction action) {
get_proxy_actions_.push(action);
}
void MockMojoProxyResolver::WaitForNextRequest() {
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
void MockMojoProxyResolver::WakeWaiter() {
if (!quit_closure_.is_null())
quit_closure_.Run();
quit_closure_.Reset();
}
void MockMojoProxyResolver::SetPacScript(
const mojo::String& data,
const mojo::Callback<void(int32_t)>& callback) {
pac_script_data_ = data.To<std::string>();
ASSERT_FALSE(pac_script_actions_.empty());
SetPacScriptAction action = pac_script_actions_.front();
pac_script_actions_.pop();
switch (action.action) {
case SetPacScriptAction::COMPLETE:
callback.Run(action.error);
break;
case SetPacScriptAction::DISCONNECT:
binding_.Close();
break;
}
WakeWaiter();
}
void MockMojoProxyResolver::GetProxyForUrl(
const mojo::String& url,
interfaces::ProxyResolverRequestClientPtr client) {
ASSERT_FALSE(get_proxy_actions_.empty());
GetProxyForUrlAction action = get_proxy_actions_.front();
get_proxy_actions_.pop();
EXPECT_EQ(action.expected_url.spec(), url.To<std::string>());
switch (action.action) {
case GetProxyForUrlAction::COMPLETE:
client->ReportResult(action.error, action.proxy_servers.Pass());
break;
case GetProxyForUrlAction::DROP:
client.reset();
break;
case GetProxyForUrlAction::DISCONNECT:
binding_.Close();
break;
case GetProxyForUrlAction::WAIT_FOR_CLIENT_DISCONNECT:
ASSERT_FALSE(client.WaitForIncomingMethodCall());
break;
}
WakeWaiter();
}
class TestMojoProxyResolverFactory : public MojoProxyResolverFactory {
public:
TestMojoProxyResolverFactory();
~TestMojoProxyResolverFactory();
// Overridden from MojoProxyResolverFactory:
void Create(mojo::InterfaceRequest<interfaces::ProxyResolver> req,
interfaces::HostResolverPtr host_resolver) override;
MockMojoProxyResolver& GetMockResolver() { return *mock_proxy_resolver_; }
void AddFuturePacScriptAction(int creation, SetPacScriptAction action);
void AddFutureGetProxyAction(int creation, GetProxyForUrlAction action);
int num_create_calls() const { return num_create_calls_; }
void FailNextCreate() { fail_next_create_ = true; }
private:
int num_create_calls_;
std::map<int, std::list<SetPacScriptAction>> pac_script_actions_;
std::map<int, std::list<GetProxyForUrlAction>> get_proxy_actions_;
bool fail_next_create_;
scoped_ptr<MockMojoProxyResolver> mock_proxy_resolver_;
};
TestMojoProxyResolverFactory::TestMojoProxyResolverFactory()
: num_create_calls_(0), fail_next_create_(false) {
}
TestMojoProxyResolverFactory::~TestMojoProxyResolverFactory() {
}
void TestMojoProxyResolverFactory::Create(
mojo::InterfaceRequest<interfaces::ProxyResolver> req,
interfaces::HostResolverPtr host_resolver) {
if (fail_next_create_) {
req = nullptr;
fail_next_create_ = false;
} else {
mock_proxy_resolver_.reset(new MockMojoProxyResolver(req.Pass()));
for (const auto& action : pac_script_actions_[num_create_calls_])
mock_proxy_resolver_->AddPacScriptAction(action);
for (const auto& action : get_proxy_actions_[num_create_calls_])
mock_proxy_resolver_->AddGetProxyAction(action);
}
num_create_calls_++;
}
void TestMojoProxyResolverFactory::AddFuturePacScriptAction(
int creation,
SetPacScriptAction action) {
pac_script_actions_[creation].push_back(action);
}
void TestMojoProxyResolverFactory::AddFutureGetProxyAction(
int creation,
GetProxyForUrlAction action) {
get_proxy_actions_[creation].push_back(action);
}
class Request {
public:
Request(ProxyResolverMojo* resolver, const GURL& url);
int Resolve();
void Cancel();
int WaitForResult();
int error() const { return error_; }
const ProxyInfo& results() const { return results_; }
private:
ProxyResolverMojo* resolver_;
const GURL url_;
ProxyInfo results_;
ProxyResolver::RequestHandle handle_;
int error_;
TestCompletionCallback callback_;
};
Request::Request(ProxyResolverMojo* resolver, const GURL& url)
: resolver_(resolver), url_(url), error_(0) {
}
int Request::Resolve() {
BoundNetLog net_log;
error_ = resolver_->GetProxyForURL(url_, &results_, callback_.callback(),
&handle_, net_log);
return error_;
}
void Request::Cancel() {
resolver_->CancelRequest(handle_);
}
int Request::WaitForResult() {
error_ = callback_.WaitForResult();
return error_;
}
} // namespace
class ProxyResolverMojoTest : public testing::Test {
public:
void SetUp() override {
proxy_resolver_mojo_.reset(new ProxyResolverMojo(
&mojo_proxy_resolver_factory_, &mock_host_resolver_));
}
scoped_ptr<Request> MakeRequest(const GURL& url) {
return make_scoped_ptr(new Request(proxy_resolver_mojo_.get(), url));
}
mojo::Array<interfaces::ProxyServerPtr> ProxyServersFromPacString(
const std::string& pac_string) {
ProxyInfo proxy_info;
proxy_info.UsePacString(pac_string);
return mojo::Array<interfaces::ProxyServerPtr>::From(
proxy_info.proxy_list().GetAll());
}
void SetPacScript(int instance) {
mojo_proxy_resolver_factory_.AddFuturePacScriptAction(
instance, SetPacScriptAction::ReturnResult(OK));
TestCompletionCallback callback;
scoped_refptr<ProxyResolverScriptData> pac_script(
ProxyResolverScriptData::FromUTF8(kScriptData));
EXPECT_EQ(OK, callback.GetResult(proxy_resolver_mojo_->SetPacScript(
pac_script, callback.callback())));
}
void RunCallbackAndSetPacScript(const net::CompletionCallback& callback,
const net::CompletionCallback& pac_callback,
int instance,
int result) {
callback.Run(result);
mojo_proxy_resolver_factory_.AddFuturePacScriptAction(
instance, SetPacScriptAction::ReturnResult(OK));
scoped_refptr<ProxyResolverScriptData> pac_script(
ProxyResolverScriptData::FromUTF8(kScriptData));
EXPECT_EQ(ERR_IO_PENDING,
proxy_resolver_mojo_->SetPacScript(pac_script, pac_callback));
}
MockHostResolver mock_host_resolver_;
TestMojoProxyResolverFactory mojo_proxy_resolver_factory_;
scoped_ptr<ProxyResolverMojo> proxy_resolver_mojo_;
};
TEST_F(ProxyResolverMojoTest, SetPacScript) {
SetPacScript(0);
EXPECT_EQ(kScriptData,
mojo_proxy_resolver_factory_.GetMockResolver().pac_script_data());
}
TEST_F(ProxyResolverMojoTest, SetPacScript_Empty) {
TestCompletionCallback callback;
scoped_refptr<ProxyResolverScriptData> pac_script(
ProxyResolverScriptData::FromUTF8(""));
EXPECT_EQ(ERR_PAC_SCRIPT_FAILED,
callback.GetResult(proxy_resolver_mojo_->SetPacScript(
pac_script, callback.callback())));
}
TEST_F(ProxyResolverMojoTest, SetPacScript_Url) {
TestCompletionCallback callback;
scoped_refptr<ProxyResolverScriptData> pac_script(
ProxyResolverScriptData::FromURL(GURL(kExampleUrl)));
EXPECT_EQ(ERR_PAC_SCRIPT_FAILED,
callback.GetResult(proxy_resolver_mojo_->SetPacScript(
pac_script, callback.callback())));
}
TEST_F(ProxyResolverMojoTest, SetPacScript_Failed) {
mojo_proxy_resolver_factory_.AddFuturePacScriptAction(
0, SetPacScriptAction::ReturnResult(ERR_PAC_STATUS_NOT_OK));
TestCompletionCallback callback;
scoped_refptr<ProxyResolverScriptData> pac_script(
ProxyResolverScriptData::FromUTF8(kScriptData));
EXPECT_EQ(ERR_PAC_STATUS_NOT_OK,
callback.GetResult(proxy_resolver_mojo_->SetPacScript(
pac_script, callback.callback())));
}
TEST_F(ProxyResolverMojoTest, SetPacScript_Disconnected) {
mojo_proxy_resolver_factory_.AddFuturePacScriptAction(
0, SetPacScriptAction::Disconnect());
scoped_refptr<ProxyResolverScriptData> pac_script(
ProxyResolverScriptData::FromUTF8(kScriptData));
TestCompletionCallback callback;
EXPECT_EQ(ERR_IO_PENDING, proxy_resolver_mojo_->SetPacScript(
pac_script, callback.callback()));
EXPECT_EQ(ERR_PAC_SCRIPT_TERMINATED, callback.GetResult(ERR_IO_PENDING));
}
TEST_F(ProxyResolverMojoTest, SetPacScript_SuccessThenDisconnect) {
mojo_proxy_resolver_factory_.AddFuturePacScriptAction(
0, SetPacScriptAction::ReturnResult(OK));
mojo_proxy_resolver_factory_.AddFuturePacScriptAction(
0, SetPacScriptAction::Disconnect());
mojo_proxy_resolver_factory_.AddFuturePacScriptAction(
1, SetPacScriptAction::ReturnResult(ERR_FAILED));
{
scoped_refptr<ProxyResolverScriptData> pac_script(
ProxyResolverScriptData::FromUTF8(kScriptData));
TestCompletionCallback callback;
EXPECT_EQ(OK, callback.GetResult(proxy_resolver_mojo_->SetPacScript(
pac_script, callback.callback())));
EXPECT_EQ(kScriptData,
mojo_proxy_resolver_factory_.GetMockResolver().pac_script_data());
}
{
scoped_refptr<ProxyResolverScriptData> pac_script(
ProxyResolverScriptData::FromUTF8(kScriptData2));
TestCompletionCallback callback;
EXPECT_EQ(ERR_IO_PENDING, proxy_resolver_mojo_->SetPacScript(
pac_script, callback.callback()));
EXPECT_EQ(ERR_PAC_SCRIPT_TERMINATED, callback.GetResult(ERR_IO_PENDING));
}
{
scoped_refptr<ProxyResolverScriptData> pac_script(
ProxyResolverScriptData::FromUTF8(kScriptData2));
TestCompletionCallback callback;
EXPECT_EQ(ERR_IO_PENDING, proxy_resolver_mojo_->SetPacScript(
pac_script, callback.callback()));
EXPECT_EQ(ERR_FAILED, callback.GetResult(ERR_IO_PENDING));
}
// The service should have been recreated on the last SetPacScript call.
EXPECT_EQ(2, mojo_proxy_resolver_factory_.num_create_calls());
}
TEST_F(ProxyResolverMojoTest, SetPacScript_Cancel) {
mojo_proxy_resolver_factory_.AddFuturePacScriptAction(
0, SetPacScriptAction::ReturnResult(OK));
scoped_refptr<ProxyResolverScriptData> pac_script(
ProxyResolverScriptData::FromUTF8(kScriptData));
TestCompletionCallback callback;
EXPECT_EQ(ERR_IO_PENDING, proxy_resolver_mojo_->SetPacScript(
pac_script, callback.callback()));
proxy_resolver_mojo_->CancelSetPacScript();
// The Mojo request is still made.
mojo_proxy_resolver_factory_.GetMockResolver().WaitForNextRequest();
}
TEST_F(ProxyResolverMojoTest, SetPacScript_CancelAndSetAgain) {
mojo_proxy_resolver_factory_.AddFuturePacScriptAction(
0, SetPacScriptAction::ReturnResult(ERR_FAILED));
mojo_proxy_resolver_factory_.AddFuturePacScriptAction(
0, SetPacScriptAction::ReturnResult(ERR_UNEXPECTED));
scoped_refptr<ProxyResolverScriptData> pac_script(
ProxyResolverScriptData::FromUTF8(kScriptData));
TestCompletionCallback callback1;
EXPECT_EQ(ERR_IO_PENDING, proxy_resolver_mojo_->SetPacScript(
pac_script, callback1.callback()));
proxy_resolver_mojo_->CancelSetPacScript();
TestCompletionCallback callback2;
EXPECT_EQ(ERR_IO_PENDING, proxy_resolver_mojo_->SetPacScript(
pac_script, callback2.callback()));
EXPECT_EQ(ERR_UNEXPECTED, callback2.GetResult(ERR_IO_PENDING));
}
TEST_F(ProxyResolverMojoTest, GetProxyForURL) {
mojo_proxy_resolver_factory_.AddFutureGetProxyAction(
0, GetProxyForUrlAction::ReturnServers(
GURL(kExampleUrl), ProxyServersFromPacString("DIRECT")));
SetPacScript(0);
scoped_ptr<Request> request(MakeRequest(GURL(kExampleUrl)));
EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
EXPECT_EQ(OK, request->WaitForResult());
EXPECT_EQ("DIRECT", request->results().ToPacString());
}
TEST_F(ProxyResolverMojoTest, GetProxyForURL_WithoutSetPacScript) {
scoped_ptr<Request> request(MakeRequest(GURL(kExampleUrl)));
EXPECT_EQ(ERR_PAC_SCRIPT_TERMINATED, request->Resolve());
}
TEST_F(ProxyResolverMojoTest, GetProxyForURL_MultipleResults) {
static const char kPacString[] =
"PROXY foo1:80;DIRECT;SOCKS foo2:1234;"
"SOCKS5 foo3:1080;HTTPS foo4:443;QUIC foo6:8888";
mojo_proxy_resolver_factory_.AddFutureGetProxyAction(
0, GetProxyForUrlAction::ReturnServers(
GURL(kExampleUrl), ProxyServersFromPacString(kPacString)));
SetPacScript(0);
scoped_ptr<Request> request(MakeRequest(GURL(kExampleUrl)));
EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
EXPECT_EQ(OK, request->WaitForResult());
EXPECT_EQ(kPacString, request->results().ToPacString());
}
TEST_F(ProxyResolverMojoTest, GetProxyForURL_Error) {
mojo_proxy_resolver_factory_.AddFutureGetProxyAction(
0, GetProxyForUrlAction::ReturnError(GURL(kExampleUrl), ERR_UNEXPECTED));
SetPacScript(0);
scoped_ptr<Request> request(MakeRequest(GURL(kExampleUrl)));
EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
EXPECT_EQ(ERR_UNEXPECTED, request->WaitForResult());
EXPECT_TRUE(request->results().is_empty());
}
TEST_F(ProxyResolverMojoTest, GetProxyForURL_Cancel) {
mojo_proxy_resolver_factory_.AddFutureGetProxyAction(
0, GetProxyForUrlAction::WaitForClientDisconnect(GURL(kExampleUrl)));
SetPacScript(0);
scoped_ptr<Request> request(MakeRequest(GURL(kExampleUrl)));
EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
request->Cancel();
// The Mojo request is still made.
mojo_proxy_resolver_factory_.GetMockResolver().WaitForNextRequest();
}
TEST_F(ProxyResolverMojoTest, GetProxyForURL_MultipleRequests) {
mojo_proxy_resolver_factory_.AddFutureGetProxyAction(
0, GetProxyForUrlAction::ReturnServers(
GURL(kExampleUrl), ProxyServersFromPacString("DIRECT")));
mojo_proxy_resolver_factory_.AddFutureGetProxyAction(
0, GetProxyForUrlAction::ReturnServers(
GURL("https://www.chromium.org"),
ProxyServersFromPacString("HTTPS foo:443")));
SetPacScript(0);
scoped_ptr<Request> request1(MakeRequest(GURL(kExampleUrl)));
EXPECT_EQ(ERR_IO_PENDING, request1->Resolve());
scoped_ptr<Request> request2(MakeRequest(GURL("https://www.chromium.org")));
EXPECT_EQ(ERR_IO_PENDING, request2->Resolve());
EXPECT_EQ(OK, request1->WaitForResult());
EXPECT_EQ(OK, request2->WaitForResult());
EXPECT_EQ("DIRECT", request1->results().ToPacString());
EXPECT_EQ("HTTPS foo:443", request2->results().ToPacString());
}
TEST_F(ProxyResolverMojoTest, GetProxyForURL_Disconnect) {
mojo_proxy_resolver_factory_.AddFutureGetProxyAction(
0, GetProxyForUrlAction::Disconnect(GURL(kExampleUrl)));
mojo_proxy_resolver_factory_.AddFutureGetProxyAction(
1, GetProxyForUrlAction::ReturnServers(
GURL(kExampleUrl), ProxyServersFromPacString("DIRECT")));
{
SetPacScript(0);
scoped_ptr<Request> request(MakeRequest(GURL(kExampleUrl)));
EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
EXPECT_EQ(ERR_PAC_SCRIPT_TERMINATED, request->WaitForResult());
EXPECT_TRUE(request->results().is_empty());
}
{
// Calling GetProxyForURL without first setting the pac script should fail.
scoped_ptr<Request> request(MakeRequest(GURL(kExampleUrl)));
EXPECT_EQ(ERR_PAC_SCRIPT_TERMINATED, request->Resolve());
}
{
SetPacScript(1);
scoped_ptr<Request> request(MakeRequest(GURL(kExampleUrl)));
EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
EXPECT_EQ(OK, request->WaitForResult());
EXPECT_EQ("DIRECT", request->results().ToPacString());
}
EXPECT_EQ(2, mojo_proxy_resolver_factory_.num_create_calls());
}
TEST_F(ProxyResolverMojoTest, GetProxyForURL_ClientClosed) {
mojo_proxy_resolver_factory_.AddFutureGetProxyAction(
0, GetProxyForUrlAction::DropRequest(GURL(kExampleUrl)));
SetPacScript(0);
scoped_ptr<Request> request1(MakeRequest(GURL(kExampleUrl)));
EXPECT_EQ(ERR_IO_PENDING, request1->Resolve());
EXPECT_EQ(ERR_PAC_SCRIPT_TERMINATED, request1->WaitForResult());
EXPECT_EQ(1, mojo_proxy_resolver_factory_.num_create_calls());
}
TEST_F(ProxyResolverMojoTest, DisconnectAndFailCreate) {
mojo_proxy_resolver_factory_.AddFutureGetProxyAction(
0, GetProxyForUrlAction::Disconnect(GURL(kExampleUrl)));
{
SetPacScript(0);
scoped_ptr<Request> request(MakeRequest(GURL(kExampleUrl)));
EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
EXPECT_EQ(ERR_PAC_SCRIPT_TERMINATED, request->WaitForResult());
EXPECT_TRUE(request->results().is_empty());
}
// The service should attempt to create a new connection, but that should
// fail.
{
scoped_refptr<ProxyResolverScriptData> pac_script(
ProxyResolverScriptData::FromUTF8(kScriptData));
TestCompletionCallback callback;
mojo_proxy_resolver_factory_.FailNextCreate();
EXPECT_EQ(ERR_PAC_SCRIPT_TERMINATED,
callback.GetResult(proxy_resolver_mojo_->SetPacScript(
pac_script, callback.callback())));
}
// A third attempt should succeed.
SetPacScript(2);
EXPECT_EQ(3, mojo_proxy_resolver_factory_.num_create_calls());
}
TEST_F(ProxyResolverMojoTest, DisconnectAndReconnectInCallback) {
mojo_proxy_resolver_factory_.AddFutureGetProxyAction(
0, GetProxyForUrlAction::Disconnect(GURL(kExampleUrl)));
mojo_proxy_resolver_factory_.AddFutureGetProxyAction(
1, GetProxyForUrlAction::ReturnServers(
GURL(kExampleUrl), ProxyServersFromPacString("DIRECT")));
// In this first resolve request, the Mojo service is disconnected causing the
// request to return ERR_PAC_SCRIPT_TERMINATED. In the request callback, form
// a new connection to a Mojo resolver service by calling SetPacScript().
SetPacScript(0);
ProxyInfo results;
TestCompletionCallback resolve_callback;
TestCompletionCallback pac_callback;
ProxyResolver::RequestHandle handle;
BoundNetLog net_log;
int error = proxy_resolver_mojo_->GetProxyForURL(
GURL(kExampleUrl), &results,
base::Bind(&ProxyResolverMojoTest::RunCallbackAndSetPacScript,
base::Unretained(this), resolve_callback.callback(),
pac_callback.callback(), 1),
&handle, net_log);
EXPECT_EQ(ERR_IO_PENDING, error);
EXPECT_EQ(ERR_PAC_SCRIPT_TERMINATED, resolve_callback.WaitForResult());
EXPECT_EQ(OK, pac_callback.WaitForResult());
// Setting the PAC script above should have been successful and let us send a
// resolve request.
scoped_ptr<Request> request(MakeRequest(GURL(kExampleUrl)));
EXPECT_EQ(ERR_IO_PENDING, request->Resolve());
EXPECT_EQ(OK, request->WaitForResult());
EXPECT_EQ("DIRECT", request->results().ToPacString());
EXPECT_EQ(2, mojo_proxy_resolver_factory_.num_create_calls());
}
} // namespace net
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment