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. 4 5 #include "net/http/http_server_properties_impl.h" 6 7 #include "base/bind.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 15 namespace net { 16 17 namespace { 18 19 const uint64 kBrokenAlternateProtocolDelaySecs = 300; 20 21 } // namespace 22 23 HttpServerPropertiesImpl::HttpServerPropertiesImpl() 24 : spdy_servers_map_(SpdyServerHostPortMap::NO_AUTO_EVICT), 25 alternate_protocol_map_(AlternateProtocolMap::NO_AUTO_EVICT), 26 alternate_protocol_experiment_( 27 ALTERNATE_PROTOCOL_NOT_PART_OF_EXPERIMENT), 28 spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT), 29 weak_ptr_factory_(this) { 30 canoncial_suffixes_.push_back(".c.youtube.com"); 31 canoncial_suffixes_.push_back(".googlevideo.com"); 32 canoncial_suffixes_.push_back(".googleusercontent.com"); 33 } 34 35 HttpServerPropertiesImpl::~HttpServerPropertiesImpl() { 36 } 37 38 void HttpServerPropertiesImpl::InitializeSpdyServers( 39 std::vector<std::string>* spdy_servers, 40 bool support_spdy) { 41 DCHECK(CalledOnValidThread()); 42 if (!spdy_servers) 43 return; 44 // Add the entries from persisted data. 45 for (std::vector<std::string>::reverse_iterator it = spdy_servers->rbegin(); 46 it != spdy_servers->rend(); ++it) { 47 spdy_servers_map_.Put(*it, support_spdy); 48 } 49 } 50 51 void HttpServerPropertiesImpl::InitializeAlternateProtocolServers( 52 AlternateProtocolMap* alternate_protocol_map) { 53 // Keep all the ALTERNATE_PROTOCOL_BROKEN ones since those don't 54 // get persisted. 55 for (AlternateProtocolMap::iterator it = alternate_protocol_map_.begin(); 56 it != alternate_protocol_map_.end();) { 57 AlternateProtocolMap::iterator old_it = it; 58 ++it; 59 if (old_it->second.protocol != ALTERNATE_PROTOCOL_BROKEN) { 60 alternate_protocol_map_.Erase(old_it); 61 } 62 } 63 64 // Add the entries from persisted data. 65 for (AlternateProtocolMap::reverse_iterator it = 66 alternate_protocol_map->rbegin(); 67 it != alternate_protocol_map->rend(); ++it) { 68 alternate_protocol_map_.Put(it->first, it->second); 69 } 70 71 // Attempt to find canonical servers. 72 int canonical_ports[] = { 80, 443 }; 73 for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) { 74 std::string canonical_suffix = canoncial_suffixes_[i]; 75 for (size_t j = 0; j < arraysize(canonical_ports); ++j) { 76 HostPortPair canonical_host(canonical_suffix, canonical_ports[j]); 77 // If we already have a valid canonical server, we're done. 78 if (ContainsKey(canonical_host_to_origin_map_, canonical_host) && 79 (alternate_protocol_map_.Peek(canonical_host_to_origin_map_[ 80 canonical_host]) != alternate_protocol_map_.end())) { 81 continue; 82 } 83 // Now attempt to find a server which matches this origin and set it as 84 // canonical . 85 for (AlternateProtocolMap::const_iterator it = 86 alternate_protocol_map_.begin(); 87 it != alternate_protocol_map_.end(); ++it) { 88 if (EndsWith(it->first.host(), canoncial_suffixes_[i], false)) { 89 canonical_host_to_origin_map_[canonical_host] = it->first; 90 break; 91 } 92 } 93 } 94 } 95 } 96 97 void HttpServerPropertiesImpl::InitializeSpdySettingsServers( 98 SpdySettingsMap* spdy_settings_map) { 99 for (SpdySettingsMap::reverse_iterator it = spdy_settings_map->rbegin(); 100 it != spdy_settings_map->rend(); ++it) { 101 spdy_settings_map_.Put(it->first, it->second); 102 } 103 } 104 105 void HttpServerPropertiesImpl::GetSpdyServerList( 106 base::ListValue* spdy_server_list, 107 size_t max_size) const { 108 DCHECK(CalledOnValidThread()); 109 DCHECK(spdy_server_list); 110 spdy_server_list->Clear(); 111 size_t count = 0; 112 // Get the list of servers (host/port) that support SPDY. 113 for (SpdyServerHostPortMap::const_iterator it = spdy_servers_map_.begin(); 114 it != spdy_servers_map_.end() && count < max_size; ++it) { 115 const std::string spdy_server_host_port = it->first; 116 if (it->second) { 117 spdy_server_list->Append(new base::StringValue(spdy_server_host_port)); 118 ++count; 119 } 120 } 121 } 122 123 // static 124 std::string HttpServerPropertiesImpl::GetFlattenedSpdyServer( 125 const net::HostPortPair& host_port_pair) { 126 std::string spdy_server; 127 spdy_server.append(host_port_pair.host()); 128 spdy_server.append(":"); 129 base::StringAppendF(&spdy_server, "%d", host_port_pair.port()); 130 return spdy_server; 131 } 132 133 static const PortAlternateProtocolPair* g_forced_alternate_protocol = NULL; 134 135 // static 136 void HttpServerPropertiesImpl::ForceAlternateProtocol( 137 const PortAlternateProtocolPair& pair) { 138 // Note: we're going to leak this. 139 if (g_forced_alternate_protocol) 140 delete g_forced_alternate_protocol; 141 g_forced_alternate_protocol = new PortAlternateProtocolPair(pair); 142 } 143 144 // static 145 void HttpServerPropertiesImpl::DisableForcedAlternateProtocol() { 146 delete g_forced_alternate_protocol; 147 g_forced_alternate_protocol = NULL; 148 } 149 150 base::WeakPtr<HttpServerProperties> HttpServerPropertiesImpl::GetWeakPtr() { 151 return weak_ptr_factory_.GetWeakPtr(); 152 } 153 154 void HttpServerPropertiesImpl::Clear() { 155 DCHECK(CalledOnValidThread()); 156 spdy_servers_map_.Clear(); 157 alternate_protocol_map_.Clear(); 158 spdy_settings_map_.Clear(); 159 } 160 161 bool HttpServerPropertiesImpl::SupportsSpdy( 162 const net::HostPortPair& host_port_pair) { 163 DCHECK(CalledOnValidThread()); 164 if (host_port_pair.host().empty()) 165 return false; 166 std::string spdy_server = GetFlattenedSpdyServer(host_port_pair); 167 168 SpdyServerHostPortMap::iterator spdy_host_port = 169 spdy_servers_map_.Get(spdy_server); 170 if (spdy_host_port != spdy_servers_map_.end()) 171 return spdy_host_port->second; 172 return false; 173 } 174 175 void HttpServerPropertiesImpl::SetSupportsSpdy( 176 const net::HostPortPair& host_port_pair, 177 bool support_spdy) { 178 DCHECK(CalledOnValidThread()); 179 if (host_port_pair.host().empty()) 180 return; 181 std::string spdy_server = GetFlattenedSpdyServer(host_port_pair); 182 183 SpdyServerHostPortMap::iterator spdy_host_port = 184 spdy_servers_map_.Get(spdy_server); 185 if ((spdy_host_port != spdy_servers_map_.end()) && 186 (spdy_host_port->second == support_spdy)) { 187 return; 188 } 189 // Cache the data. 190 spdy_servers_map_.Put(spdy_server, support_spdy); 191 } 192 193 bool HttpServerPropertiesImpl::HasAlternateProtocol( 194 const HostPortPair& server) { 195 if (alternate_protocol_map_.Get(server) != alternate_protocol_map_.end() || 196 g_forced_alternate_protocol) 197 return true; 198 199 return GetCanonicalHost(server) != canonical_host_to_origin_map_.end(); 200 } 201 202 std::string HttpServerPropertiesImpl::GetCanonicalSuffix( 203 const HostPortPair& server) { 204 // If this host ends with a canonical suffix, then return the canonical 205 // suffix. 206 for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) { 207 std::string canonical_suffix = canoncial_suffixes_[i]; 208 if (EndsWith(server.host(), canoncial_suffixes_[i], false)) { 209 return canonical_suffix; 210 } 211 } 212 return std::string(); 213 } 214 215 PortAlternateProtocolPair 216 HttpServerPropertiesImpl::GetAlternateProtocol( 217 const HostPortPair& server) { 218 DCHECK(HasAlternateProtocol(server)); 219 220 // First check the map. 221 AlternateProtocolMap::iterator it = alternate_protocol_map_.Get(server); 222 if (it != alternate_protocol_map_.end()) 223 return it->second; 224 225 // Next check the canonical host. 226 CanonicalHostMap::const_iterator canonical_host = GetCanonicalHost(server); 227 if (canonical_host != canonical_host_to_origin_map_.end()) 228 return alternate_protocol_map_.Get(canonical_host->second)->second; 229 230 // We must be forcing an alternate. 231 DCHECK(g_forced_alternate_protocol); 232 return *g_forced_alternate_protocol; 233 } 234 235 void HttpServerPropertiesImpl::SetAlternateProtocol( 236 const HostPortPair& server, 237 uint16 alternate_port, 238 AlternateProtocol alternate_protocol) { 239 if (alternate_protocol == ALTERNATE_PROTOCOL_BROKEN) { 240 LOG(DFATAL) << "Call SetBrokenAlternateProtocol() instead."; 241 return; 242 } 243 244 PortAlternateProtocolPair alternate; 245 alternate.port = alternate_port; 246 alternate.protocol = alternate_protocol; 247 if (HasAlternateProtocol(server)) { 248 const PortAlternateProtocolPair existing_alternate = 249 GetAlternateProtocol(server); 250 251 if (existing_alternate.protocol == ALTERNATE_PROTOCOL_BROKEN) { 252 DVLOG(1) << "Ignore alternate protocol since it's known to be broken."; 253 return; 254 } 255 256 if (alternate_protocol != ALTERNATE_PROTOCOL_BROKEN && 257 !existing_alternate.Equals(alternate)) { 258 LOG(WARNING) << "Changing the alternate protocol for: " 259 << server.ToString() 260 << " from [Port: " << existing_alternate.port 261 << ", Protocol: " << existing_alternate.protocol 262 << "] to [Port: " << alternate_port 263 << ", Protocol: " << alternate_protocol 264 << "]."; 265 } 266 } else { 267 // TODO(rch): Consider the case where multiple requests are started 268 // before the first completes. In this case, only one of the jobs 269 // would reach this code, whereas all of them should should have. 270 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING, 271 alternate_protocol_experiment_); 272 } 273 274 alternate_protocol_map_.Put(server, alternate); 275 276 // If this host ends with a canonical suffix, then set it as the 277 // canonical host. 278 for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) { 279 std::string canonical_suffix = canoncial_suffixes_[i]; 280 if (EndsWith(server.host(), canoncial_suffixes_[i], false)) { 281 HostPortPair canonical_host(canonical_suffix, server.port()); 282 canonical_host_to_origin_map_[canonical_host] = server; 283 break; 284 } 285 } 286 } 287 288 void HttpServerPropertiesImpl::SetBrokenAlternateProtocol( 289 const HostPortPair& server) { 290 AlternateProtocolMap::iterator it = alternate_protocol_map_.Get(server); 291 if (it != alternate_protocol_map_.end()) { 292 it->second.protocol = ALTERNATE_PROTOCOL_BROKEN; 293 } else { 294 PortAlternateProtocolPair alternate; 295 alternate.protocol = ALTERNATE_PROTOCOL_BROKEN; 296 alternate_protocol_map_.Put(server, alternate); 297 } 298 int count = ++broken_alternate_protocol_map_[server]; 299 base::TimeDelta delay = 300 base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs); 301 BrokenAlternateProtocolEntry entry; 302 entry.server = server; 303 entry.when = base::TimeTicks::Now() + delay * (1 << (count - 1)); 304 broken_alternate_protocol_list_.push_back(entry); 305 // If this is the only entry in the list, schedule an expiration task. 306 // Otherwse it will be rescheduled automatically when the pending 307 // task runs. 308 if (broken_alternate_protocol_list_.size() == 1) { 309 ScheduleBrokenAlternateProtocolMappingsExpiration(); 310 } 311 } 312 313 bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken( 314 const HostPortPair& server) { 315 return ContainsKey(broken_alternate_protocol_map_, server); 316 } 317 318 void HttpServerPropertiesImpl::ConfirmAlternateProtocol( 319 const HostPortPair& server) { 320 broken_alternate_protocol_map_.erase(server); 321 } 322 323 void HttpServerPropertiesImpl::ClearAlternateProtocol( 324 const HostPortPair& server) { 325 AlternateProtocolMap::iterator it = alternate_protocol_map_.Peek(server); 326 if (it != alternate_protocol_map_.end()) 327 alternate_protocol_map_.Erase(it); 328 } 329 330 const AlternateProtocolMap& 331 HttpServerPropertiesImpl::alternate_protocol_map() const { 332 return alternate_protocol_map_; 333 } 334 335 void HttpServerPropertiesImpl::SetAlternateProtocolExperiment( 336 AlternateProtocolExperiment experiment) { 337 alternate_protocol_experiment_ = experiment; 338 } 339 340 AlternateProtocolExperiment 341 HttpServerPropertiesImpl::GetAlternateProtocolExperiment() const { 342 return alternate_protocol_experiment_; 343 } 344 345 const SettingsMap& HttpServerPropertiesImpl::GetSpdySettings( 346 const HostPortPair& host_port_pair) { 347 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair); 348 if (it == spdy_settings_map_.end()) { 349 CR_DEFINE_STATIC_LOCAL(SettingsMap, kEmptySettingsMap, ()); 350 return kEmptySettingsMap; 351 } 352 return it->second; 353 } 354 355 bool HttpServerPropertiesImpl::SetSpdySetting( 356 const HostPortPair& host_port_pair, 357 SpdySettingsIds id, 358 SpdySettingsFlags flags, 359 uint32 value) { 360 if (!(flags & SETTINGS_FLAG_PLEASE_PERSIST)) 361 return false; 362 363 SettingsFlagsAndValue flags_and_value(SETTINGS_FLAG_PERSISTED, value); 364 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair); 365 if (it == spdy_settings_map_.end()) { 366 SettingsMap settings_map; 367 settings_map[id] = flags_and_value; 368 spdy_settings_map_.Put(host_port_pair, settings_map); 369 } else { 370 SettingsMap& settings_map = it->second; 371 settings_map[id] = flags_and_value; 372 } 373 return true; 374 } 375 376 void HttpServerPropertiesImpl::ClearSpdySettings( 377 const HostPortPair& host_port_pair) { 378 SpdySettingsMap::iterator it = spdy_settings_map_.Peek(host_port_pair); 379 if (it != spdy_settings_map_.end()) 380 spdy_settings_map_.Erase(it); 381 } 382 383 void HttpServerPropertiesImpl::ClearAllSpdySettings() { 384 spdy_settings_map_.Clear(); 385 } 386 387 const SpdySettingsMap& 388 HttpServerPropertiesImpl::spdy_settings_map() const { 389 return spdy_settings_map_; 390 } 391 392 void HttpServerPropertiesImpl::SetServerNetworkStats( 393 const HostPortPair& host_port_pair, 394 NetworkStats stats) { 395 server_network_stats_map_[host_port_pair] = stats; 396 } 397 398 const HttpServerProperties::NetworkStats* 399 HttpServerPropertiesImpl::GetServerNetworkStats( 400 const HostPortPair& host_port_pair) const { 401 ServerNetworkStatsMap::const_iterator it = 402 server_network_stats_map_.find(host_port_pair); 403 if (it == server_network_stats_map_.end()) { 404 return NULL; 405 } 406 return &it->second; 407 } 408 409 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator 410 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server) const { 411 for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) { 412 std::string canonical_suffix = canoncial_suffixes_[i]; 413 if (EndsWith(server.host(), canoncial_suffixes_[i], false)) { 414 HostPortPair canonical_host(canonical_suffix, server.port()); 415 return canonical_host_to_origin_map_.find(canonical_host); 416 } 417 } 418 419 return canonical_host_to_origin_map_.end(); 420 } 421 422 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() { 423 base::TimeTicks now = base::TimeTicks::Now(); 424 while (!broken_alternate_protocol_list_.empty()) { 425 BrokenAlternateProtocolEntry entry = 426 broken_alternate_protocol_list_.front(); 427 if (now < entry.when) { 428 break; 429 } 430 431 ClearAlternateProtocol(entry.server); 432 broken_alternate_protocol_list_.pop_front(); 433 } 434 ScheduleBrokenAlternateProtocolMappingsExpiration(); 435 } 436 437 void 438 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() { 439 if (broken_alternate_protocol_list_.empty()) { 440 return; 441 } 442 base::TimeTicks now = base::TimeTicks::Now(); 443 base::TimeTicks when = broken_alternate_protocol_list_.front().when; 444 base::TimeDelta delay = when > now ? when - now : base::TimeDelta(); 445 base::MessageLoop::current()->PostDelayedTask( 446 FROM_HERE, 447 base::Bind( 448 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings, 449 weak_ptr_factory_.GetWeakPtr()), 450 delay); 451 } 452 453 } // namespace net 454