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"
20 const uint64 kBrokenAlternateProtocolDelaySecs
= 300;
24 HttpServerPropertiesImpl::HttpServerPropertiesImpl()
25 : spdy_servers_map_(SpdyServerHostPortMap::NO_AUTO_EVICT
),
26 alternate_protocol_map_(AlternateProtocolMap::NO_AUTO_EVICT
),
27 spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT
),
28 server_network_stats_map_(ServerNetworkStatsMap::NO_AUTO_EVICT
),
29 alternate_protocol_probability_threshold_(1),
30 weak_ptr_factory_(this) {
31 canonical_suffixes_
.push_back(".c.youtube.com");
32 canonical_suffixes_
.push_back(".googlevideo.com");
33 canonical_suffixes_
.push_back(".googleusercontent.com");
36 HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
39 void HttpServerPropertiesImpl::InitializeSpdyServers(
40 std::vector
<std::string
>* spdy_servers
,
42 DCHECK(CalledOnValidThread());
45 // Add the entries from persisted data.
46 for (std::vector
<std::string
>::reverse_iterator it
= spdy_servers
->rbegin();
47 it
!= spdy_servers
->rend(); ++it
) {
48 spdy_servers_map_
.Put(*it
, support_spdy
);
52 void HttpServerPropertiesImpl::InitializeAlternateProtocolServers(
53 AlternateProtocolMap
* alternate_protocol_map
) {
54 // Keep all the broken ones since those don't get persisted.
55 for (AlternateProtocolMap::iterator it
= alternate_protocol_map_
.begin();
56 it
!= alternate_protocol_map_
.end();) {
57 const AlternativeService
alternative_service(
58 it
->second
.protocol
, it
->first
.host(), it
->second
.port
);
59 if (IsAlternativeServiceBroken(alternative_service
)) {
62 it
= alternate_protocol_map_
.Erase(it
);
66 // Add the entries from persisted data.
67 for (AlternateProtocolMap::reverse_iterator it
=
68 alternate_protocol_map
->rbegin();
69 it
!= alternate_protocol_map
->rend(); ++it
) {
70 alternate_protocol_map_
.Put(it
->first
, it
->second
);
73 // Attempt to find canonical servers.
74 uint16 canonical_ports
[] = { 80, 443 };
75 for (size_t i
= 0; i
< canonical_suffixes_
.size(); ++i
) {
76 std::string canonical_suffix
= canonical_suffixes_
[i
];
77 for (size_t j
= 0; j
< arraysize(canonical_ports
); ++j
) {
78 HostPortPair
canonical_host(canonical_suffix
, canonical_ports
[j
]);
79 // If we already have a valid canonical server, we're done.
80 if (ContainsKey(canonical_host_to_origin_map_
, canonical_host
) &&
81 (alternate_protocol_map_
.Peek(canonical_host_to_origin_map_
[
82 canonical_host
]) != alternate_protocol_map_
.end())) {
85 // Now attempt to find a server which matches this origin and set it as
87 for (AlternateProtocolMap::const_iterator it
=
88 alternate_protocol_map_
.begin();
89 it
!= alternate_protocol_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 static const AlternateProtocolInfo
* g_forced_alternate_protocol
= NULL
;
143 void HttpServerPropertiesImpl::ForceAlternateProtocol(
144 const AlternateProtocolInfo
& info
) {
145 // Note: we're going to leak this.
146 if (g_forced_alternate_protocol
)
147 delete g_forced_alternate_protocol
;
148 g_forced_alternate_protocol
= new AlternateProtocolInfo(info
);
152 void HttpServerPropertiesImpl::DisableForcedAlternateProtocol() {
153 delete g_forced_alternate_protocol
;
154 g_forced_alternate_protocol
= NULL
;
157 base::WeakPtr
<HttpServerProperties
> HttpServerPropertiesImpl::GetWeakPtr() {
158 return weak_ptr_factory_
.GetWeakPtr();
161 void HttpServerPropertiesImpl::Clear() {
162 DCHECK(CalledOnValidThread());
163 spdy_servers_map_
.Clear();
164 alternate_protocol_map_
.Clear();
165 canonical_host_to_origin_map_
.clear();
166 spdy_settings_map_
.Clear();
167 last_quic_address_
.clear();
168 server_network_stats_map_
.Clear();
171 bool HttpServerPropertiesImpl::SupportsRequestPriority(
172 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() && spdy_host_port
->second
)
182 const AlternativeService alternative_service
=
183 GetAlternativeService(host_port_pair
);
184 return alternative_service
.protocol
== QUIC
;
187 void HttpServerPropertiesImpl::SetSupportsSpdy(
188 const HostPortPair
& host_port_pair
,
190 DCHECK(CalledOnValidThread());
191 if (host_port_pair
.host().empty())
194 SpdyServerHostPortMap::iterator spdy_host_port
=
195 spdy_servers_map_
.Get(host_port_pair
.ToString());
196 if ((spdy_host_port
!= spdy_servers_map_
.end()) &&
197 (spdy_host_port
->second
== support_spdy
)) {
201 spdy_servers_map_
.Put(host_port_pair
.ToString(), support_spdy
);
204 bool HttpServerPropertiesImpl::RequiresHTTP11(
205 const net::HostPortPair
& host_port_pair
) {
206 DCHECK(CalledOnValidThread());
207 if (host_port_pair
.host().empty())
210 return (http11_servers_
.find(host_port_pair
) != http11_servers_
.end());
213 void HttpServerPropertiesImpl::SetHTTP11Required(
214 const net::HostPortPair
& host_port_pair
) {
215 DCHECK(CalledOnValidThread());
216 if (host_port_pair
.host().empty())
219 http11_servers_
.insert(host_port_pair
);
222 void HttpServerPropertiesImpl::MaybeForceHTTP11(const HostPortPair
& server
,
223 SSLConfig
* ssl_config
) {
224 if (RequiresHTTP11(server
)) {
225 ForceHTTP11(ssl_config
);
229 std::string
HttpServerPropertiesImpl::GetCanonicalSuffix(
230 const std::string
& host
) {
231 // If this host ends with a canonical suffix, then return the canonical
233 for (size_t i
= 0; i
< canonical_suffixes_
.size(); ++i
) {
234 std::string canonical_suffix
= canonical_suffixes_
[i
];
235 if (EndsWith(host
, canonical_suffixes_
[i
], false)) {
236 return canonical_suffix
;
239 return std::string();
242 AlternativeService
HttpServerPropertiesImpl::GetAlternativeService(
243 const HostPortPair
& origin
) {
244 AlternateProtocolMap::const_iterator it
=
245 GetAlternateProtocolIterator(origin
);
246 if (it
!= alternate_protocol_map_
.end() &&
247 it
->second
.probability
>= alternate_protocol_probability_threshold_
)
248 return AlternativeService(it
->second
.protocol
, origin
.host(),
251 UMA_HISTOGRAM_BOOLEAN("Net.ForceAlternativeService",
252 g_forced_alternate_protocol
!= nullptr);
253 if (g_forced_alternate_protocol
)
254 return AlternativeService(g_forced_alternate_protocol
->protocol
,
255 origin
.host(), g_forced_alternate_protocol
->port
);
257 AlternativeService uninitialize_alternative_service
;
258 return uninitialize_alternative_service
;
261 void HttpServerPropertiesImpl::SetAlternativeService(
262 const HostPortPair
& origin
,
263 const AlternativeService
& alternative_service
,
264 double alternative_probability
) {
265 if (IsAlternativeServiceBroken(alternative_service
)) {
266 DVLOG(1) << "Ignore alternative service since it is known to be broken.";
270 const AlternateProtocolInfo
alternate(alternative_service
.port
,
271 alternative_service
.protocol
,
272 alternative_probability
);
273 AlternateProtocolMap::const_iterator it
=
274 GetAlternateProtocolIterator(origin
);
275 if (it
!= alternate_protocol_map_
.end()) {
276 const AlternateProtocolInfo existing_alternate
= it
->second
;
278 if (!existing_alternate
.Equals(alternate
)) {
279 LOG(WARNING
) << "Changing the alternate protocol for: "
281 << " from [Port: " << existing_alternate
.port
282 << ", Protocol: " << existing_alternate
.protocol
283 << ", Probability: " << existing_alternate
.probability
284 << "] to [Port: " << alternative_service
.port
285 << ", Protocol: " << alternative_service
.protocol
286 << ", Probability: " << alternative_probability
<< "].";
289 if (alternative_probability
>= alternate_protocol_probability_threshold_
) {
290 // TODO(rch): Consider the case where multiple requests are started
291 // before the first completes. In this case, only one of the jobs
292 // would reach this code, whereas all of them should should have.
293 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING
);
297 alternate_protocol_map_
.Put(origin
, alternate
);
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 (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 if (alternative_service
.protocol
== UNINITIALIZED_ALTERNATE_PROTOCOL
) {
314 LOG(DFATAL
) << "Trying to mark unknown alternate protocol broken.";
317 int count
= ++recently_broken_alternative_services_
[alternative_service
];
318 base::TimeDelta delay
=
319 base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs
);
320 base::TimeTicks when
= base::TimeTicks::Now() + delay
* (1 << (count
- 1));
321 auto result
= broken_alternative_services_
.insert(
322 std::make_pair(alternative_service
, when
));
323 // Return if alternative service is already in expiration queue.
324 if (!result
.second
) {
328 // If this is the only entry in the list, schedule an expiration task.
329 // Otherwise it will be rescheduled automatically when the pending task runs.
330 if (broken_alternative_services_
.size() == 1) {
331 ScheduleBrokenAlternateProtocolMappingsExpiration();
335 void HttpServerPropertiesImpl::MarkAlternativeServiceRecentlyBroken(
336 const AlternativeService
& alternative_service
) {
337 if (!ContainsKey(recently_broken_alternative_services_
, alternative_service
))
338 recently_broken_alternative_services_
[alternative_service
] = 1;
341 bool HttpServerPropertiesImpl::IsAlternativeServiceBroken(
342 const AlternativeService
& alternative_service
) {
343 return ContainsKey(broken_alternative_services_
, alternative_service
);
346 bool HttpServerPropertiesImpl::WasAlternativeServiceRecentlyBroken(
347 const AlternativeService
& alternative_service
) {
348 if (alternative_service
.protocol
== UNINITIALIZED_ALTERNATE_PROTOCOL
)
350 return ContainsKey(recently_broken_alternative_services_
,
351 alternative_service
);
354 void HttpServerPropertiesImpl::ConfirmAlternativeService(
355 const AlternativeService
& alternative_service
) {
356 if (alternative_service
.protocol
== UNINITIALIZED_ALTERNATE_PROTOCOL
)
358 broken_alternative_services_
.erase(alternative_service
);
359 recently_broken_alternative_services_
.erase(alternative_service
);
362 void HttpServerPropertiesImpl::ClearAlternativeService(
363 const HostPortPair
& origin
) {
364 RemoveCanonicalHost(origin
);
366 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Peek(origin
);
367 if (it
== alternate_protocol_map_
.end()) {
370 const AlternativeService
alternative_service(
371 it
->second
.protocol
, it
->first
.host(), it
->second
.port
);
372 alternate_protocol_map_
.Erase(it
);
374 // The following is temporary to keep the existing semantics, which is that if
375 // there is a broken alternative service in the mapping, then this method
376 // leaves it in a non-broken, but recently broken state.
379 // 1. Verify and document the class invariant that no broken alternative
380 // service can be in the mapping.
381 // 2. Remove the rest of this method as it will be moot.
382 // 3. Provide a SetAlternativeServiceRecentlyBroken if necessary.
383 broken_alternative_services_
.erase(alternative_service
);
386 const AlternateProtocolMap
&
387 HttpServerPropertiesImpl::alternate_protocol_map() const {
388 return alternate_protocol_map_
;
391 const SettingsMap
& HttpServerPropertiesImpl::GetSpdySettings(
392 const HostPortPair
& host_port_pair
) {
393 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
394 if (it
== spdy_settings_map_
.end()) {
395 CR_DEFINE_STATIC_LOCAL(SettingsMap
, kEmptySettingsMap
, ());
396 return kEmptySettingsMap
;
401 bool HttpServerPropertiesImpl::SetSpdySetting(
402 const HostPortPair
& host_port_pair
,
404 SpdySettingsFlags flags
,
406 if (!(flags
& SETTINGS_FLAG_PLEASE_PERSIST
))
409 SettingsFlagsAndValue
flags_and_value(SETTINGS_FLAG_PERSISTED
, value
);
410 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
411 if (it
== spdy_settings_map_
.end()) {
412 SettingsMap settings_map
;
413 settings_map
[id
] = flags_and_value
;
414 spdy_settings_map_
.Put(host_port_pair
, settings_map
);
416 SettingsMap
& settings_map
= it
->second
;
417 settings_map
[id
] = flags_and_value
;
422 void HttpServerPropertiesImpl::ClearSpdySettings(
423 const HostPortPair
& host_port_pair
) {
424 SpdySettingsMap::iterator it
= spdy_settings_map_
.Peek(host_port_pair
);
425 if (it
!= spdy_settings_map_
.end())
426 spdy_settings_map_
.Erase(it
);
429 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
430 spdy_settings_map_
.Clear();
433 const SpdySettingsMap
&
434 HttpServerPropertiesImpl::spdy_settings_map() const {
435 return spdy_settings_map_
;
438 bool HttpServerPropertiesImpl::GetSupportsQuic(
439 IPAddressNumber
* last_address
) const {
440 if (last_quic_address_
.empty())
443 *last_address
= last_quic_address_
;
447 void HttpServerPropertiesImpl::SetSupportsQuic(bool used_quic
,
448 const IPAddressNumber
& address
) {
450 last_quic_address_
.clear();
452 last_quic_address_
= address
;
456 void HttpServerPropertiesImpl::SetServerNetworkStats(
457 const HostPortPair
& host_port_pair
,
458 ServerNetworkStats stats
) {
459 server_network_stats_map_
.Put(host_port_pair
, stats
);
462 const ServerNetworkStats
* HttpServerPropertiesImpl::GetServerNetworkStats(
463 const HostPortPair
& host_port_pair
) {
464 ServerNetworkStatsMap::iterator it
=
465 server_network_stats_map_
.Get(host_port_pair
);
466 if (it
== server_network_stats_map_
.end()) {
472 const ServerNetworkStatsMap
&
473 HttpServerPropertiesImpl::server_network_stats_map() const {
474 return server_network_stats_map_
;
477 void HttpServerPropertiesImpl::SetAlternateProtocolProbabilityThreshold(
479 alternate_protocol_probability_threshold_
= threshold
;
482 AlternateProtocolMap::const_iterator
483 HttpServerPropertiesImpl::GetAlternateProtocolIterator(
484 const HostPortPair
& server
) {
485 AlternateProtocolMap::const_iterator it
= alternate_protocol_map_
.Get(server
);
486 if (it
!= alternate_protocol_map_
.end())
489 CanonicalHostMap::const_iterator canonical
= GetCanonicalHost(server
);
490 if (canonical
== canonical_host_to_origin_map_
.end()) {
491 return alternate_protocol_map_
.end();
494 const HostPortPair canonical_host_port
= canonical
->second
;
495 it
= alternate_protocol_map_
.Get(canonical_host_port
);
496 if (it
== alternate_protocol_map_
.end()) {
497 return alternate_protocol_map_
.end();
500 const AlternativeService
alternative_service(
501 it
->second
.protocol
, canonical_host_port
.host(), it
->second
.port
);
502 if (!IsAlternativeServiceBroken(alternative_service
)) {
506 RemoveCanonicalHost(canonical_host_port
);
507 return alternate_protocol_map_
.end();
510 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
511 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server
) const {
512 for (size_t i
= 0; i
< canonical_suffixes_
.size(); ++i
) {
513 std::string canonical_suffix
= canonical_suffixes_
[i
];
514 if (EndsWith(server
.host(), canonical_suffixes_
[i
], false)) {
515 HostPortPair
canonical_host(canonical_suffix
, server
.port());
516 return canonical_host_to_origin_map_
.find(canonical_host
);
520 return canonical_host_to_origin_map_
.end();
523 void HttpServerPropertiesImpl::RemoveCanonicalHost(
524 const HostPortPair
& server
) {
525 CanonicalHostMap::const_iterator canonical
= GetCanonicalHost(server
);
526 if (canonical
== canonical_host_to_origin_map_
.end())
529 if (!canonical
->second
.Equals(server
))
532 canonical_host_to_origin_map_
.erase(canonical
->first
);
535 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() {
536 base::TimeTicks now
= base::TimeTicks::Now();
537 while (!broken_alternative_services_
.empty()) {
538 BrokenAlternativeServices::iterator it
=
539 broken_alternative_services_
.begin();
540 if (now
< it
->second
) {
544 const AlternativeService alternative_service
= it
->first
;
545 broken_alternative_services_
.erase(it
);
546 ClearAlternativeService(
547 HostPortPair(alternative_service
.host
, alternative_service
.port
));
549 ScheduleBrokenAlternateProtocolMappingsExpiration();
553 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
554 if (broken_alternative_services_
.empty()) {
557 base::TimeTicks now
= base::TimeTicks::Now();
558 base::TimeTicks when
= broken_alternative_services_
.front().second
;
559 base::TimeDelta delay
= when
> now
? when
- now
: base::TimeDelta();
560 base::MessageLoop::current()->PostDelayedTask(
563 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings
,
564 weak_ptr_factory_
.GetWeakPtr()),