ProjectingObserverChromeos: Drop DBusThreadManager dependency for better testing.
[chromium-blink-merge.git] / components / data_reduction_proxy / browser / data_reduction_proxy_usage_stats.cc
bloba7426f564aa2f52108810a20e8f119c043b35cf3
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/browser/data_reduction_proxy_usage_stats.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/metrics/histogram.h"
11 #include "base/metrics/sparse_histogram.h"
12 #include "base/prefs/pref_member.h"
13 #include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h"
14 #include "net/base/net_errors.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/http/http_status_code.h"
17 #include "net/proxy/proxy_retry_info.h"
18 #include "net/proxy/proxy_server.h"
19 #include "net/proxy/proxy_service.h"
20 #include "net/url_request/url_request.h"
21 #include "net/url_request/url_request_context.h"
23 using base::MessageLoopProxy;
24 using net::HostPortPair;
25 using net::ProxyServer;
26 using net::ProxyService;
27 using net::NetworkChangeNotifier;
28 using net::URLRequest;
30 namespace data_reduction_proxy {
32 namespace {
34 const int kMinFailedRequestsWhenUnavailable = 1;
35 const int kMaxSuccessfulRequestsWhenUnavailable = 0;
36 const int kMaxFailedRequestsBeforeReset = 3;
38 // Records a net error code that resulted in bypassing the data reduction
39 // proxy (|is_primary| is true) or the data reduction proxy fallback.
40 void RecordDataReductionProxyBypassOnNetworkError(
41 bool is_primary,
42 const ProxyServer& proxy_server,
43 int net_error) {
44 if (is_primary) {
45 UMA_HISTOGRAM_SPARSE_SLOWLY(
46 "DataReductionProxy.BypassOnNetworkErrorPrimary",
47 std::abs(net_error));
48 return;
50 UMA_HISTOGRAM_SPARSE_SLOWLY(
51 "DataReductionProxy.BypassOnNetworkErrorFallback",
52 std::abs(net_error));
55 } // namespace
57 // static
58 void DataReductionProxyUsageStats::RecordDataReductionProxyBypassInfo(
59 bool is_primary,
60 bool bypass_all,
61 const net::ProxyServer& proxy_server,
62 DataReductionProxyBypassType bypass_type) {
63 if (bypass_all) {
64 if (is_primary) {
65 UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.BlockTypePrimary",
66 bypass_type, BYPASS_EVENT_TYPE_MAX);
67 } else {
68 UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.BlockTypeFallback",
69 bypass_type, BYPASS_EVENT_TYPE_MAX);
71 } else {
72 if (is_primary) {
73 UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.BypassTypePrimary",
74 bypass_type, BYPASS_EVENT_TYPE_MAX);
75 } else {
76 UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.BypassTypeFallback",
77 bypass_type, BYPASS_EVENT_TYPE_MAX);
82 // static
83 void DataReductionProxyUsageStats::DetectAndRecordMissingViaHeaderResponseCode(
84 bool is_primary,
85 const net::HttpResponseHeaders* headers) {
86 if (HasDataReductionProxyViaHeader(headers, NULL)) {
87 // The data reduction proxy via header is present, so don't record anything.
88 return;
91 if (is_primary) {
92 UMA_HISTOGRAM_SPARSE_SLOWLY(
93 "DataReductionProxy.MissingViaHeader.ResponseCode.Primary",
94 headers->response_code());
95 } else {
96 UMA_HISTOGRAM_SPARSE_SLOWLY(
97 "DataReductionProxy.MissingViaHeader.ResponseCode.Fallback",
98 headers->response_code());
102 DataReductionProxyUsageStats::DataReductionProxyUsageStats(
103 DataReductionProxyParams* params,
104 const scoped_refptr<MessageLoopProxy>& ui_thread_proxy)
105 : data_reduction_proxy_params_(params),
106 last_bypass_type_(BYPASS_EVENT_TYPE_MAX),
107 triggering_request_(true),
108 ui_thread_proxy_(ui_thread_proxy),
109 successful_requests_through_proxy_count_(0),
110 proxy_net_errors_count_(0),
111 unavailable_(false) {
112 DCHECK(params);
114 NetworkChangeNotifier::AddNetworkChangeObserver(this);
117 DataReductionProxyUsageStats::~DataReductionProxyUsageStats() {
118 NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
121 void DataReductionProxyUsageStats::OnUrlRequestCompleted(
122 const net::URLRequest* request, bool started) {
123 DCHECK(thread_checker_.CalledOnValidThread());
125 if (request->status().status() == net::URLRequestStatus::SUCCESS &&
126 data_reduction_proxy_params_->WasDataReductionProxyUsed(request, NULL)) {
127 successful_requests_through_proxy_count_++;
128 NotifyUnavailabilityIfChanged();
132 void DataReductionProxyUsageStats::OnNetworkChanged(
133 NetworkChangeNotifier::ConnectionType type) {
134 DCHECK(thread_checker_.CalledOnValidThread());
135 ClearRequestCounts();
138 void DataReductionProxyUsageStats::ClearRequestCounts() {
139 DCHECK(thread_checker_.CalledOnValidThread());
140 successful_requests_through_proxy_count_ = 0;
141 proxy_net_errors_count_ = 0;
144 void DataReductionProxyUsageStats::NotifyUnavailabilityIfChanged() {
145 bool prev_unavailable = unavailable_;
146 unavailable_ =
147 (proxy_net_errors_count_ >= kMinFailedRequestsWhenUnavailable &&
148 successful_requests_through_proxy_count_ <=
149 kMaxSuccessfulRequestsWhenUnavailable);
150 if (prev_unavailable != unavailable_) {
151 ui_thread_proxy_->PostTask(FROM_HERE, base::Bind(
152 &DataReductionProxyUsageStats::NotifyUnavailabilityOnUIThread,
153 base::Unretained(this),
154 unavailable_));
158 void DataReductionProxyUsageStats::NotifyUnavailabilityOnUIThread(
159 bool unavailable) {
160 DCHECK(ui_thread_proxy_->BelongsToCurrentThread());
161 if (!unavailable_callback_.is_null())
162 unavailable_callback_.Run(unavailable);
165 void DataReductionProxyUsageStats::SetBypassType(
166 DataReductionProxyBypassType type) {
167 last_bypass_type_ = type;
168 triggering_request_ = true;
171 void DataReductionProxyUsageStats::RecordBytesHistograms(
172 net::URLRequest* request,
173 const BooleanPrefMember& data_reduction_proxy_enabled,
174 const net::ProxyConfig& data_reduction_proxy_config) {
175 RecordBypassedBytesHistograms(request, data_reduction_proxy_enabled,
176 data_reduction_proxy_config);
177 RecordMissingViaHeaderBytes(request);
180 void DataReductionProxyUsageStats::RecordBypassedBytesHistograms(
181 net::URLRequest* request,
182 const BooleanPrefMember& data_reduction_proxy_enabled,
183 const net::ProxyConfig& data_reduction_proxy_config) {
184 int64 content_length = request->received_response_content_length();
186 if (data_reduction_proxy_enabled.GetValue() &&
187 !data_reduction_proxy_config.Equals(
188 request->context()->proxy_service()->config())) {
189 RecordBypassedBytes(last_bypass_type_,
190 DataReductionProxyUsageStats::MANAGED_PROXY_CONFIG,
191 content_length);
192 return;
195 if (data_reduction_proxy_params_->WasDataReductionProxyUsed(request, NULL)) {
196 RecordBypassedBytes(last_bypass_type_,
197 DataReductionProxyUsageStats::NOT_BYPASSED,
198 content_length);
199 return;
202 if (data_reduction_proxy_enabled.GetValue() &&
203 request->url().SchemeIs(url::kHttpsScheme)) {
204 RecordBypassedBytes(last_bypass_type_,
205 DataReductionProxyUsageStats::SSL,
206 content_length);
207 return;
210 if (data_reduction_proxy_enabled.GetValue() &&
211 data_reduction_proxy_params_->IsBypassedByDataReductionProxyLocalRules(
212 *request, data_reduction_proxy_config)) {
213 RecordBypassedBytes(last_bypass_type_,
214 DataReductionProxyUsageStats::LOCAL_BYPASS_RULES,
215 content_length);
216 return;
219 // Only record separate triggering request UMA for short, medium, and long
220 // bypass events.
221 if (triggering_request_ &&
222 (last_bypass_type_ == BYPASS_EVENT_TYPE_SHORT ||
223 last_bypass_type_ == BYPASS_EVENT_TYPE_MEDIUM ||
224 last_bypass_type_ == BYPASS_EVENT_TYPE_LONG)) {
225 std::string mime_type;
226 request->GetMimeType(&mime_type);
227 // MIME types are named by <media-type>/<subtype>. Check to see if the
228 // media type is audio or video. Only record when triggered by short bypass,
229 // there isn't an audio or video bucket for medium or long bypasses.
230 if (last_bypass_type_ == BYPASS_EVENT_TYPE_SHORT &&
231 (mime_type.compare(0, 6, "audio/") == 0 ||
232 mime_type.compare(0, 6, "video/") == 0)) {
233 RecordBypassedBytes(last_bypass_type_,
234 DataReductionProxyUsageStats::AUDIO_VIDEO,
235 content_length);
236 return;
239 RecordBypassedBytes(last_bypass_type_,
240 DataReductionProxyUsageStats::TRIGGERING_REQUEST,
241 content_length);
242 triggering_request_ = false;
243 return;
246 if (last_bypass_type_ != BYPASS_EVENT_TYPE_MAX) {
247 RecordBypassedBytes(last_bypass_type_,
248 DataReductionProxyUsageStats::BYPASSED_BYTES_TYPE_MAX,
249 content_length);
250 return;
253 if (data_reduction_proxy_params_->AreDataReductionProxiesBypassed(*request,
254 NULL)) {
255 RecordBypassedBytes(last_bypass_type_,
256 DataReductionProxyUsageStats::NETWORK_ERROR,
257 content_length);
261 void DataReductionProxyUsageStats::OnProxyFallback(
262 const net::ProxyServer& bypassed_proxy,
263 int net_error) {
264 DataReductionProxyTypeInfo data_reduction_proxy_info;
265 if (bypassed_proxy.is_valid() && !bypassed_proxy.is_direct() &&
266 data_reduction_proxy_params_->IsDataReductionProxy(
267 bypassed_proxy.host_port_pair(), &data_reduction_proxy_info)) {
268 if (data_reduction_proxy_info.is_ssl)
269 return;
271 proxy_net_errors_count_++;
273 // To account for the case when the proxy is reachable for sometime, and
274 // then gets blocked, we reset counts when number of errors exceed
275 // the threshold.
276 if (proxy_net_errors_count_ >= kMaxFailedRequestsBeforeReset &&
277 successful_requests_through_proxy_count_ >
278 kMaxSuccessfulRequestsWhenUnavailable) {
279 ClearRequestCounts();
280 } else {
281 NotifyUnavailabilityIfChanged();
284 if (!data_reduction_proxy_info.is_fallback) {
285 RecordDataReductionProxyBypassInfo(
286 true, false, bypassed_proxy, BYPASS_EVENT_TYPE_NETWORK_ERROR);
287 RecordDataReductionProxyBypassOnNetworkError(
288 true, bypassed_proxy, net_error);
289 } else {
290 RecordDataReductionProxyBypassInfo(
291 false, false, bypassed_proxy, BYPASS_EVENT_TYPE_NETWORK_ERROR);
292 RecordDataReductionProxyBypassOnNetworkError(
293 false, bypassed_proxy, net_error);
298 void DataReductionProxyUsageStats::RecordBypassedBytes(
299 DataReductionProxyBypassType bypass_type,
300 DataReductionProxyUsageStats::BypassedBytesType bypassed_bytes_type,
301 int64 content_length) {
302 // Individual histograms are needed to count the bypassed bytes for each
303 // bypass type so that we can see the size of requests. This helps us
304 // remove outliers that would skew the sum of bypassed bytes for each type.
305 switch (bypassed_bytes_type) {
306 case DataReductionProxyUsageStats::NOT_BYPASSED:
307 UMA_HISTOGRAM_COUNTS(
308 "DataReductionProxy.BypassedBytes.NotBypassed", content_length);
309 break;
310 case DataReductionProxyUsageStats::SSL:
311 UMA_HISTOGRAM_COUNTS(
312 "DataReductionProxy.BypassedBytes.SSL", content_length);
313 break;
314 case DataReductionProxyUsageStats::LOCAL_BYPASS_RULES:
315 UMA_HISTOGRAM_COUNTS(
316 "DataReductionProxy.BypassedBytes.LocalBypassRules",
317 content_length);
318 break;
319 case DataReductionProxyUsageStats::MANAGED_PROXY_CONFIG:
320 UMA_HISTOGRAM_COUNTS(
321 "DataReductionProxy.BypassedBytes.ManagedProxyConfig",
322 content_length);
323 break;
324 case DataReductionProxyUsageStats::AUDIO_VIDEO:
325 if (last_bypass_type_ == BYPASS_EVENT_TYPE_SHORT) {
326 UMA_HISTOGRAM_COUNTS(
327 "DataReductionProxy.BypassedBytes.ShortAudioVideo",
328 content_length);
330 break;
331 case DataReductionProxyUsageStats::TRIGGERING_REQUEST:
332 switch (bypass_type) {
333 case BYPASS_EVENT_TYPE_SHORT:
334 UMA_HISTOGRAM_COUNTS(
335 "DataReductionProxy.BypassedBytes.ShortTriggeringRequest",
336 content_length);
337 break;
338 case BYPASS_EVENT_TYPE_MEDIUM:
339 UMA_HISTOGRAM_COUNTS(
340 "DataReductionProxy.BypassedBytes.MediumTriggeringRequest",
341 content_length);
342 break;
343 case BYPASS_EVENT_TYPE_LONG:
344 UMA_HISTOGRAM_COUNTS(
345 "DataReductionProxy.BypassedBytes.LongTriggeringRequest",
346 content_length);
347 break;
348 default:
349 break;
351 break;
352 case DataReductionProxyUsageStats::NETWORK_ERROR:
353 UMA_HISTOGRAM_COUNTS(
354 "DataReductionProxy.BypassedBytes.NetworkErrorOther",
355 content_length);
356 break;
357 case DataReductionProxyUsageStats::BYPASSED_BYTES_TYPE_MAX:
358 switch (bypass_type) {
359 case BYPASS_EVENT_TYPE_CURRENT:
360 UMA_HISTOGRAM_COUNTS("DataReductionProxy.BypassedBytes.Current",
361 content_length);
362 break;
363 case BYPASS_EVENT_TYPE_SHORT:
364 UMA_HISTOGRAM_COUNTS("DataReductionProxy.BypassedBytes.ShortAll",
365 content_length);
366 break;
367 case BYPASS_EVENT_TYPE_MEDIUM:
368 UMA_HISTOGRAM_COUNTS("DataReductionProxy.BypassedBytes.MediumAll",
369 content_length);
370 break;
371 case BYPASS_EVENT_TYPE_LONG:
372 UMA_HISTOGRAM_COUNTS("DataReductionProxy.BypassedBytes.LongAll",
373 content_length);
374 break;
375 case BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX:
376 UMA_HISTOGRAM_COUNTS(
377 "DataReductionProxy.BypassedBytes.MissingViaHeader4xx",
378 content_length);
379 break;
380 case BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER:
381 UMA_HISTOGRAM_COUNTS(
382 "DataReductionProxy.BypassedBytes.MissingViaHeaderOther",
383 content_length);
384 break;
385 case BYPASS_EVENT_TYPE_MALFORMED_407:
386 UMA_HISTOGRAM_COUNTS("DataReductionProxy.BypassedBytes.Malformed407",
387 content_length);
388 break;
389 case BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR:
390 UMA_HISTOGRAM_COUNTS(
391 "DataReductionProxy.BypassedBytes."
392 "Status500HttpInternalServerError",
393 content_length);
394 break;
395 case BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY:
396 UMA_HISTOGRAM_COUNTS(
397 "DataReductionProxy.BypassedBytes.Status502HttpBadGateway",
398 content_length);
399 break;
400 case BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE:
401 UMA_HISTOGRAM_COUNTS(
402 "DataReductionProxy.BypassedBytes."
403 "Status503HttpServiceUnavailable",
404 content_length);
405 break;
406 default:
407 break;
409 break;
413 void DataReductionProxyUsageStats::RecordMissingViaHeaderBytes(
414 URLRequest* request) {
415 // Responses that were served from cache should have been filtered out
416 // already.
417 DCHECK(!request->was_cached());
419 if (!data_reduction_proxy_params_->WasDataReductionProxyUsed(request, NULL) ||
420 HasDataReductionProxyViaHeader(request->response_headers(), NULL)) {
421 // Only track requests that used the data reduction proxy and had responses
422 // that were missing the data reduction proxy via header.
423 return;
426 if (request->GetResponseCode() >= net::HTTP_BAD_REQUEST &&
427 request->GetResponseCode() < net::HTTP_INTERNAL_SERVER_ERROR) {
428 // Track 4xx responses that are missing via headers separately.
429 UMA_HISTOGRAM_COUNTS("DataReductionProxy.MissingViaHeader.Bytes.4xx",
430 request->received_response_content_length());
431 } else {
432 UMA_HISTOGRAM_COUNTS("DataReductionProxy.MissingViaHeader.Bytes.Other",
433 request->received_response_content_length());
437 } // namespace data_reduction_proxy