普通文本  |  454行  |  15.05 KB

// Copyright (c) 2012 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_server_properties_impl.h"

#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"

namespace net {

namespace {

const uint64 kBrokenAlternateProtocolDelaySecs = 300;

}  // namespace

HttpServerPropertiesImpl::HttpServerPropertiesImpl()
    : spdy_servers_map_(SpdyServerHostPortMap::NO_AUTO_EVICT),
      alternate_protocol_map_(AlternateProtocolMap::NO_AUTO_EVICT),
      alternate_protocol_experiment_(
          ALTERNATE_PROTOCOL_NOT_PART_OF_EXPERIMENT),
      spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT),
      weak_ptr_factory_(this) {
  canoncial_suffixes_.push_back(".c.youtube.com");
  canoncial_suffixes_.push_back(".googlevideo.com");
  canoncial_suffixes_.push_back(".googleusercontent.com");
}

HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
}

void HttpServerPropertiesImpl::InitializeSpdyServers(
    std::vector<std::string>* spdy_servers,
    bool support_spdy) {
  DCHECK(CalledOnValidThread());
  if (!spdy_servers)
    return;
  // Add the entries from persisted data.
  for (std::vector<std::string>::reverse_iterator it = spdy_servers->rbegin();
       it != spdy_servers->rend(); ++it) {
    spdy_servers_map_.Put(*it, support_spdy);
  }
}

void HttpServerPropertiesImpl::InitializeAlternateProtocolServers(
    AlternateProtocolMap* alternate_protocol_map) {
  // Keep all the ALTERNATE_PROTOCOL_BROKEN ones since those don't
  // get persisted.
  for (AlternateProtocolMap::iterator it = alternate_protocol_map_.begin();
       it != alternate_protocol_map_.end();) {
    AlternateProtocolMap::iterator old_it = it;
    ++it;
    if (old_it->second.protocol != ALTERNATE_PROTOCOL_BROKEN) {
      alternate_protocol_map_.Erase(old_it);
    }
  }

  // Add the entries from persisted data.
  for (AlternateProtocolMap::reverse_iterator it =
           alternate_protocol_map->rbegin();
       it != alternate_protocol_map->rend(); ++it) {
    alternate_protocol_map_.Put(it->first, it->second);
  }

  // Attempt to find canonical servers.
  int canonical_ports[] = { 80, 443 };
  for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) {
    std::string canonical_suffix = canoncial_suffixes_[i];
    for (size_t j = 0; j < arraysize(canonical_ports); ++j) {
      HostPortPair canonical_host(canonical_suffix, canonical_ports[j]);
      // If we already have a valid canonical server, we're done.
      if (ContainsKey(canonical_host_to_origin_map_, canonical_host) &&
          (alternate_protocol_map_.Peek(canonical_host_to_origin_map_[
               canonical_host]) != alternate_protocol_map_.end())) {
        continue;
      }
      // Now attempt to find a server which matches this origin and set it as
      // canonical .
      for (AlternateProtocolMap::const_iterator it =
               alternate_protocol_map_.begin();
           it != alternate_protocol_map_.end(); ++it) {
        if (EndsWith(it->first.host(), canoncial_suffixes_[i], false)) {
          canonical_host_to_origin_map_[canonical_host] = it->first;
          break;
        }
      }
    }
  }
}

void HttpServerPropertiesImpl::InitializeSpdySettingsServers(
    SpdySettingsMap* spdy_settings_map) {
  for (SpdySettingsMap::reverse_iterator it = spdy_settings_map->rbegin();
       it != spdy_settings_map->rend(); ++it) {
    spdy_settings_map_.Put(it->first, it->second);
  }
}

void HttpServerPropertiesImpl::GetSpdyServerList(
    base::ListValue* spdy_server_list,
    size_t max_size) const {
  DCHECK(CalledOnValidThread());
  DCHECK(spdy_server_list);
  spdy_server_list->Clear();
  size_t count = 0;
  // Get the list of servers (host/port) that support SPDY.
  for (SpdyServerHostPortMap::const_iterator it = spdy_servers_map_.begin();
       it != spdy_servers_map_.end() && count < max_size; ++it) {
    const std::string spdy_server_host_port = it->first;
    if (it->second) {
      spdy_server_list->Append(new base::StringValue(spdy_server_host_port));
      ++count;
    }
  }
}

// static
std::string HttpServerPropertiesImpl::GetFlattenedSpdyServer(
    const net::HostPortPair& host_port_pair) {
  std::string spdy_server;
  spdy_server.append(host_port_pair.host());
  spdy_server.append(":");
  base::StringAppendF(&spdy_server, "%d", host_port_pair.port());
  return spdy_server;
}

static const PortAlternateProtocolPair* g_forced_alternate_protocol = NULL;

// static
void HttpServerPropertiesImpl::ForceAlternateProtocol(
    const PortAlternateProtocolPair& pair) {
  // Note: we're going to leak this.
  if (g_forced_alternate_protocol)
    delete g_forced_alternate_protocol;
  g_forced_alternate_protocol = new PortAlternateProtocolPair(pair);
}

// static
void HttpServerPropertiesImpl::DisableForcedAlternateProtocol() {
  delete g_forced_alternate_protocol;
  g_forced_alternate_protocol = NULL;
}

base::WeakPtr<HttpServerProperties> HttpServerPropertiesImpl::GetWeakPtr() {
  return weak_ptr_factory_.GetWeakPtr();
}

void HttpServerPropertiesImpl::Clear() {
  DCHECK(CalledOnValidThread());
  spdy_servers_map_.Clear();
  alternate_protocol_map_.Clear();
  spdy_settings_map_.Clear();
}

bool HttpServerPropertiesImpl::SupportsSpdy(
    const net::HostPortPair& host_port_pair) {
  DCHECK(CalledOnValidThread());
  if (host_port_pair.host().empty())
    return false;
  std::string spdy_server = GetFlattenedSpdyServer(host_port_pair);

  SpdyServerHostPortMap::iterator spdy_host_port =
      spdy_servers_map_.Get(spdy_server);
  if (spdy_host_port != spdy_servers_map_.end())
    return spdy_host_port->second;
  return false;
}

void HttpServerPropertiesImpl::SetSupportsSpdy(
    const net::HostPortPair& host_port_pair,
    bool support_spdy) {
  DCHECK(CalledOnValidThread());
  if (host_port_pair.host().empty())
    return;
  std::string spdy_server = GetFlattenedSpdyServer(host_port_pair);

  SpdyServerHostPortMap::iterator spdy_host_port =
      spdy_servers_map_.Get(spdy_server);
  if ((spdy_host_port != spdy_servers_map_.end()) &&
      (spdy_host_port->second == support_spdy)) {
    return;
  }
  // Cache the data.
  spdy_servers_map_.Put(spdy_server, support_spdy);
}

bool HttpServerPropertiesImpl::HasAlternateProtocol(
    const HostPortPair& server) {
  if (alternate_protocol_map_.Get(server) != alternate_protocol_map_.end() ||
      g_forced_alternate_protocol)
    return true;

  return GetCanonicalHost(server) != canonical_host_to_origin_map_.end();
}

std::string HttpServerPropertiesImpl::GetCanonicalSuffix(
    const HostPortPair& server) {
  // If this host ends with a canonical suffix, then return the canonical
  // suffix.
  for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) {
    std::string canonical_suffix = canoncial_suffixes_[i];
    if (EndsWith(server.host(), canoncial_suffixes_[i], false)) {
      return canonical_suffix;
    }
  }
  return std::string();
}

PortAlternateProtocolPair
HttpServerPropertiesImpl::GetAlternateProtocol(
    const HostPortPair& server) {
  DCHECK(HasAlternateProtocol(server));

  // First check the map.
  AlternateProtocolMap::iterator it = alternate_protocol_map_.Get(server);
  if (it != alternate_protocol_map_.end())
    return it->second;

  // Next check the canonical host.
  CanonicalHostMap::const_iterator canonical_host = GetCanonicalHost(server);
  if (canonical_host != canonical_host_to_origin_map_.end())
    return alternate_protocol_map_.Get(canonical_host->second)->second;

  // We must be forcing an alternate.
  DCHECK(g_forced_alternate_protocol);
  return *g_forced_alternate_protocol;
}

void HttpServerPropertiesImpl::SetAlternateProtocol(
    const HostPortPair& server,
    uint16 alternate_port,
    AlternateProtocol alternate_protocol) {
  if (alternate_protocol == ALTERNATE_PROTOCOL_BROKEN) {
    LOG(DFATAL) << "Call SetBrokenAlternateProtocol() instead.";
    return;
  }

  PortAlternateProtocolPair alternate;
  alternate.port = alternate_port;
  alternate.protocol = alternate_protocol;
  if (HasAlternateProtocol(server)) {
    const PortAlternateProtocolPair existing_alternate =
        GetAlternateProtocol(server);

    if (existing_alternate.protocol == ALTERNATE_PROTOCOL_BROKEN) {
      DVLOG(1) << "Ignore alternate protocol since it's known to be broken.";
      return;
    }

    if (alternate_protocol != ALTERNATE_PROTOCOL_BROKEN &&
        !existing_alternate.Equals(alternate)) {
      LOG(WARNING) << "Changing the alternate protocol for: "
                   << server.ToString()
                   << " from [Port: " << existing_alternate.port
                   << ", Protocol: " << existing_alternate.protocol
                   << "] to [Port: " << alternate_port
                   << ", Protocol: " << alternate_protocol
                   << "].";
    }
  } else {
    // TODO(rch): Consider the case where multiple requests are started
    // before the first completes. In this case, only one of the jobs
    // would reach this code, whereas all of them should should have.
    HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING,
                                    alternate_protocol_experiment_);
  }

  alternate_protocol_map_.Put(server, alternate);

  // If this host ends with a canonical suffix, then set it as the
  // canonical host.
  for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) {
    std::string canonical_suffix = canoncial_suffixes_[i];
    if (EndsWith(server.host(), canoncial_suffixes_[i], false)) {
      HostPortPair canonical_host(canonical_suffix, server.port());
      canonical_host_to_origin_map_[canonical_host] = server;
      break;
    }
  }
}

void HttpServerPropertiesImpl::SetBrokenAlternateProtocol(
    const HostPortPair& server) {
  AlternateProtocolMap::iterator it = alternate_protocol_map_.Get(server);
  if (it != alternate_protocol_map_.end()) {
    it->second.protocol = ALTERNATE_PROTOCOL_BROKEN;
  } else {
    PortAlternateProtocolPair alternate;
    alternate.protocol = ALTERNATE_PROTOCOL_BROKEN;
    alternate_protocol_map_.Put(server, alternate);
  }
  int count = ++broken_alternate_protocol_map_[server];
  base::TimeDelta delay =
      base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs);
  BrokenAlternateProtocolEntry entry;
  entry.server = server;
  entry.when = base::TimeTicks::Now() + delay * (1 << (count - 1));
  broken_alternate_protocol_list_.push_back(entry);
  // If this is the only entry in the list, schedule an expiration task.
  // Otherwse it will be rescheduled automatically when the pending
  // task runs.
  if (broken_alternate_protocol_list_.size() == 1) {
    ScheduleBrokenAlternateProtocolMappingsExpiration();
  }
}

bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken(
    const HostPortPair& server) {
  return ContainsKey(broken_alternate_protocol_map_, server);
}

void HttpServerPropertiesImpl::ConfirmAlternateProtocol(
    const HostPortPair& server) {
  broken_alternate_protocol_map_.erase(server);
}

void HttpServerPropertiesImpl::ClearAlternateProtocol(
    const HostPortPair& server) {
  AlternateProtocolMap::iterator it = alternate_protocol_map_.Peek(server);
  if (it != alternate_protocol_map_.end())
    alternate_protocol_map_.Erase(it);
}

const AlternateProtocolMap&
HttpServerPropertiesImpl::alternate_protocol_map() const {
  return alternate_protocol_map_;
}

void HttpServerPropertiesImpl::SetAlternateProtocolExperiment(
    AlternateProtocolExperiment experiment) {
  alternate_protocol_experiment_ = experiment;
}

AlternateProtocolExperiment
HttpServerPropertiesImpl::GetAlternateProtocolExperiment() const {
  return alternate_protocol_experiment_;
}

const SettingsMap& HttpServerPropertiesImpl::GetSpdySettings(
    const HostPortPair& host_port_pair) {
  SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
  if (it == spdy_settings_map_.end()) {
    CR_DEFINE_STATIC_LOCAL(SettingsMap, kEmptySettingsMap, ());
    return kEmptySettingsMap;
  }
  return it->second;
}

bool HttpServerPropertiesImpl::SetSpdySetting(
    const HostPortPair& host_port_pair,
    SpdySettingsIds id,
    SpdySettingsFlags flags,
    uint32 value) {
  if (!(flags & SETTINGS_FLAG_PLEASE_PERSIST))
      return false;

  SettingsFlagsAndValue flags_and_value(SETTINGS_FLAG_PERSISTED, value);
  SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
  if (it == spdy_settings_map_.end()) {
    SettingsMap settings_map;
    settings_map[id] = flags_and_value;
    spdy_settings_map_.Put(host_port_pair, settings_map);
  } else {
    SettingsMap& settings_map = it->second;
    settings_map[id] = flags_and_value;
  }
  return true;
}

void HttpServerPropertiesImpl::ClearSpdySettings(
    const HostPortPair& host_port_pair) {
  SpdySettingsMap::iterator it = spdy_settings_map_.Peek(host_port_pair);
  if (it != spdy_settings_map_.end())
    spdy_settings_map_.Erase(it);
}

void HttpServerPropertiesImpl::ClearAllSpdySettings() {
  spdy_settings_map_.Clear();
}

const SpdySettingsMap&
HttpServerPropertiesImpl::spdy_settings_map() const {
  return spdy_settings_map_;
}

void HttpServerPropertiesImpl::SetServerNetworkStats(
    const HostPortPair& host_port_pair,
    NetworkStats stats) {
  server_network_stats_map_[host_port_pair] = stats;
}

const HttpServerProperties::NetworkStats*
HttpServerPropertiesImpl::GetServerNetworkStats(
    const HostPortPair& host_port_pair) const {
  ServerNetworkStatsMap::const_iterator it =
      server_network_stats_map_.find(host_port_pair);
  if (it == server_network_stats_map_.end()) {
    return NULL;
  }
  return &it->second;
}

HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server) const {
  for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) {
    std::string canonical_suffix = canoncial_suffixes_[i];
    if (EndsWith(server.host(), canoncial_suffixes_[i], false)) {
      HostPortPair canonical_host(canonical_suffix, server.port());
      return canonical_host_to_origin_map_.find(canonical_host);
    }
  }

  return canonical_host_to_origin_map_.end();
}

void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() {
  base::TimeTicks now = base::TimeTicks::Now();
  while (!broken_alternate_protocol_list_.empty()) {
    BrokenAlternateProtocolEntry entry =
        broken_alternate_protocol_list_.front();
    if (now < entry.when) {
      break;
    }

    ClearAlternateProtocol(entry.server);
    broken_alternate_protocol_list_.pop_front();
  }
  ScheduleBrokenAlternateProtocolMappingsExpiration();
}

void
HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
  if (broken_alternate_protocol_list_.empty()) {
    return;
  }
  base::TimeTicks now = base::TimeTicks::Now();
  base::TimeTicks when = broken_alternate_protocol_list_.front().when;
  base::TimeDelta delay = when > now ? when - now : base::TimeDelta();
  base::MessageLoop::current()->PostDelayedTask(
      FROM_HERE,
      base::Bind(
          &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings,
          weak_ptr_factory_.GetWeakPtr()),
      delay);
}

}  // namespace net