Home | History | Annotate | Download | only in http
      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