Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / net / http / http_server_properties_impl.cc
blob8b3bed224ed83dcf9129b9dc838b3ea6e9c07a3a
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"
15 #include "base/values.h"
17 namespace net {
19 namespace {
21 const uint64 kBrokenAlternativeProtocolDelaySecs = 300;
23 } // namespace
25 HttpServerPropertiesImpl::HttpServerPropertiesImpl()
26 : spdy_servers_map_(SpdyServerHostPortMap::NO_AUTO_EVICT),
27 alternative_service_map_(AlternativeServiceMap::NO_AUTO_EVICT),
28 spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT),
29 server_network_stats_map_(ServerNetworkStatsMap::NO_AUTO_EVICT),
30 alternate_protocol_probability_threshold_(1),
31 weak_ptr_factory_(this) {
32 canonical_suffixes_.push_back(".c.youtube.com");
33 canonical_suffixes_.push_back(".googlevideo.com");
34 canonical_suffixes_.push_back(".googleusercontent.com");
37 HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
40 void HttpServerPropertiesImpl::InitializeSpdyServers(
41 std::vector<std::string>* spdy_servers,
42 bool support_spdy) {
43 DCHECK(CalledOnValidThread());
44 if (!spdy_servers)
45 return;
46 // Add the entries from persisted data.
47 for (std::vector<std::string>::reverse_iterator it = spdy_servers->rbegin();
48 it != spdy_servers->rend(); ++it) {
49 spdy_servers_map_.Put(*it, support_spdy);
53 void HttpServerPropertiesImpl::InitializeAlternativeServiceServers(
54 AlternativeServiceMap* alternative_service_map) {
55 // Keep all the broken ones since those don't get persisted.
56 for (AlternativeServiceMap::iterator it = alternative_service_map_.begin();
57 it != alternative_service_map_.end();) {
58 if (IsAlternativeServiceBroken(it->second.alternative_service)) {
59 ++it;
60 } else {
61 it = alternative_service_map_.Erase(it);
65 // Add the entries from persisted data.
66 for (AlternativeServiceMap::reverse_iterator it =
67 alternative_service_map->rbegin();
68 it != alternative_service_map->rend(); ++it) {
69 alternative_service_map_.Put(it->first, it->second);
72 // Attempt to find canonical servers.
73 uint16 canonical_ports[] = { 80, 443 };
74 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
75 std::string canonical_suffix = canonical_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 (alternative_service_map_.Peek(
81 canonical_host_to_origin_map_[canonical_host]) !=
82 alternative_service_map_.end())) {
83 continue;
85 // Now attempt to find a server which matches this origin and set it as
86 // canonical.
87 for (AlternativeServiceMap::const_iterator it =
88 alternative_service_map_.begin();
89 it != alternative_service_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 base::WeakPtr<HttpServerProperties> HttpServerPropertiesImpl::GetWeakPtr() {
141 return weak_ptr_factory_.GetWeakPtr();
144 void HttpServerPropertiesImpl::Clear() {
145 DCHECK(CalledOnValidThread());
146 spdy_servers_map_.Clear();
147 alternative_service_map_.Clear();
148 canonical_host_to_origin_map_.clear();
149 spdy_settings_map_.Clear();
150 last_quic_address_.clear();
151 server_network_stats_map_.Clear();
154 bool HttpServerPropertiesImpl::SupportsRequestPriority(
155 const HostPortPair& host_port_pair) {
156 DCHECK(CalledOnValidThread());
157 if (host_port_pair.host().empty())
158 return false;
160 SpdyServerHostPortMap::iterator spdy_host_port =
161 spdy_servers_map_.Get(host_port_pair.ToString());
162 if (spdy_host_port != spdy_servers_map_.end() && spdy_host_port->second)
163 return true;
165 const AlternativeService alternative_service =
166 GetAlternativeService(host_port_pair);
167 return alternative_service.protocol == QUIC;
170 void HttpServerPropertiesImpl::SetSupportsSpdy(
171 const HostPortPair& host_port_pair,
172 bool support_spdy) {
173 DCHECK(CalledOnValidThread());
174 if (host_port_pair.host().empty())
175 return;
177 SpdyServerHostPortMap::iterator spdy_host_port =
178 spdy_servers_map_.Get(host_port_pair.ToString());
179 if ((spdy_host_port != spdy_servers_map_.end()) &&
180 (spdy_host_port->second == support_spdy)) {
181 return;
183 // Cache the data.
184 spdy_servers_map_.Put(host_port_pair.ToString(), support_spdy);
187 bool HttpServerPropertiesImpl::RequiresHTTP11(
188 const HostPortPair& host_port_pair) {
189 DCHECK(CalledOnValidThread());
190 if (host_port_pair.host().empty())
191 return false;
193 return (http11_servers_.find(host_port_pair) != http11_servers_.end());
196 void HttpServerPropertiesImpl::SetHTTP11Required(
197 const HostPortPair& host_port_pair) {
198 DCHECK(CalledOnValidThread());
199 if (host_port_pair.host().empty())
200 return;
202 http11_servers_.insert(host_port_pair);
205 void HttpServerPropertiesImpl::MaybeForceHTTP11(const HostPortPair& server,
206 SSLConfig* ssl_config) {
207 if (RequiresHTTP11(server)) {
208 ForceHTTP11(ssl_config);
212 std::string HttpServerPropertiesImpl::GetCanonicalSuffix(
213 const std::string& host) {
214 // If this host ends with a canonical suffix, then return the canonical
215 // suffix.
216 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
217 std::string canonical_suffix = canonical_suffixes_[i];
218 if (EndsWith(host, canonical_suffixes_[i], false)) {
219 return canonical_suffix;
222 return std::string();
225 AlternativeService HttpServerPropertiesImpl::GetAlternativeService(
226 const HostPortPair& origin) {
227 AlternativeServiceMap::const_iterator it =
228 alternative_service_map_.Get(origin);
229 if (it != alternative_service_map_.end()) {
230 if (it->second.probability < alternate_protocol_probability_threshold_) {
231 return AlternativeService();
233 AlternativeService alternative_service(it->second.alternative_service);
234 if (alternative_service.host.empty()) {
235 alternative_service.host = origin.host();
237 return alternative_service;
240 CanonicalHostMap::const_iterator canonical = GetCanonicalHost(origin);
241 if (canonical == canonical_host_to_origin_map_.end()) {
242 return AlternativeService();
244 it = alternative_service_map_.Get(canonical->second);
245 if (it == alternative_service_map_.end()) {
246 return AlternativeService();
248 if (it->second.probability < alternate_protocol_probability_threshold_) {
249 return AlternativeService();
251 AlternativeService alternative_service(it->second.alternative_service);
252 if (alternative_service.host.empty()) {
253 alternative_service.host = canonical->second.host();
255 if (IsAlternativeServiceBroken(alternative_service)) {
256 RemoveCanonicalHost(canonical->second);
257 return AlternativeService();
259 // Empty hostname: if alternative service for with hostname of canonical host
260 // is not broken, then return alternative service with hostname of origin.
261 if (it->second.alternative_service.host.empty()) {
262 alternative_service.host = origin.host();
264 return alternative_service;
267 void HttpServerPropertiesImpl::SetAlternativeService(
268 const HostPortPair& origin,
269 const AlternativeService& alternative_service,
270 double alternative_probability) {
271 AlternativeService complete_alternative_service(alternative_service);
272 if (complete_alternative_service.host.empty()) {
273 complete_alternative_service.host = origin.host();
275 if (IsAlternativeServiceBroken(complete_alternative_service)) {
276 DVLOG(1) << "Ignore alternative service since it is known to be broken.";
277 return;
280 const AlternativeServiceInfo alternative_service_info(
281 alternative_service, alternative_probability);
282 AlternativeServiceMap::const_iterator it =
283 GetAlternateProtocolIterator(origin);
284 if (it != alternative_service_map_.end()) {
285 const AlternativeServiceInfo existing_alternative_service_info = it->second;
286 if (existing_alternative_service_info != alternative_service_info) {
287 LOG(WARNING) << "Changing the alternative service for: "
288 << origin.ToString() << " from "
289 << existing_alternative_service_info.ToString() << " to "
290 << alternative_service_info.ToString() << ".";
292 } else {
293 if (alternative_probability >= alternate_protocol_probability_threshold_) {
294 // TODO(rch): Consider the case where multiple requests are started
295 // before the first completes. In this case, only one of the jobs
296 // would reach this code, whereas all of them should should have.
297 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING);
301 alternative_service_map_.Put(origin, alternative_service_info);
303 // If this host ends with a canonical suffix, then set it as the
304 // canonical host.
305 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
306 std::string canonical_suffix = canonical_suffixes_[i];
307 if (EndsWith(origin.host(), canonical_suffixes_[i], false)) {
308 HostPortPair canonical_host(canonical_suffix, origin.port());
309 canonical_host_to_origin_map_[canonical_host] = origin;
310 break;
315 void HttpServerPropertiesImpl::MarkAlternativeServiceBroken(
316 const AlternativeService& alternative_service) {
317 if (alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL) {
318 LOG(DFATAL) << "Trying to mark unknown alternate protocol broken.";
319 return;
321 int count = ++recently_broken_alternative_services_[alternative_service];
322 base::TimeDelta delay =
323 base::TimeDelta::FromSeconds(kBrokenAlternativeProtocolDelaySecs);
324 base::TimeTicks when = base::TimeTicks::Now() + delay * (1 << (count - 1));
325 auto result = broken_alternative_services_.insert(
326 std::make_pair(alternative_service, when));
327 // Return if alternative service is already in expiration queue.
328 if (!result.second) {
329 return;
332 // If this is the only entry in the list, schedule an expiration task.
333 // Otherwise it will be rescheduled automatically when the pending task runs.
334 if (broken_alternative_services_.size() == 1) {
335 ScheduleBrokenAlternateProtocolMappingsExpiration();
339 void HttpServerPropertiesImpl::MarkAlternativeServiceRecentlyBroken(
340 const AlternativeService& alternative_service) {
341 if (!ContainsKey(recently_broken_alternative_services_, alternative_service))
342 recently_broken_alternative_services_[alternative_service] = 1;
345 bool HttpServerPropertiesImpl::IsAlternativeServiceBroken(
346 const AlternativeService& alternative_service) const {
347 // Empty host means use host of origin, callers are supposed to substitute.
348 DCHECK(!alternative_service.host.empty());
349 return ContainsKey(broken_alternative_services_, alternative_service);
352 bool HttpServerPropertiesImpl::WasAlternativeServiceRecentlyBroken(
353 const AlternativeService& alternative_service) {
354 if (alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL)
355 return false;
356 return ContainsKey(recently_broken_alternative_services_,
357 alternative_service);
360 void HttpServerPropertiesImpl::ConfirmAlternativeService(
361 const AlternativeService& alternative_service) {
362 if (alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL)
363 return;
364 broken_alternative_services_.erase(alternative_service);
365 recently_broken_alternative_services_.erase(alternative_service);
368 void HttpServerPropertiesImpl::ClearAlternativeService(
369 const HostPortPair& origin) {
370 RemoveCanonicalHost(origin);
372 AlternativeServiceMap::iterator it = alternative_service_map_.Peek(origin);
373 if (it == alternative_service_map_.end()) {
374 return;
376 AlternativeService alternative_service(it->second.alternative_service);
377 if (alternative_service.host.empty()) {
378 alternative_service.host = origin.host();
380 alternative_service_map_.Erase(it);
382 // The following is temporary to keep the existing semantics, which is that if
383 // there is a broken alternative service in the mapping, then this method
384 // leaves it in a non-broken, but recently broken state.
386 // TODO(bnc):
387 // 1. Verify and document the class invariant that no broken alternative
388 // service can be in the mapping.
389 // 2. Remove the rest of this method as it will be moot.
390 broken_alternative_services_.erase(alternative_service);
393 const AlternativeServiceMap& HttpServerPropertiesImpl::alternative_service_map()
394 const {
395 return alternative_service_map_;
398 base::Value* HttpServerPropertiesImpl::GetAlternativeServiceInfoAsValue()
399 const {
400 base::ListValue* dict_list = new base::ListValue();
401 for (const auto& alternative_service_map_item : alternative_service_map_) {
402 const HostPortPair& host_port_pair = alternative_service_map_item.first;
403 const AlternativeServiceInfo& alternative_service_info =
404 alternative_service_map_item.second;
405 std::string alternative_service_string(alternative_service_info.ToString());
406 AlternativeService alternative_service(
407 alternative_service_info.alternative_service);
408 if (alternative_service.host.empty()) {
409 alternative_service.host = host_port_pair.host();
411 if (IsAlternativeServiceBroken(alternative_service)) {
412 alternative_service_string.append(" (broken)");
415 base::DictionaryValue* dict = new base::DictionaryValue();
416 dict->SetString("host_port_pair", host_port_pair.ToString());
417 dict->SetString("alternative_service", alternative_service_string);
418 dict_list->Append(dict);
420 return dict_list;
423 const SettingsMap& HttpServerPropertiesImpl::GetSpdySettings(
424 const HostPortPair& host_port_pair) {
425 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
426 if (it == spdy_settings_map_.end()) {
427 CR_DEFINE_STATIC_LOCAL(SettingsMap, kEmptySettingsMap, ());
428 return kEmptySettingsMap;
430 return it->second;
433 bool HttpServerPropertiesImpl::SetSpdySetting(
434 const HostPortPair& host_port_pair,
435 SpdySettingsIds id,
436 SpdySettingsFlags flags,
437 uint32 value) {
438 if (!(flags & SETTINGS_FLAG_PLEASE_PERSIST))
439 return false;
441 SettingsFlagsAndValue flags_and_value(SETTINGS_FLAG_PERSISTED, value);
442 SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair);
443 if (it == spdy_settings_map_.end()) {
444 SettingsMap settings_map;
445 settings_map[id] = flags_and_value;
446 spdy_settings_map_.Put(host_port_pair, settings_map);
447 } else {
448 SettingsMap& settings_map = it->second;
449 settings_map[id] = flags_and_value;
451 return true;
454 void HttpServerPropertiesImpl::ClearSpdySettings(
455 const HostPortPair& host_port_pair) {
456 SpdySettingsMap::iterator it = spdy_settings_map_.Peek(host_port_pair);
457 if (it != spdy_settings_map_.end())
458 spdy_settings_map_.Erase(it);
461 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
462 spdy_settings_map_.Clear();
465 const SpdySettingsMap&
466 HttpServerPropertiesImpl::spdy_settings_map() const {
467 return spdy_settings_map_;
470 bool HttpServerPropertiesImpl::GetSupportsQuic(
471 IPAddressNumber* last_address) const {
472 if (last_quic_address_.empty())
473 return false;
475 *last_address = last_quic_address_;
476 return true;
479 void HttpServerPropertiesImpl::SetSupportsQuic(bool used_quic,
480 const IPAddressNumber& address) {
481 if (!used_quic) {
482 last_quic_address_.clear();
483 } else {
484 last_quic_address_ = address;
488 void HttpServerPropertiesImpl::SetServerNetworkStats(
489 const HostPortPair& host_port_pair,
490 ServerNetworkStats stats) {
491 server_network_stats_map_.Put(host_port_pair, stats);
494 const ServerNetworkStats* HttpServerPropertiesImpl::GetServerNetworkStats(
495 const HostPortPair& host_port_pair) {
496 ServerNetworkStatsMap::iterator it =
497 server_network_stats_map_.Get(host_port_pair);
498 if (it == server_network_stats_map_.end()) {
499 return NULL;
501 return &it->second;
504 const ServerNetworkStatsMap&
505 HttpServerPropertiesImpl::server_network_stats_map() const {
506 return server_network_stats_map_;
509 void HttpServerPropertiesImpl::SetAlternateProtocolProbabilityThreshold(
510 double threshold) {
511 alternate_protocol_probability_threshold_ = threshold;
514 AlternativeServiceMap::const_iterator
515 HttpServerPropertiesImpl::GetAlternateProtocolIterator(
516 const HostPortPair& server) {
517 AlternativeServiceMap::const_iterator it =
518 alternative_service_map_.Get(server);
519 if (it != alternative_service_map_.end())
520 return it;
522 CanonicalHostMap::const_iterator canonical = GetCanonicalHost(server);
523 if (canonical == canonical_host_to_origin_map_.end()) {
524 return alternative_service_map_.end();
527 const HostPortPair canonical_host_port = canonical->second;
528 it = alternative_service_map_.Get(canonical_host_port);
529 if (it == alternative_service_map_.end()) {
530 return alternative_service_map_.end();
533 const AlternativeService alternative_service(
534 it->second.alternative_service.protocol, canonical_host_port.host(),
535 it->second.alternative_service.port);
536 if (!IsAlternativeServiceBroken(alternative_service)) {
537 return it;
540 RemoveCanonicalHost(canonical_host_port);
541 return alternative_service_map_.end();
544 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
545 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server) const {
546 for (size_t i = 0; i < canonical_suffixes_.size(); ++i) {
547 std::string canonical_suffix = canonical_suffixes_[i];
548 if (EndsWith(server.host(), canonical_suffixes_[i], false)) {
549 HostPortPair canonical_host(canonical_suffix, server.port());
550 return canonical_host_to_origin_map_.find(canonical_host);
554 return canonical_host_to_origin_map_.end();
557 void HttpServerPropertiesImpl::RemoveCanonicalHost(
558 const HostPortPair& server) {
559 CanonicalHostMap::const_iterator canonical = GetCanonicalHost(server);
560 if (canonical == canonical_host_to_origin_map_.end())
561 return;
563 if (!canonical->second.Equals(server))
564 return;
566 canonical_host_to_origin_map_.erase(canonical->first);
569 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() {
570 base::TimeTicks now = base::TimeTicks::Now();
571 while (!broken_alternative_services_.empty()) {
572 BrokenAlternativeServices::iterator it =
573 broken_alternative_services_.begin();
574 if (now < it->second) {
575 break;
578 const AlternativeService alternative_service = it->first;
579 broken_alternative_services_.erase(it);
580 // TODO(bnc): Make sure broken alternative services are not in the mapping.
581 ClearAlternativeService(
582 HostPortPair(alternative_service.host, alternative_service.port));
584 ScheduleBrokenAlternateProtocolMappingsExpiration();
587 void
588 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
589 if (broken_alternative_services_.empty()) {
590 return;
592 base::TimeTicks now = base::TimeTicks::Now();
593 base::TimeTicks when = broken_alternative_services_.front().second;
594 base::TimeDelta delay = when > now ? when - now : base::TimeDelta();
595 base::MessageLoop::current()->PostDelayedTask(
596 FROM_HERE,
597 base::Bind(
598 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings,
599 weak_ptr_factory_.GetWeakPtr()),
600 delay);
603 } // namespace net