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/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"
21 const uint64 kBrokenAlternativeProtocolDelaySecs
= 300;
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
,
43 DCHECK(CalledOnValidThread());
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
)) {
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())) {
85 // Now attempt to find a server which matches this origin and set it as
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
;
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
) {
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();
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
;
134 spdy_server_list
->Append(new base::StringValue(spdy_server_host_port
));
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())
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
)
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
,
173 DCHECK(CalledOnValidThread());
174 if (host_port_pair
.host().empty())
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
)) {
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())
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())
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
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.";
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() << ".";
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
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
;
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.";
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
) {
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
)
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
)
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()) {
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.
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()
395 return alternative_service_map_
;
398 base::Value
* HttpServerPropertiesImpl::GetAlternativeServiceInfoAsValue()
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
);
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
;
433 bool HttpServerPropertiesImpl::SetSpdySetting(
434 const HostPortPair
& host_port_pair
,
436 SpdySettingsFlags flags
,
438 if (!(flags
& SETTINGS_FLAG_PLEASE_PERSIST
))
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
);
448 SettingsMap
& settings_map
= it
->second
;
449 settings_map
[id
] = flags_and_value
;
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())
475 *last_address
= last_quic_address_
;
479 void HttpServerPropertiesImpl::SetSupportsQuic(bool used_quic
,
480 const IPAddressNumber
& address
) {
482 last_quic_address_
.clear();
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()) {
504 const ServerNetworkStatsMap
&
505 HttpServerPropertiesImpl::server_network_stats_map() const {
506 return server_network_stats_map_
;
509 void HttpServerPropertiesImpl::SetAlternateProtocolProbabilityThreshold(
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())
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
)) {
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())
563 if (!canonical
->second
.Equals(server
))
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
) {
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();
588 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
589 if (broken_alternative_services_
.empty()) {
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(
598 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings
,
599 weak_ptr_factory_
.GetWeakPtr()),