// 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/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "net/http/http_pipelined_host_capability.h"
namespace net {
// TODO(simonjam): Run experiments with different values of this to see what
// value is good at avoiding evictions without eating too much memory. Until
// then, this is just a bad guess.
static const int kDefaultNumHostsToRemember = 200;
HttpServerPropertiesImpl::HttpServerPropertiesImpl()
: pipeline_capability_map_(
new CachedPipelineCapabilityMap(kDefaultNumHostsToRemember)),
weak_ptr_factory_(this) {
}
HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
}
void HttpServerPropertiesImpl::InitializeSpdyServers(
std::vector<std::string>* spdy_servers,
bool support_spdy) {
DCHECK(CalledOnValidThread());
spdy_servers_table_.clear();
if (!spdy_servers)
return;
for (std::vector<std::string>::iterator it = spdy_servers->begin();
it != spdy_servers->end(); ++it) {
spdy_servers_table_[*it] = support_spdy;
}
}
void HttpServerPropertiesImpl::InitializeAlternateProtocolServers(
AlternateProtocolMap* alternate_protocol_map) {
// First swap, and then add back all the ALTERNATE_PROTOCOL_BROKEN ones since
// those don't get persisted.
alternate_protocol_map_.swap(*alternate_protocol_map);
for (AlternateProtocolMap::const_iterator it =
alternate_protocol_map->begin();
it != alternate_protocol_map->end(); ++it) {
if (it->second.protocol == ALTERNATE_PROTOCOL_BROKEN)
alternate_protocol_map_[it->first] = it->second;
}
}
void HttpServerPropertiesImpl::InitializeSpdySettingsServers(
SpdySettingsMap* spdy_settings_map) {
spdy_settings_map_.swap(*spdy_settings_map);
}
void HttpServerPropertiesImpl::InitializePipelineCapabilities(
const PipelineCapabilityMap* pipeline_capability_map) {
PipelineCapabilityMap::const_iterator it;
pipeline_capability_map_->Clear();
for (it = pipeline_capability_map->begin();
it != pipeline_capability_map->end(); ++it) {
pipeline_capability_map_->Put(it->first, it->second);
}
}
void HttpServerPropertiesImpl::SetNumPipelinedHostsToRemember(int max_size) {
DCHECK(pipeline_capability_map_->empty());
pipeline_capability_map_.reset(new CachedPipelineCapabilityMap(max_size));
}
void HttpServerPropertiesImpl::GetSpdyServerList(
base::ListValue* spdy_server_list) const {
DCHECK(CalledOnValidThread());
DCHECK(spdy_server_list);
spdy_server_list->Clear();
// Get the list of servers (host/port) that support SPDY.
for (SpdyServerHostPortTable::const_iterator it = spdy_servers_table_.begin();
it != spdy_servers_table_.end(); ++it) {
const std::string spdy_server_host_port = it->first;
if (it->second)
spdy_server_list->Append(new base::StringValue(spdy_server_host_port));
}
}
// 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_table_.clear();
alternate_protocol_map_.clear();
spdy_settings_map_.clear();
pipeline_capability_map_->Clear();
}
bool HttpServerPropertiesImpl::SupportsSpdy(
const net::HostPortPair& host_port_pair) const {
DCHECK(CalledOnValidThread());
if (host_port_pair.host().empty())
return false;
std::string spdy_server = GetFlattenedSpdyServer(host_port_pair);
SpdyServerHostPortTable::const_iterator spdy_host_port =
spdy_servers_table_.find(spdy_server);
if (spdy_host_port != spdy_servers_table_.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);
SpdyServerHostPortTable::iterator spdy_host_port =
spdy_servers_table_.find(spdy_server);
if ((spdy_host_port != spdy_servers_table_.end()) &&
(spdy_host_port->second == support_spdy)) {
return;
}
// Cache the data.
spdy_servers_table_[spdy_server] = support_spdy;
}
bool HttpServerPropertiesImpl::HasAlternateProtocol(
const HostPortPair& server) const {
return ContainsKey(alternate_protocol_map_, server) ||
g_forced_alternate_protocol;
}
PortAlternateProtocolPair
HttpServerPropertiesImpl::GetAlternateProtocol(
const HostPortPair& server) const {
DCHECK(HasAlternateProtocol(server));
// First check the map.
AlternateProtocolMap::const_iterator it =
alternate_protocol_map_.find(server);
if (it != alternate_protocol_map_.end())
return it->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
<< "].";
}
}
alternate_protocol_map_[server] = alternate;
}
void HttpServerPropertiesImpl::SetBrokenAlternateProtocol(
const HostPortPair& server) {
alternate_protocol_map_[server].protocol = ALTERNATE_PROTOCOL_BROKEN;
}
const AlternateProtocolMap&
HttpServerPropertiesImpl::alternate_protocol_map() const {
return alternate_protocol_map_;
}
const SettingsMap& HttpServerPropertiesImpl::GetSpdySettings(
const HostPortPair& host_port_pair) const {
SpdySettingsMap::const_iterator it = spdy_settings_map_.find(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;
SettingsMap& settings_map = spdy_settings_map_[host_port_pair];
SettingsFlagsAndValue flags_and_value(SETTINGS_FLAG_PERSISTED, value);
settings_map[id] = flags_and_value;
return true;
}
void HttpServerPropertiesImpl::ClearSpdySettings(
const HostPortPair& host_port_pair) {
spdy_settings_map_.erase(host_port_pair);
}
void HttpServerPropertiesImpl::ClearAllSpdySettings() {
spdy_settings_map_.clear();
}
const SpdySettingsMap&
HttpServerPropertiesImpl::spdy_settings_map() const {
return spdy_settings_map_;
}
HttpPipelinedHostCapability HttpServerPropertiesImpl::GetPipelineCapability(
const HostPortPair& origin) {
HttpPipelinedHostCapability capability = PIPELINE_UNKNOWN;
CachedPipelineCapabilityMap::const_iterator it =
pipeline_capability_map_->Get(origin);
if (it != pipeline_capability_map_->end()) {
capability = it->second;
}
return capability;
}
void HttpServerPropertiesImpl::SetPipelineCapability(
const HostPortPair& origin,
HttpPipelinedHostCapability capability) {
CachedPipelineCapabilityMap::iterator it =
pipeline_capability_map_->Peek(origin);
if (it == pipeline_capability_map_->end() ||
it->second != PIPELINE_INCAPABLE) {
pipeline_capability_map_->Put(origin, capability);
}
}
void HttpServerPropertiesImpl::ClearPipelineCapabilities() {
pipeline_capability_map_->Clear();
}
PipelineCapabilityMap
HttpServerPropertiesImpl::GetPipelineCapabilityMap() const {
PipelineCapabilityMap result;
CachedPipelineCapabilityMap::const_iterator it;
for (it = pipeline_capability_map_->begin();
it != pipeline_capability_map_->end(); ++it) {
result[it->first] = it->second;
}
return result;
}
} // namespace net