| Index: net/http/http_broken_alternative_services_manager.cc
|
| diff --git a/net/http/http_broken_alternative_services_manager.cc b/net/http/http_broken_alternative_services_manager.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..02ab78f8815040e7f4f6c9e2f2255a8a87b6c30d
|
| --- /dev/null
|
| +++ b/net/http/http_broken_alternative_services_manager.cc
|
| @@ -0,0 +1,203 @@
|
| +// Copyright (c) 2017 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 "net/http/http_broken_alternative_services_manager.h"
|
| +
|
| +#include "base/memory/singleton.h"
|
| +#include "base/time/time.h"
|
| +#include "net/http/http_server_properties_impl.h"
|
| +
|
| +namespace net {
|
| +
|
| +namespace {
|
| +
|
| +// Initial delay for broken alternative services.
|
| +const uint64_t kBrokenAlternativeProtocolDelaySecs = 300;
|
| +// Subsequent failures result in exponential (base 2) backoff.
|
| +// Limit binary shift to limit delay to approximately 2 days.
|
| +const int kBrokenDelayMaxShift = 9;
|
| +
|
| +base::TimeDelta ComputeBrokenAlternativeServiceExpirationDelay(
|
| + int broken_count) {
|
| + DCHECK(broken_count >= 0);
|
| + if (broken_count > kBrokenDelayMaxShift)
|
| + broken_count = kBrokenDelayMaxShift;
|
| + return base::TimeDelta::FromSeconds(kBrokenAlternativeProtocolDelaySecs) *
|
| + (1 << broken_count);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +HttpBrokenAlternativeServicesManager::DefaultClock*
|
| +HttpBrokenAlternativeServicesManager::DefaultClock::GetInstance() {
|
| + return base::Singleton<
|
| + HttpBrokenAlternativeServicesManager::DefaultClock>::get();
|
| +}
|
| +
|
| +base::TimeTicks HttpBrokenAlternativeServicesManager::DefaultClock::Now()
|
| + const {
|
| + return base::TimeTicks::Now();
|
| +}
|
| +
|
| +HttpBrokenAlternativeServicesManager::HttpBrokenAlternativeServicesManager(
|
| + Delegate* delegate,
|
| + Clock* clock)
|
| + : delegate_(delegate),
|
| + recently_broken_alternative_services_(
|
| + RecentlyBrokenAlternativeServices::NO_AUTO_EVICT),
|
| + clock_(clock),
|
| + weak_ptr_factory_(this) {}
|
| +
|
| +HttpBrokenAlternativeServicesManager::~HttpBrokenAlternativeServicesManager() {}
|
| +
|
| +base::TimeDelta
|
| +HttpBrokenAlternativeServicesManager::ComputeBrokenAltSvcExpirationDelayForTest(
|
| + int broken_count) {
|
| + return ComputeBrokenAlternativeServiceExpirationDelay(broken_count);
|
| +}
|
| +
|
| +void HttpBrokenAlternativeServicesManager::MarkAlternativeServiceBroken(
|
| + const AlternativeService& alternative_service) {
|
| + // Empty host means use host of origin, callers are supposed to substitute.
|
| + DCHECK(!alternative_service.host.empty());
|
| + if (alternative_service.protocol == kProtoUnknown) {
|
| + LOG(DFATAL) << "Trying to mark unknown alternate protocol broken.";
|
| + return;
|
| + }
|
| + auto it = recently_broken_alternative_services_.Get(alternative_service);
|
| + int broken_count = 0;
|
| + if (it == recently_broken_alternative_services_.end()) {
|
| + recently_broken_alternative_services_.Put(alternative_service, 1);
|
| + } else {
|
| + broken_count = it->second++;
|
| + }
|
| + base::TimeTicks expiration =
|
| + clock_->Now() +
|
| + ComputeBrokenAlternativeServiceExpirationDelay(broken_count);
|
| + // Return if alternative service is already in expiration queue.
|
| + BrokenAlternativeServiceList::iterator list_it;
|
| + if (!AddToBrokenAlternativeServiceListAndMap(alternative_service, expiration,
|
| + &list_it)) {
|
| + return;
|
| + }
|
| +
|
| + // If this is now the first entry in the list (i.e. |alternative_service| is
|
| + // the next alt svc to expire), schedule an expiration task for it.
|
| + if (list_it == broken_alternative_service_list_.begin()) {
|
| + ScheduleBrokenAlternateProtocolMappingsExpiration();
|
| + }
|
| +}
|
| +
|
| +void HttpBrokenAlternativeServicesManager::MarkAlternativeServiceRecentlyBroken(
|
| + const AlternativeService& alternative_service) {
|
| + if (recently_broken_alternative_services_.Get(alternative_service) ==
|
| + recently_broken_alternative_services_.end()) {
|
| + recently_broken_alternative_services_.Put(alternative_service, 1);
|
| + }
|
| +}
|
| +
|
| +bool HttpBrokenAlternativeServicesManager::IsAlternativeServiceBroken(
|
| + const AlternativeService& alternative_service) const {
|
| + // Empty host means use host of origin, callers are supposed to substitute.
|
| + DCHECK(!alternative_service.host.empty());
|
| + return broken_alternative_service_map_.find(alternative_service) !=
|
| + broken_alternative_service_map_.end();
|
| +}
|
| +
|
| +bool HttpBrokenAlternativeServicesManager::WasAlternativeServiceRecentlyBroken(
|
| + const AlternativeService& alternative_service) {
|
| + if (alternative_service.protocol == kProtoUnknown)
|
| + return false;
|
| +
|
| + return recently_broken_alternative_services_.Get(alternative_service) !=
|
| + recently_broken_alternative_services_.end();
|
| +}
|
| +
|
| +void HttpBrokenAlternativeServicesManager::ConfirmAlternativeService(
|
| + const AlternativeService& alternative_service) {
|
| + if (alternative_service.protocol == kProtoUnknown)
|
| + return;
|
| +
|
| + // Remove |alternative_service| from |alternative_service_list_| and
|
| + // |alternative_service_map_|.
|
| + auto map_it = broken_alternative_service_map_.find(alternative_service);
|
| + if (map_it != broken_alternative_service_map_.end()) {
|
| + broken_alternative_service_list_.erase(map_it->second);
|
| + broken_alternative_service_map_.erase(map_it);
|
| + }
|
| +
|
| + auto it = recently_broken_alternative_services_.Get(alternative_service);
|
| + if (it != recently_broken_alternative_services_.end()) {
|
| + recently_broken_alternative_services_.Erase(it);
|
| + }
|
| +}
|
| +
|
| +bool HttpBrokenAlternativeServicesManager ::
|
| + AddToBrokenAlternativeServiceListAndMap(
|
| + const AlternativeService& alternative_service,
|
| + base::TimeTicks expiration,
|
| + BrokenAlternativeServiceList::iterator* it) {
|
| + DCHECK(it);
|
| +
|
| + auto map_it = broken_alternative_service_map_.find(alternative_service);
|
| + if (map_it != broken_alternative_service_map_.end())
|
| + return false;
|
| +
|
| + // Iterate from end of |broken_alternative_service_list_| to find where to
|
| + // insert it to keep the list sorted by expiration time.
|
| + auto list_it = broken_alternative_service_list_.end();
|
| + while (list_it != broken_alternative_service_list_.begin()) {
|
| + --list_it;
|
| + if (list_it->expiration <= expiration) {
|
| + ++list_it;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // Insert |alternative_service| into the list and the map
|
| + list_it = broken_alternative_service_list_.insert(
|
| + list_it, BrokenAltSvcExpireInfo(alternative_service, expiration));
|
| + broken_alternative_service_map_.insert(
|
| + std::make_pair(alternative_service, list_it));
|
| +
|
| + *it = list_it;
|
| + return true;
|
| +}
|
| +
|
| +void HttpBrokenAlternativeServicesManager ::
|
| + ExpireBrokenAlternateProtocolMappings() {
|
| + base::TimeTicks now = clock_->Now();
|
| + ;
|
| + while (!broken_alternative_service_list_.empty()) {
|
| + auto it = broken_alternative_service_list_.begin();
|
| + if (now < it->expiration) {
|
| + break;
|
| + }
|
| +
|
| + const AlternativeService expired_alternative_service =
|
| + it->alternative_service;
|
| + broken_alternative_service_map_.erase(expired_alternative_service);
|
| + broken_alternative_service_list_.erase(it);
|
| +
|
| + delegate_->OnExpireBrokenAlternativeService(expired_alternative_service);
|
| + }
|
| +
|
| + if (!broken_alternative_service_list_.empty())
|
| + ScheduleBrokenAlternateProtocolMappingsExpiration();
|
| +}
|
| +
|
| +void HttpBrokenAlternativeServicesManager ::
|
| + ScheduleBrokenAlternateProtocolMappingsExpiration() {
|
| + DCHECK(!broken_alternative_service_list_.empty());
|
| + base::TimeTicks now = clock_->Now();
|
| + base::TimeTicks when = broken_alternative_service_list_.front().expiration;
|
| + base::TimeDelta delay = when > now ? when - now : base::TimeDelta();
|
| + timer_.Stop();
|
| + timer_.Start(FROM_HERE, delay,
|
| + base::Bind(&HttpBrokenAlternativeServicesManager ::
|
| + ExpireBrokenAlternateProtocolMappings,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +} // namespace net
|
|
|