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/domain_reliability/monitor.h"
6
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"
18
19namespace domain_reliability {
20
21DomainReliabilityMonitor::DomainReliabilityMonitor(
22    const std::string& upload_reporter_string,
23    scoped_refptr<base::SingleThreadTaskRunner> pref_thread,
24    scoped_refptr<base::SingleThreadTaskRunner> network_thread)
25    : time_(new ActualTime()),
26      upload_reporter_string_(upload_reporter_string),
27      scheduler_params_(
28          DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()),
29      dispatcher_(time_.get()),
30      pref_task_runner_(pref_thread),
31      network_task_runner_(network_thread),
32      moved_to_network_thread_(false),
33      discard_uploads_set_(false),
34      weak_factory_(this) {
35  DCHECK(OnPrefThread());
36}
37
38DomainReliabilityMonitor::DomainReliabilityMonitor(
39    const std::string& upload_reporter_string,
40    scoped_refptr<base::SingleThreadTaskRunner> pref_thread,
41    scoped_refptr<base::SingleThreadTaskRunner> network_thread,
42    scoped_ptr<MockableTime> time)
43    : time_(time.Pass()),
44      upload_reporter_string_(upload_reporter_string),
45      scheduler_params_(
46          DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()),
47      dispatcher_(time_.get()),
48      pref_task_runner_(pref_thread),
49      network_task_runner_(network_thread),
50      moved_to_network_thread_(false),
51      discard_uploads_set_(false),
52      weak_factory_(this) {
53  DCHECK(OnPrefThread());
54}
55
56DomainReliabilityMonitor::~DomainReliabilityMonitor() {
57  if (moved_to_network_thread_)
58    DCHECK(OnNetworkThread());
59  else
60    DCHECK(OnPrefThread());
61
62  ClearContexts();
63}
64
65void DomainReliabilityMonitor::MoveToNetworkThread() {
66  DCHECK(OnPrefThread());
67  DCHECK(!moved_to_network_thread_);
68
69  moved_to_network_thread_ = true;
70}
71
72void DomainReliabilityMonitor::InitURLRequestContext(
73    net::URLRequestContext* url_request_context) {
74  DCHECK(OnNetworkThread());
75  DCHECK(moved_to_network_thread_);
76
77  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
78      new net::TrivialURLRequestContextGetter(url_request_context,
79                                              network_task_runner_);
80  InitURLRequestContext(url_request_context_getter);
81}
82
83void DomainReliabilityMonitor::InitURLRequestContext(
84    scoped_refptr<net::URLRequestContextGetter> url_request_context_getter) {
85  DCHECK(OnNetworkThread());
86  DCHECK(moved_to_network_thread_);
87
88  // Make sure the URLRequestContext actually lives on what was declared to be
89  // the network thread.
90  DCHECK(url_request_context_getter->GetNetworkTaskRunner()->
91         RunsTasksOnCurrentThread());
92
93  uploader_ = DomainReliabilityUploader::Create(url_request_context_getter);
94}
95
96void DomainReliabilityMonitor::AddBakedInConfigs() {
97  DCHECK(OnNetworkThread());
98  DCHECK(moved_to_network_thread_);
99
100  base::Time now = base::Time::Now();
101  for (size_t i = 0; kBakedInJsonConfigs[i]; ++i) {
102    std::string json(kBakedInJsonConfigs[i]);
103    scoped_ptr<const DomainReliabilityConfig> config =
104        DomainReliabilityConfig::FromJSON(json);
105    if (config && config->IsExpired(now)) {
106      LOG(WARNING) << "Baked-in Domain Reliability config for "
107                   << config->domain << " is expired.";
108      continue;
109    }
110    AddContext(config.Pass());
111  }
112}
113
114void DomainReliabilityMonitor::SetDiscardUploads(bool discard_uploads) {
115  DCHECK(OnNetworkThread());
116  DCHECK(moved_to_network_thread_);
117  DCHECK(uploader_);
118
119  uploader_->set_discard_uploads(discard_uploads);
120  discard_uploads_set_ = true;
121}
122
123void DomainReliabilityMonitor::OnBeforeRedirect(net::URLRequest* request) {
124  DCHECK(OnNetworkThread());
125  DCHECK(discard_uploads_set_);
126
127  // Record the redirect itself in addition to the final request.
128  OnRequestLegComplete(RequestInfo(*request));
129}
130
131void DomainReliabilityMonitor::OnCompleted(net::URLRequest* request,
132                                           bool started) {
133  DCHECK(OnNetworkThread());
134  DCHECK(discard_uploads_set_);
135
136  if (!started)
137    return;
138  RequestInfo request_info(*request);
139  if (request_info.AccessedNetwork()) {
140    OnRequestLegComplete(request_info);
141    // A request was just using the network, so now is a good time to run any
142    // pending and eligible uploads.
143    dispatcher_.RunEligibleTasks();
144  }
145}
146
147void DomainReliabilityMonitor::ClearBrowsingData(
148   DomainReliabilityClearMode mode) {
149  DCHECK(OnNetworkThread());
150
151  switch (mode) {
152    case CLEAR_BEACONS: {
153      ContextMap::const_iterator it;
154      for (it = contexts_.begin(); it != contexts_.end(); ++it)
155        it->second->ClearBeacons();
156      break;
157    };
158    case CLEAR_CONTEXTS:
159      ClearContexts();
160      break;
161    case MAX_CLEAR_MODE:
162      NOTREACHED();
163  }
164}
165
166scoped_ptr<base::Value> DomainReliabilityMonitor::GetWebUIData() const {
167  DCHECK(OnNetworkThread());
168
169  base::ListValue* contexts_value = new base::ListValue();
170  for (ContextMap::const_iterator it = contexts_.begin();
171       it != contexts_.end();
172       ++it) {
173    contexts_value->Append(it->second->GetWebUIData().release());
174  }
175
176  base::DictionaryValue* data_value = new base::DictionaryValue();
177  data_value->Set("contexts", contexts_value);
178
179  return scoped_ptr<base::Value>(data_value);
180}
181
182DomainReliabilityContext* DomainReliabilityMonitor::AddContextForTesting(
183    scoped_ptr<const DomainReliabilityConfig> config) {
184  DCHECK(OnNetworkThread());
185
186  return AddContext(config.Pass());
187}
188
189DomainReliabilityMonitor::RequestInfo::RequestInfo() {}
190
191DomainReliabilityMonitor::RequestInfo::RequestInfo(
192    const net::URLRequest& request)
193    : url(request.url()),
194      status(request.status()),
195      response_info(request.response_info()),
196      load_flags(request.load_flags()),
197      is_upload(DomainReliabilityUploader::URLRequestIsUpload(request)) {
198  request.GetLoadTimingInfo(&load_timing_info);
199}
200
201DomainReliabilityMonitor::RequestInfo::~RequestInfo() {}
202
203bool DomainReliabilityMonitor::RequestInfo::AccessedNetwork() const {
204  return status.status() != net::URLRequestStatus::CANCELED &&
205     response_info.network_accessed;
206}
207
208DomainReliabilityContext* DomainReliabilityMonitor::AddContext(
209    scoped_ptr<const DomainReliabilityConfig> config) {
210  DCHECK(OnNetworkThread());
211  DCHECK(config);
212  DCHECK(config->IsValid());
213
214  // Grab a copy of the domain before transferring ownership of |config|.
215  std::string domain = config->domain;
216
217  DomainReliabilityContext* context =
218      new DomainReliabilityContext(time_.get(),
219                                   scheduler_params_,
220                                   upload_reporter_string_,
221                                   &dispatcher_,
222                                   uploader_.get(),
223                                   config.Pass());
224
225  std::pair<ContextMap::iterator, bool> map_it =
226      contexts_.insert(make_pair(domain, context));
227  // Make sure the domain wasn't already in the map.
228  DCHECK(map_it.second);
229
230  return map_it.first->second;
231}
232
233void DomainReliabilityMonitor::ClearContexts() {
234  STLDeleteContainerPairSecondPointers(
235      contexts_.begin(), contexts_.end());
236  contexts_.clear();
237}
238
239void DomainReliabilityMonitor::OnRequestLegComplete(
240    const RequestInfo& request) {
241  // Check these again because unit tests call this directly.
242  DCHECK(OnNetworkThread());
243  DCHECK(discard_uploads_set_);
244
245  int response_code;
246  if (request.response_info.headers.get())
247    response_code = request.response_info.headers->response_code();
248  else
249    response_code = -1;
250  std::string beacon_status;
251
252  int error_code = net::OK;
253  if (request.status.status() == net::URLRequestStatus::FAILED)
254    error_code = request.status.error();
255
256  DomainReliabilityContext* context = GetContextForHost(request.url.host());
257
258  // Ignore requests where:
259  // 1. There is no context for the request host.
260  // 2. The request did not access the network.
261  // 3. The request is not supposed to send cookies (to avoid associating the
262  //    request with any potentially unique data in the config).
263  // 4. The request was itself a Domain Reliability upload (to avoid loops).
264  // 5. There is no defined beacon status for the error or HTTP response code
265  //    (to avoid leaking network-local errors).
266  if (!context ||
267      !request.AccessedNetwork() ||
268      (request.load_flags & net::LOAD_DO_NOT_SEND_COOKIES) ||
269      request.is_upload ||
270      !GetDomainReliabilityBeaconStatus(
271          error_code, response_code, &beacon_status)) {
272    return;
273  }
274
275  DomainReliabilityBeacon beacon;
276  beacon.status = beacon_status;
277  beacon.chrome_error = error_code;
278  if (!request.response_info.was_fetched_via_proxy)
279    beacon.server_ip = request.response_info.socket_address.host();
280  else
281    beacon.server_ip.clear();
282  beacon.protocol = GetDomainReliabilityProtocol(
283      request.response_info.connection_info,
284      request.response_info.ssl_info.is_valid());
285  beacon.http_response_code = response_code;
286  beacon.start_time = request.load_timing_info.request_start;
287  beacon.elapsed = time_->NowTicks() - beacon.start_time;
288  beacon.domain = request.url.host();
289  context->OnBeacon(request.url, beacon);
290}
291
292// TODO(ttuttle): Keep a separate wildcard_contexts_ map to avoid having to
293// prepend '*.' to domains.
294DomainReliabilityContext* DomainReliabilityMonitor::GetContextForHost(
295    const std::string& host) const {
296  DCHECK(OnNetworkThread());
297
298  ContextMap::const_iterator context_it;
299
300  context_it = contexts_.find(host);
301  if (context_it != contexts_.end())
302    return context_it->second;
303
304  std::string host_with_asterisk = "*." + host;
305  context_it = contexts_.find(host_with_asterisk);
306  if (context_it != contexts_.end())
307    return context_it->second;
308
309  size_t dot_pos = host.find('.');
310  if (dot_pos == std::string::npos)
311    return NULL;
312
313  // TODO(ttuttle): Make sure parent is not in PSL before using.
314
315  std::string parent_with_asterisk = "*." + host.substr(dot_pos + 1);
316  context_it = contexts_.find(parent_with_asterisk);
317  if (context_it != contexts_.end())
318    return context_it->second;
319
320  return NULL;
321}
322
323base::WeakPtr<DomainReliabilityMonitor>
324DomainReliabilityMonitor::MakeWeakPtr() {
325  return weak_factory_.GetWeakPtr();
326}
327
328}  // namespace domain_reliability
329