Commit b9fb9f49 authored by Eric Roman's avatar Eric Roman Committed by Commit Bot

Make chrome.proxy.onProxyError extension API work under the Network Service.

This adds an optional "ProxyErrorClient" client to the NetworkContext which
receives notifications of errors in the PAC script, as well as notifications
of failed URL loads that may have been proxy related.

(Reland of d576f34a).

Bug: 876568,851609,879491
Cq-Include-Trybots: luci.chromium.try:linux_mojo
Change-Id: I08987d658879913d94c9b13b6fdc1de2a32a6d54
Reviewed-on: https://chromium-review.googlesource.com/1200076
Commit-Queue: Eric Roman <eroman@chromium.org>
Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#588259}
parent f9731ff2
......@@ -100,7 +100,6 @@ class ChromeExtensionsNetworkDelegateImpl
private:
// ChromeExtensionsNetworkDelegate implementation.
void ForwardProxyErrors(net::URLRequest* request, int net_error) override;
void ForwardStartRequestStatus(net::URLRequest* request) override;
void ForwardDoneRequestStatus(net::URLRequest* request) override;
int OnBeforeURLRequest(net::URLRequest* request,
......@@ -124,7 +123,6 @@ class ChromeExtensionsNetworkDelegateImpl
bool started,
int net_error) override;
void OnURLRequestDestroyed(net::URLRequest* request) override;
void OnPACScriptError(int line_number, const base::string16& error) override;
net::NetworkDelegate::AuthRequiredResponse OnAuthRequired(
net::URLRequest* request,
const net::AuthChallengeInfo& auth_info,
......@@ -159,19 +157,6 @@ ChromeExtensionsNetworkDelegateImpl::~ChromeExtensionsNetworkDelegateImpl() {
DCHECK(active_requests_.empty());
}
void ChromeExtensionsNetworkDelegateImpl::ForwardProxyErrors(
net::URLRequest* request,
int net_error) {
if (net_error != net::OK) {
switch (net_error) {
case net::ERR_PROXY_AUTH_UNSUPPORTED:
case net::ERR_PROXY_CONNECTION_FAILED:
case net::ERR_TUNNEL_CONNECTION_FAILED:
extensions::ProxyEventRouter::GetInstance()->OnProxyError(
event_router_.get(), profile_, net_error);
}
}
}
void ChromeExtensionsNetworkDelegateImpl::ForwardStartRequestStatus(
net::URLRequest* request) {
......@@ -253,7 +238,6 @@ void ChromeExtensionsNetworkDelegateImpl::OnResponseStarted(
info->AddResponseInfoFromURLRequest(request);
ExtensionWebRequestEventRouter::GetInstance()->OnResponseStarted(
profile_, extension_info_map_.get(), info, net_error);
ForwardProxyErrors(request, net_error);
}
void ChromeExtensionsNetworkDelegateImpl::OnCompleted(net::URLRequest* request,
......@@ -286,13 +270,6 @@ void ChromeExtensionsNetworkDelegateImpl::OnURLRequestDestroyed(
active_requests_.erase(it);
}
void ChromeExtensionsNetworkDelegateImpl::OnPACScriptError(
int line_number,
const base::string16& error) {
extensions::ProxyEventRouter::GetInstance()->OnPACScriptError(
event_router_.get(), profile_, line_number, error);
}
net::NetworkDelegate::AuthRequiredResponse
ChromeExtensionsNetworkDelegateImpl::OnAuthRequired(
net::URLRequest* request,
......@@ -333,10 +310,6 @@ void ChromeExtensionsNetworkDelegate::set_extension_info_map(
#endif
}
void ChromeExtensionsNetworkDelegate::ForwardProxyErrors(
net::URLRequest* request,
int net_error) {}
void ChromeExtensionsNetworkDelegate::ForwardStartRequestStatus(
net::URLRequest* request) {
}
......
......@@ -35,10 +35,6 @@ class ChromeExtensionsNetworkDelegate : public net::NetworkDelegateImpl {
profile_ = profile;
}
// If the |request| failed due to problems with a proxy, forward the error to
// the proxy extension API.
virtual void ForwardProxyErrors(net::URLRequest* request, int net_error);
// Notifies the extensions::ProcessManager for the associated RenderFrame, if
// any, that a request has started or stopped.
virtual void ForwardStartRequestStatus(net::URLRequest* request);
......
......@@ -245,7 +245,6 @@ void ChromeNetworkDelegate::OnCompleted(net::URLRequest* request,
extensions_delegate_->NotifyCompleted(request, started, net_error);
if (domain_reliability_monitor_)
domain_reliability_monitor_->OnCompleted(request, started);
extensions_delegate_->ForwardProxyErrors(request, net_error);
extensions_delegate_->ForwardDoneRequestStatus(request);
}
......@@ -253,11 +252,6 @@ void ChromeNetworkDelegate::OnURLRequestDestroyed(net::URLRequest* request) {
extensions_delegate_->NotifyURLRequestDestroyed(request);
}
void ChromeNetworkDelegate::OnPACScriptError(int line_number,
const base::string16& error) {
extensions_delegate_->NotifyPACScriptError(line_number, error);
}
net::NetworkDelegate::AuthRequiredResponse
ChromeNetworkDelegate::OnAuthRequired(net::URLRequest* request,
const net::AuthChallengeInfo& auth_info,
......
......@@ -131,7 +131,6 @@ class ChromeNetworkDelegate : public net::NetworkDelegateImpl {
bool started,
int net_error) override;
void OnURLRequestDestroyed(net::URLRequest* request) override;
void OnPACScriptError(int line_number, const base::string16& error) override;
net::NetworkDelegate::AuthRequiredResponse OnAuthRequired(
net::URLRequest* request,
const net::AuthChallengeInfo& auth_info,
......
......@@ -4,6 +4,7 @@
#include "chrome/browser/net/proxy_config_monitor.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/net/proxy_service_factory.h"
......@@ -16,10 +17,18 @@
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#endif // defined(OS_CHROMEOS)
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "chrome/browser/extensions/api/proxy/proxy_api.h"
#endif
ProxyConfigMonitor::ProxyConfigMonitor(Profile* profile) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(profile);
#if BUILDFLAG(ENABLE_EXTENSIONS)
profile_ = profile;
#endif
// If this is the ChromeOS sign-in profile, just create the tracker from global
// state.
#if defined(OS_CHROMEOS)
......@@ -68,9 +77,15 @@ void ProxyConfigMonitor::AddToNetworkContextParams(
mojo::MakeRequest(&proxy_config_client);
proxy_config_client_set_.AddPtr(std::move(proxy_config_client));
binding_set_.AddBinding(
poller_binding_set_.AddBinding(
this,
mojo::MakeRequest(&network_context_params->proxy_config_poller_client));
#if BUILDFLAG(ENABLE_EXTENSIONS)
error_binding_set_.AddBinding(
this, mojo::MakeRequest(&network_context_params->proxy_error_client));
#endif
net::ProxyConfigWithAnnotation proxy_config;
net::ProxyConfigService::ConfigAvailability availability =
proxy_config_service_->GetLatestProxyConfig(&proxy_config);
......@@ -107,3 +122,29 @@ void ProxyConfigMonitor::OnProxyConfigChanged(
void ProxyConfigMonitor::OnLazyProxyConfigPoll() {
proxy_config_service_->OnLazyPoll();
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
void ProxyConfigMonitor::OnPACScriptError(int32_t line_number,
const std::string& details) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
extensions::ProxyEventRouter::GetInstance()->OnPACScriptError(
g_browser_process->extension_event_router_forwarder(), profile_,
line_number, base::UTF8ToUTF16(details));
}
void ProxyConfigMonitor::OnRequestMaybeFailedDueToProxySettings(
int32_t net_error) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (net_error >= 0) {
// If the error is obviously wrong, don't dispatch it to extensions. If the
// PAC executor process is compromised, then |net_error| could be attacker
// controlled.
return;
}
extensions::ProxyEventRouter::GetInstance()->OnProxyError(
g_browser_process->extension_event_router_forwarder(), profile_,
net_error);
}
#endif
......@@ -8,6 +8,8 @@
#include <memory>
#include "base/macros.h"
#include "build/buildflag.h"
#include "extensions/buildflags/buildflags.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/interface_ptr_set.h"
#include "net/proxy_resolution/proxy_config_service.h"
......@@ -22,9 +24,17 @@ class Profile;
class PrefProxyConfigTracker;
// Tracks the ProxyConfig to use, and passes any updates to a NetworkContext's
// ProxyConfigClient.
// ProxyConfigClient. This also responds to errors related to proxy settings
// from the NetworkContext, and forwards them any listening Chrome extensions
// associated with the profile.
class ProxyConfigMonitor : public net::ProxyConfigService::Observer,
public network::mojom::ProxyConfigPollerClient {
public network::mojom::ProxyConfigPollerClient
#if BUILDFLAG(ENABLE_EXTENSIONS)
,
public network::mojom::ProxyErrorClient
#endif
{
public:
// Creates a ProxyConfigMonitor that gets proxy settings from |profile| and
// watches for changes. The created ProxyConfigMonitor must be destroyed
......@@ -60,15 +70,27 @@ class ProxyConfigMonitor : public net::ProxyConfigService::Observer,
// network::mojom::ProxyConfigPollerClient implementation:
void OnLazyProxyConfigPoll() override;
#if BUILDFLAG(ENABLE_EXTENSIONS)
// network::mojom::ProxyErrorClient implementation:
void OnPACScriptError(int32_t line_number,
const std::string& details) override;
void OnRequestMaybeFailedDueToProxySettings(int32_t net_error) override;
#endif
std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
// Monitors global and Profile prefs related to proxy configuration.
std::unique_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_;
mojo::BindingSet<network::mojom::ProxyConfigPollerClient> binding_set_;
mojo::BindingSet<network::mojom::ProxyConfigPollerClient> poller_binding_set_;
mojo::InterfacePtrSet<network::mojom::ProxyConfigClient>
proxy_config_client_set_;
#if BUILDFLAG(ENABLE_EXTENSIONS)
mojo::BindingSet<network::mojom::ProxyErrorClient> error_binding_set_;
Profile* profile_ = nullptr;
#endif
DISALLOW_COPY_AND_ASSIGN(ProxyConfigMonitor);
};
......
......@@ -109,12 +109,12 @@ void LayeredNetworkDelegate::OnBeforeRedirectInternal(
void LayeredNetworkDelegate::OnResponseStarted(URLRequest* request,
int net_error) {
OnResponseStartedInternal(request);
OnResponseStartedInternal(request, net_error);
nested_network_delegate_->NotifyResponseStarted(request, net_error);
}
void LayeredNetworkDelegate::OnResponseStartedInternal(URLRequest* request) {
}
void LayeredNetworkDelegate::OnResponseStartedInternal(URLRequest* request,
int net_error) {}
void LayeredNetworkDelegate::OnNetworkBytesReceived(URLRequest* request,
int64_t bytes_received) {
......
......@@ -134,7 +134,7 @@ class NET_EXPORT LayeredNetworkDelegate : public NetworkDelegate {
virtual void OnBeforeRedirectInternal(URLRequest* request,
const GURL& new_location);
virtual void OnResponseStartedInternal(URLRequest* request);
virtual void OnResponseStartedInternal(URLRequest* request, int net_error);
virtual void OnNetworkBytesReceivedInternal(URLRequest* request,
int64_t bytes_received);
......
......@@ -256,7 +256,7 @@ class TestLayeredNetworkDelegate : public LayeredNetworkDelegate {
EXPECT_EQ(1, (*counters_)["on_before_redirect_count"]);
}
void OnResponseStartedInternal(URLRequest* request) override {
void OnResponseStartedInternal(URLRequest* request, int net_error) override {
++(*counters_)["on_response_started_count"];
EXPECT_EQ(1, (*counters_)["on_response_started_count"]);
}
......
......@@ -18,6 +18,7 @@
#include "base/sequenced_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "build/build_config.h"
......@@ -242,12 +243,16 @@ class NetworkContext::ContextNetworkDelegate
std::unique_ptr<net::NetworkDelegate> nested_network_delegate,
bool enable_referrers,
bool validate_referrer_policy_on_initial_request,
mojom::ProxyErrorClientPtrInfo proxy_error_client_info,
NetworkContext* network_context)
: LayeredNetworkDelegate(std::move(nested_network_delegate)),
enable_referrers_(enable_referrers),
validate_referrer_policy_on_initial_request_(
validate_referrer_policy_on_initial_request),
network_context_(network_context) {}
network_context_(network_context) {
if (proxy_error_client_info)
proxy_error_client_.Bind(std::move(proxy_error_client_info));
}
~ContextNetworkDelegate() override {}
......@@ -284,6 +289,8 @@ class NetworkContext::ContextNetworkDelegate
"Net.HttpRequestCompletionErrorCodes.MainFrame", -net_error);
}
}
ForwardProxyErrors(net_error);
}
bool OnCancelURLRequestWithPolicyViolatingReferrerHeaderInternal(
......@@ -338,13 +345,43 @@ class NetworkContext::ContextNetworkDelegate
.IsCookieAccessAllowed(url, site_for_cookies);
}
void OnResponseStartedInternal(net::URLRequest* request,
int net_error) override {
ForwardProxyErrors(net_error);
}
void OnPACScriptErrorInternal(int line_number,
const base::string16& error) override {
if (!proxy_error_client_)
return;
proxy_error_client_->OnPACScriptError(line_number,
base::UTF16ToUTF8(error));
}
void set_enable_referrers(bool enable_referrers) {
enable_referrers_ = enable_referrers;
}
private:
void ForwardProxyErrors(int net_error) {
if (!proxy_error_client_)
return;
// TODO(https://crbug.com/876848): Provide justification for the currently
// enumerated errors.
switch (net_error) {
case net::ERR_PROXY_AUTH_UNSUPPORTED:
case net::ERR_PROXY_CONNECTION_FAILED:
case net::ERR_TUNNEL_CONNECTION_FAILED:
proxy_error_client_->OnRequestMaybeFailedDueToProxySettings(net_error);
break;
}
}
bool enable_referrers_;
bool validate_referrer_policy_on_initial_request_;
mojom::ProxyErrorClientPtr proxy_error_client_;
NetworkContext* network_context_;
DISALLOW_COPY_AND_ASSIGN(ContextNetworkDelegate);
......@@ -1202,6 +1239,7 @@ URLRequestContextOwner NetworkContext::ApplyContextParamsToBuilder(
network_context_params->enable_referrers,
network_context_params
->validate_referrer_policy_on_initial_request,
std::move(network_context_params->proxy_error_client),
network_context);
if (out_context_network_delegate)
*out_context_network_delegate = context_network_delegate.get();
......
......@@ -38,6 +38,7 @@
#include "components/network_session_configurator/browser/network_session_configurator.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/cpp/system/data_pipe_utils.h"
#include "net/base/cache_type.h"
#include "net/base/hash_value.h"
......@@ -70,6 +71,7 @@
#include "net/test/embedded_test_server/controllable_http_response.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/embedded_test_server_connection_listener.h"
#include "net/test/gtest_util.h"
#include "net/test/test_data_directory.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/http_user_agent_settings.h"
......@@ -3423,6 +3425,341 @@ TEST_F(NetworkContextTest, QueryHSTS) {
EXPECT_TRUE(result2);
}
// This is a test ProxyErrorClient that records the sequence of calls made to
// OnPACScriptError() and OnRequestMaybeFailedDueToProxySettings().
class TestProxyErrorClient final : public mojom::ProxyErrorClient {
public:
struct PacScriptError {
int line = -1;
std::string details;
};
TestProxyErrorClient() : binding_(this) {}
~TestProxyErrorClient() override {}
void OnPACScriptError(int32_t line_number,
const std::string& details) override {
on_pac_script_error_calls_.push_back({line_number, details});
}
void OnRequestMaybeFailedDueToProxySettings(int32_t net_error) override {
on_request_maybe_failed_calls_.push_back(net_error);
}
const std::vector<int>& on_request_maybe_failed_calls() const {
return on_request_maybe_failed_calls_;
}
const std::vector<PacScriptError>& on_pac_script_error_calls() const {
return on_pac_script_error_calls_;
}
// Creates an InterfacePtrInfo, binds it to |*this| and returns it.
mojom::ProxyErrorClientPtrInfo CreateInterfacePtrInfo() {
mojom::ProxyErrorClientPtrInfo client_ptr_info;
binding_.Bind(mojo::MakeRequest(&client_ptr_info));
binding_.set_connection_error_handler(base::BindOnce(
&TestProxyErrorClient::OnMojoPipeError, base::Unretained(this)));
return client_ptr_info;
}
// Runs until the message pipe is closed due to an error.
void RunUntilMojoPipeError() {
if (has_received_mojo_pipe_error_)
return;
base::RunLoop run_loop;
quit_closure_for_on_mojo_pipe_error_ = run_loop.QuitClosure();
run_loop.Run();
}
private:
void OnMojoPipeError() {
if (has_received_mojo_pipe_error_)
return;
has_received_mojo_pipe_error_ = true;
if (quit_closure_for_on_mojo_pipe_error_)
std::move(quit_closure_for_on_mojo_pipe_error_).Run();
}
mojo::Binding<mojom::ProxyErrorClient> binding_;
base::OnceClosure quit_closure_for_on_mojo_pipe_error_;
bool has_received_mojo_pipe_error_ = false;
std::vector<int> on_request_maybe_failed_calls_;
std::vector<PacScriptError> on_pac_script_error_calls_;
DISALLOW_COPY_AND_ASSIGN(TestProxyErrorClient);
};
// While in scope, all host resolutions will fail with ERR_NAME_NOT_RESOLVED,
// including localhost (so this precludes the use of embedded test server).
class ScopedFailAllHostResolutions {
public:
ScopedFailAllHostResolutions()
: mock_resolver_proc_(new net::RuleBasedHostResolverProc(nullptr)),
default_resolver_proc_(mock_resolver_proc_.get()) {
mock_resolver_proc_->AddSimulatedFailure("*");
}
private:
scoped_refptr<net::RuleBasedHostResolverProc> mock_resolver_proc_;
net::ScopedDefaultHostResolverProc default_resolver_proc_;
};
// Tests that when a ProxyErrorClient is provided to NetworkContextParams, this
// client's OnRequestMaybeFailedDueToProxySettings() method is called exactly
// once when a request fails due to a proxy server connectivity failure.
TEST_F(NetworkContextTest, ProxyErrorClientNotifiedOfProxyConnection) {
// Avoid the test having a network dependency on DNS.
ScopedFailAllHostResolutions fail_dns;
// Set up the NetworkContext, such that it uses an unreachable proxy
// (proxy and is configured to send "proxy errors" to
// |proxy_error_client|.
TestProxyErrorClient proxy_error_client;
mojom::NetworkContextParamsPtr context_params =
mojom::NetworkContextParams::New();
context_params->proxy_error_client =
proxy_error_client.CreateInterfacePtrInfo();
net::ProxyConfig proxy_config;
// Set the proxy to an unreachable address (host resolution fails).
proxy_config.proxy_rules().ParseFromString("proxy.bad.dns");
context_params->initial_proxy_config = net::ProxyConfigWithAnnotation(
proxy_config, TRAFFIC_ANNOTATION_FOR_TESTS);
std::unique_ptr<NetworkContext> network_context =
CreateContextWithParams(std::move(context_params));
// Issue an HTTP request. It doesn't matter exactly what the URL is, since it
// will be sent to the proxy.
ResourceRequest request;
request.url = GURL("http://example.test");
mojom::URLLoaderFactoryPtr loader_factory;
mojom::URLLoaderFactoryParamsPtr loader_params =
mojom::URLLoaderFactoryParams::New();
loader_params->process_id = mojom::kBrowserProcessId;
network_context->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory),
std::move(loader_params));
mojom::URLLoaderPtr loader;
TestURLLoaderClient client;
loader_factory->CreateLoaderAndStart(
mojo::MakeRequest(&loader), 0 /* routing_id */, 0 /* request_id */,
0 /* options */, request, client.CreateInterfacePtr(),
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
// Confirm the the resource request failed due to an unreachable proxy.
client.RunUntilComplete();
EXPECT_THAT(client.completion_status().error_code,
net::test::IsError(net::ERR_PROXY_CONNECTION_FAILED));
// Tear down the network context and wait for a pipe error to ensure
// that all queued messages on |proxy_error_client| have been processed.
network_context.reset();
proxy_error_client.RunUntilMojoPipeError();
// Confirm that the ProxyErrorClient received the expected calls.
const auto& request_errors =
proxy_error_client.on_request_maybe_failed_calls();
const auto& pac_errors = proxy_error_client.on_pac_script_error_calls();
ASSERT_EQ(1u, request_errors.size());
EXPECT_THAT(request_errors[0],
net::test::IsError(net::ERR_PROXY_CONNECTION_FAILED));
EXPECT_EQ(0u, pac_errors.size());
}
// Tests that when a ProxyErrorClient is provided to NetworkContextParams, this
// client's OnRequestMaybeFailedDueToProxySettings() method is
// NOT called when a request fails due to a non-proxy related error (in this
// case the target host is unreachable).
TEST_F(NetworkContextTest, ProxyErrorClientNotNotifiedOfUnreachableError) {
// Avoid the test having a network dependency on DNS.
ScopedFailAllHostResolutions fail_dns;
// Set up the NetworkContext that uses the default DIRECT proxy
// configuration.
TestProxyErrorClient proxy_error_client;
mojom::NetworkContextParamsPtr context_params = CreateContextParams();
context_params->proxy_error_client =
proxy_error_client.CreateInterfacePtrInfo();
std::unique_ptr<NetworkContext> network_context =
CreateContextWithParams(std::move(context_params));
// Issue an HTTP request to an unreachable URL.
ResourceRequest request;
request.url = GURL("http://server.bad.dns/fail");
mojom::URLLoaderFactoryPtr loader_factory;
mojom::URLLoaderFactoryParamsPtr loader_params =
mojom::URLLoaderFactoryParams::New();
loader_params->process_id = mojom::kBrowserProcessId;
network_context->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory),
std::move(loader_params));
mojom::URLLoaderPtr loader;
TestURLLoaderClient client;
loader_factory->CreateLoaderAndStart(
mojo::MakeRequest(&loader), 0 /* routing_id */, 0 /* request_id */,
0 /* options */, request, client.CreateInterfacePtr(),
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
// Confirm the the resource request failed.
client.RunUntilComplete();
EXPECT_THAT(client.completion_status().error_code,
net::test::IsError(net::ERR_NAME_NOT_RESOLVED));
// Tear down the network context and wait for a pipe error to ensure
// that all queued messages on |proxy_error_client| have been processed.
network_context.reset();
proxy_error_client.RunUntilMojoPipeError();
// Confirm that the ProxyErrorClient received no calls.
const auto& request_errors =
proxy_error_client.on_request_maybe_failed_calls();
const auto& pac_errors = proxy_error_client.on_pac_script_error_calls();
EXPECT_EQ(0u, request_errors.size());
EXPECT_EQ(0u, pac_errors.size());
}
// Test mojom::ProxyResolver that completes calls to GetProxyForUrl() with a
// DIRECT "proxy". It additionall emits a script error on line 42 for every call
// to GetProxyForUrl().
class MockMojoProxyResolver : public proxy_resolver::mojom::ProxyResolver {
public:
MockMojoProxyResolver() {}
private:
// Overridden from proxy_resolver::mojom::ProxyResolver:
void GetProxyForUrl(
const GURL& url,
proxy_resolver::mojom::ProxyResolverRequestClientPtr client) override {
// Report a Javascript error and then complete the request successfully,
// having chosen DIRECT connections.
client->OnError(42, "Failed: FindProxyForURL(url=" + url.spec() + ")");
net::ProxyInfo result;
result.UseDirect();
client->ReportResult(net::OK, result);
}
DISALLOW_COPY_AND_ASSIGN(MockMojoProxyResolver);
};
// Test mojom::ProxyResolverFactory implementation that successfully completes
// any CreateResolver() requests, and binds the request to a new
// MockMojoProxyResolver.
class MockMojoProxyResolverFactory
: public proxy_resolver::mojom::ProxyResolverFactory {
public:
MockMojoProxyResolverFactory() {}
// Binds and returns a mock ProxyResolverFactory whose lifetime is bound to
// the message pipe.
static proxy_resolver::mojom::ProxyResolverFactoryPtrInfo Create() {
proxy_resolver::mojom::ProxyResolverFactoryPtrInfo ptr_info;
mojo::MakeStrongBinding(std::make_unique<MockMojoProxyResolverFactory>(),
mojo::MakeRequest(&ptr_info));
return ptr_info;
}
private:
void CreateResolver(
const std::string& pac_url,
mojo::InterfaceRequest<proxy_resolver::mojom::ProxyResolver> request,
proxy_resolver::mojom::ProxyResolverFactoryRequestClientPtr client)
override {
// Bind |request| to a new MockMojoProxyResolver, and return success.
mojo::MakeStrongBinding(std::make_unique<MockMojoProxyResolver>(),
std::move(request));
client->ReportResult(net::OK);
}
DISALLOW_COPY_AND_ASSIGN(MockMojoProxyResolverFactory);
};
// Tests that when a ProxyErrorClient is provided to NetworkContextParams, this
// client's OnPACScriptError() method is called whenever the PAC script throws
// an error.
TEST_F(NetworkContextTest, ProxyErrorClientNotifiedOfPacError) {
// Avoid the test having a network dependency on DNS.
ScopedFailAllHostResolutions fail_dns;
// Set up the NetworkContext so that it sends "proxy errors" to
// |proxy_error_client|, and uses a mock ProxyResolverFactory that emits
// script errors.
TestProxyErrorClient proxy_error_client;
mojom::NetworkContextParamsPtr context_params =
mojom::NetworkContextParams::New();
context_params->proxy_error_client =
proxy_error_client.CreateInterfacePtrInfo();
// The PAC URL doesn't matter, since the test is configured to use a
// mock ProxyResolverFactory which doesn't actually evaluate it. It just
// needs to be a data: URL to ensure the network fetch doesn't fail.
//
// That said, the mock PAC evalulator being used behaves similarly to the
// script embedded in the data URL below.
net::ProxyConfig proxy_config = net::ProxyConfig::CreateFromCustomPacURL(
GURL("data:,function FindProxyForURL(url,host){throw url}"));
context_params->initial_proxy_config = net::ProxyConfigWithAnnotation(
proxy_config, TRAFFIC_ANNOTATION_FOR_TESTS);
context_params->proxy_resolver_factory =
MockMojoProxyResolverFactory::Create();
std::unique_ptr<NetworkContext> network_context =
CreateContextWithParams(std::move(context_params));
// Issue an HTTP request. This will end up being sent DIRECT since the PAC
// script is broken.
ResourceRequest request;
request.url = GURL("http://server.bad.dns");
mojom::URLLoaderFactoryPtr loader_factory;
mojom::URLLoaderFactoryParamsPtr loader_params =
mojom::URLLoaderFactoryParams::New();
loader_params->process_id = mojom::kBrowserProcessId;
network_context->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory),
std::move(loader_params));
mojom::URLLoaderPtr loader;
TestURLLoaderClient client;
loader_factory->CreateLoaderAndStart(
mojo::MakeRequest(&loader), 0 /* routing_id */, 0 /* request_id */,
0 /* options */, request, client.CreateInterfacePtr(),
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
// Confirm the the resource request failed.
client.RunUntilComplete();
EXPECT_THAT(client.completion_status().error_code,
net::test::IsError(net::ERR_NAME_NOT_RESOLVED));
// Tear down the network context and wait for a pipe error to ensure
// that all queued messages on |proxy_error_client| have been processed.
network_context.reset();
proxy_error_client.RunUntilMojoPipeError();
// Confirm that the ProxyErrorClient received the expected calls.
const auto& request_errors =
proxy_error_client.on_request_maybe_failed_calls();
const auto& pac_errors = proxy_error_client.on_pac_script_error_calls();
EXPECT_EQ(0u, request_errors.size());
// TODO(eroman): Why is this failing on iOS?
#if defined(OS_IOS)
if (pac_errors.empty())
return;
#endif
ASSERT_EQ(1u, pac_errors.size());
EXPECT_EQ(pac_errors[0].line, 42);
EXPECT_EQ(pac_errors[0].details,
"Failed: FindProxyForURL(url=http://server.bad.dns/)");
}
} // namespace
} // namespace network
......@@ -149,6 +149,10 @@ struct NetworkContextParams {
// ProxyConfigServices be modified not to need this notification?
ProxyConfigPollerClient? proxy_config_poller_client;
// Optional client that will be notified of errors related to the proxy
// settings.
ProxyErrorClient? proxy_error_client;
// When PAC quick checking is enabled, DNS lookups for PAC script's host are
// timed out aggressively. This prevents hanging all network request on DNS
// lookups that are slow or are blockholed, at the cost of making it more
......
......@@ -23,3 +23,26 @@ interface ProxyConfigClient {
interface ProxyConfigPollerClient {
OnLazyProxyConfigPoll();
};
// Called to notify error related to the configured proxy settings.
interface ProxyErrorClient {
// Called when the PAC script being used by the NetworkContext throws a
// JavaScript error or fails to execute. This error is not necessarily
// fatal for URL loading, since by default errors in a PAC script
// result in a fallback to DIRECT connections.
OnPACScriptError(int32 line_number, string details);
// This is a best effort notification that a URL request failed due to
// a problem with the proxy settings. |net_error| is the error code that the
// request failed with.
//
// This only surfaces failures for an entire URL load, and not from
// individual proxy servers. For instance if a PAC script returned 4 proxy
// servers, and sending the request through the first three failed before
// successfulyl sending through the fourth, this method is NOT called.
//
// There is some ambiguity with how errors are classified as being a
// "proxy error". The current implementation includes a mix of
// connection and protocol errors.
OnRequestMaybeFailedDueToProxySettings(int32 net_error);
};
......@@ -91,15 +91,6 @@
-ResourceLoadingHintsBrowserTest.ResourceLoadingHintsHttp
-ResourceLoadingHintsBrowserTest.ResourceLoadingHintsHttpsNoWhitelisted
# https://crbug.com/851609
# Extension listeners for "chrome.proxy.onProxyError" are not called for errors
# in PAC scripts.
-ProxySettingsApiTest.ProxyEventsParseError
# https://crbug.com/876568
# Extension listeners for "chrome.proxy.onProxyError" are not called for
# failures connecting to proxy.
-ProxySettingsApiTest.ProxyEventsInvalidProxy
# https://crbug.com/827582
# Editing response cookies through headers with webRequest is not supported with
# the network service.
......
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