| // 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 <limits> |
| #include <utility> |
| |
| #include "base/memory/ptr_util.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/time/time.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h" |
| #include "components/data_reduction_proxy/core/browser/data_use_group.h" |
| #include "components/data_reduction_proxy/core/browser/data_use_group_provider.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_util.h" |
| #include "components/data_reduction_proxy/core/common/lofi_decider.h" |
| #include "net/base/load_flags.h" |
| #include "net/base/mime_util.h" |
| #include "net/http/http_request_headers.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/nqe/effective_connection_type.h" |
| #include "net/nqe/network_quality_estimator.h" |
| #include "net/proxy/proxy_info.h" |
| #include "net/proxy/proxy_server.h" |
| #include "net/proxy/proxy_service.h" |
| #include "net/url_request/url_request.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_status.h" |
| #include "url/gurl.h" |
| |
| namespace data_reduction_proxy { |
| |
| namespace { |
| |
| // |lofi_low_header_added| is set to true iff Lo-Fi "q=low" request header can |
| // be added to the Chrome proxy headers. |
| // |received_content_length| is the number of prefilter bytes received. |
| // |original_content_length| is the length of resource if accessed directly |
| // without data saver proxy. |
| // |freshness_lifetime| contains information on how long the resource will be |
| // fresh for and how long is the usability. |
| void RecordContentLengthHistograms(bool lofi_low_header_added, |
| bool is_https, |
| bool is_video, |
| int64_t received_content_length, |
| int64_t original_content_length, |
| const base::TimeDelta& freshness_lifetime) { |
| // Add the current resource to these histograms only when a valid |
| // X-Original-Content-Length header is present. |
| if (original_content_length >= 0) { |
| UMA_HISTOGRAM_COUNTS_1M("Net.HttpContentLengthWithValidOCL", |
| received_content_length); |
| UMA_HISTOGRAM_COUNTS_1M("Net.HttpOriginalContentLengthWithValidOCL", |
| original_content_length); |
| UMA_HISTOGRAM_COUNTS_1M("Net.HttpContentLengthDifferenceWithValidOCL", |
| original_content_length - received_content_length); |
| |
| // Populate Lo-Fi content length histograms. |
| if (lofi_low_header_added) { |
| UMA_HISTOGRAM_COUNTS_1M("Net.HttpContentLengthWithValidOCL.LoFiOn", |
| received_content_length); |
| UMA_HISTOGRAM_COUNTS_1M( |
| "Net.HttpOriginalContentLengthWithValidOCL.LoFiOn", |
| original_content_length); |
| UMA_HISTOGRAM_COUNTS_1M( |
| "Net.HttpContentLengthDifferenceWithValidOCL.LoFiOn", |
| original_content_length - received_content_length); |
| } |
| |
| } else { |
| // Presume the original content length is the same as the received content |
| // length if the X-Original-Content-Header is not present. |
| original_content_length = received_content_length; |
| } |
| UMA_HISTOGRAM_COUNTS_1M("Net.HttpContentLength", received_content_length); |
| if (is_https) { |
| UMA_HISTOGRAM_COUNTS_1M("Net.HttpContentLength.Https", |
| received_content_length); |
| } else { |
| UMA_HISTOGRAM_COUNTS_1M("Net.HttpContentLength.Http", |
| received_content_length); |
| } |
| if (is_video) { |
| UMA_HISTOGRAM_COUNTS_1M("Net.HttpContentLength.Video", |
| received_content_length); |
| } |
| UMA_HISTOGRAM_COUNTS_1M("Net.HttpOriginalContentLength", |
| original_content_length); |
| UMA_HISTOGRAM_COUNTS_1M("Net.HttpContentLengthDifference", |
| original_content_length - received_content_length); |
| UMA_HISTOGRAM_CUSTOM_COUNTS("Net.HttpContentFreshnessLifetime", |
| freshness_lifetime.InSeconds(), |
| base::TimeDelta::FromHours(1).InSeconds(), |
| base::TimeDelta::FromDays(30).InSeconds(), |
| 100); |
| if (freshness_lifetime.InSeconds() <= 0) |
| return; |
| UMA_HISTOGRAM_COUNTS_1M("Net.HttpContentLengthCacheable", |
| received_content_length); |
| if (freshness_lifetime.InHours() < 4) |
| return; |
| UMA_HISTOGRAM_COUNTS_1M("Net.HttpContentLengthCacheable4Hours", |
| received_content_length); |
| |
| if (freshness_lifetime.InHours() < 24) |
| return; |
| UMA_HISTOGRAM_COUNTS_1M("Net.HttpContentLengthCacheable24Hours", |
| received_content_length); |
| } |
| |
| // Given a |request| that went through the Data Reduction Proxy, this function |
| // estimates how many bytes would have been received if the response had been |
| // received directly from the origin using HTTP/1.1 with a content length of |
| // |adjusted_original_content_length|. |
| int64_t EstimateOriginalReceivedBytes(const net::URLRequest& request) { |
| if (request.was_cached() || !request.response_headers()) |
| return request.GetTotalReceivedBytes(); |
| |
| // TODO(sclittle): Remove headers added by Data Reduction Proxy when computing |
| // original size. http://crbug/535701. |
| return request.response_headers()->raw_headers().size() + |
| util::CalculateEffectiveOCL(request); |
| } |
| |
| // Verifies that the chrome proxy related request headers are set correctly. |
| // |via_chrome_proxy| is true if the request is being fetched via Chrome Data |
| // Saver proxy. |
| void VerifyHttpRequestHeaders(bool via_chrome_proxy, |
| const net::HttpRequestHeaders& headers) { |
| if (via_chrome_proxy) { |
| DCHECK(headers.HasHeader(chrome_proxy_header())); |
| DCHECK(headers.HasHeader(chrome_proxy_ect_header())); |
| } else { |
| DCHECK(!headers.HasHeader(chrome_proxy_header())); |
| DCHECK(!headers.HasHeader(chrome_proxy_accept_transform_header())); |
| DCHECK(!headers.HasHeader(chrome_proxy_ect_header())); |
| } |
| } |
| |
| } // namespace |
| |
| DataReductionProxyNetworkDelegate::DataReductionProxyNetworkDelegate( |
| std::unique_ptr<net::NetworkDelegate> network_delegate, |
| DataReductionProxyConfig* config, |
| DataReductionProxyRequestOptions* request_options, |
| const DataReductionProxyConfigurator* configurator) |
| : LayeredNetworkDelegate(std::move(network_delegate)), |
| data_reduction_proxy_config_(config), |
| data_reduction_proxy_bypass_stats_(nullptr), |
| data_reduction_proxy_request_options_(request_options), |
| data_reduction_proxy_io_data_(nullptr), |
| configurator_(configurator) { |
| DCHECK(data_reduction_proxy_config_); |
| DCHECK(data_reduction_proxy_request_options_); |
| DCHECK(configurator_); |
| } |
| |
| DataReductionProxyNetworkDelegate::~DataReductionProxyNetworkDelegate() { |
| } |
| |
| void DataReductionProxyNetworkDelegate::InitIODataAndUMA( |
| DataReductionProxyIOData* io_data, |
| DataReductionProxyBypassStats* bypass_stats) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(bypass_stats); |
| data_reduction_proxy_io_data_ = io_data; |
| data_reduction_proxy_bypass_stats_ = bypass_stats; |
| } |
| |
| void DataReductionProxyNetworkDelegate::OnBeforeURLRequestInternal( |
| net::URLRequest* request, |
| const net::CompletionCallback& callback, |
| GURL* new_url) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (data_use_group_provider_) { |
| // Creates and initializes a |DataUseGroup| for the |request| if it does not |
| // exist. Even though we do not use the |DataUseGroup| here, we want to |
| // associate one with a request as early as possible in case the frame |
| // associated with the request goes away before the request is completed. |
| scoped_refptr<DataUseGroup> data_use_group = |
| data_use_group_provider_->GetDataUseGroup(request); |
| data_use_group->Initialize(); |
| } |
| |
| // |data_reduction_proxy_io_data_| can be NULL for Webview. |
| if (data_reduction_proxy_io_data_ && |
| (request->load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED)) { |
| data_reduction_proxy_io_data_->SetLoFiModeActiveOnMainFrame(false); |
| } |
| } |
| |
| void DataReductionProxyNetworkDelegate::OnBeforeStartTransactionInternal( |
| net::URLRequest* request, |
| const net::CompletionCallback& callback, |
| net::HttpRequestHeaders* headers) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| if (!data_reduction_proxy_io_data_) |
| return; |
| if (!data_reduction_proxy_io_data_->IsEnabled()) |
| return; |
| |
| if (request->url().SchemeIsCryptographic() || |
| !request->url().SchemeIsHTTPOrHTTPS()) { |
| return; |
| } |
| |
| if (data_reduction_proxy_io_data_->resource_type_provider()) { |
| // Sets content type of |request| in the resource type provider, so it can |
| // be later used for determining the proxy that should be used for fetching |
| // |request|. |
| data_reduction_proxy_io_data_->resource_type_provider()->SetContentType( |
| *request); |
| } |
| |
| if (data_reduction_proxy_io_data_->lofi_decider()) { |
| data_reduction_proxy_io_data_->lofi_decider() |
| ->MaybeSetAcceptTransformHeader( |
| *request, data_reduction_proxy_config_->lofi_off(), headers); |
| } |
| |
| MaybeAddChromeProxyECTHeader(headers, *request); |
| } |
| |
| void DataReductionProxyNetworkDelegate::OnBeforeSendHeadersInternal( |
| net::URLRequest* request, |
| const net::ProxyInfo& proxy_info, |
| const net::ProxyRetryInfoMap& proxy_retry_info, |
| net::HttpRequestHeaders* headers) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(data_reduction_proxy_config_); |
| DCHECK(request); |
| |
| // If there was a redirect or request bypass, use the same page ID for both |
| // requests. As long as the session ID has not changed. Re-issued requests |
| // and client redirects will be assigned a new page ID as they are different |
| // URLRequests. |
| DataReductionProxyData* data = DataReductionProxyData::GetData(*request); |
| base::Optional<uint64_t> page_id; |
| if (data && data->session_key() == |
| data_reduction_proxy_request_options_->GetSecureSession()) { |
| page_id = data->page_id(); |
| } |
| |
| // Reset |request|'s DataReductionProxyData. |
| DataReductionProxyData::ClearData(request); |
| |
| if (params::IsIncludedInHoldbackFieldTrial()) { |
| if (!WasEligibleWithoutHoldback(*request, proxy_info, proxy_retry_info)) |
| return; |
| // For the holdback field trial, still log UMA as if the proxy was used. |
| data = DataReductionProxyData::GetDataAndCreateIfNecessary(request); |
| if (data) |
| data->set_used_data_reduction_proxy(true); |
| VerifyHttpRequestHeaders(false, *headers); |
| return; |
| } |
| |
| bool using_data_reduction_proxy = true; |
| // The following checks rule out direct, invalid, and other connection types. |
| if (!proxy_info.is_http() && !proxy_info.is_https() && |
| !proxy_info.is_quic()) { |
| using_data_reduction_proxy = false; |
| } else if (proxy_info.proxy_server().host_port_pair().IsEmpty()) { |
| using_data_reduction_proxy = false; |
| } else if (!data_reduction_proxy_config_->IsDataReductionProxy( |
| proxy_info.proxy_server(), nullptr)) { |
| using_data_reduction_proxy = false; |
| } |
| |
| LoFiDecider* lofi_decider = nullptr; |
| if (data_reduction_proxy_io_data_) |
| lofi_decider = data_reduction_proxy_io_data_->lofi_decider(); |
| |
| if (!using_data_reduction_proxy) { |
| if (lofi_decider) { |
| // If not using the data reduction proxy, strip the |
| // Chrome-Proxy-Accept-Transform header. |
| lofi_decider->RemoveAcceptTransformHeader(headers); |
| } |
| RemoveChromeProxyECTHeader(headers); |
| VerifyHttpRequestHeaders(false, *headers); |
| return; |
| } |
| |
| // Retrieves DataReductionProxyData from a request, creating a new instance |
| // if needed. |
| data = DataReductionProxyData::GetDataAndCreateIfNecessary(request); |
| if (data) { |
| data->set_used_data_reduction_proxy(true); |
| // Only set GURL, NQE and session key string for main frame requests since |
| // they are not needed for sub-resources. |
| if (request->load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED) { |
| data->set_session_key( |
| data_reduction_proxy_request_options_->GetSecureSession()); |
| data->set_request_url(request->url()); |
| if (request->context()->network_quality_estimator()) { |
| data->set_effective_connection_type(request->context() |
| ->network_quality_estimator() |
| ->GetEffectiveConnectionType()); |
| } |
| } |
| } |
| |
| if (data_reduction_proxy_io_data_ && |
| (request->load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED)) { |
| data_reduction_proxy_io_data_->SetLoFiModeActiveOnMainFrame( |
| lofi_decider ? lofi_decider->IsSlowPagePreviewRequested(*headers) |
| : false); |
| } |
| |
| if (data) { |
| data->set_lofi_requested( |
| lofi_decider ? lofi_decider->ShouldRecordLoFiUMA(*request) : false); |
| } |
| MaybeAddBrotliToAcceptEncodingHeader(proxy_info, headers, *request); |
| |
| // Generate a page ID for main frame requests that don't already have one. |
| // TODO(ryansturm): remove LOAD_MAIN_FRAME_DEPRECATED from d_r_p. |
| // crbug.com/709621 |
| if (request->load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED) { |
| if (!page_id) { |
| page_id = data_reduction_proxy_request_options_->GeneratePageId(); |
| } |
| data->set_page_id(page_id.value()); |
| } |
| |
| data_reduction_proxy_request_options_->AddRequestHeader(headers, page_id); |
| |
| if (lofi_decider) |
| lofi_decider->MaybeSetIgnorePreviewsBlacklistDirective(headers); |
| VerifyHttpRequestHeaders(true, *headers); |
| } |
| |
| void DataReductionProxyNetworkDelegate::OnBeforeRedirectInternal( |
| net::URLRequest* request, |
| const GURL& new_location) { |
| // Since this is after a redirect response, reset |request|'s |
| // DataReductionProxyData, but keep page ID and session. |
| // TODO(ryansturm): Change ClearData logic to have persistent and |
| // non-persistent (WRT redirects) data. |
| // crbug.com/709564 |
| DataReductionProxyData* data = DataReductionProxyData::GetData(*request); |
| base::Optional<uint64_t> page_id; |
| if (data && data->session_key() == |
| data_reduction_proxy_request_options_->GetSecureSession()) { |
| page_id = data->page_id(); |
| } |
| |
| DataReductionProxyData::ClearData(request); |
| |
| if (page_id) { |
| data = DataReductionProxyData::GetDataAndCreateIfNecessary(request); |
| data->set_page_id(page_id.value()); |
| data->set_session_key( |
| data_reduction_proxy_request_options_->GetSecureSession()); |
| } |
| } |
| |
| void DataReductionProxyNetworkDelegate::OnCompletedInternal( |
| net::URLRequest* request, |
| bool started) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(request); |
| // TODO(maksims): remove this once OnCompletedInternal() has net_error in |
| // arguments. |
| int net_error = request->status().error(); |
| DCHECK_NE(net::ERR_IO_PENDING, net_error); |
| if (data_reduction_proxy_bypass_stats_) |
| data_reduction_proxy_bypass_stats_->OnUrlRequestCompleted(request, started, |
| net_error); |
| |
| net::HttpRequestHeaders request_headers; |
| if (data_reduction_proxy_io_data_ && request->response_headers() && |
| IsEmptyImagePreview(*(request->response_headers()))) { |
| data_reduction_proxy_io_data_->lofi_ui_service()->OnLoFiReponseReceived( |
| *request); |
| } else if (data_reduction_proxy_io_data_ && request->response_headers() && |
| IsLitePagePreview(*(request->response_headers()))) { |
| RecordLitePageTransformationType(LITE_PAGE); |
| } else if (request->GetFullRequestHeaders(&request_headers)) { |
| // TODO(bengr): transform processing logic should happen elsewhere. |
| std::string header_value; |
| request_headers.GetHeader(chrome_proxy_accept_transform_header(), |
| &header_value); |
| if (header_value == lite_page_directive()) |
| RecordLitePageTransformationType(NO_TRANSFORMATION_LITE_PAGE_REQUESTED); |
| } |
| |
| if (!request->response_info().network_accessed || |
| !request->url().SchemeIsHTTPOrHTTPS() || |
| request->GetTotalReceivedBytes() == 0) { |
| return; |
| } |
| |
| DataReductionProxyRequestType request_type = GetDataReductionProxyRequestType( |
| *request, configurator_->GetProxyConfig(), *data_reduction_proxy_config_); |
| |
| // Determine the original content length if present. |
| int64_t original_content_length = |
| request->response_headers() |
| ? request->response_headers()->GetInt64HeaderValue( |
| "x-original-content-length") |
| : -1; |
| |
| CalculateAndRecordDataUsage(*request, request_type); |
| |
| RecordContentLength(*request, request_type, original_content_length); |
| } |
| |
| void DataReductionProxyNetworkDelegate::OnHeadersReceivedInternal( |
| net::URLRequest* request, |
| const net::CompletionCallback& callback, |
| const net::HttpResponseHeaders* original_response_headers, |
| scoped_refptr<net::HttpResponseHeaders>* override_response_headers, |
| GURL* allowed_unsafe_redirect_url) { |
| if (!original_response_headers) |
| return; |
| if (IsEmptyImagePreview(*original_response_headers)) { |
| DataReductionProxyData* data = |
| DataReductionProxyData::GetDataAndCreateIfNecessary(request); |
| data->set_lofi_received(true); |
| } else if (IsLitePagePreview(*original_response_headers)) { |
| DataReductionProxyData* data = |
| DataReductionProxyData::GetDataAndCreateIfNecessary(request); |
| data->set_lite_page_received(true); |
| } |
| } |
| |
| void DataReductionProxyNetworkDelegate::CalculateAndRecordDataUsage( |
| const net::URLRequest& request, |
| DataReductionProxyRequestType request_type) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| int64_t data_used = request.GetTotalReceivedBytes(); |
| |
| // Estimate how many bytes would have been used if the DataReductionProxy was |
| // not used, and record the data usage. |
| int64_t original_size = data_used; |
| |
| if (request_type == VIA_DATA_REDUCTION_PROXY) |
| original_size = EstimateOriginalReceivedBytes(request); |
| |
| std::string mime_type; |
| if (request.response_headers()) |
| request.response_headers()->GetMimeType(&mime_type); |
| |
| scoped_refptr<DataUseGroup> data_use_group = |
| data_use_group_provider_ |
| ? data_use_group_provider_->GetDataUseGroup(&request) |
| : nullptr; |
| AccumulateDataUsage(data_used, original_size, request_type, data_use_group, |
| mime_type); |
| } |
| |
| void DataReductionProxyNetworkDelegate::AccumulateDataUsage( |
| int64_t data_used, |
| int64_t original_size, |
| DataReductionProxyRequestType request_type, |
| const scoped_refptr<DataUseGroup>& data_use_group, |
| const std::string& mime_type) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK_GE(data_used, 0); |
| DCHECK_GE(original_size, 0); |
| if (data_reduction_proxy_io_data_) { |
| data_reduction_proxy_io_data_->UpdateContentLengths( |
| data_used, original_size, data_reduction_proxy_io_data_->IsEnabled(), |
| request_type, data_use_group, mime_type); |
| } |
| } |
| |
| void DataReductionProxyNetworkDelegate::RecordContentLength( |
| const net::URLRequest& request, |
| DataReductionProxyRequestType request_type, |
| int64_t original_content_length) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!request.response_headers() || request.was_cached() || |
| request.received_response_content_length() == 0) { |
| return; |
| } |
| |
| // Record content length histograms for the request. |
| base::TimeDelta freshness_lifetime = |
| request.response_headers() |
| ->GetFreshnessLifetimes(request.response_info().response_time) |
| .freshness; |
| |
| bool is_https = request.url().SchemeIs("https"); |
| bool is_video = false; |
| std::string mime_type; |
| if (request.response_headers()->GetMimeType(&mime_type)) { |
| is_video = net::MatchesMimeType("video/*", mime_type); |
| } |
| |
| RecordContentLengthHistograms( |
| // |data_reduction_proxy_io_data_| can be NULL for Webview. |
| data_reduction_proxy_io_data_ && |
| data_reduction_proxy_io_data_->IsEnabled() && |
| data_reduction_proxy_io_data_->lofi_decider() && |
| data_reduction_proxy_io_data_->lofi_decider()->IsUsingLoFi(request), |
| is_https, is_video, request.received_response_content_length(), |
| original_content_length, freshness_lifetime); |
| |
| if (data_reduction_proxy_io_data_ && data_reduction_proxy_bypass_stats_) { |
| // Record BypassedBytes histograms for the request. |
| data_reduction_proxy_bypass_stats_->RecordBytesHistograms( |
| request, data_reduction_proxy_io_data_->IsEnabled(), |
| configurator_->GetProxyConfig()); |
| } |
| } |
| |
| void DataReductionProxyNetworkDelegate::RecordLitePageTransformationType( |
| LitePageTransformationType type) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.LoFi.TransformationType", type, |
| LITE_PAGE_TRANSFORMATION_TYPES_INDEX_BOUNDARY); |
| } |
| |
| bool DataReductionProxyNetworkDelegate::WasEligibleWithoutHoldback( |
| const net::URLRequest& request, |
| const net::ProxyInfo& proxy_info, |
| const net::ProxyRetryInfoMap& proxy_retry_info) const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(proxy_info.is_empty() || proxy_info.is_direct() || |
| !data_reduction_proxy_config_->IsDataReductionProxy( |
| proxy_info.proxy_server(), nullptr)); |
| if (!util::EligibleForDataReductionProxy(proxy_info, request.url(), |
| request.method())) { |
| return false; |
| } |
| net::ProxyConfig proxy_config = |
| data_reduction_proxy_config_->ProxyConfigIgnoringHoldback(); |
| net::ProxyInfo data_reduction_proxy_info; |
| return util::ApplyProxyConfigToProxyInfo(proxy_config, proxy_retry_info, |
| request.url(), |
| &data_reduction_proxy_info); |
| } |
| |
| void DataReductionProxyNetworkDelegate::SetDataUseGroupProvider( |
| std::unique_ptr<DataUseGroupProvider> data_use_group_provider) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| data_use_group_provider_ = std::move(data_use_group_provider); |
| } |
| |
| void DataReductionProxyNetworkDelegate::MaybeAddBrotliToAcceptEncodingHeader( |
| const net::ProxyInfo& proxy_info, |
| net::HttpRequestHeaders* request_headers, |
| const net::URLRequest& request) const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| // This method should be called only when the resolved proxy was a data |
| // saver proxy. |
| DCHECK(data_reduction_proxy_config_->IsDataReductionProxy( |
| proxy_info.proxy_server(), nullptr)); |
| DCHECK(request.url().is_valid()); |
| DCHECK(!request.url().SchemeIsCryptographic()); |
| DCHECK(request.url().SchemeIsHTTPOrHTTPS()); |
| |
| static const char kBrotli[] = "br"; |
| |
| if (!request.context()->enable_brotli()) { |
| // Verify that Brotli is enabled globally. |
| return; |
| } |
| |
| if (!params::IsBrotliAcceptEncodingEnabled()) { |
| // Verify that Brotli is enabled for data reduction proxy. |
| return; |
| } |
| |
| if (!proxy_info.proxy_server().is_https() && |
| !proxy_info.proxy_server().is_quic()) { |
| // Brotli encoding can be used only when the proxy server is a secure proxy |
| // server. |
| return; |
| } |
| |
| if (!request_headers->HasHeader(net::HttpRequestHeaders::kAcceptEncoding)) |
| return; |
| |
| std::string header_value; |
| request_headers->GetHeader(net::HttpRequestHeaders::kAcceptEncoding, |
| &header_value); |
| |
| // Brotli should not be already present in the header since the URL is non- |
| // cryptographic. This is an approximate check, and would trigger even if the |
| // accept-encoding header contains an encoding that has prefix |kBrotli|. |
| DCHECK_EQ(std::string::npos, header_value.find(kBrotli)); |
| |
| request_headers->RemoveHeader(net::HttpRequestHeaders::kAcceptEncoding); |
| if (!header_value.empty()) |
| header_value += ", "; |
| header_value += kBrotli; |
| request_headers->SetHeader(net::HttpRequestHeaders::kAcceptEncoding, |
| header_value); |
| } |
| |
| void DataReductionProxyNetworkDelegate::MaybeAddChromeProxyECTHeader( |
| net::HttpRequestHeaders* request_headers, |
| const net::URLRequest& request) const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| // This method should be called only when the resolved proxy was a data |
| // saver proxy. |
| DCHECK(request.url().is_valid()); |
| DCHECK(!request.url().SchemeIsCryptographic()); |
| DCHECK(request.url().SchemeIsHTTPOrHTTPS()); |
| |
| if (request_headers->HasHeader(chrome_proxy_ect_header())) |
| request_headers->RemoveHeader(chrome_proxy_ect_header()); |
| |
| if (request.context()->network_quality_estimator()) { |
| net::EffectiveConnectionType type = request.context() |
| ->network_quality_estimator() |
| ->GetEffectiveConnectionType(); |
| if (type > net::EFFECTIVE_CONNECTION_TYPE_OFFLINE) { |
| DCHECK_NE(net::EFFECTIVE_CONNECTION_TYPE_LAST, type); |
| request_headers->SetHeader(chrome_proxy_ect_header(), |
| net::GetNameForEffectiveConnectionType(type)); |
| return; |
| } |
| } |
| request_headers->SetHeader(chrome_proxy_ect_header(), |
| net::GetNameForEffectiveConnectionType( |
| net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN)); |
| |
| static_assert(net::EFFECTIVE_CONNECTION_TYPE_OFFLINE + 1 == |
| net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G, |
| "ECT enum value is not handled."); |
| static_assert(net::EFFECTIVE_CONNECTION_TYPE_4G + 1 == |
| net::EFFECTIVE_CONNECTION_TYPE_LAST, |
| "ECT enum value is not handled."); |
| } |
| |
| void DataReductionProxyNetworkDelegate::RemoveChromeProxyECTHeader( |
| net::HttpRequestHeaders* request_headers) const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| request_headers->RemoveHeader(chrome_proxy_ect_header()); |
| } |
| |
| } // namespace data_reduction_proxy |