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/domain_reliability/monitor.h"
7 #include "base/command_line.h"
8 #include "base/logging.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/task_runner.h"
11 #include "base/time/time.h"
12 #include "components/domain_reliability/baked_in_configs.h"
13 #include "net/base/load_flags.h"
14 #include "net/http/http_response_headers.h"
15 #include "net/url_request/url_request.h"
16 #include "net/url_request/url_request_context.h"
17 #include "net/url_request/url_request_context_getter.h"
19 namespace domain_reliability
{
21 DomainReliabilityMonitor::DomainReliabilityMonitor(
22 const std::string
& upload_reporter_string
,
23 scoped_refptr
<base::SingleThreadTaskRunner
> pref_thread
,
24 scoped_refptr
<base::SingleThreadTaskRunner
> network_thread
,
25 PrefService
* local_state_pref_service
,
26 const char* reporting_pref_name
)
27 : time_(new ActualTime()),
28 upload_reporter_string_(upload_reporter_string
),
30 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()),
31 dispatcher_(time_
.get()),
32 pref_task_runner_(pref_thread
),
33 network_task_runner_(network_thread
),
34 moved_to_network_thread_(false),
36 DCHECK(OnPrefThread());
37 InitReportingPref(local_state_pref_service
, reporting_pref_name
);
40 DomainReliabilityMonitor::DomainReliabilityMonitor(
41 const std::string
& upload_reporter_string
,
42 scoped_refptr
<base::SingleThreadTaskRunner
> pref_thread
,
43 scoped_refptr
<base::SingleThreadTaskRunner
> network_thread
,
44 PrefService
* local_state_pref_service
,
45 const char* reporting_pref_name
,
46 scoped_ptr
<MockableTime
> time
)
48 upload_reporter_string_(upload_reporter_string
),
50 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()),
51 dispatcher_(time_
.get()),
52 pref_task_runner_(pref_thread
),
53 network_task_runner_(network_thread
),
54 moved_to_network_thread_(false),
56 DCHECK(OnPrefThread());
57 InitReportingPref(local_state_pref_service
, reporting_pref_name
);
60 DomainReliabilityMonitor::~DomainReliabilityMonitor() {
61 if (moved_to_network_thread_
)
62 DCHECK(OnNetworkThread());
64 DCHECK(OnPrefThread());
69 void DomainReliabilityMonitor::MoveToNetworkThread() {
70 DCHECK(OnPrefThread());
71 DCHECK(!moved_to_network_thread_
);
73 reporting_pref_
.MoveToThread(network_task_runner_
);
74 moved_to_network_thread_
= true;
77 void DomainReliabilityMonitor::DestroyReportingPref() {
78 DCHECK(OnPrefThread());
80 reporting_pref_
.Destroy();
83 void DomainReliabilityMonitor::InitURLRequestContext(
84 net::URLRequestContext
* url_request_context
) {
85 DCHECK(OnNetworkThread());
86 DCHECK(moved_to_network_thread_
);
88 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter
=
89 new net::TrivialURLRequestContextGetter(url_request_context
,
90 network_task_runner_
);
91 InitURLRequestContext(url_request_context_getter
);
94 void DomainReliabilityMonitor::InitURLRequestContext(
95 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter
) {
96 DCHECK(OnNetworkThread());
97 DCHECK(moved_to_network_thread_
);
99 // Make sure the URLRequestContext actually lives on what was declared to be
100 // the network thread.
101 DCHECK(url_request_context_getter
->GetNetworkTaskRunner()->
102 RunsTasksOnCurrentThread());
104 uploader_
= DomainReliabilityUploader::Create(url_request_context_getter
);
105 // Make sure the uploader is sending or discarding uploads according to pref.
106 OnReportingPrefChanged();
109 void DomainReliabilityMonitor::AddBakedInConfigs() {
110 DCHECK(OnNetworkThread());
112 base::Time now
= base::Time::Now();
113 for (size_t i
= 0; kBakedInJsonConfigs
[i
]; ++i
) {
114 std::string
json(kBakedInJsonConfigs
[i
]);
115 scoped_ptr
<const DomainReliabilityConfig
> config
=
116 DomainReliabilityConfig::FromJSON(json
);
117 if (config
&& config
->IsExpired(now
)) {
118 LOG(WARNING
) << "Baked-in Domain Reliability config for "
119 << config
->domain
<< " is expired.";
122 AddContext(config
.Pass());
126 void DomainReliabilityMonitor::OnBeforeRedirect(net::URLRequest
* request
) {
127 DCHECK(OnNetworkThread());
129 // Record the redirect itself in addition to the final request.
130 OnRequestLegComplete(RequestInfo(*request
));
133 void DomainReliabilityMonitor::OnCompleted(net::URLRequest
* request
,
135 DCHECK(OnNetworkThread());
139 RequestInfo
request_info(*request
);
140 if (request_info
.AccessedNetwork()) {
141 OnRequestLegComplete(request_info
);
142 // A request was just using the network, so now is a good time to run any
143 // pending and eligible uploads.
144 dispatcher_
.RunEligibleTasks();
148 void DomainReliabilityMonitor::ClearBrowsingData(
149 DomainReliabilityClearMode mode
) {
150 DCHECK(OnNetworkThread());
153 case CLEAR_BEACONS
: {
154 ContextMap::const_iterator it
;
155 for (it
= contexts_
.begin(); it
!= contexts_
.end(); ++it
)
156 it
->second
->ClearBeacons();
167 scoped_ptr
<base::Value
> DomainReliabilityMonitor::GetWebUIData() const {
168 DCHECK(OnNetworkThread());
170 base::ListValue
* contexts_value
= new base::ListValue();
171 for (ContextMap::const_iterator it
= contexts_
.begin();
172 it
!= contexts_
.end();
174 contexts_value
->Append(it
->second
->GetWebUIData().release());
177 base::DictionaryValue
* data_value
= new base::DictionaryValue();
178 data_value
->Set("contexts", contexts_value
);
180 return scoped_ptr
<base::Value
>(data_value
);
183 DomainReliabilityContext
* DomainReliabilityMonitor::AddContextForTesting(
184 scoped_ptr
<const DomainReliabilityConfig
> config
) {
185 DCHECK(OnNetworkThread());
187 return AddContext(config
.Pass());
190 DomainReliabilityMonitor::RequestInfo::RequestInfo() {}
192 DomainReliabilityMonitor::RequestInfo::RequestInfo(
193 const net::URLRequest
& request
)
194 : url(request
.url()),
195 status(request
.status()),
196 response_info(request
.response_info()),
197 load_flags(request
.load_flags()),
198 is_upload(DomainReliabilityUploader::URLRequestIsUpload(request
)) {
199 request
.GetLoadTimingInfo(&load_timing_info
);
202 DomainReliabilityMonitor::RequestInfo::~RequestInfo() {}
204 bool DomainReliabilityMonitor::RequestInfo::AccessedNetwork() const {
205 return status
.status() != net::URLRequestStatus::CANCELED
&&
206 response_info
.network_accessed
;
209 DomainReliabilityContext
* DomainReliabilityMonitor::AddContext(
210 scoped_ptr
<const DomainReliabilityConfig
> config
) {
211 DCHECK(OnNetworkThread());
213 DCHECK(config
->IsValid());
215 // Grab a copy of the domain before transferring ownership of |config|.
216 std::string domain
= config
->domain
;
218 DomainReliabilityContext
* context
=
219 new DomainReliabilityContext(time_
.get(),
221 upload_reporter_string_
,
226 std::pair
<ContextMap::iterator
, bool> map_it
=
227 contexts_
.insert(make_pair(domain
, context
));
228 // Make sure the domain wasn't already in the map.
229 DCHECK(map_it
.second
);
231 return map_it
.first
->second
;
234 void DomainReliabilityMonitor::ClearContexts() {
235 STLDeleteContainerPairSecondPointers(
236 contexts_
.begin(), contexts_
.end());
240 void DomainReliabilityMonitor::OnRequestLegComplete(
241 const RequestInfo
& request
) {
243 if (request
.response_info
.headers
.get())
244 response_code
= request
.response_info
.headers
->response_code();
247 std::string beacon_status
;
249 int error_code
= net::OK
;
250 if (request
.status
.status() == net::URLRequestStatus::FAILED
)
251 error_code
= request
.status
.error();
253 DomainReliabilityContext
* context
= GetContextForHost(request
.url
.host());
255 // Ignore requests where:
256 // 1. There is no context for the request host.
257 // 2. The request did not access the network.
258 // 3. The request is not supposed to send cookies (to avoid associating the
259 // request with any potentially unique data in the config).
260 // 4. The request was itself a Domain Reliability upload (to avoid loops).
261 // 5. There is no defined beacon status for the error or HTTP response code
262 // (to avoid leaking network-local errors).
264 !request
.AccessedNetwork() ||
265 (request
.load_flags
& net::LOAD_DO_NOT_SEND_COOKIES
) ||
267 !GetDomainReliabilityBeaconStatus(
268 error_code
, response_code
, &beacon_status
)) {
272 DomainReliabilityBeacon beacon
;
273 beacon
.status
= beacon_status
;
274 beacon
.chrome_error
= error_code
;
275 if (!request
.response_info
.was_fetched_via_proxy
)
276 beacon
.server_ip
= request
.response_info
.socket_address
.host();
278 beacon
.server_ip
.clear();
279 beacon
.protocol
= GetDomainReliabilityProtocol(
280 request
.response_info
.connection_info
,
281 request
.response_info
.ssl_info
.is_valid());
282 beacon
.http_response_code
= response_code
;
283 beacon
.start_time
= request
.load_timing_info
.request_start
;
284 beacon
.elapsed
= time_
->NowTicks() - beacon
.start_time
;
285 context
->OnBeacon(request
.url
, beacon
);
288 void DomainReliabilityMonitor::InitReportingPref(
289 PrefService
* local_state_pref_service
,
290 const char* reporting_pref_name
) {
291 reporting_pref_
.Init(
293 local_state_pref_service
,
294 base::Bind(&DomainReliabilityMonitor::OnReportingPrefChanged
,
295 base::Unretained(this)));
298 void DomainReliabilityMonitor::OnReportingPrefChanged() {
299 DCHECK(OnNetworkThread());
301 // When metrics reporting is disabled, discard Domain Reliability uploads.
303 uploader_
->set_discard_uploads(!*reporting_pref_
);
306 // TODO(ttuttle): Keep a separate wildcard_contexts_ map to avoid having to
307 // prepend '*.' to domains.
308 DomainReliabilityContext
* DomainReliabilityMonitor::GetContextForHost(
309 const std::string
& host
) const {
310 DCHECK(OnNetworkThread());
312 ContextMap::const_iterator context_it
;
314 context_it
= contexts_
.find(host
);
315 if (context_it
!= contexts_
.end())
316 return context_it
->second
;
318 std::string host_with_asterisk
= "*." + host
;
319 context_it
= contexts_
.find(host_with_asterisk
);
320 if (context_it
!= contexts_
.end())
321 return context_it
->second
;
323 size_t dot_pos
= host
.find('.');
324 if (dot_pos
== std::string::npos
)
327 // TODO(ttuttle): Make sure parent is not in PSL before using.
329 std::string parent_with_asterisk
= "*." + host
.substr(dot_pos
+ 1);
330 context_it
= contexts_
.find(parent_with_asterisk
);
331 if (context_it
!= contexts_
.end())
332 return context_it
->second
;
337 base::WeakPtr
<DomainReliabilityMonitor
>
338 DomainReliabilityMonitor::MakeWeakPtr() {
339 return weak_factory_
.GetWeakPtr();
342 } // namespace domain_reliability