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_(0),
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::GetSpdyServerList(
107 base::ListValue
* spdy_server_list
,
108 size_t max_size
) const {
109 DCHECK(CalledOnValidThread());
110 DCHECK(spdy_server_list
);
111 spdy_server_list
->Clear();
113 // Get the list of servers (host/port) that support SPDY.
114 for (SpdyServerHostPortMap::const_iterator it
= spdy_servers_map_
.begin();
115 it
!= spdy_servers_map_
.end() && count
< max_size
; ++it
) {
116 const std::string spdy_server_host_port
= it
->first
;
118 spdy_server_list
->Append(new base::StringValue(spdy_server_host_port
));
125 std::string
HttpServerPropertiesImpl::GetFlattenedSpdyServer(
126 const net::HostPortPair
& host_port_pair
) {
127 std::string spdy_server
;
128 spdy_server
.append(host_port_pair
.host());
129 spdy_server
.append(":");
130 base::StringAppendF(&spdy_server
, "%d", host_port_pair
.port());
134 static const AlternateProtocolInfo
* g_forced_alternate_protocol
= NULL
;
137 void HttpServerPropertiesImpl::ForceAlternateProtocol(
138 const AlternateProtocolInfo
& info
) {
139 // Note: we're going to leak this.
140 if (g_forced_alternate_protocol
)
141 delete g_forced_alternate_protocol
;
142 g_forced_alternate_protocol
= new AlternateProtocolInfo(info
);
146 void HttpServerPropertiesImpl::DisableForcedAlternateProtocol() {
147 delete g_forced_alternate_protocol
;
148 g_forced_alternate_protocol
= NULL
;
151 base::WeakPtr
<HttpServerProperties
> HttpServerPropertiesImpl::GetWeakPtr() {
152 return weak_ptr_factory_
.GetWeakPtr();
155 void HttpServerPropertiesImpl::Clear() {
156 DCHECK(CalledOnValidThread());
157 spdy_servers_map_
.Clear();
158 alternate_protocol_map_
.Clear();
159 canonical_host_to_origin_map_
.clear();
160 spdy_settings_map_
.Clear();
163 bool HttpServerPropertiesImpl::SupportsSpdy(
164 const net::HostPortPair
& host_port_pair
) {
165 DCHECK(CalledOnValidThread());
166 if (host_port_pair
.host().empty())
168 std::string spdy_server
= GetFlattenedSpdyServer(host_port_pair
);
170 SpdyServerHostPortMap::iterator spdy_host_port
=
171 spdy_servers_map_
.Get(spdy_server
);
172 if (spdy_host_port
!= spdy_servers_map_
.end())
173 return spdy_host_port
->second
;
177 void HttpServerPropertiesImpl::SetSupportsSpdy(
178 const net::HostPortPair
& host_port_pair
,
180 DCHECK(CalledOnValidThread());
181 if (host_port_pair
.host().empty())
183 std::string spdy_server
= GetFlattenedSpdyServer(host_port_pair
);
185 SpdyServerHostPortMap::iterator spdy_host_port
=
186 spdy_servers_map_
.Get(spdy_server
);
187 if ((spdy_host_port
!= spdy_servers_map_
.end()) &&
188 (spdy_host_port
->second
== support_spdy
)) {
192 spdy_servers_map_
.Put(spdy_server
, support_spdy
);
195 bool HttpServerPropertiesImpl::HasAlternateProtocol(
196 const HostPortPair
& server
) {
197 if (g_forced_alternate_protocol
)
199 AlternateProtocolMap::const_iterator it
= alternate_protocol_map_
.Get(server
);
200 if (it
!= alternate_protocol_map_
.end() &&
201 it
->second
.probability
> alternate_protocol_probability_threshold_
) {
205 return GetCanonicalHost(server
) != canonical_host_to_origin_map_
.end();
208 std::string
HttpServerPropertiesImpl::GetCanonicalSuffix(
209 const HostPortPair
& server
) {
210 // If this host ends with a canonical suffix, then return the canonical
212 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
213 std::string canonical_suffix
= canoncial_suffixes_
[i
];
214 if (EndsWith(server
.host(), canoncial_suffixes_
[i
], false)) {
215 return canonical_suffix
;
218 return std::string();
221 AlternateProtocolInfo
222 HttpServerPropertiesImpl::GetAlternateProtocol(
223 const HostPortPair
& server
) {
224 DCHECK(HasAlternateProtocol(server
));
226 // First check the map.
227 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Get(server
);
228 if (it
!= alternate_protocol_map_
.end())
231 // Next check the canonical host.
232 CanonicalHostMap::const_iterator canonical_host
= GetCanonicalHost(server
);
233 if (canonical_host
!= canonical_host_to_origin_map_
.end())
234 return alternate_protocol_map_
.Get(canonical_host
->second
)->second
;
236 // We must be forcing an alternate.
237 DCHECK(g_forced_alternate_protocol
);
238 return *g_forced_alternate_protocol
;
241 void HttpServerPropertiesImpl::SetAlternateProtocol(
242 const HostPortPair
& server
,
243 uint16 alternate_port
,
244 AlternateProtocol alternate_protocol
,
245 double alternate_probability
) {
246 if (alternate_protocol
== ALTERNATE_PROTOCOL_BROKEN
) {
247 LOG(DFATAL
) << "Call SetBrokenAlternateProtocol() instead.";
251 AlternateProtocolInfo
alternate(alternate_port
,
253 alternate_probability
);
254 if (HasAlternateProtocol(server
)) {
255 const AlternateProtocolInfo existing_alternate
=
256 GetAlternateProtocol(server
);
258 if (existing_alternate
.protocol
== ALTERNATE_PROTOCOL_BROKEN
) {
259 DVLOG(1) << "Ignore alternate protocol since it's known to be broken.";
263 if (alternate_protocol
!= ALTERNATE_PROTOCOL_BROKEN
&&
264 !existing_alternate
.Equals(alternate
)) {
265 LOG(WARNING
) << "Changing the alternate protocol for: "
267 << " from [Port: " << existing_alternate
.port
268 << ", Protocol: " << existing_alternate
.protocol
269 << ", Probability: " << existing_alternate
.probability
270 << "] to [Port: " << alternate_port
271 << ", Protocol: " << alternate_protocol
272 << ", Probability: " << alternate_probability
276 // TODO(rch): Consider the case where multiple requests are started
277 // before the first completes. In this case, only one of the jobs
278 // would reach this code, whereas all of them should should have.
279 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING
,
280 alternate_protocol_experiment_
);
283 alternate_protocol_map_
.Put(server
, alternate
);
285 // If this host ends with a canonical suffix, then set it as the
287 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
288 std::string canonical_suffix
= canoncial_suffixes_
[i
];
289 if (EndsWith(server
.host(), canoncial_suffixes_
[i
], false)) {
290 HostPortPair
canonical_host(canonical_suffix
, server
.port());
291 canonical_host_to_origin_map_
[canonical_host
] = server
;
297 void HttpServerPropertiesImpl::SetBrokenAlternateProtocol(
298 const HostPortPair
& server
) {
299 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Get(server
);
300 if (it
!= alternate_protocol_map_
.end()) {
301 it
->second
.protocol
= ALTERNATE_PROTOCOL_BROKEN
;
303 AlternateProtocolInfo
alternate(server
.port(),
304 ALTERNATE_PROTOCOL_BROKEN
,
306 alternate_protocol_map_
.Put(server
, alternate
);
308 int count
= ++broken_alternate_protocol_map_
[server
];
309 base::TimeDelta delay
=
310 base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs
);
311 BrokenAlternateProtocolEntry entry
;
312 entry
.server
= server
;
313 entry
.when
= base::TimeTicks::Now() + delay
* (1 << (count
- 1));
314 broken_alternate_protocol_list_
.push_back(entry
);
316 // Do not leave this host as canonical so that we don't infer the other
317 // hosts are also broken without testing them first.
318 RemoveCanonicalHost(server
);
320 // If this is the only entry in the list, schedule an expiration task.
321 // Otherwse it will be rescheduled automatically when the pending
323 if (broken_alternate_protocol_list_
.size() == 1) {
324 ScheduleBrokenAlternateProtocolMappingsExpiration();
328 bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken(
329 const HostPortPair
& server
) {
330 return ContainsKey(broken_alternate_protocol_map_
, server
);
333 void HttpServerPropertiesImpl::ConfirmAlternateProtocol(
334 const HostPortPair
& server
) {
335 broken_alternate_protocol_map_
.erase(server
);
338 void HttpServerPropertiesImpl::ClearAlternateProtocol(
339 const HostPortPair
& server
) {
340 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Peek(server
);
341 if (it
!= alternate_protocol_map_
.end())
342 alternate_protocol_map_
.Erase(it
);
344 RemoveCanonicalHost(server
);
347 const AlternateProtocolMap
&
348 HttpServerPropertiesImpl::alternate_protocol_map() const {
349 return alternate_protocol_map_
;
352 void HttpServerPropertiesImpl::SetAlternateProtocolExperiment(
353 AlternateProtocolExperiment experiment
) {
354 alternate_protocol_experiment_
= experiment
;
357 AlternateProtocolExperiment
358 HttpServerPropertiesImpl::GetAlternateProtocolExperiment() const {
359 return alternate_protocol_experiment_
;
362 const SettingsMap
& HttpServerPropertiesImpl::GetSpdySettings(
363 const HostPortPair
& host_port_pair
) {
364 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
365 if (it
== spdy_settings_map_
.end()) {
366 CR_DEFINE_STATIC_LOCAL(SettingsMap
, kEmptySettingsMap
, ());
367 return kEmptySettingsMap
;
372 bool HttpServerPropertiesImpl::SetSpdySetting(
373 const HostPortPair
& host_port_pair
,
375 SpdySettingsFlags flags
,
377 if (!(flags
& SETTINGS_FLAG_PLEASE_PERSIST
))
380 SettingsFlagsAndValue
flags_and_value(SETTINGS_FLAG_PERSISTED
, value
);
381 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
382 if (it
== spdy_settings_map_
.end()) {
383 SettingsMap settings_map
;
384 settings_map
[id
] = flags_and_value
;
385 spdy_settings_map_
.Put(host_port_pair
, settings_map
);
387 SettingsMap
& settings_map
= it
->second
;
388 settings_map
[id
] = flags_and_value
;
393 void HttpServerPropertiesImpl::ClearSpdySettings(
394 const HostPortPair
& host_port_pair
) {
395 SpdySettingsMap::iterator it
= spdy_settings_map_
.Peek(host_port_pair
);
396 if (it
!= spdy_settings_map_
.end())
397 spdy_settings_map_
.Erase(it
);
400 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
401 spdy_settings_map_
.Clear();
404 const SpdySettingsMap
&
405 HttpServerPropertiesImpl::spdy_settings_map() const {
406 return spdy_settings_map_
;
409 void HttpServerPropertiesImpl::SetServerNetworkStats(
410 const HostPortPair
& host_port_pair
,
411 NetworkStats stats
) {
412 server_network_stats_map_
[host_port_pair
] = stats
;
415 const HttpServerProperties::NetworkStats
*
416 HttpServerPropertiesImpl::GetServerNetworkStats(
417 const HostPortPair
& host_port_pair
) const {
418 ServerNetworkStatsMap::const_iterator it
=
419 server_network_stats_map_
.find(host_port_pair
);
420 if (it
== server_network_stats_map_
.end()) {
426 void HttpServerPropertiesImpl::SetAlternateProtocolProbabilityThreshold(
428 alternate_protocol_probability_threshold_
= threshold
;
431 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
432 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server
) const {
433 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
434 std::string canonical_suffix
= canoncial_suffixes_
[i
];
435 if (EndsWith(server
.host(), canoncial_suffixes_
[i
], false)) {
436 HostPortPair
canonical_host(canonical_suffix
, server
.port());
437 return canonical_host_to_origin_map_
.find(canonical_host
);
441 return canonical_host_to_origin_map_
.end();
444 void HttpServerPropertiesImpl::RemoveCanonicalHost(
445 const HostPortPair
& server
) {
446 CanonicalHostMap::const_iterator canonical
= GetCanonicalHost(server
);
447 if (canonical
== canonical_host_to_origin_map_
.end())
450 if (!canonical
->second
.Equals(server
))
453 canonical_host_to_origin_map_
.erase(canonical
->first
);
456 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() {
457 base::TimeTicks now
= base::TimeTicks::Now();
458 while (!broken_alternate_protocol_list_
.empty()) {
459 BrokenAlternateProtocolEntry entry
=
460 broken_alternate_protocol_list_
.front();
461 if (now
< entry
.when
) {
465 ClearAlternateProtocol(entry
.server
);
466 broken_alternate_protocol_list_
.pop_front();
468 ScheduleBrokenAlternateProtocolMappingsExpiration();
472 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
473 if (broken_alternate_protocol_list_
.empty()) {
476 base::TimeTicks now
= base::TimeTicks::Now();
477 base::TimeTicks when
= broken_alternate_protocol_list_
.front().when
;
478 base::TimeDelta delay
= when
> now
? when
- now
: base::TimeDelta();
479 base::MessageLoop::current()->PostDelayedTask(
482 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings
,
483 weak_ptr_factory_
.GetWeakPtr()),