Home | History | Annotate | Download | only in domain_reliability
      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 
     19 namespace domain_reliability {
     20 
     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     : 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 
     38 DomainReliabilityMonitor::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 
     56 DomainReliabilityMonitor::~DomainReliabilityMonitor() {
     57   if (moved_to_network_thread_)
     58     DCHECK(OnNetworkThread());
     59   else
     60     DCHECK(OnPrefThread());
     61 
     62   ClearContexts();
     63 }
     64 
     65 void DomainReliabilityMonitor::MoveToNetworkThread() {
     66   DCHECK(OnPrefThread());
     67   DCHECK(!moved_to_network_thread_);
     68 
     69   moved_to_network_thread_ = true;
     70 }
     71 
     72 void 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 
     83 void 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 
     96 void 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 
    114 void 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 
    123 void 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 
    131 void 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 
    147 void 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 
    166 scoped_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 
    182 DomainReliabilityContext* DomainReliabilityMonitor::AddContextForTesting(
    183     scoped_ptr<const DomainReliabilityConfig> config) {
    184   DCHECK(OnNetworkThread());
    185 
    186   return AddContext(config.Pass());
    187 }
    188 
    189 DomainReliabilityMonitor::RequestInfo::RequestInfo() {}
    190 
    191 DomainReliabilityMonitor::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 
    201 DomainReliabilityMonitor::RequestInfo::~RequestInfo() {}
    202 
    203 bool DomainReliabilityMonitor::RequestInfo::AccessedNetwork() const {
    204   return status.status() != net::URLRequestStatus::CANCELED &&
    205      response_info.network_accessed;
    206 }
    207 
    208 DomainReliabilityContext* 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 
    233 void DomainReliabilityMonitor::ClearContexts() {
    234   STLDeleteContainerPairSecondPointers(
    235       contexts_.begin(), contexts_.end());
    236   contexts_.clear();
    237 }
    238 
    239 void 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.
    294 DomainReliabilityContext* 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 
    323 base::WeakPtr<DomainReliabilityMonitor>
    324 DomainReliabilityMonitor::MakeWeakPtr() {
    325   return weak_factory_.GetWeakPtr();
    326 }
    327 
    328 }  // namespace domain_reliability
    329