1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/http/http_server_properties_impl.h"
8 #include "base/logging.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "net/http/http_pipelined_host_capability.h"
20 // TODO(simonjam): Run experiments with different values of this to see what
21 // value is good at avoiding evictions without eating too much memory. Until
22 // then, this is just a bad guess.
23 const int kDefaultNumHostsToRemember = 200;
25 const uint64 kBrokenAlternateProtocolDelaySecs = 300;
29 HttpServerPropertiesImpl::HttpServerPropertiesImpl()
30 : spdy_servers_map_(SpdyServerHostPortMap::NO_AUTO_EVICT),
31 alternate_protocol_map_(AlternateProtocolMap::NO_AUTO_EVICT),
32 spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT),
33 pipeline_capability_map_(
34 new CachedPipelineCapabilityMap(kDefaultNumHostsToRemember)),
35 weak_ptr_factory_(this) {
36 canoncial_suffixes_.push_back(".c.youtube.com");
37 canoncial_suffixes_.push_back(".googlevideo.com");
40 HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
43 void HttpServerPropertiesImpl::InitializeSpdyServers(
44 std::vector<std::string>* spdy_servers,
46 DCHECK(CalledOnValidThread());
49 // Add the entries from persisted data.
50 for (std::vector<std::string>::reverse_iterator it = spdy_servers->rbegin();
51 it != spdy_servers->rend(); ++it) {
52 spdy_servers_map_.Put(*it, support_spdy);
56 void HttpServerPropertiesImpl::InitializeAlternateProtocolServers(
57 AlternateProtocolMap* alternate_protocol_map) {
58 // Keep all the ALTERNATE_PROTOCOL_BROKEN ones since those don't
60 for (AlternateProtocolMap::iterator it = alternate_protocol_map_.begin();
61 it != alternate_protocol_map_.end();) {
62 AlternateProtocolMap::iterator old_it = it;
64 if (old_it->second.protocol != ALTERNATE_PROTOCOL_BROKEN) {
65 alternate_protocol_map_.Erase(old_it);
69 // Add the entries from persisted data.
70 for (AlternateProtocolMap::reverse_iterator it =
71 alternate_protocol_map->rbegin();
72 it != alternate_protocol_map->rend(); ++it) {
73 alternate_protocol_map_.Put(it->first, it->second);
76 // Attempt to find canonical servers.
77 int canonical_ports[] = { 80, 443 };
78 for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) {
79 std::string canonical_suffix = canoncial_suffixes_[i];
80 for (size_t j = 0; j < arraysize(canonical_ports); ++j) {
81 HostPortPair canonical_host(canonical_suffix, canonical_ports[j]);
82 // If we already have a valid canonical server, we're done.
83 if (ContainsKey(canonical_host_to_origin_map_, canonical_host) &&
84 (alternate_protocol_map_.Peek(canonical_host_to_origin_map_[
85 canonical_host]) != alternate_protocol_map_.end())) {
88 // Now attempt to find a server which matches this origin and set it as
90 for (AlternateProtocolMap::const_iterator it =
91 alternate_protocol_map_.begin();
92 it != alternate_protocol_map_.end(); ++it) {
93 if (EndsWith(it->first.host(), canoncial_suffixes_[i], false)) {
94 canonical_host_to_origin_map_[canonical_host] = it->first;
102 void HttpServerPropertiesImpl::InitializeSpdySettingsServers(
103 SpdySettingsMap* spdy_settings_map) {
104 for (SpdySettingsMap::reverse_iterator it = spdy_settings_map->rbegin();
105 it != spdy_settings_map->rend(); ++it) {
106 spdy_settings_map_.Put(it->first, it->second);
110 void HttpServerPropertiesImpl::InitializePipelineCapabilities(
111 const PipelineCapabilityMap* pipeline_capability_map) {
112 PipelineCapabilityMap::const_iterator it;
113 pipeline_capability_map_->Clear();
114 for (it = pipeline_capability_map->begin();
115 it != pipeline_capability_map->end(); ++it) {
116 pipeline_capability_map_->Put(it->first, it->second);
120 void HttpServerPropertiesImpl::SetNumPipelinedHostsToRemember(int max_size) {
121 DCHECK(pipeline_capability_map_->empty());
122 pipeline_capability_map_.reset(new CachedPipelineCapabilityMap(max_size));
125 void HttpServerPropertiesImpl::GetSpdyServerList(
126 base::ListValue* spdy_server_list,
127 size_t max_size) const {
128 DCHECK(CalledOnValidThread());
129 DCHECK(spdy_server_list);
130 spdy_server_list->Clear();
132 // Get the list of servers (host/port) that support SPDY.
133 for (SpdyServerHostPortMap::const_iterator it = spdy_servers_map_.begin();
134 it != spdy_servers_map_.end() && count < max_size; ++it) {
135 const std::string spdy_server_host_port = it->first;
137 spdy_server_list->Append(new base::StringValue(spdy_server_host_port));
144 std::string HttpServerPropertiesImpl::GetFlattenedSpdyServer(
145 const net::HostPortPair& host_port_pair) {
146 std::string spdy_server;
147 spdy_server.append(host_port_pair.host());
148 spdy_server.append(":");
149 base::StringAppendF(&spdy_server, "%d", host_port_pair.port());
153 static const PortAlternateProtocolPair* g_forced_alternate_protocol = NULL;
156 void HttpServerPropertiesImpl::ForceAlternateProtocol(
157 const PortAlternateProtocolPair& pair) {
158 // Note: we're going to leak this.
159 if (g_forced_alternate_protocol)
160 delete g_forced_alternate_protocol;
161 g_forced_alternate_protocol = new PortAlternateProtocolPair(pair);
165 void HttpServerPropertiesImpl::DisableForcedAlternateProtocol() {
166 delete g_forced_alternate_protocol;
167 g_forced_alternate_protocol = NULL;
170 base::WeakPtr<HttpServerProperties> HttpServerPropertiesImpl::GetWeakPtr() {
171 return weak_ptr_factory_.GetWeakPtr();
174 void HttpServerPropertiesImpl::Clear() {
175 DCHECK(CalledOnValidThread());
176 spdy_servers_map_.Clear();
177 alternate_protocol_map_.Clear();
178 spdy_settings_map_.Clear();
179 pipeline_capability_map_->Clear();
182 bool HttpServerPropertiesImpl::SupportsSpdy(
183 const net::HostPortPair& host_port_pair) {
184 DCHECK(CalledOnValidThread());
185 if (host_port_pair.host().empty())
187 std::string spdy_server = GetFlattenedSpdyServer(host_port_pair);
189 SpdyServerHostPortMap::iterator spdy_host_port =
190 spdy_servers_map_.Get(spdy_server);
191 if (spdy_host_port != spdy_servers_map_.end())
192 return spdy_host_port->second;
196 void HttpServerPropertiesImpl::SetSupportsSpdy(
197 const net::HostPortPair& host_port_pair,
199 DCHECK(CalledOnValidThread());
200 if (host_port_pair.host().empty())
202 std::string spdy_server = GetFlattenedSpdyServer(host_port_pair);
204 SpdyServerHostPortMap::iterator spdy_host_port =
205 spdy_servers_map_.Get(spdy_server);
206 if ((spdy_host_port != spdy_servers_map_.end()) &&
207 (spdy_host_port->second == support_spdy)) {
211 spdy_servers_map_.Put(spdy_server, support_spdy);
214 bool HttpServerPropertiesImpl::HasAlternateProtocol(
215 const HostPortPair& server) {
216 if (alternate_protocol_map_.Get(server) != alternate_protocol_map_.end() ||
217 g_forced_alternate_protocol)
220 return GetCanonicalHost(server) != canonical_host_to_origin_map_.end();
223 PortAlternateProtocolPair
224 HttpServerPropertiesImpl::GetAlternateProtocol(
225 const HostPortPair& server) {
226 DCHECK(HasAlternateProtocol(server));
228 // First check the map.
229 AlternateProtocolMap::iterator it = alternate_protocol_map_.Get(server);
230 if (it != alternate_protocol_map_.end())
233 // Next check the canonical host.
234 CanonicalHostMap::const_iterator canonical_host = GetCanonicalHost(server);
235 if (canonical_host != canonical_host_to_origin_map_.end())
236 return alternate_protocol_map_.Get(canonical_host->second)->second;
238 // We must be forcing an alternate.
239 DCHECK(g_forced_alternate_protocol);
240 return *g_forced_alternate_protocol;
243 void HttpServerPropertiesImpl::SetAlternateProtocol(
244 const HostPortPair& server,
245 uint16 alternate_port,
246 AlternateProtocol alternate_protocol) {
247 if (alternate_protocol == ALTERNATE_PROTOCOL_BROKEN) {
248 LOG(DFATAL) << "Call SetBrokenAlternateProtocol() instead.";
252 PortAlternateProtocolPair alternate;
253 alternate.port = alternate_port;
254 alternate.protocol = alternate_protocol;
255 if (HasAlternateProtocol(server)) {
256 const PortAlternateProtocolPair existing_alternate =
257 GetAlternateProtocol(server);
259 if (existing_alternate.protocol == ALTERNATE_PROTOCOL_BROKEN) {
260 DVLOG(1) << "Ignore alternate protocol since it's known to be broken.";
264 if (alternate_protocol != ALTERNATE_PROTOCOL_BROKEN &&
265 !existing_alternate.Equals(alternate)) {
266 LOG(WARNING) << "Changing the alternate protocol for: "
268 << " from [Port: " << existing_alternate.port
269 << ", Protocol: " << existing_alternate.protocol
270 << "] to [Port: " << alternate_port
271 << ", Protocol: " << alternate_protocol
275 // TODO(rch): Consider the case where multiple requests are started
276 // before the first completes. In this case, only one of the jobs
277 // would reach this code, whereas all of them should should have.
278 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING);
281 alternate_protocol_map_.Put(server, alternate);
283 // If this host ends with a canonical suffix, then set it as the
285 for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) {
286 std::string canonical_suffix = canoncial_suffixes_[i];
287 if (EndsWith(server.host(), canoncial_suffixes_[i], false)) {
288 HostPortPair canonical_host(canonical_suffix, server.port());
289 canonical_host_to_origin_map_[canonical_host] = server;
295 void HttpServerPropertiesImpl::SetBrokenAlternateProtocol(
296 const HostPortPair& server) {
297 AlternateProtocolMap::iterator it = alternate_protocol_map_.Get(server);
298 if (it != alternate_protocol_map_.end()) {
299 it->second.protocol = ALTERNATE_PROTOCOL_BROKEN;
302 PortAlternateProtocolPair alternate;
303 alternate.protocol = ALTERNATE_PROTOCOL_BROKEN;
304 alternate_protocol_map_.Put(server, alternate);
306 int count = ++broken_alternate_protocol_map_[server];
307 base::TimeDelta delay =
308 base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs);
309 BrokenAlternateProtocolEntry entry;
310 entry.server = server;
311 entry.when = base::TimeTicks::Now() + delay * (1 << (count - 1));
312 broken_alternate_protocol_list_.push_back(entry);
313 // If this is the only entry in the list, schedule an expiration task.
314 // Otherwse it will be rescheduled automatically when the pending
316 if (broken_alternate_protocol_list_.size() == 1) {
317 ScheduleBrokenAlternateProtocolMappingsExpiration();
321 bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken(
322 const HostPortPair& server) {
323 return ContainsKey(broken_alternate_protocol_map_, server);
326 void HttpServerPropertiesImpl::ConfirmAlternateProtocol(
327 const HostPortPair& server) {
328 broken_alternate_protocol_map_.erase(server);
331 void HttpServerPropertiesImpl::ClearAlternateProtocol(
332 const HostPortPair& server) {
333 AlternateProtocolMap::iterator it = alternate_protocol_map_.Peek(server);
334 if (it != alternate_protocol_map_.end())
335 alternate_protocol_map_.Erase(it);
338 const AlternateProtocolMap&
339 HttpServerPropertiesImpl::alternate_protocol_map() const {
340 return alternate_protocol_map_;
343 const SettingsMap& HttpServerPropertiesImpl::GetSpdySettings(
344 const HostPortPair& host_port_pair) {
345 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
346 if (it == spdy_settings_map_.end()) {
347 CR_DEFINE_STATIC_LOCAL(SettingsMap, kEmptySettingsMap, ());
348 return kEmptySettingsMap;
353 bool HttpServerPropertiesImpl::SetSpdySetting(
354 const HostPortPair& host_port_pair,
356 SpdySettingsFlags flags,
358 if (!(flags & SETTINGS_FLAG_PLEASE_PERSIST))
361 SettingsFlagsAndValue flags_and_value(SETTINGS_FLAG_PERSISTED, value);
362 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
363 if (it == spdy_settings_map_.end()) {
364 SettingsMap settings_map;
365 settings_map[id] = flags_and_value;
366 spdy_settings_map_.Put(host_port_pair, settings_map);
368 SettingsMap& settings_map = it->second;
369 settings_map[id] = flags_and_value;
374 void HttpServerPropertiesImpl::ClearSpdySettings(
375 const HostPortPair& host_port_pair) {
376 SpdySettingsMap::iterator it = spdy_settings_map_.Peek(host_port_pair);
377 if (it != spdy_settings_map_.end())
378 spdy_settings_map_.Erase(it);
381 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
382 spdy_settings_map_.Clear();
385 const SpdySettingsMap&
386 HttpServerPropertiesImpl::spdy_settings_map() const {
387 return spdy_settings_map_;
390 void HttpServerPropertiesImpl::SetServerNetworkStats(
391 const HostPortPair& host_port_pair,
392 NetworkStats stats) {
393 server_network_stats_map_[host_port_pair] = stats;
396 const HttpServerProperties::NetworkStats*
397 HttpServerPropertiesImpl::GetServerNetworkStats(
398 const HostPortPair& host_port_pair) const {
399 ServerNetworkStatsMap::const_iterator it =
400 server_network_stats_map_.find(host_port_pair);
401 if (it == server_network_stats_map_.end()) {
407 HttpPipelinedHostCapability HttpServerPropertiesImpl::GetPipelineCapability(
408 const HostPortPair& origin) {
409 HttpPipelinedHostCapability capability = PIPELINE_UNKNOWN;
410 CachedPipelineCapabilityMap::const_iterator it =
411 pipeline_capability_map_->Get(origin);
412 if (it != pipeline_capability_map_->end()) {
413 capability = it->second;
418 void HttpServerPropertiesImpl::SetPipelineCapability(
419 const HostPortPair& origin,
420 HttpPipelinedHostCapability capability) {
421 CachedPipelineCapabilityMap::iterator it =
422 pipeline_capability_map_->Peek(origin);
423 if (it == pipeline_capability_map_->end() ||
424 it->second != PIPELINE_INCAPABLE) {
425 pipeline_capability_map_->Put(origin, capability);
429 void HttpServerPropertiesImpl::ClearPipelineCapabilities() {
430 pipeline_capability_map_->Clear();
433 PipelineCapabilityMap
434 HttpServerPropertiesImpl::GetPipelineCapabilityMap() const {
435 PipelineCapabilityMap result;
436 CachedPipelineCapabilityMap::const_iterator it;
437 for (it = pipeline_capability_map_->begin();
438 it != pipeline_capability_map_->end(); ++it) {
439 result[it->first] = it->second;
444 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
445 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server) const {
446 for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) {
447 std::string canonical_suffix = canoncial_suffixes_[i];
448 if (EndsWith(server.host(), canoncial_suffixes_[i], false)) {
449 HostPortPair canonical_host(canonical_suffix, server.port());
450 return canonical_host_to_origin_map_.find(canonical_host);
454 return canonical_host_to_origin_map_.end();
457 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() {
458 base::TimeTicks now = base::TimeTicks::Now();
459 while (!broken_alternate_protocol_list_.empty()) {
460 BrokenAlternateProtocolEntry entry =
461 broken_alternate_protocol_list_.front();
462 if (now < entry.when) {
466 ClearAlternateProtocol(entry.server);
467 broken_alternate_protocol_list_.pop_front();
469 ScheduleBrokenAlternateProtocolMappingsExpiration();
473 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
474 if (broken_alternate_protocol_list_.empty()) {
477 base::TimeTicks now = base::TimeTicks::Now();
478 base::TimeTicks when = broken_alternate_protocol_list_.front().when;
479 base::TimeDelta delay = when > now ? when - now : base::TimeDelta();
480 base::MessageLoop::current()->PostDelayedTask(
483 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings,
484 weak_ptr_factory_.GetWeakPtr()),