• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
HttpServerPropertiesImpl()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 
~HttpServerPropertiesImpl()36 HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
37 }
38 
InitializeSpdyServers(std::vector<std::string> * spdy_servers,bool support_spdy)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 
InitializeAlternateProtocolServers(AlternateProtocolMap * alternate_protocol_map)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 
InitializeSpdySettingsServers(SpdySettingsMap * spdy_settings_map)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 
InitializeSupportsQuic(SupportsQuicMap * supports_quic_map)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 
GetSpdyServerList(base::ListValue * spdy_server_list,size_t max_size) const115 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
GetFlattenedSpdyServer(const net::HostPortPair & host_port_pair)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
ForceAlternateProtocol(const AlternateProtocolInfo & info)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
DisableForcedAlternateProtocol()155 void HttpServerPropertiesImpl::DisableForcedAlternateProtocol() {
156   delete g_forced_alternate_protocol;
157   g_forced_alternate_protocol = NULL;
158 }
159 
GetWeakPtr()160 base::WeakPtr<HttpServerProperties> HttpServerPropertiesImpl::GetWeakPtr() {
161   return weak_ptr_factory_.GetWeakPtr();
162 }
163 
Clear()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 
SupportsSpdy(const net::HostPortPair & host_port_pair)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 
SetSupportsSpdy(const net::HostPortPair & host_port_pair,bool support_spdy)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 
HasAlternateProtocol(const HostPortPair & server)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 
GetCanonicalSuffix(const HostPortPair & server)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
GetAlternateProtocol(const HostPortPair & server)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 
SetAlternateProtocol(const HostPortPair & server,uint16 alternate_port,AlternateProtocol alternate_protocol,double alternate_probability)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 
SetBrokenAlternateProtocol(const HostPortPair & server)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 
WasAlternateProtocolRecentlyBroken(const HostPortPair & server)338 bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken(
339     const HostPortPair& server) {
340   return ContainsKey(broken_alternate_protocol_map_, server);
341 }
342 
ConfirmAlternateProtocol(const HostPortPair & server)343 void HttpServerPropertiesImpl::ConfirmAlternateProtocol(
344     const HostPortPair& server) {
345   broken_alternate_protocol_map_.erase(server);
346 }
347 
ClearAlternateProtocol(const HostPortPair & server)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&
alternate_protocol_map() const358 HttpServerPropertiesImpl::alternate_protocol_map() const {
359   return alternate_protocol_map_;
360 }
361 
SetAlternateProtocolExperiment(AlternateProtocolExperiment experiment)362 void HttpServerPropertiesImpl::SetAlternateProtocolExperiment(
363     AlternateProtocolExperiment experiment) {
364   alternate_protocol_experiment_ = experiment;
365 }
366 
367 AlternateProtocolExperiment
GetAlternateProtocolExperiment() const368 HttpServerPropertiesImpl::GetAlternateProtocolExperiment() const {
369   return alternate_protocol_experiment_;
370 }
371 
GetSpdySettings(const HostPortPair & host_port_pair)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 
SetSpdySetting(const HostPortPair & host_port_pair,SpdySettingsIds id,SpdySettingsFlags flags,uint32 value)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 
ClearSpdySettings(const HostPortPair & host_port_pair)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 
ClearAllSpdySettings()410 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
411   spdy_settings_map_.Clear();
412 }
413 
414 const SpdySettingsMap&
spdy_settings_map() const415 HttpServerPropertiesImpl::spdy_settings_map() const {
416   return spdy_settings_map_;
417 }
418 
GetSupportsQuic(const HostPortPair & host_port_pair) const419 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 
SetSupportsQuic(const HostPortPair & host_port_pair,bool used_quic,const std::string & address)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&
supports_quic_map() const438 HttpServerPropertiesImpl::supports_quic_map() const {
439   return supports_quic_map_;
440 }
441 
SetServerNetworkStats(const HostPortPair & host_port_pair,NetworkStats stats)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*
GetServerNetworkStats(const HostPortPair & host_port_pair) const449 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 
SetAlternateProtocolProbabilityThreshold(double threshold)459 void HttpServerPropertiesImpl::SetAlternateProtocolProbabilityThreshold(
460     double threshold) {
461   alternate_protocol_probability_threshold_ = threshold;
462 }
463 
464 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
GetCanonicalHost(HostPortPair server) const465 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 
RemoveCanonicalHost(const HostPortPair & server)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 
ExpireBrokenAlternateProtocolMappings()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
ScheduleBrokenAlternateProtocolMappingsExpiration()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