From 2b814a415137c887eb170ebb7994e9ec81219ebd Mon Sep 17 00:00:00 2001 From: jeremyim Date: Tue, 21 Apr 2015 18:00:03 -0700 Subject: [PATCH] Add capabilities for retrieving a remote Data Reduction Proxy configuration. - Add support for a new header field for the secure session ID - Add flag to control the server from which the configuration is retrieved BUG=466753 Review URL: https://codereview.chromium.org/1032943002 Cr-Commit-Position: refs/heads/master@{#326203} --- .../data_reduction_proxy_config_service_client.cc | 189 +++++++++++++++++---- .../data_reduction_proxy_config_service_client.h | 92 ++++++++-- ...duction_proxy_config_service_client_unittest.cc | 185 +++++++++++++++++++- .../core/browser/data_reduction_proxy_io_data.cc | 8 +- .../core/browser/data_reduction_proxy_io_data.h | 6 + .../data_reduction_proxy_request_options.cc | 51 ++++-- .../browser/data_reduction_proxy_request_options.h | 8 + ...ata_reduction_proxy_request_options_unittest.cc | 55 ++++-- .../core/browser/data_reduction_proxy_service.cc | 7 + .../core/browser/data_reduction_proxy_service.h | 1 + .../core/browser/data_reduction_proxy_settings.cc | 1 + .../browser/data_reduction_proxy_test_utils.cc | 21 ++- .../core/browser/data_reduction_proxy_test_utils.h | 10 +- .../core/common/data_reduction_proxy_switches.cc | 3 + .../core/common/data_reduction_proxy_switches.h | 1 + 15 files changed, 558 insertions(+), 80 deletions(-) diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc index af4c5719085b..67728a995b16 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/command_line.h" #include "base/json/json_writer.h" #include "base/location.h" #include "base/logging.h" @@ -19,14 +20,22 @@ #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h" #include "components/data_reduction_proxy/proto/client_config.pb.h" #include "net/base/host_port_pair.h" +#include "net/base/load_flags.h" +#include "net/http/http_status_code.h" #include "net/proxy/proxy_server.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_request_status.h" namespace data_reduction_proxy { namespace { +// Default URL for retrieving the Data Reduction Proxy configuration. +const char kClientConfigURL[] = ""; + // This is the default backoff policy used to communicate with the Data // Reduction Proxy configuration service. const net::BackoffEntry::Policy kDefaultBackoffPolicy = { @@ -80,6 +89,23 @@ const net::BackoffEntry::Policy& GetBackoffPolicy() { return kDefaultBackoffPolicy; } +// static +GURL DataReductionProxyConfigServiceClient::GetConfigServiceURL( + const base::CommandLine& command_line) { + if (!command_line.HasSwitch(switches::kDataReductionProxyConfigURL)) + return GURL(kClientConfigURL); + + std::string value( + command_line.GetSwitchValueASCII(switches::kDataReductionProxyConfigURL)); + GURL result(value); + if (result.is_valid()) + return result; + + LOG(WARNING) << "The following client config URL specified at the " + << "command-line is invalid: " << value; + return GURL(kClientConfigURL); +} + DataReductionProxyConfigServiceClient::DataReductionProxyConfigServiceClient( scoped_ptr params, const net::BackoffEntry::Policy& backoff_policy, @@ -90,7 +116,11 @@ DataReductionProxyConfigServiceClient::DataReductionProxyConfigServiceClient( request_options_(request_options), config_values_(config_values), config_(config), - backoff_entry_(&backoff_policy) { + backoff_entry_(&backoff_policy), + config_service_url_( + GetConfigServiceURL(*base::CommandLine::ForCurrentProcess())), + use_local_config_(!config_service_url_.is_valid()), + url_request_context_getter_(nullptr) { DCHECK(request_options); DCHECK(config_values); DCHECK(config); @@ -100,47 +130,24 @@ DataReductionProxyConfigServiceClient::DataReductionProxyConfigServiceClient( DataReductionProxyConfigServiceClient:: ~DataReductionProxyConfigServiceClient() { + net::NetworkChangeNotifier::RemoveIPAddressObserver(this); +} + +void DataReductionProxyConfigServiceClient::InitializeOnIOThread( + net::URLRequestContextGetter* url_request_context_getter) { + DCHECK(url_request_context_getter); + net::NetworkChangeNotifier::AddIPAddressObserver(this); + url_request_context_getter_ = url_request_context_getter; } void DataReductionProxyConfigServiceClient::RetrieveConfig() { DCHECK(thread_checker_.CalledOnValidThread()); - std::string static_response = ConstructStaticResponse(); - ClientConfig config; - bool succeeded = false; - if (config_parser::ParseClientConfig(static_response, &config)) { - if (config.has_proxy_config()) { - net::ProxyServer origin; - net::ProxyServer fallback_origin; - std::vector proxies = - GetProxiesForHTTP(config.proxy_config()); - if (proxies.size() > 0) { - origin = proxies[0]; - if (proxies.size() > 1) - fallback_origin = proxies[1]; - - std::string session; - std::string credentials; - if (DataReductionProxyRequestOptions::ParseLocalSessionKey( - config.session_key(), &session, &credentials)) { - request_options_->SetCredentials(session, credentials); - config_values_->UpdateValues(origin, fallback_origin); - config_->ReloadConfig(); - succeeded = true; - } - } - } - } - - base::Time expiration_time; - if (succeeded) { - expiration_time = config_parser::TimestampToTime(config.expire_time()); + if (use_local_config_) { + ReadAndApplyStaticConfig(); + return; } - GetBackoffEntry()->InformOfRequest(succeeded); - base::TimeDelta next_config_refresh_time = - CalculateNextConfigRefreshTime(succeeded, expiration_time, Now(), - GetBackoffEntry()->GetTimeUntilRelease()); - SetConfigRefreshTimer(next_config_refresh_time); + RetrieveRemoteConfig(); } net::BackoffEntry* DataReductionProxyConfigServiceClient::GetBackoffEntry() { @@ -171,4 +178,114 @@ DataReductionProxyConfigServiceClient::ConstructStaticResponse() const { return response; } +void DataReductionProxyConfigServiceClient::OnIPAddressChanged() { + GetBackoffEntry()->Reset(); + RetrieveConfig(); +} + +void DataReductionProxyConfigServiceClient::OnURLFetchComplete( + const net::URLFetcher* source) { + DCHECK(source == fetcher_.get()); + net::URLRequestStatus status = source->GetStatus(); + std::string response; + source->GetResponseAsString(&response); + HandleResponse(response, status, source->GetResponseCode()); +} + +void DataReductionProxyConfigServiceClient::ReadAndApplyStaticConfig() { + std::string static_response = ConstructStaticResponse(); + HandleResponse(static_response, net::URLRequestStatus(), net::HTTP_OK); +} + +void DataReductionProxyConfigServiceClient::RetrieveRemoteConfig() { + scoped_ptr fetcher = + GetURLFetcherForConfig(config_service_url_, std::string()); + if (!fetcher.get()) { + HandleResponse(std::string(), + net::URLRequestStatus(net::URLRequestStatus::CANCELED, 0), + net::URLFetcher::RESPONSE_CODE_INVALID); + return; + } + + fetcher_ = fetcher.Pass(); + fetcher_->Start(); +} + +scoped_ptr +DataReductionProxyConfigServiceClient::GetURLFetcherForConfig( + const GURL& secure_proxy_check_url, + const std::string& request_body) { + scoped_ptr fetcher(net::URLFetcher::Create( + secure_proxy_check_url, net::URLFetcher::POST, this)); + fetcher->SetLoadFlags(net::LOAD_BYPASS_PROXY); + fetcher->SetUploadData("application/json", request_body); + DCHECK(url_request_context_getter_); + fetcher->SetRequestContext(url_request_context_getter_); + // Configure max retries to be at most kMaxRetries times for 5xx errors. + static const int kMaxRetries = 5; + fetcher->SetMaxRetriesOn5xx(kMaxRetries); + fetcher->SetAutomaticallyRetryOnNetworkChanges(kMaxRetries); + return fetcher.Pass(); +} + +void DataReductionProxyConfigServiceClient::HandleResponse( + const std::string& config_data, + const net::URLRequestStatus& status, + int response_code) { + ClientConfig config; + bool succeeded = false; + + if (status.status() == net::URLRequestStatus::SUCCESS && + response_code == net::HTTP_OK && + config_parser::ParseClientConfig(config_data, &config)) { + succeeded = ParseAndApplyProxyConfig(config); + } + + base::Time expiration_time; + if (succeeded) { + expiration_time = config_parser::TimestampToTime(config.expire_time()); + } + + GetBackoffEntry()->InformOfRequest(succeeded); + base::TimeDelta next_config_refresh_time = + CalculateNextConfigRefreshTime(succeeded, expiration_time, Now(), + GetBackoffEntry()->GetTimeUntilRelease()); + SetConfigRefreshTimer(next_config_refresh_time); +} + +bool DataReductionProxyConfigServiceClient::ParseAndApplyProxyConfig( + const ClientConfig& config) { + if (!config.has_proxy_config()) + return false; + + std::vector proxies = + GetProxiesForHTTP(config.proxy_config()); + if (proxies.empty()) + return false; + + net::ProxyServer origin = proxies[0]; + net::ProxyServer fallback_origin; + if (proxies.size() > 1) + fallback_origin = proxies[1]; + + if (!use_local_config_) { + request_options_->SetSecureSession(config.session_key()); + config_values_->UpdateValues(origin, fallback_origin); + config_->ReloadConfig(); + return true; + } + + std::string session; + std::string credentials; + if (!DataReductionProxyRequestOptions::ParseLocalSessionKey( + config.session_key(), &session, &credentials)) { + return false; + } + + request_options_->SetCredentials(session, credentials); + config_values_->UpdateValues(origin, fallback_origin); + config_->ReloadConfig(); + return true; +} + } // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h index 8487829532b4..41b042449519 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h @@ -13,18 +13,30 @@ #include "base/threading/thread_checker.h" #include "base/timer/timer.h" #include "net/base/backoff_entry.h" +#include "net/base/network_change_notifier.h" +#include "net/url_request/url_fetcher_delegate.h" +#include "url/gurl.h" namespace base { +class CommandLine; class Time; class TimeDelta; } +namespace net { +class URLFetcher; +class URLRequestContextGetter; +class URLRequestStatus; +} + namespace data_reduction_proxy { +class ClientConfig; class DataReductionProxyConfig; class DataReductionProxyMutableConfigValues; class DataReductionProxyParams; class DataReductionProxyRequestOptions; +class DataReductionProxyService; // Retrieves the default net::BackoffEntry::Policy for the Data Reduction Proxy // configuration service client. @@ -32,11 +44,19 @@ const net::BackoffEntry::Policy& GetBackoffPolicy(); // Retrieves the Data Reduction Proxy configuration from a remote service. This // object lives on the IO thread. -class DataReductionProxyConfigServiceClient { +// TODO(jeremyim): Rename the class to DataReductionProxyConfigGetter(?). +class DataReductionProxyConfigServiceClient + : public net::NetworkChangeNotifier::IPAddressObserver, + public net::URLFetcherDelegate { public: + // Returns the URL from which the Data Reduction Proxy configuration should + // be retrieved. + static GURL GetConfigServiceURL(const base::CommandLine& command_line); + // The caller must ensure that all parameters remain alive for the lifetime of // the |DataReductionProxyConfigClient|, with the exception of |params| - // which this instance will own. + // which this instance will own. The |io_task_runner| is used to enforce that + // configurations are applied on the IO thread. DataReductionProxyConfigServiceClient( scoped_ptr params, const net::BackoffEntry::Policy& backoff_policy, @@ -44,9 +64,14 @@ class DataReductionProxyConfigServiceClient { DataReductionProxyMutableConfigValues* config_values, DataReductionProxyConfig* config); - virtual ~DataReductionProxyConfigServiceClient(); + ~DataReductionProxyConfigServiceClient() override; - // Request the retrieval of the Data Reduction Proxy configuration. + // Performs initialization on the IO thread. + void InitializeOnIOThread( + net::URLRequestContextGetter* url_request_context_getter); + + // Request the retrieval of the Data Reduction Proxy configuration. This + // operation takes place asynchronously. void RetrieveConfig(); protected: @@ -54,24 +79,55 @@ class DataReductionProxyConfigServiceClient { // Virtual for testing. virtual net::BackoffEntry* GetBackoffEntry(); - // Sets a timer to determine when to next refresh the Data Reduction Proxy - // configuration. - // Virtual for testing. - virtual void SetConfigRefreshTimer(const base::TimeDelta& delay); - // Returns the current time. // Virtual for testing. virtual base::Time Now(); + // Sets a timer to determine when to next refresh the Data Reduction Proxy + // configuration. + void SetConfigRefreshTimer(const base::TimeDelta& delay); + // Constructs a synthetic response based on |params_|. - // Virtual for testing. - virtual std::string ConstructStaticResponse() const; + std::string ConstructStaticResponse() const; private: FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigServiceClientTest, TestConstructStaticResponse); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigServiceClientTest, + OnIPAddressChange); friend class TestDataReductionProxyConfigServiceClient; + // Override of net::NetworkChangeNotifier::IPAddressObserver. + void OnIPAddressChanged() override; + + // Override of net::URLFetcherDelegate. + void OnURLFetchComplete(const net::URLFetcher* source) override; + + // Retrieves the Data Reduction Proxy configuration from |params_|. + void ReadAndApplyStaticConfig(); + + // Retrieves the Data Reduction Proxy configuration from a remote service. + void RetrieveRemoteConfig(); + + // Returns a fetcher to retrieve the Data Reduction Proxy configuration. + // |secure_proxy_check_url| is the url from which to retrieve the config. + // |request_body| is the request body sent to the configuration service. + scoped_ptr GetURLFetcherForConfig( + const GURL& secure_proxy_check_url, + const std::string& request_body); + + // Handles the response from the remote Data Reduction Proxy configuration + // service. |response| is the response body, |status| is the + // |net::URLRequestStatus| of the response, and response_code is the HTTP + // response code (if available). + void HandleResponse(const std::string& response, + const net::URLRequestStatus& status, + int response_code); + + // Parses out the proxy configuration portion of |config| and applies it to + // |config_| and |request_options_|. + bool ParseAndApplyProxyConfig(const ClientConfig& config); + // Contains the static configuration data to use. scoped_ptr params_; @@ -87,11 +143,25 @@ class DataReductionProxyConfigServiceClient { // Used to calculate the backoff time on request failures. net::BackoffEntry backoff_entry_; + // The URL for retrieving the Data Reduction Proxy configuration. + GURL config_service_url_; + + // Whether to use |params_| to obtain the Data Reduction Proxy configuration + // or the remote server specified by |config_service_url_|. + // TODO(jeremyim): Remove this as part of bug 479282. + bool use_local_config_; + + // Used for setting up |fetcher_|. + net::URLRequestContextGetter* url_request_context_getter_; + // An event that fires when it is time to refresh the Data Reduction Proxy // configuration. base::OneShotTimer config_refresh_timer_; + // A |net::URLFetcher| to retrieve the Data Reduction Proxy configuration. + scoped_ptr fetcher_; + // Enforce usage on the IO thread. base::ThreadChecker thread_checker_; diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc index b72ee57469d3..19ec5f4ec8cf 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc @@ -6,6 +6,7 @@ #include +#include "base/command_line.h" #include "base/memory/scoped_ptr.h" #include "base/time/tick_clock.h" #include "base/values.h" @@ -15,9 +16,28 @@ #include "components/data_reduction_proxy/core/common/data_reduction_proxy_client_config_parser.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_switches.h" #include "components/data_reduction_proxy/proto/client_config.pb.h" +#include "net/socket/socket_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace { + +const char kSuccessResponse[] = + "{ \"sessionKey\": \"SecretSessionKey\", " + "\"expireTime\": \"1970-01-01T00:01:00.000Z\", " + "\"proxyConfig\": { \"httpProxyServers\": [" + "{ \"scheme\": \"HTTPS\", \"host\": \"origin.net\", \"port\": 443 }," + "{ \"scheme\": \"HTTP\", \"host\": \"fallback.net\", \"port\": 80 }" + "] } }"; +// The following values should match the ones in the response above. +const char kSuccessOrigin[] = "https://origin.net:443"; +const char kSuccessFallback[] = "fallback.net:80"; +const char kSuccessSessionKey[] = "SecretSessionKey"; + +} // namespace namespace data_reduction_proxy { @@ -50,6 +70,8 @@ void PopulateResponseFailure(base::DictionaryValue* response) { class DataReductionProxyConfigServiceClientTest : public testing::Test { protected: + DataReductionProxyConfigServiceClientTest() : context_(true) {} + void SetUp() override { test_context_ = DataReductionProxyTestContext::Builder() @@ -57,12 +79,15 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test { DataReductionProxyParams::kFallbackAllowed | DataReductionProxyParams::kPromoAllowed) .WithParamsDefinitions(TestDataReductionProxyParams::HAS_EVERYTHING) + .WithURLRequestContext(&context_) + .WithMockClientSocketFactory(&mock_socket_factory_) .WithTestConfigurator() .WithMockRequestOptions() .WithTestConfigClient() .Build(); - test_context_->test_config_client()->SetCustomReleaseTime( - base::TimeTicks::UnixEpoch()); + context_.set_client_socket_factory(&mock_socket_factory_); + context_.Init(); + ResetBackoffEntryReleaseTime(); test_context_->test_config_client()->SetNow(base::Time::UnixEpoch()); } @@ -84,6 +109,18 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test { test_context_->io_data()->config())); } + void ResetBackoffEntryReleaseTime() { + config_client()->SetCustomReleaseTime(base::TimeTicks::UnixEpoch()); + } + + void VerifyRemoteSuccess() { + EXPECT_EQ(base::TimeDelta::FromMinutes(1), config_client()->GetDelay()); + EXPECT_EQ(kSuccessOrigin, configurator()->origin()); + EXPECT_EQ(kSuccessFallback, configurator()->fallback_origin()); + EXPECT_TRUE(configurator()->ssl_origin().empty()); + EXPECT_EQ(kSuccessSessionKey, request_options()->GetSecureSession()); + } + DataReductionProxyParams* params() { return test_context_->test_params(); } @@ -104,7 +141,14 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test { test_context_->RunUntilIdle(); } + net::MockClientSocketFactory* mock_socket_factory() { + return &mock_socket_factory_; + } + private: + net::TestURLRequestContext context_; + net::MockClientSocketFactory mock_socket_factory_; + scoped_ptr test_context_; scoped_ptr request_options_; }; @@ -118,6 +162,8 @@ TEST_F(DataReductionProxyConfigServiceClientTest, TestConstructStaticResponse) { } TEST_F(DataReductionProxyConfigServiceClientTest, SuccessfulLoop) { + // Use a local/static config. + config_client()->SetConfigServiceURL(GURL()); RequestOptionsPopulator populator( base::Time::UnixEpoch() + base::TimeDelta::FromDays(1), base::TimeDelta::FromDays(1)); @@ -148,6 +194,8 @@ TEST_F(DataReductionProxyConfigServiceClientTest, SuccessfulLoop) { } TEST_F(DataReductionProxyConfigServiceClientTest, SuccessfulLoopShortDuration) { + // Use a local/static config. + config_client()->SetConfigServiceURL(GURL()); RequestOptionsPopulator populator( base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(1), base::TimeDelta::FromSeconds(1)); @@ -168,6 +216,8 @@ TEST_F(DataReductionProxyConfigServiceClientTest, SuccessfulLoopShortDuration) { } TEST_F(DataReductionProxyConfigServiceClientTest, EnsureBackoff) { + // Use a local/static config. + config_client()->SetConfigServiceURL(GURL()); SetDataReductionProxyEnabled(true); EXPECT_TRUE(configurator()->origin().empty()); EXPECT_TRUE(configurator()->fallback_origin().empty()); @@ -188,6 +238,8 @@ TEST_F(DataReductionProxyConfigServiceClientTest, EnsureBackoff) { } TEST_F(DataReductionProxyConfigServiceClientTest, ConfigDisabled) { + // Use a local/static config. + config_client()->SetConfigServiceURL(GURL()); RequestOptionsPopulator populator( base::Time::UnixEpoch() + base::TimeDelta::FromDays(1), base::TimeDelta::FromDays(1)); @@ -206,4 +258,133 @@ TEST_F(DataReductionProxyConfigServiceClientTest, ConfigDisabled) { EXPECT_EQ(base::TimeDelta::FromDays(1), config_client()->GetDelay()); } +TEST_F(DataReductionProxyConfigServiceClientTest, GetConfigServiceURL) { + const struct { + std::string flag_value; + GURL expected; + } tests[] = { + { + "", GURL(), + }, + { + "http://configservice.chrome-test.com", + GURL("http://configservice.chrome-test.com"), + }, + }; + + for (const auto& test : tests) { + // Reset all flags. + base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL); + base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kDataReductionProxyConfigURL, test.flag_value); + EXPECT_EQ(test.expected, + DataReductionProxyConfigServiceClient::GetConfigServiceURL( + *base::CommandLine::ForCurrentProcess())); + } +} + +TEST_F(DataReductionProxyConfigServiceClientTest, RemoteConfigSuccess) { + net::MockRead mock_reads[] = { + net::MockRead("HTTP/1.1 200 OK\r\n\r\n"), + net::MockRead(kSuccessResponse), + net::MockRead(net::SYNCHRONOUS, net::OK), + }; + net::StaticSocketDataProvider socket_data_provider( + mock_reads, arraysize(mock_reads), nullptr, 0); + mock_socket_factory()->AddSocketDataProvider(&socket_data_provider); + config_client()->SetConfigServiceURL(GURL("http://configservice.com")); + SetDataReductionProxyEnabled(true); + EXPECT_TRUE(configurator()->origin().empty()); + EXPECT_TRUE(configurator()->fallback_origin().empty()); + EXPECT_TRUE(configurator()->ssl_origin().empty()); + EXPECT_CALL(*request_options(), PopulateConfigResponse(testing::_)).Times(0); + config_client()->RetrieveConfig(); + RunUntilIdle(); + VerifyRemoteSuccess(); +} + +TEST_F(DataReductionProxyConfigServiceClientTest, + RemoteConfigSuccessAfterFailure) { + net::MockRead mock_reads_array[][3] = { + { + // Failure due to 404 error. + net::MockRead("HTTP/1.1 404 Not found\r\n\r\n"), + net::MockRead(""), + net::MockRead(net::SYNCHRONOUS, net::OK), + }, + { + // Success. + net::MockRead("HTTP/1.1 200 OK\r\n\r\n"), + net::MockRead(kSuccessResponse), + net::MockRead(net::SYNCHRONOUS, net::OK), + }, + }; + + ScopedVector socket_data_providers; + for (net::MockRead* mock_reads : mock_reads_array) { + socket_data_providers.push_back( + new net::StaticSocketDataProvider(mock_reads, 3, nullptr, 0)); + mock_socket_factory()->AddSocketDataProvider(socket_data_providers.back()); + } + + config_client()->SetConfigServiceURL(GURL("http://configservice.com")); + SetDataReductionProxyEnabled(true); + EXPECT_TRUE(configurator()->origin().empty()); + EXPECT_TRUE(configurator()->fallback_origin().empty()); + EXPECT_TRUE(configurator()->ssl_origin().empty()); + EXPECT_CALL(*request_options(), PopulateConfigResponse(testing::_)).Times(0); + config_client()->RetrieveConfig(); + RunUntilIdle(); + EXPECT_EQ(base::TimeDelta::FromSeconds(20), config_client()->GetDelay()); + EXPECT_TRUE(configurator()->origin().empty()); + EXPECT_TRUE(configurator()->fallback_origin().empty()); + EXPECT_TRUE(configurator()->ssl_origin().empty()); + EXPECT_TRUE(request_options()->GetSecureSession().empty()); + config_client()->RetrieveConfig(); + RunUntilIdle(); + VerifyRemoteSuccess(); +} + +TEST_F(DataReductionProxyConfigServiceClientTest, OnIPAddressChange) { + config_client()->SetConfigServiceURL(GURL("http://configservice.com")); + SetDataReductionProxyEnabled(true); + config_client()->RetrieveConfig(); + EXPECT_CALL(*request_options(), PopulateConfigResponse(testing::_)).Times(0); + + static const int kFailureCount = 5; + + net::MockRead failure_reads[] = { + net::MockRead("HTTP/1.1 404 Not found\r\n\r\n"), + net::MockRead(""), + net::MockRead(net::SYNCHRONOUS, net::OK), + }; + + ScopedVector socket_data_providers; + for (int i = 0; i < kFailureCount; ++i) { + socket_data_providers.push_back( + new net::StaticSocketDataProvider(failure_reads, 3, nullptr, 0)); + mock_socket_factory()->AddSocketDataProvider(socket_data_providers.back()); + config_client()->RetrieveConfig(); + RunUntilIdle(); + } + + EXPECT_EQ(base::TimeDelta::FromSeconds(320), config_client()->GetDelay()); + EXPECT_EQ(kFailureCount, config_client()->GetBackoffErrorCount()); + config_client()->OnIPAddressChanged(); + EXPECT_EQ(0, config_client()->GetBackoffErrorCount()); + ResetBackoffEntryReleaseTime(); + + net::MockRead success_reads[] = { + net::MockRead("HTTP/1.1 200 OK\r\n\r\n"), + net::MockRead(kSuccessResponse), + net::MockRead(net::SYNCHRONOUS, net::OK), + }; + net::StaticSocketDataProvider socket_data_provider( + success_reads, arraysize(success_reads), nullptr, 0); + mock_socket_factory()->AddSocketDataProvider(&socket_data_provider); + config_client()->RetrieveConfig(); + RunUntilIdle(); + VerifyRemoteSuccess(); +} + } // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc index 5e0c3f8e1ad1..470eda02dd55 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc @@ -187,7 +187,7 @@ void DataReductionProxyIOData::InitializeOnIOThread() { DCHECK(io_task_runner_->BelongsToCurrentThread()); config_->InitializeOnIOThread(basic_url_request_context_getter_.get()); if (config_client_.get()) - config_client_->RetrieveConfig(); + config_client_->InitializeOnIOThread(url_request_context_getter_); ui_task_runner_->PostTask( FROM_HERE, base::Bind(&DataReductionProxyService::SetIOData, @@ -201,6 +201,12 @@ bool DataReductionProxyIOData::IsEnabled() const { switches::kEnableDataReductionProxy); } +void DataReductionProxyIOData::RetrieveConfig() { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + if (config_client_) + config_client_->RetrieveConfig(); +} + scoped_ptr DataReductionProxyIOData::CreateInterceptor() { DCHECK(io_task_runner_->BelongsToCurrentThread()); diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h index c5b13efc1b72..1567b4f1499a 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h @@ -60,6 +60,8 @@ class DataReductionProxyIOData { void SetDataReductionProxyService( base::WeakPtr data_reduction_proxy_service); + void RetrieveConfig(); + // Creates an interceptor suitable for following the Data Reduction Proxy // bypass protocol. scoped_ptr CreateInterceptor(); @@ -106,6 +108,10 @@ class DataReductionProxyIOData { return request_options_.get(); } + DataReductionProxyConfigServiceClient* config_client() const { + return config_client_.get(); + } + net::ProxyDelegate* proxy_delegate() const { return proxy_delegate_.get(); } diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc index 8bd3d23aae33..12b678567c8d 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc @@ -4,6 +4,8 @@ #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h" +#include + #include "base/bind.h" #include "base/command_line.h" #include "base/single_thread_task_runner.h" @@ -40,6 +42,7 @@ std::string FormatOption(const std::string& name, const std::string& value) { const char kSessionHeaderOption[] = "ps"; const char kCredentialsHeaderOption[] = "sid"; +const char kSecureSessionHeaderOption[] = "s"; const char kBuildNumberHeaderOption[] = "b"; const char kPatchNumberHeaderOption[] = "p"; const char kClientHeaderOption[] = "c"; @@ -300,6 +303,19 @@ void DataReductionProxyRequestOptions::SetCredentials( DCHECK(thread_checker_.CalledOnValidThread()); session_ = session; credentials_ = credentials; + secure_session_.clear(); + // Force skipping of credential regeneration. It should be handled by the + // caller. + use_assigned_credentials_ = true; + RegenerateRequestHeaderValue(); +} + +void DataReductionProxyRequestOptions::SetSecureSession( + const std::string& secure_session) { + DCHECK(thread_checker_.CalledOnValidThread()); + session_.clear(); + credentials_.clear(); + secure_session_ = secure_session; // Force skipping of credential regeneration. It should be handled by the // caller. use_assigned_credentials_ = true; @@ -326,6 +342,10 @@ std::string DataReductionProxyRequestOptions::GetDefaultKey() const { return key; } +const std::string& DataReductionProxyRequestOptions::GetSecureSession() const { + return secure_session_; +} + void DataReductionProxyRequestOptions::MaybeAddRequestHeaderImpl( const net::HostPortPair& proxy_server, bool expect_ssl, @@ -341,18 +361,27 @@ void DataReductionProxyRequestOptions::MaybeAddRequestHeaderImpl( } void DataReductionProxyRequestOptions::RegenerateRequestHeaderValue() { - header_value_ = FormatOption(kSessionHeaderOption, session_) - + ", " + FormatOption(kCredentialsHeaderOption, credentials_) - + (client_.empty() ? - "" : ", " + FormatOption(kClientHeaderOption, client_)) - + (build_.empty() || patch_.empty() ? - "" : - ", " + FormatOption(kBuildNumberHeaderOption, build_) - + ", " + FormatOption(kPatchNumberHeaderOption, patch_)) - + (lofi_.empty() ? - "" : ", " + FormatOption(kLoFiHeaderOption, lofi_)); + std::vector headers; + if (!session_.empty()) + headers.push_back(FormatOption(kSessionHeaderOption, session_)); + if (!credentials_.empty()) + headers.push_back(FormatOption(kCredentialsHeaderOption, credentials_)); + if (!secure_session_.empty()) { + headers.push_back( + FormatOption(kSecureSessionHeaderOption, secure_session_)); + } + if (!client_.empty()) + headers.push_back(FormatOption(kClientHeaderOption, client_)); + if (!build_.empty() && !patch_.empty()) { + headers.push_back(FormatOption(kBuildNumberHeaderOption, build_)); + headers.push_back(FormatOption(kPatchNumberHeaderOption, patch_)); + } + if (!lofi_.empty()) + headers.push_back(FormatOption(kLoFiHeaderOption, lofi_)); for (const auto& experiment : experiments_) - header_value_ += ", " + FormatOption(kExperimentsOption, experiment); + headers.push_back(FormatOption(kExperimentsOption, experiment)); + + header_value_ = JoinString(headers, ", "); } } // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h index 2aa8dcef56b1..0721dab5ff93 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h @@ -29,6 +29,7 @@ namespace data_reduction_proxy { extern const char kSessionHeaderOption[]; extern const char kCredentialsHeaderOption[]; +extern const char kSecureSessionHeaderOption[]; extern const char kBuildNumberHeaderOption[]; extern const char kPatchNumberHeaderOption[]; extern const char kClientHeaderOption[]; @@ -126,6 +127,9 @@ class DataReductionProxyRequestOptions { void SetCredentials(const std::string& session, const std::string& credentials); + // Sets the credentials for sending to the Data Reduction Proxy. + void SetSecureSession(const std::string& secure_session); + protected: void SetHeader(net::HttpRequestHeaders* headers); @@ -146,6 +150,9 @@ class DataReductionProxyRequestOptions { const std::string& version, DataReductionProxyConfig* config); + // Visible for testing. + virtual const std::string& GetSecureSession() const; + private: FRIEND_TEST_ALL_PREFIXES(DataReductionProxyRequestOptionsTest, AuthHashForSalt); @@ -201,6 +208,7 @@ class DataReductionProxyRequestOptions { std::string version_; std::string session_; std::string credentials_; + std::string secure_session_; std::string build_; std::string patch_; std::string lofi_; diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc index 95c3465705db..4ea8a9a2b155 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc @@ -35,6 +35,8 @@ const char kTestKey2[] = "test-key2"; const char kExpectedCredentials2[] = "c911fdb402f578787562cf7f00eda972"; const char kExpectedSession2[] = "0-1633771873-1633771873-1633771873"; const char kDataReductionProxyKey[] = "12345"; + +const char kSecureSession[] = "TestSecureSessionKey"; } // namespace @@ -78,6 +80,7 @@ const char kClientStr[] = ""; void SetHeaderExpectations(const std::string& session, const std::string& credentials, + const std::string& secure_session, const std::string& client, const std::string& build, const std::string& patch, @@ -93,6 +96,10 @@ void SetHeaderExpectations(const std::string& session, expected_options.push_back( std::string(kCredentialsHeaderOption) + "=" + credentials); } + if (!secure_session.empty()) { + expected_options.push_back(std::string(kSecureSessionHeaderOption) + "=" + + secure_session); + } if (!client.empty()) { expected_options.push_back( std::string(kClientHeaderOption) + "=" + client); @@ -182,15 +189,17 @@ TEST_F(DataReductionProxyRequestOptionsTest, AuthHashForSalt) { TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationOnIOThread) { std::string expected_header; - SetHeaderExpectations(kExpectedSession2, kExpectedCredentials2, kClientStr, - kExpectedBuild, kExpectedPatch, std::string(), - std::vector(), &expected_header); + SetHeaderExpectations(kExpectedSession2, kExpectedCredentials2, std::string(), + kClientStr, kExpectedBuild, kExpectedPatch, + std::string(), std::vector(), + &expected_header); std::string expected_header2; SetHeaderExpectations("86401-1633771873-1633771873-1633771873", - "d7c1c34ef6b90303b01c48a6c1db6419", kClientStr, - kExpectedBuild, kExpectedPatch, std::string(), - std::vector(), &expected_header2); + "d7c1c34ef6b90303b01c48a6c1db6419", std::string(), + kClientStr, kExpectedBuild, kExpectedPatch, + std::string(), std::vector(), + &expected_header2); CreateRequestOptions(kVersion); test_context_->RunUntilIdle(); @@ -233,9 +242,10 @@ TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationOnIOThread) { TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationIgnoresEmptyKey) { std::string expected_header; - SetHeaderExpectations(kExpectedSession, kExpectedCredentials, kClientStr, - kExpectedBuild, kExpectedPatch, std::string(), - std::vector(), &expected_header); + SetHeaderExpectations(kExpectedSession, kExpectedCredentials, std::string(), + kClientStr, kExpectedBuild, kExpectedPatch, + std::string(), std::vector(), + &expected_header); CreateRequestOptions(kVersion); VerifyExpectedHeader(params()->DefaultOrigin(), expected_header); @@ -247,8 +257,8 @@ TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationIgnoresEmptyKey) { TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationBogusVersion) { std::string expected_header; - SetHeaderExpectations(kExpectedSession2, kExpectedCredentials2, kClientStr, - std::string(), std::string(), std::string(), + SetHeaderExpectations(kExpectedSession2, kExpectedCredentials2, std::string(), + kClientStr, std::string(), std::string(), std::string(), std::vector(), &expected_header); CreateRequestOptions(kBogusVersion); @@ -260,8 +270,8 @@ TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationBogusVersion) { TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationLoFi) { std::string expected_header; - SetHeaderExpectations(kExpectedSession, kExpectedCredentials, kClientStr, - std::string(), std::string(), "low", + SetHeaderExpectations(kExpectedSession, kExpectedCredentials, std::string(), + kClientStr, std::string(), std::string(), "low", std::vector(), &expected_header); base::CommandLine::ForCurrentProcess()->AppendSwitch( @@ -277,11 +287,22 @@ TEST_F(DataReductionProxyRequestOptionsTest, LoFiOn) { data_reduction_proxy::switches::kEnableDataReductionProxyLoFi); std::string expected_header; - SetHeaderExpectations(kExpectedSession, kExpectedCredentials, kClientStr, - std::string(), std::string(), "low", + SetHeaderExpectations(kExpectedSession, kExpectedCredentials, std::string(), + kClientStr, std::string(), std::string(), "low", + std::vector(), &expected_header); + + CreateRequestOptions(kBogusVersion); + VerifyExpectedHeader(params()->DefaultOrigin(), expected_header); +} + +TEST_F(DataReductionProxyRequestOptionsTest, SecureSession) { + std::string expected_header; + SetHeaderExpectations(std::string(), std::string(), kSecureSession, + kClientStr, std::string(), std::string(), std::string(), std::vector(), &expected_header); CreateRequestOptions(kBogusVersion); + request_options()->SetSecureSession(kSecureSession); VerifyExpectedHeader(params()->DefaultOrigin(), expected_header); } @@ -293,8 +314,8 @@ TEST_F(DataReductionProxyRequestOptionsTest, ParseExperiments) { expected_experiments.push_back("staging"); expected_experiments.push_back("\"foo,bar\""); std::string expected_header; - SetHeaderExpectations(kExpectedSession, kExpectedCredentials, kClientStr, - std::string(), std::string(), std::string(), + SetHeaderExpectations(kExpectedSession, kExpectedCredentials, std::string(), + kClientStr, std::string(), std::string(), std::string(), expected_experiments, &expected_header); CreateRequestOptions(kBogusVersion); diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc index 1b90a14d0456..c5a4e54e6ffb 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc @@ -83,6 +83,13 @@ void DataReductionProxyService::SetProxyPrefs(bool enabled, io_data_, enabled, alternative_enabled, at_startup)); } +void DataReductionProxyService::RetrieveConfig() { + DCHECK(CalledOnValidThread()); + io_task_runner_->PostTask( + FROM_HERE, + base::Bind(&DataReductionProxyIOData::RetrieveConfig, io_data_)); +} + void DataReductionProxyService::AddObserver( DataReductionProxyServiceObserver* observer) { DCHECK(CalledOnValidThread()); diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h index ed3930739675..f107881cb456 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h @@ -81,6 +81,7 @@ class DataReductionProxyService : public base::NonThreadSafe { virtual void SetProxyPrefs(bool enabled, bool alternative_enabled, bool at_startup); + void RetrieveConfig(); // Methods for adding/removing observers on |this|. void AddObserver(DataReductionProxyServiceObserver* observer); diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc index 408ad45cbafd..33c1433c9e13 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc @@ -226,6 +226,7 @@ void DataReductionProxySettings::UpdateIOData(bool at_startup) { data_reduction_proxy_service_->SetProxyPrefs( IsDataReductionProxyEnabled(), IsDataReductionProxyAlternativeEnabled(), at_startup); + data_reduction_proxy_service_->RetrieveConfig(); } void DataReductionProxySettings::MaybeActivateDataReductionProxy( diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc index 1966107f4d4b..ae43031a48fc 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc @@ -23,6 +23,7 @@ #include "net/url_request/url_request_intercepting_job_factory.h" #include "net/url_request/url_request_job_factory_impl.h" #include "net/url_request/url_request_test_util.h" +#include "url/gurl.h" namespace { @@ -71,11 +72,16 @@ void TestDataReductionProxyRequestOptions::set_offset( now_offset_ = now_offset; } +const std::string& TestDataReductionProxyRequestOptions::GetSecureSession() + const { + return DataReductionProxyRequestOptions::GetSecureSession(); +} + MockDataReductionProxyRequestOptions::MockDataReductionProxyRequestOptions( Client client, const std::string& version, DataReductionProxyConfig* config) - : DataReductionProxyRequestOptions(client, version, config) { + : TestDataReductionProxyRequestOptions(client, version, config) { } MockDataReductionProxyRequestOptions::~MockDataReductionProxyRequestOptions() { @@ -113,6 +119,16 @@ base::TimeDelta TestDataReductionProxyConfigServiceClient::GetDelay() const { return config_refresh_timer_.GetCurrentDelay(); } +int TestDataReductionProxyConfigServiceClient::GetBackoffErrorCount() { + return test_backoff_entry_.failure_count(); +} + +void TestDataReductionProxyConfigServiceClient::SetConfigServiceURL( + const GURL& service_url) { + config_service_url_ = service_url; + use_local_config_ = !config_service_url_.is_valid(); +} + base::Time TestDataReductionProxyConfigServiceClient::Now() { return tick_clock_.Now(); } @@ -426,6 +442,9 @@ void DataReductionProxyTestContext::InitSettingsWithoutCheck() { CreateDataReductionProxyServiceInternal()); io_data_->SetDataReductionProxyService( settings_->data_reduction_proxy_service()->GetWeakPtr()); + if (io_data_->config_client()) + io_data_->config_client()->InitializeOnIOThread( + request_context_getter_.get()); settings_->data_reduction_proxy_service()->SetIOData(io_data_->GetWeakPtr()); } diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h index 653c5c8ee0da..7c4135606a59 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h @@ -25,6 +25,7 @@ #include "net/url_request/url_request_context_getter.h" #include "testing/gmock/include/gmock/gmock.h" +class GURL; class TestingPrefServiceSimple; namespace base { @@ -67,13 +68,16 @@ class TestDataReductionProxyRequestOptions // Time after the unix epoch that Now() reports. void set_offset(const base::TimeDelta& now_offset); + // Visible for testing. + const std::string& GetSecureSession() const override; + private: base::TimeDelta now_offset_; }; // Mock version of |DataReductionProxyRequestOptions|. class MockDataReductionProxyRequestOptions - : public DataReductionProxyRequestOptions { + : public TestDataReductionProxyRequestOptions { public: MockDataReductionProxyRequestOptions(Client client, const std::string& version, @@ -104,6 +108,10 @@ class TestDataReductionProxyConfigServiceClient base::TimeDelta GetDelay() const; + int GetBackoffErrorCount(); + + void SetConfigServiceURL(const GURL& service_url); + protected: // Overrides of DataReductionProxyConfigServiceClient base::Time Now() override; diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc index 2386931cdb08..bf676d32f034 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc @@ -74,5 +74,8 @@ const char kClearDataReductionProxyDataSavings[] = const char kEnableDataReductionProxyConfigClient[] = "enable-data-reduction-proxy-config-client"; +// The URL from which to retrieve the Data Reduction Proxy configuration. +const char kDataReductionProxyConfigURL[] = "data-reduction-proxy-config-url"; + } // namespace switches } // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h index 1a8f7c046cbf..90d98df021e6 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h @@ -29,6 +29,7 @@ extern const char kEnableDataReductionProxyLoFi[]; extern const char kEnableDataReductionProxyBypassWarning[]; extern const char kClearDataReductionProxyDataSavings[]; extern const char kEnableDataReductionProxyConfigClient[]; +extern const char kDataReductionProxyConfigURL[]; } // namespace switches } // namespace data_reduction_proxy -- 2.11.4.GIT