blob: e36c559e8654db63a68f93d1965e6ac7363e1e88 [file] [log] [blame]
// Copyright (c) 2012 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_resolution/proxy_resolution_service.h"
#include <cstdarg>
#include <string>
#include <vector>
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "net/base/net_errors.h"
#include "net/base/proxy_delegate.h"
#include "net/base/proxy_server.h"
#include "net/base/test_completion_callback.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_with_source.h"
#include "net/log/test_net_log.h"
#include "net/log/test_net_log_entry.h"
#include "net/log/test_net_log_util.h"
#include "net/proxy_resolution/dhcp_pac_file_fetcher.h"
#include "net/proxy_resolution/mock_pac_file_fetcher.h"
#include "net/proxy_resolution/mock_proxy_resolver.h"
#include "net/proxy_resolution/pac_file_fetcher.h"
#include "net/proxy_resolution/proxy_config_service.h"
#include "net/proxy_resolution/proxy_resolver.h"
#include "net/test/gtest_util.h"
#include "net/test/test_with_scoped_task_environment.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using testing::ElementsAre;
using testing::Key;
using net::test::IsError;
using net::test::IsOk;
using base::ASCIIToUTF16;
// TODO(eroman): Write a test which exercises
// ProxyResolutionService::SuspendAllPendingRequests().
namespace net {
namespace {
// This polling policy will decide to poll every 1 ms.
class ImmediatePollPolicy : public ProxyResolutionService::PacPollPolicy {
public:
ImmediatePollPolicy() = default;
Mode GetNextDelay(int error,
base::TimeDelta current_delay,
base::TimeDelta* next_delay) const override {
*next_delay = base::TimeDelta::FromMilliseconds(1);
return MODE_USE_TIMER;
}
private:
DISALLOW_COPY_AND_ASSIGN(ImmediatePollPolicy);
};
// This polling policy chooses a fantastically large delay. In other words, it
// will never trigger a poll
class NeverPollPolicy : public ProxyResolutionService::PacPollPolicy {
public:
NeverPollPolicy() = default;
Mode GetNextDelay(int error,
base::TimeDelta current_delay,
base::TimeDelta* next_delay) const override {
*next_delay = base::TimeDelta::FromDays(60);
return MODE_USE_TIMER;
}
private:
DISALLOW_COPY_AND_ASSIGN(NeverPollPolicy);
};
// This polling policy starts a poll immediately after network activity.
class ImmediateAfterActivityPollPolicy
: public ProxyResolutionService::PacPollPolicy {
public:
ImmediateAfterActivityPollPolicy() = default;
Mode GetNextDelay(int error,
base::TimeDelta current_delay,
base::TimeDelta* next_delay) const override {
*next_delay = base::TimeDelta();
return MODE_START_AFTER_ACTIVITY;
}
private:
DISALLOW_COPY_AND_ASSIGN(ImmediateAfterActivityPollPolicy);
};
// This test fixture is used to partially disable the background polling done by
// the ProxyResolutionService (which it uses to detect whenever its PAC script
// contents or WPAD results have changed).
//
// We disable the feature by setting the poll interval to something really
// large, so it will never actually be reached even on the slowest bots that run
// these tests.
//
// We disable the polling in order to avoid any timing dependencies in the
// tests. If the bot were to run the tests very slowly and we hadn't disabled
// polling, then it might start a background re-try in the middle of our test
// and confuse our expectations leading to flaky failures.
//
// The tests which verify the polling code re-enable the polling behavior but
// are careful to avoid timing problems.
class ProxyResolutionServiceTest : public TestWithScopedTaskEnvironment {
protected:
void SetUp() override {
testing::Test::SetUp();
previous_policy_ =
ProxyResolutionService::set_pac_script_poll_policy(&never_poll_policy_);
}
void TearDown() override {
// Restore the original policy.
ProxyResolutionService::set_pac_script_poll_policy(previous_policy_);
testing::Test::TearDown();
}
private:
NeverPollPolicy never_poll_policy_;
const ProxyResolutionService::PacPollPolicy* previous_policy_;
};
const char kValidPacScript1[] = "pac-script-v1-FindProxyForURL";
const char kValidPacScript2[] = "pac-script-v2-FindProxyForURL";
class MockProxyConfigService: public ProxyConfigService {
public:
explicit MockProxyConfigService(const ProxyConfig& config)
: availability_(CONFIG_VALID),
config_(
ProxyConfigWithAnnotation(config, TRAFFIC_ANNOTATION_FOR_TESTS)) {}
explicit MockProxyConfigService(const std::string& pac_url)
: availability_(CONFIG_VALID),
config_(ProxyConfig::CreateFromCustomPacURL(GURL(pac_url)),
TRAFFIC_ANNOTATION_FOR_TESTS) {}
void AddObserver(Observer* observer) override {
observers_.AddObserver(observer);
}
void RemoveObserver(Observer* observer) override {
observers_.RemoveObserver(observer);
}
ConfigAvailability GetLatestProxyConfig(
ProxyConfigWithAnnotation* results) override {
if (availability_ == CONFIG_VALID)
*results = config_;
return availability_;
}
void SetConfig(const ProxyConfigWithAnnotation& config) {
availability_ = CONFIG_VALID;
config_ = config;
for (auto& observer : observers_)
observer.OnProxyConfigChanged(config_, availability_);
}
void SetPacUrlConfig(base::StringPiece pac_url) {
SetConfig(ProxyConfigWithAnnotation(
ProxyConfig::CreateFromCustomPacURL(GURL(pac_url)),
TRAFFIC_ANNOTATION_FOR_TESTS));
}
private:
ConfigAvailability availability_;
ProxyConfigWithAnnotation config_;
base::ObserverList<Observer, true>::Unchecked observers_;
};
// A test network delegate that exercises the OnResolveProxy callback.
class TestResolveProxyDelegate : public ProxyDelegate {
public:
void OnResolveProxy(const GURL& url,
const std::string& method,
const ProxyRetryInfoMap& proxy_retry_info,
ProxyInfo* result) override {
method_ = method;
num_resolve_proxy_called_++;
proxy_retry_info_ = proxy_retry_info;
DCHECK(!add_proxy_ || !remove_proxy_);
if (add_proxy_) {
result->UseNamedProxy("delegate_proxy.com");
} else if (remove_proxy_) {
result->UseDirect();
}
}
int num_resolve_proxy_called() const { return num_resolve_proxy_called_; }
const std::string& method() const { return method_; }
void set_add_proxy(bool add_proxy) {
add_proxy_ = add_proxy;
}
void set_remove_proxy(bool remove_proxy) {
remove_proxy_ = remove_proxy;
}
const ProxyRetryInfoMap& proxy_retry_info() const {
return proxy_retry_info_;
}
void OnFallback(const ProxyServer& bad_proxy, int net_error) override {}
private:
int num_resolve_proxy_called_ = 0;
bool add_proxy_ = false;
bool remove_proxy_ = false;
std::string method_;
ProxyRetryInfoMap proxy_retry_info_;
};
// A test network delegate that exercises the OnProxyFallback callback.
class TestProxyFallbackProxyDelegate : public ProxyDelegate {
public:
// ProxyDelegate implementation:
void OnResolveProxy(const GURL& url,
const std::string& method,
const ProxyRetryInfoMap& proxy_retry_info,
ProxyInfo* result) override {}
void OnFallback(const ProxyServer& bad_proxy, int net_error) override {
proxy_server_ = bad_proxy;
last_proxy_fallback_net_error_ = net_error;
num_proxy_fallback_called_++;
}
bool num_proxy_fallback_called() const { return num_proxy_fallback_called_; }
const ProxyServer& proxy_server() const {
return proxy_server_;
}
int last_proxy_fallback_net_error() const {
return last_proxy_fallback_net_error_;
}
private:
int num_proxy_fallback_called_ = 0;
ProxyServer proxy_server_;
int last_proxy_fallback_net_error_ = OK;
};
using JobMap = std::map<GURL, MockAsyncProxyResolver::Job*>;
// Given a jobmap and a list of target URLs |urls|, asserts that the set of URLs
// of the jobs appearing in |list| is exactly the set of URLs in |urls|.
JobMap GetJobsForURLs(const JobMap& map, const std::vector<GURL>& urls) {
size_t a = urls.size();
size_t b = map.size();
if (a != b) {
ADD_FAILURE() << "map size (" << map.size() << ") != urls size ("
<< urls.size() << ")";
return map;
}
for (const auto& it : urls) {
if (map.count(it) != 1U) {
ADD_FAILURE() << "url not in map: " << it.spec();
break;
}
}
return map;
}
// Given a MockAsyncProxyResolver |resolver| and some GURLs, validates that the
// set of pending request URLs for |resolver| is exactly the supplied list of
// URLs and returns a map from URLs to the corresponding pending jobs.
JobMap GetPendingJobsForURLs(const MockAsyncProxyResolver& resolver,
const GURL& url1 = GURL(),
const GURL& url2 = GURL(),
const GURL& url3 = GURL()) {
std::vector<GURL> urls;
if (!url1.is_empty())
urls.push_back(url1);
if (!url2.is_empty())
urls.push_back(url2);
if (!url3.is_empty())
urls.push_back(url3);
JobMap map;
for (MockAsyncProxyResolver::Job* it : resolver.pending_jobs()) {
DCHECK(it);
map[it->url()] = it;
}
return GetJobsForURLs(map, urls);
}
// Given a MockAsyncProxyResolver |resolver| and some GURLs, validates that the
// set of cancelled request URLs for |resolver| is exactly the supplied list of
// URLs and returns a map from URLs to the corresponding cancelled jobs.
JobMap GetCancelledJobsForURLs(const MockAsyncProxyResolver& resolver,
const GURL& url1 = GURL(),
const GURL& url2 = GURL(),
const GURL& url3 = GURL()) {
std::vector<GURL> urls;
if (!url1.is_empty())
urls.push_back(url1);
if (!url2.is_empty())
urls.push_back(url2);
if (!url3.is_empty())
urls.push_back(url3);
JobMap map;
for (const std::unique_ptr<MockAsyncProxyResolver::Job>& it :
resolver.cancelled_jobs()) {
DCHECK(it);
map[it->url()] = it.get();
}
return GetJobsForURLs(map, urls);
}
// Helper class to verify the bucket counts for PacUrlScheme histogram.
class PacUrlSchemeHistogramTester {
public:
void VerifyHistogram() const {
const char kPacUrlSchemeHistogram[] =
"Net.ProxyResolutionService.PacUrlScheme";
int total = GetTotal();
histograms_.ExpectTotalCount(kPacUrlSchemeHistogram, total);
if (total > 0) {
histograms_.ExpectBucketCount(kPacUrlSchemeHistogram, 0, num_other);
histograms_.ExpectBucketCount(kPacUrlSchemeHistogram, 1, num_http);
histograms_.ExpectBucketCount(kPacUrlSchemeHistogram, 2, num_https);
histograms_.ExpectBucketCount(kPacUrlSchemeHistogram, 3, num_ftp);
histograms_.ExpectBucketCount(kPacUrlSchemeHistogram, 4, num_file);
histograms_.ExpectBucketCount(kPacUrlSchemeHistogram, 5, num_data);
}
}
int num_http = 0;
int num_https = 0;
int num_ftp = 0;
int num_data = 0;
int num_file = 0;
int num_other = 0;
private:
int GetTotal() const {
return num_http + num_https + num_ftp + num_data + num_file + num_other;
}
base::HistogramTester histograms_;
};
} // namespace
TEST_F(ProxyResolutionServiceTest, Direct) {
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(ProxyConfig::CreateDirect()),
base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
ProxyInfo info;
TestCompletionCallback callback;
BoundTestNetLog log;
std::unique_ptr<ProxyResolutionService::Request> request;
int rv = service.ResolveProxy(url, std::string(), &info, callback.callback(),
&request, log.bound());
EXPECT_THAT(rv, IsOk());
EXPECT_TRUE(factory->pending_requests().empty());
EXPECT_TRUE(info.is_direct());
EXPECT_TRUE(info.proxy_resolve_start_time().is_null());
EXPECT_TRUE(info.proxy_resolve_end_time().is_null());
// Check the NetLog was filled correctly.
TestNetLogEntry::List entries;
log.GetEntries(&entries);
EXPECT_EQ(3u, entries.size());
EXPECT_TRUE(LogContainsBeginEvent(entries, 0,
NetLogEventType::PROXY_RESOLUTION_SERVICE));
EXPECT_TRUE(LogContainsEvent(
entries, 1, NetLogEventType::PROXY_RESOLUTION_SERVICE_RESOLVED_PROXY_LIST,
NetLogEventPhase::NONE));
EXPECT_TRUE(LogContainsEndEvent(entries, 2,
NetLogEventType::PROXY_RESOLUTION_SERVICE));
}
TEST_F(ProxyResolutionServiceTest, OnResolveProxyCallbackAddProxy) {
ProxyConfig config;
config.proxy_rules().ParseFromString("badproxy:8080,foopy1:8080");
config.set_auto_detect(false);
config.proxy_rules().bypass_rules.ParseFromString("*.org");
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
GURL url("http://www.google.com/");
GURL bypass_url("http://internet.org");
ProxyInfo info;
TestCompletionCallback callback;
BoundTestNetLog log;
// First, warm up the ProxyResolutionService and fake an error to mark the
// first server as bad.
std::unique_ptr<ProxyResolutionService::Request> request;
int rv = service.ResolveProxy(url, std::string(), &info, callback.callback(),
&request, log.bound());
EXPECT_THAT(rv, IsOk());
EXPECT_EQ("badproxy:8080", info.proxy_server().ToURI());
EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
service.ReportSuccess(info);
// Verify that network delegate is invoked.
TestResolveProxyDelegate delegate;
service.SetProxyDelegate(&delegate);
rv = service.ResolveProxy(url, "GET", &info, callback.callback(), &request,
log.bound());
EXPECT_EQ(1, delegate.num_resolve_proxy_called());
EXPECT_THAT(delegate.proxy_retry_info(), ElementsAre(Key("badproxy:8080")));
EXPECT_EQ(delegate.method(), "GET");
// Verify that the ProxyDelegate's behavior is stateless across
// invocations of ResolveProxy. Start by having the callback add a proxy
// and checking that subsequent jobs are not affected.
delegate.set_add_proxy(true);
// Callback should interpose:
rv = service.ResolveProxy(url, "GET", &info, callback.callback(), &request,
log.bound());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ(info.proxy_server().host_port_pair().host(), "delegate_proxy.com");
delegate.set_add_proxy(false);
// Check non-bypassed URL:
rv = service.ResolveProxy(url, "GET", &info, callback.callback(), &request,
log.bound());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ(info.proxy_server().host_port_pair().host(), "foopy1");
// Check bypassed URL:
rv = service.ResolveProxy(bypass_url, "GET", &info, callback.callback(),
&request, log.bound());
EXPECT_TRUE(info.is_direct());
}
TEST_F(ProxyResolutionServiceTest, OnResolveProxyCallbackRemoveProxy) {
// Same as OnResolveProxyCallbackAddProxy, but verify that the
// ProxyDelegate's behavior is stateless across invocations after it
// *removes* a proxy.
ProxyConfig config;
config.proxy_rules().ParseFromString("foopy1:8080");
config.set_auto_detect(false);
config.proxy_rules().bypass_rules.ParseFromString("*.org");
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
GURL url("http://www.google.com/");
GURL bypass_url("http://internet.org");
ProxyInfo info;
TestCompletionCallback callback;
BoundTestNetLog log;
// First, warm up the ProxyResolutionService.
std::unique_ptr<ProxyResolutionService::Request> request;
int rv = service.ResolveProxy(url, std::string(), &info, callback.callback(),
&request, log.bound());
EXPECT_THAT(rv, IsOk());
TestResolveProxyDelegate delegate;
service.SetProxyDelegate(&delegate);
delegate.set_remove_proxy(true);
// Callback should interpose:
rv = service.ResolveProxy(url, "GET", &info, callback.callback(), &request,
log.bound());
EXPECT_TRUE(info.is_direct());
delegate.set_remove_proxy(false);
// Check non-bypassed URL:
rv = service.ResolveProxy(url, "GET", &info, callback.callback(), &request,
log.bound());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ(info.proxy_server().host_port_pair().host(), "foopy1");
// Check bypassed URL:
rv = service.ResolveProxy(bypass_url, "GET", &info, callback.callback(),
&request, log.bound());
EXPECT_TRUE(info.is_direct());
}
// Test callback that deletes an item when called. This is used to test various
// permutations of important objects being deleted in the middle of a series of
// requests.
template <typename T>
class DeletingCallback : public TestCompletionCallbackBase {
public:
explicit DeletingCallback(std::unique_ptr<T>* deletee);
~DeletingCallback() override;
CompletionOnceCallback callback() {
return base::BindOnce(&DeletingCallback::DeleteItem,
base::Unretained(this));
}
private:
void DeleteItem(int result) {
deletee_->reset();
SetResult(result);
}
std::unique_ptr<T>* deletee_;
DISALLOW_COPY_AND_ASSIGN(DeletingCallback);
};
template <typename T>
DeletingCallback<T>::DeletingCallback(std::unique_ptr<T>* deletee)
: deletee_(deletee) {}
template <typename T>
DeletingCallback<T>::~DeletingCallback() = default;
// Test that the ProxyResolutionService correctly handles the case where a
// request callback deletes another request.
TEST_F(ProxyResolutionServiceTest, CallbackDeletesRequest) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
std::unique_ptr<ProxyResolutionService> service =
std::make_unique<ProxyResolutionService>(
base::WrapUnique(config_service), base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
GURL url2("http://www.example.com/");
ProxyInfo info;
std::unique_ptr<ProxyResolutionService::Request> request, request2;
DeletingCallback<ProxyResolutionService::Request> callback(&request2);
net::CompletionOnceCallback callback2 =
base::BindOnce([](int result) { ASSERT_FALSE(true); });
int rv = service->ResolveProxy(url, std::string(), &info, callback.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = service->ResolveProxy(url2, std::string(), &info, std::move(callback2),
&request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Run pending requests.
ASSERT_EQ(1u, factory->pending_requests().size());
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(2u, resolver.pending_jobs().size());
// Job order is nondeterministic, as requests are stored in an std::set, so
// this loop figures out which one is the correct one to start.
int deleting_job = 2;
for (int i = 0; i < 2; i++) {
if (resolver.pending_jobs()[i]->url() == url) {
deleting_job = i;
break;
}
ASSERT_LE(i, 1); // The loop should never actually make it to the end.
}
// Set the result in proxy resolver.
resolver.pending_jobs()[deleting_job]->results()->UseNamedProxy("foopy");
resolver.pending_jobs()[deleting_job]->CompleteNow(OK);
//// Only one of the callbacks should have been run:
EXPECT_TRUE(callback.have_result());
EXPECT_THAT(callback.WaitForResult(), IsOk());
ASSERT_EQ(0u, resolver.pending_jobs().size());
ASSERT_EQ(1u, resolver.cancelled_jobs().size());
ASSERT_EQ(url2, resolver.cancelled_jobs()[0]->url());
}
// Test that the ProxyResolutionService correctly handles the case where a
// request callback deletes another request. (Triggered by the loop in
// ProxyResolutionService's destructor).
TEST_F(ProxyResolutionServiceTest, CallbackDeletesRequestDuringDestructor) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
std::unique_ptr<ProxyResolutionService> service =
std::make_unique<ProxyResolutionService>(
base::WrapUnique(config_service), base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
ProxyInfo info;
std::unique_ptr<ProxyResolutionService::Request> request, request2;
DeletingCallback<ProxyResolutionService::Request> callback(&request2),
callback2(&request);
int rv = service->ResolveProxy(url, std::string(), &info, callback.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = service->ResolveProxy(url, std::string(), &info, callback2.callback(),
&request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Make sure that ProxyResolutionServices is deleted before the requests, as
// this triggers completion of the pending requests.
service.reset();
// Only one of the callbacks should have been run:
EXPECT_TRUE(callback.have_result() ^ callback2.have_result());
// Callbacks run during destruction of ProxyResolutionService for Requests
// that have not been started are called with net::ERR_ABORTED
if (callback.have_result()) {
EXPECT_THAT(callback.WaitForResult(),
IsError(net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED));
}
if (callback2.have_result()) {
EXPECT_THAT(callback2.WaitForResult(),
IsError(net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED));
}
}
// Test that the ProxyResolutionService correctly handles the case where a
// request callback deletes its own handle.
TEST_F(ProxyResolutionServiceTest, CallbackDeletesSelf) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
std::unique_ptr<ProxyResolutionService> service =
std::make_unique<ProxyResolutionService>(
base::WrapUnique(config_service), base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
ProxyInfo info;
std::unique_ptr<ProxyResolutionService::Request> request1;
TestCompletionCallback callback1;
int rv =
service->ResolveProxy(url, std::string(), &info, callback1.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
GURL url2("http://www.example.com/");
std::unique_ptr<ProxyResolutionService::Request> request2;
DeletingCallback<ProxyResolutionService::Request> callback2(&request2);
rv = service->ResolveProxy(url2, std::string(), &info, callback2.callback(),
&request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
std::unique_ptr<ProxyResolutionService::Request> request3;
TestCompletionCallback callback3;
rv = service->ResolveProxy(url, std::string(), &info, callback3.callback(),
&request3, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ASSERT_EQ(1u, factory->pending_requests().size());
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(3u, resolver.pending_jobs().size());
// Job order is nondeterministic, as requests are stored in an std::set, so
// this loop figures out which one is the correct one to start.
int self_deleting_job = 3;
for (int i = 0; i < 3; i++) {
if (resolver.pending_jobs()[i]->url() == url2) {
self_deleting_job = i;
break;
}
ASSERT_LE(i, 2); // The loop should never actually make it to the end.
}
// Set the result in proxy resolver.
resolver.pending_jobs()[self_deleting_job]->results()->UseNamedProxy("foopy");
resolver.pending_jobs()[self_deleting_job]->CompleteNow(OK);
ASSERT_EQ(2u, resolver.pending_jobs().size());
ASSERT_EQ(0u, resolver.cancelled_jobs().size());
ASSERT_EQ(url, resolver.pending_jobs()[0]->url());
ASSERT_EQ(url, resolver.pending_jobs()[1]->url());
}
// Test that the ProxyResolutionService correctly handles the case where a
// request callback deletes its own handle, when triggered by
// ProxyResolutionService's destructor.
TEST_F(ProxyResolutionServiceTest, CallbackDeletesSelfDuringDestructor) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
std::unique_ptr<ProxyResolutionService> service =
std::make_unique<ProxyResolutionService>(
base::WrapUnique(config_service), base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
ProxyInfo info;
std::unique_ptr<ProxyResolutionService::Request> request1;
TestCompletionCallback callback1;
int rv =
service->ResolveProxy(url, std::string(), &info, callback1.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
std::unique_ptr<ProxyResolutionService::Request> request2;
DeletingCallback<ProxyResolutionService::Request> callback2(&request2);
rv = service->ResolveProxy(url, std::string(), &info, callback2.callback(),
&request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
std::unique_ptr<ProxyResolutionService::Request> request3;
TestCompletionCallback callback3;
rv = service->ResolveProxy(url, std::string(), &info, callback3.callback(),
&request3, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
service.reset();
EXPECT_THAT(callback1.WaitForResult(),
IsError(net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED));
EXPECT_THAT(callback2.WaitForResult(),
IsError(net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED));
EXPECT_THAT(callback3.WaitForResult(),
IsError(net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED));
}
TEST_F(ProxyResolutionServiceTest, ProxyServiceDeletedBeforeRequest) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
GURL url("http://www.google.com/");
ProxyInfo info;
TestCompletionCallback callback;
std::unique_ptr<ProxyResolutionService::Request> request;
BoundTestNetLog log;
int rv;
{
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
rv = service.ResolveProxy(url, std::string(), &info, callback.callback(),
&request, log.bound());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(LOAD_STATE_RESOLVING_PROXY_FOR_URL, request->GetLoadState());
ASSERT_EQ(1u, factory->pending_requests().size());
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
}
ASSERT_EQ(0u, resolver.pending_jobs().size());
EXPECT_THAT(callback.WaitForResult(), IsOk());
}
// Test that the ProxyResolutionService correctly handles the case where a
// request callback deletes the service.
TEST_F(ProxyResolutionServiceTest, CallbackDeletesService) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
std::unique_ptr<ProxyResolutionService> service =
std::make_unique<ProxyResolutionService>(
base::WrapUnique(config_service), base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
ProxyInfo info;
DeletingCallback<ProxyResolutionService> callback(&service);
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv = service->ResolveProxy(url, std::string(), &info, callback.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(LOAD_STATE_RESOLVING_PROXY_FOR_URL, request1->GetLoadState());
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv = service->ResolveProxy(url, std::string(), &info, callback2.callback(),
&request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
TestCompletionCallback callback3;
std::unique_ptr<ProxyResolutionService::Request> request3;
rv = service->ResolveProxy(url, std::string(), &info, callback3.callback(),
&request3, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
config_service->SetConfig(ProxyConfigWithAnnotation(
ProxyConfig::CreateDirect(), TRAFFIC_ANNOTATION_FOR_TESTS));
ASSERT_EQ(0u, resolver.pending_jobs().size());
ASSERT_THAT(callback.WaitForResult(), IsOk());
ASSERT_THAT(callback2.WaitForResult(), IsOk());
ASSERT_THAT(callback3.WaitForResult(), IsOk());
}
TEST_F(ProxyResolutionServiceTest, PAC) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
ProxyInfo info;
TestCompletionCallback callback;
std::unique_ptr<ProxyResolutionService::Request> request;
BoundTestNetLog log;
int rv = service.ResolveProxy(url, std::string(), &info, callback.callback(),
&request, log.bound());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(LOAD_STATE_RESOLVING_PROXY_FOR_URL, request->GetLoadState());
ASSERT_EQ(1u, factory->pending_requests().size());
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// Set the result in proxy resolver.
resolver.pending_jobs()[0]->results()->UseNamedProxy("foopy");
resolver.pending_jobs()[0]->CompleteNow(OK);
EXPECT_THAT(callback.WaitForResult(), IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy:80", info.proxy_server().ToURI());
EXPECT_TRUE(info.did_use_pac_script());
EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
// Check the NetLog was filled correctly.
TestNetLogEntry::List entries;
log.GetEntries(&entries);
EXPECT_EQ(5u, entries.size());
EXPECT_TRUE(LogContainsBeginEvent(entries, 0,
NetLogEventType::PROXY_RESOLUTION_SERVICE));
EXPECT_TRUE(LogContainsBeginEvent(
entries, 1,
NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC));
EXPECT_TRUE(LogContainsEndEvent(
entries, 2,
NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC));
EXPECT_TRUE(LogContainsEndEvent(entries, 4,
NetLogEventType::PROXY_RESOLUTION_SERVICE));
}
// Test that the proxy resolver does not see the URL's username/password
// or its reference section.
TEST_F(ProxyResolutionServiceTest, PAC_NoIdentityOrHash) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
GURL url("http://username:password@www.google.com/?ref#hash#hash");
ProxyInfo info;
TestCompletionCallback callback;
std::unique_ptr<ProxyResolutionService::Request> request;
int rv = service.ResolveProxy(url, std::string(), &info, callback.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
// The URL should have been simplified, stripping the username/password/hash.
EXPECT_EQ(GURL("http://www.google.com/?ref"),
resolver.pending_jobs()[0]->url());
// We end here without ever completing the request -- destruction of
// ProxyResolutionService will cancel the outstanding request.
}
TEST_F(ProxyResolutionServiceTest, PAC_FailoverWithoutDirect) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
ProxyInfo info;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv = service.ResolveProxy(url, std::string(), &info, callback1.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// Set the result in proxy resolver.
resolver.pending_jobs()[0]->results()->UseNamedProxy("foopy:8080");
resolver.pending_jobs()[0]->CompleteNow(OK);
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy:8080", info.proxy_server().ToURI());
EXPECT_TRUE(info.did_use_pac_script());
EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
// Now, imagine that connecting to foopy:8080 fails: there is nothing
// left to fallback to, since our proxy list was NOT terminated by
// DIRECT.
EXPECT_FALSE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
EXPECT_TRUE(info.is_empty());
}
// Test that if the execution of the PAC script fails (i.e. javascript runtime
// error), and the PAC settings are non-mandatory, that we fall-back to direct.
TEST_F(ProxyResolutionServiceTest, PAC_RuntimeError) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
GURL url("http://this-causes-js-error/");
ProxyInfo info;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv = service.ResolveProxy(url, std::string(), &info, callback1.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// Simulate a failure in the PAC executor.
resolver.pending_jobs()[0]->CompleteNow(ERR_PAC_SCRIPT_FAILED);
EXPECT_THAT(callback1.WaitForResult(), IsOk());
// Since the PAC script was non-mandatory, we should have fallen-back to
// DIRECT.
EXPECT_TRUE(info.is_direct());
EXPECT_TRUE(info.did_use_pac_script());
EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
}
// The proxy list could potentially contain the DIRECT fallback choice
// in a location other than the very end of the list, and could even
// specify it multiple times.
//
// This is not a typical usage, but we will obey it.
// (If we wanted to disallow this type of input, the right place to
// enforce it would be in parsing the PAC result string).
//
// This test will use the PAC result string:
//
// "DIRECT ; PROXY foobar:10 ; DIRECT ; PROXY foobar:20"
//
// For which we expect it to try DIRECT, then foobar:10, then DIRECT again,
// then foobar:20, and then give up and error.
//
// The important check of this test is to make sure that DIRECT is not somehow
// cached as being a bad proxy.
TEST_F(ProxyResolutionServiceTest, PAC_FailoverAfterDirect) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
ProxyInfo info;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv = service.ResolveProxy(url, std::string(), &info, callback1.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// Set the result in proxy resolver.
resolver.pending_jobs()[0]->results()->UsePacString(
"DIRECT ; PROXY foobar:10 ; DIRECT ; PROXY foobar:20");
resolver.pending_jobs()[0]->CompleteNow(OK);
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_TRUE(info.is_direct());
// Fallback 1.
EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foobar:10", info.proxy_server().ToURI());
// Fallback 2.
EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
EXPECT_TRUE(info.is_direct());
// Fallback 3.
EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foobar:20", info.proxy_server().ToURI());
// Fallback 4 -- Nothing to fall back to!
EXPECT_FALSE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
EXPECT_TRUE(info.is_empty());
}
TEST_F(ProxyResolutionServiceTest, PAC_ConfigSourcePropagates) {
// Test whether the ProxyConfigSource set by the ProxyConfigService is applied
// to ProxyInfo after the proxy is resolved via a PAC script.
ProxyConfig config =
ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy/proxy.pac"));
MockProxyConfigService* config_service = new MockProxyConfigService(config);
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
// Resolve something.
GURL url("http://www.google.com/");
ProxyInfo info;
TestCompletionCallback callback;
std::unique_ptr<ProxyResolutionService::Request> request;
int rv = service.ResolveProxy(url, std::string(), &info, callback.callback(),
&request, NetLogWithSource());
ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
// Set the result in proxy resolver.
resolver.pending_jobs()[0]->results()->UseNamedProxy("foopy");
resolver.pending_jobs()[0]->CompleteNow(OK);
EXPECT_THAT(callback.WaitForResult(), IsOk());
EXPECT_EQ(MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
info.traffic_annotation());
EXPECT_TRUE(info.did_use_pac_script());
EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
}
TEST_F(ProxyResolutionServiceTest, ProxyResolverFails) {
// Test what happens when the ProxyResolver fails. The download and setting
// of the PAC script have already succeeded, so this corresponds with a
// javascript runtime error while calling FindProxyForURL().
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
// Start first resolve request.
GURL url("http://www.google.com/");
ProxyInfo info;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request;
int rv = service.ResolveProxy(url, std::string(), &info, callback1.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// Fail the first resolve request in MockAsyncProxyResolver.
resolver.pending_jobs()[0]->CompleteNow(ERR_FAILED);
// Although the proxy resolver failed the request, ProxyResolutionService
// implicitly falls-back to DIRECT.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_TRUE(info.is_direct());
// Failed PAC executions still have proxy resolution times.
EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
// The second resolve request will try to run through the proxy resolver,
// regardless of whether the first request failed in it.
TestCompletionCallback callback2;
rv = service.ResolveProxy(url, std::string(), &info, callback2.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// This time we will have the resolver succeed (perhaps the PAC script has
// a dependency on the current time).
resolver.pending_jobs()[0]->results()->UseNamedProxy("foopy_valid:8080");
resolver.pending_jobs()[0]->CompleteNow(OK);
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy_valid:8080", info.proxy_server().ToURI());
}
TEST_F(ProxyResolutionServiceTest, ProxyResolverTerminatedDuringRequest) {
// Test what happens when the ProxyResolver fails with a fatal error while
// a GetProxyForURL() call is in progress.
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
// Start first resolve request.
GURL url("http://www.google.com/");
ProxyInfo info;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request;
int rv = service.ResolveProxy(url, std::string(), &info, callback1.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ASSERT_EQ(1u, factory->pending_requests().size());
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// Fail the first resolve request in MockAsyncProxyResolver.
resolver.pending_jobs()[0]->CompleteNow(ERR_PAC_SCRIPT_TERMINATED);
// Although the proxy resolver failed the request, ProxyResolutionService
// implicitly falls-back to DIRECT.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_TRUE(info.is_direct());
// Failed PAC executions still have proxy resolution times.
EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
// With no other requests, the ProxyResolutionService waits for a new request
// before initializing a new ProxyResolver.
EXPECT_TRUE(factory->pending_requests().empty());
TestCompletionCallback callback2;
rv = service.ResolveProxy(url, std::string(), &info, callback2.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ASSERT_EQ(1u, factory->pending_requests().size());
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// This time we will have the resolver succeed.
resolver.pending_jobs()[0]->results()->UseNamedProxy("foopy_valid:8080");
resolver.pending_jobs()[0]->CompleteNow(OK);
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy_valid:8080", info.proxy_server().ToURI());
}
TEST_F(ProxyResolutionServiceTest,
ProxyResolverTerminatedDuringRequestWithConcurrentRequest) {
// Test what happens when the ProxyResolver fails with a fatal error while
// a GetProxyForURL() call is in progress.
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
// Start two resolve requests.
GURL url1("http://www.google.com/");
GURL url2("https://www.google.com/");
ProxyInfo info;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1, request2;
int rv =
service.ResolveProxy(url1, std::string(), &info, callback1.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
TestCompletionCallback callback2;
rv = service.ResolveProxy(url2, std::string(), &info, callback2.callback(),
&request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ASSERT_EQ(1u, factory->pending_requests().size());
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
JobMap jobs = GetPendingJobsForURLs(resolver, url1, url2);
// Fail the first resolve request in MockAsyncProxyResolver.
jobs[url1]->CompleteNow(ERR_PAC_SCRIPT_TERMINATED);
// Although the proxy resolver failed the request, ProxyResolutionService
// implicitly falls-back to DIRECT.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_TRUE(info.is_direct());
// Failed PAC executions still have proxy resolution times.
EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
// The second request is cancelled when the proxy resolver terminates.
jobs = GetCancelledJobsForURLs(resolver, url2);
// Since a second request was in progress, the ProxyResolutionService starts
// initializating a new ProxyResolver.
ASSERT_EQ(1u, factory->pending_requests().size());
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
jobs = GetPendingJobsForURLs(resolver, url2);
// This request succeeds.
jobs[url2]->results()->UseNamedProxy("foopy_valid:8080");
jobs[url2]->CompleteNow(OK);
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy_valid:8080", info.proxy_server().ToURI());
}
TEST_F(ProxyResolutionServiceTest, PacFileFetcherFailsDownloadingMandatoryPac) {
// Test what happens when the ProxyResolver fails to download a mandatory PAC
// script.
ProxyConfig config(
ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy/proxy.pac")));
config.set_pac_mandatory(true);
MockProxyConfigService* config_service = new MockProxyConfigService(config);
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
// Start first resolve request.
GURL url("http://www.google.com/");
ProxyInfo info;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request;
int rv = service.ResolveProxy(url, std::string(), &info, callback1.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNow(ERR_FAILED, nullptr);
ASSERT_EQ(0u, factory->pending_requests().size());
// As the proxy resolver factory failed the request and is configured for a
// mandatory PAC script, ProxyResolutionService must not implicitly fall-back
// to DIRECT.
EXPECT_EQ(ERR_MANDATORY_PROXY_CONFIGURATION_FAILED,
callback1.WaitForResult());
EXPECT_FALSE(info.is_direct());
// As the proxy resolver factory failed the request and is configured for a
// mandatory PAC script, ProxyResolutionService must not implicitly fall-back
// to DIRECT.
TestCompletionCallback callback2;
rv = service.ResolveProxy(url, std::string(), &info, callback2.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_MANDATORY_PROXY_CONFIGURATION_FAILED));
EXPECT_FALSE(info.is_direct());
}
TEST_F(ProxyResolutionServiceTest,
ProxyResolverFailsParsingJavaScriptMandatoryPac) {
// Test what happens when the ProxyResolver fails that is configured to use a
// mandatory PAC script. The download of the PAC script has already
// succeeded but the PAC script contains no valid javascript.
ProxyConfig config(
ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy/proxy.pac")));
config.set_pac_mandatory(true);
MockProxyConfigService* config_service = new MockProxyConfigService(config);
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start resolve request.
GURL url("http://www.google.com/");
ProxyInfo info;
TestCompletionCallback callback;
std::unique_ptr<ProxyResolutionService::Request> request;
int rv = service.ResolveProxy(url, std::string(), &info, callback.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Check that nothing has been sent to the proxy resolver factory yet.
ASSERT_EQ(0u, factory->pending_requests().size());
// Downloading the PAC script succeeds.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
fetcher->NotifyFetchCompletion(OK, "invalid-script-contents");
EXPECT_FALSE(fetcher->has_pending_request());
ASSERT_EQ(0u, factory->pending_requests().size());
// Since PacFileDecider failed to identify a valid PAC and PAC was
// mandatory for this configuration, the ProxyResolutionService must not
// implicitly fall-back to DIRECT.
EXPECT_EQ(ERR_MANDATORY_PROXY_CONFIGURATION_FAILED,
callback.WaitForResult());
EXPECT_FALSE(info.is_direct());
}
TEST_F(ProxyResolutionServiceTest, ProxyResolverFailsInJavaScriptMandatoryPac) {
// Test what happens when the ProxyResolver fails that is configured to use a
// mandatory PAC script. The download and setting of the PAC script have
// already succeeded, so this corresponds with a javascript runtime error
// while calling FindProxyForURL().
ProxyConfig config(
ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy/proxy.pac")));
config.set_pac_mandatory(true);
MockProxyConfigService* config_service = new MockProxyConfigService(config);
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
// Start first resolve request.
GURL url("http://www.google.com/");
ProxyInfo info;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request;
int rv = service.ResolveProxy(url, std::string(), &info, callback1.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// Fail the first resolve request in MockAsyncProxyResolver.
resolver.pending_jobs()[0]->CompleteNow(ERR_FAILED);
// As the proxy resolver failed the request and is configured for a mandatory
// PAC script, ProxyResolutionService must not implicitly fall-back to DIRECT.
EXPECT_EQ(ERR_MANDATORY_PROXY_CONFIGURATION_FAILED,
callback1.WaitForResult());
EXPECT_FALSE(info.is_direct());
// The second resolve request will try to run through the proxy resolver,
// regardless of whether the first request failed in it.
TestCompletionCallback callback2;
rv = service.ResolveProxy(url, std::string(), &info, callback2.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// This time we will have the resolver succeed (perhaps the PAC script has
// a dependency on the current time).
resolver.pending_jobs()[0]->results()->UseNamedProxy("foopy_valid:8080");
resolver.pending_jobs()[0]->CompleteNow(OK);
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy_valid:8080", info.proxy_server().ToURI());
}
TEST_F(ProxyResolutionServiceTest, ProxyFallback) {
// Test what happens when we specify multiple proxy servers and some of them
// are bad.
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
// Get the proxy information.
ProxyInfo info;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request;
int rv = service.ResolveProxy(url, std::string(), &info, callback1.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// Set the result in proxy resolver.
resolver.pending_jobs()[0]->results()->UseNamedProxy(
"foopy1:8080;foopy2:9090");
resolver.pending_jobs()[0]->CompleteNow(OK);
// The first item is valid.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
base::TimeTicks proxy_resolve_start_time = info.proxy_resolve_start_time();
base::TimeTicks proxy_resolve_end_time = info.proxy_resolve_end_time();
// Fake an error on the proxy.
EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
// Proxy times should not have been modified by fallback.
EXPECT_EQ(proxy_resolve_start_time, info.proxy_resolve_start_time());
EXPECT_EQ(proxy_resolve_end_time, info.proxy_resolve_end_time());
// The second proxy should be specified.
EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
// Report back that the second proxy worked. This will globally mark the
// first proxy as bad.
TestProxyFallbackProxyDelegate test_delegate;
service.SetProxyDelegate(&test_delegate);
service.ReportSuccess(info);
EXPECT_EQ("foopy1:8080", test_delegate.proxy_server().ToURI());
EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED,
test_delegate.last_proxy_fallback_net_error());
service.SetProxyDelegate(nullptr);
TestCompletionCallback callback3;
rv = service.ResolveProxy(url, std::string(), &info, callback3.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// Set the result in proxy resolver -- the second result is already known
// to be bad, so we will not try to use it initially.
resolver.pending_jobs()[0]->results()->UseNamedProxy(
"foopy3:7070;foopy1:8080;foopy2:9090");
resolver.pending_jobs()[0]->CompleteNow(OK);
EXPECT_THAT(callback3.WaitForResult(), IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy3:7070", info.proxy_server().ToURI());
// Proxy times should have been updated, so get them again.
EXPECT_LE(proxy_resolve_end_time, info.proxy_resolve_start_time());
EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
proxy_resolve_start_time = info.proxy_resolve_start_time();
proxy_resolve_end_time = info.proxy_resolve_end_time();
// We fake another error. It should now try the third one.
EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
// We fake another error. At this point we have tried all of the
// proxy servers we thought were valid; next we try the proxy server
// that was in our bad proxies map (foopy1:8080).
EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
// Fake another error, the last proxy is gone, the list should now be empty,
// so there is nothing left to try.
EXPECT_FALSE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
EXPECT_FALSE(info.is_direct());
EXPECT_TRUE(info.is_empty());
// Proxy times should not have been modified by fallback.
EXPECT_EQ(proxy_resolve_start_time, info.proxy_resolve_start_time());
EXPECT_EQ(proxy_resolve_end_time, info.proxy_resolve_end_time());
// Look up proxies again
TestCompletionCallback callback7;
rv = service.ResolveProxy(url, std::string(), &info, callback7.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// This time, the first 3 results have been found to be bad, but only the
// first proxy has been confirmed ...
resolver.pending_jobs()[0]->results()->UseNamedProxy(
"foopy1:8080;foopy3:7070;foopy2:9090;foopy4:9091");
resolver.pending_jobs()[0]->CompleteNow(OK);
// ... therefore, we should see the second proxy first.
EXPECT_THAT(callback7.WaitForResult(), IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy3:7070", info.proxy_server().ToURI());
EXPECT_LE(proxy_resolve_end_time, info.proxy_resolve_start_time());
EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
// TODO(nsylvain): Test that the proxy can be retried after the delay.
}
// This test is similar to ProxyFallback, but this time we have an explicit
// fallback choice to DIRECT.
TEST_F(ProxyResolutionServiceTest, ProxyFallbackToDirect) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
// Get the proxy information.
ProxyInfo info;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv = service.ResolveProxy(url, std::string(), &info, callback1.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// Set the result in proxy resolver.
resolver.pending_jobs()[0]->results()->UsePacString(
"PROXY foopy1:8080; PROXY foopy2:9090; DIRECT");
resolver.pending_jobs()[0]->CompleteNow(OK);
// Get the first result.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
// Fake an error on the proxy.
EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
// Now we get back the second proxy.
EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
// Fake an error on this proxy as well.
EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
// Finally, we get back DIRECT.
EXPECT_TRUE(info.is_direct());
EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
// Now we tell the proxy service that even DIRECT failed.
// There was nothing left to try after DIRECT, so we are out of
// choices.
EXPECT_FALSE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
}
TEST_F(ProxyResolutionServiceTest, ProxyFallback_BadConfig) {
// Test proxy failover when the configuration is bad.
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
// Get the proxy information.
ProxyInfo info;
TestCompletionCallback callback1;
TestResolveProxyDelegate delegate;
std::unique_ptr<ProxyResolutionService::Request> request;
service.SetProxyDelegate(&delegate);
int rv = service.ResolveProxy(url, std::string(), &info, callback1.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
resolver.pending_jobs()[0]->results()->UseNamedProxy(
"foopy1:8080;foopy2:9090");
resolver.pending_jobs()[0]->CompleteNow(OK);
// The first item is valid.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
// Fake a proxy error.
EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
// The first proxy is ignored, and the second one is selected.
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
// Persist foopy1's failure to |service|'s cache of bad proxies, so it will
// be considered by subsequent calls to ResolveProxy().
service.ReportSuccess(info);
// Fake a PAC failure.
ProxyInfo info2;
TestCompletionCallback callback2;
rv = service.ResolveProxy(url, std::string(), &info2, callback2.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// This simulates a javascript runtime error in the PAC script.
resolver.pending_jobs()[0]->CompleteNow(ERR_FAILED);
// Although the resolver failed, the ProxyResolutionService will implicitly
// fall-back to a DIRECT connection.
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_TRUE(info2.is_direct());
EXPECT_FALSE(info2.is_empty());
// The PAC script will work properly next time and successfully return a
// proxy list. Since we have not marked the configuration as bad, it should
// "just work" the next time we call it.
ProxyInfo info3;
TestCompletionCallback callback3;
std::unique_ptr<ProxyResolutionService::Request> request3;
rv = service.ResolveProxy(url, std::string(), &info3, callback3.callback(),
&request3, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
resolver.pending_jobs()[0]->results()->UseNamedProxy(
"foopy1:8080;foopy2:9090");
resolver.pending_jobs()[0]->CompleteNow(OK);
// The first proxy was deprioritized since it was added to the bad proxies
// list by the earlier ReportSuccess().
EXPECT_THAT(callback3.WaitForResult(), IsOk());
EXPECT_FALSE(info3.is_direct());
EXPECT_EQ("foopy2:9090", info3.proxy_server().ToURI());
EXPECT_EQ(2u, info3.proxy_list().size());
EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
EXPECT_EQ(3, delegate.num_resolve_proxy_called());
}
TEST_F(ProxyResolutionServiceTest, ProxyFallback_BadConfigMandatory) {
// Test proxy failover when the configuration is bad.
ProxyConfig config(
ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy/proxy.pac")));
config.set_pac_mandatory(true);
MockProxyConfigService* config_service = new MockProxyConfigService(config);
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
// Get the proxy information.
ProxyInfo info;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv = service.ResolveProxy(url, std::string(), &info, callback1.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
resolver.pending_jobs()[0]->results()->UseNamedProxy(
"foopy1:8080;foopy2:9090");
resolver.pending_jobs()[0]->CompleteNow(OK);
// The first item is valid.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
// Fake a proxy error.
EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, NetLogWithSource()));
// The first proxy is ignored, and the second one is selected.
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
// Persist foopy1's failure to |service|'s cache of bad proxies, so it will
// be considered by subsequent calls to ResolveProxy().
service.ReportSuccess(info);
// Fake a PAC failure.
ProxyInfo info2;
TestCompletionCallback callback3;
std::unique_ptr<ProxyResolutionService::Request> request3;
rv = service.ResolveProxy(url, std::string(), &info2, callback3.callback(),
&request3, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// This simulates a javascript runtime error in the PAC script.
resolver.pending_jobs()[0]->CompleteNow(ERR_FAILED);
// Although the resolver failed, the ProxyResolutionService will NOT fall-back
// to a DIRECT connection as it is configured as mandatory.
EXPECT_EQ(ERR_MANDATORY_PROXY_CONFIGURATION_FAILED,
callback3.WaitForResult());
EXPECT_FALSE(info2.is_direct());
EXPECT_TRUE(info2.is_empty());
// The PAC script will work properly next time and successfully return a
// proxy list. Since we have not marked the configuration as bad, it should
// "just work" the next time we call it.
ProxyInfo info3;
TestCompletionCallback callback4;
std::unique_ptr<ProxyResolutionService::Request> request4;
rv = service.ResolveProxy(url, std::string(), &info3, callback4.callback(),
&request4, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
resolver.pending_jobs()[0]->results()->UseNamedProxy(
"foopy1:8080;foopy2:9090");
resolver.pending_jobs()[0]->CompleteNow(OK);
// The first proxy was deprioritized since it was added to the bad proxies
// list by the earlier ReportSuccess().
EXPECT_THAT(callback4.WaitForResult(), IsOk());
EXPECT_FALSE(info3.is_direct());
EXPECT_EQ("foopy2:9090", info3.proxy_server().ToURI());
EXPECT_EQ(2u, info3.proxy_list().size());
}
TEST_F(ProxyResolutionServiceTest, ProxyBypassList) {
// Test that the proxy bypass rules are consulted.
TestCompletionCallback callback[2];
ProxyInfo info[2];
ProxyConfig config;
config.proxy_rules().ParseFromString("foopy1:8080;foopy2:9090");
config.set_auto_detect(false);
config.proxy_rules().bypass_rules.ParseFromString("*.org");
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
int rv;
GURL url1("http://www.webkit.org");
GURL url2("http://www.webkit.com");
std::unique_ptr<ProxyResolutionService::Request> request1;
std::unique_ptr<ProxyResolutionService::Request> request2;
// Request for a .org domain should bypass proxy.
rv = service.ResolveProxy(url1, std::string(), &info[0],
callback[0].callback(), &request1,
NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_TRUE(info[0].is_direct());
// Request for a .com domain hits the proxy.
rv = service.ResolveProxy(url2, std::string(), &info[1],
callback[1].callback(), &request2,
NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_EQ("foopy1:8080", info[1].proxy_server().ToURI());
}
TEST_F(ProxyResolutionServiceTest, MarkProxiesAsBadTests) {
ProxyConfig config;
config.proxy_rules().ParseFromString(
"http=foopy1:8080;http=foopy2:8080;http=foopy3.8080;http=foopy4:8080");
config.set_auto_detect(false);
ProxyList proxy_list;
std::vector<ProxyServer> additional_bad_proxies;
for (const ProxyServer& proxy_server :
config.proxy_rules().proxies_for_http.GetAll()) {
proxy_list.AddProxyServer(proxy_server);
if (proxy_server == config.proxy_rules().proxies_for_http.Get())
continue;
additional_bad_proxies.push_back(proxy_server);
}
EXPECT_EQ(3u, additional_bad_proxies.size());
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
ProxyInfo proxy_info;
proxy_info.UseProxyList(proxy_list);
const ProxyRetryInfoMap& retry_info = service.proxy_retry_info();
service.MarkProxiesAsBadUntil(proxy_info, base::TimeDelta::FromSeconds(1),
additional_bad_proxies, NetLogWithSource());
ASSERT_EQ(4u, retry_info.size());
for (const ProxyServer& proxy_server :
config.proxy_rules().proxies_for_http.GetAll()) {
auto i = retry_info.find(proxy_server.host_port_pair().ToString());
ASSERT_TRUE(i != retry_info.end());
}
}
TEST_F(ProxyResolutionServiceTest, PerProtocolProxyTests) {
ProxyConfig config;
config.proxy_rules().ParseFromString("http=foopy1:8080;https=foopy2:8080");
config.set_auto_detect(false);
std::unique_ptr<ProxyResolutionService::Request> request;
{
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
GURL test_url("http://www.msn.com");
ProxyInfo info;
TestCompletionCallback callback;
int rv =
service.ResolveProxy(test_url, std::string(), &info,
callback.callback(), &request, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
}
{
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
GURL test_url("ftp://ftp.google.com");
ProxyInfo info;
TestCompletionCallback callback;
int rv =
service.ResolveProxy(test_url, std::string(), &info,
callback.callback(), &request, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_TRUE(info.is_direct());
EXPECT_EQ("direct://", info.proxy_server().ToURI());
}
{
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
GURL test_url("https://webbranch.techcu.com");
ProxyInfo info;
TestCompletionCallback callback;
int rv =
service.ResolveProxy(test_url, std::string(), &info,
callback.callback(), &request, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy2:8080", info.proxy_server().ToURI());
}
{
config.proxy_rules().ParseFromString("foopy1:8080");
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
GURL test_url("http://www.microsoft.com");
ProxyInfo info;
TestCompletionCallback callback;
int rv =
service.ResolveProxy(test_url, std::string(), &info,
callback.callback(), &request, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
}
}
TEST_F(ProxyResolutionServiceTest, ProxyConfigTrafficAnnotationPropagates) {
// Test that the proxy config source is set correctly when resolving proxies
// using manual proxy rules. Namely, the config source should only be set if
// any of the rules were applied.
std::unique_ptr<ProxyResolutionService::Request> request;
{
ProxyConfig config;
config.proxy_rules().ParseFromString("https=foopy2:8080");
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
GURL test_url("http://www.google.com");
ProxyInfo info;
TestCompletionCallback callback;
int rv =
service.ResolveProxy(test_url, std::string(), &info,
callback.callback(), &request, NetLogWithSource());
ASSERT_THAT(rv, IsOk());
// Should be test, even if there are no HTTP proxies configured.
EXPECT_EQ(MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
info.traffic_annotation());
}
{
ProxyConfig config;
config.proxy_rules().ParseFromString("https=foopy2:8080");
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
GURL test_url("https://www.google.com");
ProxyInfo info;
TestCompletionCallback callback;
int rv =
service.ResolveProxy(test_url, std::string(), &info,
callback.callback(), &request, NetLogWithSource());
ASSERT_THAT(rv, IsOk());
// Used the HTTPS proxy. So traffic annotation should test.
EXPECT_EQ(MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
info.traffic_annotation());
}
{
ProxyConfig config;
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
GURL test_url("http://www.google.com");
ProxyInfo info;
TestCompletionCallback callback;
int rv =
service.ResolveProxy(test_url, std::string(), &info,
callback.callback(), &request, NetLogWithSource());
ASSERT_THAT(rv, IsOk());
// ProxyConfig is empty. Traffic annotation should still be TEST.
EXPECT_EQ(MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
info.traffic_annotation());
}
}
// If only HTTP and a SOCKS proxy are specified, check if ftp/https queries
// fall back to the SOCKS proxy.
TEST_F(ProxyResolutionServiceTest, DefaultProxyFallbackToSOCKS) {
ProxyConfig config;
config.proxy_rules().ParseFromString("http=foopy1:8080;socks=foopy2:1080");
config.set_auto_detect(false);
EXPECT_EQ(ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME,
config.proxy_rules().type);
std::unique_ptr<ProxyResolutionService::Request> request;
{
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
GURL test_url("http://www.msn.com");
ProxyInfo info;
TestCompletionCallback callback;
int rv =
service.ResolveProxy(test_url, std::string(), &info,
callback.callback(), &request, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
}
{
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
GURL test_url("ftp://ftp.google.com");
ProxyInfo info;
TestCompletionCallback callback;
int rv =
service.ResolveProxy(test_url, std::string(), &info,
callback.callback(), &request, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI());
}
{
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
GURL test_url("https://webbranch.techcu.com");
ProxyInfo info;
TestCompletionCallback callback;
int rv =
service.ResolveProxy(test_url, std::string(), &info,
callback.callback(), &request, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI());
}
{
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config), nullptr, nullptr);
GURL test_url("unknown://www.microsoft.com");
ProxyInfo info;
TestCompletionCallback callback;
int rv =
service.ResolveProxy(test_url, std::string(), &info,
callback.callback(), &request, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI());
}
}
// Test cancellation of an in-progress request.
TEST_F(ProxyResolutionServiceTest, CancelInProgressRequest) {
const GURL url1("http://request1");
const GURL url2("http://request2");
const GURL url3("http://request3");
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
// Start 3 requests.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(url1, std::string(), &info1, callback1.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Successfully initialize the PAC script.
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
GetPendingJobsForURLs(resolver, url1);
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv = service.ResolveProxy(url2, std::string(), &info2, callback2.callback(),
&request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
GetPendingJobsForURLs(resolver, url1, url2);
ProxyInfo info3;
TestCompletionCallback callback3;
std::unique_ptr<ProxyResolutionService::Request> request3;
rv = service.ResolveProxy(url3, std::string(), &info3, callback3.callback(),
&request3, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
GetPendingJobsForURLs(resolver, url1, url2, url3);
// Cancel the second request
request2.reset();
JobMap jobs = GetPendingJobsForURLs(resolver, url1, url3);
// Complete the two un-cancelled jobs.
// We complete the last one first, just to mix it up a bit.
jobs[url3]->results()->UseNamedProxy("request3:80");
jobs[url3]->CompleteNow(OK); // dsaadsasd
jobs[url1]->results()->UseNamedProxy("request1:80");
jobs[url1]->CompleteNow(OK);
EXPECT_EQ(OK, callback1.WaitForResult());
EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
EXPECT_FALSE(callback2.have_result()); // Cancelled.
GetCancelledJobsForURLs(resolver, url2);
EXPECT_THAT(callback3.WaitForResult(), IsOk());
EXPECT_EQ("request3:80", info3.proxy_server().ToURI());
}
// Test the initial PAC download for resolver that expects bytes.
TEST_F(ProxyResolutionServiceTest, InitialPACScriptDownload) {
const GURL url1("http://request1");
const GURL url2("http://request2");
const GURL url3("http://request3");
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start 3 requests.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(url1, std::string(), &info1, callback1.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The first request should have triggered download of PAC script.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv = service.ResolveProxy(url2, std::string(), &info2, callback2.callback(),
&request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ProxyInfo info3;
TestCompletionCallback callback3;
std::unique_ptr<ProxyResolutionService::Request> request3;
rv = service.ResolveProxy(url3, std::string(), &info3, callback3.callback(),
&request3, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Nothing has been sent to the factory yet.
EXPECT_TRUE(factory->pending_requests().empty());
EXPECT_EQ(LOAD_STATE_DOWNLOADING_PAC_FILE, request1->GetLoadState());
EXPECT_EQ(LOAD_STATE_DOWNLOADING_PAC_FILE, request2->GetLoadState());
EXPECT_EQ(LOAD_STATE_DOWNLOADING_PAC_FILE, request3->GetLoadState());
// At this point the ProxyResolutionService should be waiting for the
// PacFileFetcher to invoke its completion callback, notifying it of
// PAC script download completion.
fetcher->NotifyFetchCompletion(OK, kValidPacScript1);
// Now that the PAC script is downloaded, it will have been sent to the proxy
// resolver.
EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
JobMap jobs = GetPendingJobsForURLs(resolver, url1, url2, url3);
EXPECT_EQ(LOAD_STATE_RESOLVING_PROXY_FOR_URL, request1->GetLoadState());
EXPECT_EQ(LOAD_STATE_RESOLVING_PROXY_FOR_URL, request2->GetLoadState());
EXPECT_EQ(LOAD_STATE_RESOLVING_PROXY_FOR_URL, request3->GetLoadState());
// Complete all the jobs (in some order).
jobs[url3]->results()->UseNamedProxy("request3:80");
jobs[url3]->CompleteNow(OK);
jobs[url1]->results()->UseNamedProxy("request1:80");
jobs[url1]->CompleteNow(OK);
jobs[url2]->results()->UseNamedProxy("request2:80");
jobs[url2]->CompleteNow(OK);
// Complete and verify that jobs ran as expected.
EXPECT_EQ(OK, callback1.WaitForResult());
// ProxyResolver::GetProxyForURL() to take a std::unique_ptr<Request>* rather
// than a RequestHandle* (patchset #11 id:200001 of
// https://codereview.chromium.org/1439053002/ )
EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
EXPECT_FALSE(info1.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info1.proxy_resolve_end_time().is_null());
EXPECT_LE(info1.proxy_resolve_start_time(), info1.proxy_resolve_end_time());
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
EXPECT_FALSE(info2.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info2.proxy_resolve_end_time().is_null());
EXPECT_LE(info2.proxy_resolve_start_time(), info2.proxy_resolve_end_time());
EXPECT_THAT(callback3.WaitForResult(), IsOk());
EXPECT_EQ("request3:80", info3.proxy_server().ToURI());
EXPECT_FALSE(info3.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info3.proxy_resolve_end_time().is_null());
EXPECT_LE(info3.proxy_resolve_start_time(), info3.proxy_resolve_end_time());
}
// Test changing the PacFileFetcher while PAC download is in progress.
TEST_F(ProxyResolutionServiceTest,
ChangeScriptFetcherWhilePACDownloadInProgress) {
const GURL url1("http://request1");
const GURL url2("http://request2");
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start 2 jobs.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(url1, std::string(), &info1, callback1.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The first request should have triggered download of PAC script.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv = service.ResolveProxy(url2, std::string(), &info2, callback2.callback(),
&request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// At this point the ProxyResolutionService should be waiting for the
// PacFileFetcher to invoke its completion callback, notifying it of
// PAC script download completion.
// We now change out the ProxyResolutionService's script fetcher. We should
// restart the initialization with the new fetcher.
fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Nothing has been sent to the factory yet.
EXPECT_TRUE(factory->pending_requests().empty());
fetcher->NotifyFetchCompletion(OK, kValidPacScript1);
// Now that the PAC script is downloaded, it will have been sent to the proxy
// resolver.
EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
GetPendingJobsForURLs(resolver, url1, url2);
}
// Test cancellation of a request, while the PAC script is being fetched.
TEST_F(ProxyResolutionServiceTest, CancelWhilePACFetching) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start 3 requests.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
BoundTestNetLog log1;
int rv = service.ResolveProxy(GURL("http://request1"), std::string(), &info1,
callback1.callback(), &request1, log1.bound());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The first request should have triggered download of PAC script.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv =
service.ResolveProxy(GURL("http://request2"), std::string(), &info2,
callback2.callback(), &request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ProxyInfo info3;
TestCompletionCallback callback3;
std::unique_ptr<ProxyResolutionService::Request> request3;
rv =
service.ResolveProxy(GURL("http://request3"), std::string(), &info3,
callback3.callback(), &request3, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Nothing has been sent to the factory yet.
EXPECT_TRUE(factory->pending_requests().empty());
// Cancel the first 2 jobs.
request1.reset();
request2.reset();
// At this point the ProxyResolutionService should be waiting for the
// PacFileFetcher to invoke its completion callback, notifying it of
// PAC script download completion.
fetcher->NotifyFetchCompletion(OK, kValidPacScript1);
// Now that the PAC script is downloaded, it will have been sent to the
// proxy resolver.
EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(GURL("http://request3"), resolver.pending_jobs()[0]->url());
// Complete all the jobs.
resolver.pending_jobs()[0]->results()->UseNamedProxy("request3:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
EXPECT_THAT(callback3.WaitForResult(), IsOk());
EXPECT_EQ("request3:80", info3.proxy_server().ToURI());
EXPECT_TRUE(resolver.cancelled_jobs().empty());
EXPECT_FALSE(callback1.have_result()); // Cancelled.
EXPECT_FALSE(callback2.have_result()); // Cancelled.
TestNetLogEntry::List entries1;
log1.GetEntries(&entries1);
// Check the NetLog for request 1 (which was cancelled) got filled properly.
EXPECT_EQ(4u, entries1.size());
EXPECT_TRUE(LogContainsBeginEvent(entries1, 0,
NetLogEventType::PROXY_RESOLUTION_SERVICE));
EXPECT_TRUE(LogContainsBeginEvent(
entries1, 1,
NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC));
// Note that PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC is never completed
// before the cancellation occured.
EXPECT_TRUE(LogContainsEvent(entries1, 2, NetLogEventType::CANCELLED,
NetLogEventPhase::NONE));
EXPECT_TRUE(LogContainsEndEvent(entries1, 3,
NetLogEventType::PROXY_RESOLUTION_SERVICE));
}
// Test that if auto-detect fails, we fall-back to the custom pac.
TEST_F(ProxyResolutionServiceTest, FallbackFromAutodetectToCustomPac) {
const GURL url1("http://request1");
const GURL url2("http://request2");
ProxyConfig config;
config.set_auto_detect(true);
config.set_pac_url(GURL("http://foopy/proxy.pac"));
config.proxy_rules().ParseFromString("http=foopy:80"); // Won't be used.
MockProxyConfigService* config_service = new MockProxyConfigService(config);
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start 2 requests.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(url1, std::string(), &info1, callback1.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv = service.ResolveProxy(url2, std::string(), &info2, callback2.callback(),
&request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Check that nothing has been sent to the proxy resolver factory yet.
ASSERT_EQ(0u, factory->pending_requests().size());
// It should be trying to auto-detect first -- FAIL the autodetect during
// the script download.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url());
fetcher->NotifyFetchCompletion(ERR_FAILED, std::string());
// Next it should be trying the custom PAC url.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
fetcher->NotifyFetchCompletion(OK, kValidPacScript1);
EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
// Now finally, the pending jobs should have been sent to the resolver
// (which was initialized with custom PAC script).
JobMap jobs = GetPendingJobsForURLs(resolver, url1, url2);
// Complete the pending jobs.
jobs[url2]->results()->UseNamedProxy("request2:80");
jobs[url2]->CompleteNow(OK);
jobs[url1]->results()->UseNamedProxy("request1:80");
jobs[url1]->CompleteNow(OK);
// Verify that jobs ran as expected.
EXPECT_EQ(OK, callback1.WaitForResult());
// ProxyResolver::GetProxyForURL() to take a std::unique_ptr<Request>* rather
// than a RequestHandle* (patchset #11 id:200001 of
// https://codereview.chromium.org/1439053002/ )
EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
EXPECT_FALSE(info1.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info1.proxy_resolve_end_time().is_null());
EXPECT_LE(info1.proxy_resolve_start_time(), info1.proxy_resolve_end_time());
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
EXPECT_FALSE(info2.proxy_resolve_start_time().is_null());
EXPECT_FALSE(info2.proxy_resolve_end_time().is_null());
EXPECT_LE(info2.proxy_resolve_start_time(), info2.proxy_resolve_end_time());
}
// This is the same test as FallbackFromAutodetectToCustomPac, except
// the auto-detect script fails parsing rather than downloading.
TEST_F(ProxyResolutionServiceTest, FallbackFromAutodetectToCustomPac2) {
const GURL url1("http://request1");
const GURL url2("http://request2");
ProxyConfig config;
config.set_auto_detect(true);
config.set_pac_url(GURL("http://foopy/proxy.pac"));
config.proxy_rules().ParseFromString("http=foopy:80"); // Won't be used.
MockProxyConfigService* config_service = new MockProxyConfigService(config);
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start 2 requests.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(url1, std::string(), &info1, callback1.callback(),
&request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv = service.ResolveProxy(url2, std::string(), &info2, callback2.callback(),
&request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Check that nothing has been sent to the proxy resolver factory yet.
ASSERT_EQ(0u, factory->pending_requests().size());
// It should be trying to auto-detect first -- succeed the download.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url());
fetcher->NotifyFetchCompletion(OK, "invalid-script-contents");
// The script contents passed failed basic verification step (since didn't
// contain token FindProxyForURL), so it was never passed to the resolver.
// Next it should be trying the custom PAC url.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
fetcher->NotifyFetchCompletion(OK, kValidPacScript1);
EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
// Now finally, the pending jobs should have been sent to the resolver
// (which was initialized with custom PAC script).
JobMap jobs = GetPendingJobsForURLs(resolver, url1, url2);
// Complete the pending jobs.
jobs[url2]->results()->UseNamedProxy("request2:80");
jobs[url2]->CompleteNow(OK);
jobs[url1]->results()->UseNamedProxy("request1:80");
jobs[url1]->CompleteNow(OK);
// Verify that jobs ran as expected.
EXPECT_EQ(OK, callback1.WaitForResult());
// ProxyResolver::GetProxyForURL() to take a std::unique_ptr<Request>* rather
// than a RequestHandle* (patchset #11 id:200001 of
// https://codereview.chromium.org/1439053002/ )
EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
}
// Test that if all of auto-detect, a custom PAC script, and manual settings
// are given, then we will try them in that order.
TEST_F(ProxyResolutionServiceTest, FallbackFromAutodetectToCustomToManual) {
ProxyConfig config;
config.set_auto_detect(true);
config.set_pac_url(GURL("http://foopy/proxy.pac"));
config.proxy_rules().ParseFromString("http=foopy:80");
MockProxyConfigService* config_service = new MockProxyConfigService(config);
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start 2 jobs.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(GURL("http://request1"), std::string(), &info1,
callback1.callback(), &request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv =
service.ResolveProxy(GURL("http://request2"), std::string(), &info2,
callback2.callback(), &request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Check that nothing has been sent to the proxy resolver factory yet.
ASSERT_EQ(0u, factory->pending_requests().size());
// It should be trying to auto-detect first -- fail the download.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url());
fetcher->NotifyFetchCompletion(ERR_FAILED, std::string());
// Next it should be trying the custom PAC url -- fail the download.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
fetcher->NotifyFetchCompletion(ERR_FAILED, std::string());
// Since we never managed to initialize a resolver, nothing should have been
// sent to it.
ASSERT_EQ(0u, factory->pending_requests().size());
// Verify that jobs ran as expected -- they should have fallen back to
// the manual proxy configuration for HTTP urls.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_EQ("foopy:80", info1.proxy_server().ToURI());
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_EQ("foopy:80", info2.proxy_server().ToURI());
}
// Test that the bypass rules are NOT applied when using autodetect.
TEST_F(ProxyResolutionServiceTest, BypassDoesntApplyToPac) {
ProxyConfig config;
config.set_auto_detect(true);
config.set_pac_url(GURL("http://foopy/proxy.pac"));
config.proxy_rules().ParseFromString("http=foopy:80"); // Not used.
config.proxy_rules().bypass_rules.ParseFromString("www.google.com");
MockProxyConfigService* config_service = new MockProxyConfigService(config);
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start 1 requests.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(GURL("http://www.google.com"), std::string(), &info1,
callback1.callback(), &request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Check that nothing has been sent to the proxy resolver factory yet.
ASSERT_EQ(0u, factory->pending_requests().size());
// It should be trying to auto-detect first -- succeed the download.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url());
fetcher->NotifyFetchCompletion(OK, kValidPacScript1);
EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(GURL("http://www.google.com"), resolver.pending_jobs()[0]->url());
// Complete the pending request.
resolver.pending_jobs()[0]->results()->UseNamedProxy("request1:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
// Verify that request ran as expected.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
// Start another request, it should pickup the bypass item.
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv =
service.ResolveProxy(GURL("http://www.google.com"), std::string(), &info2,
callback2.callback(), &request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(GURL("http://www.google.com"), resolver.pending_jobs()[0]->url());
// Complete the pending request.
resolver.pending_jobs()[0]->results()->UseNamedProxy("request2:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
}
// Delete the ProxyResolutionService while InitProxyResolver has an outstanding
// request to the script fetcher. When run under valgrind, should not
// have any memory errors (used to be that the PacFileFetcher was
// being deleted prior to the InitProxyResolver).
TEST_F(ProxyResolutionServiceTest,
DeleteWhileInitProxyResolverHasOutstandingFetch) {
ProxyConfig config =
ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy/proxy.pac"));
MockProxyConfigService* config_service = new MockProxyConfigService(config);
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start 1 request.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(GURL("http://www.google.com"), std::string(), &info1,
callback1.callback(), &request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Check that nothing has been sent to the proxy resolver factory yet.
ASSERT_EQ(0u, factory->pending_requests().size());
// InitProxyResolver should have issued a request to the PacFileFetcher
// and be waiting on that to complete.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
}
// Delete the ProxyResolutionService while InitProxyResolver has an outstanding
// request to the proxy resolver. When run under valgrind, should not
// have any memory errors (used to be that the ProxyResolver was
// being deleted prior to the InitProxyResolver).
TEST_F(ProxyResolutionServiceTest,
DeleteWhileInitProxyResolverHasOutstandingSet) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
GURL url("http://www.google.com/");
ProxyInfo info;
TestCompletionCallback callback;
std::unique_ptr<ProxyResolutionService::Request> request;
int rv = service.ResolveProxy(url, std::string(), &info, callback.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
}
TEST_F(ProxyResolutionServiceTest, ResetProxyConfigService) {
ProxyConfig config1;
config1.proxy_rules().ParseFromString("foopy1:8080");
config1.set_auto_detect(false);
ProxyResolutionService service(
std::make_unique<MockProxyConfigService>(config1), nullptr, nullptr);
ProxyInfo info;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(GURL("http://request1"), std::string(), &info,
callback1.callback(), &request1, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
ProxyConfig config2;
config2.proxy_rules().ParseFromString("foopy2:8080");
config2.set_auto_detect(false);
service.ResetConfigService(std::make_unique<MockProxyConfigService>(config2));
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv =
service.ResolveProxy(GURL("http://request2"), std::string(), &info,
callback2.callback(), &request2, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_EQ("foopy2:8080", info.proxy_server().ToURI());
}
// Test that when going from a configuration that required PAC to one
// that does NOT, we unset the variable |should_use_proxy_resolver_|.
TEST_F(ProxyResolutionServiceTest, UpdateConfigFromPACToDirect) {
ProxyConfig config = ProxyConfig::CreateAutoDetect();
MockProxyConfigService* config_service = new MockProxyConfigService(config);
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(false);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
// Start 1 request.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(GURL("http://www.google.com"), std::string(), &info1,
callback1.callback(), &request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Successfully set the autodetect script.
EXPECT_EQ(PacFileData::TYPE_AUTO_DETECT,
factory->pending_requests()[0]->script_data()->type());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
// Complete the pending request.
ASSERT_EQ(1u, resolver.pending_jobs().size());
resolver.pending_jobs()[0]->results()->UseNamedProxy("request1:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
// Verify that request ran as expected.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
// Force the ProxyResolutionService to pull down a new proxy configuration.
// (Even though the configuration isn't old/bad).
//
// This new configuration no longer has auto_detect set, so
// jobs should complete synchronously now as direct-connect.
config_service->SetConfig(ProxyConfigWithAnnotation::CreateDirect());
// Start another request -- the effective configuration has changed.
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv =
service.ResolveProxy(GURL("http://www.google.com"), std::string(), &info2,
callback2.callback(), &request2, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_TRUE(info2.is_direct());
}
TEST_F(ProxyResolutionServiceTest, NetworkChangeTriggersPacRefetch) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
TestNetLog log;
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), &log);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Disable the "wait after IP address changes" hack, so this unit-test can
// complete quickly.
service.set_stall_proxy_auto_config_delay(base::TimeDelta());
// Start 1 request.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(GURL("http://request1"), std::string(), &info1,
callback1.callback(), &request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The first request should have triggered initial download of PAC script.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
// Nothing has been sent to the factory yet.
EXPECT_TRUE(factory->pending_requests().empty());
// At this point the ProxyResolutionService should be waiting for the
// PacFileFetcher to invoke its completion callback, notifying it of
// PAC script download completion.
fetcher->NotifyFetchCompletion(OK, kValidPacScript1);
// Now that the PAC script is downloaded, the request will have been sent to
// the proxy resolver.
EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(GURL("http://request1"), resolver.pending_jobs()[0]->url());
// Complete the pending request.
resolver.pending_jobs()[0]->results()->UseNamedProxy("request1:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
// Wait for completion callback, and verify that the request ran as expected.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
// Now simluate a change in the network. The ProxyConfigService is still
// going to return the same PAC URL as before, but this URL needs to be
// refetched on the new network.
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
base::RunLoop().RunUntilIdle(); // Notification happens async.
// Start a second request.
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv =
service.ResolveProxy(GURL("http://request2"), std::string(), &info2,
callback2.callback(), &request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// This second request should have triggered the re-download of the PAC
// script (since we marked the network as having changed).
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
// Nothing has been sent to the factory yet.
EXPECT_TRUE(factory->pending_requests().empty());
// Simulate the PAC script fetch as having completed (this time with
// different data).
fetcher->NotifyFetchCompletion(OK, kValidPacScript2);
// Now that the PAC script is downloaded, the second request will have been
// sent to the proxy resolver.
EXPECT_EQ(ASCIIToUTF16(kValidPacScript2),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(GURL("http://request2"), resolver.pending_jobs()[0]->url());
// Complete the pending second request.
resolver.pending_jobs()[0]->results()->UseNamedProxy("request2:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
// Wait for completion callback, and verify that the request ran as expected.
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
// Check that the expected events were output to the log stream. In particular
// PROXY_CONFIG_CHANGED should have only been emitted once (for the initial
// setup), and NOT a second time when the IP address changed.
TestNetLogEntry::List entries;
log.GetEntries(&entries);
EXPECT_TRUE(LogContainsEntryWithType(entries, 0,
NetLogEventType::PROXY_CONFIG_CHANGED));
ASSERT_EQ(9u, entries.size());
for (size_t i = 1; i < entries.size(); ++i)
EXPECT_NE(NetLogEventType::PROXY_CONFIG_CHANGED, entries[i].type);
}
// This test verifies that the PAC script specified by the settings is
// periodically polled for changes. Specifically, if the initial fetch fails due
// to a network error, we will eventually re-configure the service to use the
// script once it becomes available.
TEST_F(ProxyResolutionServiceTest, PACScriptRefetchAfterFailure) {
// Change the retry policy to wait a mere 1 ms before retrying, so the test
// runs quickly.
ImmediatePollPolicy poll_policy;
ProxyResolutionService::set_pac_script_poll_policy(&poll_policy);
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start 1 request.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(GURL("http://request1"), std::string(), &info1,
callback1.callback(), &request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The first request should have triggered initial download of PAC script.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
// Nothing has been sent to the factory yet.
EXPECT_TRUE(factory->pending_requests().empty());
// At this point the ProxyResolutionService should be waiting for the
// PacFileFetcher to invoke its completion callback, notifying it of
// PAC script download completion.
//
// We simulate a failed download attempt, the proxy service should now
// fall-back to DIRECT connections.
fetcher->NotifyFetchCompletion(ERR_FAILED, std::string());
ASSERT_TRUE(factory->pending_requests().empty());
// Wait for completion callback, and verify it used DIRECT.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_TRUE(info1.is_direct());
// At this point we have initialized the proxy service using a PAC script,
// however it failed and fell-back to DIRECT.
//
// A background task to periodically re-check the PAC script for validity will
// have been started. We will now wait for the next download attempt to start.
//
// Note that we shouldn't have to wait long here, since our test enables a
// special unit-test mode.
fetcher->WaitUntilFetch();
ASSERT_TRUE(factory->pending_requests().empty());
// Make sure that our background checker is trying to download the expected
// PAC script (same one as before). This time we will simulate a successful
// download of the script.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
fetcher->NotifyFetchCompletion(OK, kValidPacScript1);
base::RunLoop().RunUntilIdle();
// Now that the PAC script is downloaded, it should be used to initialize the
// ProxyResolver. Simulate a successful parse.
EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
// At this point the ProxyResolutionService should have re-configured itself
// to use the PAC script (thereby recovering from the initial fetch failure).
// We will verify that the next Resolve request uses the resolver rather than
// DIRECT.
// Start a second request.
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv =
service.ResolveProxy(GURL("http://request2"), std::string(), &info2,
callback2.callback(), &request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Check that it was sent to the resolver.
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(GURL("http://request2"), resolver.pending_jobs()[0]->url());
// Complete the pending second request.
resolver.pending_jobs()[0]->results()->UseNamedProxy("request2:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
// Wait for completion callback, and verify that the request ran as expected.
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
}
// This test verifies that the PAC script specified by the settings is
// periodically polled for changes. Specifically, if the initial fetch succeeds,
// however at a later time its *contents* change, we will eventually
// re-configure the service to use the new script.
TEST_F(ProxyResolutionServiceTest, PACScriptRefetchAfterContentChange) {
// Change the retry policy to wait a mere 1 ms before retrying, so the test
// runs quickly.
ImmediatePollPolicy poll_policy;
ProxyResolutionService::set_pac_script_poll_policy(&poll_policy);
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start 1 request.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(GURL("http://request1"), std::string(), &info1,
callback1.callback(), &request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The first request should have triggered initial download of PAC script.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
// Nothing has been sent to the factory yet.
EXPECT_TRUE(factory->pending_requests().empty());
// At this point the ProxyResolutionService should be waiting for the
// PacFileFetcher to invoke its completion callback, notifying it of
// PAC script download completion.
fetcher->NotifyFetchCompletion(OK, kValidPacScript1);
// Now that the PAC script is downloaded, the request will have been sent to
// the proxy resolver.
EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(GURL("http://request1"), resolver.pending_jobs()[0]->url());
// Complete the pending request.
resolver.pending_jobs()[0]->results()->UseNamedProxy("request1:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
// Wait for completion callback, and verify that the request ran as expected.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
// At this point we have initialized the proxy service using a PAC script.
//
// A background task to periodically re-check the PAC script for validity will
// have been started. We will now wait for the next download attempt to start.
//
// Note that we shouldn't have to wait long here, since our test enables a
// special unit-test mode.
fetcher->WaitUntilFetch();
ASSERT_TRUE(factory->pending_requests().empty());
ASSERT_TRUE(resolver.pending_jobs().empty());
// Make sure that our background checker is trying to download the expected
// PAC script (same one as before). This time we will simulate a successful
// download of a DIFFERENT script.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
fetcher->NotifyFetchCompletion(OK, kValidPacScript2);
base::RunLoop().RunUntilIdle();
// Now that the PAC script is downloaded, it should be used to initialize the
// ProxyResolver. Simulate a successful parse.
EXPECT_EQ(ASCIIToUTF16(kValidPacScript2),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
// At this point the ProxyResolutionService should have re-configured itself
// to use the new PAC script.
// Start a second request.
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv =
service.ResolveProxy(GURL("http://request2"), std::string(), &info2,
callback2.callback(), &request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Check that it was sent to the resolver.
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(GURL("http://request2"), resolver.pending_jobs()[0]->url());
// Complete the pending second request.
resolver.pending_jobs()[0]->results()->UseNamedProxy("request2:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
// Wait for completion callback, and verify that the request ran as expected.
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
}
// This test verifies that the PAC script specified by the settings is
// periodically polled for changes. Specifically, if the initial fetch succeeds
// and so does the next poll, however the contents of the downloaded script
// have NOT changed, then we do not bother to re-initialize the proxy resolver.
TEST_F(ProxyResolutionServiceTest, PACScriptRefetchAfterContentUnchanged) {
// Change the retry policy to wait a mere 1 ms before retrying, so the test
// runs quickly.
ImmediatePollPolicy poll_policy;
ProxyResolutionService::set_pac_script_poll_policy(&poll_policy);
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start 1 request.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(GURL("http://request1"), std::string(), &info1,
callback1.callback(), &request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The first request should have triggered initial download of PAC script.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
// Nothing has been sent to the factory yet.
EXPECT_TRUE(factory->pending_requests().empty());
// At this point the ProxyResolutionService should be waiting for the
// PacFileFetcher to invoke its completion callback, notifying it of
// PAC script download completion.
fetcher->NotifyFetchCompletion(OK, kValidPacScript1);
// Now that the PAC script is downloaded, the request will have been sent to
// the proxy resolver.
EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(GURL("http://request1"), resolver.pending_jobs()[0]->url());
// Complete the pending request.
resolver.pending_jobs()[0]->results()->UseNamedProxy("request1:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
// Wait for completion callback, and verify that the request ran as expected.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
// At this point we have initialized the proxy service using a PAC script.
//
// A background task to periodically re-check the PAC script for validity will
// have been started. We will now wait for the next download attempt to start.
//
// Note that we shouldn't have to wait long here, since our test enables a
// special unit-test mode.
fetcher->WaitUntilFetch();
ASSERT_TRUE(factory->pending_requests().empty());
ASSERT_TRUE(resolver.pending_jobs().empty());
// Make sure that our background checker is trying to download the expected
// PAC script (same one as before). We will simulate the same response as
// last time (i.e. the script is unchanged).
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
fetcher->NotifyFetchCompletion(OK, kValidPacScript1);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(factory->pending_requests().empty());
ASSERT_TRUE(resolver.pending_jobs().empty());
// At this point the ProxyResolutionService is still running the same PAC
// script as before.
// Start a second request.
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv =
service.ResolveProxy(GURL("http://request2"), std::string(), &info2,
callback2.callback(), &request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Check that it was sent to the resolver.
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(GURL("http://request2"), resolver.pending_jobs()[0]->url());
// Complete the pending second request.
resolver.pending_jobs()[0]->results()->UseNamedProxy("request2:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
// Wait for completion callback, and verify that the request ran as expected.
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
}
// This test verifies that the PAC script specified by the settings is
// periodically polled for changes. Specifically, if the initial fetch succeeds,
// however at a later time it starts to fail, we should re-configure the
// ProxyResolutionService to stop using that PAC script.
TEST_F(ProxyResolutionServiceTest, PACScriptRefetchAfterSuccess) {
// Change the retry policy to wait a mere 1 ms before retrying, so the test
// runs quickly.
ImmediatePollPolicy poll_policy;
ProxyResolutionService::set_pac_script_poll_policy(&poll_policy);
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start 1 request.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(GURL("http://request1"), std::string(), &info1,
callback1.callback(), &request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The first request should have triggered initial download of PAC script.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
// Nothing has been sent to the factory yet.
EXPECT_TRUE(factory->pending_requests().empty());
// At this point the ProxyResolutionService should be waiting for the
// PacFileFetcher to invoke its completion callback, notifying it of
// PAC script download completion.
fetcher->NotifyFetchCompletion(OK, kValidPacScript1);
// Now that the PAC script is downloaded, the request will have been sent to
// the proxy resolver.
EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(GURL("http://request1"), resolver.pending_jobs()[0]->url());
// Complete the pending request.
resolver.pending_jobs()[0]->results()->UseNamedProxy("request1:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
// Wait for completion callback, and verify that the request ran as expected.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
// At this point we have initialized the proxy service using a PAC script.
//
// A background task to periodically re-check the PAC script for validity will
// have been started. We will now wait for the next download attempt to start.
//
// Note that we shouldn't have to wait long here, since our test enables a
// special unit-test mode.
fetcher->WaitUntilFetch();
ASSERT_TRUE(factory->pending_requests().empty());
ASSERT_TRUE(resolver.pending_jobs().empty());
// Make sure that our background checker is trying to download the expected
// PAC script (same one as before). This time we will simulate a failure
// to download the script.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
fetcher->NotifyFetchCompletion(ERR_FAILED, std::string());
base::RunLoop().RunUntilIdle();
// At this point the ProxyResolutionService should have re-configured itself
// to use DIRECT connections rather than the given proxy resolver.
// Start a second request.
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv =
service.ResolveProxy(GURL("http://request2"), std::string(), &info2,
callback2.callback(), &request2, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_TRUE(info2.is_direct());
}
// Tests that the code which decides at what times to poll the PAC
// script follows the expected policy.
TEST_F(ProxyResolutionServiceTest, PACScriptPollingPolicy) {
// Retrieve the internal polling policy implementation used by
// ProxyResolutionService.
std::unique_ptr<ProxyResolutionService::PacPollPolicy> policy =
ProxyResolutionService::CreateDefaultPacPollPolicy();
int error;
ProxyResolutionService::PacPollPolicy::Mode mode;
const base::TimeDelta initial_delay = base::TimeDelta::FromMilliseconds(-1);
base::TimeDelta delay = initial_delay;
// --------------------------------------------------
// Test the poll sequence in response to a failure.
// --------------------------------------------------
error = ERR_NAME_NOT_RESOLVED;
// Poll #0
mode = policy->GetNextDelay(error, initial_delay, &delay);
EXPECT_EQ(8, delay.InSeconds());
EXPECT_EQ(ProxyResolutionService::PacPollPolicy::MODE_USE_TIMER, mode);
// Poll #1
mode = policy->GetNextDelay(error, delay, &delay);
EXPECT_EQ(32, delay.InSeconds());
EXPECT_EQ(ProxyResolutionService::PacPollPolicy::MODE_START_AFTER_ACTIVITY,
mode);
// Poll #2
mode = policy->GetNextDelay(error, delay, &delay);
EXPECT_EQ(120, delay.InSeconds());
EXPECT_EQ(ProxyResolutionService::PacPollPolicy::MODE_START_AFTER_ACTIVITY,
mode);
// Poll #3
mode = policy->GetNextDelay(error, delay, &delay);
EXPECT_EQ(14400, delay.InSeconds());
EXPECT_EQ(ProxyResolutionService::PacPollPolicy::MODE_START_AFTER_ACTIVITY,
mode);
// Poll #4
mode = policy->GetNextDelay(error, delay, &delay);
EXPECT_EQ(14400, delay.InSeconds());
EXPECT_EQ(ProxyResolutionService::PacPollPolicy::MODE_START_AFTER_ACTIVITY,
mode);
// --------------------------------------------------
// Test the poll sequence in response to a success.
// --------------------------------------------------
error = OK;
// Poll #0
mode = policy->GetNextDelay(error, initial_delay, &delay);
EXPECT_EQ(43200, delay.InSeconds());
EXPECT_EQ(ProxyResolutionService::PacPollPolicy::MODE_START_AFTER_ACTIVITY,
mode);
// Poll #1
mode = policy->GetNextDelay(error, delay, &delay);
EXPECT_EQ(43200, delay.InSeconds());
EXPECT_EQ(ProxyResolutionService::PacPollPolicy::MODE_START_AFTER_ACTIVITY,
mode);
// Poll #2
mode = policy->GetNextDelay(error, delay, &delay);
EXPECT_EQ(43200, delay.InSeconds());
EXPECT_EQ(ProxyResolutionService::PacPollPolicy::MODE_START_AFTER_ACTIVITY,
mode);
}
// This tests the polling of the PAC script. Specifically, it tests that
// polling occurs in response to user activity.
TEST_F(ProxyResolutionServiceTest, PACScriptRefetchAfterActivity) {
ImmediateAfterActivityPollPolicy poll_policy;
ProxyResolutionService::set_pac_script_poll_policy(&poll_policy);
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
// Start 1 request.
ProxyInfo info1;
TestCompletionCallback callback1;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv =
service.ResolveProxy(GURL("http://request1"), std::string(), &info1,
callback1.callback(), &request1, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The first request should have triggered initial download of PAC script.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
// Nothing has been sent to the factory yet.
EXPECT_TRUE(factory->pending_requests().empty());
// At this point the ProxyResolutionService should be waiting for the
// PacFileFetcher to invoke its completion callback, notifying it of
// PAC script download completion.
fetcher->NotifyFetchCompletion(OK, kValidPacScript1);
// Now that the PAC script is downloaded, the request will have been sent to
// the proxy resolver.
EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
factory->pending_requests()[0]->script_data()->utf16());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(GURL("http://request1"), resolver.pending_jobs()[0]->url());
// Complete the pending request.
resolver.pending_jobs()[0]->results()->UseNamedProxy("request1:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
// Wait for completion callback, and verify that the request ran as expected.
EXPECT_THAT(callback1.WaitForResult(), IsOk());
EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
// At this point we have initialized the proxy service using a PAC script.
// Our PAC poller is set to update ONLY in response to network activity,
// (i.e. another call to ResolveProxy()).
ASSERT_FALSE(fetcher->has_pending_request());
ASSERT_TRUE(factory->pending_requests().empty());
ASSERT_TRUE(resolver.pending_jobs().empty());
// Start a second request.
ProxyInfo info2;
TestCompletionCallback callback2;
std::unique_ptr<ProxyResolutionService::Request> request2;
rv =
service.ResolveProxy(GURL("http://request2"), std::string(), &info2,
callback2.callback(), &request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// This request should have sent work to the resolver; complete it.
ASSERT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(GURL("http://request2"), resolver.pending_jobs()[0]->url());
resolver.pending_jobs()[0]->results()->UseNamedProxy("request2:80");
resolver.pending_jobs()[0]->CompleteNow(OK);
EXPECT_THAT(callback2.WaitForResult(), IsOk());
EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
// In response to getting that resolve request, the poller should have
// started the next poll, and made it as far as to request the download.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
// This time we will fail the download, to simulate a PAC script change.
fetcher->NotifyFetchCompletion(ERR_FAILED, std::string());
// Drain the message loop, so ProxyResolutionService is notified of the change
// and has a chance to re-configure itself.
base::RunLoop().RunUntilIdle();
// Start a third request -- this time we expect to get a direct connection
// since the PAC script poller experienced a failure.
ProxyInfo info3;
TestCompletionCallback callback3;
std::unique_ptr<ProxyResolutionService::Request> request3;
rv =
service.ResolveProxy(GURL("http://request3"), std::string(), &info3,
callback3.callback(), &request3, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_TRUE(info3.is_direct());
}
// Helper class to exercise URL sanitization using the different policies. This
// works by submitted URLs to the ProxyResolutionService. In turn the
// ProxyResolutionService sanitizes the URL and then passes it along to the
// ProxyResolver. This helper returns the URL seen by the ProxyResolver.
class SanitizeUrlHelper {
public:
SanitizeUrlHelper() {
std::unique_ptr<MockProxyConfigService> config_service(
new MockProxyConfigService("http://foopy/proxy.pac"));
factory = new MockAsyncProxyResolverFactory(false);
service_.reset(new ProxyResolutionService(
std::move(config_service), base::WrapUnique(factory), nullptr));
// Do an initial request to initialize the service (configure the PAC
// script).
GURL url("http://example.com");
ProxyInfo info;
TestCompletionCallback callback;
std::unique_ptr<ProxyResolutionService::Request> request;
int rv =
service_->ResolveProxy(url, std::string(), &info, callback.callback(),
&request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// First step is to download the PAC script.
EXPECT_EQ(GURL("http://foopy/proxy.pac"),
factory->pending_requests()[0]->script_data()->url());
factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
EXPECT_EQ(1u, resolver.pending_jobs().size());
EXPECT_EQ(url, resolver.pending_jobs()[0]->url());
// Complete the request.
resolver.pending_jobs()[0]->results()->UsePacString("DIRECT");
resolver.pending_jobs()[0]->CompleteNow(OK);
EXPECT_THAT(callback.WaitForResult(), IsOk());
EXPECT_TRUE(info.is_direct());
}
// Changes the URL sanitization policy for the underlying
// ProxyResolutionService. This will affect subsequent calls to SanitizeUrl.
void SetSanitizeUrlPolicy(ProxyResolutionService::SanitizeUrlPolicy policy) {
service_->set_sanitize_url_policy(policy);
}
// Makes a proxy resolution request through the ProxyResolutionService, and
// returns the URL that was submitted to the Proxy Resolver.
GURL SanitizeUrl(const GURL& raw_url) {
// Issue a request and see what URL is sent to the proxy resolver.
ProxyInfo info;
TestCompletionCallback callback;
std::unique_ptr<ProxyResolutionService::Request> request1;
int rv = service_->ResolveProxy(raw_url, std::string(), &info,
callback.callback(), &request1,
NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(1u, resolver.pending_jobs().size());
GURL sanitized_url = resolver.pending_jobs()[0]->url();
// Complete the request.
resolver.pending_jobs()[0]->results()->UsePacString("DIRECT");
resolver.pending_jobs()[0]->CompleteNow(OK);
EXPECT_THAT(callback.WaitForResult(), IsOk());
EXPECT_TRUE(info.is_direct());
return sanitized_url;
}
// Changes the ProxyResolutionService's URL sanitization policy and then
// sanitizes |raw_url|.
GURL SanitizeUrl(const GURL& raw_url,
ProxyResolutionService::SanitizeUrlPolicy policy) {
service_->set_sanitize_url_policy(policy);
return SanitizeUrl(raw_url);
}
private:
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory;
std::unique_ptr<ProxyResolutionService> service_;
};
TEST_F(ProxyResolutionServiceTest, SanitizeUrlDefaultsToSafe) {
SanitizeUrlHelper helper;
// Without changing the URL sanitization policy, the default should be to
// strip https:// URLs.
EXPECT_EQ(GURL("https://example.com/"),
helper.SanitizeUrl(
GURL("https://foo:bar@example.com/foo/bar/baz?hello#sigh")));
}
// Tests URL sanitization with input URLs that have a // non-cryptographic
// scheme (i.e. http://). The sanitized result is consistent regardless of the
// stripping mode selected.
TEST_F(ProxyResolutionServiceTest, SanitizeUrlForPacScriptNonCryptographic) {
const struct {
const char* raw_url;
const char* sanitized_url;
} kTests[] = {
// Embedded identity is stripped.
{
"http://foo:bar@example.com/", "http://example.com/",
},
{
"ftp://foo:bar@example.com/", "ftp://example.com/",
},
{
"ftp://example.com/some/path/here",
"ftp://example.com/some/path/here",
},
// Reference fragment is stripped.
{
"http://example.com/blah#hello", "http://example.com/blah",
},
// Query parameters are NOT stripped.
{
"http://example.com/foo/bar/baz?hello",
"http://example.com/foo/bar/baz?hello",
},
// Fragment is stripped, but path and query are left intact.
{
"http://foo:bar@example.com/foo/bar/baz?hello#sigh",
"http://example.com/foo/bar/baz?hello",
},
// Port numbers are not affected.
{
"http://example.com:88/hi", "http://example.com:88/hi",
},
};
SanitizeUrlHelper helper;
for (const auto& test : kTests) {
// The result of SanitizeUrlForPacScript() is the same regardless of the
// second parameter (sanitization mode), since the input URLs do not use a
// cryptographic scheme.
GURL raw_url(test.raw_url);
ASSERT_TRUE(raw_url.is_valid());
EXPECT_FALSE(raw_url.SchemeIsCryptographic());
EXPECT_EQ(GURL(test.sanitized_url),
helper.SanitizeUrl(
raw_url, ProxyResolutionService::SanitizeUrlPolicy::UNSAFE));
EXPECT_EQ(GURL(test.sanitized_url),
helper.SanitizeUrl(
raw_url, ProxyResolutionService::SanitizeUrlPolicy::SAFE));
}
}
// Tests URL sanitization using input URLs that have a cryptographic schemes
// (i.e. https://). The sanitized result differs depending on the sanitization
// mode chosen.
TEST_F(ProxyResolutionServiceTest, SanitizeUrlForPacScriptCryptographic) {
const struct {
// Input URL.
const char* raw_url;
// Output URL when stripping of cryptographic URLs is disabled.
const char* sanitized_url_unstripped;
// Output URL when stripping of cryptographic URLs is enabled.
const char* sanitized_url;
} kTests[] = {
// Embedded identity is always stripped.
{
"https://foo:bar@example.com/", "https://example.com/",
"https://example.com/",
},
// Fragments are always stripped, but stripping path is conditional on the
// mode.
{
"https://example.com/blah#hello", "https://example.com/blah",
"https://example.com/",
},
// Stripping the query is conditional on the mode.
{
"https://example.com/?hello", "https://example.com/?hello",
"https://example.com/",
},
// The embedded identity and fragment is always stripped, however path and
// query are conditional on the stripping mode.
{
"https://foo:bar@example.com/foo/bar/baz?hello#sigh",
"https://example.com/foo/bar/baz?hello", "https://example.com/",
},
// The URL's port should not be stripped.
{
"https://example.com:88/hi", "https://example.com:88/hi",
"https://example.com:88/",
},
// Try a wss:// URL, to make sure it also strips (is is also a
// cryptographic URL).
{
"wss://example.com:88/hi", "wss://example.com:88/hi",
"wss://example.com:88/",
},
};
SanitizeUrlHelper helper;
for (const auto& test : kTests) {
GURL raw_url(test.raw_url);
ASSERT_TRUE(raw_url.is_valid());
EXPECT_TRUE(raw_url.SchemeIsCryptographic());
EXPECT_EQ(GURL(test.sanitized_url_unstripped),
helper.SanitizeUrl(
raw_url, ProxyResolutionService::SanitizeUrlPolicy::UNSAFE));
EXPECT_EQ(GURL(test.sanitized_url),
helper.SanitizeUrl(
raw_url, ProxyResolutionService::SanitizeUrlPolicy::SAFE));
}
}
TEST_F(ProxyResolutionServiceTest, OnShutdownWithLiveRequest) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
ProxyInfo info;
TestCompletionCallback callback;
std::unique_ptr<ProxyResolutionService::Request> request;
int rv =
service.ResolveProxy(GURL("http://request/"), std::string(), &info,
callback.callback(), &request, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The first request should have triggered download of PAC script.
EXPECT_TRUE(fetcher->has_pending_request());
EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
service.OnShutdown();
EXPECT_THAT(callback.WaitForResult(), IsOk());
EXPECT_FALSE(fetcher->has_pending_request());
EXPECT_TRUE(info.is_direct());
}
TEST_F(ProxyResolutionServiceTest, OnShutdownFollowedByRequest) {
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
MockAsyncProxyResolver resolver;
MockAsyncProxyResolverFactory* factory =
new MockAsyncProxyResolverFactory(true);
ProxyResolutionService service(base::WrapUnique(config_service),
base::WrapUnique(factory), nullptr);
MockPacFileFetcher* fetcher = new MockPacFileFetcher;
service.SetPacFileFetchers(base::WrapUnique(fetcher),
std::make_unique<DoNothingDhcpPacFileFetcher>());
service.OnShutdown();
ProxyInfo info;
TestCompletionCallback callback;
std::unique_ptr<ProxyResolutionService::Request> request;
int rv =
service.ResolveProxy(GURL("http://request/"), std::string(), &info,
callback.callback(), &request, NetLogWithSource());
EXPECT_THAT(rv, IsOk());
EXPECT_FALSE(fetcher->has_pending_request());
EXPECT_TRUE(info.is_direct());
}
// Tests that the URL scheme for PAC files gets output to the histogram.
TEST_F(ProxyResolutionServiceTest, PacUrlSchemeHistogram) {
PacUrlSchemeHistogramTester pac_histogram;
MockProxyConfigService* config_service =
new MockProxyConfigService(ProxyConfig::CreateDirect());
ProxyResolutionService service(
base::WrapUnique(config_service),
std::make_unique<MockAsyncProxyResolverFactory>(false), nullptr);
pac_histogram.VerifyHistogram();
// Set an http:// PAC.
config_service->SetPacUrlConfig("http://example.test/");
pac_histogram.num_http++;
pac_histogram.VerifyHistogram();
// Set an https:// PAC.
config_service->SetPacUrlConfig("hTTps://example.test/wpad.dat");
pac_histogram.num_https++;
pac_histogram.VerifyHistogram();
// Set an ftp:// PAC.
config_service->SetPacUrlConfig("ftp://example.test/pac.js");
pac_histogram.num_ftp++;
pac_histogram.VerifyHistogram();
// Set an file:// PAC.
config_service->SetPacUrlConfig("file://example.test/boo");
pac_histogram.num_file++;
pac_histogram.VerifyHistogram();
// Set an mailto: PAC.
config_service->SetPacUrlConfig("mailto:foo@example.test");
pac_histogram.num_other++;
pac_histogram.VerifyHistogram();
// Set an data: PAC.
config_service->SetPacUrlConfig("data:,Hello%2C%20World!");
pac_histogram.num_data++;
pac_histogram.VerifyHistogram();
// Set an filesystem: PAC.
config_service->SetPacUrlConfig("filesystem:http://example.test/pac.js");
pac_histogram.num_other++;
pac_histogram.VerifyHistogram();
// Set another https:// as PAC.
config_service->SetPacUrlConfig("https://example2.test/wpad.dat");
pac_histogram.num_https++;
pac_histogram.VerifyHistogram();
}
} // namespace net