Record forced alternative service histogram.
[chromium-blink-merge.git] / net / http / http_server_properties_impl.cc
blob38ab18ee0ee6c64a35ec71a409db1c97c2d46c92
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.
5 #include "net/http/http_server_properties_impl.h"
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/metrics/histogram.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
16 namespace net {
18 namespace {
20 const uint64 kBrokenAlternateProtocolDelaySecs = 300;
22 } // namespace
24 HttpServerPropertiesImpl::HttpServerPropertiesImpl()
25 : spdy_servers_map_(SpdyServerHostPortMap::NO_AUTO_EVICT),
26 alternate_protocol_map_(AlternateProtocolMap::NO_AUTO_EVICT),
27 spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT),
28 server_network_stats_map_(ServerNetworkStatsMap::NO_AUTO_EVICT),
29 alternate_protocol_probability_threshold_(1),
30 weak_ptr_factory_(this) {
31 canonical_suffixes_.push_back(".c.youtube.com");
32 canonical_suffixes_.push_back(".googlevideo.com");
33 canonical_suffixes_.push_back(".googleusercontent.com");
36 HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
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);
52 void HttpServerPropertiesImpl::InitializeAlternateProtocolServers(
53 AlternateProtocolMap* alternate_protocol_map) {
54 // Keep all the broken ones since those don't get persisted.
55 for (AlternateProtocolMap::iterator it = alternate_protocol_map_.begin();
56 it != alternate_protocol_map_.end();) {
57 const AlternativeService alternative_service(
58 it->second.protocol, it->first.host(), it->second.port);
59 if (IsAlternativeServiceBroken(alternative_service)) {
60 ++it;
61 } else {
62 it = alternate_protocol_map_.Erase(it);
66 // Add the entries from persisted data.
67 for (AlternateProtocolMap::reverse_iterator it =
68 alternate_protocol_map->rbegin();
69 it != alternate_protocol_map->rend(); ++it) {
70 alternate_protocol_map_.Put(it->first, it->second);
73 // Attempt to find canonical servers.
74 uint16 canonical_ports[] = { 80, 443 };
75 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
76 std::string canonical_suffix = canonical_suffixes_[i];
77 for (size_t j = 0; j < arraysize(canonical_ports); ++j) {
78 HostPortPair canonical_host(canonical_suffix, canonical_ports[j]);
79 // If we already have a valid canonical server, we're done.
80 if (ContainsKey(canonical_host_to_origin_map_, canonical_host) &&
81 (alternate_protocol_map_.Peek(canonical_host_to_origin_map_[
82 canonical_host]) != alternate_protocol_map_.end())) {
83 continue;
85 // Now attempt to find a server which matches this origin and set it as
86 // canonical .
87 for (AlternateProtocolMap::const_iterator it =
88 alternate_protocol_map_.begin();
89 it != alternate_protocol_map_.end(); ++it) {
90 if (EndsWith(it->first.host(), canonical_suffixes_[i], false)) {
91 canonical_host_to_origin_map_[canonical_host] = it->first;
92 break;
99 void HttpServerPropertiesImpl::InitializeSpdySettingsServers(
100 SpdySettingsMap* spdy_settings_map) {
101 for (SpdySettingsMap::reverse_iterator it = spdy_settings_map->rbegin();
102 it != spdy_settings_map->rend(); ++it) {
103 spdy_settings_map_.Put(it->first, it->second);
107 void HttpServerPropertiesImpl::InitializeSupportsQuic(
108 IPAddressNumber* last_address) {
109 if (last_address)
110 last_quic_address_ = *last_address;
113 void HttpServerPropertiesImpl::InitializeServerNetworkStats(
114 ServerNetworkStatsMap* server_network_stats_map) {
115 for (ServerNetworkStatsMap::reverse_iterator it =
116 server_network_stats_map->rbegin();
117 it != server_network_stats_map->rend(); ++it) {
118 server_network_stats_map_.Put(it->first, it->second);
122 void HttpServerPropertiesImpl::GetSpdyServerList(
123 base::ListValue* spdy_server_list,
124 size_t max_size) const {
125 DCHECK(CalledOnValidThread());
126 DCHECK(spdy_server_list);
127 spdy_server_list->Clear();
128 size_t count = 0;
129 // Get the list of servers (host/port) that support SPDY.
130 for (SpdyServerHostPortMap::const_iterator it = spdy_servers_map_.begin();
131 it != spdy_servers_map_.end() && count < max_size; ++it) {
132 const std::string spdy_server_host_port = it->first;
133 if (it->second) {
134 spdy_server_list->Append(new base::StringValue(spdy_server_host_port));
135 ++count;
140 static const AlternateProtocolInfo* g_forced_alternate_protocol = NULL;
142 // static
143 void HttpServerPropertiesImpl::ForceAlternateProtocol(
144 const AlternateProtocolInfo& info) {
145 // Note: we're going to leak this.
146 if (g_forced_alternate_protocol)
147 delete g_forced_alternate_protocol;
148 g_forced_alternate_protocol = new AlternateProtocolInfo(info);
151 // static
152 void HttpServerPropertiesImpl::DisableForcedAlternateProtocol() {
153 delete g_forced_alternate_protocol;
154 g_forced_alternate_protocol = NULL;
157 base::WeakPtr<HttpServerProperties> HttpServerPropertiesImpl::GetWeakPtr() {
158 return weak_ptr_factory_.GetWeakPtr();
161 void HttpServerPropertiesImpl::Clear() {
162 DCHECK(CalledOnValidThread());
163 spdy_servers_map_.Clear();
164 alternate_protocol_map_.Clear();
165 canonical_host_to_origin_map_.clear();
166 spdy_settings_map_.Clear();
167 last_quic_address_.clear();
168 server_network_stats_map_.Clear();
171 bool HttpServerPropertiesImpl::SupportsRequestPriority(
172 const HostPortPair& host_port_pair) {
173 DCHECK(CalledOnValidThread());
174 if (host_port_pair.host().empty())
175 return false;
177 SpdyServerHostPortMap::iterator spdy_host_port =
178 spdy_servers_map_.Get(host_port_pair.ToString());
179 if (spdy_host_port != spdy_servers_map_.end() && spdy_host_port->second)
180 return true;
182 const AlternativeService alternative_service =
183 GetAlternativeService(host_port_pair);
184 return alternative_service.protocol == QUIC;
187 void HttpServerPropertiesImpl::SetSupportsSpdy(
188 const HostPortPair& host_port_pair,
189 bool support_spdy) {
190 DCHECK(CalledOnValidThread());
191 if (host_port_pair.host().empty())
192 return;
194 SpdyServerHostPortMap::iterator spdy_host_port =
195 spdy_servers_map_.Get(host_port_pair.ToString());
196 if ((spdy_host_port != spdy_servers_map_.end()) &&
197 (spdy_host_port->second == support_spdy)) {
198 return;
200 // Cache the data.
201 spdy_servers_map_.Put(host_port_pair.ToString(), support_spdy);
204 bool HttpServerPropertiesImpl::RequiresHTTP11(
205 const net::HostPortPair& host_port_pair) {
206 DCHECK(CalledOnValidThread());
207 if (host_port_pair.host().empty())
208 return false;
210 return (http11_servers_.find(host_port_pair) != http11_servers_.end());
213 void HttpServerPropertiesImpl::SetHTTP11Required(
214 const net::HostPortPair& host_port_pair) {
215 DCHECK(CalledOnValidThread());
216 if (host_port_pair.host().empty())
217 return;
219 http11_servers_.insert(host_port_pair);
222 void HttpServerPropertiesImpl::MaybeForceHTTP11(const HostPortPair& server,
223 SSLConfig* ssl_config) {
224 if (RequiresHTTP11(server)) {
225 ForceHTTP11(ssl_config);
229 std::string HttpServerPropertiesImpl::GetCanonicalSuffix(
230 const std::string& host) {
231 // If this host ends with a canonical suffix, then return the canonical
232 // suffix.
233 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
234 std::string canonical_suffix = canonical_suffixes_[i];
235 if (EndsWith(host, canonical_suffixes_[i], false)) {
236 return canonical_suffix;
239 return std::string();
242 AlternativeService HttpServerPropertiesImpl::GetAlternativeService(
243 const HostPortPair& origin) {
244 AlternateProtocolMap::const_iterator it =
245 GetAlternateProtocolIterator(origin);
246 if (it != alternate_protocol_map_.end() &&
247 it->second.probability >= alternate_protocol_probability_threshold_)
248 return AlternativeService(it->second.protocol, origin.host(),
249 it->second.port);
251 UMA_HISTOGRAM_BOOLEAN("Net.ForceAlternativeService",
252 g_forced_alternate_protocol != nullptr);
253 if (g_forced_alternate_protocol)
254 return AlternativeService(g_forced_alternate_protocol->protocol,
255 origin.host(), g_forced_alternate_protocol->port);
257 AlternativeService uninitialize_alternative_service;
258 return uninitialize_alternative_service;
261 void HttpServerPropertiesImpl::SetAlternateProtocol(
262 const HostPortPair& origin,
263 uint16 alternate_port,
264 AlternateProtocol alternate_protocol,
265 double alternate_probability) {
266 const AlternativeService alternative_service(alternate_protocol,
267 origin.host(), alternate_port);
268 if (IsAlternativeServiceBroken(alternative_service)) {
269 DVLOG(1) << "Ignore alternative service since it is known to be broken.";
270 return;
273 const AlternateProtocolInfo alternate(alternate_port, alternate_protocol,
274 alternate_probability);
275 AlternateProtocolMap::const_iterator it =
276 GetAlternateProtocolIterator(origin);
277 if (it != alternate_protocol_map_.end()) {
278 const AlternateProtocolInfo existing_alternate = it->second;
280 if (!existing_alternate.Equals(alternate)) {
281 LOG(WARNING) << "Changing the alternate protocol for: "
282 << origin.ToString()
283 << " from [Port: " << existing_alternate.port
284 << ", Protocol: " << existing_alternate.protocol
285 << ", Probability: " << existing_alternate.probability
286 << "] to [Port: " << alternate_port
287 << ", Protocol: " << alternate_protocol
288 << ", Probability: " << alternate_probability << "].";
290 } else {
291 if (alternate_probability >= alternate_protocol_probability_threshold_) {
292 // TODO(rch): Consider the case where multiple requests are started
293 // before the first completes. In this case, only one of the jobs
294 // would reach this code, whereas all of them should should have.
295 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING);
299 alternate_protocol_map_.Put(origin, alternate);
301 // If this host ends with a canonical suffix, then set it as the
302 // canonical host.
303 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
304 std::string canonical_suffix = canonical_suffixes_[i];
305 if (EndsWith(origin.host(), canonical_suffixes_[i], false)) {
306 HostPortPair canonical_host(canonical_suffix, origin.port());
307 canonical_host_to_origin_map_[canonical_host] = origin;
308 break;
313 void HttpServerPropertiesImpl::SetBrokenAlternateProtocol(
314 const HostPortPair& origin) {
315 const AlternativeService alternative_service = GetAlternativeService(origin);
316 if (alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL) {
317 LOG(DFATAL) << "Trying to mark unknown alternate protocol broken.";
318 return;
320 int count = ++recently_broken_alternative_services_[alternative_service];
321 base::TimeDelta delay =
322 base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs);
323 base::TimeTicks when = base::TimeTicks::Now() + delay * (1 << (count - 1));
324 auto result = broken_alternative_services_.insert(
325 std::make_pair(alternative_service, when));
326 // Return if alternative service is already in expiration queue.
327 if (!result.second) {
328 return;
331 // Do not leave this host as canonical so that we don't infer the other
332 // hosts are also broken without testing them first.
333 RemoveCanonicalHost(origin);
335 // If this is the only entry in the list, schedule an expiration task.
336 // Otherwise it will be rescheduled automatically when the pending task runs.
337 if (broken_alternative_services_.size() == 1) {
338 ScheduleBrokenAlternateProtocolMappingsExpiration();
342 void HttpServerPropertiesImpl::MarkAlternativeServiceRecentlyBroken(
343 const AlternativeService& alternative_service) {
344 if (!ContainsKey(recently_broken_alternative_services_, alternative_service))
345 recently_broken_alternative_services_[alternative_service] = 1;
348 bool HttpServerPropertiesImpl::IsAlternativeServiceBroken(
349 const AlternativeService& alternative_service) {
350 return ContainsKey(broken_alternative_services_, alternative_service);
353 bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken(
354 const HostPortPair& origin) {
355 const AlternativeService alternative_service = GetAlternativeService(origin);
356 if (alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL)
357 return false;
358 return ContainsKey(recently_broken_alternative_services_,
359 alternative_service);
362 void HttpServerPropertiesImpl::ConfirmAlternateProtocol(
363 const HostPortPair& origin) {
364 const AlternativeService alternative_service = GetAlternativeService(origin);
365 if (alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL)
366 return;
367 broken_alternative_services_.erase(alternative_service);
368 recently_broken_alternative_services_.erase(alternative_service);
371 void HttpServerPropertiesImpl::ClearAlternateProtocol(
372 const HostPortPair& origin) {
373 RemoveCanonicalHost(origin);
375 AlternateProtocolMap::iterator it = alternate_protocol_map_.Peek(origin);
376 if (it == alternate_protocol_map_.end()) {
377 return;
379 const AlternativeService alternative_service(
380 it->second.protocol, it->first.host(), it->second.port);
381 alternate_protocol_map_.Erase(it);
383 // The following is temporary to keep the existing semantics, which is that if
384 // there is a broken alternative service in the mapping, then this method
385 // leaves it in a non-broken, but recently broken state.
387 // TODO(bnc):
388 // 1. Verify and document the class invariant that no broken alternative
389 // service can be in the mapping.
390 // 2. Remove the rest of this method as it will be moot.
391 // 3. Provide a SetAlternativeServiceRecentlyBroken if necessary.
392 broken_alternative_services_.erase(alternative_service);
395 const AlternateProtocolMap&
396 HttpServerPropertiesImpl::alternate_protocol_map() const {
397 return alternate_protocol_map_;
400 const SettingsMap& HttpServerPropertiesImpl::GetSpdySettings(
401 const HostPortPair& host_port_pair) {
402 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
403 if (it == spdy_settings_map_.end()) {
404 CR_DEFINE_STATIC_LOCAL(SettingsMap, kEmptySettingsMap, ());
405 return kEmptySettingsMap;
407 return it->second;
410 bool HttpServerPropertiesImpl::SetSpdySetting(
411 const HostPortPair& host_port_pair,
412 SpdySettingsIds id,
413 SpdySettingsFlags flags,
414 uint32 value) {
415 if (!(flags & SETTINGS_FLAG_PLEASE_PERSIST))
416 return false;
418 SettingsFlagsAndValue flags_and_value(SETTINGS_FLAG_PERSISTED, value);
419 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
420 if (it == spdy_settings_map_.end()) {
421 SettingsMap settings_map;
422 settings_map[id] = flags_and_value;
423 spdy_settings_map_.Put(host_port_pair, settings_map);
424 } else {
425 SettingsMap& settings_map = it->second;
426 settings_map[id] = flags_and_value;
428 return true;
431 void HttpServerPropertiesImpl::ClearSpdySettings(
432 const HostPortPair& host_port_pair) {
433 SpdySettingsMap::iterator it = spdy_settings_map_.Peek(host_port_pair);
434 if (it != spdy_settings_map_.end())
435 spdy_settings_map_.Erase(it);
438 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
439 spdy_settings_map_.Clear();
442 const SpdySettingsMap&
443 HttpServerPropertiesImpl::spdy_settings_map() const {
444 return spdy_settings_map_;
447 bool HttpServerPropertiesImpl::GetSupportsQuic(
448 IPAddressNumber* last_address) const {
449 if (last_quic_address_.empty())
450 return false;
452 *last_address = last_quic_address_;
453 return true;
456 void HttpServerPropertiesImpl::SetSupportsQuic(bool used_quic,
457 const IPAddressNumber& address) {
458 if (!used_quic) {
459 last_quic_address_.clear();
460 } else {
461 last_quic_address_ = address;
465 void HttpServerPropertiesImpl::SetServerNetworkStats(
466 const HostPortPair& host_port_pair,
467 ServerNetworkStats stats) {
468 server_network_stats_map_.Put(host_port_pair, stats);
471 const ServerNetworkStats* HttpServerPropertiesImpl::GetServerNetworkStats(
472 const HostPortPair& host_port_pair) {
473 ServerNetworkStatsMap::iterator it =
474 server_network_stats_map_.Get(host_port_pair);
475 if (it == server_network_stats_map_.end()) {
476 return NULL;
478 return &it->second;
481 const ServerNetworkStatsMap&
482 HttpServerPropertiesImpl::server_network_stats_map() const {
483 return server_network_stats_map_;
486 void HttpServerPropertiesImpl::SetAlternateProtocolProbabilityThreshold(
487 double threshold) {
488 alternate_protocol_probability_threshold_ = threshold;
491 AlternateProtocolMap::const_iterator
492 HttpServerPropertiesImpl::GetAlternateProtocolIterator(
493 const HostPortPair& server) {
494 AlternateProtocolMap::const_iterator it = alternate_protocol_map_.Get(server);
495 if (it != alternate_protocol_map_.end())
496 return it;
498 CanonicalHostMap::const_iterator canonical = GetCanonicalHost(server);
499 if (canonical != canonical_host_to_origin_map_.end())
500 return alternate_protocol_map_.Get(canonical->second);
502 return alternate_protocol_map_.end();
505 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
506 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server) const {
507 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
508 std::string canonical_suffix = canonical_suffixes_[i];
509 if (EndsWith(server.host(), canonical_suffixes_[i], false)) {
510 HostPortPair canonical_host(canonical_suffix, server.port());
511 return canonical_host_to_origin_map_.find(canonical_host);
515 return canonical_host_to_origin_map_.end();
518 void HttpServerPropertiesImpl::RemoveCanonicalHost(
519 const HostPortPair& server) {
520 CanonicalHostMap::const_iterator canonical = GetCanonicalHost(server);
521 if (canonical == canonical_host_to_origin_map_.end())
522 return;
524 if (!canonical->second.Equals(server))
525 return;
527 canonical_host_to_origin_map_.erase(canonical->first);
530 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() {
531 base::TimeTicks now = base::TimeTicks::Now();
532 while (!broken_alternative_services_.empty()) {
533 BrokenAlternativeServices::iterator it =
534 broken_alternative_services_.begin();
535 if (now < it->second) {
536 break;
539 const AlternativeService alternative_service = it->first;
540 broken_alternative_services_.erase(it);
541 ClearAlternateProtocol(
542 HostPortPair(alternative_service.host, alternative_service.port));
544 ScheduleBrokenAlternateProtocolMappingsExpiration();
547 void
548 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
549 if (broken_alternative_services_.empty()) {
550 return;
552 base::TimeTicks now = base::TimeTicks::Now();
553 base::TimeTicks when = broken_alternative_services_.front().second;
554 base::TimeDelta delay = when > now ? when - now : base::TimeDelta();
555 base::MessageLoop::current()->PostDelayedTask(
556 FROM_HERE,
557 base::Bind(
558 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings,
559 weak_ptr_factory_.GetWeakPtr()),
560 delay);
563 } // namespace net