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"
19 const uint64 kBrokenAlternateProtocolDelaySecs = 300;
23 HttpServerPropertiesImpl::HttpServerPropertiesImpl()
24 : spdy_servers_map_(SpdyServerHostPortMap::NO_AUTO_EVICT),
25 alternate_protocol_map_(AlternateProtocolMap::NO_AUTO_EVICT),
26 spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT),
27 alternate_protocol_probability_threshold_(1),
28 weak_ptr_factory_(this) {
29 canonical_suffixes_.push_back(".c.youtube.com");
30 canonical_suffixes_.push_back(".googlevideo.com");
31 canonical_suffixes_.push_back(".googleusercontent.com");
34 HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
37 void HttpServerPropertiesImpl::InitializeSpdyServers(
38 std::vector<std::string>* spdy_servers,
40 DCHECK(CalledOnValidThread());
43 // Add the entries from persisted data.
44 for (std::vector<std::string>::reverse_iterator it = spdy_servers->rbegin();
45 it != spdy_servers->rend(); ++it) {
46 spdy_servers_map_.Put(*it, support_spdy);
50 void HttpServerPropertiesImpl::InitializeAlternateProtocolServers(
51 AlternateProtocolMap* alternate_protocol_map) {
52 // Keep all the broken ones since those don't get persisted.
53 for (AlternateProtocolMap::iterator it = alternate_protocol_map_.begin();
54 it != alternate_protocol_map_.end();) {
55 AlternateProtocolMap::iterator old_it = it;
57 if (!old_it->second.is_broken) {
58 alternate_protocol_map_.Erase(old_it);
62 // Add the entries from persisted data.
63 for (AlternateProtocolMap::reverse_iterator it =
64 alternate_protocol_map->rbegin();
65 it != alternate_protocol_map->rend(); ++it) {
66 alternate_protocol_map_.Put(it->first, it->second);
69 // Attempt to find canonical servers.
70 int canonical_ports[] = { 80, 443 };
71 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
72 std::string canonical_suffix = canonical_suffixes_[i];
73 for (size_t j = 0; j < arraysize(canonical_ports); ++j) {
74 HostPortPair canonical_host(canonical_suffix, canonical_ports[j]);
75 // If we already have a valid canonical server, we're done.
76 if (ContainsKey(canonical_host_to_origin_map_, canonical_host) &&
77 (alternate_protocol_map_.Peek(canonical_host_to_origin_map_[
78 canonical_host]) != alternate_protocol_map_.end())) {
81 // Now attempt to find a server which matches this origin and set it as
83 for (AlternateProtocolMap::const_iterator it =
84 alternate_protocol_map_.begin();
85 it != alternate_protocol_map_.end(); ++it) {
86 if (EndsWith(it->first.host(), canonical_suffixes_[i], false)) {
87 canonical_host_to_origin_map_[canonical_host] = it->first;
95 void HttpServerPropertiesImpl::InitializeSpdySettingsServers(
96 SpdySettingsMap* spdy_settings_map) {
97 for (SpdySettingsMap::reverse_iterator it = spdy_settings_map->rbegin();
98 it != spdy_settings_map->rend(); ++it) {
99 spdy_settings_map_.Put(it->first, it->second);
103 void HttpServerPropertiesImpl::InitializeSupportsQuic(
104 SupportsQuicMap* supports_quic_map) {
105 for (SupportsQuicMap::reverse_iterator it = supports_quic_map->rbegin();
106 it != supports_quic_map->rend();
108 supports_quic_map_.insert(std::make_pair(it->first, it->second));
112 void HttpServerPropertiesImpl::GetSpdyServerList(
113 base::ListValue* spdy_server_list,
114 size_t max_size) const {
115 DCHECK(CalledOnValidThread());
116 DCHECK(spdy_server_list);
117 spdy_server_list->Clear();
119 // Get the list of servers (host/port) that support SPDY.
120 for (SpdyServerHostPortMap::const_iterator it = spdy_servers_map_.begin();
121 it != spdy_servers_map_.end() && count < max_size; ++it) {
122 const std::string spdy_server_host_port = it->first;
124 spdy_server_list->Append(new base::StringValue(spdy_server_host_port));
131 std::string HttpServerPropertiesImpl::GetFlattenedSpdyServer(
132 const net::HostPortPair& host_port_pair) {
133 std::string spdy_server;
134 spdy_server.append(host_port_pair.host());
135 spdy_server.append(":");
136 base::StringAppendF(&spdy_server, "%d", host_port_pair.port());
140 static const AlternateProtocolInfo* g_forced_alternate_protocol = NULL;
143 void HttpServerPropertiesImpl::ForceAlternateProtocol(
144 const AlternateProtocolInfo& info) {
145 // Note: we're going to leak this.
146 if (g_forced_alternate_protocol)
147 delete g_forced_alternate_protocol;
148 g_forced_alternate_protocol = new AlternateProtocolInfo(info);
152 void HttpServerPropertiesImpl::DisableForcedAlternateProtocol() {
153 delete g_forced_alternate_protocol;
154 g_forced_alternate_protocol = NULL;
157 base::WeakPtr<HttpServerProperties> HttpServerPropertiesImpl::GetWeakPtr() {
158 return weak_ptr_factory_.GetWeakPtr();
161 void HttpServerPropertiesImpl::Clear() {
162 DCHECK(CalledOnValidThread());
163 spdy_servers_map_.Clear();
164 alternate_protocol_map_.Clear();
165 canonical_host_to_origin_map_.clear();
166 spdy_settings_map_.Clear();
167 supports_quic_map_.clear();
170 bool HttpServerPropertiesImpl::SupportsSpdy(
171 const net::HostPortPair& host_port_pair) {
172 DCHECK(CalledOnValidThread());
173 if (host_port_pair.host().empty())
175 std::string spdy_server = GetFlattenedSpdyServer(host_port_pair);
177 SpdyServerHostPortMap::iterator spdy_host_port =
178 spdy_servers_map_.Get(spdy_server);
179 if (spdy_host_port != spdy_servers_map_.end())
180 return spdy_host_port->second;
184 void HttpServerPropertiesImpl::SetSupportsSpdy(
185 const net::HostPortPair& host_port_pair,
187 DCHECK(CalledOnValidThread());
188 if (host_port_pair.host().empty())
190 std::string spdy_server = GetFlattenedSpdyServer(host_port_pair);
192 SpdyServerHostPortMap::iterator spdy_host_port =
193 spdy_servers_map_.Get(spdy_server);
194 if ((spdy_host_port != spdy_servers_map_.end()) &&
195 (spdy_host_port->second == support_spdy)) {
199 spdy_servers_map_.Put(spdy_server, support_spdy);
202 bool HttpServerPropertiesImpl::HasAlternateProtocol(
203 const HostPortPair& server) {
204 if (g_forced_alternate_protocol)
206 AlternateProtocolMap::const_iterator it = alternate_protocol_map_.Get(server);
207 if (it != alternate_protocol_map_.end() &&
208 it->second.probability >= alternate_protocol_probability_threshold_) {
212 return GetCanonicalHost(server) != canonical_host_to_origin_map_.end();
215 std::string HttpServerPropertiesImpl::GetCanonicalSuffix(
216 const HostPortPair& server) {
217 // If this host ends with a canonical suffix, then return the canonical
219 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
220 std::string canonical_suffix = canonical_suffixes_[i];
221 if (EndsWith(server.host(), canonical_suffixes_[i], false)) {
222 return canonical_suffix;
225 return std::string();
228 AlternateProtocolInfo
229 HttpServerPropertiesImpl::GetAlternateProtocol(
230 const HostPortPair& server) {
231 DCHECK(HasAlternateProtocol(server));
233 // First check the map.
234 AlternateProtocolMap::iterator it = alternate_protocol_map_.Get(server);
235 if (it != alternate_protocol_map_.end())
238 // Next check the canonical host.
239 CanonicalHostMap::const_iterator canonical_host = GetCanonicalHost(server);
240 if (canonical_host != canonical_host_to_origin_map_.end())
241 return alternate_protocol_map_.Get(canonical_host->second)->second;
243 // We must be forcing an alternate.
244 DCHECK(g_forced_alternate_protocol);
245 return *g_forced_alternate_protocol;
248 void HttpServerPropertiesImpl::SetAlternateProtocol(
249 const HostPortPair& server,
250 uint16 alternate_port,
251 AlternateProtocol alternate_protocol,
252 double alternate_probability) {
254 AlternateProtocolInfo alternate(alternate_port,
256 alternate_probability);
257 if (HasAlternateProtocol(server)) {
258 const AlternateProtocolInfo existing_alternate =
259 GetAlternateProtocol(server);
261 if (existing_alternate.is_broken) {
262 DVLOG(1) << "Ignore alternate protocol since it's known to be broken.";
266 if (!existing_alternate.Equals(alternate)) {
267 LOG(WARNING) << "Changing the alternate protocol for: "
269 << " from [Port: " << existing_alternate.port
270 << ", Protocol: " << existing_alternate.protocol
271 << ", Probability: " << existing_alternate.probability
272 << "] to [Port: " << alternate_port
273 << ", Protocol: " << alternate_protocol
274 << ", Probability: " << alternate_probability
278 if (alternate_probability >= alternate_protocol_probability_threshold_) {
279 // TODO(rch): Consider the case where multiple requests are started
280 // before the first completes. In this case, only one of the jobs
281 // would reach this code, whereas all of them should should have.
282 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING);
286 alternate_protocol_map_.Put(server, alternate);
288 // If this host ends with a canonical suffix, then set it as the
290 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
291 std::string canonical_suffix = canonical_suffixes_[i];
292 if (EndsWith(server.host(), canonical_suffixes_[i], false)) {
293 HostPortPair canonical_host(canonical_suffix, server.port());
294 canonical_host_to_origin_map_[canonical_host] = server;
300 void HttpServerPropertiesImpl::SetBrokenAlternateProtocol(
301 const HostPortPair& server) {
302 AlternateProtocolMap::iterator it = alternate_protocol_map_.Get(server);
303 if (it == alternate_protocol_map_.end()) {
304 LOG(DFATAL) << "Trying to mark unknown alternate protocol broken.";
307 it->second.is_broken = true;
308 int count = ++broken_alternate_protocol_map_[server];
309 base::TimeDelta delay =
310 base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs);
311 BrokenAlternateProtocolEntry entry;
312 entry.server = server;
313 entry.when = base::TimeTicks::Now() + delay * (1 << (count - 1));
314 broken_alternate_protocol_list_.push_back(entry);
316 // Do not leave this host as canonical so that we don't infer the other
317 // hosts are also broken without testing them first.
318 RemoveCanonicalHost(server);
320 // If this is the only entry in the list, schedule an expiration task.
321 // Otherwse it will be rescheduled automatically when the pending
323 if (broken_alternate_protocol_list_.size() == 1) {
324 ScheduleBrokenAlternateProtocolMappingsExpiration();
328 bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken(
329 const HostPortPair& server) {
330 return ContainsKey(broken_alternate_protocol_map_, server);
333 void HttpServerPropertiesImpl::ConfirmAlternateProtocol(
334 const HostPortPair& server) {
335 broken_alternate_protocol_map_.erase(server);
338 void HttpServerPropertiesImpl::ClearAlternateProtocol(
339 const HostPortPair& server) {
340 AlternateProtocolMap::iterator it = alternate_protocol_map_.Peek(server);
341 if (it != alternate_protocol_map_.end())
342 alternate_protocol_map_.Erase(it);
344 RemoveCanonicalHost(server);
347 const AlternateProtocolMap&
348 HttpServerPropertiesImpl::alternate_protocol_map() const {
349 return alternate_protocol_map_;
352 const SettingsMap& HttpServerPropertiesImpl::GetSpdySettings(
353 const HostPortPair& host_port_pair) {
354 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
355 if (it == spdy_settings_map_.end()) {
356 CR_DEFINE_STATIC_LOCAL(SettingsMap, kEmptySettingsMap, ());
357 return kEmptySettingsMap;
362 bool HttpServerPropertiesImpl::SetSpdySetting(
363 const HostPortPair& host_port_pair,
365 SpdySettingsFlags flags,
367 if (!(flags & SETTINGS_FLAG_PLEASE_PERSIST))
370 SettingsFlagsAndValue flags_and_value(SETTINGS_FLAG_PERSISTED, value);
371 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
372 if (it == spdy_settings_map_.end()) {
373 SettingsMap settings_map;
374 settings_map[id] = flags_and_value;
375 spdy_settings_map_.Put(host_port_pair, settings_map);
377 SettingsMap& settings_map = it->second;
378 settings_map[id] = flags_and_value;
383 void HttpServerPropertiesImpl::ClearSpdySettings(
384 const HostPortPair& host_port_pair) {
385 SpdySettingsMap::iterator it = spdy_settings_map_.Peek(host_port_pair);
386 if (it != spdy_settings_map_.end())
387 spdy_settings_map_.Erase(it);
390 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
391 spdy_settings_map_.Clear();
394 const SpdySettingsMap&
395 HttpServerPropertiesImpl::spdy_settings_map() const {
396 return spdy_settings_map_;
399 SupportsQuic HttpServerPropertiesImpl::GetSupportsQuic(
400 const HostPortPair& host_port_pair) const {
401 SupportsQuicMap::const_iterator it = supports_quic_map_.find(host_port_pair);
402 if (it == supports_quic_map_.end()) {
403 CR_DEFINE_STATIC_LOCAL(SupportsQuic, kEmptySupportsQuic, ());
404 return kEmptySupportsQuic;
409 void HttpServerPropertiesImpl::SetSupportsQuic(
410 const HostPortPair& host_port_pair,
412 const std::string& address) {
413 SupportsQuic supports_quic(used_quic, address);
414 supports_quic_map_.insert(std::make_pair(host_port_pair, supports_quic));
417 const SupportsQuicMap&
418 HttpServerPropertiesImpl::supports_quic_map() const {
419 return supports_quic_map_;
422 void HttpServerPropertiesImpl::SetServerNetworkStats(
423 const HostPortPair& host_port_pair,
424 NetworkStats stats) {
425 server_network_stats_map_[host_port_pair] = stats;
428 const HttpServerProperties::NetworkStats*
429 HttpServerPropertiesImpl::GetServerNetworkStats(
430 const HostPortPair& host_port_pair) const {
431 ServerNetworkStatsMap::const_iterator it =
432 server_network_stats_map_.find(host_port_pair);
433 if (it == server_network_stats_map_.end()) {
439 void HttpServerPropertiesImpl::SetAlternateProtocolProbabilityThreshold(
441 alternate_protocol_probability_threshold_ = threshold;
444 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
445 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server) const {
446 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
447 std::string canonical_suffix = canonical_suffixes_[i];
448 if (EndsWith(server.host(), canonical_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::RemoveCanonicalHost(
458 const HostPortPair& server) {
459 CanonicalHostMap::const_iterator canonical = GetCanonicalHost(server);
460 if (canonical == canonical_host_to_origin_map_.end())
463 if (!canonical->second.Equals(server))
466 canonical_host_to_origin_map_.erase(canonical->first);
469 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() {
470 base::TimeTicks now = base::TimeTicks::Now();
471 while (!broken_alternate_protocol_list_.empty()) {
472 BrokenAlternateProtocolEntry entry =
473 broken_alternate_protocol_list_.front();
474 if (now < entry.when) {
478 ClearAlternateProtocol(entry.server);
479 broken_alternate_protocol_list_.pop_front();
481 ScheduleBrokenAlternateProtocolMappingsExpiration();
485 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
486 if (broken_alternate_protocol_list_.empty()) {
489 base::TimeTicks now = base::TimeTicks::Now();
490 base::TimeTicks when = broken_alternate_protocol_list_.front().when;
491 base::TimeDelta delay = when > now ? when - now : base::TimeDelta();
492 base::MessageLoop::current()->PostDelayedTask(
495 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings,
496 weak_ptr_factory_.GetWeakPtr()),