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/ip_endpoint.h"
15 #include "net/base/load_flags.h"
16 #include "net/base/net_errors.h"
17 #include "net/base/net_util.h"
18 #include "net/http/http_response_headers.h"
19 #include "net/url_request/url_request.h"
20 #include "net/url_request/url_request_context.h"
21 #include "net/url_request/url_request_context_getter.h"
23 namespace domain_reliability
{
27 int URLRequestStatusToNetError(const net::URLRequestStatus
& status
) {
28 switch (status
.status()) {
29 case net::URLRequestStatus::SUCCESS
:
31 case net::URLRequestStatus::IO_PENDING
:
32 return net::ERR_IO_PENDING
;
33 case net::URLRequestStatus::CANCELED
:
34 return net::ERR_ABORTED
;
35 case net::URLRequestStatus::FAILED
:
36 return status
.error();
39 return net::ERR_UNEXPECTED
;
43 // Updates the status, chrome_error, and server_ip fields of |beacon| from
44 // the endpoint and result of |attempt|. If there is no matching status for
45 // the result, returns false (which means the attempt should not result in a
46 // beacon being reported).
47 bool UpdateBeaconFromAttempt(DomainReliabilityBeacon
* beacon
,
48 const net::ConnectionAttempt
& attempt
) {
49 if (!GetDomainReliabilityBeaconStatus(
50 attempt
.result
, beacon
->http_response_code
, &beacon
->status
)) {
53 beacon
->chrome_error
= attempt
.result
;
54 if (!attempt
.endpoint
.address().empty())
55 beacon
->server_ip
= attempt
.endpoint
.ToString();
57 beacon
->server_ip
= "";
61 // TODO(ttuttle): This function is absurd. See if |socket_address| in
62 // HttpResponseInfo can become an IPEndPoint.
63 bool ConvertHostPortPairToIPEndPoint(const net::HostPortPair
& host_port_pair
,
64 net::IPEndPoint
* ip_endpoint_out
) {
65 net::IPAddressNumber ip_address_number
;
66 if (!net::ParseIPLiteralToNumber(host_port_pair
.host(), &ip_address_number
))
68 *ip_endpoint_out
= net::IPEndPoint(ip_address_number
, host_port_pair
.port());
74 DomainReliabilityMonitor::DomainReliabilityMonitor(
75 const std::string
& upload_reporter_string
,
76 const scoped_refptr
<base::SingleThreadTaskRunner
>& pref_thread
,
77 const scoped_refptr
<base::SingleThreadTaskRunner
>& network_thread
)
78 : time_(new ActualTime()),
79 upload_reporter_string_(upload_reporter_string
),
81 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()),
82 dispatcher_(time_
.get()),
83 context_manager_(this),
84 pref_task_runner_(pref_thread
),
85 network_task_runner_(network_thread
),
86 moved_to_network_thread_(false),
87 discard_uploads_set_(false),
89 DCHECK(OnPrefThread());
90 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
93 DomainReliabilityMonitor::DomainReliabilityMonitor(
94 const std::string
& upload_reporter_string
,
95 const scoped_refptr
<base::SingleThreadTaskRunner
>& pref_thread
,
96 const scoped_refptr
<base::SingleThreadTaskRunner
>& network_thread
,
97 scoped_ptr
<MockableTime
> time
)
99 upload_reporter_string_(upload_reporter_string
),
101 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()),
102 dispatcher_(time_
.get()),
103 context_manager_(this),
104 pref_task_runner_(pref_thread
),
105 network_task_runner_(network_thread
),
106 moved_to_network_thread_(false),
107 discard_uploads_set_(false),
108 weak_factory_(this) {
109 DCHECK(OnPrefThread());
110 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
113 DomainReliabilityMonitor::~DomainReliabilityMonitor() {
114 if (moved_to_network_thread_
)
115 DCHECK(OnNetworkThread());
117 DCHECK(OnPrefThread());
119 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
122 void DomainReliabilityMonitor::MoveToNetworkThread() {
123 DCHECK(OnPrefThread());
124 DCHECK(!moved_to_network_thread_
);
126 moved_to_network_thread_
= true;
129 void DomainReliabilityMonitor::InitURLRequestContext(
130 net::URLRequestContext
* url_request_context
) {
131 DCHECK(OnNetworkThread());
132 DCHECK(moved_to_network_thread_
);
134 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter
=
135 new net::TrivialURLRequestContextGetter(url_request_context
,
136 network_task_runner_
);
137 InitURLRequestContext(url_request_context_getter
);
140 void DomainReliabilityMonitor::InitURLRequestContext(
141 const scoped_refptr
<net::URLRequestContextGetter
>&
142 url_request_context_getter
) {
143 DCHECK(OnNetworkThread());
144 DCHECK(moved_to_network_thread_
);
146 // Make sure the URLRequestContext actually lives on what was declared to be
147 // the network thread.
148 DCHECK(url_request_context_getter
->GetNetworkTaskRunner()->
149 RunsTasksOnCurrentThread());
151 uploader_
= DomainReliabilityUploader::Create(time_
.get(),
152 url_request_context_getter
);
155 void DomainReliabilityMonitor::AddBakedInConfigs() {
156 // TODO(ttuttle): Remove ScopedTracker below once crbug.com/436671 is fixed.
157 tracked_objects::ScopedTracker
tracking_profile(
158 FROM_HERE_WITH_EXPLICIT_FUNCTION(
159 "436671 DomainReliabilityMonitor::AddBakedInConfigs"));
161 DCHECK(OnNetworkThread());
162 DCHECK(moved_to_network_thread_
);
164 base::Time now
= base::Time::Now();
165 for (size_t i
= 0; kBakedInJsonConfigs
[i
]; ++i
) {
166 base::StringPiece
json(kBakedInJsonConfigs
[i
]);
167 scoped_ptr
<const DomainReliabilityConfig
> config
=
168 DomainReliabilityConfig::FromJSON(json
);
171 } else if (config
->IsExpired(now
)) {
172 LOG(WARNING
) << "Baked-in Domain Reliability config for "
173 << config
->domain
<< " is expired.";
176 context_manager_
.AddContextForConfig(config
.Pass());
180 void DomainReliabilityMonitor::SetDiscardUploads(bool discard_uploads
) {
181 DCHECK(OnNetworkThread());
182 DCHECK(moved_to_network_thread_
);
185 uploader_
->set_discard_uploads(discard_uploads
);
186 discard_uploads_set_
= true;
189 void DomainReliabilityMonitor::OnBeforeRedirect(net::URLRequest
* request
) {
190 DCHECK(OnNetworkThread());
191 DCHECK(discard_uploads_set_
);
193 // Record the redirect itself in addition to the final request.
194 OnRequestLegComplete(RequestInfo(*request
));
197 void DomainReliabilityMonitor::OnCompleted(net::URLRequest
* request
,
199 DCHECK(OnNetworkThread());
200 DCHECK(discard_uploads_set_
);
204 RequestInfo
request_info(*request
);
205 OnRequestLegComplete(request_info
);
207 if (request_info
.response_info
.network_accessed
) {
208 // A request was just using the network, so now is a good time to run any
209 // pending and eligible uploads.
210 dispatcher_
.RunEligibleTasks();
214 void DomainReliabilityMonitor::OnNetworkChanged(
215 net::NetworkChangeNotifier::ConnectionType type
) {
216 last_network_change_time_
= time_
->NowTicks();
219 void DomainReliabilityMonitor::ClearBrowsingData(
220 DomainReliabilityClearMode mode
) {
221 DCHECK(OnNetworkThread());
225 context_manager_
.ClearBeaconsInAllContexts();
228 context_manager_
.RemoveAllContexts();
235 scoped_ptr
<base::Value
> DomainReliabilityMonitor::GetWebUIData() const {
236 DCHECK(OnNetworkThread());
238 scoped_ptr
<base::DictionaryValue
> data_value(new base::DictionaryValue());
239 data_value
->Set("contexts", context_manager_
.GetWebUIData());
240 return data_value
.Pass();
243 DomainReliabilityContext
* DomainReliabilityMonitor::AddContextForTesting(
244 scoped_ptr
<const DomainReliabilityConfig
> config
) {
245 DCHECK(OnNetworkThread());
247 return context_manager_
.AddContextForConfig(config
.Pass());
250 scoped_ptr
<DomainReliabilityContext
>
251 DomainReliabilityMonitor::CreateContextForConfig(
252 scoped_ptr
<const DomainReliabilityConfig
> config
) {
253 DCHECK(OnNetworkThread());
255 DCHECK(config
->IsValid());
257 return make_scoped_ptr(new DomainReliabilityContext(
260 upload_reporter_string_
,
261 &last_network_change_time_
,
267 DomainReliabilityMonitor::RequestInfo::RequestInfo() {}
269 DomainReliabilityMonitor::RequestInfo::RequestInfo(
270 const net::URLRequest
& request
)
271 : url(request
.url()),
272 status(request
.status()),
273 response_info(request
.response_info()),
274 load_flags(request
.load_flags()),
275 is_upload(DomainReliabilityUploader::URLRequestIsUpload(request
)) {
276 request
.GetLoadTimingInfo(&load_timing_info
);
277 request
.GetConnectionAttempts(&connection_attempts
);
280 DomainReliabilityMonitor::RequestInfo::~RequestInfo() {}
283 bool DomainReliabilityMonitor::RequestInfo::ShouldReportRequest(
284 const DomainReliabilityMonitor::RequestInfo
& request
) {
285 // Don't report requests for Domain Reliability uploads or that weren't
286 // supposed to send cookies.
287 if (request
.is_upload
)
289 if (request
.load_flags
& net::LOAD_DO_NOT_SEND_COOKIES
)
291 // Report requests that accessed the network or failed with an error code
292 // that Domain Reliability is interested in.
293 if (request
.response_info
.network_accessed
)
295 if (URLRequestStatusToNetError(request
.status
) != net::OK
)
300 void DomainReliabilityMonitor::OnRequestLegComplete(
301 const RequestInfo
& request
) {
302 // Check these again because unit tests call this directly.
303 DCHECK(OnNetworkThread());
304 DCHECK(discard_uploads_set_
);
306 if (!RequestInfo::ShouldReportRequest(request
))
310 if (request
.response_info
.headers
.get())
311 response_code
= request
.response_info
.headers
->response_code();
315 net::IPEndPoint url_request_endpoint
;
316 // If response was cached, socket address will be from the serialized
317 // response info in the cache, so don't report it.
318 // TODO(ttuttle): Plumb out the "current" socket address so we can always
320 if (!request
.response_info
.was_cached
&&
321 !request
.response_info
.was_fetched_via_proxy
) {
322 ConvertHostPortPairToIPEndPoint(request
.response_info
.socket_address
,
323 &url_request_endpoint
);
326 net::ConnectionAttempt
url_request_attempt(
327 url_request_endpoint
, URLRequestStatusToNetError(request
.status
));
329 DomainReliabilityBeacon beacon
;
330 beacon
.protocol
= GetDomainReliabilityProtocol(
331 request
.response_info
.connection_info
,
332 request
.response_info
.ssl_info
.is_valid());
333 beacon
.http_response_code
= response_code
;
334 beacon
.start_time
= request
.load_timing_info
.request_start
;
335 beacon
.elapsed
= time_
->NowTicks() - beacon
.start_time
;
336 beacon
.was_proxied
= request
.response_info
.was_fetched_via_proxy
;
337 beacon
.domain
= request
.url
.host();
339 // This is not foolproof -- it's possible that we'll see the same error twice
340 // (e.g. an SSL error during connection on one attempt, and then an error
341 // that maps to the same code during a read).
342 // TODO(ttuttle): Find a way for this code to reliably tell whether we
343 // eventually established a connection or not.
344 bool url_request_attempt_is_duplicate
= false;
345 for (const auto& attempt
: request
.connection_attempts
) {
346 if (attempt
.result
== url_request_attempt
.result
)
347 url_request_attempt_is_duplicate
= true;
348 if (!UpdateBeaconFromAttempt(&beacon
, attempt
))
350 context_manager_
.RouteBeacon(request
.url
, beacon
);
353 if (url_request_attempt_is_duplicate
)
355 if (!UpdateBeaconFromAttempt(&beacon
, url_request_attempt
))
357 context_manager_
.RouteBeacon(request
.url
, beacon
);
360 base::WeakPtr
<DomainReliabilityMonitor
>
361 DomainReliabilityMonitor::MakeWeakPtr() {
362 return weak_factory_
.GetWeakPtr();
365 } // namespace domain_reliability