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 alternate_protocol_experiment_(
27 ALTERNATE_PROTOCOL_NOT_PART_OF_EXPERIMENT
),
28 spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT
),
29 alternate_protocol_probability_threshold_(1),
30 weak_ptr_factory_(this) {
31 canoncial_suffixes_
.push_back(".c.youtube.com");
32 canoncial_suffixes_
.push_back(".googlevideo.com");
33 canoncial_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 ALTERNATE_PROTOCOL_BROKEN ones since those don't
56 for (AlternateProtocolMap::iterator it
= alternate_protocol_map_
.begin();
57 it
!= alternate_protocol_map_
.end();) {
58 AlternateProtocolMap::iterator old_it
= it
;
60 if (old_it
->second
.protocol
!= ALTERNATE_PROTOCOL_BROKEN
) {
61 alternate_protocol_map_
.Erase(old_it
);
65 // Add the entries from persisted data.
66 for (AlternateProtocolMap::reverse_iterator it
=
67 alternate_protocol_map
->rbegin();
68 it
!= alternate_protocol_map
->rend(); ++it
) {
69 alternate_protocol_map_
.Put(it
->first
, it
->second
);
72 // Attempt to find canonical servers.
73 int canonical_ports
[] = { 80, 443 };
74 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
75 std::string canonical_suffix
= canoncial_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 (alternate_protocol_map_
.Peek(canonical_host_to_origin_map_
[
81 canonical_host
]) != alternate_protocol_map_
.end())) {
84 // Now attempt to find a server which matches this origin and set it as
86 for (AlternateProtocolMap::const_iterator it
=
87 alternate_protocol_map_
.begin();
88 it
!= alternate_protocol_map_
.end(); ++it
) {
89 if (EndsWith(it
->first
.host(), canoncial_suffixes_
[i
], false)) {
90 canonical_host_to_origin_map_
[canonical_host
] = it
->first
;
98 void HttpServerPropertiesImpl::InitializeSpdySettingsServers(
99 SpdySettingsMap
* spdy_settings_map
) {
100 for (SpdySettingsMap::reverse_iterator it
= spdy_settings_map
->rbegin();
101 it
!= spdy_settings_map
->rend(); ++it
) {
102 spdy_settings_map_
.Put(it
->first
, it
->second
);
106 void HttpServerPropertiesImpl::InitializeSupportsQuic(
107 SupportsQuicMap
* supports_quic_map
) {
108 for (SupportsQuicMap::reverse_iterator it
= supports_quic_map
->rbegin();
109 it
!= supports_quic_map
->rend();
111 supports_quic_map_
.insert(std::make_pair(it
->first
, it
->second
));
115 void HttpServerPropertiesImpl::GetSpdyServerList(
116 base::ListValue
* spdy_server_list
,
117 size_t max_size
) const {
118 DCHECK(CalledOnValidThread());
119 DCHECK(spdy_server_list
);
120 spdy_server_list
->Clear();
122 // Get the list of servers (host/port) that support SPDY.
123 for (SpdyServerHostPortMap::const_iterator it
= spdy_servers_map_
.begin();
124 it
!= spdy_servers_map_
.end() && count
< max_size
; ++it
) {
125 const std::string spdy_server_host_port
= it
->first
;
127 spdy_server_list
->Append(new base::StringValue(spdy_server_host_port
));
134 std::string
HttpServerPropertiesImpl::GetFlattenedSpdyServer(
135 const net::HostPortPair
& host_port_pair
) {
136 std::string spdy_server
;
137 spdy_server
.append(host_port_pair
.host());
138 spdy_server
.append(":");
139 base::StringAppendF(&spdy_server
, "%d", host_port_pair
.port());
143 static const AlternateProtocolInfo
* g_forced_alternate_protocol
= NULL
;
146 void HttpServerPropertiesImpl::ForceAlternateProtocol(
147 const AlternateProtocolInfo
& info
) {
148 // Note: we're going to leak this.
149 if (g_forced_alternate_protocol
)
150 delete g_forced_alternate_protocol
;
151 g_forced_alternate_protocol
= new AlternateProtocolInfo(info
);
155 void HttpServerPropertiesImpl::DisableForcedAlternateProtocol() {
156 delete g_forced_alternate_protocol
;
157 g_forced_alternate_protocol
= NULL
;
160 base::WeakPtr
<HttpServerProperties
> HttpServerPropertiesImpl::GetWeakPtr() {
161 return weak_ptr_factory_
.GetWeakPtr();
164 void HttpServerPropertiesImpl::Clear() {
165 DCHECK(CalledOnValidThread());
166 spdy_servers_map_
.Clear();
167 alternate_protocol_map_
.Clear();
168 canonical_host_to_origin_map_
.clear();
169 spdy_settings_map_
.Clear();
170 supports_quic_map_
.clear();
173 bool HttpServerPropertiesImpl::SupportsSpdy(
174 const net::HostPortPair
& host_port_pair
) {
175 DCHECK(CalledOnValidThread());
176 if (host_port_pair
.host().empty())
178 std::string spdy_server
= GetFlattenedSpdyServer(host_port_pair
);
180 SpdyServerHostPortMap::iterator spdy_host_port
=
181 spdy_servers_map_
.Get(spdy_server
);
182 if (spdy_host_port
!= spdy_servers_map_
.end())
183 return spdy_host_port
->second
;
187 void HttpServerPropertiesImpl::SetSupportsSpdy(
188 const net::HostPortPair
& host_port_pair
,
190 DCHECK(CalledOnValidThread());
191 if (host_port_pair
.host().empty())
193 std::string spdy_server
= GetFlattenedSpdyServer(host_port_pair
);
195 SpdyServerHostPortMap::iterator spdy_host_port
=
196 spdy_servers_map_
.Get(spdy_server
);
197 if ((spdy_host_port
!= spdy_servers_map_
.end()) &&
198 (spdy_host_port
->second
== support_spdy
)) {
202 spdy_servers_map_
.Put(spdy_server
, support_spdy
);
205 bool HttpServerPropertiesImpl::HasAlternateProtocol(
206 const HostPortPair
& server
) {
207 if (g_forced_alternate_protocol
)
209 AlternateProtocolMap::const_iterator it
= alternate_protocol_map_
.Get(server
);
210 if (it
!= alternate_protocol_map_
.end() &&
211 it
->second
.probability
>= alternate_protocol_probability_threshold_
) {
215 return GetCanonicalHost(server
) != canonical_host_to_origin_map_
.end();
218 std::string
HttpServerPropertiesImpl::GetCanonicalSuffix(
219 const HostPortPair
& server
) {
220 // If this host ends with a canonical suffix, then return the canonical
222 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
223 std::string canonical_suffix
= canoncial_suffixes_
[i
];
224 if (EndsWith(server
.host(), canoncial_suffixes_
[i
], false)) {
225 return canonical_suffix
;
228 return std::string();
231 AlternateProtocolInfo
232 HttpServerPropertiesImpl::GetAlternateProtocol(
233 const HostPortPair
& server
) {
234 DCHECK(HasAlternateProtocol(server
));
236 // First check the map.
237 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Get(server
);
238 if (it
!= alternate_protocol_map_
.end())
241 // Next check the canonical host.
242 CanonicalHostMap::const_iterator canonical_host
= GetCanonicalHost(server
);
243 if (canonical_host
!= canonical_host_to_origin_map_
.end())
244 return alternate_protocol_map_
.Get(canonical_host
->second
)->second
;
246 // We must be forcing an alternate.
247 DCHECK(g_forced_alternate_protocol
);
248 return *g_forced_alternate_protocol
;
251 void HttpServerPropertiesImpl::SetAlternateProtocol(
252 const HostPortPair
& server
,
253 uint16 alternate_port
,
254 AlternateProtocol alternate_protocol
,
255 double alternate_probability
) {
256 if (alternate_protocol
== ALTERNATE_PROTOCOL_BROKEN
) {
257 LOG(DFATAL
) << "Call SetBrokenAlternateProtocol() instead.";
261 AlternateProtocolInfo
alternate(alternate_port
,
263 alternate_probability
);
264 if (HasAlternateProtocol(server
)) {
265 const AlternateProtocolInfo existing_alternate
=
266 GetAlternateProtocol(server
);
268 if (existing_alternate
.protocol
== ALTERNATE_PROTOCOL_BROKEN
) {
269 DVLOG(1) << "Ignore alternate protocol since it's known to be broken.";
273 if (alternate_protocol
!= ALTERNATE_PROTOCOL_BROKEN
&&
274 !existing_alternate
.Equals(alternate
)) {
275 LOG(WARNING
) << "Changing the alternate protocol for: "
277 << " from [Port: " << existing_alternate
.port
278 << ", Protocol: " << existing_alternate
.protocol
279 << ", Probability: " << existing_alternate
.probability
280 << "] to [Port: " << alternate_port
281 << ", Protocol: " << alternate_protocol
282 << ", Probability: " << alternate_probability
286 if (alternate_probability
>= alternate_protocol_probability_threshold_
) {
287 // TODO(rch): Consider the case where multiple requests are started
288 // before the first completes. In this case, only one of the jobs
289 // would reach this code, whereas all of them should should have.
290 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING
,
291 alternate_protocol_experiment_
);
295 alternate_protocol_map_
.Put(server
, alternate
);
297 // If this host ends with a canonical suffix, then set it as the
299 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
300 std::string canonical_suffix
= canoncial_suffixes_
[i
];
301 if (EndsWith(server
.host(), canoncial_suffixes_
[i
], false)) {
302 HostPortPair
canonical_host(canonical_suffix
, server
.port());
303 canonical_host_to_origin_map_
[canonical_host
] = server
;
309 void HttpServerPropertiesImpl::SetBrokenAlternateProtocol(
310 const HostPortPair
& server
) {
311 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Get(server
);
312 if (it
!= alternate_protocol_map_
.end()) {
313 it
->second
.protocol
= ALTERNATE_PROTOCOL_BROKEN
;
315 AlternateProtocolInfo
alternate(server
.port(),
316 ALTERNATE_PROTOCOL_BROKEN
,
318 alternate_protocol_map_
.Put(server
, alternate
);
320 int count
= ++broken_alternate_protocol_map_
[server
];
321 base::TimeDelta delay
=
322 base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs
);
323 BrokenAlternateProtocolEntry entry
;
324 entry
.server
= server
;
325 entry
.when
= base::TimeTicks::Now() + delay
* (1 << (count
- 1));
326 broken_alternate_protocol_list_
.push_back(entry
);
328 // Do not leave this host as canonical so that we don't infer the other
329 // hosts are also broken without testing them first.
330 RemoveCanonicalHost(server
);
332 // If this is the only entry in the list, schedule an expiration task.
333 // Otherwse it will be rescheduled automatically when the pending
335 if (broken_alternate_protocol_list_
.size() == 1) {
336 ScheduleBrokenAlternateProtocolMappingsExpiration();
340 bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken(
341 const HostPortPair
& server
) {
342 return ContainsKey(broken_alternate_protocol_map_
, server
);
345 void HttpServerPropertiesImpl::ConfirmAlternateProtocol(
346 const HostPortPair
& server
) {
347 broken_alternate_protocol_map_
.erase(server
);
350 void HttpServerPropertiesImpl::ClearAlternateProtocol(
351 const HostPortPair
& server
) {
352 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Peek(server
);
353 if (it
!= alternate_protocol_map_
.end())
354 alternate_protocol_map_
.Erase(it
);
356 RemoveCanonicalHost(server
);
359 const AlternateProtocolMap
&
360 HttpServerPropertiesImpl::alternate_protocol_map() const {
361 return alternate_protocol_map_
;
364 void HttpServerPropertiesImpl::SetAlternateProtocolExperiment(
365 AlternateProtocolExperiment experiment
) {
366 alternate_protocol_experiment_
= experiment
;
369 AlternateProtocolExperiment
370 HttpServerPropertiesImpl::GetAlternateProtocolExperiment() const {
371 return alternate_protocol_experiment_
;
374 const SettingsMap
& HttpServerPropertiesImpl::GetSpdySettings(
375 const HostPortPair
& host_port_pair
) {
376 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
377 if (it
== spdy_settings_map_
.end()) {
378 CR_DEFINE_STATIC_LOCAL(SettingsMap
, kEmptySettingsMap
, ());
379 return kEmptySettingsMap
;
384 bool HttpServerPropertiesImpl::SetSpdySetting(
385 const HostPortPair
& host_port_pair
,
387 SpdySettingsFlags flags
,
389 if (!(flags
& SETTINGS_FLAG_PLEASE_PERSIST
))
392 SettingsFlagsAndValue
flags_and_value(SETTINGS_FLAG_PERSISTED
, value
);
393 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
394 if (it
== spdy_settings_map_
.end()) {
395 SettingsMap settings_map
;
396 settings_map
[id
] = flags_and_value
;
397 spdy_settings_map_
.Put(host_port_pair
, settings_map
);
399 SettingsMap
& settings_map
= it
->second
;
400 settings_map
[id
] = flags_and_value
;
405 void HttpServerPropertiesImpl::ClearSpdySettings(
406 const HostPortPair
& host_port_pair
) {
407 SpdySettingsMap::iterator it
= spdy_settings_map_
.Peek(host_port_pair
);
408 if (it
!= spdy_settings_map_
.end())
409 spdy_settings_map_
.Erase(it
);
412 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
413 spdy_settings_map_
.Clear();
416 const SpdySettingsMap
&
417 HttpServerPropertiesImpl::spdy_settings_map() const {
418 return spdy_settings_map_
;
421 SupportsQuic
HttpServerPropertiesImpl::GetSupportsQuic(
422 const HostPortPair
& host_port_pair
) const {
423 SupportsQuicMap::const_iterator it
= supports_quic_map_
.find(host_port_pair
);
424 if (it
== supports_quic_map_
.end()) {
425 CR_DEFINE_STATIC_LOCAL(SupportsQuic
, kEmptySupportsQuic
, ());
426 return kEmptySupportsQuic
;
431 void HttpServerPropertiesImpl::SetSupportsQuic(
432 const HostPortPair
& host_port_pair
,
434 const std::string
& address
) {
435 SupportsQuic
supports_quic(used_quic
, address
);
436 supports_quic_map_
.insert(std::make_pair(host_port_pair
, supports_quic
));
439 const SupportsQuicMap
&
440 HttpServerPropertiesImpl::supports_quic_map() const {
441 return supports_quic_map_
;
444 void HttpServerPropertiesImpl::SetServerNetworkStats(
445 const HostPortPair
& host_port_pair
,
446 NetworkStats stats
) {
447 server_network_stats_map_
[host_port_pair
] = stats
;
450 const HttpServerProperties::NetworkStats
*
451 HttpServerPropertiesImpl::GetServerNetworkStats(
452 const HostPortPair
& host_port_pair
) const {
453 ServerNetworkStatsMap::const_iterator it
=
454 server_network_stats_map_
.find(host_port_pair
);
455 if (it
== server_network_stats_map_
.end()) {
461 void HttpServerPropertiesImpl::SetAlternateProtocolProbabilityThreshold(
463 alternate_protocol_probability_threshold_
= threshold
;
466 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
467 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server
) const {
468 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
469 std::string canonical_suffix
= canoncial_suffixes_
[i
];
470 if (EndsWith(server
.host(), canoncial_suffixes_
[i
], false)) {
471 HostPortPair
canonical_host(canonical_suffix
, server
.port());
472 return canonical_host_to_origin_map_
.find(canonical_host
);
476 return canonical_host_to_origin_map_
.end();
479 void HttpServerPropertiesImpl::RemoveCanonicalHost(
480 const HostPortPair
& server
) {
481 CanonicalHostMap::const_iterator canonical
= GetCanonicalHost(server
);
482 if (canonical
== canonical_host_to_origin_map_
.end())
485 if (!canonical
->second
.Equals(server
))
488 canonical_host_to_origin_map_
.erase(canonical
->first
);
491 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() {
492 base::TimeTicks now
= base::TimeTicks::Now();
493 while (!broken_alternate_protocol_list_
.empty()) {
494 BrokenAlternateProtocolEntry entry
=
495 broken_alternate_protocol_list_
.front();
496 if (now
< entry
.when
) {
500 ClearAlternateProtocol(entry
.server
);
501 broken_alternate_protocol_list_
.pop_front();
503 ScheduleBrokenAlternateProtocolMappingsExpiration();
507 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
508 if (broken_alternate_protocol_list_
.empty()) {
511 base::TimeTicks now
= base::TimeTicks::Now();
512 base::TimeTicks when
= broken_alternate_protocol_list_
.front().when
;
513 base::TimeDelta delay
= when
> now
? when
- now
: base::TimeDelta();
514 base::MessageLoop::current()->PostDelayedTask(
517 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings
,
518 weak_ptr_factory_
.GetWeakPtr()),