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/message_loop/message_loop.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/task_runner.h"
12 #include "base/threading/thread_checker.h"
13 #include "base/time/time.h"
14 #include "components/domain_reliability/baked_in_configs.h"
15 #include "net/base/load_flags.h"
16 #include "net/http/http_response_headers.h"
17 #include "net/url_request/url_request.h"
18 #include "net/url_request/url_request_context.h"
19 #include "net/url_request/url_request_context_getter.h"
21 namespace domain_reliability {
23 DomainReliabilityMonitor::DomainReliabilityMonitor(
24 const std::string& upload_reporter_string)
25 : time_(new ActualTime()),
26 upload_reporter_string_(upload_reporter_string),
28 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()),
29 dispatcher_(time_.get()),
30 weak_factory_(this) {}
32 DomainReliabilityMonitor::DomainReliabilityMonitor(
33 const std::string& upload_reporter_string,
34 scoped_ptr<MockableTime> time)
36 upload_reporter_string_(upload_reporter_string),
38 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()),
39 dispatcher_(time_.get()),
40 weak_factory_(this) {}
42 DomainReliabilityMonitor::~DomainReliabilityMonitor() {
46 void DomainReliabilityMonitor::Init(
47 net::URLRequestContext* url_request_context,
48 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
49 DCHECK(!thread_checker_);
51 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
52 new net::TrivialURLRequestContextGetter(url_request_context,
54 Init(url_request_context_getter);
57 void DomainReliabilityMonitor::Init(
58 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter) {
59 DCHECK(!thread_checker_);
61 DCHECK(url_request_context_getter->GetNetworkTaskRunner()->
62 RunsTasksOnCurrentThread());
64 uploader_ = DomainReliabilityUploader::Create(url_request_context_getter);
65 thread_checker_.reset(new base::ThreadChecker());
68 void DomainReliabilityMonitor::AddBakedInConfigs() {
69 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
70 base::Time now = base::Time::Now();
71 for (size_t i = 0; kBakedInJsonConfigs[i]; ++i) {
72 std::string json(kBakedInJsonConfigs[i]);
73 scoped_ptr<const DomainReliabilityConfig> config =
74 DomainReliabilityConfig::FromJSON(json);
75 if (config && config->IsExpired(now)) {
76 LOG(WARNING) << "Baked-in Domain Reliability config for "
77 << config->domain << " is expired.";
80 AddContext(config.Pass());
84 void DomainReliabilityMonitor::OnBeforeRedirect(net::URLRequest* request) {
85 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
86 // Record the redirect itself in addition to the final request.
87 OnRequestLegComplete(RequestInfo(*request));
90 void DomainReliabilityMonitor::OnCompleted(net::URLRequest* request,
92 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
95 RequestInfo request_info(*request);
96 if (request_info.AccessedNetwork()) {
97 OnRequestLegComplete(request_info);
98 // A request was just using the network, so now is a good time to run any
99 // pending and eligible uploads.
100 dispatcher_.RunEligibleTasks();
104 void DomainReliabilityMonitor::ClearBrowsingData(
105 DomainReliabilityClearMode mode) {
106 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
109 case CLEAR_BEACONS: {
110 ContextMap::const_iterator it;
111 for (it = contexts_.begin(); it != contexts_.end(); ++it)
112 it->second->ClearBeacons();
123 scoped_ptr<base::Value> DomainReliabilityMonitor::GetWebUIData() const {
124 base::ListValue* contexts_value = new base::ListValue();
125 for (ContextMap::const_iterator it = contexts_.begin();
126 it != contexts_.end();
128 contexts_value->Append(it->second->GetWebUIData().release());
131 base::DictionaryValue* data_value = new base::DictionaryValue();
132 data_value->Set("contexts", contexts_value);
134 return scoped_ptr<base::Value>(data_value);
137 DomainReliabilityContext* DomainReliabilityMonitor::AddContextForTesting(
138 scoped_ptr<const DomainReliabilityConfig> config) {
139 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
140 return AddContext(config.Pass());
143 DomainReliabilityMonitor::RequestInfo::RequestInfo() {}
145 DomainReliabilityMonitor::RequestInfo::RequestInfo(
146 const net::URLRequest& request)
147 : url(request.url()),
148 status(request.status()),
149 response_info(request.response_info()),
150 load_flags(request.load_flags()),
151 is_upload(DomainReliabilityUploader::URLRequestIsUpload(request)) {
152 request.GetLoadTimingInfo(&load_timing_info);
155 DomainReliabilityMonitor::RequestInfo::~RequestInfo() {}
157 bool DomainReliabilityMonitor::RequestInfo::AccessedNetwork() const {
158 return status.status() != net::URLRequestStatus::CANCELED &&
159 response_info.network_accessed;
162 DomainReliabilityContext* DomainReliabilityMonitor::AddContext(
163 scoped_ptr<const DomainReliabilityConfig> config) {
165 DCHECK(config->IsValid());
167 // Grab a copy of the domain before transferring ownership of |config|.
168 std::string domain = config->domain;
170 DomainReliabilityContext* context =
171 new DomainReliabilityContext(time_.get(),
173 upload_reporter_string_,
178 std::pair<ContextMap::iterator, bool> map_it =
179 contexts_.insert(make_pair(domain, context));
180 // Make sure the domain wasn't already in the map.
181 DCHECK(map_it.second);
183 return map_it.first->second;
186 void DomainReliabilityMonitor::ClearContexts() {
187 STLDeleteContainerPairSecondPointers(
188 contexts_.begin(), contexts_.end());
192 void DomainReliabilityMonitor::OnRequestLegComplete(
193 const RequestInfo& request) {
195 if (request.response_info.headers)
196 response_code = request.response_info.headers->response_code();
199 std::string beacon_status;
201 int error_code = net::OK;
202 if (request.status.status() == net::URLRequestStatus::FAILED)
203 error_code = request.status.error();
205 DomainReliabilityContext* context = GetContextForHost(request.url.host());
207 // Ignore requests where:
208 // 1. There is no context for the request host.
209 // 2. The request did not access the network.
210 // 3. The request is not supposed to send cookies (to avoid associating the
211 // request with any potentially unique data in the config).
212 // 4. The request was itself a Domain Reliability upload (to avoid loops).
213 // 5. There is no defined beacon status for the error or HTTP response code
214 // (to avoid leaking network-local errors).
216 !request.AccessedNetwork() ||
217 (request.load_flags & net::LOAD_DO_NOT_SEND_COOKIES) ||
219 !GetDomainReliabilityBeaconStatus(
220 error_code, response_code, &beacon_status)) {
224 DomainReliabilityBeacon beacon;
225 beacon.status = beacon_status;
226 beacon.chrome_error = error_code;
227 if (!request.response_info.was_fetched_via_proxy)
228 beacon.server_ip = request.response_info.socket_address.host();
230 beacon.server_ip.clear();
231 beacon.protocol = GetDomainReliabilityProtocol(
232 request.response_info.connection_info,
233 request.response_info.ssl_info.is_valid());
234 beacon.http_response_code = response_code;
235 beacon.start_time = request.load_timing_info.request_start;
236 beacon.elapsed = time_->NowTicks() - beacon.start_time;
237 context->OnBeacon(request.url, beacon);
240 // TODO(ttuttle): Keep a separate wildcard_contexts_ map to avoid having to
241 // prepend '*.' to domains.
242 DomainReliabilityContext* DomainReliabilityMonitor::GetContextForHost(
243 const std::string& host) const {
244 ContextMap::const_iterator context_it;
246 context_it = contexts_.find(host);
247 if (context_it != contexts_.end())
248 return context_it->second;
250 std::string host_with_asterisk = "*." + host;
251 context_it = contexts_.find(host_with_asterisk);
252 if (context_it != contexts_.end())
253 return context_it->second;
255 size_t dot_pos = host.find('.');
256 if (dot_pos == std::string::npos)
259 // TODO(ttuttle): Make sure parent is not in PSL before using.
261 std::string parent_with_asterisk = "*." + host.substr(dot_pos + 1);
262 context_it = contexts_.find(parent_with_asterisk);
263 if (context_it != contexts_.end())
264 return context_it->second;
269 base::WeakPtr<DomainReliabilityMonitor>
270 DomainReliabilityMonitor::MakeWeakPtr() {
271 return weak_factory_.GetWeakPtr();
274 } // namespace domain_reliability