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/profiler/scoped_tracker.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/task_runner.h"
12 #include "base/time/time.h"
13 #include "components/domain_reliability/baked_in_configs.h"
14 #include "net/base/load_flags.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/url_request/url_request.h"
17 #include "net/url_request/url_request_context.h"
18 #include "net/url_request/url_request_context_getter.h"
20 namespace domain_reliability
{
22 DomainReliabilityMonitor::DomainReliabilityMonitor(
23 const std::string
& upload_reporter_string
,
24 scoped_refptr
<base::SingleThreadTaskRunner
> pref_thread
,
25 scoped_refptr
<base::SingleThreadTaskRunner
> network_thread
)
26 : time_(new ActualTime()),
27 upload_reporter_string_(upload_reporter_string
),
29 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()),
30 dispatcher_(time_
.get()),
31 pref_task_runner_(pref_thread
),
32 network_task_runner_(network_thread
),
33 moved_to_network_thread_(false),
34 discard_uploads_set_(false),
36 DCHECK(OnPrefThread());
37 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
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 scoped_ptr
<MockableTime
> time
)
46 upload_reporter_string_(upload_reporter_string
),
48 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()),
49 dispatcher_(time_
.get()),
50 pref_task_runner_(pref_thread
),
51 network_task_runner_(network_thread
),
52 moved_to_network_thread_(false),
53 discard_uploads_set_(false),
55 DCHECK(OnPrefThread());
56 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
59 DomainReliabilityMonitor::~DomainReliabilityMonitor() {
60 if (moved_to_network_thread_
)
61 DCHECK(OnNetworkThread());
63 DCHECK(OnPrefThread());
66 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
69 void DomainReliabilityMonitor::MoveToNetworkThread() {
70 DCHECK(OnPrefThread());
71 DCHECK(!moved_to_network_thread_
);
73 moved_to_network_thread_
= true;
76 void DomainReliabilityMonitor::InitURLRequestContext(
77 net::URLRequestContext
* url_request_context
) {
78 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436671 is fixed.
79 tracked_objects::ScopedTracker
tracking_profile(
80 FROM_HERE_WITH_EXPLICIT_FUNCTION(
81 "436671 DomainReliabilityMonitor::InitURLRequestContext"));
83 DCHECK(OnNetworkThread());
84 DCHECK(moved_to_network_thread_
);
86 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter
=
87 new net::TrivialURLRequestContextGetter(url_request_context
,
88 network_task_runner_
);
89 InitURLRequestContext(url_request_context_getter
);
92 void DomainReliabilityMonitor::InitURLRequestContext(
93 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter
) {
94 DCHECK(OnNetworkThread());
95 DCHECK(moved_to_network_thread_
);
97 // Make sure the URLRequestContext actually lives on what was declared to be
98 // the network thread.
99 DCHECK(url_request_context_getter
->GetNetworkTaskRunner()->
100 RunsTasksOnCurrentThread());
102 uploader_
= DomainReliabilityUploader::Create(time_
.get(),
103 url_request_context_getter
);
106 void DomainReliabilityMonitor::AddBakedInConfigs() {
107 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436671 is fixed.
108 tracked_objects::ScopedTracker
tracking_profile(
109 FROM_HERE_WITH_EXPLICIT_FUNCTION(
110 "436671 DomainReliabilityMonitor::AddBakedInConfigs"));
112 DCHECK(OnNetworkThread());
113 DCHECK(moved_to_network_thread_
);
115 base::Time now
= base::Time::Now();
116 for (size_t i
= 0; kBakedInJsonConfigs
[i
]; ++i
) {
117 std::string
json(kBakedInJsonConfigs
[i
]);
118 scoped_ptr
<const DomainReliabilityConfig
> config
=
119 DomainReliabilityConfig::FromJSON(json
);
120 if (config
&& config
->IsExpired(now
)) {
121 LOG(WARNING
) << "Baked-in Domain Reliability config for "
122 << config
->domain
<< " is expired.";
125 AddContext(config
.Pass());
129 void DomainReliabilityMonitor::SetDiscardUploads(bool discard_uploads
) {
130 DCHECK(OnNetworkThread());
131 DCHECK(moved_to_network_thread_
);
134 uploader_
->set_discard_uploads(discard_uploads
);
135 discard_uploads_set_
= true;
138 void DomainReliabilityMonitor::OnBeforeRedirect(net::URLRequest
* request
) {
139 DCHECK(OnNetworkThread());
140 DCHECK(discard_uploads_set_
);
142 // Record the redirect itself in addition to the final request.
143 OnRequestLegComplete(RequestInfo(*request
));
146 void DomainReliabilityMonitor::OnCompleted(net::URLRequest
* request
,
148 DCHECK(OnNetworkThread());
149 DCHECK(discard_uploads_set_
);
153 RequestInfo
request_info(*request
);
154 if (request_info
.AccessedNetwork()) {
155 OnRequestLegComplete(request_info
);
156 // A request was just using the network, so now is a good time to run any
157 // pending and eligible uploads.
158 dispatcher_
.RunEligibleTasks();
162 void DomainReliabilityMonitor::OnNetworkChanged(
163 net::NetworkChangeNotifier::ConnectionType type
) {
164 last_network_change_time_
= time_
->NowTicks();
167 void DomainReliabilityMonitor::ClearBrowsingData(
168 DomainReliabilityClearMode mode
) {
169 DCHECK(OnNetworkThread());
172 case CLEAR_BEACONS
: {
173 ContextMap::const_iterator it
;
174 for (it
= contexts_
.begin(); it
!= contexts_
.end(); ++it
)
175 it
->second
->ClearBeacons();
186 scoped_ptr
<base::Value
> DomainReliabilityMonitor::GetWebUIData() const {
187 DCHECK(OnNetworkThread());
189 base::ListValue
* contexts_value
= new base::ListValue();
190 for (ContextMap::const_iterator it
= contexts_
.begin();
191 it
!= contexts_
.end();
193 contexts_value
->Append(it
->second
->GetWebUIData().release());
196 base::DictionaryValue
* data_value
= new base::DictionaryValue();
197 data_value
->Set("contexts", contexts_value
);
199 return scoped_ptr
<base::Value
>(data_value
);
202 DomainReliabilityContext
* DomainReliabilityMonitor::AddContextForTesting(
203 scoped_ptr
<const DomainReliabilityConfig
> config
) {
204 DCHECK(OnNetworkThread());
206 return AddContext(config
.Pass());
209 DomainReliabilityMonitor::RequestInfo::RequestInfo() {}
211 DomainReliabilityMonitor::RequestInfo::RequestInfo(
212 const net::URLRequest
& request
)
213 : url(request
.url()),
214 status(request
.status()),
215 response_info(request
.response_info()),
216 load_flags(request
.load_flags()),
217 is_upload(DomainReliabilityUploader::URLRequestIsUpload(request
)) {
218 request
.GetLoadTimingInfo(&load_timing_info
);
221 DomainReliabilityMonitor::RequestInfo::~RequestInfo() {}
223 bool DomainReliabilityMonitor::RequestInfo::AccessedNetwork() const {
224 return status
.status() != net::URLRequestStatus::CANCELED
&&
225 response_info
.network_accessed
;
228 DomainReliabilityContext
* DomainReliabilityMonitor::AddContext(
229 scoped_ptr
<const DomainReliabilityConfig
> config
) {
230 DCHECK(OnNetworkThread());
232 DCHECK(config
->IsValid());
234 // Grab a copy of the domain before transferring ownership of |config|.
235 std::string domain
= config
->domain
;
237 DomainReliabilityContext
* context
=
238 new DomainReliabilityContext(time_
.get(),
240 upload_reporter_string_
,
241 &last_network_change_time_
,
246 std::pair
<ContextMap::iterator
, bool> map_it
=
247 contexts_
.insert(make_pair(domain
, context
));
248 // Make sure the domain wasn't already in the map.
249 DCHECK(map_it
.second
);
251 return map_it
.first
->second
;
254 void DomainReliabilityMonitor::ClearContexts() {
255 STLDeleteContainerPairSecondPointers(
256 contexts_
.begin(), contexts_
.end());
260 void DomainReliabilityMonitor::OnRequestLegComplete(
261 const RequestInfo
& request
) {
262 // Check these again because unit tests call this directly.
263 DCHECK(OnNetworkThread());
264 DCHECK(discard_uploads_set_
);
267 if (request
.response_info
.headers
.get())
268 response_code
= request
.response_info
.headers
->response_code();
271 std::string beacon_status
;
273 int error_code
= net::OK
;
274 if (request
.status
.status() == net::URLRequestStatus::FAILED
)
275 error_code
= request
.status
.error();
277 DomainReliabilityContext
* context
= GetContextForHost(request
.url
.host());
279 // Ignore requests where:
280 // 1. There is no context for the request host.
281 // 2. The request did not access the network.
282 // 3. The request is not supposed to send cookies (to avoid associating the
283 // request with any potentially unique data in the config).
284 // 4. The request was itself a Domain Reliability upload (to avoid loops).
285 // 5. There is no defined beacon status for the error or HTTP response code
286 // (to avoid leaking network-local errors).
288 !request
.AccessedNetwork() ||
289 (request
.load_flags
& net::LOAD_DO_NOT_SEND_COOKIES
) ||
291 !GetDomainReliabilityBeaconStatus(
292 error_code
, response_code
, &beacon_status
)) {
296 DomainReliabilityBeacon beacon
;
297 beacon
.status
= beacon_status
;
298 beacon
.chrome_error
= error_code
;
299 // If the response was cached, the socket address was the address that the
300 // response was originally received from, so it shouldn't be copied into the
303 // TODO(ttuttle): Wire up a way to get the real socket address in that case.
304 if (!request
.response_info
.was_cached
&&
305 !request
.response_info
.was_fetched_via_proxy
) {
306 beacon
.server_ip
= request
.response_info
.socket_address
.host();
308 beacon
.protocol
= GetDomainReliabilityProtocol(
309 request
.response_info
.connection_info
,
310 request
.response_info
.ssl_info
.is_valid());
311 beacon
.http_response_code
= response_code
;
312 beacon
.start_time
= request
.load_timing_info
.request_start
;
313 beacon
.elapsed
= time_
->NowTicks() - beacon
.start_time
;
314 beacon
.domain
= request
.url
.host();
315 context
->OnBeacon(request
.url
, beacon
);
318 // TODO(ttuttle): Keep a separate wildcard_contexts_ map to avoid having to
319 // prepend '*.' to domains.
320 DomainReliabilityContext
* DomainReliabilityMonitor::GetContextForHost(
321 const std::string
& host
) const {
322 DCHECK(OnNetworkThread());
324 ContextMap::const_iterator context_it
;
326 context_it
= contexts_
.find(host
);
327 if (context_it
!= contexts_
.end())
328 return context_it
->second
;
330 std::string host_with_asterisk
= "*." + host
;
331 context_it
= contexts_
.find(host_with_asterisk
);
332 if (context_it
!= contexts_
.end())
333 return context_it
->second
;
335 size_t dot_pos
= host
.find('.');
336 if (dot_pos
== std::string::npos
)
339 // TODO(ttuttle): Make sure parent is not in PSL before using.
341 std::string parent_with_asterisk
= "*." + host
.substr(dot_pos
+ 1);
342 context_it
= contexts_
.find(parent_with_asterisk
);
343 if (context_it
!= contexts_
.end())
344 return context_it
->second
;
349 base::WeakPtr
<DomainReliabilityMonitor
>
350 DomainReliabilityMonitor::MakeWeakPtr() {
351 return weak_factory_
.GetWeakPtr();
354 } // namespace domain_reliability