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       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