// Copyright (c) 2010 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_alternate_protocols.h"
#include "base/logging.h"
#include "base/stringprintf.h"
#include "base/stl_util-inl.h"
namespace net {
const char HttpAlternateProtocols::kHeader[] = "Alternate-Protocol";
const char* const HttpAlternateProtocols::kProtocolStrings[] = {
"npn-spdy/1",
"npn-spdy/2",
};
const char* HttpAlternateProtocols::ProtocolToString(
HttpAlternateProtocols::Protocol protocol) {
switch (protocol) {
case HttpAlternateProtocols::NPN_SPDY_1:
case HttpAlternateProtocols::NPN_SPDY_2:
return HttpAlternateProtocols::kProtocolStrings[protocol];
case HttpAlternateProtocols::BROKEN:
return "Broken";
case HttpAlternateProtocols::UNINITIALIZED:
return "Uninitialized";
default:
NOTREACHED();
return "";
}
}
std::string HttpAlternateProtocols::PortProtocolPair::ToString() const {
return base::StringPrintf("%d:%s", port,
HttpAlternateProtocols::ProtocolToString(protocol));
}
// static
HttpAlternateProtocols::PortProtocolPair*
HttpAlternateProtocols::forced_alternate_protocol_ = NULL;
HttpAlternateProtocols::HttpAlternateProtocols() {}
HttpAlternateProtocols::~HttpAlternateProtocols() {}
bool HttpAlternateProtocols::HasAlternateProtocolFor(
const HostPortPair& http_host_port_pair) const {
return ContainsKey(protocol_map_, http_host_port_pair) ||
forced_alternate_protocol_;
}
bool HttpAlternateProtocols::HasAlternateProtocolFor(
const std::string& host, uint16 port) const {
HostPortPair http_host_port_pair(host, port);
return HasAlternateProtocolFor(http_host_port_pair);
}
HttpAlternateProtocols::PortProtocolPair
HttpAlternateProtocols::GetAlternateProtocolFor(
const HostPortPair& http_host_port_pair) const {
DCHECK(HasAlternateProtocolFor(http_host_port_pair));
// First check the map.
ProtocolMap::const_iterator it = protocol_map_.find(http_host_port_pair);
if (it != protocol_map_.end())
return it->second;
// We must be forcing an alternate.
DCHECK(forced_alternate_protocol_);
return *forced_alternate_protocol_;
}
HttpAlternateProtocols::PortProtocolPair
HttpAlternateProtocols::GetAlternateProtocolFor(
const std::string& host, uint16 port) const {
HostPortPair http_host_port_pair(host, port);
return GetAlternateProtocolFor(http_host_port_pair);
}
void HttpAlternateProtocols::SetAlternateProtocolFor(
const HostPortPair& http_host_port_pair,
uint16 alternate_port,
Protocol alternate_protocol) {
if (alternate_protocol == BROKEN) {
LOG(DFATAL) << "Call MarkBrokenAlternateProtocolFor() instead.";
return;
}
PortProtocolPair alternate;
alternate.port = alternate_port;
alternate.protocol = alternate_protocol;
if (HasAlternateProtocolFor(http_host_port_pair)) {
const PortProtocolPair existing_alternate =
GetAlternateProtocolFor(http_host_port_pair);
if (existing_alternate.protocol == BROKEN) {
DVLOG(1) << "Ignore alternate protocol since it's known to be broken.";
return;
}
if (alternate_protocol != BROKEN && !existing_alternate.Equals(alternate)) {
LOG(WARNING) << "Changing the alternate protocol for: "
<< http_host_port_pair.ToString()
<< " from [Port: " << existing_alternate.port
<< ", Protocol: " << existing_alternate.protocol
<< "] to [Port: " << alternate_port
<< ", Protocol: " << alternate_protocol
<< "].";
}
}
protocol_map_[http_host_port_pair] = alternate;
}
void HttpAlternateProtocols::MarkBrokenAlternateProtocolFor(
const HostPortPair& http_host_port_pair) {
protocol_map_[http_host_port_pair].protocol = BROKEN;
}
// static
void HttpAlternateProtocols::ForceAlternateProtocol(
const PortProtocolPair& pair) {
// Note: we're going to leak this.
if (forced_alternate_protocol_)
delete forced_alternate_protocol_;
forced_alternate_protocol_ = new PortProtocolPair(pair);
}
// static
void HttpAlternateProtocols::DisableForcedAlternateProtocol() {
delete forced_alternate_protocol_;
forced_alternate_protocol_ = NULL;
}
} // namespace net