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
15namespace net {
16
17namespace {
18
19const uint64 kBrokenAlternateProtocolDelaySecs = 300;
20
21}  // namespace
22
23HttpServerPropertiesImpl::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
36HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
37}
38
39void 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
52void 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
98void 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
106void 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
115void 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
134std::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
143static const AlternateProtocolInfo* g_forced_alternate_protocol = NULL;
144
145// static
146void 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
155void HttpServerPropertiesImpl::DisableForcedAlternateProtocol() {
156  delete g_forced_alternate_protocol;
157  g_forced_alternate_protocol = NULL;
158}
159
160base::WeakPtr<HttpServerProperties> HttpServerPropertiesImpl::GetWeakPtr() {
161  return weak_ptr_factory_.GetWeakPtr();
162}
163
164void 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
173bool 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
187void 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
205bool 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
218std::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
231AlternateProtocolInfo
232HttpServerPropertiesImpl::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
251void 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
307void 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
338bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken(
339    const HostPortPair& server) {
340  return ContainsKey(broken_alternate_protocol_map_, server);
341}
342
343void HttpServerPropertiesImpl::ConfirmAlternateProtocol(
344    const HostPortPair& server) {
345  broken_alternate_protocol_map_.erase(server);
346}
347
348void 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
357const AlternateProtocolMap&
358HttpServerPropertiesImpl::alternate_protocol_map() const {
359  return alternate_protocol_map_;
360}
361
362void HttpServerPropertiesImpl::SetAlternateProtocolExperiment(
363    AlternateProtocolExperiment experiment) {
364  alternate_protocol_experiment_ = experiment;
365}
366
367AlternateProtocolExperiment
368HttpServerPropertiesImpl::GetAlternateProtocolExperiment() const {
369  return alternate_protocol_experiment_;
370}
371
372const 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
382bool 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
403void 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
410void HttpServerPropertiesImpl::ClearAllSpdySettings() {
411  spdy_settings_map_.Clear();
412}
413
414const SpdySettingsMap&
415HttpServerPropertiesImpl::spdy_settings_map() const {
416  return spdy_settings_map_;
417}
418
419SupportsQuic 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
429void 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
437const SupportsQuicMap&
438HttpServerPropertiesImpl::supports_quic_map() const {
439  return supports_quic_map_;
440}
441
442void HttpServerPropertiesImpl::SetServerNetworkStats(
443    const HostPortPair& host_port_pair,
444    NetworkStats stats) {
445  server_network_stats_map_[host_port_pair] = stats;
446}
447
448const HttpServerProperties::NetworkStats*
449HttpServerPropertiesImpl::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
459void HttpServerPropertiesImpl::SetAlternateProtocolProbabilityThreshold(
460    double threshold) {
461  alternate_protocol_probability_threshold_ = threshold;
462}
463
464HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
465HttpServerPropertiesImpl::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
477void 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
489void 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
504void
505HttpServerPropertiesImpl::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