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/stl_util.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
19 const uint64 kBrokenAlternateProtocolDelaySecs
= 300;
23 HttpServerPropertiesImpl::HttpServerPropertiesImpl()
24 : spdy_servers_map_(SpdyServerHostPortMap::NO_AUTO_EVICT
),
25 alternate_protocol_map_(AlternateProtocolMap::NO_AUTO_EVICT
),
26 spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT
),
27 alternate_protocol_probability_threshold_(1),
28 weak_ptr_factory_(this) {
29 canonical_suffixes_
.push_back(".c.youtube.com");
30 canonical_suffixes_
.push_back(".googlevideo.com");
31 canonical_suffixes_
.push_back(".googleusercontent.com");
34 HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
37 void HttpServerPropertiesImpl::InitializeSpdyServers(
38 std::vector
<std::string
>* spdy_servers
,
40 DCHECK(CalledOnValidThread());
43 // Add the entries from persisted data.
44 for (std::vector
<std::string
>::reverse_iterator it
= spdy_servers
->rbegin();
45 it
!= spdy_servers
->rend(); ++it
) {
46 spdy_servers_map_
.Put(*it
, support_spdy
);
50 void HttpServerPropertiesImpl::InitializeAlternateProtocolServers(
51 AlternateProtocolMap
* alternate_protocol_map
) {
52 // Keep all the broken ones since those don't get persisted.
53 for (AlternateProtocolMap::iterator it
= alternate_protocol_map_
.begin();
54 it
!= alternate_protocol_map_
.end();) {
55 AlternateProtocolMap::iterator old_it
= it
;
57 if (!old_it
->second
.is_broken
) {
58 alternate_protocol_map_
.Erase(old_it
);
62 // Add the entries from persisted data.
63 for (AlternateProtocolMap::reverse_iterator it
=
64 alternate_protocol_map
->rbegin();
65 it
!= alternate_protocol_map
->rend(); ++it
) {
66 alternate_protocol_map_
.Put(it
->first
, it
->second
);
69 // Attempt to find canonical servers.
70 uint16 canonical_ports
[] = { 80, 443 };
71 for (size_t i
= 0; i
< canonical_suffixes_
.size(); ++i
) {
72 std::string canonical_suffix
= canonical_suffixes_
[i
];
73 for (size_t j
= 0; j
< arraysize(canonical_ports
); ++j
) {
74 HostPortPair
canonical_host(canonical_suffix
, canonical_ports
[j
]);
75 // If we already have a valid canonical server, we're done.
76 if (ContainsKey(canonical_host_to_origin_map_
, canonical_host
) &&
77 (alternate_protocol_map_
.Peek(canonical_host_to_origin_map_
[
78 canonical_host
]) != alternate_protocol_map_
.end())) {
81 // Now attempt to find a server which matches this origin and set it as
83 for (AlternateProtocolMap::const_iterator it
=
84 alternate_protocol_map_
.begin();
85 it
!= alternate_protocol_map_
.end(); ++it
) {
86 if (EndsWith(it
->first
.host(), canonical_suffixes_
[i
], false)) {
87 canonical_host_to_origin_map_
[canonical_host
] = it
->first
;
95 void HttpServerPropertiesImpl::InitializeSpdySettingsServers(
96 SpdySettingsMap
* spdy_settings_map
) {
97 for (SpdySettingsMap::reverse_iterator it
= spdy_settings_map
->rbegin();
98 it
!= spdy_settings_map
->rend(); ++it
) {
99 spdy_settings_map_
.Put(it
->first
, it
->second
);
103 void HttpServerPropertiesImpl::InitializeSupportsQuic(
104 SupportsQuicMap
* supports_quic_map
) {
105 for (SupportsQuicMap::reverse_iterator it
= supports_quic_map
->rbegin();
106 it
!= supports_quic_map
->rend();
108 supports_quic_map_
.insert(std::make_pair(it
->first
, it
->second
));
112 void HttpServerPropertiesImpl::GetSpdyServerList(
113 base::ListValue
* spdy_server_list
,
114 size_t max_size
) const {
115 DCHECK(CalledOnValidThread());
116 DCHECK(spdy_server_list
);
117 spdy_server_list
->Clear();
119 // Get the list of servers (host/port) that support SPDY.
120 for (SpdyServerHostPortMap::const_iterator it
= spdy_servers_map_
.begin();
121 it
!= spdy_servers_map_
.end() && count
< max_size
; ++it
) {
122 const std::string spdy_server_host_port
= it
->first
;
124 spdy_server_list
->Append(new base::StringValue(spdy_server_host_port
));
130 static const AlternateProtocolInfo
* g_forced_alternate_protocol
= NULL
;
133 void HttpServerPropertiesImpl::ForceAlternateProtocol(
134 const AlternateProtocolInfo
& info
) {
135 // Note: we're going to leak this.
136 if (g_forced_alternate_protocol
)
137 delete g_forced_alternate_protocol
;
138 g_forced_alternate_protocol
= new AlternateProtocolInfo(info
);
142 void HttpServerPropertiesImpl::DisableForcedAlternateProtocol() {
143 delete g_forced_alternate_protocol
;
144 g_forced_alternate_protocol
= NULL
;
147 base::WeakPtr
<HttpServerProperties
> HttpServerPropertiesImpl::GetWeakPtr() {
148 return weak_ptr_factory_
.GetWeakPtr();
151 void HttpServerPropertiesImpl::Clear() {
152 DCHECK(CalledOnValidThread());
153 spdy_servers_map_
.Clear();
154 alternate_protocol_map_
.Clear();
155 canonical_host_to_origin_map_
.clear();
156 spdy_settings_map_
.Clear();
157 supports_quic_map_
.clear();
160 bool HttpServerPropertiesImpl::SupportsSpdy(
161 const net::HostPortPair
& host_port_pair
) {
162 DCHECK(CalledOnValidThread());
163 if (host_port_pair
.host().empty())
166 SpdyServerHostPortMap::iterator spdy_host_port
=
167 spdy_servers_map_
.Get(host_port_pair
.ToString());
168 if (spdy_host_port
!= spdy_servers_map_
.end())
169 return spdy_host_port
->second
;
173 void HttpServerPropertiesImpl::SetSupportsSpdy(
174 const net::HostPortPair
& host_port_pair
,
176 DCHECK(CalledOnValidThread());
177 if (host_port_pair
.host().empty())
180 SpdyServerHostPortMap::iterator spdy_host_port
=
181 spdy_servers_map_
.Get(host_port_pair
.ToString());
182 if ((spdy_host_port
!= spdy_servers_map_
.end()) &&
183 (spdy_host_port
->second
== support_spdy
)) {
187 spdy_servers_map_
.Put(host_port_pair
.ToString(), support_spdy
);
190 bool HttpServerPropertiesImpl::HasAlternateProtocol(
191 const HostPortPair
& server
) {
192 if (g_forced_alternate_protocol
)
194 AlternateProtocolMap::const_iterator it
= alternate_protocol_map_
.Get(server
);
195 if (it
!= alternate_protocol_map_
.end())
196 return it
->second
.probability
>= alternate_protocol_probability_threshold_
;
198 auto canonical
= GetCanonicalHost(server
);
199 if (canonical
== canonical_host_to_origin_map_
.end() ||
200 canonical
->second
.Equals(server
)) {
204 return HasAlternateProtocol(canonical
->second
);
207 std::string
HttpServerPropertiesImpl::GetCanonicalSuffix(
208 const HostPortPair
& server
) {
209 // If this host ends with a canonical suffix, then return the canonical
211 for (size_t i
= 0; i
< canonical_suffixes_
.size(); ++i
) {
212 std::string canonical_suffix
= canonical_suffixes_
[i
];
213 if (EndsWith(server
.host(), canonical_suffixes_
[i
], false)) {
214 return canonical_suffix
;
217 return std::string();
220 AlternateProtocolInfo
221 HttpServerPropertiesImpl::GetAlternateProtocol(
222 const HostPortPair
& server
) {
223 DCHECK(HasAlternateProtocol(server
));
225 // First check the map.
226 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Get(server
);
227 if (it
!= alternate_protocol_map_
.end())
230 // Next check the canonical host.
231 CanonicalHostMap::const_iterator canonical_host
= GetCanonicalHost(server
);
232 if (canonical_host
!= canonical_host_to_origin_map_
.end())
233 return alternate_protocol_map_
.Get(canonical_host
->second
)->second
;
235 // We must be forcing an alternate.
236 DCHECK(g_forced_alternate_protocol
);
237 return *g_forced_alternate_protocol
;
240 void HttpServerPropertiesImpl::SetAlternateProtocol(
241 const HostPortPair
& server
,
242 uint16 alternate_port
,
243 AlternateProtocol alternate_protocol
,
244 double alternate_probability
) {
246 AlternateProtocolInfo
alternate(alternate_port
,
248 alternate_probability
);
249 if (HasAlternateProtocol(server
)) {
250 const AlternateProtocolInfo existing_alternate
=
251 GetAlternateProtocol(server
);
253 if (existing_alternate
.is_broken
) {
254 DVLOG(1) << "Ignore alternate protocol since it's known to be broken.";
258 if (!existing_alternate
.Equals(alternate
)) {
259 LOG(WARNING
) << "Changing the alternate protocol for: "
261 << " from [Port: " << existing_alternate
.port
262 << ", Protocol: " << existing_alternate
.protocol
263 << ", Probability: " << existing_alternate
.probability
264 << "] to [Port: " << alternate_port
265 << ", Protocol: " << alternate_protocol
266 << ", Probability: " << alternate_probability
270 if (alternate_probability
>= alternate_protocol_probability_threshold_
) {
271 // TODO(rch): Consider the case where multiple requests are started
272 // before the first completes. In this case, only one of the jobs
273 // would reach this code, whereas all of them should should have.
274 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING
);
278 alternate_protocol_map_
.Put(server
, alternate
);
280 // If this host ends with a canonical suffix, then set it as the
282 for (size_t i
= 0; i
< canonical_suffixes_
.size(); ++i
) {
283 std::string canonical_suffix
= canonical_suffixes_
[i
];
284 if (EndsWith(server
.host(), canonical_suffixes_
[i
], false)) {
285 HostPortPair
canonical_host(canonical_suffix
, server
.port());
286 canonical_host_to_origin_map_
[canonical_host
] = server
;
292 void HttpServerPropertiesImpl::SetBrokenAlternateProtocol(
293 const HostPortPair
& server
) {
294 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Get(server
);
295 if (it
== alternate_protocol_map_
.end()) {
296 if (!HasAlternateProtocol(server
)) {
297 LOG(DFATAL
) << "Trying to mark unknown alternate protocol broken.";
300 // This server's alternate protocol information is coming from a canonical
301 // server. Add an entry in the map for this server explicitly so that
302 // it can be marked as broken.
303 it
= alternate_protocol_map_
.Put(server
, GetAlternateProtocol(server
));
305 it
->second
.is_broken
= true;
306 int count
= ++broken_alternate_protocol_map_
[server
];
307 base::TimeDelta delay
=
308 base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs
);
309 BrokenAlternateProtocolEntry entry
;
310 entry
.server
= server
;
311 entry
.when
= base::TimeTicks::Now() + delay
* (1 << (count
- 1));
312 broken_alternate_protocol_list_
.push_back(entry
);
314 // Do not leave this host as canonical so that we don't infer the other
315 // hosts are also broken without testing them first.
316 RemoveCanonicalHost(server
);
318 // If this is the only entry in the list, schedule an expiration task.
319 // Otherwse it will be rescheduled automatically when the pending
321 if (broken_alternate_protocol_list_
.size() == 1) {
322 ScheduleBrokenAlternateProtocolMappingsExpiration();
326 bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken(
327 const HostPortPair
& server
) {
328 return ContainsKey(broken_alternate_protocol_map_
, server
);
331 void HttpServerPropertiesImpl::ConfirmAlternateProtocol(
332 const HostPortPair
& server
) {
333 broken_alternate_protocol_map_
.erase(server
);
336 void HttpServerPropertiesImpl::ClearAlternateProtocol(
337 const HostPortPair
& server
) {
338 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Peek(server
);
339 if (it
!= alternate_protocol_map_
.end())
340 alternate_protocol_map_
.Erase(it
);
342 RemoveCanonicalHost(server
);
345 const AlternateProtocolMap
&
346 HttpServerPropertiesImpl::alternate_protocol_map() const {
347 return alternate_protocol_map_
;
350 const SettingsMap
& HttpServerPropertiesImpl::GetSpdySettings(
351 const HostPortPair
& host_port_pair
) {
352 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
353 if (it
== spdy_settings_map_
.end()) {
354 CR_DEFINE_STATIC_LOCAL(SettingsMap
, kEmptySettingsMap
, ());
355 return kEmptySettingsMap
;
360 bool HttpServerPropertiesImpl::SetSpdySetting(
361 const HostPortPair
& host_port_pair
,
363 SpdySettingsFlags flags
,
365 if (!(flags
& SETTINGS_FLAG_PLEASE_PERSIST
))
368 SettingsFlagsAndValue
flags_and_value(SETTINGS_FLAG_PERSISTED
, value
);
369 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
370 if (it
== spdy_settings_map_
.end()) {
371 SettingsMap settings_map
;
372 settings_map
[id
] = flags_and_value
;
373 spdy_settings_map_
.Put(host_port_pair
, settings_map
);
375 SettingsMap
& settings_map
= it
->second
;
376 settings_map
[id
] = flags_and_value
;
381 void HttpServerPropertiesImpl::ClearSpdySettings(
382 const HostPortPair
& host_port_pair
) {
383 SpdySettingsMap::iterator it
= spdy_settings_map_
.Peek(host_port_pair
);
384 if (it
!= spdy_settings_map_
.end())
385 spdy_settings_map_
.Erase(it
);
388 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
389 spdy_settings_map_
.Clear();
392 const SpdySettingsMap
&
393 HttpServerPropertiesImpl::spdy_settings_map() const {
394 return spdy_settings_map_
;
397 SupportsQuic
HttpServerPropertiesImpl::GetSupportsQuic(
398 const HostPortPair
& host_port_pair
) const {
399 SupportsQuicMap::const_iterator it
= supports_quic_map_
.find(host_port_pair
);
400 if (it
== supports_quic_map_
.end()) {
401 CR_DEFINE_STATIC_LOCAL(SupportsQuic
, kEmptySupportsQuic
, ());
402 return kEmptySupportsQuic
;
407 void HttpServerPropertiesImpl::SetSupportsQuic(
408 const HostPortPair
& host_port_pair
,
410 const std::string
& address
) {
411 SupportsQuic
supports_quic(used_quic
, address
);
412 supports_quic_map_
.insert(std::make_pair(host_port_pair
, supports_quic
));
415 const SupportsQuicMap
&
416 HttpServerPropertiesImpl::supports_quic_map() const {
417 return supports_quic_map_
;
420 void HttpServerPropertiesImpl::SetServerNetworkStats(
421 const HostPortPair
& host_port_pair
,
422 NetworkStats stats
) {
423 server_network_stats_map_
[host_port_pair
] = stats
;
426 const HttpServerProperties::NetworkStats
*
427 HttpServerPropertiesImpl::GetServerNetworkStats(
428 const HostPortPair
& host_port_pair
) const {
429 ServerNetworkStatsMap::const_iterator it
=
430 server_network_stats_map_
.find(host_port_pair
);
431 if (it
== server_network_stats_map_
.end()) {
437 void HttpServerPropertiesImpl::SetAlternateProtocolProbabilityThreshold(
439 alternate_protocol_probability_threshold_
= threshold
;
442 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
443 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server
) const {
444 for (size_t i
= 0; i
< canonical_suffixes_
.size(); ++i
) {
445 std::string canonical_suffix
= canonical_suffixes_
[i
];
446 if (EndsWith(server
.host(), canonical_suffixes_
[i
], false)) {
447 HostPortPair
canonical_host(canonical_suffix
, server
.port());
448 return canonical_host_to_origin_map_
.find(canonical_host
);
452 return canonical_host_to_origin_map_
.end();
455 void HttpServerPropertiesImpl::RemoveCanonicalHost(
456 const HostPortPair
& server
) {
457 CanonicalHostMap::const_iterator canonical
= GetCanonicalHost(server
);
458 if (canonical
== canonical_host_to_origin_map_
.end())
461 if (!canonical
->second
.Equals(server
))
464 canonical_host_to_origin_map_
.erase(canonical
->first
);
467 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() {
468 base::TimeTicks now
= base::TimeTicks::Now();
469 while (!broken_alternate_protocol_list_
.empty()) {
470 BrokenAlternateProtocolEntry entry
=
471 broken_alternate_protocol_list_
.front();
472 if (now
< entry
.when
) {
476 ClearAlternateProtocol(entry
.server
);
477 broken_alternate_protocol_list_
.pop_front();
479 ScheduleBrokenAlternateProtocolMappingsExpiration();
483 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
484 if (broken_alternate_protocol_list_
.empty()) {
487 base::TimeTicks now
= base::TimeTicks::Now();
488 base::TimeTicks when
= broken_alternate_protocol_list_
.front().when
;
489 base::TimeDelta delay
= when
> now
? when
- now
: base::TimeDelta();
490 base::MessageLoop::current()->PostDelayedTask(
493 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings
,
494 weak_ptr_factory_
.GetWeakPtr()),