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