blob: 9ebaa95a87dbaa219d92a3bc0d9b123199c8a06c [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
#include <stddef.h>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/field_trial.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/time.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.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_configurator.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
#include "components/data_reduction_proxy/core/browser/network_properties_manager.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "net/http/http_network_session.h"
#include "net/proxy_resolution/proxy_info.h"
#include "net/proxy_resolution/proxy_resolution_service.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_interceptor.h"
#include "net/url_request/url_request_test_util.h"
#include "services/network/test/test_network_connection_tracker.h"
#include "services/network/test/test_network_quality_tracker.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace data_reduction_proxy {
namespace {
// Used only to verify that a wrapped network delegate gets called.
class CountingNetworkDelegate : public net::NetworkDelegateImpl {
public:
CountingNetworkDelegate() : created_requests_(0) {
}
~CountingNetworkDelegate() final {
}
int OnBeforeURLRequest(net::URLRequest* request,
net::CompletionOnceCallback callback,
GURL* new_url) final {
created_requests_++;
return net::OK;
}
int created_requests() const {
return created_requests_;
}
private:
int created_requests_;
};
std::string CreateEncodedConfig(
const std::vector<DataReductionProxyServer> proxy_servers) {
ClientConfig config;
config.set_session_key("session");
for (const auto& proxy_server : proxy_servers) {
ProxyServer* config_proxy =
config.mutable_proxy_config()->add_http_proxy_servers();
net::HostPortPair host_port_pair =
proxy_server.proxy_server().host_port_pair();
config_proxy->set_scheme(ProxyServer_ProxyScheme_HTTP);
config_proxy->set_host(host_port_pair.host());
config_proxy->set_port(host_port_pair.port());
config_proxy->set_type(proxy_server.IsCoreProxy()
? ProxyServer_ProxyType_CORE
: ProxyServer_ProxyType_UNSPECIFIED_TYPE);
}
return EncodeConfig(config);
}
} // namespace
class DataReductionProxyIODataTest : public testing::Test {
public:
DataReductionProxyIODataTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::IO) {}
void SetUp() override {
RegisterSimpleProfilePrefs(prefs_.registry());
}
void RequestCallback(int err) {
}
net::TestDelegate* delegate() {
return &delegate_;
}
const net::TestURLRequestContext& context() const {
return context_;
}
PrefService* prefs() {
return &prefs_;
}
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
private:
net::TestDelegate delegate_;
net::TestURLRequestContext context_;
TestingPrefServiceSimple prefs_;
};
TEST_F(DataReductionProxyIODataTest, TestConstruction) {
std::unique_ptr<DataReductionProxyIOData> io_data(
new DataReductionProxyIOData(
Client::UNKNOWN, prefs(),
network::TestNetworkConnectionTracker::GetInstance(),
scoped_task_environment_.GetMainThreadTaskRunner(),
scoped_task_environment_.GetMainThreadTaskRunner(),
false /* enabled */, std::string() /* user_agent */,
std::string() /* channel */));
// Check that io_data creates an interceptor. Such an interceptor is
// thoroughly tested by DataReductionProxyInterceptoTest.
std::unique_ptr<net::URLRequestInterceptor> interceptor =
io_data->CreateInterceptor();
EXPECT_NE(nullptr, interceptor.get());
// When creating a network delegate, expect that it properly wraps a
// network delegate. Such a network delegate is thoroughly tested by
// DataReductionProxyNetworkDelegateTest.
std::unique_ptr<net::URLRequest> fake_request =
context().CreateRequest(GURL("http://www.foo.com/"), net::IDLE,
delegate(), TRAFFIC_ANNOTATION_FOR_TESTS);
CountingNetworkDelegate* wrapped_network_delegate =
new CountingNetworkDelegate();
std::unique_ptr<DataReductionProxyNetworkDelegate> network_delegate =
io_data->CreateNetworkDelegate(base::WrapUnique(wrapped_network_delegate),
false);
network_delegate->NotifyBeforeURLRequest(
fake_request.get(),
base::BindOnce(&DataReductionProxyIODataTest::RequestCallback,
base::Unretained(this)),
nullptr);
EXPECT_EQ(1, wrapped_network_delegate->created_requests());
EXPECT_NE(nullptr, io_data->bypass_stats());
// Creating a second delegate with bypass statistics tracking should result
// in usage stats being created.
io_data->CreateNetworkDelegate(std::make_unique<CountingNetworkDelegate>(),
true);
EXPECT_NE(nullptr, io_data->bypass_stats());
io_data->ShutdownOnUIThread();
}
TEST_F(DataReductionProxyIODataTest, TestResetBadProxyListOnDisableDataSaver) {
net::TestURLRequestContext context(false);
std::unique_ptr<DataReductionProxyTestContext> drp_test_context =
DataReductionProxyTestContext::Builder()
.WithURLRequestContext(&context)
.SkipSettingsInitialization()
.Build();
drp_test_context->SetDataReductionProxyEnabled(true);
drp_test_context->InitSettings();
DataReductionProxyIOData* io_data = drp_test_context->io_data();
std::vector<net::ProxyServer> proxies;
proxies.push_back(net::ProxyServer::FromURI("http://foo1.com",
net::ProxyServer::SCHEME_HTTP));
net::ProxyResolutionService* proxy_resolution_service =
io_data->url_request_context_getter_->GetURLRequestContext()
->proxy_resolution_service();
net::ProxyInfo proxy_info;
proxy_info.UseNamedProxy("http://foo2.com");
const net::ProxyRetryInfoMap& bad_proxy_list =
proxy_resolution_service->proxy_retry_info();
// Simulate network error to add proxies to the bad proxy list.
proxy_resolution_service->MarkProxiesAsBadUntil(
proxy_info, base::TimeDelta::FromDays(1), proxies,
net::NetLogWithSource());
base::RunLoop().RunUntilIdle();
// Verify that there are 2 proxies in the bad proxies list.
EXPECT_EQ(2UL, bad_proxy_list.size());
// Turn Data Saver off.
drp_test_context->settings()->SetDataReductionProxyEnabled(false);
base::RunLoop().RunUntilIdle();
// Verify that bad proxy list is empty.
EXPECT_EQ(0UL, bad_proxy_list.size());
}
TEST_F(DataReductionProxyIODataTest, HoldbackConfiguresProxies) {
net::TestURLRequestContext context(false);
base::FieldTrialList field_trial_list(nullptr);
ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
"DataCompressionProxyHoldback", "Enabled"));
std::unique_ptr<DataReductionProxyTestContext> drp_test_context =
DataReductionProxyTestContext::Builder()
.WithURLRequestContext(&context)
.SkipSettingsInitialization()
.Build();
EXPECT_TRUE(drp_test_context->test_params()->proxies_for_http().size() > 0);
EXPECT_FALSE(drp_test_context->test_params()
->proxies_for_http()
.front()
.proxy_server()
.is_direct());
}
class TestCustomProxyConfigClient
: public network::mojom::CustomProxyConfigClient {
public:
TestCustomProxyConfigClient(
network::mojom::CustomProxyConfigClientRequest request)
: binding_(this, std::move(request)) {}
// network::mojom::CustomProxyConfigClient implementation:
void OnCustomProxyConfigUpdated(
network::mojom::CustomProxyConfigPtr proxy_config) override {
config = std::move(proxy_config);
}
void MarkProxiesAsBad(base::TimeDelta bypass_duration,
const net::ProxyList& bad_proxies,
MarkProxiesAsBadCallback callback) override {}
void ClearBadProxiesCache() override {}
network::mojom::CustomProxyConfigPtr config;
private:
mojo::Binding<network::mojom::CustomProxyConfigClient> binding_;
};
TEST_F(DataReductionProxyIODataTest, TestCustomProxyConfigClient) {
auto proxy_server = net::ProxyServer::FromPacString("PROXY foo");
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kDataReductionProxyHttpProxies, proxy_server.ToURI());
net::TestURLRequestContext context(false);
std::unique_ptr<DataReductionProxyTestContext> drp_test_context =
DataReductionProxyTestContext::Builder()
.WithURLRequestContext(&context)
.Build();
drp_test_context->SetDataReductionProxyEnabled(true);
drp_test_context->test_network_quality_tracker()
->ReportEffectiveConnectionTypeForTesting(
net::EFFECTIVE_CONNECTION_TYPE_4G);
DataReductionProxyIOData* io_data = drp_test_context->io_data();
network::mojom::CustomProxyConfigClientPtrInfo client_ptr_info;
TestCustomProxyConfigClient client(mojo::MakeRequest(&client_ptr_info));
io_data->SetCustomProxyConfigClient(std::move(client_ptr_info));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(client.config->rules.proxies_for_http.Get(), proxy_server);
EXPECT_TRUE(
client.config->post_cache_headers.HasHeader(chrome_proxy_header()));
EXPECT_TRUE(
client.config->pre_cache_headers.HasHeader(chrome_proxy_ect_header()));
// Alternate proxy list should be empty because there are no core proxies.
EXPECT_TRUE(client.config->alternate_rules.empty());
}
TEST_F(DataReductionProxyIODataTest, TestCustomProxyConfigUpdatedOnECTChange) {
net::TestURLRequestContext context(false);
std::unique_ptr<DataReductionProxyTestContext> drp_test_context =
DataReductionProxyTestContext::Builder()
.WithURLRequestContext(&context)
.Build();
drp_test_context->SetDataReductionProxyEnabled(true);
drp_test_context->test_network_quality_tracker()
->ReportEffectiveConnectionTypeForTesting(
net::EFFECTIVE_CONNECTION_TYPE_4G);
DataReductionProxyIOData* io_data = drp_test_context->io_data();
network::mojom::CustomProxyConfigClientPtrInfo client_ptr_info;
TestCustomProxyConfigClient client(mojo::MakeRequest(&client_ptr_info));
io_data->SetCustomProxyConfigClient(std::move(client_ptr_info));
base::RunLoop().RunUntilIdle();
std::string value;
EXPECT_TRUE(client.config->pre_cache_headers.GetHeader(
chrome_proxy_ect_header(), &value));
EXPECT_EQ(value, "4G");
drp_test_context->test_network_quality_tracker()
->ReportEffectiveConnectionTypeForTesting(
net::EFFECTIVE_CONNECTION_TYPE_2G);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(client.config->pre_cache_headers.GetHeader(
chrome_proxy_ect_header(), &value));
EXPECT_EQ(value, "2G");
}
TEST_F(DataReductionProxyIODataTest,
TestCustomProxyConfigUpdatedOnHeaderChange) {
DataReductionProxyIOData io_data(
Client::UNKNOWN, prefs(),
network::TestNetworkConnectionTracker::GetInstance(),
scoped_task_environment_.GetMainThreadTaskRunner(),
scoped_task_environment_.GetMainThreadTaskRunner(), false /* enabled */,
std::string() /* user_agent */, std::string() /* channel */);
network::mojom::CustomProxyConfigClientPtrInfo client_ptr_info;
TestCustomProxyConfigClient client(mojo::MakeRequest(&client_ptr_info));
io_data.SetCustomProxyConfigClient(std::move(client_ptr_info));
base::RunLoop().RunUntilIdle();
std::string value;
EXPECT_TRUE(client.config->post_cache_headers.GetHeader(chrome_proxy_header(),
&value));
io_data.request_options()->SetSecureSession("session_value");
base::RunLoop().RunUntilIdle();
std::string changed_value;
EXPECT_TRUE(client.config->post_cache_headers.GetHeader(chrome_proxy_header(),
&changed_value));
EXPECT_NE(value, changed_value);
}
TEST_F(DataReductionProxyIODataTest,
TestCustomProxyConfigUpdatedOnProxyChange) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableDataReductionProxyWarmupURLFetch);
DataReductionProxyIOData io_data(
Client::UNKNOWN, prefs(),
network::TestNetworkConnectionTracker::GetInstance(),
scoped_task_environment_.GetMainThreadTaskRunner(),
scoped_task_environment_.GetMainThreadTaskRunner(), false /* enabled */,
std::string() /* user_agent */, std::string() /* channel */);
NetworkPropertiesManager network_properties_manager(
base::DefaultClock::GetInstance(), prefs(),
scoped_task_environment_.GetMainThreadTaskRunner());
io_data.config()->SetNetworkPropertiesManagerForTesting(
&network_properties_manager);
io_data.config()->UpdateConfigForTesting(true, true, true);
auto proxy_server1 = net::ProxyServer::FromPacString("PROXY foo");
io_data.config_client()->ApplySerializedConfig(CreateEncodedConfig(
{DataReductionProxyServer(proxy_server1, ProxyServer_ProxyType_CORE)}));
network::mojom::CustomProxyConfigClientPtrInfo client_ptr_info;
TestCustomProxyConfigClient client(mojo::MakeRequest(&client_ptr_info));
io_data.SetCustomProxyConfigClient(std::move(client_ptr_info));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(client.config->rules.proxies_for_http.Get(), proxy_server1);
auto proxy_server2 = net::ProxyServer::FromPacString("PROXY bar");
io_data.config_client()->SetRemoteConfigAppliedForTesting(false);
io_data.config_client()->ApplySerializedConfig(CreateEncodedConfig(
{DataReductionProxyServer(proxy_server2, ProxyServer_ProxyType_CORE)}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(client.config->rules.proxies_for_http.Get(), proxy_server2);
}
TEST_F(DataReductionProxyIODataTest,
TestCustomProxyConfigHasAlternateProxyListOfCoreProxies) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableDataReductionProxyWarmupURLFetch);
DataReductionProxyIOData io_data(
Client::UNKNOWN, prefs(),
network::TestNetworkConnectionTracker::GetInstance(),
scoped_task_environment_.GetMainThreadTaskRunner(),
scoped_task_environment_.GetMainThreadTaskRunner(), false /* enabled */,
std::string() /* user_agent */, std::string() /* channel */);
NetworkPropertiesManager network_properties_manager(
base::DefaultClock::GetInstance(), prefs(),
scoped_task_environment_.GetMainThreadTaskRunner());
io_data.config()->SetNetworkPropertiesManagerForTesting(
&network_properties_manager);
io_data.config()->UpdateConfigForTesting(true, true, true);
auto core_proxy_server = net::ProxyServer::FromPacString("PROXY foo");
io_data.config_client()->ApplySerializedConfig(CreateEncodedConfig(
{DataReductionProxyServer(core_proxy_server, ProxyServer_ProxyType_CORE),
DataReductionProxyServer(net::ProxyServer::FromPacString("PROXY bar"),
ProxyServer_ProxyType_UNSPECIFIED_TYPE)}));
network::mojom::CustomProxyConfigClientPtrInfo client_ptr_info;
TestCustomProxyConfigClient client(mojo::MakeRequest(&client_ptr_info));
io_data.SetCustomProxyConfigClient(std::move(client_ptr_info));
base::RunLoop().RunUntilIdle();
net::ProxyConfig::ProxyRules expected_rules;
expected_rules.type =
net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME;
expected_rules.proxies_for_http.AddProxyServer(core_proxy_server);
expected_rules.proxies_for_http.AddProxyServer(net::ProxyServer::Direct());
EXPECT_TRUE(client.config->alternate_rules.Equals(expected_rules));
}
} // namespace data_reduction_proxy