• 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       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 
~HttpServerPropertiesImpl()35 HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
36 }
37 
InitializeSpdyServers(std::vector<std::string> * spdy_servers,bool support_spdy)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 
InitializeAlternateProtocolServers(AlternateProtocolMap * alternate_protocol_map)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 
InitializeSpdySettingsServers(SpdySettingsMap * spdy_settings_map)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 
GetSpdyServerList(base::ListValue * spdy_server_list,size_t max_size) const105 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
GetFlattenedSpdyServer(const net::HostPortPair & host_port_pair)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
ForceAlternateProtocol(const PortAlternateProtocolPair & pair)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
DisableForcedAlternateProtocol()145 void HttpServerPropertiesImpl::DisableForcedAlternateProtocol() {
146   delete g_forced_alternate_protocol;
147   g_forced_alternate_protocol = NULL;
148 }
149 
GetWeakPtr()150 base::WeakPtr<HttpServerProperties> HttpServerPropertiesImpl::GetWeakPtr() {
151   return weak_ptr_factory_.GetWeakPtr();
152 }
153 
Clear()154 void HttpServerPropertiesImpl::Clear() {
155   DCHECK(CalledOnValidThread());
156   spdy_servers_map_.Clear();
157   alternate_protocol_map_.Clear();
158   spdy_settings_map_.Clear();
159 }
160 
SupportsSpdy(const net::HostPortPair & host_port_pair)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 
SetSupportsSpdy(const net::HostPortPair & host_port_pair,bool support_spdy)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 
HasAlternateProtocol(const HostPortPair & server)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 
GetCanonicalSuffix(const HostPortPair & server)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
GetAlternateProtocol(const HostPortPair & server)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 
SetAlternateProtocol(const HostPortPair & server,uint16 alternate_port,AlternateProtocol alternate_protocol)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 
SetBrokenAlternateProtocol(const HostPortPair & server)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 
WasAlternateProtocolRecentlyBroken(const HostPortPair & server)313 bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken(
314     const HostPortPair& server) {
315   return ContainsKey(broken_alternate_protocol_map_, server);
316 }
317 
ConfirmAlternateProtocol(const HostPortPair & server)318 void HttpServerPropertiesImpl::ConfirmAlternateProtocol(
319     const HostPortPair& server) {
320   broken_alternate_protocol_map_.erase(server);
321 }
322 
ClearAlternateProtocol(const HostPortPair & server)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&
alternate_protocol_map() const331 HttpServerPropertiesImpl::alternate_protocol_map() const {
332   return alternate_protocol_map_;
333 }
334 
SetAlternateProtocolExperiment(AlternateProtocolExperiment experiment)335 void HttpServerPropertiesImpl::SetAlternateProtocolExperiment(
336     AlternateProtocolExperiment experiment) {
337   alternate_protocol_experiment_ = experiment;
338 }
339 
340 AlternateProtocolExperiment
GetAlternateProtocolExperiment() const341 HttpServerPropertiesImpl::GetAlternateProtocolExperiment() const {
342   return alternate_protocol_experiment_;
343 }
344 
GetSpdySettings(const HostPortPair & host_port_pair)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 
SetSpdySetting(const HostPortPair & host_port_pair,SpdySettingsIds id,SpdySettingsFlags flags,uint32 value)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 
ClearSpdySettings(const HostPortPair & host_port_pair)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 
ClearAllSpdySettings()383 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
384   spdy_settings_map_.Clear();
385 }
386 
387 const SpdySettingsMap&
spdy_settings_map() const388 HttpServerPropertiesImpl::spdy_settings_map() const {
389   return spdy_settings_map_;
390 }
391 
SetServerNetworkStats(const HostPortPair & host_port_pair,NetworkStats stats)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*
GetServerNetworkStats(const HostPortPair & host_port_pair) const399 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
GetCanonicalHost(HostPortPair server) const410 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 
ExpireBrokenAlternateProtocolMappings()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
ScheduleBrokenAlternateProtocolMappingsExpiration()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