blob: 6dd7bd245b412e49622962629244de038920c46a [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_config_service_linux.h"
#include <map>
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/format_macros.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/proxy_resolution/proxy_config.h"
#include "net/proxy_resolution/proxy_config_service_common_unittest.h"
#include "net/test/test_with_scoped_task_environment.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
// TODO(eroman): Convert these to parameterized tests using TEST_P().
namespace net {
namespace {
// Set of values for all environment variables that we might
// query. NULL represents an unset variable.
struct EnvVarValues {
// The strange capitalization is so that the field matches the
// environment variable name exactly.
const char* DESKTOP_SESSION;
const char* HOME;
const char* KDEHOME;
const char* KDE_SESSION_VERSION;
const char* XDG_CURRENT_DESKTOP;
const char* auto_proxy;
const char* all_proxy;
const char* http_proxy;
const char* https_proxy;
const char* ftp_proxy;
const char* SOCKS_SERVER;
const char* SOCKS_VERSION;
const char* no_proxy;
};
// Undo macro pollution from GDK includes (from message_loop.h).
#undef TRUE
#undef FALSE
// So as to distinguish between an unset boolean variable and
// one that is false.
enum BoolSettingValue { UNSET = 0, TRUE, FALSE };
// Set of values for all gsettings settings that we might query.
struct GSettingsValues {
// strings
const char* mode;
const char* autoconfig_url;
const char* http_host;
const char* secure_host;
const char* ftp_host;
const char* socks_host;
// integers
int http_port;
int secure_port;
int ftp_port;
int socks_port;
// booleans
BoolSettingValue use_proxy;
BoolSettingValue same_proxy;
BoolSettingValue use_auth;
// string list
std::vector<std::string> ignore_hosts;
};
// Mapping from a setting name to the location of the corresponding
// value (inside a EnvVarValues or GSettingsValues struct).
template <typename key_type, typename value_type>
struct SettingsTable {
typedef std::map<key_type, value_type*> map_type;
// Gets the value from its location
value_type Get(key_type key) {
auto it = settings.find(key);
// In case there's a typo or the unittest becomes out of sync.
CHECK(it != settings.end()) << "key " << key << " not found";
value_type* value_ptr = it->second;
return *value_ptr;
}
map_type settings;
};
class MockEnvironment : public base::Environment {
public:
MockEnvironment() {
#define ENTRY(x) table_[#x] = &values.x
ENTRY(DESKTOP_SESSION);
ENTRY(HOME);
ENTRY(KDEHOME);
ENTRY(KDE_SESSION_VERSION);
ENTRY(XDG_CURRENT_DESKTOP);
ENTRY(auto_proxy);
ENTRY(all_proxy);
ENTRY(http_proxy);
ENTRY(https_proxy);
ENTRY(ftp_proxy);
ENTRY(no_proxy);
ENTRY(SOCKS_SERVER);
ENTRY(SOCKS_VERSION);
#undef ENTRY
Reset();
}
// Zeroes all environment values.
void Reset() {
EnvVarValues zero_values = {0};
values = zero_values;
}
// Begin base::Environment implementation.
bool GetVar(base::StringPiece variable_name, std::string* result) override {
auto it = table_.find(variable_name);
if (it == table_.end() || !*it->second)
return false;
// Note that the variable may be defined but empty.
*result = *(it->second);
return true;
}
bool SetVar(base::StringPiece variable_name,
const std::string& new_value) override {
ADD_FAILURE();
return false;
}
bool UnSetVar(base::StringPiece variable_name) override {
ADD_FAILURE();
return false;
}
// End base::Environment implementation.
// Intentionally public, for convenience when setting up a test.
EnvVarValues values;
private:
std::map<base::StringPiece, const char**> table_;
};
class MockSettingGetter : public ProxyConfigServiceLinux::SettingGetter {
public:
typedef ProxyConfigServiceLinux::SettingGetter SettingGetter;
MockSettingGetter() {
#define ENTRY(key, field) \
strings_table.settings[SettingGetter::key] = &values.field
ENTRY(PROXY_MODE, mode);
ENTRY(PROXY_AUTOCONF_URL, autoconfig_url);
ENTRY(PROXY_HTTP_HOST, http_host);
ENTRY(PROXY_HTTPS_HOST, secure_host);
ENTRY(PROXY_FTP_HOST, ftp_host);
ENTRY(PROXY_SOCKS_HOST, socks_host);
#undef ENTRY
#define ENTRY(key, field) \
ints_table.settings[SettingGetter::key] = &values.field
ENTRY(PROXY_HTTP_PORT, http_port);
ENTRY(PROXY_HTTPS_PORT, secure_port);
ENTRY(PROXY_FTP_PORT, ftp_port);
ENTRY(PROXY_SOCKS_PORT, socks_port);
#undef ENTRY
#define ENTRY(key, field) \
bools_table.settings[SettingGetter::key] = &values.field
ENTRY(PROXY_USE_HTTP_PROXY, use_proxy);
ENTRY(PROXY_USE_SAME_PROXY, same_proxy);
ENTRY(PROXY_USE_AUTHENTICATION, use_auth);
#undef ENTRY
string_lists_table.settings[SettingGetter::PROXY_IGNORE_HOSTS] =
&values.ignore_hosts;
Reset();
}
// Zeros all environment values.
void Reset() {
GSettingsValues zero_values = {0};
values = zero_values;
}
bool Init(const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner)
override {
task_runner_ = glib_task_runner;
return true;
}
void ShutDown() override {}
bool SetUpNotifications(
ProxyConfigServiceLinux::Delegate* delegate) override {
return true;
}
const scoped_refptr<base::SequencedTaskRunner>& GetNotificationTaskRunner()
override {
return task_runner_;
}
bool GetString(StringSetting key, std::string* result) override {
const char* value = strings_table.Get(key);
if (value) {
*result = value;
return true;
}
return false;
}
bool GetBool(BoolSetting key, bool* result) override {
BoolSettingValue value = bools_table.Get(key);
switch (value) {
case UNSET:
return false;
case TRUE:
*result = true;
break;
case FALSE:
*result = false;
}
return true;
}
bool GetInt(IntSetting key, int* result) override {
// We don't bother to distinguish unset keys from 0 values.
*result = ints_table.Get(key);
return true;
}
bool GetStringList(StringListSetting key,
std::vector<std::string>* result) override {
*result = string_lists_table.Get(key);
// We don't bother to distinguish unset keys from empty lists.
return !result->empty();
}
bool BypassListIsReversed() override { return false; }
bool MatchHostsUsingSuffixMatching() override { return false; }
// Intentionally public, for convenience when setting up a test.
GSettingsValues values;
private:
scoped_refptr<base::SequencedTaskRunner> task_runner_;
SettingsTable<StringSetting, const char*> strings_table;
SettingsTable<BoolSetting, BoolSettingValue> bools_table;
SettingsTable<IntSetting, int> ints_table;
SettingsTable<StringListSetting, std::vector<std::string>> string_lists_table;
};
// This helper class runs ProxyConfigServiceLinux::GetLatestProxyConfig() on
// the main TaskRunner and synchronously waits for the result.
// Some code duplicated from pac_file_fetcher_unittest.cc.
class SyncConfigGetter : public ProxyConfigService::Observer {
public:
// Takes ownership of |config_service|.
explicit SyncConfigGetter(ProxyConfigServiceLinux* config_service)
: event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED),
main_thread_("Main_Thread"),
config_service_(config_service),
matches_pac_url_event_(
base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED) {
// Start the main IO thread.
base::Thread::Options options;
options.message_loop_type = base::MessageLoop::TYPE_IO;
main_thread_.StartWithOptions(options);
// Make sure the thread started.
main_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&SyncConfigGetter::Init, base::Unretained(this)));
Wait();
}
~SyncConfigGetter() override {
// Clean up the main thread.
main_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&SyncConfigGetter::CleanUp, base::Unretained(this)));
Wait();
}
// Does gsettings setup and initial fetch of the proxy config,
// all on the calling thread (meant to be the thread with the
// default glib main loop, which is the glib thread).
void SetupAndInitialFetch() {
config_service_->SetupAndFetchInitialConfig(
base::ThreadTaskRunnerHandle::Get(), main_thread_.task_runner(),
TRAFFIC_ANNOTATION_FOR_TESTS);
}
// Synchronously gets the proxy config.
ProxyConfigService::ConfigAvailability SyncGetLatestProxyConfig(
ProxyConfigWithAnnotation* config) {
main_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&SyncConfigGetter::GetLatestConfigOnIOThread,
base::Unretained(this)));
Wait();
*config = proxy_config_;
return get_latest_config_result_;
}
// Instructs |matches_pac_url_event_| to be signalled once the configuration
// changes to |pac_url|. The way to use this function is:
//
// SetExpectedPacUrl(..);
// WriteFile(...)
// WaitUntilPacUrlMatchesExpectation();
//
// The expectation must be set *before* any file-level mutation is done,
// otherwise the change may be received before
// WaitUntilPacUrlMatchesExpectation(), and subsequently be lost.
void SetExpectedPacUrl(const std::string& pac_url) {
base::AutoLock lock(lock_);
expected_pac_url_ = GURL(pac_url);
}
// Blocks until the proxy config service has received a configuration
// matching the value previously passed to SetExpectedPacUrl().
void WaitUntilPacUrlMatchesExpectation() {
matches_pac_url_event_.Wait();
matches_pac_url_event_.Reset();
}
private:
void OnProxyConfigChanged(
const ProxyConfigWithAnnotation& config,
ProxyConfigService::ConfigAvailability availability) override {
// If the configuration changed to |expected_pac_url_| signal the event.
base::AutoLock lock(lock_);
if (config.value().has_pac_url() &&
config.value().pac_url() == expected_pac_url_) {
expected_pac_url_ = GURL();
matches_pac_url_event_.Signal();
}
}
// [Runs on |main_thread_|]
void Init() {
config_service_->AddObserver(this);
event_.Signal();
}
// Calls GetLatestProxyConfig, running on |main_thread_| Signals |event_|
// on completion.
void GetLatestConfigOnIOThread() {
get_latest_config_result_ =
config_service_->GetLatestProxyConfig(&proxy_config_);
event_.Signal();
}
// [Runs on |main_thread_|] Signals |event_| on cleanup completion.
void CleanUp() {
config_service_->RemoveObserver(this);
delete config_service_;
base::RunLoop().RunUntilIdle();
event_.Signal();
}
void Wait() {
event_.Wait();
event_.Reset();
}
base::WaitableEvent event_;
base::Thread main_thread_;
ProxyConfigServiceLinux* config_service_;
// The config obtained by |main_thread_| and read back by the main
// thread.
ProxyConfigWithAnnotation proxy_config_;
// Return value from GetLatestProxyConfig().
ProxyConfigService::ConfigAvailability get_latest_config_result_;
// If valid, |expected_pac_url_| is the URL that is being waited for in
// the proxy configuration. The URL should only be accessed while |lock_|
// is held. Once a configuration arrives for |expected_pac_url_| then the
// event |matches_pac_url_event_| will be signalled.
base::Lock lock_;
GURL expected_pac_url_;
base::WaitableEvent matches_pac_url_event_;
};
// This test fixture is only really needed for the KDEConfigParser test case,
// but all the test cases with the same prefix ("ProxyConfigServiceLinuxTest")
// must use the same test fixture class (also "ProxyConfigServiceLinuxTest").
class ProxyConfigServiceLinuxTest : public PlatformTest,
public WithScopedTaskEnvironment {
protected:
void SetUp() override {
PlatformTest::SetUp();
// Set up a temporary KDE home directory.
std::string prefix("ProxyConfigServiceLinuxTest_user_home");
base::CreateNewTempDirectory(prefix, &user_home_);
config_home_ = user_home_.Append(FILE_PATH_LITERAL(".config"));
kde_home_ = user_home_.Append(FILE_PATH_LITERAL(".kde"));
base::FilePath path = kde_home_.Append(FILE_PATH_LITERAL("share"));
path = path.Append(FILE_PATH_LITERAL("config"));
base::CreateDirectory(path);
kioslaverc_ = path.Append(FILE_PATH_LITERAL("kioslaverc"));
// Set up paths but do not create the directory for .kde4.
kde4_home_ = user_home_.Append(FILE_PATH_LITERAL(".kde4"));
path = kde4_home_.Append(FILE_PATH_LITERAL("share"));
kde4_config_ = path.Append(FILE_PATH_LITERAL("config"));
kioslaverc4_ = kde4_config_.Append(FILE_PATH_LITERAL("kioslaverc"));
// Set up paths for KDE 5
kioslaverc5_ = config_home_.Append(FILE_PATH_LITERAL("kioslaverc"));
}
void TearDown() override {
// Delete the temporary KDE home directory.
base::DeleteFile(user_home_, true);
PlatformTest::TearDown();
}
base::FilePath user_home_;
base::FilePath config_home_;
// KDE3 paths.
base::FilePath kde_home_;
base::FilePath kioslaverc_;
// KDE4 paths.
base::FilePath kde4_home_;
base::FilePath kde4_config_;
base::FilePath kioslaverc4_;
// KDE5 paths.
base::FilePath kioslaverc5_;
};
// Builds an identifier for each test in an array.
#define TEST_DESC(desc) base::StringPrintf("at line %d <%s>", __LINE__, desc)
TEST_F(ProxyConfigServiceLinuxTest, BasicGSettingsTest) {
std::vector<std::string> empty_ignores;
std::vector<std::string> google_ignores;
google_ignores.push_back("*.google.com");
// Inspired from proxy_config_service_win_unittest.cc.
// Very neat, but harder to track down failures though.
const struct {
// Short description to identify the test
std::string description;
// Input.
GSettingsValues values;
// Expected outputs (availability and fields of ProxyConfig).
ProxyConfigService::ConfigAvailability availability;
bool auto_detect;
GURL pac_url;
ProxyRulesExpectation proxy_rules;
} tests[] = {
{
TEST_DESC("No proxying"),
{
// Input.
"none", // mode
"", // autoconfig_url
"", "", "", "", // hosts
0, 0, 0, 0, // ports
FALSE, FALSE, FALSE, // use, same, auth
empty_ignores, // ignore_hosts
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Auto detect"),
{
// Input.
"auto", // mode
"", // autoconfig_url
"", "", "", "", // hosts
0, 0, 0, 0, // ports
FALSE, FALSE, FALSE, // use, same, auth
empty_ignores, // ignore_hosts
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
true, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Valid PAC URL"),
{
// Input.
"auto", // mode
"http://wpad/wpad.dat", // autoconfig_url
"", "", "", "", // hosts
0, 0, 0, 0, // ports
FALSE, FALSE, FALSE, // use, same, auth
empty_ignores, // ignore_hosts
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL("http://wpad/wpad.dat"), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Invalid PAC URL"),
{
// Input.
"auto", // mode
"wpad.dat", // autoconfig_url
"", "", "", "", // hosts
0, 0, 0, 0, // ports
FALSE, FALSE, FALSE, // use, same, auth
empty_ignores, // ignore_hosts
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Single-host in proxy list"),
{
// Input.
"manual", // mode
"", // autoconfig_url
"www.google.com", "", "", "", // hosts
80, 0, 0, 0, // ports
TRUE, TRUE, FALSE, // use, same, auth
empty_ignores, // ignore_hosts
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Single("www.google.com:80", // single proxy
""), // bypass rules
},
{
TEST_DESC("use_http_proxy is honored"),
{
// Input.
"manual", // mode
"", // autoconfig_url
"www.google.com", "", "", "", // hosts
80, 0, 0, 0, // ports
FALSE, TRUE, FALSE, // use, same, auth
empty_ignores, // ignore_hosts
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("use_http_proxy and use_same_proxy are optional"),
{
// Input.
"manual", // mode
"", // autoconfig_url
"www.google.com", "", "", "", // hosts
80, 0, 0, 0, // ports
UNSET, UNSET, FALSE, // use, same, auth
empty_ignores, // ignore_hosts
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"", // https
"", // ftp
""), // bypass rules
},
{
TEST_DESC("Single-host, different port"),
{
// Input.
"manual", // mode
"", // autoconfig_url
"www.google.com", "", "", "", // hosts
88, 0, 0, 0, // ports
TRUE, TRUE, FALSE, // use, same, auth
empty_ignores, // ignore_hosts
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Single("www.google.com:88", // single proxy
""), // bypass rules
},
{
TEST_DESC("Per-scheme proxy rules"),
{
// Input.
"manual", // mode
"", // autoconfig_url
"www.google.com", // http_host
"www.foo.com", // secure_host
"ftp.foo.com", // ftp
"", // socks
88, 110, 121, 0, // ports
TRUE, FALSE, FALSE, // use, same, auth
empty_ignores, // ignore_hosts
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:88", // http
"www.foo.com:110", // https
"ftp.foo.com:121", // ftp
""), // bypass rules
},
{
TEST_DESC("socks"),
{
// Input.
"manual", // mode
"", // autoconfig_url
"", "", "", "socks.com", // hosts
0, 0, 0, 99, // ports
TRUE, FALSE, FALSE, // use, same, auth
empty_ignores, // ignore_hosts
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Single(
"socks5://socks.com:99", // single proxy
"") // bypass rules
},
{
TEST_DESC("Per-scheme proxy rules with fallback to SOCKS"),
{
// Input.
"manual", // mode
"", // autoconfig_url
"www.google.com", // http_host
"www.foo.com", // secure_host
"ftp.foo.com", // ftp
"foobar.net", // socks
88, 110, 121, 99, // ports
TRUE, FALSE, FALSE, // use, same, auth
empty_ignores, // ignore_hosts
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerSchemeWithSocks(
"www.google.com:88", // http
"www.foo.com:110", // https
"ftp.foo.com:121", // ftp
"socks5://foobar.net:99", // socks
""), // bypass rules
},
{
TEST_DESC(
"Per-scheme proxy rules (just HTTP) with fallback to SOCKS"),
{
// Input.
"manual", // mode
"", // autoconfig_url
"www.google.com", // http_host
"", // secure_host
"", // ftp
"foobar.net", // socks
88, 0, 0, 99, // ports
TRUE, FALSE, FALSE, // use, same, auth
empty_ignores, // ignore_hosts
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerSchemeWithSocks(
"www.google.com:88", // http
"", // https
"", // ftp
"socks5://foobar.net:99", // socks
""), // bypass rules
},
{
TEST_DESC("Bypass *.google.com"),
{
// Input.
"manual", // mode
"", // autoconfig_url
"www.google.com", "", "", "", // hosts
80, 0, 0, 0, // ports
TRUE, TRUE, FALSE, // use, same, auth
google_ignores, // ignore_hosts
},
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Single("www.google.com:80", // single proxy
"*.google.com"), // bypass rules
},
};
for (size_t i = 0; i < arraysize(tests); ++i) {
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "] %s", i,
tests[i].description.c_str()));
std::unique_ptr<MockEnvironment> env(new MockEnvironment);
MockSettingGetter* setting_getter = new MockSettingGetter;
SyncConfigGetter sync_config_getter(new ProxyConfigServiceLinux(
std::move(env), setting_getter, TRAFFIC_ANNOTATION_FOR_TESTS));
ProxyConfigWithAnnotation config;
setting_getter->values = tests[i].values;
sync_config_getter.SetupAndInitialFetch();
ProxyConfigService::ConfigAvailability availability =
sync_config_getter.SyncGetLatestProxyConfig(&config);
EXPECT_EQ(tests[i].availability, availability);
if (availability == ProxyConfigService::CONFIG_VALID) {
EXPECT_EQ(tests[i].auto_detect, config.value().auto_detect());
EXPECT_EQ(tests[i].pac_url, config.value().pac_url());
EXPECT_TRUE(tests[i].proxy_rules.Matches(config.value().proxy_rules()));
}
}
}
TEST_F(ProxyConfigServiceLinuxTest, BasicEnvTest) {
// Inspired from proxy_config_service_win_unittest.cc.
const struct {
// Short description to identify the test
std::string description;
// Input.
EnvVarValues values;
// Expected outputs (availability and fields of ProxyConfig).
ProxyConfigService::ConfigAvailability availability;
bool auto_detect;
GURL pac_url;
ProxyRulesExpectation proxy_rules;
} tests[] = {
{
TEST_DESC("No proxying"),
{
// Input.
nullptr, // DESKTOP_SESSION
nullptr, // HOME
nullptr, // KDEHOME
nullptr, // KDE_SESSION_VERSION
nullptr, // XDG_CURRENT_DESKTOP
nullptr, // auto_proxy
nullptr, // all_proxy
nullptr, nullptr, nullptr, // per-proto proxies
nullptr, nullptr, // SOCKS
"*", // no_proxy
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Auto detect"),
{
// Input.
nullptr, // DESKTOP_SESSION
nullptr, // HOME
nullptr, // KDEHOME
nullptr, // KDE_SESSION_VERSION
nullptr, // XDG_CURRENT_DESKTOP
"", // auto_proxy
nullptr, // all_proxy
nullptr, nullptr, nullptr, // per-proto proxies
nullptr, nullptr, // SOCKS
nullptr, // no_proxy
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
true, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Valid PAC URL"),
{
// Input.
nullptr, // DESKTOP_SESSION
nullptr, // HOME
nullptr, // KDEHOME
nullptr, // KDE_SESSION_VERSION
nullptr, // XDG_CURRENT_DESKTOP
"http://wpad/wpad.dat", // auto_proxy
nullptr, // all_proxy
nullptr, nullptr, nullptr, // per-proto proxies
nullptr, nullptr, // SOCKS
nullptr, // no_proxy
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL("http://wpad/wpad.dat"), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Invalid PAC URL"),
{
// Input.
nullptr, // DESKTOP_SESSION
nullptr, // HOME
nullptr, // KDEHOME
nullptr, // KDE_SESSION_VERSION
nullptr, // XDG_CURRENT_DESKTOP
"wpad.dat", // auto_proxy
nullptr, // all_proxy
nullptr, nullptr, nullptr, // per-proto proxies
nullptr, nullptr, // SOCKS
nullptr, // no_proxy
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Single-host in proxy list"),
{
// Input.
nullptr, // DESKTOP_SESSION
nullptr, // HOME
nullptr, // KDEHOME
nullptr, // KDE_SESSION_VERSION
nullptr, // XDG_CURRENT_DESKTOP
nullptr, // auto_proxy
"www.google.com", // all_proxy
nullptr, nullptr, nullptr, // per-proto proxies
nullptr, nullptr, // SOCKS
nullptr, // no_proxy
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Single("www.google.com:80", // single proxy
""), // bypass rules
},
{
TEST_DESC("Single-host, different port"),
{
// Input.
nullptr, // DESKTOP_SESSION
nullptr, // HOME
nullptr, // KDEHOME
nullptr, // KDE_SESSION_VERSION
nullptr, // XDG_CURRENT_DESKTOP
nullptr, // auto_proxy
"www.google.com:99", // all_proxy
nullptr, nullptr, nullptr, // per-proto proxies
nullptr, nullptr, // SOCKS
nullptr, // no_proxy
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Single("www.google.com:99", // single
""), // bypass rules
},
{
TEST_DESC("Tolerate a scheme"),
{
// Input.
nullptr, // DESKTOP_SESSION
nullptr, // HOME
nullptr, // KDEHOME
nullptr, // KDE_SESSION_VERSION
nullptr, // XDG_CURRENT_DESKTOP
nullptr, // auto_proxy
"http://www.google.com:99", // all_proxy
nullptr, nullptr, nullptr, // per-proto proxies
nullptr, nullptr, // SOCKS
nullptr, // no_proxy
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Single("www.google.com:99", // single proxy
""), // bypass rules
},
{
TEST_DESC("Per-scheme proxy rules"),
{
// Input.
nullptr, // DESKTOP_SESSION
nullptr, // HOME
nullptr, // KDEHOME
nullptr, // KDE_SESSION_VERSION
nullptr, // XDG_CURRENT_DESKTOP
nullptr, // auto_proxy
nullptr, // all_proxy
"www.google.com:80", "www.foo.com:110",
"ftp.foo.com:121", // per-proto
nullptr, nullptr, // SOCKS
nullptr, // no_proxy
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"www.foo.com:110", // https
"ftp.foo.com:121", // ftp
""), // bypass rules
},
{
TEST_DESC("socks"),
{
// Input.
nullptr, // DESKTOP_SESSION
nullptr, // HOME
nullptr, // KDEHOME
nullptr, // KDE_SESSION_VERSION
nullptr, // XDG_CURRENT_DESKTOP
nullptr, // auto_proxy
"", // all_proxy
nullptr, nullptr, nullptr, // per-proto proxies
"socks.com:888", nullptr, // SOCKS
nullptr, // no_proxy
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Single(
"socks5://socks.com:888", // single proxy
""), // bypass rules
},
{
TEST_DESC("socks4"),
{
// Input.
nullptr, // DESKTOP_SESSION
nullptr, // HOME
nullptr, // KDEHOME
nullptr, // KDE_SESSION_VERSION
nullptr, // XDG_CURRENT_DESKTOP
nullptr, // auto_proxy
"", // all_proxy
nullptr, nullptr, nullptr, // per-proto proxies
"socks.com:888", "4", // SOCKS
nullptr, // no_proxy
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Single(
"socks4://socks.com:888", // single proxy
""), // bypass rules
},
{
TEST_DESC("socks default port"),
{
// Input.
nullptr, // DESKTOP_SESSION
nullptr, // HOME
nullptr, // KDEHOME
nullptr, // KDE_SESSION_VERSION
nullptr, // XDG_CURRENT_DESKTOP
nullptr, // auto_proxy
"", // all_proxy
nullptr, nullptr, nullptr, // per-proto proxies
"socks.com", nullptr, // SOCKS
nullptr, // no_proxy
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Single(
"socks5://socks.com:1080", // single proxy
""), // bypass rules
},
{
TEST_DESC("bypass"),
{
// Input.
nullptr, // DESKTOP_SESSION
nullptr, // HOME
nullptr, // KDEHOME
nullptr, // KDE_SESSION_VERSION
nullptr, // XDG_CURRENT_DESKTOP
nullptr, // auto_proxy
"www.google.com", // all_proxy
nullptr, nullptr, nullptr, // per-proto
nullptr, nullptr, // SOCKS
".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8", // no_proxy
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Single(
"www.google.com:80",
"*.google.com,*foo.com:99,1.2.3.4:22,127.0.0.1/8"),
},
};
for (size_t i = 0; i < arraysize(tests); ++i) {
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "] %s", i,
tests[i].description.c_str()));
std::unique_ptr<MockEnvironment> env(new MockEnvironment);
env->values = tests[i].values;
MockSettingGetter* setting_getter = new MockSettingGetter;
SyncConfigGetter sync_config_getter(new ProxyConfigServiceLinux(
std::move(env), setting_getter, TRAFFIC_ANNOTATION_FOR_TESTS));
ProxyConfigWithAnnotation config;
sync_config_getter.SetupAndInitialFetch();
ProxyConfigService::ConfigAvailability availability =
sync_config_getter.SyncGetLatestProxyConfig(&config);
EXPECT_EQ(tests[i].availability, availability);
if (availability == ProxyConfigService::CONFIG_VALID) {
EXPECT_EQ(tests[i].auto_detect, config.value().auto_detect());
EXPECT_EQ(tests[i].pac_url, config.value().pac_url());
EXPECT_TRUE(tests[i].proxy_rules.Matches(config.value().proxy_rules()));
}
}
}
TEST_F(ProxyConfigServiceLinuxTest, GSettingsNotification) {
std::unique_ptr<MockEnvironment> env(new MockEnvironment);
MockSettingGetter* setting_getter = new MockSettingGetter;
ProxyConfigServiceLinux* service = new ProxyConfigServiceLinux(
std::move(env), setting_getter, TRAFFIC_ANNOTATION_FOR_TESTS);
SyncConfigGetter sync_config_getter(service);
ProxyConfigWithAnnotation config;
// Start with no proxy.
setting_getter->values.mode = "none";
sync_config_getter.SetupAndInitialFetch();
EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
sync_config_getter.SyncGetLatestProxyConfig(&config));
EXPECT_FALSE(config.value().auto_detect());
// Now set to auto-detect.
setting_getter->values.mode = "auto";
// Simulate setting change notification callback.
service->OnCheckProxyConfigSettings();
EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
sync_config_getter.SyncGetLatestProxyConfig(&config));
EXPECT_TRUE(config.value().auto_detect());
// Simulate two settings changes, where PROXY_MODE is missing. This will make
// the settings be interpreted as DIRECT.
//
// Trigering the check a *second* time is a regression test for
// https://crbug.com/848237, where a comparison is done between two nullopts.
for (size_t i = 0; i < 2; ++i) {
setting_getter->values.mode = nullptr;
service->OnCheckProxyConfigSettings();
EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
sync_config_getter.SyncGetLatestProxyConfig(&config));
EXPECT_FALSE(config.value().auto_detect());
EXPECT_TRUE(config.value().proxy_rules().empty());
}
}
TEST_F(ProxyConfigServiceLinuxTest, KDEConfigParser) {
// One of the tests below needs a worst-case long line prefix. We build it
// programmatically so that it will always be the right size.
std::string long_line;
size_t limit = ProxyConfigServiceLinux::SettingGetter::BUFFER_SIZE - 1;
for (size_t i = 0; i < limit; ++i)
long_line += "-";
// Inspired from proxy_config_service_win_unittest.cc.
const struct {
// Short description to identify the test
std::string description;
// Input.
std::string kioslaverc;
EnvVarValues env_values;
// Expected outputs (availability and fields of ProxyConfig).
ProxyConfigService::ConfigAvailability availability;
bool auto_detect;
GURL pac_url;
ProxyRulesExpectation proxy_rules;
} tests[] = {
{
TEST_DESC("No proxying"),
// Input.
"[Proxy Settings]\nProxyType=0\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Invalid proxy type (ProxyType=-3)"),
// Input.
"[Proxy Settings]\nProxyType=-3\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Invalid proxy type (ProxyType=AB-)"),
// Input.
"[Proxy Settings]\nProxyType=AB-\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Auto detect"),
// Input.
"[Proxy Settings]\nProxyType=3\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
true, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Valid PAC URL"),
// Input.
"[Proxy Settings]\nProxyType=2\n"
"Proxy Config Script=http://wpad/wpad.dat\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL("http://wpad/wpad.dat"), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Valid PAC file without file://"),
// Input.
"[Proxy Settings]\nProxyType=2\n"
"Proxy Config Script=/wpad/wpad.dat\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL("file:///wpad/wpad.dat"), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Per-scheme proxy rules"),
// Input.
"[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
"httpsProxy=www.foo.com\nftpProxy=ftp.foo.com\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"www.foo.com:80", // https
"ftp.foo.com:80", // http
""), // bypass rules
},
{
TEST_DESC("Only HTTP proxy specified"),
// Input.
"[Proxy Settings]\nProxyType=1\n"
"httpProxy=www.google.com\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"", // https
"", // ftp
""), // bypass rules
},
{
TEST_DESC("Only HTTP proxy specified, different port"),
// Input.
"[Proxy Settings]\nProxyType=1\n"
"httpProxy=www.google.com:88\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:88", // http
"", // https
"", // ftp
""), // bypass rules
},
{
TEST_DESC(
"Only HTTP proxy specified, different port, space-delimited"),
// Input.
"[Proxy Settings]\nProxyType=1\n"
"httpProxy=www.google.com 88\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:88", // http
"", // https
"", // ftp
""), // bypass rules
},
{
TEST_DESC("Bypass *.google.com"),
// Input.
"[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
"NoProxyFor=.google.com\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"", // https
"", // ftp
"*.google.com"), // bypass rules
},
{
TEST_DESC("Bypass *.google.com and *.kde.org"),
// Input.
"[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
"NoProxyFor=.google.com,.kde.org\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme(
"www.google.com:80", // http
"", // https
"", // ftp
"*.google.com,*.kde.org"), // bypass rules
},
{
TEST_DESC("Correctly parse bypass list with ReversedException=true"),
// Input.
"[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
"NoProxyFor=.google.com\nReversedException=true\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerSchemeWithBypassReversed(
"www.google.com:80", // http
"", // https
"", // ftp
"*.google.com"), // bypass rules
},
{
TEST_DESC("Correctly parse bypass list with ReversedException=false"),
// Input.
"[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
"NoProxyFor=.google.com\nReversedException=false\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"", // https
"", // ftp
"*.google.com"), // bypass rules
},
{
TEST_DESC("Correctly parse bypass list with ReversedException=1"),
// Input.
"[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
"NoProxyFor=.google.com\nReversedException=1\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerSchemeWithBypassReversed(
"www.google.com:80", // http
"", // https
"", // ftp
"*.google.com"), // bypass rules
},
{
TEST_DESC("Overflow: ReversedException=18446744073709551617"),
// Input.
"[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
"NoProxyFor=.google.com\nReversedException=18446744073709551617\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"", // https
"", // ftp
"*.google.com"), // bypass rules
},
{
TEST_DESC("Not a number: ReversedException=noitpecxE"),
// Input.
"[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
"NoProxyFor=.google.com\nReversedException=noitpecxE\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"", // https
"", // ftp
"*.google.com"), // bypass rules
},
{
TEST_DESC("socks"),
// Input.
"[Proxy Settings]\nProxyType=1\nsocksProxy=socks.com 888\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Single(
"socks5://socks.com:888", // single proxy
""), // bypass rules
},
{
TEST_DESC("socks4"),
// Input.
"[Proxy Settings]\nProxyType=1\nsocksProxy=socks4://socks.com 888\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Single(
"socks4://socks.com:888", // single proxy
""), // bypass rules
},
{
TEST_DESC("Treat all hostname patterns as wildcard patterns"),
// Input.
"[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
"NoProxyFor=google.com,kde.org,<local>\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme(
"www.google.com:80", // http
"", // https
"", // ftp
"*google.com,*kde.org,<local>"), // bypass rules
},
{
TEST_DESC("Allow trailing whitespace after boolean value"),
// Input.
"[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
"NoProxyFor=.google.com\nReversedException=true \n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerSchemeWithBypassReversed(
"www.google.com:80", // http
"", // https
"", // ftp
"*.google.com"), // bypass rules
},
{
TEST_DESC("Ignore settings outside [Proxy Settings]"),
// Input.
"httpsProxy=www.foo.com\n[Proxy Settings]\nProxyType=1\n"
"httpProxy=www.google.com\n[Other Section]\nftpProxy=ftp.foo.com\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"", // https
"", // ftp
""), // bypass rules
},
{
TEST_DESC("Handle CRLF line endings"),
// Input.
"[Proxy Settings]\r\nProxyType=1\r\nhttpProxy=www.google.com\r\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"", // https
"", // ftp
""), // bypass rules
},
{
TEST_DESC("Handle blank lines and mixed line endings"),
// Input.
"[Proxy Settings]\r\n\nProxyType=1\n\r\nhttpProxy=www.google.com\n\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"", // https
"", // ftp
""), // bypass rules
},
{
TEST_DESC("Handle localized settings"),
// Input.
"[Proxy Settings]\nProxyType[$e]=1\nhttpProxy[$e]=www.google.com\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"", // https
"", // ftp
""), // bypass rules
},
{
TEST_DESC("Ignore malformed localized settings"),
// Input.
"[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
"httpsProxy$e]=www.foo.com\nftpProxy=ftp.foo.com\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"", // https
"ftp.foo.com:80", // ftp
""), // bypass rules
},
{
TEST_DESC("Handle strange whitespace"),
// Input.
"[Proxy Settings]\nProxyType [$e] =2\n"
" Proxy Config Script = http:// foo\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL("http:// foo"), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Ignore all of a line which is too long"),
// Input.
std::string("[Proxy Settings]\nProxyType=1\nftpProxy=ftp.foo.com\n") +
long_line + "httpsProxy=www.foo.com\nhttpProxy=www.google.com\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"", // https
"ftp.foo.com:80", // ftp
""), // bypass rules
},
{
TEST_DESC("Indirect Proxy - no env vars set"),
// Input.
"[Proxy Settings]\nProxyType=4\nhttpProxy=http_proxy\n"
"httpsProxy=https_proxy\nftpProxy=ftp_proxy\nNoProxyFor=no_proxy\n",
{}, // env_values
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::Empty(),
},
{
TEST_DESC("Indirect Proxy - with env vars set"),
// Input.
"[Proxy Settings]\nProxyType=4\nhttpProxy=http_proxy\n"
"httpsProxy=https_proxy\nftpProxy=ftp_proxy\nNoProxyFor=no_proxy\n",
{
// env_values
nullptr, // DESKTOP_SESSION
nullptr, // HOME
nullptr, // KDEHOME
nullptr, // KDE_SESSION_VERSION
nullptr, // XDG_CURRENT_DESKTOP
nullptr, // auto_proxy
nullptr, // all_proxy
"www.normal.com", // http_proxy
"www.secure.com", // https_proxy
"ftp.foo.com", // ftp_proxy
nullptr, nullptr, // SOCKS
".google.com, .kde.org", // no_proxy
},
// Expected result.
ProxyConfigService::CONFIG_VALID,
false, // auto_detect
GURL(), // pac_url
ProxyRulesExpectation::PerScheme(
"www.normal.com:80", // http
"www.secure.com:80", // https
"ftp.foo.com:80", // ftp
"*.google.com,*.kde.org"), // bypass rules
},
};
for (size_t i = 0; i < arraysize(tests); ++i) {
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "] %s", i,
tests[i].description.c_str()));
std::unique_ptr<MockEnvironment> env(new MockEnvironment);
env->values = tests[i].env_values;
// Force the KDE getter to be used and tell it where the test is.
env->values.DESKTOP_SESSION = "kde4";
env->values.KDEHOME = kde_home_.value().c_str();
SyncConfigGetter sync_config_getter(new ProxyConfigServiceLinux(
std::move(env), TRAFFIC_ANNOTATION_FOR_TESTS));
ProxyConfigWithAnnotation config;
// Overwrite the kioslaverc file.
base::WriteFile(kioslaverc_, tests[i].kioslaverc.c_str(),
tests[i].kioslaverc.length());
sync_config_getter.SetupAndInitialFetch();
ProxyConfigService::ConfigAvailability availability =
sync_config_getter.SyncGetLatestProxyConfig(&config);
EXPECT_EQ(tests[i].availability, availability);
if (availability == ProxyConfigService::CONFIG_VALID) {
EXPECT_EQ(tests[i].auto_detect, config.value().auto_detect());
EXPECT_EQ(tests[i].pac_url, config.value().pac_url());
EXPECT_TRUE(tests[i].proxy_rules.Matches(config.value().proxy_rules()));
}
}
}
TEST_F(ProxyConfigServiceLinuxTest, KDEHomePicker) {
// Auto detect proxy settings.
std::string slaverc3 = "[Proxy Settings]\nProxyType=3\n";
// Valid PAC URL.
std::string slaverc4 =
"[Proxy Settings]\nProxyType=2\n"
"Proxy Config Script=http://wpad/wpad.dat\n";
GURL slaverc4_pac_url("http://wpad/wpad.dat");
// Basic HTTP proxy setting.
std::string slaverc5 =
"[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com 80\n";
ProxyRulesExpectation slaverc5_rules =
ProxyRulesExpectation::PerScheme("www.google.com:80", // http
"", // https
"", // ftp
""); // bypass rules
// Overwrite the .kde kioslaverc file.
base::WriteFile(kioslaverc_, slaverc3.c_str(), slaverc3.length());
// If .kde4 exists it will mess up the first test. It should not, as
// we created the directory for $HOME in the test setup.
CHECK(!base::DirectoryExists(kde4_home_));
{
SCOPED_TRACE("KDE4, no .kde4 directory, verify fallback");
std::unique_ptr<MockEnvironment> env(new MockEnvironment);
env->values.DESKTOP_SESSION = "kde4";
env->values.HOME = user_home_.value().c_str();
SyncConfigGetter sync_config_getter(new ProxyConfigServiceLinux(
std::move(env), TRAFFIC_ANNOTATION_FOR_TESTS));
ProxyConfigWithAnnotation config;
sync_config_getter.SetupAndInitialFetch();
EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
sync_config_getter.SyncGetLatestProxyConfig(&config));
EXPECT_TRUE(config.value().auto_detect());
EXPECT_EQ(GURL(), config.value().pac_url());
}
// Now create .kde4 and put a kioslaverc in the config directory.
// Note that its timestamp will be at least as new as the .kde one.
base::CreateDirectory(kde4_config_);
base::WriteFile(kioslaverc4_, slaverc4.c_str(), slaverc4.length());
CHECK(base::PathExists(kioslaverc4_));
{
SCOPED_TRACE("KDE4, .kde4 directory present, use it");
std::unique_ptr<MockEnvironment> env(new MockEnvironment);
env->values.DESKTOP_SESSION = "kde4";
env->values.HOME = user_home_.value().c_str();
SyncConfigGetter sync_config_getter(new ProxyConfigServiceLinux(
std::move(env), TRAFFIC_ANNOTATION_FOR_TESTS));
ProxyConfigWithAnnotation config;
sync_config_getter.SetupAndInitialFetch();
EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
sync_config_getter.SyncGetLatestProxyConfig(&config));
EXPECT_FALSE(config.value().auto_detect());
EXPECT_EQ(slaverc4_pac_url, config.value().pac_url());
}
{
SCOPED_TRACE("KDE3, .kde4 directory present, ignore it");
std::unique_ptr<MockEnvironment> env(new MockEnvironment);
env->values.DESKTOP_SESSION = "kde";
env->values.HOME = user_home_.value().c_str();
SyncConfigGetter sync_config_getter(new ProxyConfigServiceLinux(
std::move(env), TRAFFIC_ANNOTATION_FOR_TESTS));
ProxyConfigWithAnnotation config;
sync_config_getter.SetupAndInitialFetch();
EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
sync_config_getter.SyncGetLatestProxyConfig(&config));
EXPECT_TRUE(config.value().auto_detect());
EXPECT_EQ(GURL(), config.value().pac_url());
}
{
SCOPED_TRACE("KDE4, .kde4 directory present, KDEHOME set to .kde");
std::unique_ptr<MockEnvironment> env(new MockEnvironment);
env->values.DESKTOP_SESSION = "kde4";
env->values.HOME = user_home_.value().c_str();
env->values.KDEHOME = kde_home_.value().c_str();
SyncConfigGetter sync_config_getter(new ProxyConfigServiceLinux(
std::move(env), TRAFFIC_ANNOTATION_FOR_TESTS));
ProxyConfigWithAnnotation config;
sync_config_getter.SetupAndInitialFetch();
EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
sync_config_getter.SyncGetLatestProxyConfig(&config));
EXPECT_TRUE(config.value().auto_detect());
EXPECT_EQ(GURL(), config.value().pac_url());
}
// Finally, make the .kde4 config directory older than the .kde directory
// and make sure we then use .kde instead of .kde4 since it's newer.
base::TouchFile(kde4_config_, base::Time(), base::Time());
{
SCOPED_TRACE("KDE4, very old .kde4 directory present, use .kde");
std::unique_ptr<MockEnvironment> env(new MockEnvironment);
env->values.DESKTOP_SESSION = "kde4";
env->values.HOME = user_home_.value().c_str();
SyncConfigGetter sync_config_getter(new ProxyConfigServiceLinux(
std::move(env), TRAFFIC_ANNOTATION_FOR_TESTS));
ProxyConfigWithAnnotation config;
sync_config_getter.SetupAndInitialFetch();
EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
sync_config_getter.SyncGetLatestProxyConfig(&config));
EXPECT_TRUE(config.value().auto_detect());
EXPECT_EQ(GURL(), config.value().pac_url());
}
// For KDE 5 create ${HOME}/.config and put a kioslaverc in the directory.
base::CreateDirectory(config_home_);
base::WriteFile(kioslaverc5_, slaverc5.c_str(), slaverc5.length());
CHECK(base::PathExists(kioslaverc5_));
{
SCOPED_TRACE("KDE5, .kde and .kde4 present, use .config");
std::unique_ptr<MockEnvironment> env(new MockEnvironment);
env->values.XDG_CURRENT_DESKTOP = "KDE";
env->values.KDE_SESSION_VERSION = "5";
env->values.HOME = user_home_.value().c_str();
SyncConfigGetter sync_config_getter(new ProxyConfigServiceLinux(
std::move(env), TRAFFIC_ANNOTATION_FOR_TESTS));
ProxyConfigWithAnnotation config;
sync_config_getter.SetupAndInitialFetch();
EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
sync_config_getter.SyncGetLatestProxyConfig(&config));
EXPECT_FALSE(config.value().auto_detect());
EXPECT_TRUE(slaverc5_rules.Matches(config.value().proxy_rules()));
}
}
void WriteFile(const base::FilePath& path, base::StringPiece data) {
EXPECT_TRUE(base::WriteFile(path, data.data(), data.size()));
}
// Tests that the KDE proxy config service watches for file and directory
// changes.
TEST_F(ProxyConfigServiceLinuxTest, KDEFileChanged) {
// Set up the initial .kde kioslaverc file.
WriteFile(kioslaverc_,
"[Proxy Settings]\nProxyType=2\n"
"Proxy Config Script=http://version1/wpad.dat\n");
// Initialize the config service using kioslaverc.
std::unique_ptr<MockEnvironment> env(new MockEnvironment);
env->values.DESKTOP_SESSION = "kde4";
env->values.HOME = user_home_.value().c_str();
SyncConfigGetter sync_config_getter(new ProxyConfigServiceLinux(
std::move(env), TRAFFIC_ANNOTATION_FOR_TESTS));
ProxyConfigWithAnnotation config;
sync_config_getter.SetupAndInitialFetch();
EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
sync_config_getter.SyncGetLatestProxyConfig(&config));
EXPECT_TRUE(config.value().has_pac_url());
EXPECT_EQ(GURL("http://version1/wpad.dat"), config.value().pac_url());
//-----------------------------------------------------
// Change the kioslaverc file by overwriting it. Verify that the change was
// observed.
sync_config_getter.SetExpectedPacUrl("http://version2/wpad.dat");
WriteFile(kioslaverc_,
"[Proxy Settings]\nProxyType=2\n"
"Proxy Config Script=http://version2/wpad.dat\n");
// Wait for change to be noticed.
sync_config_getter.WaitUntilPacUrlMatchesExpectation();
//-----------------------------------------------------
// Change the kioslaverc file by renaming it. If only the file's inode
// were being watched (rather than directory) this will not result in
// an observable change. Note that KDE when re-writing proxy settings does
// so by renaming a new file, so the inode will change.
sync_config_getter.SetExpectedPacUrl("http://version3/wpad.dat");
// Create a new file, and rename it into place.
WriteFile(kioslaverc_.AddExtension("new"),
"[Proxy Settings]\nProxyType=2\n"
"Proxy Config Script=http://version3/wpad.dat\n");
base::Move(kioslaverc_, kioslaverc_.AddExtension("old"));
base::Move(kioslaverc_.AddExtension("new"), kioslaverc_);
// Wait for change to be noticed.
sync_config_getter.WaitUntilPacUrlMatchesExpectation();
//-----------------------------------------------------
// Change the kioslaverc file once more by ovewriting it. This is really
// just another test to make sure things still work after the directory
// change was observed (this final test probably isn't very useful).
sync_config_getter.SetExpectedPacUrl("http://version4/wpad.dat");
WriteFile(kioslaverc_,
"[Proxy Settings]\nProxyType=2\n"
"Proxy Config Script=http://version4/wpad.dat\n");
// Wait for change to be noticed.
sync_config_getter.WaitUntilPacUrlMatchesExpectation();
//-----------------------------------------------------
// TODO(eroman): Add a test where kioslaverc is deleted next. Currently this
// doesn't trigger any notifications, but it probably should.
}
} // namespace
} // namespace net