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 canoncial_suffixes_
.push_back(".c.youtube.com");
30 canoncial_suffixes_
.push_back(".googlevideo.com");
31 canoncial_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 ALTERNATE_PROTOCOL_BROKEN ones since those don't
54 for (AlternateProtocolMap::iterator it
= alternate_protocol_map_
.begin();
55 it
!= alternate_protocol_map_
.end();) {
56 AlternateProtocolMap::iterator old_it
= it
;
58 if (old_it
->second
.protocol
!= ALTERNATE_PROTOCOL_BROKEN
) {
59 alternate_protocol_map_
.Erase(old_it
);
63 // Add the entries from persisted data.
64 for (AlternateProtocolMap::reverse_iterator it
=
65 alternate_protocol_map
->rbegin();
66 it
!= alternate_protocol_map
->rend(); ++it
) {
67 alternate_protocol_map_
.Put(it
->first
, it
->second
);
70 // Attempt to find canonical servers.
71 int canonical_ports
[] = { 80, 443 };
72 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
73 std::string canonical_suffix
= canoncial_suffixes_
[i
];
74 for (size_t j
= 0; j
< arraysize(canonical_ports
); ++j
) {
75 HostPortPair
canonical_host(canonical_suffix
, canonical_ports
[j
]);
76 // If we already have a valid canonical server, we're done.
77 if (ContainsKey(canonical_host_to_origin_map_
, canonical_host
) &&
78 (alternate_protocol_map_
.Peek(canonical_host_to_origin_map_
[
79 canonical_host
]) != alternate_protocol_map_
.end())) {
82 // Now attempt to find a server which matches this origin and set it as
84 for (AlternateProtocolMap::const_iterator it
=
85 alternate_protocol_map_
.begin();
86 it
!= alternate_protocol_map_
.end(); ++it
) {
87 if (EndsWith(it
->first
.host(), canoncial_suffixes_
[i
], false)) {
88 canonical_host_to_origin_map_
[canonical_host
] = it
->first
;
96 void HttpServerPropertiesImpl::InitializeSpdySettingsServers(
97 SpdySettingsMap
* spdy_settings_map
) {
98 for (SpdySettingsMap::reverse_iterator it
= spdy_settings_map
->rbegin();
99 it
!= spdy_settings_map
->rend(); ++it
) {
100 spdy_settings_map_
.Put(it
->first
, it
->second
);
104 void HttpServerPropertiesImpl::InitializeSupportsQuic(
105 SupportsQuicMap
* supports_quic_map
) {
106 for (SupportsQuicMap::reverse_iterator it
= supports_quic_map
->rbegin();
107 it
!= supports_quic_map
->rend();
109 supports_quic_map_
.insert(std::make_pair(it
->first
, it
->second
));
113 void HttpServerPropertiesImpl::GetSpdyServerList(
114 base::ListValue
* spdy_server_list
,
115 size_t max_size
) const {
116 DCHECK(CalledOnValidThread());
117 DCHECK(spdy_server_list
);
118 spdy_server_list
->Clear();
120 // Get the list of servers (host/port) that support SPDY.
121 for (SpdyServerHostPortMap::const_iterator it
= spdy_servers_map_
.begin();
122 it
!= spdy_servers_map_
.end() && count
< max_size
; ++it
) {
123 const std::string spdy_server_host_port
= it
->first
;
125 spdy_server_list
->Append(new base::StringValue(spdy_server_host_port
));
132 std::string
HttpServerPropertiesImpl::GetFlattenedSpdyServer(
133 const net::HostPortPair
& host_port_pair
) {
134 std::string spdy_server
;
135 spdy_server
.append(host_port_pair
.host());
136 spdy_server
.append(":");
137 base::StringAppendF(&spdy_server
, "%d", host_port_pair
.port());
141 static const AlternateProtocolInfo
* g_forced_alternate_protocol
= NULL
;
144 void HttpServerPropertiesImpl::ForceAlternateProtocol(
145 const AlternateProtocolInfo
& info
) {
146 // Note: we're going to leak this.
147 if (g_forced_alternate_protocol
)
148 delete g_forced_alternate_protocol
;
149 g_forced_alternate_protocol
= new AlternateProtocolInfo(info
);
153 void HttpServerPropertiesImpl::DisableForcedAlternateProtocol() {
154 delete g_forced_alternate_protocol
;
155 g_forced_alternate_protocol
= NULL
;
158 base::WeakPtr
<HttpServerProperties
> HttpServerPropertiesImpl::GetWeakPtr() {
159 return weak_ptr_factory_
.GetWeakPtr();
162 void HttpServerPropertiesImpl::Clear() {
163 DCHECK(CalledOnValidThread());
164 spdy_servers_map_
.Clear();
165 alternate_protocol_map_
.Clear();
166 canonical_host_to_origin_map_
.clear();
167 spdy_settings_map_
.Clear();
168 supports_quic_map_
.clear();
171 bool HttpServerPropertiesImpl::SupportsSpdy(
172 const net::HostPortPair
& host_port_pair
) {
173 DCHECK(CalledOnValidThread());
174 if (host_port_pair
.host().empty())
176 std::string spdy_server
= GetFlattenedSpdyServer(host_port_pair
);
178 SpdyServerHostPortMap::iterator spdy_host_port
=
179 spdy_servers_map_
.Get(spdy_server
);
180 if (spdy_host_port
!= spdy_servers_map_
.end())
181 return spdy_host_port
->second
;
185 void HttpServerPropertiesImpl::SetSupportsSpdy(
186 const net::HostPortPair
& host_port_pair
,
188 DCHECK(CalledOnValidThread());
189 if (host_port_pair
.host().empty())
191 std::string spdy_server
= GetFlattenedSpdyServer(host_port_pair
);
193 SpdyServerHostPortMap::iterator spdy_host_port
=
194 spdy_servers_map_
.Get(spdy_server
);
195 if ((spdy_host_port
!= spdy_servers_map_
.end()) &&
196 (spdy_host_port
->second
== support_spdy
)) {
200 spdy_servers_map_
.Put(spdy_server
, support_spdy
);
203 bool HttpServerPropertiesImpl::HasAlternateProtocol(
204 const HostPortPair
& server
) {
205 if (g_forced_alternate_protocol
)
207 AlternateProtocolMap::const_iterator it
= alternate_protocol_map_
.Get(server
);
208 if (it
!= alternate_protocol_map_
.end() &&
209 it
->second
.probability
>= alternate_protocol_probability_threshold_
) {
213 return GetCanonicalHost(server
) != canonical_host_to_origin_map_
.end();
216 std::string
HttpServerPropertiesImpl::GetCanonicalSuffix(
217 const HostPortPair
& server
) {
218 // If this host ends with a canonical suffix, then return the canonical
220 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
221 std::string canonical_suffix
= canoncial_suffixes_
[i
];
222 if (EndsWith(server
.host(), canoncial_suffixes_
[i
], false)) {
223 return canonical_suffix
;
226 return std::string();
229 AlternateProtocolInfo
230 HttpServerPropertiesImpl::GetAlternateProtocol(
231 const HostPortPair
& server
) {
232 DCHECK(HasAlternateProtocol(server
));
234 // First check the map.
235 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Get(server
);
236 if (it
!= alternate_protocol_map_
.end())
239 // Next check the canonical host.
240 CanonicalHostMap::const_iterator canonical_host
= GetCanonicalHost(server
);
241 if (canonical_host
!= canonical_host_to_origin_map_
.end())
242 return alternate_protocol_map_
.Get(canonical_host
->second
)->second
;
244 // We must be forcing an alternate.
245 DCHECK(g_forced_alternate_protocol
);
246 return *g_forced_alternate_protocol
;
249 void HttpServerPropertiesImpl::SetAlternateProtocol(
250 const HostPortPair
& server
,
251 uint16 alternate_port
,
252 AlternateProtocol alternate_protocol
,
253 double alternate_probability
) {
254 if (alternate_protocol
== ALTERNATE_PROTOCOL_BROKEN
) {
255 LOG(DFATAL
) << "Call SetBrokenAlternateProtocol() instead.";
259 AlternateProtocolInfo
alternate(alternate_port
,
261 alternate_probability
);
262 if (HasAlternateProtocol(server
)) {
263 const AlternateProtocolInfo existing_alternate
=
264 GetAlternateProtocol(server
);
266 if (existing_alternate
.protocol
== ALTERNATE_PROTOCOL_BROKEN
) {
267 DVLOG(1) << "Ignore alternate protocol since it's known to be broken.";
271 if (alternate_protocol
!= ALTERNATE_PROTOCOL_BROKEN
&&
272 !existing_alternate
.Equals(alternate
)) {
273 LOG(WARNING
) << "Changing the alternate protocol for: "
275 << " from [Port: " << existing_alternate
.port
276 << ", Protocol: " << existing_alternate
.protocol
277 << ", Probability: " << existing_alternate
.probability
278 << "] to [Port: " << alternate_port
279 << ", Protocol: " << alternate_protocol
280 << ", Probability: " << alternate_probability
284 if (alternate_probability
>= alternate_protocol_probability_threshold_
) {
285 // TODO(rch): Consider the case where multiple requests are started
286 // before the first completes. In this case, only one of the jobs
287 // would reach this code, whereas all of them should should have.
288 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING
);
292 alternate_protocol_map_
.Put(server
, alternate
);
294 // If this host ends with a canonical suffix, then set it as the
296 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
297 std::string canonical_suffix
= canoncial_suffixes_
[i
];
298 if (EndsWith(server
.host(), canoncial_suffixes_
[i
], false)) {
299 HostPortPair
canonical_host(canonical_suffix
, server
.port());
300 canonical_host_to_origin_map_
[canonical_host
] = server
;
306 void HttpServerPropertiesImpl::SetBrokenAlternateProtocol(
307 const HostPortPair
& server
) {
308 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Get(server
);
309 if (it
!= alternate_protocol_map_
.end()) {
310 it
->second
.protocol
= ALTERNATE_PROTOCOL_BROKEN
;
312 AlternateProtocolInfo
alternate(server
.port(),
313 ALTERNATE_PROTOCOL_BROKEN
,
315 alternate_protocol_map_
.Put(server
, alternate
);
317 int count
= ++broken_alternate_protocol_map_
[server
];
318 base::TimeDelta delay
=
319 base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs
);
320 BrokenAlternateProtocolEntry entry
;
321 entry
.server
= server
;
322 entry
.when
= base::TimeTicks::Now() + delay
* (1 << (count
- 1));
323 broken_alternate_protocol_list_
.push_back(entry
);
325 // Do not leave this host as canonical so that we don't infer the other
326 // hosts are also broken without testing them first.
327 RemoveCanonicalHost(server
);
329 // If this is the only entry in the list, schedule an expiration task.
330 // Otherwse it will be rescheduled automatically when the pending
332 if (broken_alternate_protocol_list_
.size() == 1) {
333 ScheduleBrokenAlternateProtocolMappingsExpiration();
337 bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken(
338 const HostPortPair
& server
) {
339 return ContainsKey(broken_alternate_protocol_map_
, server
);
342 void HttpServerPropertiesImpl::ConfirmAlternateProtocol(
343 const HostPortPair
& server
) {
344 broken_alternate_protocol_map_
.erase(server
);
347 void HttpServerPropertiesImpl::ClearAlternateProtocol(
348 const HostPortPair
& server
) {
349 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Peek(server
);
350 if (it
!= alternate_protocol_map_
.end())
351 alternate_protocol_map_
.Erase(it
);
353 RemoveCanonicalHost(server
);
356 const AlternateProtocolMap
&
357 HttpServerPropertiesImpl::alternate_protocol_map() const {
358 return alternate_protocol_map_
;
361 const SettingsMap
& HttpServerPropertiesImpl::GetSpdySettings(
362 const HostPortPair
& host_port_pair
) {
363 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
364 if (it
== spdy_settings_map_
.end()) {
365 CR_DEFINE_STATIC_LOCAL(SettingsMap
, kEmptySettingsMap
, ());
366 return kEmptySettingsMap
;
371 bool HttpServerPropertiesImpl::SetSpdySetting(
372 const HostPortPair
& host_port_pair
,
374 SpdySettingsFlags flags
,
376 if (!(flags
& SETTINGS_FLAG_PLEASE_PERSIST
))
379 SettingsFlagsAndValue
flags_and_value(SETTINGS_FLAG_PERSISTED
, value
);
380 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
381 if (it
== spdy_settings_map_
.end()) {
382 SettingsMap settings_map
;
383 settings_map
[id
] = flags_and_value
;
384 spdy_settings_map_
.Put(host_port_pair
, settings_map
);
386 SettingsMap
& settings_map
= it
->second
;
387 settings_map
[id
] = flags_and_value
;
392 void HttpServerPropertiesImpl::ClearSpdySettings(
393 const HostPortPair
& host_port_pair
) {
394 SpdySettingsMap::iterator it
= spdy_settings_map_
.Peek(host_port_pair
);
395 if (it
!= spdy_settings_map_
.end())
396 spdy_settings_map_
.Erase(it
);
399 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
400 spdy_settings_map_
.Clear();
403 const SpdySettingsMap
&
404 HttpServerPropertiesImpl::spdy_settings_map() const {
405 return spdy_settings_map_
;
408 SupportsQuic
HttpServerPropertiesImpl::GetSupportsQuic(
409 const HostPortPair
& host_port_pair
) const {
410 SupportsQuicMap::const_iterator it
= supports_quic_map_
.find(host_port_pair
);
411 if (it
== supports_quic_map_
.end()) {
412 CR_DEFINE_STATIC_LOCAL(SupportsQuic
, kEmptySupportsQuic
, ());
413 return kEmptySupportsQuic
;
418 void HttpServerPropertiesImpl::SetSupportsQuic(
419 const HostPortPair
& host_port_pair
,
421 const std::string
& address
) {
422 SupportsQuic
supports_quic(used_quic
, address
);
423 supports_quic_map_
.insert(std::make_pair(host_port_pair
, supports_quic
));
426 const SupportsQuicMap
&
427 HttpServerPropertiesImpl::supports_quic_map() const {
428 return supports_quic_map_
;
431 void HttpServerPropertiesImpl::SetServerNetworkStats(
432 const HostPortPair
& host_port_pair
,
433 NetworkStats stats
) {
434 server_network_stats_map_
[host_port_pair
] = stats
;
437 const HttpServerProperties::NetworkStats
*
438 HttpServerPropertiesImpl::GetServerNetworkStats(
439 const HostPortPair
& host_port_pair
) const {
440 ServerNetworkStatsMap::const_iterator it
=
441 server_network_stats_map_
.find(host_port_pair
);
442 if (it
== server_network_stats_map_
.end()) {
448 void HttpServerPropertiesImpl::SetAlternateProtocolProbabilityThreshold(
450 alternate_protocol_probability_threshold_
= threshold
;
453 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
454 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server
) const {
455 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
456 std::string canonical_suffix
= canoncial_suffixes_
[i
];
457 if (EndsWith(server
.host(), canoncial_suffixes_
[i
], false)) {
458 HostPortPair
canonical_host(canonical_suffix
, server
.port());
459 return canonical_host_to_origin_map_
.find(canonical_host
);
463 return canonical_host_to_origin_map_
.end();
466 void HttpServerPropertiesImpl::RemoveCanonicalHost(
467 const HostPortPair
& server
) {
468 CanonicalHostMap::const_iterator canonical
= GetCanonicalHost(server
);
469 if (canonical
== canonical_host_to_origin_map_
.end())
472 if (!canonical
->second
.Equals(server
))
475 canonical_host_to_origin_map_
.erase(canonical
->first
);
478 void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() {
479 base::TimeTicks now
= base::TimeTicks::Now();
480 while (!broken_alternate_protocol_list_
.empty()) {
481 BrokenAlternateProtocolEntry entry
=
482 broken_alternate_protocol_list_
.front();
483 if (now
< entry
.when
) {
487 ClearAlternateProtocol(entry
.server
);
488 broken_alternate_protocol_list_
.pop_front();
490 ScheduleBrokenAlternateProtocolMappingsExpiration();
494 HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() {
495 if (broken_alternate_protocol_list_
.empty()) {
498 base::TimeTicks now
= base::TimeTicks::Now();
499 base::TimeTicks when
= broken_alternate_protocol_list_
.front().when
;
500 base::TimeDelta delay
= when
> now
? when
- now
: base::TimeDelta();
501 base::MessageLoop::current()->PostDelayedTask(
504 &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings
,
505 weak_ptr_factory_
.GetWeakPtr()),