blob: b7ac05d60c3fdd0da67fabfb0bdfde2af394643c [file] [log] [blame]
// Copyright 2018 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 <tuple>
#include "base/bind.h"
#include "base/metrics/field_trial_param_associator.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/bind_test_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
#include "chrome/browser/metrics/subprocess_metrics_provider.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_test_utils.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
#include "components/data_reduction_proxy/core/common/uma_util.h"
#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
#include "components/prefs/pref_service.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_features.h"
#include "components/proxy_config/proxy_config_dictionary.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/network_service_util.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/test_navigation_observer.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/host_port_pair.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_request_headers.h"
#include "net/test/embedded_test_server/controllable_http_response.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/embedded_test_server/embedded_test_server_connection_listener.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/network_switches.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/network_service_test.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace data_reduction_proxy {
namespace {
using testing::HasSubstr;
using testing::Not;
constexpr char kSessionKey[] = "TheSessionKeyYay!";
constexpr char kMockHost[] = "mock.host";
constexpr char kDummyBody[] = "dummy";
constexpr char kPrimaryProxyResponse[] = "primary";
std::unique_ptr<net::test_server::HttpResponse> BasicResponse(
const std::string& content,
const net::test_server::HttpRequest& request) {
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_content(content);
response->set_content_type("text/plain");
return response;
}
void SimulateNetworkChange(network::mojom::ConnectionType type) {
if (!content::IsInProcessNetworkService()) {
mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
content::GetNetworkService()->BindTestInterface(
network_service_test.BindNewPipeAndPassReceiver());
base::RunLoop run_loop;
network_service_test->SimulateNetworkChange(type, run_loop.QuitClosure());
run_loop.Run();
return;
}
net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(
net::NetworkChangeNotifier::ConnectionType(type));
}
ClientConfig CreateConfigForServer(const net::EmbeddedTestServer& server) {
net::HostPortPair host_port_pair = server.host_port_pair();
return CreateClientConfig(kSessionKey, 1000, 0);
}
ClientConfig CreateEmptyConfig() {
return CreateClientConfig(kSessionKey, 1000, 0);
}
class TestSettingsObserver : public DataReductionProxySettingsObserver {
public:
TestSettingsObserver() = default;
~TestSettingsObserver() = default;
void OnPrefetchProxyHostsChanged(
const std::vector<GURL>& prefetch_proxies) override {
prefetch_proxies_ = prefetch_proxies;
}
const std::vector<GURL>& prefetch_proxies() const {
return prefetch_proxies_;
}
private:
std::vector<GURL> prefetch_proxies_;
};
} // namespace
#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
#define DISABLE_ON_WIN_MAC_CHROMEOS(x) DISABLED_##x
#else
#define DISABLE_ON_WIN_MAC_CHROMEOS(x) x
#endif
class DataReductionProxyBrowsertestBase : public InProcessBrowserTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(
network::switches::kForceEffectiveConnectionType, "4G");
config_server_.RegisterRequestHandler(base::BindRepeating(
&DataReductionProxyBrowsertestBase::GetConfigResponse,
base::Unretained(this)));
ASSERT_TRUE(config_server_.Start());
command_line->AppendSwitchASCII(switches::kDataReductionProxyConfigURL,
config_server_.base_url().spec());
}
void SetUp() override { InProcessBrowserTest::SetUp(); }
void SetUpOnMainThread() override {
// Make sure the favicon doesn't mess with the tests.
favicon_catcher_ =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/favicon.ico");
ASSERT_TRUE(embedded_test_server()->Start());
// Set a default proxy config if one isn't set yet.
if (!config_.has_proxy_config())
SetConfig(CreateConfigForServer(*embedded_test_server()));
host_resolver()->AddRule(kMockHost, "127.0.0.1");
EnableDataSaver(true);
WaitForConfig();
}
protected:
void EnableDataSaver(bool enabled) {
data_reduction_proxy::DataReductionProxySettings::
SetDataSaverEnabledForTesting(browser()->profile()->GetPrefs(),
enabled);
}
std::string GetBody() { return GetBody(browser()); }
std::string GetBody(Browser* browser) {
std::string body;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
browser->tab_strip_model()->GetActiveWebContents(),
"window.domAutomationController.send(document.body.textContent);",
&body));
return body;
}
GURL GetURLWithMockHost(const net::EmbeddedTestServer& server,
const std::string& relative_url) {
GURL server_base_url = server.base_url();
GURL base_url =
GURL(base::StrCat({server_base_url.scheme(), "://", kMockHost, ":",
server_base_url.port()}));
EXPECT_TRUE(base_url.is_valid()) << base_url.possibly_invalid_spec();
return base_url.Resolve(relative_url);
}
void SetConfig(const ClientConfig& config) {
config_ = config;
// Config is not fetched if the lite page
// redirect previews are not enabled. So, return early.
if (!params::ForceEnableClientConfigServiceForAllDataSaverUsers()) {
return;
}
}
void RetryForHistogramUntilCountReached(
base::HistogramTester* histogram_tester,
const std::string& histogram_name,
size_t count) {
while (true) {
base::ThreadPoolInstance::Get()->FlushForTesting();
base::RunLoop().RunUntilIdle();
content::FetchHistogramsFromChildProcesses();
SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
const std::vector<base::Bucket> buckets =
histogram_tester->GetAllSamples(histogram_name);
size_t total_count = 0;
for (const auto& bucket : buckets) {
total_count += bucket.count;
}
if (total_count >= count) {
break;
}
}
}
void WaitForConfig() {
if (!params::ForceEnableClientConfigServiceForAllDataSaverUsers()) {
return;
}
base::HistogramTester histogram_tester;
RetryForHistogramUntilCountReached(
&histogram_tester, "DataReductionProxy.Settings.ConfigReceived", 1);
}
std::string expect_exp_value_in_request_header_;
size_t count_proxy_server_requests_received_ = 0u;
base::test::ScopedFeatureList scoped_feature_list_;
private:
// Called when |config_server_| receives a request for config fetch.
std::unique_ptr<net::test_server::HttpResponse> GetConfigResponse(
const net::test_server::HttpRequest& request) {
// Config should be fetched only when lite page
// redirect previews are enabled.
EXPECT_TRUE(params::ForceEnableClientConfigServiceForAllDataSaverUsers());
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_content(config_.SerializeAsString());
response->set_content_type("text/plain");
return response;
}
ClientConfig config_;
net::EmbeddedTestServer config_server_;
std::unique_ptr<net::test_server::ControllableHttpResponse> favicon_catcher_;
};
class DataReductionProxyBrowsertest : public DataReductionProxyBrowsertestBase {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
DataReductionProxyBrowsertestBase::SetUpCommandLine(command_line);
}
};
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, UpdateConfig) {
net::EmbeddedTestServer original_server;
original_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kDummyBody));
ASSERT_TRUE(original_server.Start());
SetConfig(CreateConfigForServer(original_server));
// A network change forces the config to be fetched.
SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G);
WaitForConfig();
ui_test_utils::NavigateToURL(
browser(),
GetURLWithMockHost(original_server, "/echoheader?Chrome-Proxy"));
EXPECT_EQ(GetBody(), kDummyBody);
}
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest,
UpdatePrefetchProxyConfig) {
TestSettingsObserver observer;
DataReductionProxySettings* settings =
DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
browser()->profile());
settings->AddDataReductionProxySettingsObserver(&observer);
net::EmbeddedTestServer original_server;
original_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kDummyBody));
ASSERT_TRUE(original_server.Start());
ClientConfig config = CreateConfigForServer(original_server);
PrefetchProxyConfig_Proxy* valid_secure_proxy =
config.mutable_prefetch_proxy_config()->add_proxy_list();
valid_secure_proxy->set_type(PrefetchProxyConfig_Proxy_Type_CONNECT);
valid_secure_proxy->set_host("prefetch-proxy.com");
valid_secure_proxy->set_port(443);
valid_secure_proxy->set_scheme(PrefetchProxyConfig_Proxy_Scheme_HTTPS);
PrefetchProxyConfig_Proxy* non_connect_proxy =
config.mutable_prefetch_proxy_config()->add_proxy_list();
non_connect_proxy->set_type(PrefetchProxyConfig_Proxy_Type_UNSPECIFIED_TYPE);
non_connect_proxy->set_host("prefetch-proxy.com");
non_connect_proxy->set_port(443);
non_connect_proxy->set_scheme(PrefetchProxyConfig_Proxy_Scheme_HTTPS);
PrefetchProxyConfig_Proxy* unknown_scheme_proxy =
config.mutable_prefetch_proxy_config()->add_proxy_list();
unknown_scheme_proxy->set_type(PrefetchProxyConfig_Proxy_Type_CONNECT);
unknown_scheme_proxy->set_host("prefetch-proxy.com");
unknown_scheme_proxy->set_port(443);
unknown_scheme_proxy->set_scheme(
PrefetchProxyConfig_Proxy_Scheme_UNSPECIFIED_SCHEME);
PrefetchProxyConfig_Proxy* invalid_host =
config.mutable_prefetch_proxy_config()->add_proxy_list();
invalid_host->set_type(PrefetchProxyConfig_Proxy_Type_CONNECT);
invalid_host->set_host(std::string());
invalid_host->set_port(443);
invalid_host->set_scheme(PrefetchProxyConfig_Proxy_Scheme_HTTPS);
PrefetchProxyConfig_Proxy* insecure_proxy =
config.mutable_prefetch_proxy_config()->add_proxy_list();
insecure_proxy->set_type(PrefetchProxyConfig_Proxy_Type_CONNECT);
insecure_proxy->set_host("insecure-prefetch-proxy.com");
insecure_proxy->set_port(80);
insecure_proxy->set_scheme(PrefetchProxyConfig_Proxy_Scheme_HTTP);
SetConfig(config);
// A network change forces the config to be fetched.
SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G);
WaitForConfig();
std::vector<GURL> want_hosts = {
GURL("https://prefetch-proxy.com:443/"),
};
EXPECT_EQ(want_hosts, settings->GetPrefetchProxies());
EXPECT_EQ(want_hosts, observer.prefetch_proxies());
}
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest,
UpdatePrefetchProxyConfig_Insecure) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
"allow-insecure-prefetch-proxy-for-testing");
TestSettingsObserver observer;
DataReductionProxySettings* settings =
DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
browser()->profile());
settings->AddDataReductionProxySettingsObserver(&observer);
net::EmbeddedTestServer original_server;
original_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kPrimaryProxyResponse));
ASSERT_TRUE(original_server.Start());
ClientConfig config = CreateConfigForServer(original_server);
PrefetchProxyConfig_Proxy* valid_secure_proxy =
config.mutable_prefetch_proxy_config()->add_proxy_list();
valid_secure_proxy->set_type(PrefetchProxyConfig_Proxy_Type_CONNECT);
valid_secure_proxy->set_host("prefetch-proxy.com");
valid_secure_proxy->set_port(443);
valid_secure_proxy->set_scheme(PrefetchProxyConfig_Proxy_Scheme_HTTPS);
PrefetchProxyConfig_Proxy* valid_insecure_proxy =
config.mutable_prefetch_proxy_config()->add_proxy_list();
valid_insecure_proxy->set_type(PrefetchProxyConfig_Proxy_Type_CONNECT);
valid_insecure_proxy->set_host("insecure-prefetch-proxy.com");
valid_insecure_proxy->set_port(80);
valid_insecure_proxy->set_scheme(PrefetchProxyConfig_Proxy_Scheme_HTTP);
SetConfig(config);
// A network change forces the config to be fetched.
SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G);
WaitForConfig();
std::vector<GURL> want_hosts = {
GURL("https://prefetch-proxy.com:443/"),
GURL("http://insecure-prefetch-proxy.com/"),
};
EXPECT_EQ(want_hosts, settings->GetPrefetchProxies());
EXPECT_EQ(want_hosts, observer.prefetch_proxies());
}
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest,
UpdatePrefetchProxyConfig_CmdLineOverride_Valid) {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
"prefetch-proxy-override-proxy-hosts",
" ,NotValid, https://override-proxy.com/ ");
TestSettingsObserver observer;
DataReductionProxySettings* settings =
DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
browser()->profile());
settings->AddDataReductionProxySettingsObserver(&observer);
net::EmbeddedTestServer original_server;
original_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kPrimaryProxyResponse));
ASSERT_TRUE(original_server.Start());
ClientConfig config = CreateConfigForServer(original_server);
PrefetchProxyConfig_Proxy* valid_secure_proxy =
config.mutable_prefetch_proxy_config()->add_proxy_list();
valid_secure_proxy->set_type(PrefetchProxyConfig_Proxy_Type_CONNECT);
valid_secure_proxy->set_host("prefetch-proxy.com");
valid_secure_proxy->set_port(443);
valid_secure_proxy->set_scheme(PrefetchProxyConfig_Proxy_Scheme_HTTPS);
PrefetchProxyConfig_Proxy* valid_insecure_proxy =
config.mutable_prefetch_proxy_config()->add_proxy_list();
valid_insecure_proxy->set_type(PrefetchProxyConfig_Proxy_Type_CONNECT);
valid_insecure_proxy->set_host("insecure-prefetch-proxy.com");
valid_insecure_proxy->set_port(80);
valid_insecure_proxy->set_scheme(PrefetchProxyConfig_Proxy_Scheme_HTTP);
SetConfig(config);
// A network change forces the config to be fetched.
SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G);
WaitForConfig();
std::vector<GURL> want_hosts = {
GURL("https://override-proxy.com/"),
};
EXPECT_EQ(want_hosts, settings->GetPrefetchProxies());
EXPECT_EQ(want_hosts, observer.prefetch_proxies());
}
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest,
UpdatePrefetchProxyConfig_CmdLineOverride_Invalid) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
"allow-insecure-prefetch-proxy-for-testing");
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
"prefetch-proxy-override-proxy-hosts", " ,NotValid, also-not-valid ");
TestSettingsObserver observer;
DataReductionProxySettings* settings =
DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
browser()->profile());
settings->AddDataReductionProxySettingsObserver(&observer);
net::EmbeddedTestServer original_server;
original_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kPrimaryProxyResponse));
ASSERT_TRUE(original_server.Start());
ClientConfig config = CreateConfigForServer(original_server);
PrefetchProxyConfig_Proxy* valid_secure_proxy =
config.mutable_prefetch_proxy_config()->add_proxy_list();
valid_secure_proxy->set_type(PrefetchProxyConfig_Proxy_Type_CONNECT);
valid_secure_proxy->set_host("prefetch-proxy.com");
valid_secure_proxy->set_port(443);
valid_secure_proxy->set_scheme(PrefetchProxyConfig_Proxy_Scheme_HTTPS);
PrefetchProxyConfig_Proxy* valid_insecure_proxy =
config.mutable_prefetch_proxy_config()->add_proxy_list();
valid_insecure_proxy->set_type(PrefetchProxyConfig_Proxy_Type_CONNECT);
valid_insecure_proxy->set_host("insecure-prefetch-proxy.com");
valid_insecure_proxy->set_port(80);
valid_insecure_proxy->set_scheme(PrefetchProxyConfig_Proxy_Scheme_HTTP);
SetConfig(config);
// A network change forces the config to be fetched.
SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G);
WaitForConfig();
std::vector<GURL> want_hosts = {
GURL("https://prefetch-proxy.com:443/"),
GURL("http://insecure-prefetch-proxy.com/"),
};
EXPECT_EQ(want_hosts, settings->GetPrefetchProxies());
EXPECT_EQ(want_hosts, observer.prefetch_proxies());
}
// Verify that when a client config with no proxies is provided to Chrome,
// then usage of proxy is disabled. Later, when the client config with valid
// proxies is fetched, then the specified proxies are used.
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, EmptyConfig) {
net::EmbeddedTestServer origin_server;
origin_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kDummyBody));
ASSERT_TRUE(origin_server.Start());
// Set config to empty, and verify that the response comes from the
// |origin_server|.
SetConfig(CreateEmptyConfig());
// A network change forces the config to be fetched.
SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_2G);
WaitForConfig();
ui_test_utils::NavigateToURL(
browser(), GetURLWithMockHost(origin_server, "/echoheader?Chrome-Proxy"));
ASSERT_EQ(GetBody(), kDummyBody);
net::EmbeddedTestServer proxy_server;
proxy_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kPrimaryProxyResponse));
ASSERT_TRUE(proxy_server.Start());
// Set config to |proxy_server|, and verify that the response comes from
// |proxy_server|.
SetConfig(CreateConfigForServer(proxy_server));
// A network change forces the config to be fetched.
SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G);
WaitForConfig();
ui_test_utils::NavigateToURL(
browser(), GetURLWithMockHost(origin_server, "/echoheader?Chrome-Proxy"));
EXPECT_EQ(GetBody(), kDummyBody);
// Set config to empty again, and verify that the response comes from the
// |origin_server|.
SetConfig(CreateEmptyConfig());
// A network change forces the config to be fetched.
SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_2G);
WaitForConfig();
ui_test_utils::NavigateToURL(
browser(), GetURLWithMockHost(origin_server, "/echoheader?Chrome-Proxy"));
ASSERT_EQ(GetBody(), kDummyBody);
}
// Gets the response body for an XHR to |url| (as seen by the renderer).
std::string ReadSubresourceFromRenderer(Browser* browser,
const GURL& url,
bool asynchronous_xhr = true) {
static const char asynchronous_script[] = R"((url => {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = () => domAutomationController.send(xhr.responseText);
xhr.send();
}))";
static const char synchronous_script[] = R"((url => {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.send();
domAutomationController.send(xhr.responseText);
}))";
std::string result;
EXPECT_TRUE(ExecuteScriptAndExtractString(
browser->tab_strip_model()->GetActiveWebContents(),
base::StrCat({asynchronous_xhr ? asynchronous_script : synchronous_script,
"('", url.spec(), "')"}),
&result));
return result;
}
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, DisabledOnIncognito) {
net::EmbeddedTestServer test_server;
test_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kDummyBody));
ASSERT_TRUE(test_server.Start());
Browser* incognito = CreateIncognitoBrowser();
ASSERT_FALSE(DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
incognito->profile()));
ASSERT_TRUE(DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
browser()->profile()));
ui_test_utils::NavigateToURL(
incognito, GetURLWithMockHost(test_server, "/echoheader?Chrome-Proxy"));
EXPECT_EQ(GetBody(incognito), kDummyBody);
// Make sure subresource doesn't use DRP either.
std::string result = ReadSubresourceFromRenderer(
incognito, GetURLWithMockHost(test_server, "/echoheader?Chrome-Proxy"));
EXPECT_EQ(result, kDummyBody);
}
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest,
ChromeProxyHeaderSetForSubresource) {
net::EmbeddedTestServer test_server;
test_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kDummyBody));
ASSERT_TRUE(test_server.Start());
ui_test_utils::NavigateToURL(browser(),
GetURLWithMockHost(test_server, "/echo"));
std::string result = ReadSubresourceFromRenderer(
browser(), GetURLWithMockHost(test_server, "/echoheader?Chrome-Proxy"));
EXPECT_EQ(result, kDummyBody);
}
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest,
ChromeProxyHeaderSetForSubresourceSync) {
net::EmbeddedTestServer test_server;
test_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kDummyBody));
ASSERT_TRUE(test_server.Start());
ui_test_utils::NavigateToURL(browser(),
GetURLWithMockHost(test_server, "/echo"));
const bool asynchronous_xhr = false;
std::string result = ReadSubresourceFromRenderer(
browser(), GetURLWithMockHost(test_server, "/echoheader?Chrome-Proxy"),
asynchronous_xhr);
EXPECT_EQ(result, kDummyBody);
}
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest,
ProxyNotUsedWhenDisabled) {
net::EmbeddedTestServer test_server;
test_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kDummyBody));
ASSERT_TRUE(test_server.Start());
ui_test_utils::NavigateToURL(
browser(), GetURLWithMockHost(test_server, "/echoheader?Chrome-Proxy"));
EXPECT_EQ(GetBody(), kDummyBody);
EnableDataSaver(false);
// |test_server| only has the BasicResponse handler, so should return the
// dummy response no matter what the URL if it is not being proxied.
ui_test_utils::NavigateToURL(
browser(), GetURLWithMockHost(test_server, "/echoheader?Chrome-Proxy"));
EXPECT_EQ(GetBody(), kDummyBody);
}
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest,
ProxyNotUsedForWebSocket) {
// Expect the WebSocket handshake to be attempted with |test_server|
// directly.
base::RunLoop web_socket_handshake_loop;
net::EmbeddedTestServer test_server;
test_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kDummyBody));
test_server.RegisterRequestMonitor(base::BindLambdaForTesting(
[&web_socket_handshake_loop](
const net::test_server::HttpRequest& request) {
if (request.headers.count("upgrade") > 0u)
web_socket_handshake_loop.Quit();
}));
ASSERT_TRUE(test_server.Start());
// If the DRP client (erroneously) decides to proxy the WebSocket handshake,
// it will attempt to establish a tunnel through |drp_server|.
net::EmbeddedTestServer drp_server;
drp_server.AddDefaultHandlers(GetChromeTestDataDir());
bool tunnel_attempted = false;
drp_server.RegisterRequestMonitor(base::BindLambdaForTesting(
[&tunnel_attempted, &web_socket_handshake_loop](
const net::test_server::HttpRequest& request) {
if (request.method == net::test_server::METHOD_CONNECT) {
tunnel_attempted = true;
web_socket_handshake_loop.Quit();
}
}));
ASSERT_TRUE(drp_server.Start());
SetConfig(CreateConfigForServer(drp_server));
// A network change forces the config to be fetched.
SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G);
WaitForConfig();
ui_test_utils::NavigateToURL(browser(),
GetURLWithMockHost(test_server, "/echo"));
const std::string url =
base::StrCat({"ws://", kMockHost, ":", test_server.base_url().port()});
const std::string script = R"((url => {
var ws = new WebSocket(url);
}))";
EXPECT_TRUE(
ExecuteScript(browser()->tab_strip_model()->GetActiveWebContents(),
script + "('" + url + "')"));
web_socket_handshake_loop.Run();
EXPECT_FALSE(tunnel_attempted);
}
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest,
DoesNotOverrideExistingProxyConfig) {
// When there's a proxy configuration provided to the browser already (system
// proxy, command line, etc.), the DRP proxy must not override it.
net::EmbeddedTestServer existing_proxy_server;
existing_proxy_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kDummyBody));
ASSERT_TRUE(existing_proxy_server.Start());
browser()->profile()->GetPrefs()->Set(
proxy_config::prefs::kProxy,
ProxyConfigDictionary::CreateFixedServers(
existing_proxy_server.host_port_pair().ToString(), ""));
EnableDataSaver(true);
// Proxy will be used, so it shouldn't matter if the host cannot be resolved.
ui_test_utils::NavigateToURL(browser(),
GURL("http://does.not.resolve.com/echo"));
EXPECT_EQ(GetBody(), kDummyBody);
}
// Test that enabling the holdback disables the proxy and that the client config
// is fetched when it is force enabled.
class DataReductionProxyWithHoldbackBrowsertest
: public ::testing::WithParamInterface<bool>,
public DataReductionProxyBrowsertest {
public:
DataReductionProxyWithHoldbackBrowsertest()
: enable_config_service_fetches_(GetParam()) {}
void SetUp() override {
fetch_client_config_feature_list_.InitWithFeatureState(
data_reduction_proxy::features::kFetchClientConfig,
enable_config_service_fetches_);
InProcessBrowserTest::SetUp();
}
const bool enable_config_service_fetches_;
private:
base::test::ScopedFeatureList fetch_client_config_feature_list_;
};
IN_PROC_BROWSER_TEST_P(DataReductionProxyWithHoldbackBrowsertest,
UpdateConfig) {
net::EmbeddedTestServer proxy_server;
proxy_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kPrimaryProxyResponse));
ASSERT_TRUE(proxy_server.Start());
SetConfig(CreateConfigForServer(proxy_server));
// A network change forces the config to be fetched.
SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G);
// Ensure that the client config is fetched when lite page redirect preview is
// enabled.
WaitForConfig();
// Load a webpage in holdback group as well. This ensures that while in
// holdback group, Chrome does not fetch the client config. If Chrome were to
// fetch the client config, the DCHECKs and other conditionals that check that
// holdback is not enabled would trigger and cause the test to fail.
ui_test_utils::NavigateToURL(browser(), GURL("http://does.not.resolve/foo"));
EXPECT_NE(GetBody(), kPrimaryProxyResponse);
}
// Parameter is true if data reduction proxy config should always be fetched.
INSTANTIATE_TEST_SUITE_P(All,
DataReductionProxyWithHoldbackBrowsertest,
::testing::Bool());
class DataReductionProxyExpBrowsertest : public DataReductionProxyBrowsertest {
public:
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
data_reduction_proxy::switches::kDataReductionProxyExperiment,
"foo_experiment");
DataReductionProxyBrowsertest::SetUp();
}
};
class DataReductionProxyExpFeatureBrowsertest
: public DataReductionProxyBrowsertest {
public:
void SetUp() override {
std::map<std::string, std::string> field_trial_params;
field_trial_params[data_reduction_proxy::params::
GetDataSaverServerExperimentsOptionName()] =
experiment_name;
scoped_feature_list_.InitWithFeaturesAndParameters(
{
{data_reduction_proxy::features::
kDataReductionProxyServerExperiments,
{field_trial_params}},
},
{});
InProcessBrowserTest::SetUp();
}
const std::string experiment_name = "foo_feature_experiment";
};
// Threadsafe log for recording a sequence of events as newline separated text.
class EventLog {
public:
void Add(const std::string& event) {
base::AutoLock lock(lock_);
log_ += event + "\n";
}
std::string GetAndReset() {
base::AutoLock lock(lock_);
return std::move(log_);
}
private:
base::Lock lock_;
std::string log_;
};
// Responds to requests with the path as response body, and logs the request
// into |event_log|.
std::unique_ptr<net::test_server::HttpResponse> RespondWithRequestPathHandler(
const std::string& server_name,
EventLog* event_log,
const net::test_server::HttpRequest& request) {
if (request.relative_url == "/favicon.ico")
return nullptr;
event_log->Add(server_name + " responded 200 for " + request.relative_url);
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_content_type("text/plain");
response->set_code(net::HTTP_OK);
response->set_content(request.relative_url);
return response;
}
// Verify that requests initiated by SimpleURLLoader use the proxy only if
// render frame ID is set.
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, SimpleURLLoader) {
net::EmbeddedTestServer origin_server;
origin_server.RegisterRequestHandler(
base::BindRepeating(&BasicResponse, kDummyBody));
ASSERT_TRUE(origin_server.Start());
net::EmbeddedTestServer proxy_server;
ASSERT_TRUE(proxy_server.Start());
// Set config to |proxy_server|.
SetConfig(CreateConfigForServer(proxy_server));
// A network change forces the config to be fetched.
SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G);
WaitForConfig();
for (const bool set_render_frame_id : {false, true}) {
auto resource_request = std::make_unique<network::ResourceRequest>();
if (set_render_frame_id)
resource_request->render_frame_id = MSG_ROUTING_CONTROL;
// Change the URL for different test cases because a bypassed URL may be
// added to the local cache by network service proxy delegate.
if (set_render_frame_id) {
resource_request->url = GetURLWithMockHost(
origin_server, "/echoheader?Chrome-Proxy&random=1");
} else {
resource_request->url = GetURLWithMockHost(
origin_server, "/echoheader?Chrome-Proxy&random=2");
}
auto simple_loader = network::SimpleURLLoader::Create(
std::move(resource_request), TRAFFIC_ANNOTATION_FOR_TESTS);
auto* storage_partition =
content::BrowserContext::GetDefaultStoragePartition(
browser()->profile());
auto url_loader_factory =
storage_partition->GetURLLoaderFactoryForBrowserProcess();
base::RunLoop loop;
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory.get(),
base::BindLambdaForTesting(
[&](std::unique_ptr<std::string> response_body) {
loop.Quit();
ASSERT_TRUE(response_body);
EXPECT_EQ(kDummyBody, *response_body);
}));
loop.Run();
}
}
// Ensure that renderer initiated same-site navigations work.
IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest,
RendererInitiatedSameSiteNavigation) {
// Perform a browser-initiated navigation.
net::EmbeddedTestServer origin_server;
origin_server.ServeFilesFromSourceDirectory(
base::FilePath(FILE_PATH_LITERAL("content/test/data")));
ASSERT_TRUE(origin_server.Start());
net::EmbeddedTestServer proxy_server;
proxy_server.ServeFilesFromSourceDirectory(
base::FilePath(FILE_PATH_LITERAL("content/test/data")));
ASSERT_TRUE(proxy_server.Start());
// Set config to |proxy_server|.
SetConfig(CreateConfigForServer(proxy_server));
// A network change forces the config to be fetched.
SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G);
WaitForConfig();
{
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
GURL url(GetURLWithMockHost(origin_server, "/simple_links.html"));
ui_test_utils::NavigateToURL(browser(), url);
EXPECT_EQ(url, observer.last_navigation_url());
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_FALSE(observer.last_initiator_origin().has_value());
}
// Simulate clicking on a same-site link.
{
content::TestNavigationObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
GURL url(GetURLWithMockHost(origin_server, "/title2.html"));
bool success = false;
EXPECT_TRUE(ExecuteScriptAndExtractBool(
browser()->tab_strip_model()->GetActiveWebContents(),
"window.domAutomationController.send(clickSameSiteLink());", &success));
EXPECT_TRUE(success);
EXPECT_TRUE(
WaitForLoadStop(browser()->tab_strip_model()->GetActiveWebContents()));
EXPECT_EQ(url, observer.last_navigation_url());
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(browser()
->tab_strip_model()
->GetActiveWebContents()
->GetMainFrame()
->GetLastCommittedOrigin(),
observer.last_initiator_origin());
}
}
} // namespace data_reduction_proxy