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"
7 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/stl_util.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "net/http/http_pipelined_host_capability.h"
16 // TODO(simonjam): Run experiments with different values of this to see what
17 // value is good at avoiding evictions without eating too much memory. Until
18 // then, this is just a bad guess.
19 static const int kDefaultNumHostsToRemember
= 200;
21 HttpServerPropertiesImpl::HttpServerPropertiesImpl()
22 : alternate_protocol_map_(AlternateProtocolMap::NO_AUTO_EVICT
),
23 spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT
),
24 pipeline_capability_map_(
25 new CachedPipelineCapabilityMap(kDefaultNumHostsToRemember
)),
26 weak_ptr_factory_(this) {
27 canoncial_suffixes_
.push_back(".c.youtube.com");
28 canoncial_suffixes_
.push_back(".googlevideo.com");
31 HttpServerPropertiesImpl::~HttpServerPropertiesImpl() {
34 void HttpServerPropertiesImpl::InitializeSpdyServers(
35 std::vector
<std::string
>* spdy_servers
,
37 DCHECK(CalledOnValidThread());
38 spdy_servers_table_
.clear();
41 for (std::vector
<std::string
>::iterator it
= spdy_servers
->begin();
42 it
!= spdy_servers
->end(); ++it
) {
43 spdy_servers_table_
[*it
] = support_spdy
;
47 void HttpServerPropertiesImpl::InitializeAlternateProtocolServers(
48 AlternateProtocolMap
* alternate_protocol_map
) {
49 // Keep all the ALTERNATE_PROTOCOL_BROKEN ones since those don't
51 for (AlternateProtocolMap::iterator it
= alternate_protocol_map_
.begin();
52 it
!= alternate_protocol_map_
.end();) {
53 AlternateProtocolMap::iterator old_it
= it
;
55 if (old_it
->second
.protocol
!= ALTERNATE_PROTOCOL_BROKEN
) {
56 alternate_protocol_map_
.Erase(old_it
);
60 // Add the entries from persisted data.
61 for (AlternateProtocolMap::reverse_iterator it
=
62 alternate_protocol_map
->rbegin();
63 it
!= alternate_protocol_map
->rend(); ++it
) {
64 alternate_protocol_map_
.Put(it
->first
, it
->second
);
67 // Attempt to find canonical servers.
68 int canonical_ports
[] = { 80, 443 };
69 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
70 std::string canonical_suffix
= canoncial_suffixes_
[i
];
71 for (size_t j
= 0; j
< arraysize(canonical_ports
); ++j
) {
72 HostPortPair
canonical_host(canonical_suffix
, canonical_ports
[j
]);
73 // If we already have a valid canonical server, we're done.
74 if (ContainsKey(canonical_host_to_origin_map_
, canonical_host
) &&
75 (alternate_protocol_map_
.Peek(canonical_host_to_origin_map_
[
76 canonical_host
]) != alternate_protocol_map_
.end())) {
79 // Now attempt to find a server which matches this origin and set it as
81 for (AlternateProtocolMap::const_iterator it
=
82 alternate_protocol_map_
.begin();
83 it
!= alternate_protocol_map_
.end(); ++it
) {
84 if (EndsWith(it
->first
.host(), canoncial_suffixes_
[i
], false)) {
85 canonical_host_to_origin_map_
[canonical_host
] = it
->first
;
93 void HttpServerPropertiesImpl::InitializeSpdySettingsServers(
94 SpdySettingsMap
* spdy_settings_map
) {
95 for (SpdySettingsMap::reverse_iterator it
= spdy_settings_map
->rbegin();
96 it
!= spdy_settings_map
->rend(); ++it
) {
97 spdy_settings_map_
.Put(it
->first
, it
->second
);
101 void HttpServerPropertiesImpl::InitializePipelineCapabilities(
102 const PipelineCapabilityMap
* pipeline_capability_map
) {
103 PipelineCapabilityMap::const_iterator it
;
104 pipeline_capability_map_
->Clear();
105 for (it
= pipeline_capability_map
->begin();
106 it
!= pipeline_capability_map
->end(); ++it
) {
107 pipeline_capability_map_
->Put(it
->first
, it
->second
);
111 void HttpServerPropertiesImpl::SetNumPipelinedHostsToRemember(int max_size
) {
112 DCHECK(pipeline_capability_map_
->empty());
113 pipeline_capability_map_
.reset(new CachedPipelineCapabilityMap(max_size
));
116 void HttpServerPropertiesImpl::GetSpdyServerList(
117 base::ListValue
* spdy_server_list
) const {
118 DCHECK(CalledOnValidThread());
119 DCHECK(spdy_server_list
);
120 spdy_server_list
->Clear();
121 // Get the list of servers (host/port) that support SPDY.
122 for (SpdyServerHostPortTable::const_iterator it
= spdy_servers_table_
.begin();
123 it
!= spdy_servers_table_
.end(); ++it
) {
124 const std::string spdy_server_host_port
= it
->first
;
126 spdy_server_list
->Append(new base::StringValue(spdy_server_host_port
));
131 std::string
HttpServerPropertiesImpl::GetFlattenedSpdyServer(
132 const net::HostPortPair
& host_port_pair
) {
133 std::string spdy_server
;
134 spdy_server
.append(host_port_pair
.host());
135 spdy_server
.append(":");
136 base::StringAppendF(&spdy_server
, "%d", host_port_pair
.port());
140 static const PortAlternateProtocolPair
* g_forced_alternate_protocol
= NULL
;
143 void HttpServerPropertiesImpl::ForceAlternateProtocol(
144 const PortAlternateProtocolPair
& pair
) {
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 PortAlternateProtocolPair(pair
);
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_table_
.clear();
164 alternate_protocol_map_
.Clear();
165 spdy_settings_map_
.Clear();
166 pipeline_capability_map_
->Clear();
169 bool HttpServerPropertiesImpl::SupportsSpdy(
170 const net::HostPortPair
& host_port_pair
) const {
171 DCHECK(CalledOnValidThread());
172 if (host_port_pair
.host().empty())
174 std::string spdy_server
= GetFlattenedSpdyServer(host_port_pair
);
176 SpdyServerHostPortTable::const_iterator spdy_host_port
=
177 spdy_servers_table_
.find(spdy_server
);
178 if (spdy_host_port
!= spdy_servers_table_
.end())
179 return spdy_host_port
->second
;
183 void HttpServerPropertiesImpl::SetSupportsSpdy(
184 const net::HostPortPair
& host_port_pair
,
186 DCHECK(CalledOnValidThread());
187 if (host_port_pair
.host().empty())
189 std::string spdy_server
= GetFlattenedSpdyServer(host_port_pair
);
191 SpdyServerHostPortTable::iterator spdy_host_port
=
192 spdy_servers_table_
.find(spdy_server
);
193 if ((spdy_host_port
!= spdy_servers_table_
.end()) &&
194 (spdy_host_port
->second
== support_spdy
)) {
198 spdy_servers_table_
[spdy_server
] = support_spdy
;
201 bool HttpServerPropertiesImpl::HasAlternateProtocol(
202 const HostPortPair
& server
) {
203 if (alternate_protocol_map_
.Get(server
) != alternate_protocol_map_
.end() ||
204 g_forced_alternate_protocol
)
207 return GetCanonicalHost(server
) != canonical_host_to_origin_map_
.end();
210 PortAlternateProtocolPair
211 HttpServerPropertiesImpl::GetAlternateProtocol(
212 const HostPortPair
& server
) {
213 DCHECK(HasAlternateProtocol(server
));
215 // First check the map.
216 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Get(server
);
217 if (it
!= alternate_protocol_map_
.end())
220 // Next check the canonical host.
221 CanonicalHostMap::const_iterator canonical_host
= GetCanonicalHost(server
);
222 if (canonical_host
!= canonical_host_to_origin_map_
.end())
223 return alternate_protocol_map_
.Get(canonical_host
->second
)->second
;
225 // We must be forcing an alternate.
226 DCHECK(g_forced_alternate_protocol
);
227 return *g_forced_alternate_protocol
;
230 void HttpServerPropertiesImpl::SetAlternateProtocol(
231 const HostPortPair
& server
,
232 uint16 alternate_port
,
233 AlternateProtocol alternate_protocol
) {
234 if (alternate_protocol
== ALTERNATE_PROTOCOL_BROKEN
) {
235 LOG(DFATAL
) << "Call SetBrokenAlternateProtocol() instead.";
239 PortAlternateProtocolPair alternate
;
240 alternate
.port
= alternate_port
;
241 alternate
.protocol
= alternate_protocol
;
242 if (HasAlternateProtocol(server
)) {
243 const PortAlternateProtocolPair existing_alternate
=
244 GetAlternateProtocol(server
);
246 if (existing_alternate
.protocol
== ALTERNATE_PROTOCOL_BROKEN
) {
247 DVLOG(1) << "Ignore alternate protocol since it's known to be broken.";
251 if (alternate_protocol
!= ALTERNATE_PROTOCOL_BROKEN
&&
252 !existing_alternate
.Equals(alternate
)) {
253 LOG(WARNING
) << "Changing the alternate protocol for: "
255 << " from [Port: " << existing_alternate
.port
256 << ", Protocol: " << existing_alternate
.protocol
257 << "] to [Port: " << alternate_port
258 << ", Protocol: " << alternate_protocol
263 alternate_protocol_map_
.Put(server
, alternate
);
265 // If this host ends with a canonical suffix, then set it as the
267 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
268 std::string canonical_suffix
= canoncial_suffixes_
[i
];
269 if (EndsWith(server
.host(), canoncial_suffixes_
[i
], false)) {
270 HostPortPair
canonical_host(canonical_suffix
, server
.port());
271 canonical_host_to_origin_map_
[canonical_host
] = server
;
277 void HttpServerPropertiesImpl::SetBrokenAlternateProtocol(
278 const HostPortPair
& server
) {
279 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Get(server
);
280 if (it
!= alternate_protocol_map_
.end()) {
281 it
->second
.protocol
= ALTERNATE_PROTOCOL_BROKEN
;
284 PortAlternateProtocolPair alternate
;
285 alternate
.protocol
= ALTERNATE_PROTOCOL_BROKEN
;
286 alternate_protocol_map_
.Put(server
, alternate
);
289 void HttpServerPropertiesImpl::ClearAlternateProtocol(
290 const HostPortPair
& server
) {
291 AlternateProtocolMap::iterator it
= alternate_protocol_map_
.Peek(server
);
292 if (it
!= alternate_protocol_map_
.end())
293 alternate_protocol_map_
.Erase(it
);
296 const AlternateProtocolMap
&
297 HttpServerPropertiesImpl::alternate_protocol_map() const {
298 return alternate_protocol_map_
;
301 const SettingsMap
& HttpServerPropertiesImpl::GetSpdySettings(
302 const HostPortPair
& host_port_pair
) {
303 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
304 if (it
== spdy_settings_map_
.end()) {
305 CR_DEFINE_STATIC_LOCAL(SettingsMap
, kEmptySettingsMap
, ());
306 return kEmptySettingsMap
;
311 bool HttpServerPropertiesImpl::SetSpdySetting(
312 const HostPortPair
& host_port_pair
,
314 SpdySettingsFlags flags
,
316 if (!(flags
& SETTINGS_FLAG_PLEASE_PERSIST
))
319 SettingsFlagsAndValue
flags_and_value(SETTINGS_FLAG_PERSISTED
, value
);
320 SpdySettingsMap::iterator it
= spdy_settings_map_
.Get(host_port_pair
);
321 if (it
== spdy_settings_map_
.end()) {
322 SettingsMap settings_map
;
323 settings_map
[id
] = flags_and_value
;
324 spdy_settings_map_
.Put(host_port_pair
, settings_map
);
326 SettingsMap
& settings_map
= it
->second
;
327 settings_map
[id
] = flags_and_value
;
332 void HttpServerPropertiesImpl::ClearSpdySettings(
333 const HostPortPair
& host_port_pair
) {
334 SpdySettingsMap::iterator it
= spdy_settings_map_
.Peek(host_port_pair
);
335 if (it
!= spdy_settings_map_
.end())
336 spdy_settings_map_
.Erase(it
);
339 void HttpServerPropertiesImpl::ClearAllSpdySettings() {
340 spdy_settings_map_
.Clear();
343 const SpdySettingsMap
&
344 HttpServerPropertiesImpl::spdy_settings_map() const {
345 return spdy_settings_map_
;
348 void HttpServerPropertiesImpl::SetServerNetworkStats(
349 const HostPortPair
& host_port_pair
,
350 NetworkStats stats
) {
351 server_network_stats_map_
[host_port_pair
] = stats
;
354 const HttpServerProperties::NetworkStats
*
355 HttpServerPropertiesImpl::GetServerNetworkStats(
356 const HostPortPair
& host_port_pair
) const {
357 ServerNetworkStatsMap::const_iterator it
=
358 server_network_stats_map_
.find(host_port_pair
);
359 if (it
== server_network_stats_map_
.end()) {
365 HttpPipelinedHostCapability
HttpServerPropertiesImpl::GetPipelineCapability(
366 const HostPortPair
& origin
) {
367 HttpPipelinedHostCapability capability
= PIPELINE_UNKNOWN
;
368 CachedPipelineCapabilityMap::const_iterator it
=
369 pipeline_capability_map_
->Get(origin
);
370 if (it
!= pipeline_capability_map_
->end()) {
371 capability
= it
->second
;
376 void HttpServerPropertiesImpl::SetPipelineCapability(
377 const HostPortPair
& origin
,
378 HttpPipelinedHostCapability capability
) {
379 CachedPipelineCapabilityMap::iterator it
=
380 pipeline_capability_map_
->Peek(origin
);
381 if (it
== pipeline_capability_map_
->end() ||
382 it
->second
!= PIPELINE_INCAPABLE
) {
383 pipeline_capability_map_
->Put(origin
, capability
);
387 void HttpServerPropertiesImpl::ClearPipelineCapabilities() {
388 pipeline_capability_map_
->Clear();
391 PipelineCapabilityMap
392 HttpServerPropertiesImpl::GetPipelineCapabilityMap() const {
393 PipelineCapabilityMap result
;
394 CachedPipelineCapabilityMap::const_iterator it
;
395 for (it
= pipeline_capability_map_
->begin();
396 it
!= pipeline_capability_map_
->end(); ++it
) {
397 result
[it
->first
] = it
->second
;
402 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
403 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server
) const {
404 for (size_t i
= 0; i
< canoncial_suffixes_
.size(); ++i
) {
405 std::string canonical_suffix
= canoncial_suffixes_
[i
];
406 if (EndsWith(server
.host(), canoncial_suffixes_
[i
], false)) {
407 HostPortPair
canonical_host(canonical_suffix
, server
.port());
408 return canonical_host_to_origin_map_
.find(canonical_host
);
412 return canonical_host_to_origin_map_
.end();