blob: 81811c940c8edce21988bb2801e79904dbf33b29 [file] [log] [blame]
// Copyright 2015 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/context_manager.h"
#include <utility>
#include "base/metrics/histogram_macros.h"
namespace domain_reliability {
DomainReliabilityContextManager::DomainReliabilityContextManager(
DomainReliabilityContext::Factory* context_factory)
: context_factory_(context_factory) {
}
DomainReliabilityContextManager::~DomainReliabilityContextManager() {
RemoveContexts(
base::Callback<bool(const GURL&)>() /* no filter - delete everything */);
}
void DomainReliabilityContextManager::RouteBeacon(
std::unique_ptr<DomainReliabilityBeacon> beacon) {
DomainReliabilityContext* context = GetContextForHost(beacon->url.host());
if (!context)
return;
bool queued = context->OnBeacon(std::move(beacon));
if (!queued)
return;
base::TimeTicks now = base::TimeTicks::Now();
if (!last_routed_beacon_time_.is_null()) {
UMA_HISTOGRAM_LONG_TIMES("DomainReliability.BeaconIntervalGlobal",
now - last_routed_beacon_time_);
}
last_routed_beacon_time_ = now;
}
void DomainReliabilityContextManager::SetConfig(
const GURL& origin,
std::unique_ptr<DomainReliabilityConfig> config,
base::TimeDelta max_age) {
std::string key = origin.host();
if (!contexts_.count(key) && !removed_contexts_.count(key)) {
LOG(WARNING) << "Ignoring NEL header for unknown origin " << origin.spec()
<< ".";
return;
}
if (contexts_.count(key)) {
// Currently, there is no easy way to change the config of a context, so
// updating the config requires recreating the context, which loses
// pending beacons and collector backoff state. Therefore, don't do so
// needlessly; make sure the config has actually changed before recreating
// the context.
bool config_same = contexts_[key]->config().Equals(*config);
UMA_HISTOGRAM_BOOLEAN("DomainReliability.SetConfigRecreatedContext",
!config_same);
if (!config_same) {
DVLOG(1) << "Ignoring unchanged NEL header for existing origin "
<< origin.spec() << ".";
return;
}
// TODO(juliatuttle): Make Context accept Config changes.
}
DVLOG(1) << "Adding/replacing context for existing origin " << origin.spec()
<< ".";
removed_contexts_.erase(key);
config->origin = origin;
AddContextForConfig(std::move(config));
}
void DomainReliabilityContextManager::ClearConfig(const GURL& origin) {
std::string key = origin.host();
if (contexts_.count(key)) {
DVLOG(1) << "Removing context for existing origin " << origin.spec() << ".";
contexts_.erase(key);
removed_contexts_.insert(key);
}
}
void DomainReliabilityContextManager::ClearBeacons(
const base::Callback<bool(const GURL&)>& origin_filter) {
for (auto& context_entry : contexts_) {
if (origin_filter.is_null() ||
origin_filter.Run(context_entry.second->config().origin)) {
context_entry.second->ClearBeacons();
}
}
}
DomainReliabilityContext* DomainReliabilityContextManager::AddContextForConfig(
std::unique_ptr<const DomainReliabilityConfig> config) {
std::string key = config->origin.host();
// TODO(juliatuttle): Convert this to actual origin.
std::unique_ptr<DomainReliabilityContext> context =
context_factory_->CreateContextForConfig(std::move(config));
DomainReliabilityContext** entry = &contexts_[key];
if (*entry)
delete *entry;
*entry = context.release();
return *entry;
}
void DomainReliabilityContextManager::RemoveContexts(
const base::Callback<bool(const GURL&)>& origin_filter) {
for (ContextMap::iterator it = contexts_.begin(); it != contexts_.end(); ) {
if (!origin_filter.is_null() &&
!origin_filter.Run(it->second->config().origin)) {
++it;
continue;
}
delete it->second;
it = contexts_.erase(it);
}
}
std::unique_ptr<base::Value> DomainReliabilityContextManager::GetWebUIData()
const {
std::unique_ptr<base::ListValue> contexts_value(new base::ListValue());
for (const auto& context_entry : contexts_)
contexts_value->Append(context_entry.second->GetWebUIData());
return std::move(contexts_value);
}
DomainReliabilityContext* DomainReliabilityContextManager::GetContextForHost(
const std::string& host) {
ContextMap::const_iterator context_it;
context_it = contexts_.find(host);
if (context_it != contexts_.end())
return context_it->second;
size_t dot_pos = host.find('.');
if (dot_pos == std::string::npos)
return nullptr;
// TODO(juliatuttle): Make sure parent is not in PSL before using.
std::string parent_host = host.substr(dot_pos + 1);
context_it = contexts_.find(parent_host);
if (context_it != contexts_.end()
&& context_it->second->config().include_subdomains) {
return context_it->second;
}
return nullptr;
}
} // namespace domain_reliability