| // 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 "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/metrics/histogram.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/time/time.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_auth_request_handler.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.h" |
| #include "net/base/load_flags.h" |
| #include "net/http/http_response_headers.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_status.h" |
| |
| namespace { |
| |
| void RecordContentLengthHistograms( |
| int64 received_content_length, |
| int64 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("Net.HttpContentLengthWithValidOCL", |
| received_content_length); |
| UMA_HISTOGRAM_COUNTS("Net.HttpOriginalContentLengthWithValidOCL", |
| original_content_length); |
| UMA_HISTOGRAM_COUNTS("Net.HttpContentLengthDifferenceWithValidOCL", |
| 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("Net.HttpContentLength", received_content_length); |
| UMA_HISTOGRAM_COUNTS("Net.HttpOriginalContentLength", |
| original_content_length); |
| UMA_HISTOGRAM_COUNTS("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("Net.HttpContentLengthCacheable", |
| received_content_length); |
| if (freshness_lifetime.InHours() < 4) |
| return; |
| UMA_HISTOGRAM_COUNTS("Net.HttpContentLengthCacheable4Hours", |
| received_content_length); |
| |
| if (freshness_lifetime.InHours() < 24) |
| return; |
| UMA_HISTOGRAM_COUNTS("Net.HttpContentLengthCacheable24Hours", |
| received_content_length); |
| } |
| |
| } // namespace |
| |
| namespace data_reduction_proxy { |
| |
| DataReductionProxyNetworkDelegate::DataReductionProxyNetworkDelegate( |
| scoped_ptr<net::NetworkDelegate> network_delegate, |
| DataReductionProxyParams* params, |
| DataReductionProxyAuthRequestHandler* handler, |
| const ProxyConfigGetter& getter) |
| : LayeredNetworkDelegate(network_delegate.Pass()), |
| ui_task_runner_(NULL), |
| received_content_length_(0), |
| original_content_length_(0), |
| data_reduction_proxy_enabled_(NULL), |
| data_reduction_proxy_params_(params), |
| data_reduction_proxy_usage_stats_(NULL), |
| data_reduction_proxy_auth_request_handler_(handler), |
| proxy_config_getter_(getter) { |
| DCHECK(data_reduction_proxy_params_); |
| DCHECK(data_reduction_proxy_auth_request_handler_); |
| } |
| |
| DataReductionProxyNetworkDelegate::~DataReductionProxyNetworkDelegate() { |
| } |
| |
| void DataReductionProxyNetworkDelegate::InitProxyConfigOverrider( |
| const OnResolveProxyHandler& proxy_handler) { |
| DCHECK(!proxy_config_getter_.is_null()); |
| on_resolve_proxy_handler_ = proxy_handler; |
| } |
| |
| void DataReductionProxyNetworkDelegate::InitStatisticsPrefsAndUMA( |
| scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, |
| const base::WeakPtr<DataReductionProxyStatisticsPrefs>& statistics_prefs, |
| BooleanPrefMember* data_reduction_proxy_enabled, |
| DataReductionProxyUsageStats* usage_stats) { |
| DCHECK(data_reduction_proxy_enabled); |
| DCHECK(usage_stats); |
| ui_task_runner_ = ui_task_runner; |
| data_reduction_proxy_statistics_prefs_ = statistics_prefs; |
| data_reduction_proxy_enabled_ = data_reduction_proxy_enabled; |
| data_reduction_proxy_usage_stats_ = usage_stats; |
| } |
| |
| // static |
| // TODO(megjablon): Use data_reduction_proxy_delayed_pref_service to read prefs. |
| // Until updated the pref values may be up to an hour behind on desktop. |
| base::Value* DataReductionProxyNetworkDelegate::HistoricNetworkStatsInfoToValue( |
| PrefService* profile_prefs) { |
| int64 total_received = profile_prefs->GetInt64( |
| data_reduction_proxy::prefs::kHttpReceivedContentLength); |
| int64 total_original = profile_prefs->GetInt64( |
| data_reduction_proxy::prefs::kHttpOriginalContentLength); |
| |
| base::DictionaryValue* dict = new base::DictionaryValue(); |
| // Use strings to avoid overflow. base::Value only supports 32-bit integers. |
| dict->SetString("historic_received_content_length", |
| base::Int64ToString(total_received)); |
| dict->SetString("historic_original_content_length", |
| base::Int64ToString(total_original)); |
| return dict; |
| } |
| |
| base::Value* |
| DataReductionProxyNetworkDelegate::SessionNetworkStatsInfoToValue() const { |
| base::DictionaryValue* dict = new base::DictionaryValue(); |
| // Use strings to avoid overflow. base::Value only supports 32-bit integers. |
| dict->SetString("session_received_content_length", |
| base::Int64ToString(received_content_length_)); |
| dict->SetString("session_original_content_length", |
| base::Int64ToString(original_content_length_)); |
| return dict; |
| } |
| |
| void DataReductionProxyNetworkDelegate::OnResolveProxyInternal( |
| const GURL& url, |
| int load_flags, |
| const net::ProxyService& proxy_service, |
| net::ProxyInfo* result) { |
| // TODO(sclittle): Call OnResolveProxyHandler directly, see |
| // http://crbug.com/447346. |
| if (!on_resolve_proxy_handler_.is_null() && |
| !proxy_config_getter_.is_null()) { |
| on_resolve_proxy_handler_.Run( |
| url, load_flags, proxy_config_getter_.Run(), |
| proxy_service.proxy_retry_info(), data_reduction_proxy_params_, result); |
| } |
| } |
| |
| void DataReductionProxyNetworkDelegate::OnProxyFallbackInternal( |
| const net::ProxyServer& bad_proxy, |
| int net_error) { |
| if (data_reduction_proxy_usage_stats_) { |
| data_reduction_proxy_usage_stats_->OnProxyFallback( |
| bad_proxy, net_error); |
| } |
| } |
| |
| void DataReductionProxyNetworkDelegate::OnBeforeSendProxyHeadersInternal( |
| net::URLRequest* request, |
| const net::ProxyInfo& proxy_info, |
| net::HttpRequestHeaders* headers) { |
| if (data_reduction_proxy_auth_request_handler_) { |
| data_reduction_proxy_auth_request_handler_->MaybeAddRequestHeader( |
| request, proxy_info.proxy_server(), headers); |
| } |
| } |
| |
| void DataReductionProxyNetworkDelegate::OnCompletedInternal( |
| net::URLRequest* request, |
| bool started) { |
| DCHECK(request); |
| if (data_reduction_proxy_usage_stats_) |
| data_reduction_proxy_usage_stats_->OnUrlRequestCompleted(request, started); |
| |
| // Only record for http or https urls. |
| bool is_http = request->url().SchemeIs("http"); |
| bool is_https = request->url().SchemeIs("https"); |
| |
| if (request->status().status() != net::URLRequestStatus::SUCCESS) |
| return; |
| |
| // For better accuracy, we use the actual bytes read instead of the length |
| // specified with the Content-Length header, which may be inaccurate, |
| // or missing, as is the case with chunked encoding. |
| int64 received_content_length = request->received_response_content_length(); |
| |
| if (!request->was_cached() && // Don't record cached content |
| received_content_length && // Zero-byte responses aren't useful. |
| (is_http || is_https) && // Only record for HTTP or HTTPS urls. |
| !proxy_config_getter_.is_null()) { // Used by request type and histograms. |
| int64 original_content_length = |
| request->response_info().headers->GetInt64HeaderValue( |
| "x-original-content-length"); |
| base::TimeDelta freshness_lifetime = |
| request->response_info().headers->GetFreshnessLifetimes( |
| request->response_info().response_time).freshness; |
| DataReductionProxyRequestType request_type = |
| GetDataReductionProxyRequestType(*request, |
| proxy_config_getter_.Run(), |
| *data_reduction_proxy_params_); |
| |
| int64 adjusted_original_content_length = |
| GetAdjustedOriginalContentLength(request_type, |
| original_content_length, |
| received_content_length); |
| AccumulateContentLength(received_content_length, |
| adjusted_original_content_length, |
| request_type); |
| RecordContentLengthHistograms(received_content_length, |
| original_content_length, |
| freshness_lifetime); |
| |
| if (data_reduction_proxy_enabled_ && |
| data_reduction_proxy_usage_stats_) { |
| data_reduction_proxy_usage_stats_->RecordBytesHistograms( |
| *request, |
| *data_reduction_proxy_enabled_, |
| proxy_config_getter_.Run()); |
| } |
| DVLOG(2) << __FUNCTION__ |
| << " received content length: " << received_content_length |
| << " original content length: " << original_content_length |
| << " url: " << request->url(); |
| } |
| } |
| |
| void DataReductionProxyNetworkDelegate::AccumulateContentLength( |
| int64 received_content_length, |
| int64 original_content_length, |
| DataReductionProxyRequestType request_type) { |
| DCHECK_GE(received_content_length, 0); |
| DCHECK_GE(original_content_length, 0); |
| if (data_reduction_proxy_enabled_) { |
| ui_task_runner_->PostTask( |
| FROM_HERE, |
| base::Bind(&DataReductionProxyNetworkDelegate::UpdateContentLengthPrefs, |
| base::Unretained(this), |
| received_content_length, |
| original_content_length, |
| data_reduction_proxy_enabled_->GetValue(), |
| request_type)); |
| } |
| received_content_length_ += received_content_length; |
| original_content_length_ += original_content_length; |
| } |
| |
| void DataReductionProxyNetworkDelegate::UpdateContentLengthPrefs( |
| int received_content_length, |
| int original_content_length, |
| bool data_reduction_proxy_enabled, |
| DataReductionProxyRequestType request_type) { |
| if (data_reduction_proxy_statistics_prefs_) { |
| int64 total_received = data_reduction_proxy_statistics_prefs_->GetInt64( |
| data_reduction_proxy::prefs::kHttpReceivedContentLength); |
| int64 total_original = data_reduction_proxy_statistics_prefs_->GetInt64( |
| data_reduction_proxy::prefs::kHttpOriginalContentLength); |
| total_received += received_content_length; |
| total_original += original_content_length; |
| data_reduction_proxy_statistics_prefs_->SetInt64( |
| data_reduction_proxy::prefs::kHttpReceivedContentLength, |
| total_received); |
| data_reduction_proxy_statistics_prefs_->SetInt64( |
| data_reduction_proxy::prefs::kHttpOriginalContentLength, |
| total_original); |
| |
| UpdateContentLengthPrefsForDataReductionProxy( |
| received_content_length, |
| original_content_length, |
| data_reduction_proxy_enabled, |
| request_type, |
| base::Time::Now(), |
| data_reduction_proxy_statistics_prefs_.get()); |
| } |
| } |
| |
| void OnResolveProxyHandler(const GURL& url, |
| int load_flags, |
| const net::ProxyConfig& data_reduction_proxy_config, |
| const net::ProxyRetryInfoMap& proxy_retry_info, |
| const DataReductionProxyParams* params, |
| net::ProxyInfo* result) { |
| DCHECK(params); |
| DCHECK(result->is_empty() || result->is_direct() || |
| !params->IsDataReductionProxy(result->proxy_server().host_port_pair(), |
| NULL)); |
| if (data_reduction_proxy_config.is_valid() && |
| result->proxy_server().is_direct() && |
| result->proxy_list().size() == 1 && |
| !url.SchemeIsWSOrWSS()) { |
| net::ProxyInfo data_reduction_proxy_info; |
| data_reduction_proxy_config.proxy_rules().Apply( |
| url, &data_reduction_proxy_info); |
| data_reduction_proxy_info.DeprioritizeBadProxies(proxy_retry_info); |
| if (!data_reduction_proxy_info.proxy_server().is_direct()) |
| result->OverrideProxyList(data_reduction_proxy_info.proxy_list()); |
| } |
| |
| if ((load_flags & net::LOAD_BYPASS_DATA_REDUCTION_PROXY) && |
| DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial()) { |
| if (!result->is_empty() && |
| !result->is_direct() && |
| params->IsDataReductionProxy(result->proxy_server().host_port_pair(), |
| NULL)) { |
| result->RemoveProxiesWithoutScheme(net::ProxyServer::SCHEME_DIRECT); |
| } |
| } |
| } |
| |
| } // namespace data_reduction_proxy |