Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / components / data_reduction_proxy / browser / data_reduction_proxy_usage_stats.cc
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.
4
5 #include "components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h"
6
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"
22
23 using base::MessageLoopProxy;
24 using net::HostPortPair;
25 using net::ProxyServer;
26 using net::ProxyService;
27 using net::NetworkChangeNotifier;
28 using net::URLRequest;
29
30 namespace data_reduction_proxy {
31
32 namespace {
33
34 const int kMinFailedRequestsWhenUnavailable = 1;
35 const int kMaxSuccessfulRequestsWhenUnavailable = 0;
36 const int kMaxFailedRequestsBeforeReset = 3;
37
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;
49   }
50   UMA_HISTOGRAM_SPARSE_SLOWLY(
51       "DataReductionProxy.BypassOnNetworkErrorFallback",
52       std::abs(net_error));
53 }
54
55 }  // namespace
56
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);
70     }
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);
78     }
79   }
80 }
81
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;
89   }
90
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());
99   }
100 }
101
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);
113
114   NetworkChangeNotifier::AddNetworkChangeObserver(this);
115 };
116
117 DataReductionProxyUsageStats::~DataReductionProxyUsageStats() {
118   NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
119 };
120
121 void DataReductionProxyUsageStats::OnUrlRequestCompleted(
122     const net::URLRequest* request, bool started) {
123   DCHECK(thread_checker_.CalledOnValidThread());
124
125   if (request->status().status() == net::URLRequestStatus::SUCCESS &&
126       data_reduction_proxy_params_->WasDataReductionProxyUsed(request, NULL)) {
127     successful_requests_through_proxy_count_++;
128     NotifyUnavailabilityIfChanged();
129   }
130 }
131
132 void DataReductionProxyUsageStats::OnNetworkChanged(
133     NetworkChangeNotifier::ConnectionType type) {
134   DCHECK(thread_checker_.CalledOnValidThread());
135   ClearRequestCounts();
136 }
137
138 void DataReductionProxyUsageStats::ClearRequestCounts() {
139   DCHECK(thread_checker_.CalledOnValidThread());
140   successful_requests_through_proxy_count_ = 0;
141   proxy_net_errors_count_ = 0;
142 }
143
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_));
155   }
156 }
157
158 void DataReductionProxyUsageStats::NotifyUnavailabilityOnUIThread(
159     bool unavailable) {
160   DCHECK(ui_thread_proxy_->BelongsToCurrentThread());
161   if (!unavailable_callback_.is_null())
162     unavailable_callback_.Run(unavailable);
163 }
164
165 void DataReductionProxyUsageStats::SetBypassType(
166     DataReductionProxyBypassType type) {
167   last_bypass_type_ = type;
168   triggering_request_ = true;
169 }
170
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);
178 }
179
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();
185
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;
193   }
194
195   if (data_reduction_proxy_params_->WasDataReductionProxyUsed(request, NULL)) {
196     RecordBypassedBytes(last_bypass_type_,
197                         DataReductionProxyUsageStats::NOT_BYPASSED,
198                         content_length);
199     return;
200   }
201
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;
208   }
209
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;
217   }
218
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;
237     }
238
239     RecordBypassedBytes(last_bypass_type_,
240                         DataReductionProxyUsageStats::TRIGGERING_REQUEST,
241                         content_length);
242     triggering_request_ = false;
243     return;
244   }
245
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;
251   }
252
253   if (data_reduction_proxy_params_->AreDataReductionProxiesBypassed(*request,
254                                                                     NULL)) {
255     RecordBypassedBytes(last_bypass_type_,
256                         DataReductionProxyUsageStats::NETWORK_ERROR,
257                         content_length);
258   }
259 }
260
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;
270
271     proxy_net_errors_count_++;
272
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();
282     }
283
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);
294     }
295   }
296 }
297
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);
329       }
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;
350       }
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;
408       }
409       break;
410   }
411 }
412
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());
418
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;
424   }
425
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());
434   }
435 }
436
437 }  // namespace data_reduction_proxy
438
439