| // Copyright 2014 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_network_delegate.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <map> |
| #include <string> |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/files/file_util.h" |
| #include "base/macros.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/optional.h" |
| #include "base/path_service.h" |
| #include "base/run_loop.h" |
| #include "base/strings/safe_sprintf.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/mock_entropy_provider.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "build/build_config.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_data.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.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/core/common/lofi_decider.h" |
| #include "components/data_reduction_proxy/proto/client_config.pb.h" |
| #include "components/data_reduction_proxy/proto/data_store.pb.h" |
| #include "components/previews/core/previews_experiments.h" |
| #include "components/previews/core/previews_features.h" |
| #include "components/previews/core/test_previews_decider.h" |
| #include "net/base/host_port_pair.h" |
| #include "net/base/load_flags.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/proxy_server.h" |
| #include "net/http/http_request_headers.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/http/http_util.h" |
| #include "net/nqe/effective_connection_type.h" |
| #include "net/proxy_resolution/proxy_config.h" |
| #include "net/proxy_resolution/proxy_info.h" |
| #include "net/proxy_resolution/proxy_resolution_service.h" |
| #include "net/proxy_resolution/proxy_retry_info.h" |
| #include "net/socket/socket_test_util.h" |
| #include "net/test/cert_test_util.h" |
| #include "net/test/gtest_util.h" |
| #include "net/test/test_data_directory.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "net/url_request/url_request.h" |
| #include "net/url_request/url_request_job_factory_impl.h" |
| #include "net/url_request/url_request_status.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "services/network/test/test_network_quality_tracker.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| namespace data_reduction_proxy { |
| namespace { |
| |
| using TestNetworkDelegate = net::NetworkDelegateImpl; |
| |
| const char kOtherProxy[] = "testproxy:17"; |
| |
| const char kTestURL[] = "http://www.google.com/"; |
| const char kSecureTestURL[] = "https://www.google.com/"; |
| |
| const char kOriginalValidOCLHistogramName[] = |
| "Net.HttpOriginalContentLengthWithValidOCL"; |
| const char kDifferenceValidOCLHistogramName[] = |
| "Net.HttpContentLengthDifferenceWithValidOCL"; |
| |
| // HTTP original content length |
| const char kOriginalInsecureDirectHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Http.Direct"; |
| const char kOriginalInsecureViaDRPHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Http.ViaDRP"; |
| const char kOriginalInsecureBypassedHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Http.BypassedDRP"; |
| const char kOriginalInsecureOtherHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Http.Other"; |
| // HTTP video original content length |
| const char kOriginalVideoInsecureDirectHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Http.Direct.Video"; |
| const char kOriginalVideoInsecureViaDRPHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Http.ViaDRP.Video"; |
| const char kOriginalVideoInsecureBypassedHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Http.BypassedDRP.Video"; |
| const char kOriginalVideoInsecureOtherHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Http.Other.Video"; |
| // HTTPS original content length |
| const char kOriginalSecureDirectHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Https.Direct"; |
| const char kOriginalSecureViaDRPHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Https.ViaDRP"; |
| const char kOriginalSecureBypassedHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Https.BypassedDRP"; |
| const char kOriginalSecureOtherHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Https.Other"; |
| // HTTPS video original content length |
| const char kOriginalVideoSecureDirectHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Https.Direct.Video"; |
| const char kOriginalVideoSecureViaDRPHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Https.ViaDRP.Video"; |
| const char kOriginalVideoSecureBypassedHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Https.BypassedDRP.Video"; |
| const char kOriginalVideoSecureOtherHistogramName[] = |
| "Net.HttpOriginalContentLengthV2.Https.Other.Video"; |
| |
| const char kReceivedHistogramName[] = "Net.HttpContentLength"; |
| const char kReceivedInsecureDirectHistogramName[] = |
| "Net.HttpContentLengthV2.Http.Direct"; |
| const char kReceivedInsecureViaDRPHistogramName[] = |
| "Net.HttpContentLengthV2.Http.ViaDRP"; |
| const char kReceivedInsecureBypassedHistogramName[] = |
| "Net.HttpContentLengthV2.Http.BypassedDRP"; |
| const char kReceivedInsecureOtherHistogramName[] = |
| "Net.HttpContentLengthV2.Http.Other"; |
| const char kReceivedSecureDirectHistogramName[] = |
| "Net.HttpContentLengthV2.Https.Direct"; |
| const char kReceivedSecureViaDRPHistogramName[] = |
| "Net.HttpContentLengthV2.Https.ViaDRP"; |
| const char kReceivedSecureBypassedHistogramName[] = |
| "Net.HttpContentLengthV2.Https.BypassedDRP"; |
| const char kReceivedSecureOtherHistogramName[] = |
| "Net.HttpContentLengthV2.Https.Other"; |
| const char kReceivedVideoInsecureDirectHistogramName[] = |
| "Net.HttpContentLengthV2.Http.Direct.Video"; |
| const char kReceivedVideoInsecureViaDRPHistogramName[] = |
| "Net.HttpContentLengthV2.Http.ViaDRP.Video"; |
| const char kReceivedVideoInsecureBypassedHistogramName[] = |
| "Net.HttpContentLengthV2.Http.BypassedDRP.Video"; |
| const char kReceivedVideoInsecureOtherHistogramName[] = |
| "Net.HttpContentLengthV2.Http.Other.Video"; |
| const char kReceivedVideoSecureDirectHistogramName[] = |
| "Net.HttpContentLengthV2.Https.Direct.Video"; |
| const char kReceivedVideoSecureViaDRPHistogramName[] = |
| "Net.HttpContentLengthV2.Https.ViaDRP.Video"; |
| const char kReceivedVideoSecureBypassedHistogramName[] = |
| "Net.HttpContentLengthV2.Https.BypassedDRP.Video"; |
| const char kReceivedVideoSecureOtherHistogramName[] = |
| "Net.HttpContentLengthV2.Https.Other.Video"; |
| const char kFreshnessLifetimeHistogramName[] = |
| "Net.HttpContentFreshnessLifetime"; |
| const int64_t kResponseContentLength = 100; |
| const int64_t kOriginalContentLength = 200; |
| |
| #if defined(OS_ANDROID) |
| const Client kClient = Client::CHROME_ANDROID; |
| #elif defined(OS_IOS) |
| const Client kClient = Client::CHROME_IOS; |
| #elif defined(OS_MACOSX) |
| const Client kClient = Client::CHROME_MAC; |
| #elif defined(OS_CHROMEOS) |
| const Client kClient = Client::CHROME_CHROMEOS; |
| #elif defined(OS_LINUX) |
| const Client kClient = Client::CHROME_LINUX; |
| #elif defined(OS_WIN) |
| const Client kClient = Client::CHROME_WINDOWS; |
| #elif defined(OS_FREEBSD) |
| const Client kClient = Client::CHROME_FREEBSD; |
| #elif defined(OS_OPENBSD) |
| const Client kClient = Client::CHROME_OPENBSD; |
| #elif defined(OS_SOLARIS) |
| const Client kClient = Client::CHROME_SOLARIS; |
| #elif defined(OS_QNX) |
| const Client kClient = Client::CHROME_QNX; |
| #else |
| const Client kClient = Client::UNKNOWN; |
| #endif |
| |
| class TestLoFiDecider : public LoFiDecider { |
| public: |
| TestLoFiDecider() |
| : should_be_client_lofi_(false), |
| should_be_client_lofi_auto_reload_(false), |
| should_request_lofi_resource_(false), |
| ignore_is_using_data_reduction_proxy_check_(false) {} |
| ~TestLoFiDecider() override {} |
| |
| void SetIsUsingLoFi(bool should_request_lofi_resource) { |
| should_request_lofi_resource_ = should_request_lofi_resource; |
| } |
| |
| void SetIsUsingClientLoFi(bool should_be_client_lofi) { |
| should_be_client_lofi_ = should_be_client_lofi; |
| } |
| |
| void SetIsClientLoFiAutoReload(bool should_be_client_lofi_auto_reload) { |
| should_be_client_lofi_auto_reload_ = should_be_client_lofi_auto_reload; |
| } |
| |
| void MaybeSetAcceptTransformHeader( |
| const net::URLRequest& request, |
| net::HttpRequestHeaders* headers) const override { |
| if (should_request_lofi_resource_) { |
| headers->SetHeader(chrome_proxy_accept_transform_header(), |
| empty_image_directive()); |
| } |
| } |
| |
| bool IsSlowPagePreviewRequested( |
| const net::HttpRequestHeaders& headers) const override { |
| std::string header_value; |
| if (headers.GetHeader(chrome_proxy_accept_transform_header(), |
| &header_value)) { |
| return header_value == empty_image_directive() || |
| header_value == lite_page_directive(); |
| } |
| return false; |
| } |
| |
| bool IsLitePagePreviewRequested( |
| const net::HttpRequestHeaders& headers) const override { |
| std::string header_value; |
| if (headers.GetHeader(chrome_proxy_accept_transform_header(), |
| &header_value)) { |
| return header_value == lite_page_directive(); |
| } |
| return false; |
| } |
| |
| void RemoveAcceptTransformHeader( |
| net::HttpRequestHeaders* headers) const override { |
| if (ignore_is_using_data_reduction_proxy_check_) |
| return; |
| headers->RemoveHeader(chrome_proxy_accept_transform_header()); |
| } |
| |
| bool ShouldRecordLoFiUMA(const net::URLRequest& request) const override { |
| return should_request_lofi_resource_; |
| } |
| |
| bool IsClientLoFiImageRequest(const net::URLRequest& request) const override { |
| return should_be_client_lofi_; |
| } |
| |
| bool IsClientLoFiAutoReloadRequest( |
| const net::URLRequest& request) const override { |
| return should_be_client_lofi_auto_reload_; |
| } |
| |
| void ignore_is_using_data_reduction_proxy_check() { |
| ignore_is_using_data_reduction_proxy_check_ = true; |
| } |
| |
| private: |
| bool should_be_client_lofi_; |
| bool should_be_client_lofi_auto_reload_; |
| bool should_request_lofi_resource_; |
| bool ignore_is_using_data_reduction_proxy_check_; |
| }; |
| |
| class TestLoFiUIService : public LoFiUIService { |
| public: |
| TestLoFiUIService() : on_lofi_response_(false) {} |
| ~TestLoFiUIService() override {} |
| |
| bool DidNotifyLoFiResponse() const { return on_lofi_response_; } |
| |
| void OnLoFiReponseReceived(const net::URLRequest& request) override { |
| on_lofi_response_ = true; |
| } |
| |
| void ClearResponse() { on_lofi_response_ = false; } |
| |
| private: |
| bool on_lofi_response_; |
| }; |
| |
| class TestResourceTypeProvider : public ResourceTypeProvider { |
| public: |
| void SetContentType(const net::URLRequest& request) override {} |
| |
| ContentType GetContentType(const GURL& url) const override { |
| return ResourceTypeProvider::CONTENT_TYPE_UNKNOWN; |
| } |
| |
| bool IsNonContentInitiatedRequest( |
| const net::URLRequest& request) const override { |
| return is_non_content_initiated_request_; |
| } |
| |
| void set_is_non_content_initiated_request( |
| bool is_non_content_initiated_request) { |
| is_non_content_initiated_request_ = is_non_content_initiated_request; |
| } |
| |
| private: |
| bool is_non_content_initiated_request_ = false; |
| }; |
| |
| enum ProxyTestConfig { USE_SECURE_PROXY, USE_INSECURE_PROXY, BYPASS_PROXY }; |
| |
| class DataReductionProxyNetworkDelegateTest : public testing::Test { |
| public: |
| DataReductionProxyNetworkDelegateTest() |
| : lofi_decider_(nullptr), |
| lofi_ui_service_(nullptr), |
| ssl_socket_data_provider_(net::ASYNC, net::OK) { |
| ssl_socket_data_provider_.next_proto = net::kProtoHTTP11; |
| ssl_socket_data_provider_.ssl_info.cert = net::ImportCertFromFile( |
| net::GetTestCertsDirectory(), "unittest.selfsigned.der"); |
| } |
| |
| void Init(ProxyTestConfig proxy_config, bool enable_brotli_globally) { |
| net::ProxyServer proxy_server; |
| switch (proxy_config) { |
| case BYPASS_PROXY: |
| proxy_server = net::ProxyServer::Direct(); |
| break; |
| case USE_SECURE_PROXY: |
| proxy_server = net::ProxyServer::FromURI( |
| "https://origin.net:443", net::ProxyServer::SCHEME_HTTPS); |
| break; |
| case USE_INSECURE_PROXY: |
| proxy_server = net::ProxyServer::FromURI("http://origin.net:80", |
| net::ProxyServer::SCHEME_HTTP); |
| break; |
| } |
| context_.reset(new net::TestURLRequestContext(true)); |
| context_storage_.reset(new net::URLRequestContextStorage(context_.get())); |
| proxy_resolution_service_ = |
| net::ProxyResolutionService::CreateFixedFromPacResult( |
| proxy_server.ToPacString(), TRAFFIC_ANNOTATION_FOR_TESTS); |
| context_->set_proxy_resolution_service(proxy_resolution_service_.get()); |
| |
| mock_socket_factory_.reset(new net::MockClientSocketFactory()); |
| |
| DataReductionProxyTestContext::Builder builder; |
| builder = builder.WithClient(kClient) |
| .WithMockClientSocketFactory(mock_socket_factory_.get()) |
| .WithURLRequestContext(context_.get()); |
| |
| if (proxy_config != BYPASS_PROXY) { |
| builder = builder.WithProxiesForHttp({DataReductionProxyServer( |
| proxy_server, ProxyServer::UNSPECIFIED_TYPE)}); |
| } |
| |
| test_context_ = builder.Build(); |
| |
| context_->set_client_socket_factory(mock_socket_factory_.get()); |
| test_context_->AttachToURLRequestContext(context_storage_.get()); |
| |
| std::unique_ptr<TestLoFiDecider> lofi_decider(new TestLoFiDecider()); |
| lofi_decider_ = lofi_decider.get(); |
| test_context_->io_data()->set_lofi_decider(std::move(lofi_decider)); |
| |
| std::unique_ptr<TestLoFiUIService> lofi_ui_service(new TestLoFiUIService()); |
| lofi_ui_service_ = lofi_ui_service.get(); |
| test_context_->io_data()->set_lofi_ui_service(std::move(lofi_ui_service)); |
| |
| context_->set_enable_brotli(enable_brotli_globally); |
| context_->Init(); |
| |
| test_context_->DisableWarmupURLFetch(); |
| test_context_->EnableDataReductionProxyWithSecureProxyCheckSuccess(); |
| } |
| |
| // Build the sockets by adding appropriate mock data for |
| // |effective_connection_types.size()| number of requests. Data for |
| // chrome-Proxy-ect header is added to the mock data if |expect_ect_header| |
| // is true. |reads_list|, |mock_writes| and |writes_list| should be empty, and |
| // are owned by the caller. |
| void BuildSocket(const std::string& response_headers, |
| const std::string& response_body, |
| bool expect_ect_header, |
| const std::vector<net::EffectiveConnectionType>& |
| effective_connection_types, |
| std::vector<net::MockRead>* reads_list, |
| std::vector<std::string>* mock_writes, |
| std::vector<net::MockWrite>* writes_list) { |
| EXPECT_LT(0u, effective_connection_types.size()); |
| EXPECT_TRUE(reads_list->empty()); |
| EXPECT_TRUE(mock_writes->empty()); |
| EXPECT_TRUE(writes_list->empty()); |
| |
| for (size_t i = 0; i < effective_connection_types.size(); ++i) { |
| reads_list->push_back(net::MockRead(response_headers.c_str())); |
| reads_list->push_back(net::MockRead(response_body.c_str())); |
| } |
| reads_list->push_back(net::MockRead(net::SYNCHRONOUS, net::OK)); |
| |
| std::string prefix = std::string("GET ") |
| .append(kTestURL) |
| .append(" HTTP/1.1\r\n") |
| .append("Host: ") |
| .append(GURL(kTestURL).host()) |
| .append( |
| "\r\n" |
| "Proxy-Connection: keep-alive\r\n" |
| "User-Agent:\r\n" |
| "Accept-Encoding: gzip, deflate\r\n" |
| "Accept-Language: en-us,fr\r\n"); |
| |
| if (io_data()->test_request_options()->GetHeaderValueForTesting().empty()) { |
| // Force regeneration of Chrome-Proxy header. |
| io_data()->test_request_options()->SetSecureSession("123"); |
| } |
| |
| EXPECT_FALSE( |
| io_data()->test_request_options()->GetHeaderValueForTesting().empty()); |
| |
| std::string suffix = |
| std::string("Chrome-Proxy: ") + |
| io_data()->test_request_options()->GetHeaderValueForTesting() + |
| std::string("\r\n\r\n"); |
| |
| mock_socket_factory_->AddSSLSocketDataProvider(&ssl_socket_data_provider_); |
| |
| for (net::EffectiveConnectionType effective_connection_type : |
| effective_connection_types) { |
| std::string ect_header; |
| if (expect_ect_header) { |
| ect_header = "chrome-proxy-ect: " + |
| std::string(net::GetNameForEffectiveConnectionType( |
| effective_connection_type)) + |
| "\r\n"; |
| } |
| |
| std::string mock_write = prefix + ect_header + suffix; |
| mock_writes->push_back(mock_write); |
| writes_list->push_back(net::MockWrite(mock_writes->back().c_str())); |
| } |
| |
| EXPECT_FALSE(socket_); |
| socket_ = std::make_unique<net::StaticSocketDataProvider>(*reads_list, |
| *writes_list); |
| mock_socket_factory_->AddSocketDataProvider(socket_.get()); |
| } |
| |
| static void VerifyHeaders(bool expected_data_reduction_proxy_used, |
| bool expected_lofi_used, |
| const net::HttpRequestHeaders& headers) { |
| EXPECT_EQ(expected_data_reduction_proxy_used, |
| headers.HasHeader(chrome_proxy_header())); |
| std::string header_value; |
| headers.GetHeader(chrome_proxy_accept_transform_header(), &header_value); |
| EXPECT_EQ(expected_data_reduction_proxy_used && expected_lofi_used, |
| header_value.find("empty-image") != std::string::npos); |
| } |
| |
| void VerifyDidNotifyLoFiResponse(bool lofi_response) const { |
| EXPECT_EQ(lofi_response, lofi_ui_service_->DidNotifyLoFiResponse()); |
| } |
| |
| void ClearLoFiUIService() { lofi_ui_service_->ClearResponse(); } |
| |
| void VerifyDataReductionProxyData(const net::URLRequest& request, |
| bool data_reduction_proxy_used, |
| bool lofi_used) { |
| DataReductionProxyData* data = DataReductionProxyData::GetData(request); |
| if (!data_reduction_proxy_used) { |
| EXPECT_FALSE(data); |
| } else { |
| EXPECT_TRUE(data->used_data_reduction_proxy()); |
| EXPECT_EQ(lofi_used, data->lofi_requested()); |
| } |
| } |
| |
| // Each line in |response_headers| should end with "\r\n" and not '\0', and |
| // the last line should have a second "\r\n". |
| // An empty |response_headers| is allowed. It works by making this look like |
| // an HTTP/0.9 response, since HTTP/0.9 responses don't have headers. |
| std::unique_ptr<net::URLRequest> FetchURLRequest( |
| const GURL& url, |
| net::HttpRequestHeaders* request_headers, |
| const std::string& response_headers, |
| int64_t response_content_length, |
| int load_flags) { |
| const std::string response_body( |
| base::checked_cast<size_t>(response_content_length), ' '); |
| |
| net::MockRead reads[] = {net::MockRead(response_headers.c_str()), |
| net::MockRead(response_body.c_str()), |
| net::MockRead(net::SYNCHRONOUS, net::OK)}; |
| net::StaticSocketDataProvider socket(reads, base::span<net::MockWrite>()); |
| mock_socket_factory_->AddSocketDataProvider(&socket); |
| |
| net::TestDelegate delegate; |
| std::unique_ptr<net::URLRequest> request = context_->CreateRequest( |
| url, net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| if (request_headers) |
| request->SetExtraRequestHeaders(*request_headers); |
| request->SetLoadFlags(request->load_flags() | load_flags); |
| |
| request->Start(); |
| base::RunLoop().RunUntilIdle(); |
| return request; |
| } |
| |
| // Reads brotli encoded content to |encoded_brotli_buffer_|. |
| void ReadBrotliFile() { |
| // Get the path of data directory. |
| const size_t kDefaultBufferSize = 4096; |
| base::FilePath data_dir; |
| base::PathService::Get(base::DIR_SOURCE_ROOT, &data_dir); |
| data_dir = data_dir.AppendASCII("net"); |
| data_dir = data_dir.AppendASCII("data"); |
| data_dir = data_dir.AppendASCII("filter_unittests"); |
| |
| // Read data from the encoded file into buffer. |
| base::FilePath encoded_file_path; |
| encoded_file_path = data_dir.AppendASCII("google.br"); |
| ASSERT_TRUE( |
| base::ReadFileToString(encoded_file_path, &encoded_brotli_buffer_)); |
| ASSERT_GE(kDefaultBufferSize, encoded_brotli_buffer_.size()); |
| } |
| |
| // Fetches a single URL request, verifies the correctness of Accept-Encoding |
| // header, and verifies that the response is cached only if |expect_cached| |
| // is set to true. Each line in |response_headers| should end with "\r\n" and |
| // not '\0', and the last line should have a second "\r\n". An empty |
| // |response_headers| is allowed. It works by making this look like an |
| // HTTP/0.9 response, since HTTP/0.9 responses don't have headers. |
| void FetchURLRequestAndVerifyBrotli(net::HttpRequestHeaders* request_headers, |
| const std::string& response_headers, |
| bool expect_cached, |
| bool expect_brotli) { |
| test_network_quality_tracker()->ReportEffectiveConnectionTypeForTesting( |
| net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN); |
| base::RunLoop().RunUntilIdle(); |
| GURL url(kTestURL); |
| |
| int response_body_size = 140; |
| std::string response_body; |
| if (expect_brotli && !expect_cached) { |
| response_body = encoded_brotli_buffer_; |
| response_body_size = response_body.size(); |
| } else { |
| response_body = |
| std::string(base::checked_cast<size_t>(response_body_size), ' '); |
| } |
| |
| mock_socket_factory_->AddSSLSocketDataProvider(&ssl_socket_data_provider_); |
| |
| net::MockRead reads[] = {net::MockRead(response_headers.c_str()), |
| net::MockRead(response_body.c_str()), |
| net::MockRead(net::SYNCHRONOUS, net::OK)}; |
| |
| if (io_data()->test_request_options()->GetHeaderValueForTesting().empty()) { |
| // Force regeneration of Chrome-Proxy header. |
| io_data()->test_request_options()->SetSecureSession("123"); |
| } |
| EXPECT_FALSE( |
| io_data()->test_request_options()->GetHeaderValueForTesting().empty()); |
| |
| std::string host = GURL(kTestURL).host(); |
| std::string prefix_headers = std::string("GET ") |
| .append(kTestURL) |
| .append( |
| " HTTP/1.1\r\n" |
| "Host: ") |
| .append(host) |
| .append( |
| "\r\n" |
| "Proxy-Connection: keep-alive\r\n" |
| "User-Agent:\r\n"); |
| |
| std::string accept_language_header("Accept-Language: en-us,fr\r\n"); |
| std::string ect_header = "chrome-proxy-ect: " + |
| std::string(net::GetNameForEffectiveConnectionType( |
| net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN)) + |
| "\r\n"; |
| |
| // Brotli is included in accept-encoding header only if the request went |
| // to the network (i.e., it was not a cached response), and if data |
| // reduction ptroxy network delegate added Brotli to the header. |
| std::string accept_encoding_header = |
| expect_brotli && !expect_cached |
| ? "Accept-Encoding: gzip, deflate, br\r\n" |
| : "Accept-Encoding: gzip, deflate\r\n"; |
| |
| std::string suffix_headers = |
| std::string("Chrome-Proxy: ") + |
| io_data()->test_request_options()->GetHeaderValueForTesting() + |
| std::string("\r\n\r\n"); |
| |
| std::string mock_write = prefix_headers + accept_language_header + |
| ect_header + accept_encoding_header + |
| suffix_headers; |
| |
| if (expect_cached || !expect_brotli) { |
| // Order of headers is different if the headers were modified by data |
| // reduction proxy network delegate. |
| mock_write = prefix_headers + accept_encoding_header + |
| accept_language_header + ect_header + suffix_headers; |
| } |
| |
| net::MockWrite writes[] = {net::MockWrite(mock_write.c_str())}; |
| net::StaticSocketDataProvider socket(reads, writes); |
| mock_socket_factory_->AddSocketDataProvider(&socket); |
| |
| net::TestDelegate delegate; |
| std::unique_ptr<net::URLRequest> request = context_->CreateRequest( |
| url, net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| if (request_headers) |
| request->SetExtraRequestHeaders(*request_headers); |
| |
| request->Start(); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(0, request->status().ToNetError()); |
| |
| if (!expect_cached) { |
| EXPECT_EQ(response_body_size, |
| request->received_response_content_length()); |
| EXPECT_NE(0, request->GetTotalSentBytes()); |
| EXPECT_NE(0, request->GetTotalReceivedBytes()); |
| EXPECT_FALSE(request->was_cached()); |
| VerifyBrotliPresent(request.get(), expect_brotli); |
| } else { |
| EXPECT_TRUE(request->was_cached()); |
| std::string content_encoding_value; |
| request->GetResponseHeaderByName("Content-Encoding", |
| &content_encoding_value); |
| EXPECT_EQ(expect_brotli, content_encoding_value == "br"); |
| } |
| } |
| |
| void VerifyBrotliPresent(net::URLRequest* request, bool expect_brotli) { |
| net::HttpRequestHeaders request_headers_sent; |
| EXPECT_TRUE(request->GetFullRequestHeaders(&request_headers_sent)); |
| std::string accept_encoding_value; |
| EXPECT_TRUE(request_headers_sent.GetHeader("Accept-Encoding", |
| &accept_encoding_value)); |
| EXPECT_NE(std::string::npos, accept_encoding_value.find("gzip")); |
| |
| std::string content_encoding_value; |
| request->GetResponseHeaderByName("Content-Encoding", |
| &content_encoding_value); |
| |
| if (expect_brotli) { |
| // Brotli should be the last entry in the Accept-Encoding header. |
| EXPECT_EQ(accept_encoding_value.length() - 2, |
| accept_encoding_value.find("br")); |
| EXPECT_EQ("br", content_encoding_value); |
| } else { |
| EXPECT_EQ(std::string::npos, accept_encoding_value.find("br")); |
| } |
| } |
| |
| void FetchURLRequestAndVerifyPageIdDirective(base::Optional<uint64_t> page_id, |
| bool redirect_once) { |
| std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Content-Length: 140\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=200\r\n" |
| "Cache-Control: max-age=1200\r\n" |
| "Vary: accept-encoding\r\n\r\n"; |
| |
| GURL url(kTestURL); |
| |
| int response_body_size = 140; |
| std::string response_body = |
| std::string(base::checked_cast<size_t>(response_body_size), ' '); |
| |
| mock_socket_factory_->AddSSLSocketDataProvider(&ssl_socket_data_provider_); |
| |
| net::MockRead redirect_reads[] = { |
| net::MockRead("HTTP/1.1 302 Redirect\r\n"), |
| net::MockRead("Location: http://www.google.com/\r\n"), |
| net::MockRead("Content-Length: 0\r\n\r\n"), |
| net::MockRead(net::SYNCHRONOUS, net::OK), |
| net::MockRead(response_headers.c_str()), |
| net::MockRead(response_body.c_str()), |
| net::MockRead(net::SYNCHRONOUS, net::OK)}; |
| |
| net::MockRead reads[] = {net::MockRead(response_headers.c_str()), |
| net::MockRead(response_body.c_str()), |
| net::MockRead(net::SYNCHRONOUS, net::OK)}; |
| |
| EXPECT_FALSE( |
| io_data()->test_request_options()->GetHeaderValueForTesting().empty()); |
| |
| std::string page_id_value; |
| if (page_id) { |
| char page_id_buffer[17]; |
| if (base::strings::SafeSPrintf(page_id_buffer, "%x", page_id.value()) > |
| 0) { |
| page_id_value = std::string("pid=") + page_id_buffer; |
| } |
| } |
| |
| std::string mock_write = |
| "GET http://www.google.com/ HTTP/1.1\r\nHost: " |
| "www.google.com\r\nProxy-Connection: " |
| "keep-alive\r\nUser-Agent:\r\nAccept-Encoding: gzip, " |
| "deflate\r\nAccept-Language: en-us,fr\r\n" |
| "chrome-proxy-ect: Unknown\r\n" |
| "Chrome-Proxy: " + |
| io_data()->test_request_options()->GetHeaderValueForTesting() + |
| (page_id_value.empty() ? "" : (", " + page_id_value)) + "\r\n\r\n"; |
| |
| net::MockWrite redirect_writes[] = {net::MockWrite(mock_write.c_str()), |
| net::MockWrite(mock_write.c_str())}; |
| |
| net::MockWrite writes[] = {net::MockWrite(mock_write.c_str())}; |
| |
| std::unique_ptr<net::StaticSocketDataProvider> socket; |
| if (!redirect_once) { |
| socket = std::make_unique<net::StaticSocketDataProvider>(reads, writes); |
| } else { |
| socket = std::make_unique<net::StaticSocketDataProvider>(redirect_reads, |
| redirect_writes); |
| } |
| |
| mock_socket_factory_->AddSocketDataProvider(socket.get()); |
| |
| net::TestDelegate delegate; |
| std::unique_ptr<net::URLRequest> request = context_->CreateRequest( |
| url, net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| if (!page_id_value.empty()) { |
| request->SetLoadFlags(request->load_flags() | |
| net::LOAD_MAIN_FRAME_DEPRECATED); |
| } |
| |
| request->Start(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // Fetches a request while the effective connection type is set to |
| // |effective_connection_type|. Verifies that the request headers include the |
| // chrome-proxy-ect header only if |expect_ect_header| is true. The response |
| // must be served from the cache if |expect_cached| is true. |
| void FetchURLRequestAndVerifyECTHeader( |
| net::EffectiveConnectionType effective_connection_type, |
| bool expect_ect_header, |
| bool expect_cached) { |
| test_network_quality_tracker()->ReportEffectiveConnectionTypeForTesting( |
| effective_connection_type); |
| base::RunLoop().RunUntilIdle(); |
| |
| net::TestDelegate delegate; |
| std::unique_ptr<net::URLRequest> request = context_->CreateRequest( |
| GURL(kTestURL), net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| request->Start(); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(140, request->received_response_content_length()); |
| EXPECT_EQ(expect_cached, request->was_cached()); |
| EXPECT_EQ(expect_cached, request->GetTotalSentBytes() == 0); |
| EXPECT_EQ(expect_cached, request->GetTotalReceivedBytes() == 0); |
| |
| net::HttpRequestHeaders sent_request_headers; |
| EXPECT_NE(expect_cached, |
| request->GetFullRequestHeaders(&sent_request_headers)); |
| |
| if (expect_cached) { |
| // Request headers are missing. Return since there is nothing left to |
| // check. |
| return; |
| } |
| |
| // Verify that chrome-proxy-ect header is present in the request headers |
| // only if |expect_ect_header| is true. |
| std::string ect_value; |
| EXPECT_EQ(expect_ect_header, sent_request_headers.GetHeader( |
| chrome_proxy_ect_header(), &ect_value)); |
| |
| if (!expect_ect_header) |
| return; |
| EXPECT_EQ(net::GetNameForEffectiveConnectionType(effective_connection_type), |
| ect_value); |
| } |
| |
| void DelegateStageDone(int result) {} |
| |
| void NotifyNetworkDelegate(net::URLRequest* request, |
| const net::ProxyInfo& data_reduction_proxy_info, |
| const net::ProxyRetryInfoMap& proxy_retry_info, |
| net::HttpRequestHeaders* headers) { |
| network_delegate()->NotifyBeforeURLRequest( |
| request, |
| base::BindOnce( |
| &DataReductionProxyNetworkDelegateTest::DelegateStageDone, |
| base::Unretained(this)), |
| nullptr); |
| network_delegate()->NotifyBeforeStartTransaction( |
| request, |
| base::BindOnce( |
| &DataReductionProxyNetworkDelegateTest::DelegateStageDone, |
| base::Unretained(this)), |
| headers); |
| network_delegate()->NotifyBeforeSendHeaders( |
| request, data_reduction_proxy_info, proxy_retry_info, headers); |
| } |
| |
| void EnableDataUsageReporting() { |
| test_context_->pref_service()->SetBoolean(prefs::kDataUsageReportingEnabled, |
| true); |
| // Give the setting notification a chance to propagate. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| size_t GetOtherHostDataUsage() { |
| const auto& data_usage_map = test_context_->data_reduction_proxy_service() |
| ->compression_stats() |
| ->DataUsageMapForTesting(); |
| const auto& it = data_usage_map.find(util::GetSiteBreakdownOtherHostName()); |
| if (it != data_usage_map.end()) |
| return it->second->data_used(); |
| return 0; |
| } |
| |
| net::MockClientSocketFactory* mock_socket_factory() { |
| return mock_socket_factory_.get(); |
| } |
| |
| net::TestURLRequestContext* context() { return context_.get(); } |
| |
| net::NetworkDelegate* network_delegate() const { |
| return context_->network_delegate(); |
| } |
| |
| TestDataReductionProxyParams* params() const { |
| return test_context_->config()->test_params(); |
| } |
| |
| TestDataReductionProxyConfig* config() const { |
| return test_context_->config(); |
| } |
| |
| TestDataReductionProxyIOData* io_data() const { |
| return test_context_->io_data(); |
| } |
| |
| TestLoFiDecider* lofi_decider() const { return lofi_decider_; } |
| |
| network::TestNetworkQualityTracker* test_network_quality_tracker() { |
| return test_context_->test_network_quality_tracker(); |
| } |
| |
| net::SSLSocketDataProvider* ssl_socket_data_provider() { |
| return &ssl_socket_data_provider_; |
| } |
| |
| private: |
| base::MessageLoopForIO message_loop_; |
| std::unique_ptr<net::MockClientSocketFactory> mock_socket_factory_; |
| std::unique_ptr<net::ProxyResolutionService> proxy_resolution_service_; |
| std::unique_ptr<net::TestURLRequestContext> context_; |
| std::unique_ptr<net::URLRequestContextStorage> context_storage_; |
| |
| TestLoFiDecider* lofi_decider_; |
| TestLoFiUIService* lofi_ui_service_; |
| std::unique_ptr<DataReductionProxyTestContext> test_context_; |
| |
| net::SSLSocketDataProvider ssl_socket_data_provider_; |
| |
| std::unique_ptr<net::StaticSocketDataProvider> socket_; |
| |
| // Encoded Brotli content read from a file. May be empty. |
| std::string encoded_brotli_buffer_; |
| }; |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, AuthenticationTest) { |
| Init(USE_INSECURE_PROXY, false); |
| std::unique_ptr<net::URLRequest> fake_request( |
| FetchURLRequest(GURL(kTestURL), nullptr, std::string(), 0, 0)); |
| |
| net::ProxyInfo data_reduction_proxy_info; |
| net::ProxyRetryInfoMap proxy_retry_info; |
| std::string data_reduction_proxy; |
| data_reduction_proxy_info.UseProxyServer( |
| params()->proxies_for_http().front().proxy_server()); |
| |
| net::HttpRequestHeaders headers; |
| // Call network delegate methods to ensure that appropriate chrome proxy |
| // headers get added/removed. |
| network_delegate()->NotifyBeforeStartTransaction( |
| fake_request.get(), |
| base::BindOnce(&DataReductionProxyNetworkDelegateTest::DelegateStageDone, |
| base::Unretained(this)), |
| &headers); |
| network_delegate()->NotifyBeforeSendHeaders(fake_request.get(), |
| data_reduction_proxy_info, |
| proxy_retry_info, &headers); |
| |
| EXPECT_TRUE(headers.HasHeader(chrome_proxy_header())); |
| std::string header_value; |
| headers.GetHeader(chrome_proxy_header(), &header_value); |
| EXPECT_TRUE(header_value.find("ps=") != std::string::npos); |
| EXPECT_TRUE(header_value.find("sid=") != std::string::npos); |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, LoFiTransitions) { |
| Init(USE_INSECURE_PROXY, false); |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitWithFeatures( |
| {previews::features::kPreviews, |
| features::kDataReductionProxyDecidesTransform}, |
| {}); |
| |
| // Enable Lo-Fi. |
| bool is_data_reduction_proxy_enabled[] = {false, true}; |
| |
| for (size_t i = 0; i < arraysize(is_data_reduction_proxy_enabled); ++i) { |
| net::ProxyInfo data_reduction_proxy_info; |
| std::string proxy; |
| if (is_data_reduction_proxy_enabled[i]) { |
| data_reduction_proxy_info.UseProxyServer( |
| params()->proxies_for_http().front().proxy_server()); |
| } else { |
| base::TrimString(kOtherProxy, "/", &proxy); |
| data_reduction_proxy_info.UseNamedProxy(proxy); |
| } |
| |
| // Needed as a parameter, but functionality is not tested. |
| previews::TestPreviewsDecider test_previews_decider(true); |
| |
| { |
| // Main frame loaded. Lo-Fi should be used. |
| net::HttpRequestHeaders headers; |
| net::ProxyRetryInfoMap proxy_retry_info; |
| |
| net::TestDelegate delegate; |
| std::unique_ptr<net::URLRequest> fake_request = context()->CreateRequest( |
| GURL(kTestURL), net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| fake_request->SetLoadFlags(net::LOAD_MAIN_FRAME_DEPRECATED); |
| lofi_decider()->SetIsUsingLoFi(config()->ShouldAcceptServerPreview( |
| *fake_request, test_previews_decider)); |
| NotifyNetworkDelegate(fake_request.get(), data_reduction_proxy_info, |
| proxy_retry_info, &headers); |
| |
| VerifyHeaders(is_data_reduction_proxy_enabled[i], true, headers); |
| VerifyDataReductionProxyData(*fake_request, |
| is_data_reduction_proxy_enabled[i], |
| config()->ShouldAcceptServerPreview( |
| *fake_request, test_previews_decider)); |
| } |
| |
| { |
| // Lo-Fi is already off. Lo-Fi should not be used. |
| net::HttpRequestHeaders headers; |
| net::ProxyRetryInfoMap proxy_retry_info; |
| net::TestDelegate delegate; |
| std::unique_ptr<net::URLRequest> fake_request = context()->CreateRequest( |
| GURL(kTestURL), net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| lofi_decider()->SetIsUsingLoFi(false); |
| NotifyNetworkDelegate(fake_request.get(), data_reduction_proxy_info, |
| proxy_retry_info, &headers); |
| VerifyHeaders(is_data_reduction_proxy_enabled[i], false, headers); |
| VerifyDataReductionProxyData(*fake_request, |
| is_data_reduction_proxy_enabled[i], false); |
| } |
| |
| { |
| // Lo-Fi is already on. Lo-Fi should be used. |
| net::HttpRequestHeaders headers; |
| net::ProxyRetryInfoMap proxy_retry_info; |
| net::TestDelegate delegate; |
| std::unique_ptr<net::URLRequest> fake_request = context()->CreateRequest( |
| GURL(kTestURL), net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| lofi_decider()->SetIsUsingLoFi(true); |
| NotifyNetworkDelegate(fake_request.get(), data_reduction_proxy_info, |
| proxy_retry_info, &headers); |
| VerifyHeaders(is_data_reduction_proxy_enabled[i], true, headers); |
| VerifyDataReductionProxyData(*fake_request, |
| is_data_reduction_proxy_enabled[i], true); |
| } |
| |
| { |
| // Main frame request with Lo-Fi off. Lo-Fi should not be used. |
| // State of Lo-Fi should persist until next page load. |
| net::HttpRequestHeaders headers; |
| net::ProxyRetryInfoMap proxy_retry_info; |
| net::TestDelegate delegate; |
| std::unique_ptr<net::URLRequest> fake_request = context()->CreateRequest( |
| GURL(kTestURL), net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| fake_request->SetLoadFlags(net::LOAD_MAIN_FRAME_DEPRECATED); |
| lofi_decider()->SetIsUsingLoFi(false); |
| NotifyNetworkDelegate(fake_request.get(), data_reduction_proxy_info, |
| proxy_retry_info, &headers); |
| VerifyHeaders(is_data_reduction_proxy_enabled[i], false, headers); |
| VerifyDataReductionProxyData(*fake_request, |
| is_data_reduction_proxy_enabled[i], false); |
| } |
| |
| { |
| // Lo-Fi is off. Lo-Fi is still not used. |
| net::HttpRequestHeaders headers; |
| net::ProxyRetryInfoMap proxy_retry_info; |
| net::TestDelegate delegate; |
| std::unique_ptr<net::URLRequest> fake_request = context()->CreateRequest( |
| GURL(kTestURL), net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| lofi_decider()->SetIsUsingLoFi(false); |
| NotifyNetworkDelegate(fake_request.get(), data_reduction_proxy_info, |
| proxy_retry_info, &headers); |
| VerifyHeaders(is_data_reduction_proxy_enabled[i], false, headers); |
| VerifyDataReductionProxyData(*fake_request, |
| is_data_reduction_proxy_enabled[i], false); |
| } |
| |
| { |
| // Main frame request. Lo-Fi should be used. |
| net::HttpRequestHeaders headers; |
| net::ProxyRetryInfoMap proxy_retry_info; |
| net::TestDelegate delegate; |
| std::unique_ptr<net::URLRequest> fake_request = context()->CreateRequest( |
| GURL(kTestURL), net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| fake_request->SetLoadFlags(net::LOAD_MAIN_FRAME_DEPRECATED); |
| lofi_decider()->SetIsUsingLoFi(config()->ShouldAcceptServerPreview( |
| *fake_request, test_previews_decider)); |
| NotifyNetworkDelegate(fake_request.get(), data_reduction_proxy_info, |
| proxy_retry_info, &headers); |
| VerifyDataReductionProxyData(*fake_request, |
| is_data_reduction_proxy_enabled[i], |
| config()->ShouldAcceptServerPreview( |
| *fake_request, test_previews_decider)); |
| } |
| } |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, RequestDataConfigurations) { |
| Init(USE_INSECURE_PROXY, false); |
| const struct { |
| bool lofi_on; |
| bool used_data_reduction_proxy; |
| bool main_frame; |
| } tests[] = { |
| // Lo-Fi off. Main Frame Request. |
| {false, true, true}, |
| // Data reduction proxy not used. Main Frame Request. |
| {false, false, true}, |
| // Data reduction proxy not used, Lo-Fi should not be used. Main Frame |
| // Request. |
| {true, false, true}, |
| // Lo-Fi on. Main Frame Request. |
| {true, true, true}, |
| // Lo-Fi off. Not a Main Frame Request. |
| {false, true, false}, |
| // Data reduction proxy not used. Not a Main Frame Request. |
| {false, false, false}, |
| // Data reduction proxy not used, Lo-Fi should not be used. Not a Main |
| // Frame Request. |
| {true, false, false}, |
| // Lo-Fi on. Not a Main Frame Request. |
| {true, true, false}, |
| }; |
| |
| for (const auto& test : tests) { |
| net::ProxyInfo data_reduction_proxy_info; |
| if (test.used_data_reduction_proxy) { |
| data_reduction_proxy_info.UseProxyServer( |
| params()->proxies_for_http().front().proxy_server()); |
| } else { |
| data_reduction_proxy_info.UseNamedProxy("port.of.other.proxy"); |
| } |
| // Main frame loaded. Lo-Fi should be used. |
| net::HttpRequestHeaders headers; |
| net::ProxyRetryInfoMap proxy_retry_info; |
| |
| test_network_quality_tracker()->ReportEffectiveConnectionTypeForTesting( |
| net::EFFECTIVE_CONNECTION_TYPE_OFFLINE); |
| base::RunLoop().RunUntilIdle(); |
| |
| std::unique_ptr<net::URLRequest> request = |
| context()->CreateRequest(GURL(kTestURL), net::RequestPriority::IDLE, |
| nullptr, TRAFFIC_ANNOTATION_FOR_TESTS); |
| request->SetLoadFlags(test.main_frame ? net::LOAD_MAIN_FRAME_DEPRECATED |
| : 0); |
| lofi_decider()->SetIsUsingLoFi(test.lofi_on); |
| io_data()->request_options()->SetSecureSession("fake-session"); |
| |
| // Call network delegate methods to ensure that appropriate chrome proxy |
| // headers get added/removed. |
| network_delegate()->NotifyBeforeStartTransaction( |
| request.get(), |
| base::BindOnce( |
| &DataReductionProxyNetworkDelegateTest::DelegateStageDone, |
| base::Unretained(this)), |
| &headers); |
| network_delegate()->NotifyBeforeSendHeaders( |
| request.get(), data_reduction_proxy_info, proxy_retry_info, &headers); |
| DataReductionProxyData* data = DataReductionProxyData::GetData(*request); |
| if (!test.used_data_reduction_proxy) { |
| EXPECT_FALSE(data); |
| } else { |
| EXPECT_TRUE(data); |
| EXPECT_EQ(test.main_frame ? net::EFFECTIVE_CONNECTION_TYPE_OFFLINE |
| : net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN, |
| data->effective_connection_type()); |
| EXPECT_TRUE(data->used_data_reduction_proxy()); |
| EXPECT_EQ(test.main_frame ? GURL(kTestURL) : GURL(), data->request_url()); |
| EXPECT_EQ(test.main_frame ? "fake-session" : "", data->session_key()); |
| EXPECT_EQ(test.lofi_on, data->lofi_requested()); |
| } |
| } |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, |
| RequestDataHoldbackConfigurations) { |
| Init(USE_INSECURE_PROXY, false); |
| const struct { |
| bool data_reduction_proxy_enabled; |
| bool used_direct; |
| } tests[] = { |
| { |
| false, true, |
| }, |
| { |
| false, false, |
| }, |
| { |
| true, false, |
| }, |
| { |
| true, true, |
| }, |
| }; |
| test_network_quality_tracker()->ReportEffectiveConnectionTypeForTesting( |
| net::EFFECTIVE_CONNECTION_TYPE_4G); |
| base::RunLoop().RunUntilIdle(); |
| base::FieldTrialList field_trial_list(nullptr); |
| ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial( |
| "DataCompressionProxyHoldback", "Enabled")); |
| for (const auto& test : tests) { |
| net::ProxyInfo data_reduction_proxy_info; |
| if (test.used_direct) |
| data_reduction_proxy_info.UseDirect(); |
| else |
| data_reduction_proxy_info.UseNamedProxy("some.other.proxy"); |
| config()->UpdateConfigForTesting(test.data_reduction_proxy_enabled, true, |
| true); |
| std::unique_ptr<net::URLRequest> request = |
| context()->CreateRequest(GURL(kTestURL), net::RequestPriority::IDLE, |
| nullptr, TRAFFIC_ANNOTATION_FOR_TESTS); |
| request->SetLoadFlags(net::LOAD_MAIN_FRAME_DEPRECATED); |
| request->set_method("GET"); |
| io_data()->request_options()->SetSecureSession("fake-session"); |
| net::HttpRequestHeaders headers; |
| net::ProxyRetryInfoMap proxy_retry_info; |
| network_delegate()->NotifyBeforeSendHeaders( |
| request.get(), data_reduction_proxy_info, proxy_retry_info, &headers); |
| DataReductionProxyData* data = DataReductionProxyData::GetData(*request); |
| if (!test.data_reduction_proxy_enabled || !test.used_direct) { |
| EXPECT_FALSE(data); |
| } else { |
| EXPECT_TRUE(data); |
| EXPECT_TRUE(data->used_data_reduction_proxy()); |
| EXPECT_EQ("fake-session", data->session_key()); |
| EXPECT_EQ(GURL(kTestURL), data->request_url()); |
| EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_4G, |
| data->effective_connection_type()); |
| EXPECT_TRUE(data->page_id()); |
| } |
| } |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, RedirectRequestDataCleared) { |
| Init(USE_INSECURE_PROXY, false); |
| net::ProxyInfo data_reduction_proxy_info; |
| data_reduction_proxy_info.UseProxyServer( |
| params()->proxies_for_http().front().proxy_server()); |
| |
| // Main frame loaded. Lo-Fi should be used. |
| net::HttpRequestHeaders headers_original; |
| net::ProxyRetryInfoMap proxy_retry_info; |
| |
| test_network_quality_tracker()->ReportEffectiveConnectionTypeForTesting( |
| net::EFFECTIVE_CONNECTION_TYPE_OFFLINE); |
| base::RunLoop().RunUntilIdle(); |
| |
| std::unique_ptr<net::URLRequest> request = |
| context()->CreateRequest(GURL(kTestURL), net::RequestPriority::IDLE, |
| nullptr, TRAFFIC_ANNOTATION_FOR_TESTS); |
| request->SetLoadFlags(net::LOAD_MAIN_FRAME_DEPRECATED); |
| lofi_decider()->SetIsUsingLoFi(true); |
| io_data()->request_options()->SetSecureSession("fake-session"); |
| |
| // Call network delegate methods to ensure that appropriate chrome proxy |
| // headers get added/removed. |
| network_delegate()->NotifyBeforeStartTransaction( |
| request.get(), |
| base::BindOnce(&DataReductionProxyNetworkDelegateTest::DelegateStageDone, |
| base::Unretained(this)), |
| &headers_original); |
| network_delegate()->NotifyBeforeSendHeaders( |
| request.get(), data_reduction_proxy_info, proxy_retry_info, |
| &headers_original); |
| DataReductionProxyData* data = DataReductionProxyData::GetData(*request); |
| uint64_t original_page_id = data->page_id().value(); |
| // Artificially add a RequestInfo for sake of testing that it is persisted. |
| data->add_request_info(DataReductionProxyData::RequestInfo( |
| DataReductionProxyData::RequestInfo::Protocol::HTTP, false, |
| base::TimeDelta(), base::TimeDelta(), base::TimeDelta())); |
| |
| EXPECT_TRUE(data); |
| EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_OFFLINE, |
| data->effective_connection_type()); |
| EXPECT_TRUE(data->used_data_reduction_proxy()); |
| EXPECT_EQ(GURL(kTestURL), data->request_url()); |
| EXPECT_EQ("fake-session", data->session_key()); |
| EXPECT_TRUE(data->lofi_requested()); |
| |
| data_reduction_proxy_info.UseNamedProxy("port.of.other.proxy"); |
| |
| // Simulate a redirect even though the same URL is used. Should clear |
| // DataReductionProxyData. |
| network_delegate()->NotifyBeforeRedirect(request.get(), GURL(kTestURL)); |
| data = DataReductionProxyData::GetData(*request); |
| EXPECT_FALSE(data && data->used_data_reduction_proxy()); |
| // page_id and request_info should be persisted across redirects. |
| EXPECT_EQ(original_page_id, data->page_id().value()); |
| EXPECT_EQ(1u, data->request_info().size()); |
| |
| // Call NotifyBeforeSendHeaders again with different proxy info to check that |
| // new data isn't added. Use a new set of headers since the redirected HTTP |
| // jobs do not reuse headers from the previous jobs. Also, call network |
| // delegate methods to ensure that appropriate chrome proxy headers get |
| // added/removed. |
| net::HttpRequestHeaders headers_redirect; |
| network_delegate()->NotifyBeforeStartTransaction( |
| request.get(), |
| base::BindOnce(&DataReductionProxyNetworkDelegateTest::DelegateStageDone, |
| base::Unretained(this)), |
| &headers_redirect); |
| network_delegate()->NotifyBeforeSendHeaders( |
| request.get(), data_reduction_proxy_info, proxy_retry_info, |
| &headers_redirect); |
| data = DataReductionProxyData::GetData(*request); |
| EXPECT_FALSE(data); |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, NetHistograms) { |
| Init(USE_INSECURE_PROXY, false); |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitWithFeatures( |
| {previews::features::kPreviews, |
| features::kDataReductionProxyDecidesTransform}, |
| {}); |
| |
| base::HistogramTester histogram_tester; |
| |
| std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=" + |
| base::Int64ToString(kOriginalContentLength) + "\r\n\r\n"; |
| |
| std::unique_ptr<net::URLRequest> fake_request(FetchURLRequest( |
| GURL(kTestURL), nullptr, response_headers, kResponseContentLength, 0)); |
| fake_request->SetLoadFlags(fake_request->load_flags() | |
| net::LOAD_MAIN_FRAME_DEPRECATED); |
| |
| base::TimeDelta freshness_lifetime = |
| fake_request->response_info().headers->GetFreshnessLifetimes( |
| fake_request->response_info().response_time).freshness; |
| |
| histogram_tester.ExpectUniqueSample(kOriginalValidOCLHistogramName, |
| kOriginalContentLength, 1); |
| histogram_tester.ExpectUniqueSample(kOriginalInsecureViaDRPHistogramName, |
| kOriginalContentLength, 1); |
| histogram_tester.ExpectUniqueSample( |
| kDifferenceValidOCLHistogramName, |
| kOriginalContentLength - kResponseContentLength, 1); |
| histogram_tester.ExpectUniqueSample(kReceivedHistogramName, |
| kResponseContentLength, 1); |
| histogram_tester.ExpectUniqueSample(kReceivedInsecureViaDRPHistogramName, |
| kResponseContentLength, 1); |
| histogram_tester.ExpectTotalCount(kReceivedInsecureDirectHistogramName, 0); |
| histogram_tester.ExpectTotalCount(kReceivedSecureDirectHistogramName, 0); |
| histogram_tester.ExpectTotalCount(kReceivedVideoInsecureViaDRPHistogramName, |
| 0); |
| histogram_tester.ExpectUniqueSample(kReceivedInsecureViaDRPHistogramName, |
| kResponseContentLength, 1); |
| histogram_tester.ExpectUniqueSample(kFreshnessLifetimeHistogramName, |
| freshness_lifetime.InSeconds(), 1); |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, NetVideoHistograms) { |
| Init(USE_INSECURE_PROXY, false); |
| |
| base::HistogramTester histogram_tester; |
| |
| // Check video |
| std::string video_response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Content-Type: video/mp4\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=" + |
| base::Int64ToString(kOriginalContentLength) + "\r\n\r\n"; |
| |
| FetchURLRequest(GURL(kTestURL), nullptr, video_response_headers, |
| kResponseContentLength, 0); |
| |
| histogram_tester.ExpectUniqueSample(kReceivedInsecureViaDRPHistogramName, |
| kResponseContentLength, 1); |
| histogram_tester.ExpectTotalCount(kReceivedInsecureDirectHistogramName, 0); |
| histogram_tester.ExpectTotalCount(kReceivedInsecureBypassedHistogramName, 0); |
| histogram_tester.ExpectTotalCount(kReceivedInsecureOtherHistogramName, 0); |
| histogram_tester.ExpectTotalCount(kReceivedSecureViaDRPHistogramName, 0); |
| histogram_tester.ExpectTotalCount(kReceivedSecureDirectHistogramName, 0); |
| histogram_tester.ExpectTotalCount(kReceivedSecureBypassedHistogramName, 0); |
| histogram_tester.ExpectTotalCount(kReceivedSecureOtherHistogramName, 0); |
| histogram_tester.ExpectUniqueSample(kReceivedVideoInsecureViaDRPHistogramName, |
| kResponseContentLength, 1); |
| histogram_tester.ExpectTotalCount(kReceivedVideoInsecureDirectHistogramName, |
| 0); |
| histogram_tester.ExpectTotalCount(kReceivedVideoInsecureBypassedHistogramName, |
| 0); |
| histogram_tester.ExpectTotalCount(kReceivedVideoInsecureOtherHistogramName, |
| 0); |
| histogram_tester.ExpectTotalCount(kReceivedVideoSecureViaDRPHistogramName, 0); |
| histogram_tester.ExpectTotalCount(kReceivedVideoSecureDirectHistogramName, 0); |
| histogram_tester.ExpectTotalCount(kReceivedVideoSecureBypassedHistogramName, |
| 0); |
| histogram_tester.ExpectTotalCount(kReceivedVideoSecureOtherHistogramName, 0); |
| } |
| |
| struct ExpectedHistogram { |
| const std::string name; |
| int64_t value; |
| }; |
| |
| bool operator<(const ExpectedHistogram& hist1, const ExpectedHistogram& hist2) { |
| return hist1.name < hist2.name; |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, DetailedNetHistograms) { |
| // List of all the histograms we're concerned with. |
| // Each test case can define interesting histograms from this list. |
| // Any histogram not called out in an invidual test will have an expected |
| // count of 0 samples. |
| const std::set<std::string> all_new_net_histograms{ |
| // HTTP received content length |
| kReceivedInsecureDirectHistogramName, |
| kReceivedInsecureViaDRPHistogramName, |
| kReceivedInsecureBypassedHistogramName, |
| kReceivedInsecureOtherHistogramName, |
| // HTTP video received content length |
| kReceivedVideoInsecureDirectHistogramName, |
| kReceivedVideoInsecureViaDRPHistogramName, |
| kReceivedVideoInsecureBypassedHistogramName, |
| kReceivedVideoInsecureOtherHistogramName, |
| // HTTPS received content length |
| kReceivedSecureDirectHistogramName, kReceivedSecureViaDRPHistogramName, |
| kReceivedSecureBypassedHistogramName, kReceivedSecureOtherHistogramName, |
| // HTTPS video received content length |
| kReceivedVideoSecureDirectHistogramName, |
| kReceivedVideoSecureViaDRPHistogramName, |
| kReceivedVideoSecureBypassedHistogramName, |
| kReceivedVideoSecureOtherHistogramName, |
| // HTTP Original content length |
| kOriginalInsecureDirectHistogramName, |
| kOriginalInsecureViaDRPHistogramName, |
| kOriginalInsecureBypassedHistogramName, |
| kOriginalInsecureOtherHistogramName, |
| // HTTP video Original content length |
| kOriginalVideoInsecureDirectHistogramName, |
| kOriginalVideoInsecureViaDRPHistogramName, |
| kOriginalVideoInsecureBypassedHistogramName, |
| kOriginalVideoInsecureOtherHistogramName, |
| // HTTPS Original content length |
| kOriginalSecureDirectHistogramName, kOriginalSecureViaDRPHistogramName, |
| kOriginalSecureBypassedHistogramName, kOriginalSecureOtherHistogramName, |
| // HTTPS video Original content length |
| kOriginalVideoSecureDirectHistogramName, |
| kOriginalVideoSecureViaDRPHistogramName, |
| kOriginalVideoSecureBypassedHistogramName, |
| kOriginalVideoSecureOtherHistogramName, |
| }; |
| |
| const struct { |
| const std::string name; |
| bool is_video; |
| bool is_https; |
| ProxyTestConfig proxy_config; |
| int64_t original_content_length; |
| int64_t content_length; |
| // Any histogram listed in all_new_net_histograms but not here should have |
| // no samples. |
| const std::set<ExpectedHistogram> expected_histograms; |
| } tests[] = { |
| {"HTTP nonvideo request via DRP", |
| false, |
| false, |
| USE_INSECURE_PROXY, |
| kOriginalContentLength, |
| kResponseContentLength, |
| { |
| {kReceivedInsecureViaDRPHistogramName, kResponseContentLength}, |
| {kOriginalInsecureViaDRPHistogramName, kOriginalContentLength}, |
| }}, |
| {"HTTP video request via DRP", |
| true, |
| false, |
| USE_INSECURE_PROXY, |
| kOriginalContentLength, |
| kResponseContentLength, |
| { |
| {kReceivedInsecureViaDRPHistogramName, kResponseContentLength}, |
| {kOriginalInsecureViaDRPHistogramName, kOriginalContentLength}, |
| {kReceivedVideoInsecureViaDRPHistogramName, kResponseContentLength}, |
| {kOriginalVideoInsecureViaDRPHistogramName, kOriginalContentLength}, |
| }}, |
| {"DRP not configured for http", |
| false, |
| false, |
| BYPASS_PROXY, |
| kOriginalContentLength, |
| kResponseContentLength, |
| { |
| {kReceivedInsecureDirectHistogramName, kResponseContentLength}, |
| {kOriginalInsecureDirectHistogramName, kResponseContentLength}, |
| }}, |
| {"DRP not configured for http video", |
| true, |
| false, |
| BYPASS_PROXY, |
| kOriginalContentLength, |
| kResponseContentLength, |
| { |
| {kReceivedInsecureDirectHistogramName, kResponseContentLength}, |
| {kOriginalInsecureDirectHistogramName, kResponseContentLength}, |
| {kReceivedVideoInsecureDirectHistogramName, kResponseContentLength}, |
| {kOriginalVideoInsecureDirectHistogramName, kResponseContentLength}, |
| }}, |
| {"nonvideo over https", |
| false, |
| true, |
| BYPASS_PROXY, |
| kOriginalContentLength, |
| kResponseContentLength, |
| { |
| {kReceivedSecureDirectHistogramName, kResponseContentLength}, |
| {kOriginalSecureDirectHistogramName, kResponseContentLength}, |
| }}, |
| {"video over https", |
| true, |
| true, |
| BYPASS_PROXY, |
| kOriginalContentLength, |
| kResponseContentLength, |
| { |
| {kReceivedSecureDirectHistogramName, kResponseContentLength}, |
| {kOriginalSecureDirectHistogramName, kResponseContentLength}, |
| {kReceivedVideoSecureDirectHistogramName, kResponseContentLength}, |
| {kOriginalVideoSecureDirectHistogramName, kResponseContentLength}, |
| }}, |
| }; |
| |
| for (const auto& test : tests) { |
| LOG(INFO) << "NetHistograms: " << test.name; |
| Init(test.proxy_config, false); |
| base::HistogramTester histogram_tester; |
| |
| GURL test_url = GURL(kTestURL); |
| |
| if (test.is_https) { |
| test_url = GURL(kSecureTestURL); |
| mock_socket_factory()->AddSSLSocketDataProvider( |
| ssl_socket_data_provider()); |
| } |
| |
| std::string via_header; |
| std::string ocl_header; |
| |
| if (test.proxy_config == USE_INSECURE_PROXY) { |
| via_header = "Via: 1.1 Chrome-Compression-Proxy\r\n"; |
| ocl_header = "Chrome-Proxy: ofcl=" + |
| base::Int64ToString(kOriginalContentLength) + "\r\n"; |
| } |
| if (test.is_video) { |
| // Check video |
| std::string video_response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Content-Type: video/mp4\r\n" + |
| via_header + ocl_header + "\r\n"; |
| |
| FetchURLRequest(test_url, nullptr, video_response_headers, |
| kResponseContentLength, 0); |
| } else { |
| // Check https |
| std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" + |
| via_header + ocl_header + "\r\n\r\n"; |
| |
| FetchURLRequest(test_url, nullptr, response_headers, |
| kResponseContentLength, 0); |
| } |
| |
| for (const auto& histogram : all_new_net_histograms) { |
| auto expected_it = test.expected_histograms.find({histogram, 0}); |
| if (expected_it == test.expected_histograms.end()) { |
| histogram_tester.ExpectTotalCount(histogram, 0); |
| } else { |
| histogram_tester.ExpectUniqueSample(expected_it->name, |
| expected_it->value, 1); |
| } |
| } |
| } |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, |
| NonServerLoFiResponseDoesNotTriggerInfobar) { |
| Init(USE_INSECURE_PROXY, false); |
| |
| ClearLoFiUIService(); |
| lofi_decider()->SetIsUsingClientLoFi(false); |
| std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=200\r\n\r\n"; |
| |
| auto request = |
| FetchURLRequest(GURL(kTestURL), nullptr, response_headers, 140, 0); |
| |
| EXPECT_FALSE(DataReductionProxyData::GetData(*request)->lofi_received()); |
| VerifyDidNotifyLoFiResponse(false); |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, |
| ServerLoFiResponseDoesTriggerInfobar) { |
| Init(USE_INSECURE_PROXY, false); |
| |
| ClearLoFiUIService(); |
| lofi_decider()->SetIsUsingClientLoFi(false); |
| std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=200\r\n" |
| "Chrome-Proxy-Content-Transform: empty-image\r\n\r\n"; |
| |
| auto request = |
| FetchURLRequest(GURL(kTestURL), nullptr, response_headers, 140, 0); |
| |
| EXPECT_TRUE(DataReductionProxyData::GetData(*request)->lofi_received()); |
| VerifyDidNotifyLoFiResponse(true); |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, |
| NonClientLoFiResponseDoesNotTriggerInfobar) { |
| Init(USE_INSECURE_PROXY, false); |
| |
| ClearLoFiUIService(); |
| lofi_decider()->SetIsUsingClientLoFi(false); |
| |
| FetchURLRequest(GURL(kTestURL), nullptr, |
| "HTTP/1.1 206 Partial Content\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Content-Range: bytes 0-139/2048\r\n\r\n", |
| 140, 0); |
| |
| VerifyDidNotifyLoFiResponse(false); |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, |
| ClientLoFiCompleteResponseDoesNotTriggerInfobar) { |
| Init(USE_INSECURE_PROXY, false); |
| |
| const char* const test_response_headers[] = { |
| "HTTP/1.1 200 OK\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", |
| |
| "HTTP/1.1 204 No Content\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", |
| |
| "HTTP/1.1 404 Not Found\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", |
| |
| "HTTP/1.1 206 Partial Content\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Content-Range: bytes 0-139/140\r\n\r\n", |
| }; |
| |
| for (const char* headers : test_response_headers) { |
| ClearLoFiUIService(); |
| lofi_decider()->SetIsUsingClientLoFi(true); |
| FetchURLRequest(GURL(kTestURL), nullptr, headers, 140, 0); |
| VerifyDidNotifyLoFiResponse(false); |
| } |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, |
| ClientLoFiPartialRangeDoesTriggerInfobar) { |
| Init(USE_INSECURE_PROXY, false); |
| |
| const char* const test_response_headers[] = { |
| "HTTP/1.1 206 Partial Content\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Content-Range: bytes 0-139/2048\r\n\r\n", |
| |
| "HTTP/1.1 206 Partial Content\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Content-Range: bytes 5-144/145\r\n\r\n", |
| }; |
| |
| for (const char* headers : test_response_headers) { |
| ClearLoFiUIService(); |
| lofi_decider()->SetIsUsingClientLoFi(true); |
| FetchURLRequest(GURL(kTestURL), nullptr, headers, 140, 0); |
| VerifyDidNotifyLoFiResponse(true); |
| } |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, |
| TestLoFiTransformationTypeHistogram) { |
| const std::string regular_response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Content-Length: 140\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=200\r\n" |
| "Cache-Control: max-age=0\r\n" |
| "Vary: accept-encoding\r\n\r\n"; |
| |
| Init(USE_INSECURE_PROXY, false); |
| const char kLoFiTransformationTypeHistogram[] = |
| "DataReductionProxy.LoFi.TransformationType"; |
| base::HistogramTester histogram_tester; |
| |
| net::HttpRequestHeaders request_headers; |
| request_headers.SetHeader("chrome-proxy-accept-transform", "lite-page"); |
| lofi_decider()->ignore_is_using_data_reduction_proxy_check(); |
| FetchURLRequest(GURL(kTestURL), &request_headers, regular_response_headers, |
| 140, 0); |
| histogram_tester.ExpectBucketCount(kLoFiTransformationTypeHistogram, |
| NO_TRANSFORMATION_LITE_PAGE_REQUESTED, 1); |
| |
| std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Chrome-Proxy-Content-Transform: lite-page\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=200\r\n"; |
| |
| response_headers += "\r\n"; |
| auto request = |
| FetchURLRequest(GURL(kTestURL), nullptr, response_headers, 140, 0); |
| EXPECT_TRUE(DataReductionProxyData::GetData(*request)->lite_page_received()); |
| |
| histogram_tester.ExpectBucketCount(kLoFiTransformationTypeHistogram, |
| LITE_PAGE, 1); |
| } |
| |
| // Test that Brotli is not added to the accept-encoding header when it is |
| // disabled globally. |
| TEST_F(DataReductionProxyNetworkDelegateTest, |
| BrotliAdvertisement_BrotliDisabled) { |
| Init(USE_SECURE_PROXY, false /* enable_brotli_globally */); |
| |
| ReadBrotliFile(); |
| |
| std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Content-Length: 140\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=200\r\n" |
| "Cache-Control: max-age=1200\r\n" |
| "Vary: accept-encoding\r\n"; |
| response_headers += "\r\n"; |
| |
| // Use secure sockets when fetching the request since Brotli is only enabled |
| // for secure connections. |
| FetchURLRequestAndVerifyBrotli(nullptr, response_headers, false, false); |
| } |
| |
| // Test that Brotli is not added to the accept-encoding header when the request |
| // is fetched from an insecure proxy. |
| TEST_F(DataReductionProxyNetworkDelegateTest, |
| BrotliAdvertisementInsecureProxy) { |
| Init(USE_INSECURE_PROXY, true /* enable_brotli_globally */); |
| std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Content-Length: 140\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=200\r\n" |
| "Cache-Control: max-age=1200\r\n" |
| "Vary: accept-encoding\r\n"; |
| response_headers += "\r\n"; |
| |
| // Use secure sockets when fetching the request since Brotli is only enabled |
| // for secure connections. |
| std::unique_ptr<net::URLRequest> request( |
| FetchURLRequest(GURL(kTestURL), nullptr, response_headers, 140, 0)); |
| EXPECT_EQ(140, request->received_response_content_length()); |
| EXPECT_NE(0, request->GetTotalSentBytes()); |
| EXPECT_NE(0, request->GetTotalReceivedBytes()); |
| EXPECT_FALSE(request->was_cached()); |
| // Brotli should be added to Accept Encoding header only if secure proxy is in |
| VerifyBrotliPresent(request.get(), false); |
| } |
| |
| // Test that Brotli is not added to the accept-encoding header when it is |
| // disabled via data reduction proxy field trial. |
| TEST_F(DataReductionProxyNetworkDelegateTest, |
| BrotliAdvertisementDisabledViaFieldTrial) { |
| Init(USE_SECURE_PROXY, true /* enable_brotli_globally */); |
| |
| base::FieldTrialList field_trial_list(nullptr); |
| ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial( |
| "DataReductionProxyBrotliAcceptEncoding", "Disabled")); |
| |
| std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Content-Length: 140\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=200\r\n" |
| "Cache-Control: max-age=1200\r\n" |
| "Vary: accept-encoding\r\n"; |
| response_headers += "\r\n"; |
| |
| FetchURLRequestAndVerifyBrotli(nullptr, response_headers, false, false); |
| FetchURLRequestAndVerifyBrotli(nullptr, response_headers, true, false); |
| } |
| |
| // Test that Brotli is correctly added to the accept-encoding header when it is |
| // enabled globally. |
| TEST_F(DataReductionProxyNetworkDelegateTest, BrotliAdvertisement) { |
| Init(USE_SECURE_PROXY, true /* enable_brotli_globally */); |
| |
| std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=200\r\n" |
| "Cache-Control: max-age=1200\r\n" |
| "Content-Encoding: br\r\n" |
| "Vary: accept-encoding\r\n"; |
| response_headers += "\r\n"; |
| |
| FetchURLRequestAndVerifyBrotli(nullptr, response_headers, false, true); |
| FetchURLRequestAndVerifyBrotli(nullptr, response_headers, true, true); |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, IncrementingMainFramePageId) { |
| // This is unaffacted by brotil and insecure proxy. |
| Init(USE_SECURE_PROXY, false /* enable_brotli_globally */); |
| |
| io_data()->request_options()->SetSecureSession("new-session"); |
| uint64_t page_id = io_data()->request_options()->GeneratePageId(); |
| |
| FetchURLRequestAndVerifyPageIdDirective(++page_id, false); |
| |
| FetchURLRequestAndVerifyPageIdDirective(++page_id, false); |
| |
| FetchURLRequestAndVerifyPageIdDirective(++page_id, false); |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, ResetSessionResetsId) { |
| // This is unaffacted by brotil and insecure proxy. |
| Init(USE_SECURE_PROXY, false /* enable_brotli_globally */); |
| |
| io_data()->request_options()->SetSecureSession("new-session"); |
| uint64_t page_id = io_data()->request_options()->GeneratePageId(); |
| |
| FetchURLRequestAndVerifyPageIdDirective(++page_id, false); |
| |
| io_data()->request_options()->SetSecureSession("new-session-2"); |
| |
| page_id = io_data()->request_options()->GeneratePageId(); |
| FetchURLRequestAndVerifyPageIdDirective(++page_id, false); |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, SubResourceNoPageId) { |
| // This is unaffacted by brotil and insecure proxy. |
| Init(USE_SECURE_PROXY, false /* enable_brotli_globally */); |
| io_data()->request_options()->SetSecureSession("new-session"); |
| FetchURLRequestAndVerifyPageIdDirective(base::Optional<uint64_t>(), false); |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, RedirectSharePid) { |
| // This is unaffacted by brotil and insecure proxy. |
| Init(USE_SECURE_PROXY, false /* enable_brotli_globally */); |
| |
| io_data()->request_options()->SetSecureSession("new-session"); |
| uint64_t page_id = io_data()->request_options()->GeneratePageId(); |
| |
| FetchURLRequestAndVerifyPageIdDirective(++page_id, true); |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, |
| SessionChangeResetsPageIDOnRedirect) { |
| // This test calls directly into network delegate as it is difficult to mock |
| // state changing in between redirects within an URLRequest's lifetime. |
| |
| // This is unaffacted by brotil and insecure proxy. |
| Init(USE_INSECURE_PROXY, false /* enable_brotli_globally */); |
| net::ProxyInfo data_reduction_proxy_info; |
| data_reduction_proxy_info.UseProxyServer( |
| params()->proxies_for_http().front().proxy_server()); |
| |
| std::unique_ptr<net::URLRequest> request = |
| context()->CreateRequest(GURL(kTestURL), net::RequestPriority::IDLE, |
| nullptr, TRAFFIC_ANNOTATION_FOR_TESTS); |
| request->SetLoadFlags(net::LOAD_MAIN_FRAME_DEPRECATED); |
| io_data()->request_options()->SetSecureSession("fake-session"); |
| |
| uint64_t page_id = io_data()->request_options()->GeneratePageId(); |
| |
| net::HttpRequestHeaders headers; |
| net::ProxyRetryInfoMap proxy_retry_info; |
| |
| // Send a request and verify the page ID is 1. |
| network_delegate()->NotifyBeforeStartTransaction( |
| request.get(), |
| base::BindOnce(&DataReductionProxyNetworkDelegateTest::DelegateStageDone, |
| base::Unretained(this)), |
| &headers); |
| network_delegate()->NotifyBeforeSendHeaders( |
| request.get(), data_reduction_proxy_info, proxy_retry_info, &headers); |
| DataReductionProxyData* data = DataReductionProxyData::GetData(*request); |
| EXPECT_TRUE(data_reduction_proxy_info.is_http()); |
| EXPECT_EQ(++page_id, data->page_id().value()); |
| |
| // Send a second request and verify the page ID incremements. |
| request = context()->CreateRequest(GURL(kTestURL), net::RequestPriority::IDLE, |
| nullptr, TRAFFIC_ANNOTATION_FOR_TESTS); |
| request->SetLoadFlags(net::LOAD_MAIN_FRAME_DEPRECATED); |
| |
| network_delegate()->NotifyBeforeStartTransaction( |
| request.get(), |
| base::BindOnce(&DataReductionProxyNetworkDelegateTest::DelegateStageDone, |
| base::Unretained(this)), |
| &headers); |
| network_delegate()->NotifyBeforeSendHeaders( |
| request.get(), data_reduction_proxy_info, proxy_retry_info, &headers); |
| data = DataReductionProxyData::GetData(*request); |
| EXPECT_EQ(++page_id, data->page_id().value()); |
| |
| // Verify that redirects are the same page ID. |
| network_delegate()->NotifyBeforeRedirect(request.get(), GURL(kTestURL)); |
| network_delegate()->NotifyBeforeSendHeaders( |
| request.get(), data_reduction_proxy_info, proxy_retry_info, &headers); |
| data = DataReductionProxyData::GetData(*request); |
| EXPECT_EQ(page_id, data->page_id().value()); |
| |
| network_delegate()->NotifyBeforeRedirect(request.get(), GURL(kTestURL)); |
| io_data()->request_options()->SetSecureSession("new-session"); |
| |
| page_id = io_data()->request_options()->GeneratePageId(); |
| network_delegate()->NotifyBeforeSendHeaders( |
| request.get(), data_reduction_proxy_info, proxy_retry_info, &headers); |
| data = DataReductionProxyData::GetData(*request); |
| EXPECT_EQ(++page_id, data->page_id().value()); |
| } |
| |
| // Test that effective connection type is correctly added to the request |
| // headers when it is enabled using field trial. The server is varying on the |
| // effective connection type (ECT). |
| TEST_F(DataReductionProxyNetworkDelegateTest, ECTHeaderEnabledWithVary) { |
| Init(USE_SECURE_PROXY, false /* enable_brotli_globally */); |
| |
| std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Content-Length: 140\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Cache-Control: max-age=1200\r\n" |
| "Vary: chrome-proxy-ect\r\n" |
| "Chrome-Proxy: ofcl=200\r\n\r\n"; |
| |
| int response_body_size = 140; |
| std::string response_body(base::checked_cast<size_t>(response_body_size), |
| ' '); |
| |
| std::vector<net::MockRead> reads_list; |
| std::vector<std::string> mock_writes; |
| std::vector<net::MockWrite> writes_list; |
| |
| std::vector<net::EffectiveConnectionType> effective_connection_types; |
| effective_connection_types.push_back(net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G); |
| effective_connection_types.push_back(net::EFFECTIVE_CONNECTION_TYPE_2G); |
| |
| BuildSocket(response_headers, response_body, true, effective_connection_types, |
| &reads_list, &mock_writes, &writes_list); |
| |
| // Add 2 socket providers since 2 requests in this test are fetched from the |
| // network. |
| FetchURLRequestAndVerifyECTHeader(effective_connection_types[0], true, false); |
| |
| // When the ECT is set to the same value, fetching the same resource should |
| // result in a cache hit. |
| FetchURLRequestAndVerifyECTHeader(effective_connection_types[0], true, true); |
| |
| // When the ECT is set to a different value, the response should not be |
| // served from the cache. |
| FetchURLRequestAndVerifyECTHeader(effective_connection_types[1], true, false); |
| } |
| |
| // Test that effective connection type is correctly added to the request |
| // headers when it is enabled using field trial. The server is not varying on |
| // the effective connection type (ECT). |
| TEST_F(DataReductionProxyNetworkDelegateTest, ECTHeaderEnabledWithoutVary) { |
| Init(USE_SECURE_PROXY, false /* enable_brotli_globally */); |
| |
| std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Content-Length: 140\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Cache-Control: max-age=1200\r\n" |
| "Chrome-Proxy: ofcl=200\r\n\r\n"; |
| |
| int response_body_size = 140; |
| std::string response_body(base::checked_cast<size_t>(response_body_size), |
| ' '); |
| |
| std::vector<net::MockRead> reads_list; |
| std::vector<std::string> mock_writes; |
| std::vector<net::MockWrite> writes_list; |
| |
| std::vector<net::EffectiveConnectionType> effective_connection_types; |
| effective_connection_types.push_back(net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G); |
| effective_connection_types.push_back(net::EFFECTIVE_CONNECTION_TYPE_2G); |
| |
| BuildSocket(response_headers, response_body, true, effective_connection_types, |
| &reads_list, &mock_writes, &writes_list); |
| |
| // Add 1 socket provider since 1 request in this test is fetched from the |
| // network. |
| FetchURLRequestAndVerifyECTHeader(effective_connection_types[0], true, false); |
| |
| // When the ECT is set to the same value, fetching the same resource should |
| // result in a cache hit. |
| FetchURLRequestAndVerifyECTHeader(effective_connection_types[0], true, true); |
| |
| // When the ECT is set to a different value, the response should still be |
| // served from the cache. |
| FetchURLRequestAndVerifyECTHeader(effective_connection_types[1], true, true); |
| } |
| |
| class DataReductionProxyNetworkDelegateClientLoFiTest : public testing::Test { |
| public: |
| DataReductionProxyNetworkDelegateClientLoFiTest() : baseline_savings_(0) {} |
| ~DataReductionProxyNetworkDelegateClientLoFiTest() override; |
| |
| void Reset() { |
| drp_test_context_.reset(); |
| mock_socket_factory_.reset(); |
| context_storage_.reset(); |
| |
| context_.reset(new net::TestURLRequestContext(true)); |
| context_storage_.reset(new net::URLRequestContextStorage(context_.get())); |
| mock_socket_factory_.reset(new net::MockClientSocketFactory()); |
| context_->set_client_socket_factory(mock_socket_factory_.get()); |
| |
| net::ProxyServer proxy_server = net::ProxyServer::FromURI( |
| "http://origin.net:80", net::ProxyServer::SCHEME_HTTP); |
| |
| proxy_resolution_service_ = |
| net::ProxyResolutionService::CreateFixedFromPacResult( |
| proxy_server.ToPacString(), TRAFFIC_ANNOTATION_FOR_TESTS); |
| context_->set_proxy_resolution_service(proxy_resolution_service_.get()); |
| |
| drp_test_context_ = |
| DataReductionProxyTestContext::Builder() |
| .WithURLRequestContext(context_.get()) |
| .WithMockClientSocketFactory(mock_socket_factory_.get()) |
| .WithProxiesForHttp({DataReductionProxyServer( |
| proxy_server, ProxyServer::UNSPECIFIED_TYPE)}) |
| .Build(); |
| |
| drp_test_context_->AttachToURLRequestContext(context_storage_.get()); |
| context_->Init(); |
| base::RunLoop().RunUntilIdle(); |
| |
| baseline_savings_ = |
| drp_test_context()->settings()->GetTotalHttpContentLengthSaved(); |
| } |
| |
| void SetUpLoFiDecider(bool is_client_lofi_image, |
| bool is_client_lofi_auto_reload) const { |
| std::unique_ptr<TestLoFiDecider> lofi_decider(new TestLoFiDecider()); |
| lofi_decider->SetIsUsingClientLoFi(is_client_lofi_image); |
| lofi_decider->SetIsClientLoFiAutoReload(is_client_lofi_auto_reload); |
| drp_test_context_->io_data()->set_lofi_decider( |
| std::unique_ptr<LoFiDecider>(std::move(lofi_decider))); |
| } |
| |
| int64_t GetSavings() const { |
| return drp_test_context()->settings()->GetTotalHttpContentLengthSaved() - |
| baseline_savings_; |
| } |
| |
| net::TestURLRequestContext* context() const { return context_.get(); } |
| net::MockClientSocketFactory* mock_socket_factory() const { |
| return mock_socket_factory_.get(); |
| } |
| DataReductionProxyTestContext* drp_test_context() const { |
| return drp_test_context_.get(); |
| } |
| |
| private: |
| base::MessageLoopForIO loop; |
| std::unique_ptr<net::TestURLRequestContext> context_; |
| std::unique_ptr<net::URLRequestContextStorage> context_storage_; |
| std::unique_ptr<net::MockClientSocketFactory> mock_socket_factory_; |
| std::unique_ptr<net::ProxyResolutionService> proxy_resolution_service_; |
| std::unique_ptr<DataReductionProxyTestContext> drp_test_context_; |
| int64_t baseline_savings_; |
| }; |
| |
| DataReductionProxyNetworkDelegateClientLoFiTest:: |
| ~DataReductionProxyNetworkDelegateClientLoFiTest() {} |
| |
| TEST_F(DataReductionProxyNetworkDelegateClientLoFiTest, DataSavingsNonDRP) { |
| const char kSimple200ResponseHeaders[] = |
| "HTTP/1.1 200 OK\r\n" |
| "Content-Length: 140\r\n\r\n"; |
| |
| const struct { |
| const char* headers; |
| size_t response_length; |
| bool is_client_lofi_image; |
| bool is_client_lofi_auto_reload; |
| int64_t expected_savings; |
| } tests[] = { |
| // 200 responses shouldn't see any savings. |
| {kSimple200ResponseHeaders, 140, false, false, 0}, |
| {kSimple200ResponseHeaders, 140, true, false, 0}, |
| |
| // Client Lo-Fi Auto-reload responses should see negative savings. |
| {kSimple200ResponseHeaders, 140, false, true, |
| -(static_cast<int64_t>(sizeof(kSimple200ResponseHeaders) - 1) + 140)}, |
| {kSimple200ResponseHeaders, 140, true, true, |
| -(static_cast<int64_t>(sizeof(kSimple200ResponseHeaders) - 1) + 140)}, |
| |
| // A range response that doesn't use Client Lo-Fi shouldn't see any |
| // savings. |
| {"HTTP/1.1 206 Partial Content\r\n" |
| "Content-Range: bytes 0-2047/10000\r\n" |
| "Content-Length: 2048\r\n\r\n", |
| 2048, false, false, 0}, |
| |
| // A Client Lo-Fi range response should see savings based on the |
| // Content-Range header. |
| {"HTTP/1.1 206 Partial Content\r\n" |
| "Content-Range: bytes 0-2047/10000\r\n" |
| "Content-Length: 2048\r\n\r\n", |
| 2048, true, false, 10000 - 2048}, |
| |
| // A Client Lo-Fi range response should see savings based on the |
| // Content-Range header, which in this case is 0 savings because the range |
| // response contained the entire resource. |
| {"HTTP/1.1 206 Partial Content\r\n" |
| "Content-Range: bytes 0-999/1000\r\n" |
| "Content-Length: 1000\r\n\r\n", |
| 1000, true, false, 0}, |
| |
| // Client Lo-Fi range responses that don't have a Content-Range with the |
| // full resource length shouldn't see any savings. |
| {"HTTP/1.1 206 Partial Content\r\n" |
| "Content-Length: 2048\r\n\r\n", |
| 2048, true, false, 0}, |
| {"HTTP/1.1 206 Partial Content\r\n" |
| "Content-Range: bytes 0-2047/*\r\n" |
| "Content-Length: 2048\r\n\r\n", |
| 2048, true, false, 0}, |
| {"HTTP/1.1 206 Partial Content\r\n" |
| "Content-Range: invalid_content_range\r\n" |
| "Content-Length: 2048\r\n\r\n", |
| 2048, true, false, 0}, |
| }; |
| |
| for (const auto& test : tests) { |
| Reset(); |
| SetUpLoFiDecider(test.is_client_lofi_image, |
| test.is_client_lofi_auto_reload); |
| |
| std::string response_body(test.response_length, 'a'); |
| net::MockRead reads[] = {net::MockRead(test.headers), |
| net::MockRead(response_body.c_str()), |
| net::MockRead(net::ASYNC, net::OK)}; |
| net::StaticSocketDataProvider socket(reads, base::span<net::MockWrite>()); |
| mock_socket_factory()->AddSocketDataProvider(&socket); |
| |
| net::TestDelegate test_delegate; |
| std::unique_ptr<net::URLRequest> request = context()->CreateRequest( |
| GURL("http://example.com"), net::RequestPriority::IDLE, &test_delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| request->SetLoadFlags(request->load_flags() | net::LOAD_BYPASS_PROXY); |
| |
| request->Start(); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(test.expected_savings, GetSavings()) << (&test - tests); |
| } |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateClientLoFiTest, DataSavingsThroughDRP) { |
| Reset(); |
| drp_test_context()->DisableWarmupURLFetch(); |
| drp_test_context()->EnableDataReductionProxyWithSecureProxyCheckSuccess(); |
| SetUpLoFiDecider(true, false); |
| |
| const char kHeaders[] = |
| "HTTP/1.1 206 Partial Content\r\n" |
| "Content-Range: bytes 0-2047/10000\r\n" |
| "Content-Length: 2048\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=3000\r\n\r\n"; |
| |
| std::string response_body(2048, 'a'); |
| net::MockRead reads[] = {net::MockRead(kHeaders), |
| net::MockRead(response_body.c_str()), |
| net::MockRead(net::ASYNC, net::OK)}; |
| net::StaticSocketDataProvider socket(reads, base::span<net::MockWrite>()); |
| mock_socket_factory()->AddSocketDataProvider(&socket); |
| |
| net::TestDelegate test_delegate; |
| std::unique_ptr<net::URLRequest> request = context()->CreateRequest( |
| GURL("http://example.com"), net::RequestPriority::IDLE, &test_delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| request->Start(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Since the Data Reduction Proxy is enabled, the length of the raw headers |
| // should be used in the estimated original size. The X-OCL should be ignored. |
| EXPECT_EQ(static_cast<int64_t>(net::HttpUtil::AssembleRawHeaders( |
| kHeaders, sizeof(kHeaders) - 1) |
| .size() + |
| 10000 - request->GetTotalReceivedBytes()), |
| GetSavings()); |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, TestAcceptTransformHistogram) { |
| Init(USE_INSECURE_PROXY, false); |
| base::HistogramTester histogram_tester; |
| |
| const std::string regular_response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Content-Length: 140\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=200\r\n" |
| "Cache-Control: max-age=0\r\n" |
| "Vary: accept-encoding\r\n\r\n"; |
| |
| const char kResponseHeadersWithCPCTFormat[] = |
| "HTTP/1.1 200 OK\r\n" |
| "Chrome-Proxy-Content-Transform: %s\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=200\r\n" |
| "\r\n"; |
| |
| // Verify lite page request. |
| net::HttpRequestHeaders request_headers; |
| request_headers.SetHeader("chrome-proxy-accept-transform", "lite-page"); |
| FetchURLRequest(GURL(kTestURL), &request_headers, regular_response_headers, |
| 140, 0); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.Protocol.AcceptTransform", 1); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.Protocol.AcceptTransform", |
| 0 /* LITE_PAGE_REQUESTED */, 1); |
| // Check legacy histogram too: |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.LoFi.TransformationType", |
| NO_TRANSFORMATION_LITE_PAGE_REQUESTED, 1); |
| |
| // Verify empty image request. |
| request_headers.SetHeader("chrome-proxy-accept-transform", "empty-image"); |
| FetchURLRequest(GURL(kTestURL), &request_headers, regular_response_headers, |
| 140, 0); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.Protocol.AcceptTransform", 2); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.Protocol.AcceptTransform", |
| 3 /* EMPTY_IMAGE_REQUESTED */, 1); |
| |
| // Verify lite page response. |
| auto request = FetchURLRequest( |
| GURL(kTestURL), nullptr, |
| base::StringPrintf(kResponseHeadersWithCPCTFormat, "lite-page"), 140, 0); |
| EXPECT_TRUE(DataReductionProxyData::GetData(*request)->lite_page_received()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.Protocol.AcceptTransform", 3); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.Protocol.AcceptTransform", |
| 1 /* LITE_PAGE_TRANSFORM_RECEIVED */, 1); |
| // Check legacy histogram too: |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.LoFi.TransformationType", LITE_PAGE, 1); |
| |
| // Verify page policy response. |
| std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Chrome-Proxy: page-policies=empty-image\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "Chrome-Proxy: ofcl=200\r\n" |
| "\r\n"; |
| request = FetchURLRequest(GURL(kTestURL), nullptr, response_headers, 140, 0); |
| EXPECT_FALSE(DataReductionProxyData::GetData(*request)->lite_page_received()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.Protocol.AcceptTransform", 4); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.Protocol.AcceptTransform", |
| 2 /* EMPTY_IMAGE_POLICY_DIRECTIVE_RECEIVED */, 1); |
| |
| // Verify empty image response. |
| request = FetchURLRequest( |
| GURL(kTestURL), nullptr, |
| base::StringPrintf(kResponseHeadersWithCPCTFormat, "empty-image"), 140, |
| 0); |
| EXPECT_TRUE(DataReductionProxyData::GetData(*request)->lofi_received()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.Protocol.AcceptTransform", 5); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.Protocol.AcceptTransform", |
| 4 /* EMPTY_IMAGE_TRANSFORM_RECEIVED */, 1); |
| |
| // Verify compressed-video request. |
| request_headers.SetHeader("chrome-proxy-accept-transform", |
| "compressed-video"); |
| FetchURLRequest(GURL(kTestURL), &request_headers, std::string(), 140, 0); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.Protocol.AcceptTransform", 6); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.Protocol.AcceptTransform", |
| 5 /* COMPRESSED_VIDEO_REQUESTED */, 1); |
| |
| // Verify compressed-video response. |
| request = FetchURLRequest( |
| GURL(kTestURL), nullptr, |
| base::StringPrintf(kResponseHeadersWithCPCTFormat, "compressed-video"), |
| 140, 0); |
| EXPECT_FALSE(DataReductionProxyData::GetData(*request)->lofi_received()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.Protocol.AcceptTransform", 7); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.Protocol.AcceptTransform", |
| 8 /* COMPRESSED_VIDEO_RECEIVED */, 1); |
| |
| // Verify response with an unknown CPAT value. |
| request = FetchURLRequest(GURL(kTestURL), nullptr, |
| base::StringPrintf(kResponseHeadersWithCPCTFormat, |
| "this-is-a-fake-transform"), |
| 140, 0); |
| EXPECT_FALSE(DataReductionProxyData::GetData(*request)->lofi_received()); |
| histogram_tester.ExpectTotalCount( |
| "DataReductionProxy.Protocol.AcceptTransform", 8); |
| histogram_tester.ExpectBucketCount( |
| "DataReductionProxy.Protocol.AcceptTransform", |
| 9 /* UNKNOWN_TRANSFORM_RECEIVED */, 1); |
| } |
| |
| TEST_F(DataReductionProxyNetworkDelegateTest, RecordNonContentToOtherHost) { |
| const std::string response_headers = |
| "HTTP/1.1 200 OK\r\n" |
| "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n" |
| "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n" |
| "Via: 1.1 Chrome-Compression-Proxy\r\n" |
| "\r\n"; |
| |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitAndEnableFeature( |
| data_reduction_proxy::features:: |
| kDataSaverSiteBreakdownUsingPageLoadMetrics); |
| Init(USE_INSECURE_PROXY, false); |
| EnableDataUsageReporting(); |
| auto test_resource_type_provider = |
| std::make_unique<TestResourceTypeProvider>(); |
| test_resource_type_provider->set_is_non_content_initiated_request(true); |
| io_data()->set_resource_type_provider(std::move(test_resource_type_provider)); |
| |
| FetchURLRequest(GURL(kTestURL), nullptr, response_headers, |
| kResponseContentLength, 0); |
| base::RunLoop().RunUntilIdle(); |
| DCHECK_EQ(response_headers.size() + kResponseContentLength, |
| GetOtherHostDataUsage()); |
| } |
| |
| } // namespace |
| |
| } // namespace data_reduction_proxy |