1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h"
8 #include "base/bind_helpers.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/time/time.h"
12 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h"
13 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
14 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
15 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_experiments_stats.h"
16 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
17 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
18 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
19 #include "net/base/load_flags.h"
20 #include "net/http/http_response_headers.h"
21 #include "net/proxy/proxy_info.h"
22 #include "net/proxy/proxy_server.h"
23 #include "net/proxy/proxy_service.h"
24 #include "net/url_request/url_request.h"
25 #include "net/url_request/url_request_status.h"
29 void RecordContentLengthHistograms(
30 int64 received_content_length
,
31 int64 original_content_length
,
32 const base::TimeDelta
& freshness_lifetime
) {
33 // Add the current resource to these histograms only when a valid
34 // X-Original-Content-Length header is present.
35 if (original_content_length
>= 0) {
36 UMA_HISTOGRAM_COUNTS("Net.HttpContentLengthWithValidOCL",
37 received_content_length
);
38 UMA_HISTOGRAM_COUNTS("Net.HttpOriginalContentLengthWithValidOCL",
39 original_content_length
);
40 UMA_HISTOGRAM_COUNTS("Net.HttpContentLengthDifferenceWithValidOCL",
41 original_content_length
- received_content_length
);
43 // Presume the original content length is the same as the received content
44 // length if the X-Original-Content-Header is not present.
45 original_content_length
= received_content_length
;
47 UMA_HISTOGRAM_COUNTS("Net.HttpContentLength", received_content_length
);
48 UMA_HISTOGRAM_COUNTS("Net.HttpOriginalContentLength",
49 original_content_length
);
50 UMA_HISTOGRAM_COUNTS("Net.HttpContentLengthDifference",
51 original_content_length
- received_content_length
);
52 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.HttpContentFreshnessLifetime",
53 freshness_lifetime
.InSeconds(),
54 base::TimeDelta::FromHours(1).InSeconds(),
55 base::TimeDelta::FromDays(30).InSeconds(),
57 if (freshness_lifetime
.InSeconds() <= 0)
59 UMA_HISTOGRAM_COUNTS("Net.HttpContentLengthCacheable",
60 received_content_length
);
61 if (freshness_lifetime
.InHours() < 4)
63 UMA_HISTOGRAM_COUNTS("Net.HttpContentLengthCacheable4Hours",
64 received_content_length
);
66 if (freshness_lifetime
.InHours() < 24)
68 UMA_HISTOGRAM_COUNTS("Net.HttpContentLengthCacheable24Hours",
69 received_content_length
);
74 namespace data_reduction_proxy
{
76 DataReductionProxyNetworkDelegate::DataReductionProxyNetworkDelegate(
77 scoped_ptr
<net::NetworkDelegate
> network_delegate
,
78 DataReductionProxyConfig
* config
,
79 DataReductionProxyRequestOptions
* request_options
,
80 const DataReductionProxyConfigurator
* configurator
,
81 DataReductionProxyExperimentsStats
* experiments_stats
)
82 : LayeredNetworkDelegate(network_delegate
.Pass()),
83 received_content_length_(0),
84 original_content_length_(0),
85 data_reduction_proxy_config_(config
),
86 data_reduction_proxy_bypass_stats_(nullptr),
87 data_reduction_proxy_request_options_(request_options
),
88 data_reduction_proxy_io_data_(nullptr),
89 configurator_(configurator
),
90 experiments_stats_(experiments_stats
) {
91 DCHECK(data_reduction_proxy_config_
);
92 DCHECK(data_reduction_proxy_request_options_
);
93 DCHECK(experiments_stats_
);
96 DataReductionProxyNetworkDelegate::~DataReductionProxyNetworkDelegate() {
99 void DataReductionProxyNetworkDelegate::InitIODataAndUMA(
100 DataReductionProxyIOData
* io_data
,
101 DataReductionProxyBypassStats
* bypass_stats
) {
102 DCHECK(bypass_stats
);
103 data_reduction_proxy_io_data_
= io_data
;
104 data_reduction_proxy_bypass_stats_
= bypass_stats
;
108 DataReductionProxyNetworkDelegate::SessionNetworkStatsInfoToValue() const {
109 base::DictionaryValue
* dict
= new base::DictionaryValue();
110 // Use strings to avoid overflow. base::Value only supports 32-bit integers.
111 dict
->SetString("session_received_content_length",
112 base::Int64ToString(received_content_length_
));
113 dict
->SetString("session_original_content_length",
114 base::Int64ToString(original_content_length_
));
118 void DataReductionProxyNetworkDelegate::OnResolveProxyInternal(
121 const net::ProxyService
& proxy_service
,
122 net::ProxyInfo
* result
) {
124 OnResolveProxyHandler(url
, load_flags
, configurator_
->GetProxyConfig(),
125 proxy_service
.proxy_retry_info(),
126 data_reduction_proxy_config_
, result
);
130 void DataReductionProxyNetworkDelegate::OnProxyFallbackInternal(
131 const net::ProxyServer
& bad_proxy
,
133 if (data_reduction_proxy_bypass_stats_
) {
134 data_reduction_proxy_bypass_stats_
->OnProxyFallback(
135 bad_proxy
, net_error
);
139 void DataReductionProxyNetworkDelegate::OnBeforeSendProxyHeadersInternal(
140 net::URLRequest
* request
,
141 const net::ProxyInfo
& proxy_info
,
142 net::HttpRequestHeaders
* headers
) {
143 if (data_reduction_proxy_request_options_
) {
144 data_reduction_proxy_request_options_
->MaybeAddRequestHeader(
145 request
, proxy_info
.proxy_server(), headers
);
149 void DataReductionProxyNetworkDelegate::OnCompletedInternal(
150 net::URLRequest
* request
,
153 if (data_reduction_proxy_bypass_stats_
)
154 data_reduction_proxy_bypass_stats_
->OnUrlRequestCompleted(request
, started
);
156 // Only record for http or https urls.
157 bool is_http
= request
->url().SchemeIs("http");
158 bool is_https
= request
->url().SchemeIs("https");
160 if (request
->status().status() != net::URLRequestStatus::SUCCESS
)
163 // For better accuracy, we use the actual bytes read instead of the length
164 // specified with the Content-Length header, which may be inaccurate,
165 // or missing, as is the case with chunked encoding.
166 int64 received_content_length
= request
->received_response_content_length();
167 if (!request
->was_cached() && // Don't record cached content
168 received_content_length
&& // Zero-byte responses aren't useful.
169 (is_http
|| is_https
) && // Only record for HTTP or HTTPS urls.
170 configurator_
) { // Used by request type and histograms.
171 int64 original_content_length
=
172 request
->response_info().headers
->GetInt64HeaderValue(
173 "x-original-content-length");
174 base::TimeDelta freshness_lifetime
=
175 request
->response_info().headers
->GetFreshnessLifetimes(
176 request
->response_info().response_time
).freshness
;
177 DataReductionProxyRequestType request_type
=
178 GetDataReductionProxyRequestType(*request
,
179 configurator_
->GetProxyConfig(),
180 *data_reduction_proxy_config_
);
182 int64 adjusted_original_content_length
=
183 GetAdjustedOriginalContentLength(request_type
,
184 original_content_length
,
185 received_content_length
);
186 AccumulateContentLength(received_content_length
,
187 adjusted_original_content_length
,
189 RecordContentLengthHistograms(received_content_length
,
190 original_content_length
,
192 experiments_stats_
->RecordBytes(request
->request_time(), request_type
,
193 received_content_length
,
194 original_content_length
);
196 if (data_reduction_proxy_io_data_
&& data_reduction_proxy_bypass_stats_
) {
197 data_reduction_proxy_bypass_stats_
->RecordBytesHistograms(
198 *request
, data_reduction_proxy_io_data_
->IsEnabled(),
199 configurator_
->GetProxyConfig());
201 DVLOG(2) << __FUNCTION__
202 << " received content length: " << received_content_length
203 << " original content length: " << original_content_length
204 << " url: " << request
->url();
208 void DataReductionProxyNetworkDelegate::AccumulateContentLength(
209 int64 received_content_length
,
210 int64 original_content_length
,
211 DataReductionProxyRequestType request_type
) {
212 DCHECK_GE(received_content_length
, 0);
213 DCHECK_GE(original_content_length
, 0);
214 if (data_reduction_proxy_io_data_
) {
215 data_reduction_proxy_io_data_
->UpdateContentLengths(
216 received_content_length
, original_content_length
,
217 data_reduction_proxy_io_data_
->IsEnabled(), request_type
);
219 received_content_length_
+= received_content_length
;
220 original_content_length_
+= original_content_length
;
223 void OnResolveProxyHandler(const GURL
& url
,
225 const net::ProxyConfig
& data_reduction_proxy_config
,
226 const net::ProxyRetryInfoMap
& proxy_retry_info
,
227 const DataReductionProxyConfig
* config
,
228 net::ProxyInfo
* result
) {
230 DCHECK(result
->is_empty() || result
->is_direct() ||
231 !config
->IsDataReductionProxy(result
->proxy_server().host_port_pair(),
233 if (data_reduction_proxy_config
.is_valid() &&
234 result
->proxy_server().is_direct() &&
235 result
->proxy_list().size() == 1 &&
236 !url
.SchemeIsWSOrWSS()) {
237 net::ProxyInfo data_reduction_proxy_info
;
238 data_reduction_proxy_config
.proxy_rules().Apply(
239 url
, &data_reduction_proxy_info
);
240 data_reduction_proxy_info
.DeprioritizeBadProxies(proxy_retry_info
);
241 if (!data_reduction_proxy_info
.proxy_server().is_direct())
242 result
->OverrideProxyList(data_reduction_proxy_info
.proxy_list());
245 if ((load_flags
& net::LOAD_BYPASS_DATA_REDUCTION_PROXY
) &&
246 DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial()) {
247 if (!result
->is_empty() && !result
->is_direct() &&
248 config
->IsDataReductionProxy(result
->proxy_server().host_port_pair(),
250 result
->RemoveProxiesWithoutScheme(net::ProxyServer::SCHEME_DIRECT
);
255 } // namespace data_reduction_proxy