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/single_thread_task_runner.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/values.h"
22 const uint64 kBrokenAlternativeProtocolDelaySecs
= 300;
26 HttpServerPropertiesImpl::HttpServerPropertiesImpl()
27 : spdy_servers_map_(SpdyServerHostPortMap::NO_AUTO_EVICT
),
28 alternative_service_map_(AlternativeServiceMap::NO_AUTO_EVICT
),
29 spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT
),
30 server_network_stats_map_(ServerNetworkStatsMap::NO_AUTO_EVICT
),
31 alternative_service_probability_threshold_(1.0),
32 weak_ptr_factory_(this) {
33 canonical_suffixes_
.push_back(".c.youtube.com");
34 canonical_suffixes_
.push_back(".googlevideo.com");
35 canonical_suffixes_
.push_back(".googleusercontent.com");
38 HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
41 void HttpServerPropertiesImpl::InitializeSpdyServers(
42 std::vector
<std::string
>* spdy_servers
,
44 DCHECK(CalledOnValidThread());
47 // Add the entries from persisted data.
48 for (std::vector
<std::string
>::reverse_iterator it
= spdy_servers
->rbegin();
49 it
!= spdy_servers
->rend(); ++it
) {
50 spdy_servers_map_
.Put(*it
, support_spdy
);
54 void HttpServerPropertiesImpl::InitializeAlternativeServiceServers(
55 AlternativeServiceMap
* alternative_service_map
) {
56 // Keep all the broken ones since those don't get persisted.
57 for (AlternativeServiceMap::iterator it
= alternative_service_map_
.begin();
58 it
!= alternative_service_map_
.end();) {
59 AlternativeService
alternative_service(it
->second
.alternative_service
);
60 if (alternative_service
.host
.empty()) {
61 alternative_service
.host
= it
->first
.host();
63 if (IsAlternativeServiceBroken(alternative_service
)) {
66 it
= alternative_service_map_
.Erase(it
);
70 // Add the entries from persisted data.
71 for (AlternativeServiceMap::reverse_iterator it
=
72 alternative_service_map
->rbegin();
73 it
!= alternative_service_map
->rend(); ++it
) {
74 alternative_service_map_
.Put(it
->first
, it
->second
);
77 // Attempt to find canonical servers.
78 uint16 canonical_ports
[] = { 80, 443 };
79 for (size_t i
= 0; i
< canonical_suffixes_
.size(); ++i
) {
80 std::string canonical_suffix
= canonical_suffixes_
[i
];
81 for (size_t j
= 0; j
< arraysize(canonical_ports
); ++j
) {
82 HostPortPair
canonical_host(canonical_suffix
, canonical_ports
[j
]);
83 // If we already have a valid canonical server, we're done.
84 if (ContainsKey(canonical_host_to_origin_map_
, canonical_host
) &&
85 (alternative_service_map_
.Peek(
86 canonical_host_to_origin_map_
[canonical_host
]) !=
87 alternative_service_map_
.end())) {
90 // Now attempt to find a server which matches this origin and set it as
92 for (AlternativeServiceMap::const_iterator it
=
93 alternative_service_map_
.begin();
94 it
!= alternative_service_map_
.end(); ++it
) {
95 if (base::EndsWith(it
->first
.host(), canonical_suffixes_
[i
], false)) {
96 canonical_host_to_origin_map_
[canonical_host
] = it
->first
;
104 void HttpServerPropertiesImpl::InitializeSpdySettingsServers(
105 SpdySettingsMap
* spdy_settings_map
) {
106 for (SpdySettingsMap::reverse_iterator it
= spdy_settings_map
->rbegin();
107 it
!= spdy_settings_map
->rend(); ++it
) {
108 spdy_settings_map_
.Put(it
->first
, it
->second
);
112 void HttpServerPropertiesImpl::InitializeSupportsQuic(
113 IPAddressNumber
* last_address
) {
115 last_quic_address_
= *last_address
;
118 void HttpServerPropertiesImpl::InitializeServerNetworkStats(
119 ServerNetworkStatsMap
* server_network_stats_map
) {
120 for (ServerNetworkStatsMap::reverse_iterator it
=
121 server_network_stats_map
->rbegin();
122 it
!= server_network_stats_map
->rend(); ++it
) {
123 server_network_stats_map_
.Put(it
->first
, it
->second
);
127 void HttpServerPropertiesImpl::GetSpdyServerList(
128 base::ListValue
* spdy_server_list
,
129 size_t max_size
) const {
130 DCHECK(CalledOnValidThread());
131 DCHECK(spdy_server_list
);
132 spdy_server_list
->Clear();
134 // Get the list of servers (host/port) that support SPDY.
135 for (SpdyServerHostPortMap::const_iterator it
= spdy_servers_map_
.begin();
136 it
!= spdy_servers_map_
.end() && count
< max_size
; ++it
) {
137 const std::string spdy_server_host_port
= it
->first
;
139 spdy_server_list
->Append(new base::StringValue(spdy_server_host_port
));
145 base::WeakPtr
<HttpServerProperties
> HttpServerPropertiesImpl::GetWeakPtr() {
146 return weak_ptr_factory_
.GetWeakPtr();
149 void HttpServerPropertiesImpl::Clear() {
150 DCHECK(CalledOnValidThread());
151 spdy_servers_map_
.Clear();
152 alternative_service_map_
.Clear();
153 canonical_host_to_origin_map_
.clear();
154 spdy_settings_map_
.Clear();
155 last_quic_address_
.clear();
156 server_network_stats_map_
.Clear();
159 bool HttpServerPropertiesImpl::SupportsRequestPriority(
160 const HostPortPair
& host_port_pair
) {
161 DCHECK(CalledOnValidThread());
162 if (host_port_pair
.host().empty())
165 if (GetSupportsSpdy(host_port_pair
))
168 const AlternativeService alternative_service
=
169 GetAlternativeService(host_port_pair
);
170 return alternative_service
.protocol
== QUIC
;
173 bool HttpServerPropertiesImpl::GetSupportsSpdy(
174 const HostPortPair
& host_port_pair
) {
175 DCHECK(CalledOnValidThread());
176 if (host_port_pair
.host().empty())
179 SpdyServerHostPortMap::iterator spdy_host_port
=
180 spdy_servers_map_
.Get(host_port_pair
.ToString());
181 return spdy_host_port
!= spdy_servers_map_
.end() && spdy_host_port
->second
;
184 void HttpServerPropertiesImpl::SetSupportsSpdy(
185 const HostPortPair
& host_port_pair
,
187 DCHECK(CalledOnValidThread());
188 if (host_port_pair
.host().empty())
191 SpdyServerHostPortMap::iterator spdy_host_port
=
192 spdy_servers_map_
.Get(host_port_pair
.ToString());
193 if ((spdy_host_port
!= spdy_servers_map_
.end()) &&
194 (spdy_host_port
->second
== support_spdy
)) {
198 spdy_servers_map_
.Put(host_port_pair
.ToString(), support_spdy
);
201 bool HttpServerPropertiesImpl::RequiresHTTP11(
202 const HostPortPair
& host_port_pair
) {
203 DCHECK(CalledOnValidThread());
204 if (host_port_pair
.host().empty())
207 return (http11_servers_
.find(host_port_pair
) != http11_servers_
.end());
210 void HttpServerPropertiesImpl::SetHTTP11Required(
211 const HostPortPair
& host_port_pair
) {
212 DCHECK(CalledOnValidThread());
213 if (host_port_pair
.host().empty())
216 http11_servers_
.insert(host_port_pair
);
219 void HttpServerPropertiesImpl::MaybeForceHTTP11(const HostPortPair
& server
,
220 SSLConfig
* ssl_config
) {
221 if (RequiresHTTP11(server
)) {
222 ForceHTTP11(ssl_config
);
226 std::string
HttpServerPropertiesImpl::GetCanonicalSuffix(
227 const std::string
& host
) {
228 // If this host ends with a canonical suffix, then return the canonical
230 for (size_t i
= 0; i
< canonical_suffixes_
.size(); ++i
) {
231 std::string canonical_suffix
= canonical_suffixes_
[i
];
232 if (base::EndsWith(host
, canonical_suffixes_
[i
], false)) {
233 return canonical_suffix
;
236 return std::string();
239 AlternativeService
HttpServerPropertiesImpl::GetAlternativeService(
240 const HostPortPair
& origin
) {
241 AlternativeServiceMap::const_iterator it
=
242 alternative_service_map_
.Get(origin
);
243 if (it
!= alternative_service_map_
.end()) {
244 if (it
->second
.probability
< alternative_service_probability_threshold_
) {
245 return AlternativeService();
247 AlternativeService
alternative_service(it
->second
.alternative_service
);
248 if (alternative_service
.host
.empty()) {
249 alternative_service
.host
= origin
.host();
251 return alternative_service
;
254 CanonicalHostMap::const_iterator canonical
= GetCanonicalHost(origin
);
255 if (canonical
== canonical_host_to_origin_map_
.end()) {
256 return AlternativeService();
258 it
= alternative_service_map_
.Get(canonical
->second
);
259 if (it
== alternative_service_map_
.end()) {
260 return AlternativeService();
262 if (it
->second
.probability
< alternative_service_probability_threshold_
) {
263 return AlternativeService();
265 AlternativeService
alternative_service(it
->second
.alternative_service
);
266 if (alternative_service
.host
.empty()) {
267 alternative_service
.host
= canonical
->second
.host();
269 if (IsAlternativeServiceBroken(alternative_service
)) {
270 RemoveCanonicalHost(canonical
->second
);
271 return AlternativeService();
273 // Empty hostname: if alternative service for with hostname of canonical host
274 // is not broken, then return alternative service with hostname of origin.
275 if (it
->second
.alternative_service
.host
.empty()) {
276 alternative_service
.host
= origin
.host();
278 return alternative_service
;
281 void HttpServerPropertiesImpl::SetAlternativeService(
282 const HostPortPair
& origin
,
283 const AlternativeService
& alternative_service
,
284 double alternative_probability
) {
285 const AlternativeServiceInfo
alternative_service_info(
286 alternative_service
, alternative_probability
);
287 AlternativeServiceMap::const_iterator it
=
288 GetAlternateProtocolIterator(origin
);
289 if (it
== alternative_service_map_
.end() &&
290 alternative_probability
>= alternative_service_probability_threshold_
) {
291 // TODO(rch): Consider the case where multiple requests are started
292 // before the first completes. In this case, only one of the jobs
293 // would reach this code, whereas all of them should should have.
294 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING
);
297 alternative_service_map_
.Put(origin
, alternative_service_info
);
299 // If this host ends with a canonical suffix, then set it as the
301 for (size_t i
= 0; i
< canonical_suffixes_
.size(); ++i
) {
302 std::string canonical_suffix
= canonical_suffixes_
[i
];
303 if (base::EndsWith(origin
.host(), canonical_suffixes_
[i
], false)) {
304 HostPortPair
canonical_host(canonical_suffix
, origin
.port());
305 canonical_host_to_origin_map_
[canonical_host
] = origin
;
311 void HttpServerPropertiesImpl::MarkAlternativeServiceBroken(
312 const AlternativeService
& alternative_service
) {
313 // Empty host means use host of origin, callers are supposed to substitute.
314 DCHECK(!alternative_service
.host
.empty());
315 if (alternative_service
.protocol
== UNINITIALIZED_ALTERNATE_PROTOCOL
) {
316 LOG(DFATAL
) << "Trying to mark unknown alternate protocol broken.";
319 int count
= ++recently_broken_alternative_services_
[alternative_service
];
320 base::TimeDelta delay
=
321 base::TimeDelta::FromSeconds(kBrokenAlternativeProtocolDelaySecs
);
322 base::TimeTicks when
= base::TimeTicks::Now() + delay
* (1 << (count
- 1));
323 auto result
= broken_alternative_services_
.insert(
324 std::make_pair(alternative_service
, when
));
325 // Return if alternative service is already in expiration queue.
326 if (!result
.second
) {
330 // If this is the only entry in the list, schedule an expiration task.
331 // Otherwise it will be rescheduled automatically when the pending task runs.
332 if (broken_alternative_services_
.size() == 1) {
333 ScheduleBrokenAlternateProtocolMappingsExpiration();
337 void HttpServerPropertiesImpl::MarkAlternativeServiceRecentlyBroken(
338 const AlternativeService
& alternative_service
) {
339 if (!ContainsKey(recently_broken_alternative_services_
, alternative_service
))
340 recently_broken_alternative_services_
[alternative_service
] = 1;
343 bool HttpServerPropertiesImpl::IsAlternativeServiceBroken(
344 const AlternativeService
& alternative_service
) const {
345 // Empty host means use host of origin, callers are supposed to substitute.
346 DCHECK(!alternative_service
.host
.empty());
347 return ContainsKey(broken_alternative_services_
, alternative_service
);
350 bool HttpServerPropertiesImpl::WasAlternativeServiceRecentlyBroken(
351 const AlternativeService
& alternative_service
) {
352 if (alternative_service
.protocol
== UNINITIALIZED_ALTERNATE_PROTOCOL
)
354 return ContainsKey(recently_broken_alternative_services_
,
355 alternative_service
);
358 void HttpServerPropertiesImpl::ConfirmAlternativeService(
359 const AlternativeService
& alternative_service
) {
360 if (alternative_service
.protocol
== UNINITIALIZED_ALTERNATE_PROTOCOL
)
362 broken_alternative_services_
.erase(alternative_service
);
363 recently_broken_alternative_services_
.erase(alternative_service
);
366 void HttpServerPropertiesImpl::ClearAlternativeService(
367 const HostPortPair
& origin
) {
368 RemoveCanonicalHost(origin
);
370 AlternativeServiceMap::iterator it
= alternative_service_map_
.Peek(origin
);
371 if (it
== alternative_service_map_
.end()) {
374 alternative_service_map_
.Erase(it
);
377 const AlternativeServiceMap
& HttpServerPropertiesImpl::alternative_service_map()
379 return alternative_service_map_
;
382 scoped_ptr
<base::Value
>
383 HttpServerPropertiesImpl::GetAlternativeServiceInfoAsValue()
385 scoped_ptr
<base::ListValue
> dict_list(new base::ListValue());
386 for (const auto& alternative_service_map_item
: alternative_service_map_
) {
387 const HostPortPair
& host_port_pair
= alternative_service_map_item
.first
;
388 const AlternativeServiceInfo
& alternative_service_info
=
389 alternative_service_map_item
.second
;
390 std::string
alternative_service_string(alternative_service_info
.ToString());
391 AlternativeService
alternative_service(
392 alternative_service_info
.alternative_service
);
393 if (alternative_service
.host
.empty()) {
394 alternative_service
.host
= host_port_pair
.host();
396 if (IsAlternativeServiceBroken(alternative_service
)) {
397 alternative_service_string
.append(" (broken)");
400 scoped_ptr
<base::DictionaryValue
> dict(new base::DictionaryValue());
401 dict
->SetString("host_port_pair", host_port_pair
.ToString());
402 dict
->SetString("alternative_service", alternative_service_string
);
403 dict_list
->Append(dict
.Pass());
405 return dict_list
.Pass();
408 const SettingsMap
& HttpServerPropertiesImpl::GetSpdySettings(
409 const HostPortPair
& host_port_pair
) {
410 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
411 if (it
== spdy_settings_map_
.end()) {
412 CR_DEFINE_STATIC_LOCAL(SettingsMap
, kEmptySettingsMap
, ());
413 return kEmptySettingsMap
;
418 bool HttpServerPropertiesImpl::SetSpdySetting(
419 const HostPortPair
& host_port_pair
,
421 SpdySettingsFlags flags
,
423 if (!(flags
& SETTINGS_FLAG_PLEASE_PERSIST
))
426 SettingsFlagsAndValue
flags_and_value(SETTINGS_FLAG_PERSISTED
, value
);
427 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
428 if (it
== spdy_settings_map_
.end()) {
429 SettingsMap settings_map
;
430 settings_map
[id
] = flags_and_value
;
431 spdy_settings_map_
.Put(host_port_pair
, settings_map
);
433 SettingsMap
& settings_map
= it
->second
;
434 settings_map
[id
] = flags_and_value
;
439 void HttpServerPropertiesImpl::ClearSpdySettings(
440 const HostPortPair
& host_port_pair
) {
441 SpdySettingsMap::iterator it
= spdy_settings_map_
.Peek(host_port_pair
);
442 if (it
!= spdy_settings_map_
.end())
443 spdy_settings_map_
.Erase(it
);
446 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
447 spdy_settings_map_
.Clear();
450 const SpdySettingsMap
&
451 HttpServerPropertiesImpl::spdy_settings_map() const {
452 return spdy_settings_map_
;
455 bool HttpServerPropertiesImpl::GetSupportsQuic(
456 IPAddressNumber
* last_address
) const {
457 if (last_quic_address_
.empty())
460 *last_address
= last_quic_address_
;
464 void HttpServerPropertiesImpl::SetSupportsQuic(bool used_quic
,
465 const IPAddressNumber
& address
) {
467 last_quic_address_
.clear();
469 last_quic_address_
= address
;
473 void HttpServerPropertiesImpl::SetServerNetworkStats(
474 const HostPortPair
& host_port_pair
,
475 ServerNetworkStats stats
) {
476 server_network_stats_map_
.Put(host_port_pair
, stats
);
479 const ServerNetworkStats
* HttpServerPropertiesImpl::GetServerNetworkStats(
480 const HostPortPair
& host_port_pair
) {
481 ServerNetworkStatsMap::iterator it
=
482 server_network_stats_map_
.Get(host_port_pair
);
483 if (it
== server_network_stats_map_
.end()) {
489 const ServerNetworkStatsMap
&
490 HttpServerPropertiesImpl::server_network_stats_map() const {
491 return server_network_stats_map_
;
494 void HttpServerPropertiesImpl::SetAlternativeServiceProbabilityThreshold(
496 alternative_service_probability_threshold_
= threshold
;
499 AlternativeServiceMap::const_iterator
500 HttpServerPropertiesImpl::GetAlternateProtocolIterator(
501 const HostPortPair
& server
) {
502 AlternativeServiceMap::const_iterator it
=
503 alternative_service_map_
.Get(server
);
504 if (it
!= alternative_service_map_
.end())
507 CanonicalHostMap::const_iterator canonical
= GetCanonicalHost(server
);
508 if (canonical
== canonical_host_to_origin_map_
.end()) {
509 return alternative_service_map_
.end();
512 const HostPortPair canonical_host_port
= canonical
->second
;
513 it
= alternative_service_map_
.Get(canonical_host_port
);
514 if (it
== alternative_service_map_
.end()) {
515 return alternative_service_map_
.end();
518 const AlternativeService
alternative_service(
519 it
->second
.alternative_service
.protocol
, canonical_host_port
.host(),
520 it
->second
.alternative_service
.port
);
521 if (!IsAlternativeServiceBroken(alternative_service
)) {
525 RemoveCanonicalHost(canonical_host_port
);
526 return alternative_service_map_
.end();
529 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
530 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server
) const {
531 for (size_t i
= 0; i
< canonical_suffixes_
.size(); ++i
) {
532 std::string canonical_suffix
= canonical_suffixes_
[i
];
533 if (base::EndsWith(server
.host(), canonical_suffixes_
[i
], false)) {
534 HostPortPair
canonical_host(canonical_suffix
, server
.port());
535 return canonical_host_to_origin_map_
.find(canonical_host
);
539 return canonical_host_to_origin_map_
.end();
542 void HttpServerPropertiesImpl::RemoveCanonicalHost(
543 const HostPortPair
& server
) {
544 CanonicalHostMap::const_iterator canonical
= GetCanonicalHost(server
);
545 if (canonical
== canonical_host_to_origin_map_
.end())
548 if (!canonical
->second
.Equals(server
))
551 canonical_host_to_origin_map_
.erase(canonical
->first
);
554 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() {
555 base::TimeTicks now
= base::TimeTicks::Now();
556 while (!broken_alternative_services_
.empty()) {
557 BrokenAlternativeServices::iterator it
=
558 broken_alternative_services_
.begin();
559 if (now
< it
->second
) {
563 const AlternativeService alternative_service
= it
->first
;
564 broken_alternative_services_
.erase(it
);
565 // TODO(bnc): Make sure broken alternative services are not in the mapping.
566 ClearAlternativeService(
567 HostPortPair(alternative_service
.host
, alternative_service
.port
));
569 ScheduleBrokenAlternateProtocolMappingsExpiration();
573 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
574 if (broken_alternative_services_
.empty()) {
577 base::TimeTicks now
= base::TimeTicks::Now();
578 base::TimeTicks when
= broken_alternative_services_
.front().second
;
579 base::TimeDelta delay
= when
> now
? when
- now
: base::TimeDelta();
580 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
583 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings
,
584 weak_ptr_factory_
.GetWeakPtr()),