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"
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"
23 const uint64 kBrokenAlternativeProtocolDelaySecs
= 300;
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
,
45 DCHECK(CalledOnValidThread());
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
)) {
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())) {
87 // Now attempt to find a server which matches this origin and set it as
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
;
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
) {
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();
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
;
136 spdy_server_list
->Append(new base::StringValue(spdy_server_host_port
));
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())
162 if (GetSupportsSpdy(host_port_pair
))
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())
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
,
184 DCHECK(CalledOnValidThread());
185 if (host_port_pair
.host().empty())
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
)) {
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())
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())
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
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.";
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() << ".";
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
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
;
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.";
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
) {
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
)
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
)
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()) {
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.
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()
406 return alternative_service_map_
;
409 scoped_ptr
<base::Value
>
410 HttpServerPropertiesImpl::GetAlternativeServiceInfoAsValue()
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
;
445 bool HttpServerPropertiesImpl::SetSpdySetting(
446 const HostPortPair
& host_port_pair
,
448 SpdySettingsFlags flags
,
450 if (!(flags
& SETTINGS_FLAG_PLEASE_PERSIST
))
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
);
460 SettingsMap
& settings_map
= it
->second
;
461 settings_map
[id
] = flags_and_value
;
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())
487 *last_address
= last_quic_address_
;
491 void HttpServerPropertiesImpl::SetSupportsQuic(bool used_quic
,
492 const IPAddressNumber
& address
) {
494 last_quic_address_
.clear();
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()) {
516 const ServerNetworkStatsMap
&
517 HttpServerPropertiesImpl::server_network_stats_map() const {
518 return server_network_stats_map_
;
521 void HttpServerPropertiesImpl::SetAlternativeServiceProbabilityThreshold(
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())
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
)) {
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())
575 if (!canonical
->second
.Equals(server
))
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
) {
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();
600 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
601 if (broken_alternative_services_
.empty()) {
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(
610 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings
,
611 weak_ptr_factory_
.GetWeakPtr()),