Move StartsWith[ASCII] to base namespace.
[chromium-blink-merge.git] / net / http / http_server_properties_impl.cc
blobd5c65593f9b89215a5c3d7dbcdb0355a7dcf2c46
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/location.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/metrics/histogram.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "base/values.h"
19 namespace net {
21 namespace {
23 const uint64 kBrokenAlternativeProtocolDelaySecs = 300;
25 } // namespace
27 HttpServerPropertiesImpl::HttpServerPropertiesImpl()
28 : spdy_servers_map_(SpdyServerHostPortMap::NO_AUTO_EVICT),
29 alternative_service_map_(AlternativeServiceMap::NO_AUTO_EVICT),
30 spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT),
31 server_network_stats_map_(ServerNetworkStatsMap::NO_AUTO_EVICT),
32 alternative_service_probability_threshold_(1.0),
33 weak_ptr_factory_(this) {
34 canonical_suffixes_.push_back(".c.youtube.com");
35 canonical_suffixes_.push_back(".googlevideo.com");
36 canonical_suffixes_.push_back(".googleusercontent.com");
39 HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
42 void HttpServerPropertiesImpl::InitializeSpdyServers(
43 std::vector<std::string>* spdy_servers,
44 bool support_spdy) {
45 DCHECK(CalledOnValidThread());
46 if (!spdy_servers)
47 return;
48 // Add the entries from persisted data.
49 for (std::vector<std::string>::reverse_iterator it = spdy_servers->rbegin();
50 it != spdy_servers->rend(); ++it) {
51 spdy_servers_map_.Put(*it, support_spdy);
55 void HttpServerPropertiesImpl::InitializeAlternativeServiceServers(
56 AlternativeServiceMap* alternative_service_map) {
57 // Keep all the broken ones since those don't get persisted.
58 for (AlternativeServiceMap::iterator it = alternative_service_map_.begin();
59 it != alternative_service_map_.end();) {
60 if (IsAlternativeServiceBroken(it->second.alternative_service)) {
61 ++it;
62 } else {
63 it = alternative_service_map_.Erase(it);
67 // Add the entries from persisted data.
68 for (AlternativeServiceMap::reverse_iterator it =
69 alternative_service_map->rbegin();
70 it != alternative_service_map->rend(); ++it) {
71 alternative_service_map_.Put(it->first, it->second);
74 // Attempt to find canonical servers.
75 uint16 canonical_ports[] = { 80, 443 };
76 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
77 std::string canonical_suffix = canonical_suffixes_[i];
78 for (size_t j = 0; j < arraysize(canonical_ports); ++j) {
79 HostPortPair canonical_host(canonical_suffix, canonical_ports[j]);
80 // If we already have a valid canonical server, we're done.
81 if (ContainsKey(canonical_host_to_origin_map_, canonical_host) &&
82 (alternative_service_map_.Peek(
83 canonical_host_to_origin_map_[canonical_host]) !=
84 alternative_service_map_.end())) {
85 continue;
87 // Now attempt to find a server which matches this origin and set it as
88 // canonical.
89 for (AlternativeServiceMap::const_iterator it =
90 alternative_service_map_.begin();
91 it != alternative_service_map_.end(); ++it) {
92 if (EndsWith(it->first.host(), canonical_suffixes_[i], false)) {
93 canonical_host_to_origin_map_[canonical_host] = it->first;
94 break;
101 void HttpServerPropertiesImpl::InitializeSpdySettingsServers(
102 SpdySettingsMap* spdy_settings_map) {
103 for (SpdySettingsMap::reverse_iterator it = spdy_settings_map->rbegin();
104 it != spdy_settings_map->rend(); ++it) {
105 spdy_settings_map_.Put(it->first, it->second);
109 void HttpServerPropertiesImpl::InitializeSupportsQuic(
110 IPAddressNumber* last_address) {
111 if (last_address)
112 last_quic_address_ = *last_address;
115 void HttpServerPropertiesImpl::InitializeServerNetworkStats(
116 ServerNetworkStatsMap* server_network_stats_map) {
117 for (ServerNetworkStatsMap::reverse_iterator it =
118 server_network_stats_map->rbegin();
119 it != server_network_stats_map->rend(); ++it) {
120 server_network_stats_map_.Put(it->first, it->second);
124 void HttpServerPropertiesImpl::GetSpdyServerList(
125 base::ListValue* spdy_server_list,
126 size_t max_size) const {
127 DCHECK(CalledOnValidThread());
128 DCHECK(spdy_server_list);
129 spdy_server_list->Clear();
130 size_t count = 0;
131 // Get the list of servers (host/port) that support SPDY.
132 for (SpdyServerHostPortMap::const_iterator it = spdy_servers_map_.begin();
133 it != spdy_servers_map_.end() && count < max_size; ++it) {
134 const std::string spdy_server_host_port = it->first;
135 if (it->second) {
136 spdy_server_list->Append(new base::StringValue(spdy_server_host_port));
137 ++count;
142 base::WeakPtr<HttpServerProperties> HttpServerPropertiesImpl::GetWeakPtr() {
143 return weak_ptr_factory_.GetWeakPtr();
146 void HttpServerPropertiesImpl::Clear() {
147 DCHECK(CalledOnValidThread());
148 spdy_servers_map_.Clear();
149 alternative_service_map_.Clear();
150 canonical_host_to_origin_map_.clear();
151 spdy_settings_map_.Clear();
152 last_quic_address_.clear();
153 server_network_stats_map_.Clear();
156 bool HttpServerPropertiesImpl::SupportsRequestPriority(
157 const HostPortPair& host_port_pair) {
158 DCHECK(CalledOnValidThread());
159 if (host_port_pair.host().empty())
160 return false;
162 if (GetSupportsSpdy(host_port_pair))
163 return true;
165 const AlternativeService alternative_service =
166 GetAlternativeService(host_port_pair);
167 return alternative_service.protocol == QUIC;
170 bool HttpServerPropertiesImpl::GetSupportsSpdy(
171 const HostPortPair& host_port_pair) {
172 DCHECK(CalledOnValidThread());
173 if (host_port_pair.host().empty())
174 return false;
176 SpdyServerHostPortMap::iterator spdy_host_port =
177 spdy_servers_map_.Get(host_port_pair.ToString());
178 return spdy_host_port != spdy_servers_map_.end() && spdy_host_port->second;
181 void HttpServerPropertiesImpl::SetSupportsSpdy(
182 const HostPortPair& host_port_pair,
183 bool support_spdy) {
184 DCHECK(CalledOnValidThread());
185 if (host_port_pair.host().empty())
186 return;
188 SpdyServerHostPortMap::iterator spdy_host_port =
189 spdy_servers_map_.Get(host_port_pair.ToString());
190 if ((spdy_host_port != spdy_servers_map_.end()) &&
191 (spdy_host_port->second == support_spdy)) {
192 return;
194 // Cache the data.
195 spdy_servers_map_.Put(host_port_pair.ToString(), support_spdy);
198 bool HttpServerPropertiesImpl::RequiresHTTP11(
199 const HostPortPair& host_port_pair) {
200 DCHECK(CalledOnValidThread());
201 if (host_port_pair.host().empty())
202 return false;
204 return (http11_servers_.find(host_port_pair) != http11_servers_.end());
207 void HttpServerPropertiesImpl::SetHTTP11Required(
208 const HostPortPair& host_port_pair) {
209 DCHECK(CalledOnValidThread());
210 if (host_port_pair.host().empty())
211 return;
213 http11_servers_.insert(host_port_pair);
216 void HttpServerPropertiesImpl::MaybeForceHTTP11(const HostPortPair& server,
217 SSLConfig* ssl_config) {
218 if (RequiresHTTP11(server)) {
219 ForceHTTP11(ssl_config);
223 std::string HttpServerPropertiesImpl::GetCanonicalSuffix(
224 const std::string& host) {
225 // If this host ends with a canonical suffix, then return the canonical
226 // suffix.
227 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
228 std::string canonical_suffix = canonical_suffixes_[i];
229 if (EndsWith(host, canonical_suffixes_[i], false)) {
230 return canonical_suffix;
233 return std::string();
236 AlternativeService HttpServerPropertiesImpl::GetAlternativeService(
237 const HostPortPair& origin) {
238 AlternativeServiceMap::const_iterator it =
239 alternative_service_map_.Get(origin);
240 if (it != alternative_service_map_.end()) {
241 if (it->second.probability < alternative_service_probability_threshold_) {
242 return AlternativeService();
244 AlternativeService alternative_service(it->second.alternative_service);
245 if (alternative_service.host.empty()) {
246 alternative_service.host = origin.host();
248 return alternative_service;
251 CanonicalHostMap::const_iterator canonical = GetCanonicalHost(origin);
252 if (canonical == canonical_host_to_origin_map_.end()) {
253 return AlternativeService();
255 it = alternative_service_map_.Get(canonical->second);
256 if (it == alternative_service_map_.end()) {
257 return AlternativeService();
259 if (it->second.probability < alternative_service_probability_threshold_) {
260 return AlternativeService();
262 AlternativeService alternative_service(it->second.alternative_service);
263 if (alternative_service.host.empty()) {
264 alternative_service.host = canonical->second.host();
266 if (IsAlternativeServiceBroken(alternative_service)) {
267 RemoveCanonicalHost(canonical->second);
268 return AlternativeService();
270 // Empty hostname: if alternative service for with hostname of canonical host
271 // is not broken, then return alternative service with hostname of origin.
272 if (it->second.alternative_service.host.empty()) {
273 alternative_service.host = origin.host();
275 return alternative_service;
278 void HttpServerPropertiesImpl::SetAlternativeService(
279 const HostPortPair& origin,
280 const AlternativeService& alternative_service,
281 double alternative_probability) {
282 AlternativeService complete_alternative_service(alternative_service);
283 if (complete_alternative_service.host.empty()) {
284 complete_alternative_service.host = origin.host();
286 if (IsAlternativeServiceBroken(complete_alternative_service)) {
287 DVLOG(1) << "Ignore alternative service since it is known to be broken.";
288 return;
291 const AlternativeServiceInfo alternative_service_info(
292 alternative_service, alternative_probability);
293 AlternativeServiceMap::const_iterator it =
294 GetAlternateProtocolIterator(origin);
295 if (it != alternative_service_map_.end()) {
296 const AlternativeServiceInfo existing_alternative_service_info = it->second;
297 if (existing_alternative_service_info != alternative_service_info) {
298 LOG(WARNING) << "Changing the alternative service for: "
299 << origin.ToString() << " from "
300 << existing_alternative_service_info.ToString() << " to "
301 << alternative_service_info.ToString() << ".";
303 } else {
304 if (alternative_probability >= alternative_service_probability_threshold_) {
305 // TODO(rch): Consider the case where multiple requests are started
306 // before the first completes. In this case, only one of the jobs
307 // would reach this code, whereas all of them should should have.
308 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING);
312 alternative_service_map_.Put(origin, alternative_service_info);
314 // If this host ends with a canonical suffix, then set it as the
315 // canonical host.
316 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
317 std::string canonical_suffix = canonical_suffixes_[i];
318 if (EndsWith(origin.host(), canonical_suffixes_[i], false)) {
319 HostPortPair canonical_host(canonical_suffix, origin.port());
320 canonical_host_to_origin_map_[canonical_host] = origin;
321 break;
326 void HttpServerPropertiesImpl::MarkAlternativeServiceBroken(
327 const AlternativeService& alternative_service) {
328 if (alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL) {
329 LOG(DFATAL) << "Trying to mark unknown alternate protocol broken.";
330 return;
332 int count = ++recently_broken_alternative_services_[alternative_service];
333 base::TimeDelta delay =
334 base::TimeDelta::FromSeconds(kBrokenAlternativeProtocolDelaySecs);
335 base::TimeTicks when = base::TimeTicks::Now() + delay * (1 << (count - 1));
336 auto result = broken_alternative_services_.insert(
337 std::make_pair(alternative_service, when));
338 // Return if alternative service is already in expiration queue.
339 if (!result.second) {
340 return;
343 // If this is the only entry in the list, schedule an expiration task.
344 // Otherwise it will be rescheduled automatically when the pending task runs.
345 if (broken_alternative_services_.size() == 1) {
346 ScheduleBrokenAlternateProtocolMappingsExpiration();
350 void HttpServerPropertiesImpl::MarkAlternativeServiceRecentlyBroken(
351 const AlternativeService& alternative_service) {
352 if (!ContainsKey(recently_broken_alternative_services_, alternative_service))
353 recently_broken_alternative_services_[alternative_service] = 1;
356 bool HttpServerPropertiesImpl::IsAlternativeServiceBroken(
357 const AlternativeService& alternative_service) const {
358 // Empty host means use host of origin, callers are supposed to substitute.
359 DCHECK(!alternative_service.host.empty());
360 return ContainsKey(broken_alternative_services_, alternative_service);
363 bool HttpServerPropertiesImpl::WasAlternativeServiceRecentlyBroken(
364 const AlternativeService& alternative_service) {
365 if (alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL)
366 return false;
367 return ContainsKey(recently_broken_alternative_services_,
368 alternative_service);
371 void HttpServerPropertiesImpl::ConfirmAlternativeService(
372 const AlternativeService& alternative_service) {
373 if (alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL)
374 return;
375 broken_alternative_services_.erase(alternative_service);
376 recently_broken_alternative_services_.erase(alternative_service);
379 void HttpServerPropertiesImpl::ClearAlternativeService(
380 const HostPortPair& origin) {
381 RemoveCanonicalHost(origin);
383 AlternativeServiceMap::iterator it = alternative_service_map_.Peek(origin);
384 if (it == alternative_service_map_.end()) {
385 return;
387 AlternativeService alternative_service(it->second.alternative_service);
388 if (alternative_service.host.empty()) {
389 alternative_service.host = origin.host();
391 alternative_service_map_.Erase(it);
393 // The following is temporary to keep the existing semantics, which is that if
394 // there is a broken alternative service in the mapping, then this method
395 // leaves it in a non-broken, but recently broken state.
397 // TODO(bnc):
398 // 1. Verify and document the class invariant that no broken alternative
399 // service can be in the mapping.
400 // 2. Remove the rest of this method as it will be moot.
401 broken_alternative_services_.erase(alternative_service);
404 const AlternativeServiceMap& HttpServerPropertiesImpl::alternative_service_map()
405 const {
406 return alternative_service_map_;
409 scoped_ptr<base::Value>
410 HttpServerPropertiesImpl::GetAlternativeServiceInfoAsValue()
411 const {
412 scoped_ptr<base::ListValue> dict_list(new base::ListValue());
413 for (const auto& alternative_service_map_item : alternative_service_map_) {
414 const HostPortPair& host_port_pair = alternative_service_map_item.first;
415 const AlternativeServiceInfo& alternative_service_info =
416 alternative_service_map_item.second;
417 std::string alternative_service_string(alternative_service_info.ToString());
418 AlternativeService alternative_service(
419 alternative_service_info.alternative_service);
420 if (alternative_service.host.empty()) {
421 alternative_service.host = host_port_pair.host();
423 if (IsAlternativeServiceBroken(alternative_service)) {
424 alternative_service_string.append(" (broken)");
427 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
428 dict->SetString("host_port_pair", host_port_pair.ToString());
429 dict->SetString("alternative_service", alternative_service_string);
430 dict_list->Append(dict.Pass());
432 return dict_list.Pass();
435 const SettingsMap& HttpServerPropertiesImpl::GetSpdySettings(
436 const HostPortPair& host_port_pair) {
437 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
438 if (it == spdy_settings_map_.end()) {
439 CR_DEFINE_STATIC_LOCAL(SettingsMap, kEmptySettingsMap, ());
440 return kEmptySettingsMap;
442 return it->second;
445 bool HttpServerPropertiesImpl::SetSpdySetting(
446 const HostPortPair& host_port_pair,
447 SpdySettingsIds id,
448 SpdySettingsFlags flags,
449 uint32 value) {
450 if (!(flags & SETTINGS_FLAG_PLEASE_PERSIST))
451 return false;
453 SettingsFlagsAndValue flags_and_value(SETTINGS_FLAG_PERSISTED, value);
454 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
455 if (it == spdy_settings_map_.end()) {
456 SettingsMap settings_map;
457 settings_map[id] = flags_and_value;
458 spdy_settings_map_.Put(host_port_pair, settings_map);
459 } else {
460 SettingsMap& settings_map = it->second;
461 settings_map[id] = flags_and_value;
463 return true;
466 void HttpServerPropertiesImpl::ClearSpdySettings(
467 const HostPortPair& host_port_pair) {
468 SpdySettingsMap::iterator it = spdy_settings_map_.Peek(host_port_pair);
469 if (it != spdy_settings_map_.end())
470 spdy_settings_map_.Erase(it);
473 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
474 spdy_settings_map_.Clear();
477 const SpdySettingsMap&
478 HttpServerPropertiesImpl::spdy_settings_map() const {
479 return spdy_settings_map_;
482 bool HttpServerPropertiesImpl::GetSupportsQuic(
483 IPAddressNumber* last_address) const {
484 if (last_quic_address_.empty())
485 return false;
487 *last_address = last_quic_address_;
488 return true;
491 void HttpServerPropertiesImpl::SetSupportsQuic(bool used_quic,
492 const IPAddressNumber& address) {
493 if (!used_quic) {
494 last_quic_address_.clear();
495 } else {
496 last_quic_address_ = address;
500 void HttpServerPropertiesImpl::SetServerNetworkStats(
501 const HostPortPair& host_port_pair,
502 ServerNetworkStats stats) {
503 server_network_stats_map_.Put(host_port_pair, stats);
506 const ServerNetworkStats* HttpServerPropertiesImpl::GetServerNetworkStats(
507 const HostPortPair& host_port_pair) {
508 ServerNetworkStatsMap::iterator it =
509 server_network_stats_map_.Get(host_port_pair);
510 if (it == server_network_stats_map_.end()) {
511 return NULL;
513 return &it->second;
516 const ServerNetworkStatsMap&
517 HttpServerPropertiesImpl::server_network_stats_map() const {
518 return server_network_stats_map_;
521 void HttpServerPropertiesImpl::SetAlternativeServiceProbabilityThreshold(
522 double threshold) {
523 alternative_service_probability_threshold_ = threshold;
526 AlternativeServiceMap::const_iterator
527 HttpServerPropertiesImpl::GetAlternateProtocolIterator(
528 const HostPortPair& server) {
529 AlternativeServiceMap::const_iterator it =
530 alternative_service_map_.Get(server);
531 if (it != alternative_service_map_.end())
532 return it;
534 CanonicalHostMap::const_iterator canonical = GetCanonicalHost(server);
535 if (canonical == canonical_host_to_origin_map_.end()) {
536 return alternative_service_map_.end();
539 const HostPortPair canonical_host_port = canonical->second;
540 it = alternative_service_map_.Get(canonical_host_port);
541 if (it == alternative_service_map_.end()) {
542 return alternative_service_map_.end();
545 const AlternativeService alternative_service(
546 it->second.alternative_service.protocol, canonical_host_port.host(),
547 it->second.alternative_service.port);
548 if (!IsAlternativeServiceBroken(alternative_service)) {
549 return it;
552 RemoveCanonicalHost(canonical_host_port);
553 return alternative_service_map_.end();
556 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
557 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server) const {
558 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
559 std::string canonical_suffix = canonical_suffixes_[i];
560 if (EndsWith(server.host(), canonical_suffixes_[i], false)) {
561 HostPortPair canonical_host(canonical_suffix, server.port());
562 return canonical_host_to_origin_map_.find(canonical_host);
566 return canonical_host_to_origin_map_.end();
569 void HttpServerPropertiesImpl::RemoveCanonicalHost(
570 const HostPortPair& server) {
571 CanonicalHostMap::const_iterator canonical = GetCanonicalHost(server);
572 if (canonical == canonical_host_to_origin_map_.end())
573 return;
575 if (!canonical->second.Equals(server))
576 return;
578 canonical_host_to_origin_map_.erase(canonical->first);
581 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() {
582 base::TimeTicks now = base::TimeTicks::Now();
583 while (!broken_alternative_services_.empty()) {
584 BrokenAlternativeServices::iterator it =
585 broken_alternative_services_.begin();
586 if (now < it->second) {
587 break;
590 const AlternativeService alternative_service = it->first;
591 broken_alternative_services_.erase(it);
592 // TODO(bnc): Make sure broken alternative services are not in the mapping.
593 ClearAlternativeService(
594 HostPortPair(alternative_service.host, alternative_service.port));
596 ScheduleBrokenAlternateProtocolMappingsExpiration();
599 void
600 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
601 if (broken_alternative_services_.empty()) {
602 return;
604 base::TimeTicks now = base::TimeTicks::Now();
605 base::TimeTicks when = broken_alternative_services_.front().second;
606 base::TimeDelta delay = when > now ? when - now : base::TimeDelta();
607 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
608 FROM_HERE,
609 base::Bind(
610 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings,
611 weak_ptr_factory_.GetWeakPtr()),
612 delay);
615 } // namespace net