blob: fc25dd77c914a0df96a58534ec03328cd3fbbae0 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/domain_reliability/monitor.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/task_runner.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "components/domain_reliability/baked_in_configs.h"
#include "net/base/load_flags.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
namespace domain_reliability {
DomainReliabilityMonitor::DomainReliabilityMonitor(
const std::string& upload_reporter_string)
: time_(new ActualTime()),
upload_reporter_string_(upload_reporter_string),
scheduler_params_(
DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()),
dispatcher_(time_.get()),
was_cleared_(false),
cleared_mode_(MAX_CLEAR_MODE),
weak_factory_(this) {}
DomainReliabilityMonitor::DomainReliabilityMonitor(
const std::string& upload_reporter_string,
scoped_ptr<MockableTime> time)
: time_(time.Pass()),
upload_reporter_string_(upload_reporter_string),
scheduler_params_(
DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()),
dispatcher_(time_.get()),
was_cleared_(false),
cleared_mode_(MAX_CLEAR_MODE),
weak_factory_(this) {}
DomainReliabilityMonitor::~DomainReliabilityMonitor() {
ClearContexts();
}
void DomainReliabilityMonitor::Init(
net::URLRequestContext* url_request_context,
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
DCHECK(!thread_checker_);
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
new net::TrivialURLRequestContextGetter(url_request_context,
task_runner);
Init(url_request_context_getter);
}
void DomainReliabilityMonitor::Init(
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter) {
DCHECK(!thread_checker_);
DCHECK(url_request_context_getter->GetNetworkTaskRunner()->
RunsTasksOnCurrentThread());
uploader_ = DomainReliabilityUploader::Create(url_request_context_getter);
thread_checker_.reset(new base::ThreadChecker());
}
void DomainReliabilityMonitor::AddBakedInConfigs() {
DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
base::Time now = base::Time::Now();
for (size_t i = 0; kBakedInJsonConfigs[i]; ++i) {
std::string json(kBakedInJsonConfigs[i]);
scoped_ptr<const DomainReliabilityConfig> config =
DomainReliabilityConfig::FromJSON(json);
if (config && config->IsExpired(now)) {
LOG(WARNING) << "Baked-in Domain Reliability config for "
<< config->domain << " is expired.";
continue;
}
AddContext(config.Pass());
}
}
void DomainReliabilityMonitor::OnBeforeRedirect(net::URLRequest* request) {
DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
// Record the redirect itself in addition to the final request.
OnRequestLegComplete(RequestInfo(*request));
}
void DomainReliabilityMonitor::OnCompleted(net::URLRequest* request,
bool started) {
DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
if (!started)
return;
RequestInfo request_info(*request);
if (request_info.AccessedNetwork()) {
OnRequestLegComplete(request_info);
// A request was just using the network, so now is a good time to run any
// pending and eligible uploads.
dispatcher_.RunEligibleTasks();
}
}
void DomainReliabilityMonitor::ClearBrowsingData(
DomainReliabilityClearMode mode) {
DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
was_cleared_ = true;
cleared_mode_ = mode;
switch (mode) {
case CLEAR_BEACONS: {
ContextMap::const_iterator it;
for (it = contexts_.begin(); it != contexts_.end(); ++it)
it->second->ClearBeacons();
break;
};
case CLEAR_CONTEXTS:
ClearContexts();
break;
case MAX_CLEAR_MODE:
NOTREACHED();
}
}
DomainReliabilityContext* DomainReliabilityMonitor::AddContextForTesting(
scoped_ptr<const DomainReliabilityConfig> config) {
DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
return AddContext(config.Pass());
}
DomainReliabilityMonitor::RequestInfo::RequestInfo() {}
DomainReliabilityMonitor::RequestInfo::RequestInfo(
const net::URLRequest& request)
: url(request.url()),
status(request.status()),
response_info(request.response_info()),
load_flags(request.load_flags()),
is_upload(DomainReliabilityUploader::URLRequestIsUpload(request)) {
request.GetLoadTimingInfo(&load_timing_info);
}
DomainReliabilityMonitor::RequestInfo::~RequestInfo() {}
bool DomainReliabilityMonitor::RequestInfo::AccessedNetwork() const {
return status.status() != net::URLRequestStatus::CANCELED &&
response_info.network_accessed;
}
DomainReliabilityContext* DomainReliabilityMonitor::AddContext(
scoped_ptr<const DomainReliabilityConfig> config) {
DCHECK(config);
DCHECK(config->IsValid());
// Grab a copy of the domain before transferring ownership of |config|.
std::string domain = config->domain;
DomainReliabilityContext* context =
new DomainReliabilityContext(time_.get(),
scheduler_params_,
upload_reporter_string_,
&dispatcher_,
uploader_.get(),
config.Pass());
std::pair<ContextMap::iterator, bool> map_it =
contexts_.insert(make_pair(domain, context));
// Make sure the domain wasn't already in the map.
DCHECK(map_it.second);
return map_it.first->second;
}
void DomainReliabilityMonitor::ClearContexts() {
STLDeleteContainerPairSecondPointers(
contexts_.begin(), contexts_.end());
contexts_.clear();
}
void DomainReliabilityMonitor::OnRequestLegComplete(
const RequestInfo& request) {
int response_code;
if (request.response_info.headers)
response_code = request.response_info.headers->response_code();
else
response_code = -1;
ContextMap::iterator context_it;
std::string beacon_status;
int error_code = net::OK;
if (request.status.status() == net::URLRequestStatus::FAILED)
error_code = request.status.error();
// Ignore requests where:
// 1. There is no context for the request host.
// 2. The request did not access the network.
// 3. The request is not supposed to send cookies (to avoid associating the
// request with any potentially unique data in the config).
// 4. The request was itself a Domain Reliability upload (to avoid loops).
// 5. There is no defined beacon status for the error or HTTP response code
// (to avoid leaking network-local errors).
if ((context_it = contexts_.find(request.url.host())) == contexts_.end() ||
!request.AccessedNetwork() ||
(request.load_flags & net::LOAD_DO_NOT_SEND_COOKIES) ||
request.is_upload ||
!GetDomainReliabilityBeaconStatus(
error_code, response_code, &beacon_status)) {
return;
}
DomainReliabilityBeacon beacon;
beacon.status = beacon_status;
beacon.chrome_error = error_code;
if (!request.response_info.was_fetched_via_proxy)
beacon.server_ip = request.response_info.socket_address.host();
else
beacon.server_ip.clear();
beacon.http_response_code = response_code;
beacon.start_time = request.load_timing_info.request_start;
beacon.elapsed = time_->NowTicks() - beacon.start_time;
context_it->second->OnBeacon(request.url, beacon);
}
base::WeakPtr<DomainReliabilityMonitor>
DomainReliabilityMonitor::MakeWeakPtr() {
return weak_factory_.GetWeakPtr();
}
} // namespace domain_reliability