| // 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_config_service_client.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/base64.h" |
| #include "base/command_line.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/run_loop.h" |
| #include "base/stl_util.h" |
| #include "base/test/bind_test_util.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/mock_entropy_provider.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "base/time/default_clock.h" |
| #include "base/time/time.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_config_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_delegate.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_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/data_reduction_proxy/proto/client_config.pb.h" |
| #include "net/base/network_change_notifier.h" |
| #include "net/base/proxy_server.h" |
| #include "net/http/http_request_headers.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/socket/socket_test_util.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "net/url_request/url_request_context_storage.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| #include "services/network/test/test_url_loader_factory.h" |
| #include "services/network/test/test_utils.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| namespace { |
| |
| // The following values should match those in |
| // DataReductionProxyConfigServiceClientTest.config_: |
| const char kSuccessOrigin[] = "https://origin.net:443"; |
| const char kSuccessFallback[] = "fallback.net:80"; |
| const char kSuccessSessionKey[] = "SecretSessionKey"; |
| |
| // The following values should match those in |
| // DataReductionProxyConfigServiceClientTest.previous_config_: |
| const char kOldSuccessOrigin[] = "https://old.origin.net:443"; |
| const char kOldSuccessFallback[] = "old.fallback.net:80"; |
| const char kOldSuccessSessionKey[] = "OldSecretSessionKey"; |
| |
| // The following values should match those in |
| // DataReductionProxyConfigServiceClientTest.loaded_config_: |
| const char kPersistedOrigin[] = "https://persisted.net:443"; |
| const char kPersistedFallback[] = "persisted.net:80"; |
| const char kPersistedSessionKey[] = "PersistedSessionKey"; |
| |
| // Duration (in seconds) after which the config should be refreshed. |
| const int kConfigRefreshDurationSeconds = 600; |
| |
| #if defined(OS_ANDROID) |
| // Maximum duration to wait before fetching the config, while the application |
| // is in background. |
| const uint32_t kMaxBackgroundFetchIntervalSeconds = 6 * 60 * 60; // 6 hours. |
| #endif |
| |
| } // namespace |
| |
| namespace data_reduction_proxy { |
| |
| class DataReductionProxyConfigServiceClientTest : public testing::Test { |
| protected: |
| DataReductionProxyConfigServiceClientTest() |
| : test_shared_url_loader_factory_( |
| base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
| &test_url_loader_factory_)) { |
| // TODO(tonikitoo): Do a clean up pass to remove unneeded class members |
| // once the URLFetcher->SimpleURLLoader switch stabalizes. |
| // This includes |context_|, |context_storage_|, |mock_socket_factory_|, |
| // |success_reads_|, |previous_success_reads_| and |not_found_reads_|. |
| context_.reset(new net::TestURLRequestContext(true)); |
| context_storage_.reset(new net::URLRequestContextStorage(context_.get())); |
| mock_socket_factory_.reset(new net::MockClientSocketFactory()); |
| } |
| |
| void SetUp() override { |
| testing::Test::SetUp(); |
| |
| // Install an interceptor here to process the queue of responses |
| // (fed by calls to AddMock{Success,PreviousSuccess,Failure}. |
| test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting( |
| [&](const network::ResourceRequest& request) { |
| // Some tests trigger loading, without having an respective |
| // mock response set. Ignore such cases. |
| if (mock_responses_.size() <= curr_mock_response_index_) { |
| test_url_loader_factory_.pending_requests()->clear(); |
| return; |
| } |
| test_url_loader_factory_.ClearResponses(); |
| test_url_loader_factory_.AddResponse( |
| request.url.spec(), |
| mock_responses_[curr_mock_response_index_].response, |
| mock_responses_[curr_mock_response_index_].http_code); |
| curr_mock_response_index_++; |
| RunUntilIdle(); |
| })); |
| } |
| |
| void Init(bool use_mock_client_socket_factory) { |
| if (!use_mock_client_socket_factory) |
| mock_socket_factory_.reset(nullptr); |
| test_context_ = DataReductionProxyTestContext::Builder() |
| .WithURLLoaderFactory(test_shared_url_loader_factory_) |
| .WithMockRequestOptions() |
| .WithTestConfigClient() |
| .SkipSettingsInitialization() |
| .Build(); |
| |
| context_->set_client_socket_factory(mock_socket_factory_.get()); |
| test_context_->AttachToURLRequestContext(context_storage_.get()); |
| delegate_ = test_context_->io_data()->CreateProxyDelegate(); |
| |
| context_->Init(); |
| context_->proxy_resolution_service()->SetProxyDelegate(delegate_.get()); |
| |
| // Disable fetching of warmup URL to avoid generating extra traffic which |
| // would need to be satisfied using mock sockets. |
| test_context_->DisableWarmupURLFetch(); |
| |
| test_context_->InitSettings(); |
| ResetBackoffEntryReleaseTime(); |
| test_context_->test_config_client()->SetNow(base::Time::UnixEpoch()); |
| test_context_->test_config_client()->SetEnabled(true); |
| test_context_->test_config_client()->SetConfigServiceURL( |
| GURL("http://configservice.com")); |
| |
| ASSERT_NE(nullptr, context_->network_delegate()); |
| // Set up the various test ClientConfigs. |
| ClientConfig config = CreateConfig( |
| kSuccessSessionKey, kConfigRefreshDurationSeconds, 0, |
| ProxyServer_ProxyScheme_HTTPS, "origin.net", 443, ProxyServer::CORE, |
| ProxyServer_ProxyScheme_HTTP, "fallback.net", 80, |
| ProxyServer::UNSPECIFIED_TYPE, 0.5f, false); |
| config.SerializeToString(&config_); |
| encoded_config_ = EncodeConfig(config); |
| |
| ClientConfig previous_config = CreateConfig( |
| kOldSuccessSessionKey, kConfigRefreshDurationSeconds, 0, |
| ProxyServer_ProxyScheme_HTTPS, "old.origin.net", 443, ProxyServer::CORE, |
| ProxyServer_ProxyScheme_HTTP, "old.fallback.net", 80, |
| ProxyServer::UNSPECIFIED_TYPE, 0.0f, false); |
| previous_config.SerializeToString(&previous_config_); |
| |
| ClientConfig persisted = CreateConfig( |
| kPersistedSessionKey, kConfigRefreshDurationSeconds, 0, |
| ProxyServer_ProxyScheme_HTTPS, "persisted.net", 443, ProxyServer::CORE, |
| ProxyServer_ProxyScheme_HTTP, "persisted.net", 80, |
| ProxyServer::UNSPECIFIED_TYPE, 0.0f, false); |
| loaded_config_ = EncodeConfig(persisted); |
| |
| ClientConfig zero_reporting_fraction_config = CreateConfig( |
| kSuccessSessionKey, kConfigRefreshDurationSeconds, 0, |
| ProxyServer_ProxyScheme_HTTPS, "origin.net", 443, ProxyServer::CORE, |
| ProxyServer_ProxyScheme_HTTP, "origin.net", 0, |
| ProxyServer::UNSPECIFIED_TYPE, 0.0f, false); |
| zero_reporting_fraction_encoded_config_ = |
| EncodeConfig(zero_reporting_fraction_config); |
| |
| ClientConfig one_reporting_fraction_config = |
| CreateConfig(kSuccessSessionKey, kConfigRefreshDurationSeconds, 0, |
| ProxyServer_ProxyScheme_HTTPS, "", 443, ProxyServer::CORE, |
| ProxyServer_ProxyScheme_HTTP, "", 0, |
| ProxyServer::UNSPECIFIED_TYPE, 1.0f, false); |
| one_reporting_fraction_encoded_config_ = |
| EncodeConfig(one_reporting_fraction_config); |
| |
| // Passing in -1.0f as the reporting fraction causes the |
| // |empty_reporting_fraction_config| to have no pageload_metrics_config() |
| // set. |
| ClientConfig empty_reporting_fraction_config = CreateConfig( |
| kSuccessSessionKey, kConfigRefreshDurationSeconds, 0, |
| ProxyServer_ProxyScheme_HTTPS, "origin.net", 443, ProxyServer::CORE, |
| ProxyServer_ProxyScheme_HTTP, "origin.net", 0, |
| ProxyServer::UNSPECIFIED_TYPE, -1.0f, false); |
| empty_reporting_fraction_encoded_config_ = |
| EncodeConfig(empty_reporting_fraction_config); |
| |
| ClientConfig half_reporting_fraction_config = CreateConfig( |
| kSuccessSessionKey, kConfigRefreshDurationSeconds, 0, |
| ProxyServer_ProxyScheme_HTTPS, "origin.net", 443, ProxyServer::CORE, |
| ProxyServer_ProxyScheme_HTTP, "origin.net", 0, |
| ProxyServer::UNSPECIFIED_TYPE, 0.5f, false); |
| half_reporting_fraction_encoded_config_ = |
| EncodeConfig(half_reporting_fraction_config); |
| |
| ClientConfig ignore_black_list_config = CreateConfig( |
| kSuccessSessionKey, kConfigRefreshDurationSeconds, 0, |
| ProxyServer_ProxyScheme_HTTPS, "origin.net", 443, ProxyServer::CORE, |
| ProxyServer_ProxyScheme_HTTP, "origin.net", 0, |
| ProxyServer::UNSPECIFIED_TYPE, 0.5f, true); |
| ignore_black_list_encoded_config_ = EncodeConfig(ignore_black_list_config); |
| |
| ClientConfig no_proxies_config; |
| no_proxies_config.set_session_key(kSuccessSessionKey); |
| no_proxies_config.mutable_refresh_duration()->set_seconds( |
| kConfigRefreshDurationSeconds); |
| no_proxies_config.mutable_refresh_duration()->set_nanos(0); |
| no_proxies_config_ = EncodeConfig(no_proxies_config); |
| |
| success_reads_[0] = net::MockRead("HTTP/1.1 200 OK\r\n\r\n"); |
| success_reads_[1] = |
| net::MockRead(net::ASYNC, config_.c_str(), config_.length()); |
| success_reads_[2] = net::MockRead(net::SYNCHRONOUS, net::OK); |
| |
| previous_success_reads_[0] = net::MockRead("HTTP/1.1 200 OK\r\n\r\n"); |
| previous_success_reads_[1] = net::MockRead( |
| net::ASYNC, previous_config_.c_str(), previous_config_.length()); |
| previous_success_reads_[2] = net::MockRead(net::SYNCHRONOUS, net::OK); |
| |
| not_found_reads_[0] = net::MockRead("HTTP/1.1 404 Not found\r\n\r\n"); |
| not_found_reads_[1] = net::MockRead(net::SYNCHRONOUS, net::OK); |
| } |
| |
| void SetDataReductionProxyEnabled(bool enabled, bool secure_proxy_allowed) { |
| test_context_->config()->UpdateConfigForTesting(enabled, |
| secure_proxy_allowed, true); |
| } |
| |
| void ResetBackoffEntryReleaseTime() { |
| config_client()->SetCustomReleaseTime(base::TimeTicks::UnixEpoch()); |
| } |
| |
| void VerifyRemoteSuccess(bool expect_secure_proxies) { |
| std::vector<DataReductionProxyServer> expected_http_proxies; |
| if (expect_secure_proxies) { |
| expected_http_proxies.push_back(DataReductionProxyServer( |
| net::ProxyServer::FromURI(kSuccessOrigin, |
| net::ProxyServer::SCHEME_HTTP), |
| ProxyServer::CORE)); |
| } |
| expected_http_proxies.push_back(DataReductionProxyServer( |
| net::ProxyServer::FromURI(kSuccessFallback, |
| net::ProxyServer::SCHEME_HTTP), |
| ProxyServer::UNSPECIFIED_TYPE)); |
| |
| EXPECT_EQ(base::TimeDelta::FromSeconds(kConfigRefreshDurationSeconds), |
| config_client()->GetDelay()); |
| EXPECT_EQ(DataReductionProxyServer::ConvertToNetProxyServers( |
| expected_http_proxies), |
| GetConfiguredProxiesForHttp()); |
| EXPECT_EQ(kSuccessSessionKey, request_options()->GetSecureSession()); |
| // The config should be persisted on the pref. |
| EXPECT_EQ(encoded_config(), persisted_config()); |
| EXPECT_EQ(0.5f, pingback_reporting_fraction()); |
| |
| // Verify that the data reduction proxy servers are correctly set. |
| // The first proxy must have type CORE. The second proxy must have type |
| // UNSPECIFIED_TYPE since these are the types specified in the encoded |
| // configs. |
| ASSERT_EQ( |
| 2U, test_context_->mutable_config_values()->proxies_for_http().size()); |
| EXPECT_TRUE(test_context_->mutable_config_values() |
| ->proxies_for_http() |
| .at(0) |
| .IsCoreProxy()); |
| EXPECT_FALSE(test_context_->mutable_config_values() |
| ->proxies_for_http() |
| .at(1) |
| .IsCoreProxy()); |
| EXPECT_TRUE(test_context_->mutable_config_values() |
| ->proxies_for_http() |
| .at(0) |
| .IsSecureProxy()); |
| EXPECT_FALSE(test_context_->mutable_config_values() |
| ->proxies_for_http() |
| .at(1) |
| .IsSecureProxy()); |
| } |
| |
| void VerifyRemoteSuccessWithOldConfig() { |
| std::vector<DataReductionProxyServer> expected_http_proxies; |
| expected_http_proxies.push_back(DataReductionProxyServer( |
| net::ProxyServer::FromURI(kOldSuccessOrigin, |
| net::ProxyServer::SCHEME_HTTP), |
| ProxyServer::CORE)); |
| expected_http_proxies.push_back(DataReductionProxyServer( |
| net::ProxyServer::FromURI(kOldSuccessFallback, |
| net::ProxyServer::SCHEME_HTTP), |
| ProxyServer::UNSPECIFIED_TYPE)); |
| |
| EXPECT_EQ(base::TimeDelta::FromSeconds(kConfigRefreshDurationSeconds), |
| config_client()->GetDelay()); |
| EXPECT_EQ(DataReductionProxyServer::ConvertToNetProxyServers( |
| expected_http_proxies), |
| GetConfiguredProxiesForHttp()); |
| EXPECT_EQ(kOldSuccessSessionKey, request_options()->GetSecureSession()); |
| |
| // Verify that the data reduction proxy servers are correctly set. |
| // The first proxy must have type CORE. The second proxy must have type |
| // UNSPECIFIED_TYPE since these are the types specified in the encoded |
| // configs. |
| ASSERT_EQ( |
| 2U, test_context_->mutable_config_values()->proxies_for_http().size()); |
| EXPECT_TRUE(test_context_->mutable_config_values() |
| ->proxies_for_http() |
| .at(0) |
| .IsCoreProxy()); |
| EXPECT_FALSE(test_context_->mutable_config_values() |
| ->proxies_for_http() |
| .at(1) |
| .IsCoreProxy()); |
| } |
| |
| void VerifySuccessWithLoadedConfig(bool expect_secure_proxies) { |
| std::vector<DataReductionProxyServer> expected_http_proxies; |
| if (expect_secure_proxies) { |
| expected_http_proxies.push_back(DataReductionProxyServer( |
| net::ProxyServer::FromURI(kPersistedOrigin, |
| net::ProxyServer::SCHEME_HTTP), |
| ProxyServer::CORE)); |
| } |
| expected_http_proxies.push_back(DataReductionProxyServer( |
| net::ProxyServer::FromURI(kPersistedFallback, |
| net::ProxyServer::SCHEME_HTTP), |
| ProxyServer::UNSPECIFIED_TYPE)); |
| EXPECT_EQ(DataReductionProxyServer::ConvertToNetProxyServers( |
| expected_http_proxies), |
| GetConfiguredProxiesForHttp()); |
| EXPECT_EQ(kPersistedSessionKey, request_options()->GetSecureSession()); |
| |
| // Verify that the data reduction proxy servers are correctly set. |
| // The first proxy must have type CORE. The second proxy must have type |
| // UNSPECIFIED_TYPE since these are the types specified in the encoded |
| // configs. |
| ASSERT_EQ( |
| 2U, test_context_->mutable_config_values()->proxies_for_http().size()); |
| EXPECT_TRUE(test_context_->mutable_config_values() |
| ->proxies_for_http() |
| .at(0) |
| .IsCoreProxy()); |
| EXPECT_FALSE(test_context_->mutable_config_values() |
| ->proxies_for_http() |
| .at(1) |
| .IsCoreProxy()); |
| } |
| |
| TestDataReductionProxyConfigServiceClient* config_client() { |
| return test_context_->test_config_client(); |
| } |
| |
| DataReductionProxyConfigurator* configurator() { |
| return test_context_->configurator(); |
| } |
| |
| TestDataReductionProxyConfig* config() { return test_context_->config(); } |
| |
| MockDataReductionProxyRequestOptions* request_options() { |
| return test_context_->mock_request_options(); |
| } |
| |
| std::vector<net::ProxyServer> GetConfiguredProxiesForHttp() const { |
| return test_context_->GetConfiguredProxiesForHttp(); |
| } |
| |
| float pingback_reporting_fraction() const { |
| return test_context_->io_data()->pingback_reporting_fraction(); |
| } |
| |
| bool ignore_blacklist() const { |
| return test_context_->io_data()->ignore_blacklist(); |
| } |
| |
| void RunUntilIdle() { |
| test_context_->RunUntilIdle(); |
| } |
| |
| void AddMockSuccess() { |
| mock_responses_.push_back({success_response(), net::HTTP_OK}); |
| } |
| |
| void AddMockPreviousSuccess() { |
| mock_responses_.push_back({previous_success_response(), net::HTTP_OK}); |
| } |
| |
| void AddMockFailure() { |
| mock_responses_.push_back({std::string(), net::HTTP_NOT_FOUND}); |
| } |
| |
| std::string persisted_config() const { |
| return test_context_->pref_service()->GetString( |
| prefs::kDataReductionProxyConfig); |
| } |
| |
| base::Time persisted_config_retrieval_time() const { |
| return base::Time() + |
| base::TimeDelta::FromMicroseconds( |
| test_context_->pref_service()->GetInt64( |
| prefs::kDataReductionProxyLastConfigRetrievalTime)); |
| } |
| |
| const std::string& success_response() const { return config_; } |
| |
| const std::string& encoded_config() const { return encoded_config_; } |
| |
| const std::string& previous_success_response() const { |
| return previous_config_; |
| } |
| const std::string& empty_reporting_fraction_encoded_config() const { |
| return empty_reporting_fraction_encoded_config_; |
| } |
| const std::string& one_reporting_fraction_encoded_config() const { |
| return one_reporting_fraction_encoded_config_; |
| } |
| const std::string& zero_reporting_fraction_encoded_config() const { |
| return zero_reporting_fraction_encoded_config_; |
| } |
| const std::string& half_reporting_fraction_encoded_config() const { |
| return half_reporting_fraction_encoded_config_; |
| } |
| const std::string& ignore_black_list_encoded_config() const { |
| return ignore_black_list_encoded_config_; |
| } |
| const std::string& no_proxies_config() const { return no_proxies_config_; } |
| |
| const std::string& loaded_config() const { return loaded_config_; } |
| |
| net::TestURLRequestContext* test_url_request_context() const { |
| return context_.get(); |
| } |
| |
| private: |
| base::test::ScopedTaskEnvironment task_environment_{ |
| base::test::ScopedTaskEnvironment::MainThreadType::IO}; |
| std::unique_ptr<net::TestURLRequestContext> context_; |
| std::unique_ptr<net::MockClientSocketFactory> mock_socket_factory_; |
| |
| network::TestURLLoaderFactory test_url_loader_factory_; |
| scoped_refptr<network::SharedURLLoaderFactory> |
| test_shared_url_loader_factory_; |
| |
| protected: |
| std::unique_ptr<DataReductionProxyTestContext> test_context_; |
| |
| private: |
| std::unique_ptr<DataReductionProxyRequestOptions> request_options_; |
| |
| std::unique_ptr<DataReductionProxyDelegate> delegate_; |
| |
| // A configuration from the current remote request. The encoded version is |
| // also stored. |
| std::string config_; |
| std::string encoded_config_; |
| |
| // A configuration from a previous remote request. |
| std::string previous_config_; |
| |
| // An encoded config that represents a previously saved configuration. |
| std::string loaded_config_; |
| |
| // A configuration where the pingback reporting fraction is not set. |
| std::string empty_reporting_fraction_encoded_config_; |
| |
| // A configuration where the pingback reporting fraction is set to 1.0f. |
| std::string one_reporting_fraction_encoded_config_; |
| |
| // A configuration where the pingback reporting fraction is set to 0.0f. |
| std::string zero_reporting_fraction_encoded_config_; |
| |
| // A configuration where the pingback reporting fraction is set to 0.5f. |
| std::string half_reporting_fraction_encoded_config_; |
| |
| // A configuration where the black list rules are ignored. |
| std::string ignore_black_list_encoded_config_; |
| |
| // A configuration where no proxies are configured. |
| std::string no_proxies_config_; |
| |
| // Mock socket data. |
| std::vector<std::unique_ptr<net::SocketDataProvider>> socket_data_providers_; |
| |
| // Mock socket reads. |
| net::MockRead success_reads_[3]; |
| net::MockRead previous_success_reads_[3]; |
| net::MockRead not_found_reads_[2]; |
| |
| struct MockResponse { |
| std::string response; |
| net::HttpStatusCode http_code; |
| }; |
| std::vector<MockResponse> mock_responses_; |
| size_t curr_mock_response_index_ = 0; |
| |
| std::unique_ptr<net::URLRequestContextStorage> context_storage_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DataReductionProxyConfigServiceClientTest); |
| }; |
| |
| // Tests that backoff values increases with every time config cannot be fetched. |
| TEST_F(DataReductionProxyConfigServiceClientTest, EnsureBackoff) { |
| Init(true); |
| // Use a local/static config. |
| base::HistogramTester histogram_tester; |
| AddMockFailure(); |
| AddMockFailure(); |
| AddMockSuccess(); |
| |
| EXPECT_EQ(0, config_client()->failed_attempts_before_success()); |
| |
| SetDataReductionProxyEnabled(true, true); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| |
| // First attempt should be unsuccessful. |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(20), config_client()->GetDelay()); |
| |
| #if defined(OS_ANDROID) |
| EXPECT_FALSE(config_client()->foreground_fetch_pending()); |
| #endif |
| |
| // Second attempt should be unsuccessful and backoff time should increase. |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(40), config_client()->GetDelay()); |
| EXPECT_TRUE(persisted_config().empty()); |
| EXPECT_TRUE(persisted_config_retrieval_time().is_null()); |
| |
| #if defined(OS_ANDROID) |
| EXPECT_FALSE(config_client()->foreground_fetch_pending()); |
| #endif |
| |
| EXPECT_EQ(2, config_client()->failed_attempts_before_success()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.FetchFailedAttemptsBeforeSuccess", 0); |
| } |
| |
| // Tests that the config is read successfully on the first attempt. |
| TEST_F(DataReductionProxyConfigServiceClientTest, RemoteConfigSuccess) { |
| Init(true); |
| AddMockSuccess(); |
| SetDataReductionProxyEnabled(true, true); |
| EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| VerifyRemoteSuccess(true); |
| EXPECT_FALSE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| #if defined(OS_ANDROID) |
| EXPECT_FALSE(config_client()->foreground_fetch_pending()); |
| #endif |
| } |
| |
| // Tests that the config is read successfully on the first attempt, and secure |
| // proxies are not used if the secure check failed. |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| RemoteConfigSuccessWithSecureCheckFail) { |
| Init(true); |
| AddMockSuccess(); |
| SetDataReductionProxyEnabled(true, false); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| VerifyRemoteSuccess(false); |
| #if defined(OS_ANDROID) |
| EXPECT_FALSE(config_client()->foreground_fetch_pending()); |
| #endif |
| } |
| |
| // Tests that the config is read successfully on the first attempt, and secure |
| // proxies are not used if the secure proxy check fails later after some time. |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| RemoteConfigSuccessWithDelayedSecureCheckFail) { |
| Init(true); |
| AddMockSuccess(); |
| SetDataReductionProxyEnabled(true, true); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| VerifyRemoteSuccess(true); |
| #if defined(OS_ANDROID) |
| EXPECT_FALSE(config_client()->foreground_fetch_pending()); |
| #endif |
| |
| std::vector<DataReductionProxyServer> http_proxies; |
| http_proxies.push_back(DataReductionProxyServer( |
| net::ProxyServer::FromURI(kSuccessOrigin, net::ProxyServer::SCHEME_HTTP), |
| ProxyServer::CORE)); |
| http_proxies.push_back(DataReductionProxyServer( |
| net::ProxyServer::FromURI(kSuccessFallback, |
| net::ProxyServer::SCHEME_HTTP), |
| ProxyServer::CORE)); |
| |
| // Secure check failed. |
| TestingPrefServiceSimple test_prefs; |
| test_prefs.registry()->RegisterDictionaryPref(prefs::kNetworkProperties); |
| NetworkPropertiesManager manager(base::DefaultClock::GetInstance(), |
| &test_prefs, |
| base::ThreadTaskRunnerHandle::Get()); |
| manager.SetIsSecureProxyDisallowedByCarrier(true); |
| configurator()->Enable(manager, http_proxies); |
| VerifyRemoteSuccess(false); |
| } |
| |
| // Tests that the config is read successfully on the second attempt. |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| RemoteConfigSuccessAfterFailure) { |
| Init(true); |
| base::HistogramTester histogram_tester; |
| |
| AddMockFailure(); |
| AddMockSuccess(); |
| |
| EXPECT_EQ(0, config_client()->failed_attempts_before_success()); |
| |
| SetDataReductionProxyEnabled(true, true); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| |
| // First attempt should be unsuccessful. |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| EXPECT_EQ(1, config_client()->failed_attempts_before_success()); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(20), config_client()->GetDelay()); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| EXPECT_TRUE(request_options()->GetSecureSession().empty()); |
| |
| // Second attempt should be successful. |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| VerifyRemoteSuccess(true); |
| EXPECT_EQ(0, config_client()->failed_attempts_before_success()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "DataReductionProxy.ConfigService.FetchFailedAttemptsBeforeSuccess", 1, |
| 1); |
| } |
| |
| // Verifies that the config is fetched successfully after IP address changes. |
| TEST_F(DataReductionProxyConfigServiceClientTest, OnIPAddressChange) { |
| Init(true); |
| const struct { |
| bool secure_proxies_allowed; |
| } tests[] = { |
| { |
| true, |
| }, |
| { |
| false, |
| }, |
| }; |
| |
| for (size_t i = 0; i < base::size(tests); ++i) { |
| SetDataReductionProxyEnabled(true, tests[i].secure_proxies_allowed); |
| config_client()->RetrieveConfig(); |
| |
| const int kFailureCount = 5; |
| |
| std::vector<std::unique_ptr<net::SocketDataProvider>> socket_data_providers; |
| for (int i = 0; i < kFailureCount; ++i) { |
| AddMockFailure(); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| } |
| |
| // Verify that the backoff increased exponentially. |
| EXPECT_EQ(base::TimeDelta::FromSeconds(320), |
| config_client()->GetDelay()); // 320 = 20 * 2^(5-1) |
| EXPECT_EQ(kFailureCount, config_client()->GetBackoffErrorCount()); |
| |
| // IP address change should reset. |
| config_client()->OnConnectionChanged( |
| network::mojom::ConnectionType::CONNECTION_WIFI); |
| EXPECT_EQ(0, config_client()->GetBackoffErrorCount()); |
| EXPECT_EQ(i == 0, persisted_config().empty()); |
| EXPECT_EQ(i == 0, persisted_config_retrieval_time().is_null()); |
| ResetBackoffEntryReleaseTime(); |
| |
| // Fetching the config should be successful. |
| AddMockSuccess(); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| VerifyRemoteSuccess(tests[i].secure_proxies_allowed); |
| |
| // Disable in preparation for the next test. |
| configurator()->Disable(); |
| } |
| } |
| |
| // Verifies that the config is fetched successfully after IP address changes, |
| // and secure proxies are not used if the secure proxy check fails later after |
| // some time. |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| OnIPAddressChangeDelayedSecureProxyCheckFail) { |
| Init(true); |
| |
| SetDataReductionProxyEnabled(true, true); |
| config_client()->RetrieveConfig(); |
| |
| const int kFailureCount = 5; |
| |
| std::vector<std::unique_ptr<net::SocketDataProvider>> socket_data_providers; |
| for (int i = 0; i < kFailureCount; ++i) { |
| AddMockFailure(); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| } |
| |
| // Verify that the backoff increased exponentially. |
| EXPECT_EQ(base::TimeDelta::FromSeconds(320), |
| config_client()->GetDelay()); // 320 = 20 * 2^(5-1) |
| EXPECT_EQ(kFailureCount, config_client()->GetBackoffErrorCount()); |
| |
| // IP address change should reset. |
| config_client()->OnConnectionChanged( |
| network::mojom::ConnectionType::CONNECTION_WIFI); |
| EXPECT_EQ(0, config_client()->GetBackoffErrorCount()); |
| EXPECT_TRUE(persisted_config().empty()); |
| EXPECT_TRUE(persisted_config_retrieval_time().is_null()); |
| ResetBackoffEntryReleaseTime(); |
| |
| // Fetching the config should be successful. |
| AddMockSuccess(); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| VerifyRemoteSuccess(true); |
| |
| std::vector<DataReductionProxyServer> http_proxies; |
| http_proxies.push_back(DataReductionProxyServer( |
| net::ProxyServer::FromURI(kSuccessOrigin, net::ProxyServer::SCHEME_HTTP), |
| ProxyServer::CORE)); |
| http_proxies.push_back(DataReductionProxyServer( |
| net::ProxyServer::FromURI(kSuccessFallback, |
| net::ProxyServer::SCHEME_HTTP), |
| ProxyServer::CORE)); |
| |
| // Secure check failed. |
| TestingPrefServiceSimple test_prefs; |
| test_prefs.registry()->RegisterDictionaryPref(prefs::kNetworkProperties); |
| NetworkPropertiesManager manager(base::DefaultClock::GetInstance(), |
| &test_prefs, |
| base::ThreadTaskRunnerHandle::Get()); |
| manager.SetIsSecureProxyDisallowedByCarrier(true); |
| configurator()->Enable(manager, http_proxies); |
| VerifyRemoteSuccess(false); |
| } |
| |
| // Verifies that fetching the remote config has no effect if the config client |
| // is disabled. |
| TEST_F(DataReductionProxyConfigServiceClientTest, OnIPAddressChangeDisabled) { |
| Init(true); |
| config_client()->SetEnabled(false); |
| SetDataReductionProxyEnabled(true, true); |
| config_client()->RetrieveConfig(); |
| EXPECT_TRUE(request_options()->GetSecureSession().empty()); |
| |
| enum : int { kFailureCount = 5 }; |
| |
| for (int i = 0; i < kFailureCount; ++i) { |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| EXPECT_TRUE(request_options()->GetSecureSession().empty()); |
| } |
| |
| EXPECT_EQ(0, config_client()->GetBackoffErrorCount()); |
| config_client()->OnConnectionChanged( |
| network::mojom::ConnectionType::CONNECTION_WIFI); |
| EXPECT_EQ(0, config_client()->GetBackoffErrorCount()); |
| |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| |
| EXPECT_TRUE(request_options()->GetSecureSession().empty()); |
| } |
| |
| // Verifies the correctness of AuthFailure when the session key in the request |
| // headers matches the currrent session key. |
| TEST_F(DataReductionProxyConfigServiceClientTest, AuthFailure) { |
| Init(true); |
| net::HttpRequestHeaders request_headers; |
| request_headers.SetHeader( |
| "chrome-proxy", "something=something_else, s=" + |
| std::string(kOldSuccessSessionKey) + ", key=value"); |
| |
| base::HistogramTester histogram_tester; |
| AddMockPreviousSuccess(); |
| AddMockSuccess(); |
| AddMockPreviousSuccess(); |
| |
| SetDataReductionProxyEnabled(true, true); |
| EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.AuthExpired", 0); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| // First remote config should be fetched. |
| VerifyRemoteSuccessWithOldConfig(); |
| EXPECT_FALSE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| EXPECT_EQ(kOldSuccessSessionKey, request_options()->GetSecureSession()); |
| EXPECT_EQ(0, config_client()->GetBackoffErrorCount()); |
| histogram_tester.ExpectUniqueSample( |
| "DataReductionProxy.ConfigService.AuthExpired", false, 1); |
| |
| // Trigger an auth failure. |
| scoped_refptr<net::HttpResponseHeaders> parsed(new net::HttpResponseHeaders( |
| "HTTP/1.1 407 Proxy Authentication Required\n")); |
| net::ProxyServer origin = net::ProxyServer::FromURI( |
| kOldSuccessOrigin, net::ProxyServer::SCHEME_HTTP); |
| // Calling ShouldRetryDueToAuthFailure should trigger fetching of remote |
| // config. |
| net::LoadTimingInfo load_timing_info; |
| load_timing_info.request_start = |
| base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1); |
| load_timing_info.send_start = load_timing_info.request_start; |
| EXPECT_TRUE(config_client()->ShouldRetryDueToAuthFailure( |
| request_headers, parsed.get(), origin, load_timing_info)); |
| EXPECT_EQ(1, config_client()->GetBackoffErrorCount()); |
| EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| |
| // Persisted config on pref should be cleared. |
| EXPECT_TRUE(persisted_config().empty()); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", false, 1); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", true, 1); |
| RunUntilIdle(); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.AuthFailure.LatencyPenalty", 1); |
| |
| // Second remote config should be fetched. |
| VerifyRemoteSuccess(true); |
| |
| // Trigger a second auth failure. |
| origin = |
| net::ProxyServer::FromURI(kSuccessOrigin, net::ProxyServer::SCHEME_HTTP); |
| |
| EXPECT_EQ(kSuccessSessionKey, request_options()->GetSecureSession()); |
| request_headers.SetHeader( |
| "chrome-proxy", "something=something_else, s=" + |
| std::string(kSuccessSessionKey) + ", key=value"); |
| // Calling ShouldRetryDueToAuthFailure should trigger fetching of remote |
| // config. |
| EXPECT_TRUE(config_client()->ShouldRetryDueToAuthFailure( |
| request_headers, parsed.get(), origin, load_timing_info)); |
| EXPECT_EQ(2, config_client()->GetBackoffErrorCount()); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", false, 2); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", true, 2); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.AuthFailure.LatencyPenalty", 2); |
| RunUntilIdle(); |
| // Third remote config should be fetched. |
| VerifyRemoteSuccessWithOldConfig(); |
| |
| histogram_tester.ExpectUniqueSample( |
| "DataReductionProxy.ConfigService.AuthExpiredSessionKey", |
| 1 /* AUTH_EXPIRED_SESSION_KEY_MATCH */, 2); |
| } |
| |
| // Verifies that the persisted client config is fetched from the disk at |
| // startup, and that the persisted client config is used only if it is less |
| // than 24 hours old. |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| ValidatePersistedClientConfig) { |
| Init(true); |
| SetDataReductionProxyEnabled(true, true); |
| |
| const struct { |
| base::Optional<base::TimeDelta> staleness; |
| bool expect_valid_config; |
| } tests[] = { |
| { |
| base::nullopt, true, |
| }, |
| { |
| base::TimeDelta::FromHours(25), false, |
| }, |
| { |
| base::TimeDelta::FromHours(1), true, |
| }, |
| }; |
| |
| for (const auto& test : tests) { |
| base::HistogramTester histogram_tester; |
| // Reset the state. |
| test_context_->io_data()->test_request_options()->Invalidate(); |
| test_context_->pref_service()->ClearPref( |
| prefs::kDataReductionProxyLastConfigRetrievalTime); |
| RunUntilIdle(); |
| EXPECT_TRUE(request_options()->GetSecureSession().empty()); |
| |
| // Apply a valid config. This should be stored on the disk. |
| AddMockSuccess(); |
| config_client()->RetrieveConfig(); |
| |
| RunUntilIdle(); |
| EXPECT_EQ(kSuccessSessionKey, request_options()->GetSecureSession()); |
| |
| if (test.staleness) { |
| base::TimeDelta last_config_retrieval_time = |
| base::Time::Now() - base::Time() - test.staleness.value(); |
| |
| test_context_->pref_service()->SetInt64( |
| prefs::kDataReductionProxyLastConfigRetrievalTime, |
| last_config_retrieval_time.InMicroseconds()); |
| test_context_->pref_service()->SchedulePendingLossyWrites(); |
| test_context_->pref_service()->CommitPendingWrite(); |
| } |
| |
| RunUntilIdle(); |
| EXPECT_FALSE(persisted_config().empty()); |
| EXPECT_FALSE(persisted_config_retrieval_time().is_null()); |
| |
| // Simulate startup which should cause the empty persisted client config to |
| // be read from the disk. |
| config_client()->SetRemoteConfigApplied(false); |
| test_context_->io_data()->test_request_options()->Invalidate(); |
| test_context_->data_reduction_proxy_service()->SetIOData( |
| test_context_->io_data()->GetWeakPtr()); |
| RunUntilIdle(); |
| EXPECT_NE(test.expect_valid_config, |
| request_options()->GetSecureSession().empty()); |
| if (test.expect_valid_config) { |
| EXPECT_EQ(kSuccessSessionKey, request_options()->GetSecureSession()); |
| } |
| } |
| } |
| |
| // Verifies that a new config is not fetched due to auth failure while a |
| // previous client config fetch triggered due to auth failure is already in |
| // progress. |
| TEST_F(DataReductionProxyConfigServiceClientTest, MultipleAuthFailures) { |
| Init(true); |
| net::HttpRequestHeaders request_headers; |
| request_headers.SetHeader( |
| "chrome-proxy", "something=something_else, s=" + |
| std::string(kOldSuccessSessionKey) + ", key=value"); |
| |
| base::HistogramTester histogram_tester; |
| AddMockPreviousSuccess(); |
| AddMockSuccess(); |
| |
| SetDataReductionProxyEnabled(true, true); |
| EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.AuthExpired", 0); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| // First remote config should be fetched. |
| VerifyRemoteSuccessWithOldConfig(); |
| EXPECT_FALSE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| EXPECT_EQ(kOldSuccessSessionKey, request_options()->GetSecureSession()); |
| EXPECT_EQ(0, config_client()->GetBackoffErrorCount()); |
| histogram_tester.ExpectUniqueSample( |
| "DataReductionProxy.ConfigService.AuthExpired", false, 1); |
| |
| // Trigger an auth failure. |
| scoped_refptr<net::HttpResponseHeaders> parsed(new net::HttpResponseHeaders( |
| "HTTP/1.1 407 Proxy Authentication Required\n")); |
| net::ProxyServer origin = net::ProxyServer::FromURI( |
| kOldSuccessOrigin, net::ProxyServer::SCHEME_HTTP); |
| // Calling ShouldRetryDueToAuthFailure should trigger fetching of remote |
| // config. |
| net::LoadTimingInfo load_timing_info; |
| load_timing_info.request_start = |
| base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1); |
| load_timing_info.send_start = load_timing_info.request_start; |
| EXPECT_TRUE(config_client()->ShouldRetryDueToAuthFailure( |
| request_headers, parsed.get(), origin, load_timing_info)); |
| EXPECT_EQ(1, config_client()->GetBackoffErrorCount()); |
| EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| |
| // Persisted config on pref should be cleared. |
| EXPECT_TRUE(persisted_config().empty()); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", false, 1); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", true, 1); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.AuthFailure.LatencyPenalty", 1); |
| |
| // Trigger a second auth failure. |
| EXPECT_EQ(std::string(), request_options()->GetSecureSession()); |
| request_headers.SetHeader( |
| "chrome-proxy", "something=something_else, s=" + |
| std::string(kSuccessSessionKey) + ", key=value"); |
| // Calling ShouldRetryDueToAuthFailure should trigger fetching of remote |
| // config. |
| EXPECT_TRUE(config_client()->ShouldRetryDueToAuthFailure( |
| request_headers, parsed.get(), origin, load_timing_info)); |
| EXPECT_EQ(1, config_client()->GetBackoffErrorCount()); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", false, 1); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", true, 1); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.AuthFailure.LatencyPenalty", 1); |
| |
| RunUntilIdle(); |
| VerifyRemoteSuccess(true); |
| |
| // Config should be fetched successfully. |
| EXPECT_FALSE(persisted_config().empty()); |
| EXPECT_FALSE(persisted_config_retrieval_time().is_null()); |
| // The persisted config retrieval time should be very recent (less than 2 |
| // minutes old). 2 minutes of buffer is allowed to account for delays due to |
| // delays in running tests. |
| EXPECT_LE(persisted_config_retrieval_time(), base::Time::Now()); |
| EXPECT_GE(persisted_config_retrieval_time() + base::TimeDelta::FromMinutes(2), |
| base::Time::Now()); |
| |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpiredSessionKey", |
| 0 /* AUTH_EXPIRED_SESSION_KEY_MISMATCH */, 1); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpiredSessionKey", |
| 1 /* AUTH_EXPIRED_SESSION_KEY_MATCH */, 1); |
| |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", false, 2); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", true, 1); |
| |
| DCHECK(test_context_->data_reduction_proxy_service()); |
| test_context_->io_data()->test_request_options()->Invalidate(); |
| test_context_->data_reduction_proxy_service()->SetIOData( |
| test_context_->io_data()->GetWeakPtr()); |
| test_context_->RunUntilIdle(); |
| } |
| |
| // Verifies that a new config is not fetched due to auth failure while a |
| // previous client config fetch triggered due to IP address changeis already |
| // in progress. |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| IPAddressChangeWithAuthFailure) { |
| Init(true); |
| net::HttpRequestHeaders request_headers; |
| request_headers.SetHeader( |
| "chrome-proxy", "something=something_else, s=" + |
| std::string(kOldSuccessSessionKey) + ", key=value"); |
| |
| base::HistogramTester histogram_tester; |
| AddMockPreviousSuccess(); |
| AddMockSuccess(); |
| |
| SetDataReductionProxyEnabled(true, true); |
| EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.AuthExpired", 0); |
| config_client()->RetrieveConfig(); |
| |
| RunUntilIdle(); |
| // First remote config should be fetched. |
| VerifyRemoteSuccessWithOldConfig(); |
| EXPECT_FALSE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| EXPECT_EQ(kOldSuccessSessionKey, request_options()->GetSecureSession()); |
| EXPECT_EQ(0, config_client()->GetBackoffErrorCount()); |
| histogram_tester.ExpectUniqueSample( |
| "DataReductionProxy.ConfigService.AuthExpired", false, 1); |
| |
| // Trigger IP address change again. |
| AddMockPreviousSuccess(); |
| AddMockPreviousSuccess(); |
| |
| SetDataReductionProxyEnabled(true, true); |
| EXPECT_FALSE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| config_client()->RetrieveConfig(); |
| |
| // Trigger an auth failure. |
| scoped_refptr<net::HttpResponseHeaders> parsed(new net::HttpResponseHeaders( |
| "HTTP/1.1 407 Proxy Authentication Required\n")); |
| net::ProxyServer origin = net::ProxyServer::FromURI( |
| kOldSuccessOrigin, net::ProxyServer::SCHEME_HTTP); |
| // Calling ShouldRetryDueToAuthFailure should trigger fetching of remote |
| // config. |
| net::LoadTimingInfo load_timing_info; |
| load_timing_info.request_start = |
| base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1); |
| load_timing_info.send_start = load_timing_info.request_start; |
| EXPECT_TRUE(config_client()->ShouldRetryDueToAuthFailure( |
| request_headers, parsed.get(), origin, load_timing_info)); |
| EXPECT_EQ(1, config_client()->GetBackoffErrorCount()); |
| EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| |
| // Persisted config on pref should be cleared. |
| EXPECT_TRUE(persisted_config().empty()); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", false, 1); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", true, 1); |
| |
| EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| // Persisted config on pref should be cleared. |
| EXPECT_TRUE(persisted_config().empty()); |
| |
| // Config should be fetched now. |
| RunUntilIdle(); |
| VerifyRemoteSuccess(true); |
| |
| EXPECT_FALSE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| // Persisted config on pref should be cleared. |
| EXPECT_FALSE(persisted_config().empty()); |
| EXPECT_FALSE(persisted_config_retrieval_time().is_null()); |
| EXPECT_LE(persisted_config_retrieval_time(), base::Time::Now()); |
| EXPECT_GE(persisted_config_retrieval_time() + base::TimeDelta::FromMinutes(2), |
| base::Time::Now()); |
| EXPECT_EQ(kSuccessSessionKey, request_options()->GetSecureSession()); |
| EXPECT_EQ(0, config_client()->GetBackoffErrorCount()); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", false, 2); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", true, 1); |
| } |
| |
| // Verifies the correctness of AuthFailure when the session key in the request |
| // headers do not match the currrent session key. |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| AuthFailureWithRequestHeaders) { |
| Init(true); |
| net::HttpRequestHeaders request_headers; |
| const char kSessionKeyRequestHeaders[] = "123"; |
| ASSERT_NE(kOldSuccessSessionKey, kSessionKeyRequestHeaders); |
| request_headers.SetHeader("chrome-proxy", |
| "s=" + std::string(kSessionKeyRequestHeaders)); |
| base::HistogramTester histogram_tester; |
| AddMockPreviousSuccess(); |
| AddMockSuccess(); |
| AddMockPreviousSuccess(); |
| |
| SetDataReductionProxyEnabled(true, true); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.AuthExpired", 0); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| // First remote config should be fetched. |
| VerifyRemoteSuccessWithOldConfig(); |
| EXPECT_EQ(0, config_client()->GetBackoffErrorCount()); |
| histogram_tester.ExpectUniqueSample( |
| "DataReductionProxy.ConfigService.AuthExpired", false, 1); |
| |
| // Trigger an auth failure. |
| scoped_refptr<net::HttpResponseHeaders> parsed(new net::HttpResponseHeaders( |
| "HTTP/1.1 407 Proxy Authentication Required\n")); |
| net::ProxyServer origin = net::ProxyServer::FromURI( |
| kOldSuccessOrigin, net::ProxyServer::SCHEME_HTTP); |
| // Calling ShouldRetryDueToAuthFailure should not trigger fetching of remote |
| // config since the session key in the request headers do not match the |
| // current session key, but the request should be retried. |
| net::LoadTimingInfo load_timing_info; |
| load_timing_info.request_start = |
| base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1); |
| load_timing_info.send_start = load_timing_info.request_start; |
| |
| EXPECT_TRUE(config_client()->ShouldRetryDueToAuthFailure( |
| request_headers, parsed.get(), origin, load_timing_info)); |
| EXPECT_EQ(0, config_client()->GetBackoffErrorCount()); |
| // Persisted config on pref should be cleared. |
| EXPECT_FALSE(persisted_config().empty()); |
| EXPECT_FALSE(persisted_config_retrieval_time().is_null()); |
| EXPECT_LE(persisted_config_retrieval_time(), base::Time::Now()); |
| EXPECT_GE(persisted_config_retrieval_time() + base::TimeDelta::FromMinutes(2), |
| base::Time::Now()); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", false, 1); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.ConfigService.AuthExpired", true, 0); |
| RunUntilIdle(); |
| EXPECT_EQ(kOldSuccessSessionKey, request_options()->GetSecureSession()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "DataReductionProxy.ConfigService.AuthExpiredSessionKey", |
| 0 /* AUTH_EXPIRED_SESSION_KEY_MISMATCH */, 1); |
| } |
| |
| // Verifies that requests that were not proxied through data saver proxy due |
| // to missing config are recorded properly. |
| TEST_F(DataReductionProxyConfigServiceClientTest, HTTPRequests) { |
| Init(false); |
| const struct { |
| std::string url; |
| bool enabled_by_user; |
| bool expect_histogram; |
| } tests[] = { |
| { |
| // Request should not be logged because data saver is disabled. |
| "http://www.one.example.com/", false, false, |
| }, |
| { |
| "http://www.two.example.com/", true, true, |
| }, |
| { |
| "https://www.three.example.com/", false, false, |
| }, |
| { |
| // Request should not be logged because request is HTTPS. |
| "https://www.four.example.com/", true, false, |
| }, |
| { |
| // Request to localhost should not be logged. |
| "http://127.0.0.1/", true, false, |
| }, |
| { |
| // Special use IPv4 address for testing purposes (RFC 5735). |
| "http://198.51.100.1/", true, true, |
| }, |
| }; |
| |
| for (size_t i = 0; i < base::size(tests); ++i) { |
| base::HistogramTester histogram_tester; |
| SetDataReductionProxyEnabled(tests[i].enabled_by_user, true); |
| |
| net::TestDelegate test_delegate; |
| |
| std::unique_ptr<net::URLRequest> request( |
| test_url_request_context()->CreateRequest( |
| GURL(tests[i].url), net::IDLE, &test_delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| RunUntilIdle(); |
| |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.HTTPRequests", |
| tests[i].expect_histogram ? 1 : 0); |
| |
| if (tests[i].expect_histogram) { |
| histogram_tester.ExpectUniqueSample( |
| "DataReductionProxy.ConfigService.HTTPRequests", 0, 1); |
| } |
| } |
| } |
| |
| // Tests that the config is overriden by kDataReductionProxyServerClientConfig. |
| TEST_F(DataReductionProxyConfigServiceClientTest, ApplyClientConfigOverride) { |
| const std::string override_key = "OverrideSecureSession"; |
| std::string encoded_config; |
| ClientConfig config = CreateConfig( |
| override_key, kConfigRefreshDurationSeconds, 0, |
| ProxyServer_ProxyScheme_HTTPS, "origin.net", 443, ProxyServer::CORE, |
| ProxyServer_ProxyScheme_HTTP, "fallback.net", 80, |
| ProxyServer::UNSPECIFIED_TYPE, 0.5f, false); |
| config.SerializeToString(&encoded_config); |
| base::Base64Encode(encoded_config, &encoded_config); |
| |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| data_reduction_proxy::switches::kDataReductionProxyServerClientConfig, |
| encoded_config); |
| Init(true); |
| |
| AddMockSuccess(); |
| SetDataReductionProxyEnabled(true, true); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| // Make sure repeated fetches won't change the overridden config. |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| EXPECT_EQ(request_options()->GetSecureSession(), override_key); |
| } |
| |
| // Tests that remote config can be applied after the serialized config has |
| // been applied. |
| TEST_F(DataReductionProxyConfigServiceClientTest, ApplySerializedConfig) { |
| Init(true); |
| AddMockSuccess(); |
| |
| SetDataReductionProxyEnabled(true, true); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| config_client()->ApplySerializedConfig(loaded_config()); |
| VerifySuccessWithLoadedConfig(true); |
| EXPECT_TRUE(persisted_config().empty()); |
| |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| VerifyRemoteSuccess(true); |
| } |
| |
| // Tests that remote config can be applied after the serialized config has |
| // been applied. Verifies that if the secure transport is restricted, then the |
| // secure proxies are not used. |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| ApplySerializedConfigWithSecureTransportRestricted) { |
| Init(true); |
| |
| AddMockSuccess(); |
| |
| SetDataReductionProxyEnabled(true, false); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| config_client()->ApplySerializedConfig(loaded_config()); |
| VerifySuccessWithLoadedConfig(false); |
| EXPECT_TRUE(persisted_config().empty()); |
| |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| VerifyRemoteSuccess(false); |
| } |
| |
| // Tests that serialized config has no effect after the config has been |
| // retrieved successfully. |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| ApplySerializedConfigAfterReceipt) { |
| Init(true); |
| AddMockSuccess(); |
| |
| SetDataReductionProxyEnabled(true, true); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| EXPECT_TRUE(request_options()->GetSecureSession().empty()); |
| |
| // Retrieve the remote config. |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| VerifyRemoteSuccess(true); |
| |
| // ApplySerializedConfig should not have any effect since the remote config |
| // is already applied. |
| config_client()->ApplySerializedConfig(encoded_config()); |
| VerifyRemoteSuccess(true); |
| } |
| |
| // Tests that a local serialized config can be applied successfully if remote |
| // config has not been fetched so far. |
| TEST_F(DataReductionProxyConfigServiceClientTest, ApplySerializedConfigLocal) { |
| Init(true); |
| SetDataReductionProxyEnabled(true, true); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| EXPECT_TRUE(request_options()->GetSecureSession().empty()); |
| EXPECT_TRUE(persisted_config_retrieval_time().is_null()); |
| |
| // ApplySerializedConfig should apply the encoded config. |
| config_client()->ApplySerializedConfig(encoded_config()); |
| EXPECT_EQ(std::vector<net::ProxyServer>( |
| {net::ProxyServer::FromURI(kSuccessOrigin, |
| net::ProxyServer::SCHEME_HTTP), |
| net::ProxyServer::FromURI(kSuccessFallback, |
| net::ProxyServer::SCHEME_HTTP)}), |
| GetConfiguredProxiesForHttp()); |
| EXPECT_TRUE(persisted_config().empty()); |
| EXPECT_TRUE(persisted_config_retrieval_time().is_null()); |
| EXPECT_FALSE(request_options()->GetSecureSession().empty()); |
| } |
| |
| // Verifies that setting a client config sets the pingback reporting fraction |
| // correctly to 0.0f. |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| ApplySerializedConfigZeroReportingFraction) { |
| Init(true); |
| // ApplySerializedConfig should apply the encoded config. |
| config_client()->ApplySerializedConfig( |
| zero_reporting_fraction_encoded_config()); |
| EXPECT_EQ(0.0f, pingback_reporting_fraction()); |
| } |
| |
| // Verifies that setting a client config sets the pingback reporting fraction |
| // correctly to 0.0f when the pingback is not set in the protobuf. |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| ApplySerializedConfigEmptyReportingFraction) { |
| Init(true); |
| // ApplySerializedConfig should apply the encoded config. |
| config_client()->ApplySerializedConfig( |
| empty_reporting_fraction_encoded_config()); |
| EXPECT_EQ(0.0f, pingback_reporting_fraction()); |
| } |
| |
| // Verifies that setting a client config sets the pingback reporting fraction |
| // correctly to 1.0f. |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| ApplySerializedConfigOneReportingFraction) { |
| Init(true); |
| // ApplySerializedConfig should apply the encoded config. |
| config_client()->ApplySerializedConfig( |
| one_reporting_fraction_encoded_config()); |
| EXPECT_EQ(1.0f, pingback_reporting_fraction()); |
| } |
| |
| // Verifies that setting a client config sets the pingback reporting fraction |
| // correctly to 0.5f. |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| ApplySerializedConfigHalfReportingFraction) { |
| Init(true); |
| // ApplySerializedConfig should apply the encoded config. |
| config_client()->ApplySerializedConfig( |
| half_reporting_fraction_encoded_config()); |
| EXPECT_EQ(0.5f, pingback_reporting_fraction()); |
| EXPECT_FALSE(ignore_blacklist()); |
| } |
| |
| TEST_F(DataReductionProxyConfigServiceClientTest, |
| ApplySerializedConfigIgnoreBlackList) { |
| Init(true); |
| |
| config_client()->ApplySerializedConfig(ignore_black_list_encoded_config()); |
| EXPECT_TRUE(ignore_blacklist()); |
| } |
| |
| TEST_F(DataReductionProxyConfigServiceClientTest, EmptyConfigDisablesDRP) { |
| Init(true); |
| SetDataReductionProxyEnabled(true, true); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| |
| config_client()->ApplySerializedConfig(no_proxies_config()); |
| EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp()); |
| EXPECT_TRUE(configurator()->GetProxyConfig().proxy_rules().empty()); |
| } |
| |
| #if defined(OS_ANDROID) |
| // Verifies the correctness of fetching config when Chromium is in background |
| // and foreground. |
| TEST_F(DataReductionProxyConfigServiceClientTest, FetchConfigOnForeground) { |
| Init(true); |
| SetDataReductionProxyEnabled(true, true); |
| |
| { |
| // Tests that successful config fetches while Chromium is in background, |
| // does not trigger refetches when Chromium comes to foreground. |
| base::HistogramTester histogram_tester; |
| AddMockSuccess(); |
| config_client()->set_application_state_background(true); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| VerifyRemoteSuccess(true); |
| EXPECT_FALSE(config_client()->foreground_fetch_pending()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.FetchLatency", 1); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(kConfigRefreshDurationSeconds), |
| config_client()->GetDelay()); |
| config_client()->set_application_state_background(false); |
| config_client()->TriggerApplicationStatusToForeground(); |
| RunUntilIdle(); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(kConfigRefreshDurationSeconds), |
| config_client()->GetDelay()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.FetchLatency", 1); |
| } |
| |
| { |
| // Tests that config fetch failures while Chromium is in foreground does not |
| // trigger refetches when Chromium comes to foreground again. |
| base::HistogramTester histogram_tester; |
| AddMockFailure(); |
| config_client()->set_application_state_background(false); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| EXPECT_FALSE(config_client()->foreground_fetch_pending()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.FetchLatency", 0); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(20), config_client()->GetDelay()); |
| config_client()->TriggerApplicationStatusToForeground(); |
| RunUntilIdle(); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.FetchLatency", 0); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(20), config_client()->GetDelay()); |
| } |
| |
| { |
| // Tests that config fetch failures while Chromium is in background, trigger |
| // a refetch when Chromium comes to foreground. |
| base::HistogramTester histogram_tester; |
| AddMockFailure(); |
| AddMockSuccess(); |
| config_client()->set_application_state_background(true); |
| config_client()->RetrieveConfig(); |
| RunUntilIdle(); |
| EXPECT_TRUE(config_client()->foreground_fetch_pending()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.FetchLatency", 0); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(kMaxBackgroundFetchIntervalSeconds), |
| config_client()->GetDelay()); |
| config_client()->set_application_state_background(false); |
| config_client()->TriggerApplicationStatusToForeground(); |
| RunUntilIdle(); |
| EXPECT_FALSE(config_client()->foreground_fetch_pending()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.ConfigService.FetchLatency", 1); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(kConfigRefreshDurationSeconds), |
| config_client()->GetDelay()); |
| VerifyRemoteSuccess(true); |
| } |
| } |
| #endif |
| |
| } // namespace data_reduction_proxy |