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.
4
5 #include "net/http/http_server_properties.h"
6
7 #include "base/bind.h"
8 #include "base/check_op.h"
9 #include "base/feature_list.h"
10 #include "base/location.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "base/time/default_clock.h"
18 #include "base/time/default_tick_clock.h"
19 #include "base/values.h"
20 #include "net/base/features.h"
21 #include "net/base/url_util.h"
22 #include "net/http/http_network_session.h"
23 #include "net/http/http_server_properties_manager.h"
24 #include "net/socket/ssl_client_socket.h"
25 #include "net/ssl/ssl_config.h"
26
27 namespace net {
28
29 namespace {
30
31 // Time to wait before starting an update the preferences from the
32 // http_server_properties_impl_ cache. Scheduling another update during this
33 // period will be a no-op.
34 constexpr base::TimeDelta kUpdatePrefsDelay = base::TimeDelta::FromSeconds(60);
35
NormalizeSchemeHostPort(const url::SchemeHostPort & scheme_host_port)36 url::SchemeHostPort NormalizeSchemeHostPort(
37 const url::SchemeHostPort& scheme_host_port) {
38 if (scheme_host_port.scheme() == url::kWssScheme) {
39 return url::SchemeHostPort(url::kHttpsScheme, scheme_host_port.host(),
40 scheme_host_port.port());
41 }
42 if (scheme_host_port.scheme() == url::kWsScheme) {
43 return url::SchemeHostPort(url::kHttpScheme, scheme_host_port.host(),
44 scheme_host_port.port());
45 }
46 return scheme_host_port;
47 }
48
49 } // namespace
50
51 HttpServerProperties::PrefDelegate::~PrefDelegate() = default;
52
53 HttpServerProperties::ServerInfo::ServerInfo() = default;
54 HttpServerProperties::ServerInfo::ServerInfo(const ServerInfo& server_info) =
55 default;
56 HttpServerProperties::ServerInfo::ServerInfo(ServerInfo&& server_info) =
57 default;
58 HttpServerProperties::ServerInfo::~ServerInfo() = default;
59
empty() const60 bool HttpServerProperties::ServerInfo::empty() const {
61 return !supports_spdy.has_value() && !alternative_services.has_value() &&
62 !server_network_stats.has_value();
63 }
64
operator ==(const ServerInfo & other) const65 bool HttpServerProperties::ServerInfo::operator==(
66 const ServerInfo& other) const {
67 return supports_spdy == other.supports_spdy &&
68 alternative_services == other.alternative_services &&
69 server_network_stats == other.server_network_stats;
70 }
71
ServerInfoMapKey(url::SchemeHostPort server,const NetworkIsolationKey & network_isolation_key,bool use_network_isolation_key)72 HttpServerProperties::ServerInfoMapKey::ServerInfoMapKey(
73 url::SchemeHostPort server,
74 const NetworkIsolationKey& network_isolation_key,
75 bool use_network_isolation_key)
76 : server(std::move(server)),
77 network_isolation_key(use_network_isolation_key ? network_isolation_key
78 : NetworkIsolationKey()) {
79 // Scheme should have been normalized before this method was called.
80 DCHECK_NE(this->server.scheme(), url::kWsScheme);
81 DCHECK_NE(this->server.scheme(), url::kWssScheme);
82 }
83
84 HttpServerProperties::ServerInfoMapKey::~ServerInfoMapKey() = default;
85
operator <(const ServerInfoMapKey & other) const86 bool HttpServerProperties::ServerInfoMapKey::operator<(
87 const ServerInfoMapKey& other) const {
88 return std::tie(server, network_isolation_key) <
89 std::tie(other.server, other.network_isolation_key);
90 }
91
QuicServerInfoMapKey(const quic::QuicServerId & server_id,const NetworkIsolationKey & network_isolation_key,bool use_network_isolation_key)92 HttpServerProperties::QuicServerInfoMapKey::QuicServerInfoMapKey(
93 const quic::QuicServerId& server_id,
94 const NetworkIsolationKey& network_isolation_key,
95 bool use_network_isolation_key)
96 : server_id(server_id),
97 network_isolation_key(use_network_isolation_key ? network_isolation_key
98 : NetworkIsolationKey()) {
99 }
100
101 HttpServerProperties::QuicServerInfoMapKey::~QuicServerInfoMapKey() = default;
102
operator <(const QuicServerInfoMapKey & other) const103 bool HttpServerProperties::QuicServerInfoMapKey::operator<(
104 const QuicServerInfoMapKey& other) const {
105 return std::tie(server_id, network_isolation_key) <
106 std::tie(other.server_id, other.network_isolation_key);
107 }
108
109 // Used in tests.
operator ==(const QuicServerInfoMapKey & other) const110 bool HttpServerProperties::QuicServerInfoMapKey::operator==(
111 const QuicServerInfoMapKey& other) const {
112 return std::tie(server_id, network_isolation_key) ==
113 std::tie(other.server_id, other.network_isolation_key);
114 }
115
ServerInfoMap()116 HttpServerProperties::ServerInfoMap::ServerInfoMap()
117 : base::MRUCache<ServerInfoMapKey, ServerInfo>(kMaxServerInfoEntries) {}
118
119 HttpServerProperties::ServerInfoMap::iterator
GetOrPut(const ServerInfoMapKey & key)120 HttpServerProperties::ServerInfoMap::GetOrPut(const ServerInfoMapKey& key) {
121 auto it = Get(key);
122 if (it != end())
123 return it;
124 return Put(key, ServerInfo());
125 }
126
127 HttpServerProperties::ServerInfoMap::iterator
EraseIfEmpty(iterator server_info_it)128 HttpServerProperties::ServerInfoMap::EraseIfEmpty(iterator server_info_it) {
129 if (server_info_it->second.empty())
130 return Erase(server_info_it);
131 return ++server_info_it;
132 }
133
HttpServerProperties(std::unique_ptr<PrefDelegate> pref_delegate,NetLog * net_log,const base::TickClock * tick_clock,base::Clock * clock)134 HttpServerProperties::HttpServerProperties(
135 std::unique_ptr<PrefDelegate> pref_delegate,
136 NetLog* net_log,
137 const base::TickClock* tick_clock,
138 base::Clock* clock)
139 : tick_clock_(tick_clock ? tick_clock
140 : base::DefaultTickClock::GetInstance()),
141 clock_(clock ? clock : base::DefaultClock::GetInstance()),
142 use_network_isolation_key_(base::FeatureList::IsEnabled(
143 features::kPartitionHttpServerPropertiesByNetworkIsolationKey)),
144 is_initialized_(pref_delegate.get() == nullptr),
145 queue_write_on_load_(false),
146 properties_manager_(
147 pref_delegate
148 ? std::make_unique<HttpServerPropertiesManager>(
149 std::move(pref_delegate),
150 base::BindOnce(&HttpServerProperties::OnPrefsLoaded,
151 base::Unretained(this)),
152 kDefaultMaxQuicServerEntries,
153 net_log,
154 tick_clock_)
155 : nullptr),
156 broken_alternative_services_(kMaxRecentlyBrokenAlternativeServiceEntries,
157 this,
158 tick_clock_),
159 canonical_suffixes_({".ggpht.com", ".c.youtube.com", ".googlevideo.com",
160 ".googleusercontent.com", ".gvt1.com"}),
161 quic_server_info_map_(kDefaultMaxQuicServerEntries),
162 max_server_configs_stored_in_properties_(kDefaultMaxQuicServerEntries) {}
163
~HttpServerProperties()164 HttpServerProperties::~HttpServerProperties() {
165 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
166
167 if (properties_manager_) {
168 // Stop waiting for initial settings.
169 is_initialized_ = true;
170
171 // Stop the timer if it's running, since this will write to the properties
172 // file immediately.
173 prefs_update_timer_.Stop();
174
175 WriteProperties(base::OnceClosure());
176 }
177 }
178
Clear(base::OnceClosure callback)179 void HttpServerProperties::Clear(base::OnceClosure callback) {
180 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
181 server_info_map_.Clear();
182 broken_alternative_services_.Clear();
183 canonical_alt_svc_map_.clear();
184 last_local_address_when_quic_worked_ = IPAddress();
185 quic_server_info_map_.Clear();
186 canonical_server_info_map_.clear();
187
188 if (properties_manager_) {
189 // Stop waiting for initial settings.
190 is_initialized_ = true;
191 // Leaving this as-is doesn't actually have any effect, if it's true, but
192 // seems best to be safe.
193 queue_write_on_load_ = false;
194
195 // Stop the timer if it's running, since this will write to the properties
196 // file immediately.
197 prefs_update_timer_.Stop();
198 WriteProperties(std::move(callback));
199 } else if (callback) {
200 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
201 std::move(callback));
202 }
203 }
204
SupportsRequestPriority(const url::SchemeHostPort & server,const net::NetworkIsolationKey & network_isolation_key)205 bool HttpServerProperties::SupportsRequestPriority(
206 const url::SchemeHostPort& server,
207 const net::NetworkIsolationKey& network_isolation_key) {
208 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
209 if (server.host().empty())
210 return false;
211
212 if (GetSupportsSpdy(server, network_isolation_key))
213 return true;
214 const AlternativeServiceInfoVector alternative_service_info_vector =
215 GetAlternativeServiceInfos(server, network_isolation_key);
216 for (const AlternativeServiceInfo& alternative_service_info :
217 alternative_service_info_vector) {
218 if (alternative_service_info.alternative_service().protocol == kProtoQUIC) {
219 return true;
220 }
221 }
222 return false;
223 }
224
GetSupportsSpdy(const url::SchemeHostPort & server,const net::NetworkIsolationKey & network_isolation_key)225 bool HttpServerProperties::GetSupportsSpdy(
226 const url::SchemeHostPort& server,
227 const net::NetworkIsolationKey& network_isolation_key) {
228 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
229 return GetSupportsSpdyInternal(NormalizeSchemeHostPort(server),
230 network_isolation_key);
231 }
232
SetSupportsSpdy(const url::SchemeHostPort & server,const net::NetworkIsolationKey & network_isolation_key,bool supports_spdy)233 void HttpServerProperties::SetSupportsSpdy(
234 const url::SchemeHostPort& server,
235 const net::NetworkIsolationKey& network_isolation_key,
236 bool supports_spdy) {
237 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
238 SetSupportsSpdyInternal(NormalizeSchemeHostPort(server),
239 network_isolation_key, supports_spdy);
240 }
241
RequiresHTTP11(const url::SchemeHostPort & server,const net::NetworkIsolationKey & network_isolation_key)242 bool HttpServerProperties::RequiresHTTP11(
243 const url::SchemeHostPort& server,
244 const net::NetworkIsolationKey& network_isolation_key) {
245 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
246 return RequiresHTTP11Internal(NormalizeSchemeHostPort(server),
247 network_isolation_key);
248 }
249
SetHTTP11Required(const url::SchemeHostPort & server,const net::NetworkIsolationKey & network_isolation_key)250 void HttpServerProperties::SetHTTP11Required(
251 const url::SchemeHostPort& server,
252 const net::NetworkIsolationKey& network_isolation_key) {
253 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
254 SetHTTP11RequiredInternal(NormalizeSchemeHostPort(server),
255 network_isolation_key);
256 }
257
MaybeForceHTTP11(const url::SchemeHostPort & server,const net::NetworkIsolationKey & network_isolation_key,SSLConfig * ssl_config)258 void HttpServerProperties::MaybeForceHTTP11(
259 const url::SchemeHostPort& server,
260 const net::NetworkIsolationKey& network_isolation_key,
261 SSLConfig* ssl_config) {
262 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
263 MaybeForceHTTP11Internal(NormalizeSchemeHostPort(server),
264 network_isolation_key, ssl_config);
265 }
266
GetAlternativeServiceInfos(const url::SchemeHostPort & origin,const net::NetworkIsolationKey & network_isolation_key)267 AlternativeServiceInfoVector HttpServerProperties::GetAlternativeServiceInfos(
268 const url::SchemeHostPort& origin,
269 const net::NetworkIsolationKey& network_isolation_key) {
270 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
271 return GetAlternativeServiceInfosInternal(NormalizeSchemeHostPort(origin),
272 network_isolation_key);
273 }
274
SetHttp2AlternativeService(const url::SchemeHostPort & origin,const NetworkIsolationKey & network_isolation_key,const AlternativeService & alternative_service,base::Time expiration)275 void HttpServerProperties::SetHttp2AlternativeService(
276 const url::SchemeHostPort& origin,
277 const NetworkIsolationKey& network_isolation_key,
278 const AlternativeService& alternative_service,
279 base::Time expiration) {
280 DCHECK_EQ(alternative_service.protocol, kProtoHTTP2);
281
282 SetAlternativeServices(
283 origin, network_isolation_key,
284 AlternativeServiceInfoVector(
285 /*size=*/1, AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
286 alternative_service, expiration)));
287 }
288
SetQuicAlternativeService(const url::SchemeHostPort & origin,const NetworkIsolationKey & network_isolation_key,const AlternativeService & alternative_service,base::Time expiration,const quic::ParsedQuicVersionVector & advertised_versions)289 void HttpServerProperties::SetQuicAlternativeService(
290 const url::SchemeHostPort& origin,
291 const NetworkIsolationKey& network_isolation_key,
292 const AlternativeService& alternative_service,
293 base::Time expiration,
294 const quic::ParsedQuicVersionVector& advertised_versions) {
295 DCHECK(alternative_service.protocol == kProtoQUIC);
296
297 SetAlternativeServices(
298 origin, network_isolation_key,
299 AlternativeServiceInfoVector(
300 /*size=*/1,
301 AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
302 alternative_service, expiration, advertised_versions)));
303 }
304
SetAlternativeServices(const url::SchemeHostPort & origin,const net::NetworkIsolationKey & network_isolation_key,const AlternativeServiceInfoVector & alternative_service_info_vector)305 void HttpServerProperties::SetAlternativeServices(
306 const url::SchemeHostPort& origin,
307 const net::NetworkIsolationKey& network_isolation_key,
308 const AlternativeServiceInfoVector& alternative_service_info_vector) {
309 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
310 SetAlternativeServicesInternal(NormalizeSchemeHostPort(origin),
311 network_isolation_key,
312 alternative_service_info_vector);
313 }
314
MarkAlternativeServiceBroken(const AlternativeService & alternative_service,const net::NetworkIsolationKey & network_isolation_key)315 void HttpServerProperties::MarkAlternativeServiceBroken(
316 const AlternativeService& alternative_service,
317 const net::NetworkIsolationKey& network_isolation_key) {
318 broken_alternative_services_.MarkBroken(BrokenAlternativeService(
319 alternative_service, network_isolation_key, use_network_isolation_key_));
320 MaybeQueueWriteProperties();
321 }
322
323 void HttpServerProperties::
MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(const AlternativeService & alternative_service,const net::NetworkIsolationKey & network_isolation_key)324 MarkAlternativeServiceBrokenUntilDefaultNetworkChanges(
325 const AlternativeService& alternative_service,
326 const net::NetworkIsolationKey& network_isolation_key) {
327 broken_alternative_services_.MarkBrokenUntilDefaultNetworkChanges(
328 BrokenAlternativeService(alternative_service, network_isolation_key,
329 use_network_isolation_key_));
330 MaybeQueueWriteProperties();
331 }
332
MarkAlternativeServiceRecentlyBroken(const AlternativeService & alternative_service,const net::NetworkIsolationKey & network_isolation_key)333 void HttpServerProperties::MarkAlternativeServiceRecentlyBroken(
334 const AlternativeService& alternative_service,
335 const net::NetworkIsolationKey& network_isolation_key) {
336 broken_alternative_services_.MarkRecentlyBroken(BrokenAlternativeService(
337 alternative_service, network_isolation_key, use_network_isolation_key_));
338 MaybeQueueWriteProperties();
339 }
340
IsAlternativeServiceBroken(const AlternativeService & alternative_service,const net::NetworkIsolationKey & network_isolation_key) const341 bool HttpServerProperties::IsAlternativeServiceBroken(
342 const AlternativeService& alternative_service,
343 const net::NetworkIsolationKey& network_isolation_key) const {
344 return broken_alternative_services_.IsBroken(BrokenAlternativeService(
345 alternative_service, network_isolation_key, use_network_isolation_key_));
346 }
347
WasAlternativeServiceRecentlyBroken(const AlternativeService & alternative_service,const net::NetworkIsolationKey & network_isolation_key)348 bool HttpServerProperties::WasAlternativeServiceRecentlyBroken(
349 const AlternativeService& alternative_service,
350 const net::NetworkIsolationKey& network_isolation_key) {
351 return broken_alternative_services_.WasRecentlyBroken(
352 BrokenAlternativeService(alternative_service, network_isolation_key,
353 use_network_isolation_key_));
354 }
355
ConfirmAlternativeService(const AlternativeService & alternative_service,const net::NetworkIsolationKey & network_isolation_key)356 void HttpServerProperties::ConfirmAlternativeService(
357 const AlternativeService& alternative_service,
358 const net::NetworkIsolationKey& network_isolation_key) {
359 bool old_value =
360 IsAlternativeServiceBroken(alternative_service, network_isolation_key);
361 broken_alternative_services_.Confirm(BrokenAlternativeService(
362 alternative_service, network_isolation_key, use_network_isolation_key_));
363 bool new_value =
364 IsAlternativeServiceBroken(alternative_service, network_isolation_key);
365
366 // For persisting, we only care about the value returned by
367 // IsAlternativeServiceBroken. If that value changes, then call persist.
368 if (old_value != new_value)
369 MaybeQueueWriteProperties();
370 }
371
OnDefaultNetworkChanged()372 void HttpServerProperties::OnDefaultNetworkChanged() {
373 bool changed = broken_alternative_services_.OnDefaultNetworkChanged();
374 if (changed)
375 MaybeQueueWriteProperties();
376 }
377
GetAlternativeServiceInfoAsValue() const378 base::Value HttpServerProperties::GetAlternativeServiceInfoAsValue() const {
379 const base::Time now = clock_->Now();
380 const base::TimeTicks now_ticks = tick_clock_->NowTicks();
381 base::Value dict_list(base::Value::Type::LIST);
382 for (const auto& server_info : server_info_map_) {
383 if (!server_info.second.alternative_services.has_value())
384 continue;
385 base::Value alternative_service_list(base::Value::Type::LIST);
386 const ServerInfoMapKey& key = server_info.first;
387 for (const AlternativeServiceInfo& alternative_service_info :
388 server_info.second.alternative_services.value()) {
389 std::string alternative_service_string(
390 alternative_service_info.ToString());
391 AlternativeService alternative_service(
392 alternative_service_info.alternative_service());
393 if (alternative_service.host.empty()) {
394 alternative_service.host = key.server.host();
395 }
396 base::TimeTicks brokenness_expiration_ticks;
397 if (broken_alternative_services_.IsBroken(
398 BrokenAlternativeService(alternative_service,
399 server_info.first.network_isolation_key,
400 use_network_isolation_key_),
401 &brokenness_expiration_ticks)) {
402 // Convert |brokenness_expiration| from TimeTicks to Time
403 base::Time brokenness_expiration =
404 now + (brokenness_expiration_ticks - now_ticks);
405 base::Time::Exploded exploded;
406 brokenness_expiration.LocalExplode(&exploded);
407 std::string broken_info_string =
408 " (broken until " +
409 base::StringPrintf("%04d-%02d-%02d %0d:%0d:%0d", exploded.year,
410 exploded.month, exploded.day_of_month,
411 exploded.hour, exploded.minute,
412 exploded.second) +
413 ")";
414 alternative_service_string.append(broken_info_string);
415 }
416 alternative_service_list.Append(std::move(alternative_service_string));
417 }
418 if (alternative_service_list.GetList().empty())
419 continue;
420 base::Value dict(base::Value::Type::DICTIONARY);
421 dict.SetStringKey("server", key.server.Serialize());
422 dict.SetStringKey("network_isolation_key",
423 key.network_isolation_key.ToDebugString());
424 dict.SetKey("alternative_service", std::move(alternative_service_list));
425 dict_list.Append(std::move(dict));
426 }
427 return dict_list;
428 }
429
WasLastLocalAddressWhenQuicWorked(const IPAddress & local_address) const430 bool HttpServerProperties::WasLastLocalAddressWhenQuicWorked(
431 const IPAddress& local_address) const {
432 return !last_local_address_when_quic_worked_.empty() &&
433 last_local_address_when_quic_worked_ == local_address;
434 }
435
HasLastLocalAddressWhenQuicWorked() const436 bool HttpServerProperties::HasLastLocalAddressWhenQuicWorked() const {
437 return !last_local_address_when_quic_worked_.empty();
438 }
439
SetLastLocalAddressWhenQuicWorked(IPAddress last_local_address_when_quic_worked)440 void HttpServerProperties::SetLastLocalAddressWhenQuicWorked(
441 IPAddress last_local_address_when_quic_worked) {
442 DCHECK(!last_local_address_when_quic_worked.empty());
443 if (last_local_address_when_quic_worked_ ==
444 last_local_address_when_quic_worked) {
445 return;
446 }
447
448 last_local_address_when_quic_worked_ = last_local_address_when_quic_worked;
449 MaybeQueueWriteProperties();
450 }
451
ClearLastLocalAddressWhenQuicWorked()452 void HttpServerProperties::ClearLastLocalAddressWhenQuicWorked() {
453 if (last_local_address_when_quic_worked_.empty())
454 return;
455
456 last_local_address_when_quic_worked_ = IPAddress();
457 MaybeQueueWriteProperties();
458 }
459
SetServerNetworkStats(const url::SchemeHostPort & server,const NetworkIsolationKey & network_isolation_key,ServerNetworkStats stats)460 void HttpServerProperties::SetServerNetworkStats(
461 const url::SchemeHostPort& server,
462 const NetworkIsolationKey& network_isolation_key,
463 ServerNetworkStats stats) {
464 SetServerNetworkStatsInternal(NormalizeSchemeHostPort(server),
465 network_isolation_key, std::move(stats));
466 }
467
ClearServerNetworkStats(const url::SchemeHostPort & server,const NetworkIsolationKey & network_isolation_key)468 void HttpServerProperties::ClearServerNetworkStats(
469 const url::SchemeHostPort& server,
470 const NetworkIsolationKey& network_isolation_key) {
471 ClearServerNetworkStatsInternal(NormalizeSchemeHostPort(server),
472 network_isolation_key);
473 }
474
GetServerNetworkStats(const url::SchemeHostPort & server,const NetworkIsolationKey & network_isolation_key)475 const ServerNetworkStats* HttpServerProperties::GetServerNetworkStats(
476 const url::SchemeHostPort& server,
477 const NetworkIsolationKey& network_isolation_key) {
478 return GetServerNetworkStatsInternal(NormalizeSchemeHostPort(server),
479 network_isolation_key);
480 }
481
SetQuicServerInfo(const quic::QuicServerId & server_id,const NetworkIsolationKey & network_isolation_key,const std::string & server_info)482 void HttpServerProperties::SetQuicServerInfo(
483 const quic::QuicServerId& server_id,
484 const NetworkIsolationKey& network_isolation_key,
485 const std::string& server_info) {
486 QuicServerInfoMapKey key =
487 CreateQuicServerInfoKey(server_id, network_isolation_key);
488 auto it = quic_server_info_map_.Peek(key);
489 bool changed =
490 (it == quic_server_info_map_.end() || it->second != server_info);
491 quic_server_info_map_.Put(key, server_info);
492 UpdateCanonicalServerInfoMap(key);
493 if (changed)
494 MaybeQueueWriteProperties();
495 }
496
GetQuicServerInfo(const quic::QuicServerId & server_id,const NetworkIsolationKey & network_isolation_key)497 const std::string* HttpServerProperties::GetQuicServerInfo(
498 const quic::QuicServerId& server_id,
499 const NetworkIsolationKey& network_isolation_key) {
500 QuicServerInfoMapKey key =
501 CreateQuicServerInfoKey(server_id, network_isolation_key);
502 auto it = quic_server_info_map_.Get(key);
503 if (it != quic_server_info_map_.end()) {
504 // Since |canonical_server_info_map_| should always map to the most
505 // recent host, update it with the one that became MRU in
506 // |quic_server_info_map_|.
507 UpdateCanonicalServerInfoMap(key);
508 return &it->second;
509 }
510
511 // If the exact match for |server_id| wasn't found, check
512 // |canonical_server_info_map_| whether there is server info for a host with
513 // the same canonical host suffix.
514 auto canonical_itr = GetCanonicalServerInfoHost(key);
515 if (canonical_itr == canonical_server_info_map_.end())
516 return nullptr;
517
518 // When search in |quic_server_info_map_|, do not change the MRU order.
519 it = quic_server_info_map_.Peek(
520 CreateQuicServerInfoKey(canonical_itr->second, network_isolation_key));
521 if (it != quic_server_info_map_.end())
522 return &it->second;
523
524 return nullptr;
525 }
526
527 const HttpServerProperties::QuicServerInfoMap&
quic_server_info_map() const528 HttpServerProperties::quic_server_info_map() const {
529 return quic_server_info_map_;
530 }
531
max_server_configs_stored_in_properties() const532 size_t HttpServerProperties::max_server_configs_stored_in_properties() const {
533 return max_server_configs_stored_in_properties_;
534 }
535
SetMaxServerConfigsStoredInProperties(size_t max_server_configs_stored_in_properties)536 void HttpServerProperties::SetMaxServerConfigsStoredInProperties(
537 size_t max_server_configs_stored_in_properties) {
538 // Do nothing if the new size is the same as the old one.
539 if (max_server_configs_stored_in_properties_ ==
540 max_server_configs_stored_in_properties) {
541 return;
542 }
543
544 max_server_configs_stored_in_properties_ =
545 max_server_configs_stored_in_properties;
546
547 // MRUCache doesn't allow the capacity of the cache to be changed. Thus create
548 // a new map with the new size and add current elements and swap the new map.
549 quic_server_info_map_.ShrinkToSize(max_server_configs_stored_in_properties_);
550 QuicServerInfoMap temp_map(max_server_configs_stored_in_properties_);
551 // Update the |canonical_server_info_map_| as well, so it stays in sync with
552 // |quic_server_info_map_|.
553 canonical_server_info_map_ = QuicCanonicalMap();
554 for (auto it = quic_server_info_map_.rbegin();
555 it != quic_server_info_map_.rend(); ++it) {
556 temp_map.Put(it->first, it->second);
557 UpdateCanonicalServerInfoMap(it->first);
558 }
559
560 quic_server_info_map_.Swap(temp_map);
561 if (properties_manager_) {
562 properties_manager_->set_max_server_configs_stored_in_properties(
563 max_server_configs_stored_in_properties);
564 }
565 }
566
IsInitialized() const567 bool HttpServerProperties::IsInitialized() const {
568 return is_initialized_;
569 }
570
OnExpireBrokenAlternativeService(const AlternativeService & expired_alternative_service,const NetworkIsolationKey & network_isolation_key)571 void HttpServerProperties::OnExpireBrokenAlternativeService(
572 const AlternativeService& expired_alternative_service,
573 const NetworkIsolationKey& network_isolation_key) {
574 // Remove every occurrence of |expired_alternative_service| from
575 // |alternative_service_map_|.
576 for (auto map_it = server_info_map_.begin();
577 map_it != server_info_map_.end();) {
578 if (!map_it->second.alternative_services.has_value() ||
579 map_it->first.network_isolation_key != network_isolation_key) {
580 ++map_it;
581 continue;
582 }
583 AlternativeServiceInfoVector* service_info =
584 &map_it->second.alternative_services.value();
585 for (auto it = service_info->begin(); it != service_info->end();) {
586 AlternativeService alternative_service(it->alternative_service());
587 // Empty hostname in map means hostname of key: substitute before
588 // comparing to |expired_alternative_service|.
589 if (alternative_service.host.empty()) {
590 alternative_service.host = map_it->first.server.host();
591 }
592 if (alternative_service == expired_alternative_service) {
593 it = service_info->erase(it);
594 continue;
595 }
596 ++it;
597 }
598 // If an origin has an empty list of alternative services, then remove it
599 // from both |canonical_alt_svc_map_| and
600 // |alternative_service_map_|.
601 if (service_info->empty()) {
602 RemoveAltSvcCanonicalHost(map_it->first.server, network_isolation_key);
603 map_it->second.alternative_services.reset();
604 map_it = server_info_map_.EraseIfEmpty(map_it);
605 continue;
606 }
607 ++map_it;
608 }
609 }
610
GetUpdatePrefsDelayForTesting()611 base::TimeDelta HttpServerProperties::GetUpdatePrefsDelayForTesting() {
612 return kUpdatePrefsDelay;
613 }
614
GetSupportsSpdyInternal(url::SchemeHostPort server,const net::NetworkIsolationKey & network_isolation_key)615 bool HttpServerProperties::GetSupportsSpdyInternal(
616 url::SchemeHostPort server,
617 const net::NetworkIsolationKey& network_isolation_key) {
618 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
619 DCHECK_NE(server.scheme(), url::kWsScheme);
620 DCHECK_NE(server.scheme(), url::kWssScheme);
621 if (server.host().empty())
622 return false;
623
624 auto server_info = server_info_map_.Get(
625 CreateServerInfoKey(std::move(server), network_isolation_key));
626 return server_info != server_info_map_.end() &&
627 server_info->second.supports_spdy.value_or(false);
628 }
629
SetSupportsSpdyInternal(url::SchemeHostPort server,const net::NetworkIsolationKey & network_isolation_key,bool supports_spdy)630 void HttpServerProperties::SetSupportsSpdyInternal(
631 url::SchemeHostPort server,
632 const net::NetworkIsolationKey& network_isolation_key,
633 bool supports_spdy) {
634 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
635 DCHECK_NE(server.scheme(), url::kWsScheme);
636 DCHECK_NE(server.scheme(), url::kWssScheme);
637 if (server.host().empty())
638 return;
639
640 auto server_info = server_info_map_.GetOrPut(
641 CreateServerInfoKey(std::move(server), network_isolation_key));
642 // If value is already the same as |supports_spdy|, or value is unset and
643 // |supports_spdy| is false, don't queue a write.
644 bool queue_write =
645 server_info->second.supports_spdy.value_or(false) != supports_spdy;
646 server_info->second.supports_spdy = supports_spdy;
647
648 if (queue_write)
649 MaybeQueueWriteProperties();
650 }
651
RequiresHTTP11Internal(url::SchemeHostPort server,const net::NetworkIsolationKey & network_isolation_key)652 bool HttpServerProperties::RequiresHTTP11Internal(
653 url::SchemeHostPort server,
654 const net::NetworkIsolationKey& network_isolation_key) {
655 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
656 DCHECK_NE(server.scheme(), url::kWsScheme);
657 DCHECK_NE(server.scheme(), url::kWssScheme);
658 if (server.host().empty())
659 return false;
660
661 auto spdy_info = server_info_map_.Get(
662 CreateServerInfoKey(std::move(server), network_isolation_key));
663 return spdy_info != server_info_map_.end() &&
664 spdy_info->second.requires_http11.value_or(false);
665 }
666
SetHTTP11RequiredInternal(url::SchemeHostPort server,const net::NetworkIsolationKey & network_isolation_key)667 void HttpServerProperties::SetHTTP11RequiredInternal(
668 url::SchemeHostPort server,
669 const net::NetworkIsolationKey& network_isolation_key) {
670 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
671 DCHECK_NE(server.scheme(), url::kWsScheme);
672 DCHECK_NE(server.scheme(), url::kWssScheme);
673 if (server.host().empty())
674 return;
675
676 server_info_map_
677 .GetOrPut(CreateServerInfoKey(std::move(server), network_isolation_key))
678 ->second.requires_http11 = true;
679 // No need to call MaybeQueueWriteProperties(), as this information is not
680 // persisted to preferences.
681 }
682
MaybeForceHTTP11Internal(url::SchemeHostPort server,const net::NetworkIsolationKey & network_isolation_key,SSLConfig * ssl_config)683 void HttpServerProperties::MaybeForceHTTP11Internal(
684 url::SchemeHostPort server,
685 const net::NetworkIsolationKey& network_isolation_key,
686 SSLConfig* ssl_config) {
687 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
688 DCHECK_NE(server.scheme(), url::kWsScheme);
689 DCHECK_NE(server.scheme(), url::kWssScheme);
690 if (RequiresHTTP11(std::move(server), network_isolation_key)) {
691 ssl_config->alpn_protos.clear();
692 ssl_config->alpn_protos.push_back(kProtoHTTP11);
693 }
694 }
695
696 AlternativeServiceInfoVector
GetAlternativeServiceInfosInternal(const url::SchemeHostPort & origin,const net::NetworkIsolationKey & network_isolation_key)697 HttpServerProperties::GetAlternativeServiceInfosInternal(
698 const url::SchemeHostPort& origin,
699 const net::NetworkIsolationKey& network_isolation_key) {
700 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
701 DCHECK_NE(origin.scheme(), url::kWsScheme);
702 DCHECK_NE(origin.scheme(), url::kWssScheme);
703
704 // Copy valid alternative service infos into
705 // |valid_alternative_service_infos|.
706 AlternativeServiceInfoVector valid_alternative_service_infos;
707 const base::Time now = clock_->Now();
708 auto map_it =
709 server_info_map_.Get(CreateServerInfoKey(origin, network_isolation_key));
710 if (map_it != server_info_map_.end() &&
711 map_it->second.alternative_services.has_value()) {
712 AlternativeServiceInfoVector* service_info =
713 &map_it->second.alternative_services.value();
714 HostPortPair host_port_pair(origin.host(), origin.port());
715 for (auto it = service_info->begin(); it != service_info->end();) {
716 if (it->expiration() < now) {
717 it = service_info->erase(it);
718 continue;
719 }
720 AlternativeService alternative_service(it->alternative_service());
721 if (alternative_service.host.empty()) {
722 alternative_service.host = origin.host();
723 }
724 // If the alternative service is equivalent to the origin (same host, same
725 // port, and both TCP), skip it.
726 if (host_port_pair.Equals(alternative_service.host_port_pair()) &&
727 alternative_service.protocol == kProtoHTTP2) {
728 ++it;
729 continue;
730 }
731 if (alternative_service.protocol == kProtoQUIC) {
732 valid_alternative_service_infos.push_back(
733 AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
734 alternative_service, it->expiration(),
735 it->advertised_versions()));
736 } else {
737 valid_alternative_service_infos.push_back(
738 AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
739 alternative_service, it->expiration()));
740 }
741 ++it;
742 }
743 if (service_info->empty()) {
744 map_it->second.alternative_services.reset();
745 server_info_map_.EraseIfEmpty(map_it);
746 }
747 return valid_alternative_service_infos;
748 }
749
750 auto canonical = GetCanonicalAltSvcHost(origin, network_isolation_key);
751 if (canonical == canonical_alt_svc_map_.end()) {
752 return AlternativeServiceInfoVector();
753 }
754 map_it = server_info_map_.Get(
755 CreateServerInfoKey(canonical->second, network_isolation_key));
756 if (map_it == server_info_map_.end() ||
757 !map_it->second.alternative_services.has_value()) {
758 return AlternativeServiceInfoVector();
759 }
760 AlternativeServiceInfoVector* service_info =
761 &map_it->second.alternative_services.value();
762 for (auto it = service_info->begin(); it != service_info->end();) {
763 if (it->expiration() < now) {
764 it = service_info->erase(it);
765 continue;
766 }
767 AlternativeService alternative_service(it->alternative_service());
768 if (alternative_service.host.empty()) {
769 alternative_service.host = canonical->second.host();
770 if (IsAlternativeServiceBroken(alternative_service,
771 network_isolation_key)) {
772 ++it;
773 continue;
774 }
775 alternative_service.host = origin.host();
776 } else if (IsAlternativeServiceBroken(alternative_service,
777 network_isolation_key)) {
778 ++it;
779 continue;
780 }
781 if (alternative_service.protocol == kProtoQUIC) {
782 valid_alternative_service_infos.push_back(
783 AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
784 alternative_service, it->expiration(),
785 it->advertised_versions()));
786 } else {
787 valid_alternative_service_infos.push_back(
788 AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
789 alternative_service, it->expiration()));
790 }
791 ++it;
792 }
793 if (service_info->empty())
794 server_info_map_.EraseIfEmpty(map_it);
795 return valid_alternative_service_infos;
796 }
797
SetAlternativeServicesInternal(const url::SchemeHostPort & origin,const net::NetworkIsolationKey & network_isolation_key,const AlternativeServiceInfoVector & alternative_service_info_vector)798 void HttpServerProperties::SetAlternativeServicesInternal(
799 const url::SchemeHostPort& origin,
800 const net::NetworkIsolationKey& network_isolation_key,
801 const AlternativeServiceInfoVector& alternative_service_info_vector) {
802 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
803 DCHECK_NE(origin.scheme(), url::kWsScheme);
804 DCHECK_NE(origin.scheme(), url::kWssScheme);
805
806 if (alternative_service_info_vector.empty()) {
807 RemoveAltSvcCanonicalHost(origin, network_isolation_key);
808 // Don't bother moving to front when erasing information.
809 auto it = server_info_map_.Peek(
810 CreateServerInfoKey(origin, network_isolation_key));
811
812 if (it == server_info_map_.end() ||
813 !it->second.alternative_services.has_value()) {
814 return;
815 }
816
817 it->second.alternative_services.reset();
818 server_info_map_.EraseIfEmpty(it);
819 MaybeQueueWriteProperties();
820 return;
821 }
822
823 auto it = server_info_map_.GetOrPut(
824 CreateServerInfoKey(origin, network_isolation_key));
825 bool need_update_pref = true;
826 if (it->second.alternative_services.has_value()) {
827 DCHECK(!it->second.empty());
828 if (it->second.alternative_services->size() ==
829 alternative_service_info_vector.size()) {
830 const base::Time now = clock_->Now();
831 need_update_pref = false;
832 auto new_it = alternative_service_info_vector.begin();
833 for (const auto& old : *it->second.alternative_services) {
834 // Persist to disk immediately if new entry has different scheme, host,
835 // or port.
836 if (old.alternative_service() != new_it->alternative_service()) {
837 need_update_pref = true;
838 break;
839 }
840 // Also persist to disk if new expiration it more that twice as far or
841 // less than half as far in the future.
842 base::Time old_time = old.expiration();
843 base::Time new_time = new_it->expiration();
844 if (new_time - now > 2 * (old_time - now) ||
845 2 * (new_time - now) < (old_time - now)) {
846 need_update_pref = true;
847 break;
848 }
849 // Also persist to disk if new entry has a different list of advertised
850 // versions.
851 if (old.advertised_versions() != new_it->advertised_versions()) {
852 need_update_pref = true;
853 break;
854 }
855 ++new_it;
856 }
857 }
858 }
859
860 const bool previously_no_alternative_services =
861 (GetIteratorWithAlternativeServiceInfo(origin, network_isolation_key) ==
862 server_info_map_.end());
863
864 it->second.alternative_services = alternative_service_info_vector;
865
866 if (previously_no_alternative_services &&
867 !GetAlternativeServiceInfos(origin, network_isolation_key).empty()) {
868 // TODO(rch): Consider the case where multiple requests are started
869 // before the first completes. In this case, only one of the jobs
870 // would reach this code, whereas all of them should should have.
871 HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING,
872 IsGoogleHost(origin.host()));
873 }
874
875 // If this host ends with a canonical suffix, then set it as the
876 // canonical host.
877 const char* kCanonicalScheme = "https";
878 if (origin.scheme() == kCanonicalScheme) {
879 const std::string* canonical_suffix = GetCanonicalSuffix(origin.host());
880 if (canonical_suffix != nullptr) {
881 url::SchemeHostPort canonical_server(kCanonicalScheme, *canonical_suffix,
882 origin.port());
883 canonical_alt_svc_map_[CreateServerInfoKey(
884 canonical_server, network_isolation_key)] = origin;
885 }
886 }
887
888 if (need_update_pref)
889 MaybeQueueWriteProperties();
890 }
891
SetServerNetworkStatsInternal(url::SchemeHostPort server,const NetworkIsolationKey & network_isolation_key,ServerNetworkStats stats)892 void HttpServerProperties::SetServerNetworkStatsInternal(
893 url::SchemeHostPort server,
894 const NetworkIsolationKey& network_isolation_key,
895 ServerNetworkStats stats) {
896 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
897 DCHECK_NE(server.scheme(), url::kWsScheme);
898 DCHECK_NE(server.scheme(), url::kWssScheme);
899
900 auto server_info = server_info_map_.GetOrPut(
901 CreateServerInfoKey(std::move(server), network_isolation_key));
902 bool changed = !server_info->second.server_network_stats.has_value() ||
903 server_info->second.server_network_stats.value() != stats;
904
905 if (changed) {
906 server_info->second.server_network_stats = stats;
907 MaybeQueueWriteProperties();
908 }
909 }
910
ClearServerNetworkStatsInternal(url::SchemeHostPort server,const NetworkIsolationKey & network_isolation_key)911 void HttpServerProperties::ClearServerNetworkStatsInternal(
912 url::SchemeHostPort server,
913 const NetworkIsolationKey& network_isolation_key) {
914 auto server_info = server_info_map_.Peek(
915 CreateServerInfoKey(std::move(server), network_isolation_key));
916 // If stats are empty, nothing to do.
917 if (server_info == server_info_map_.end() ||
918 !server_info->second.server_network_stats.has_value()) {
919 return;
920 }
921
922 // Otherwise, clear and delete if needed. No need to bring to front of MRU
923 // cache when clearing data.
924 server_info->second.server_network_stats.reset();
925 if (server_info->second.empty())
926 server_info_map_.EraseIfEmpty(server_info);
927 MaybeQueueWriteProperties();
928 }
929
GetServerNetworkStatsInternal(url::SchemeHostPort server,const NetworkIsolationKey & network_isolation_key)930 const ServerNetworkStats* HttpServerProperties::GetServerNetworkStatsInternal(
931 url::SchemeHostPort server,
932 const NetworkIsolationKey& network_isolation_key) {
933 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
934 DCHECK_NE(server.scheme(), url::kWsScheme);
935 DCHECK_NE(server.scheme(), url::kWssScheme);
936
937 auto server_info = server_info_map_.Get(
938 CreateServerInfoKey(std::move(server), network_isolation_key));
939 if (server_info == server_info_map_.end() ||
940 !server_info->second.server_network_stats.has_value()) {
941 return nullptr;
942 }
943 return &server_info->second.server_network_stats.value();
944 }
945
946 HttpServerProperties::QuicServerInfoMapKey
CreateQuicServerInfoKey(const quic::QuicServerId & server_id,const NetworkIsolationKey & network_isolation_key) const947 HttpServerProperties::CreateQuicServerInfoKey(
948 const quic::QuicServerId& server_id,
949 const NetworkIsolationKey& network_isolation_key) const {
950 return QuicServerInfoMapKey(server_id, network_isolation_key,
951 use_network_isolation_key_);
952 }
953
954 HttpServerProperties::ServerInfoMapKey
CreateServerInfoKey(const url::SchemeHostPort & server,const NetworkIsolationKey & network_isolation_key) const955 HttpServerProperties::CreateServerInfoKey(
956 const url::SchemeHostPort& server,
957 const NetworkIsolationKey& network_isolation_key) const {
958 return ServerInfoMapKey(server, network_isolation_key,
959 use_network_isolation_key_);
960 }
961
962 HttpServerProperties::ServerInfoMap::const_iterator
GetIteratorWithAlternativeServiceInfo(const url::SchemeHostPort & server,const net::NetworkIsolationKey & network_isolation_key)963 HttpServerProperties::GetIteratorWithAlternativeServiceInfo(
964 const url::SchemeHostPort& server,
965 const net::NetworkIsolationKey& network_isolation_key) {
966 ServerInfoMap::const_iterator it =
967 server_info_map_.Get(CreateServerInfoKey(server, network_isolation_key));
968 if (it != server_info_map_.end() && it->second.alternative_services)
969 return it;
970
971 auto canonical = GetCanonicalAltSvcHost(server, network_isolation_key);
972 if (canonical == canonical_alt_svc_map_.end()) {
973 return server_info_map_.end();
974 }
975
976 const url::SchemeHostPort canonical_server = canonical->second;
977 it = server_info_map_.Get(
978 CreateServerInfoKey(canonical_server, network_isolation_key));
979 if (it == server_info_map_.end() || !it->second.alternative_services)
980 return server_info_map_.end();
981
982 for (const AlternativeServiceInfo& alternative_service_info :
983 it->second.alternative_services.value()) {
984 AlternativeService alternative_service(
985 alternative_service_info.alternative_service());
986 if (alternative_service.host.empty()) {
987 alternative_service.host = canonical_server.host();
988 }
989 if (!IsAlternativeServiceBroken(alternative_service,
990 network_isolation_key)) {
991 return it;
992 }
993 }
994
995 RemoveAltSvcCanonicalHost(canonical_server, network_isolation_key);
996 return server_info_map_.end();
997 }
998
999 HttpServerProperties::CanonicalMap::const_iterator
GetCanonicalAltSvcHost(const url::SchemeHostPort & server,const net::NetworkIsolationKey & network_isolation_key) const1000 HttpServerProperties::GetCanonicalAltSvcHost(
1001 const url::SchemeHostPort& server,
1002 const net::NetworkIsolationKey& network_isolation_key) const {
1003 const char* kCanonicalScheme = "https";
1004 if (server.scheme() != kCanonicalScheme)
1005 return canonical_alt_svc_map_.end();
1006
1007 const std::string* canonical_suffix = GetCanonicalSuffix(server.host());
1008 if (canonical_suffix == nullptr)
1009 return canonical_alt_svc_map_.end();
1010
1011 url::SchemeHostPort canonical_server(kCanonicalScheme, *canonical_suffix,
1012 server.port());
1013 return canonical_alt_svc_map_.find(
1014 CreateServerInfoKey(canonical_server, network_isolation_key));
1015 }
1016
1017 HttpServerProperties::QuicCanonicalMap::const_iterator
GetCanonicalServerInfoHost(const QuicServerInfoMapKey & key) const1018 HttpServerProperties::GetCanonicalServerInfoHost(
1019 const QuicServerInfoMapKey& key) const {
1020 const std::string* canonical_suffix =
1021 GetCanonicalSuffix(key.server_id.host());
1022 if (canonical_suffix == nullptr)
1023 return canonical_server_info_map_.end();
1024
1025 quic::QuicServerId canonical_server_id(*canonical_suffix,
1026 key.server_id.privacy_mode_enabled(),
1027 key.server_id.port());
1028 return canonical_server_info_map_.find(
1029 CreateQuicServerInfoKey(canonical_server_id, key.network_isolation_key));
1030 }
1031
RemoveAltSvcCanonicalHost(const url::SchemeHostPort & server,const NetworkIsolationKey & network_isolation_key)1032 void HttpServerProperties::RemoveAltSvcCanonicalHost(
1033 const url::SchemeHostPort& server,
1034 const NetworkIsolationKey& network_isolation_key) {
1035 auto canonical = GetCanonicalAltSvcHost(server, network_isolation_key);
1036 if (canonical == canonical_alt_svc_map_.end())
1037 return;
1038
1039 canonical_alt_svc_map_.erase(canonical->first);
1040 }
1041
UpdateCanonicalServerInfoMap(const QuicServerInfoMapKey & key)1042 void HttpServerProperties::UpdateCanonicalServerInfoMap(
1043 const QuicServerInfoMapKey& key) {
1044 const std::string* suffix = GetCanonicalSuffix(key.server_id.host());
1045 if (!suffix)
1046 return;
1047 quic::QuicServerId canonical_server(
1048 *suffix, key.server_id.privacy_mode_enabled(), key.server_id.port());
1049
1050 canonical_server_info_map_[CreateQuicServerInfoKey(
1051 canonical_server, key.network_isolation_key)] = key.server_id;
1052 }
1053
GetCanonicalSuffix(const std::string & host) const1054 const std::string* HttpServerProperties::GetCanonicalSuffix(
1055 const std::string& host) const {
1056 // If this host ends with a canonical suffix, then return the canonical
1057 // suffix.
1058 for (const std::string& canonical_suffix : canonical_suffixes_) {
1059 if (base::EndsWith(host, canonical_suffix,
1060 base::CompareCase::INSENSITIVE_ASCII)) {
1061 return &canonical_suffix;
1062 }
1063 }
1064 return nullptr;
1065 }
1066
OnPrefsLoaded(std::unique_ptr<ServerInfoMap> server_info_map,const IPAddress & last_local_address_when_quic_worked,std::unique_ptr<QuicServerInfoMap> quic_server_info_map,std::unique_ptr<BrokenAlternativeServiceList> broken_alternative_service_list,std::unique_ptr<RecentlyBrokenAlternativeServices> recently_broken_alternative_services)1067 void HttpServerProperties::OnPrefsLoaded(
1068 std::unique_ptr<ServerInfoMap> server_info_map,
1069 const IPAddress& last_local_address_when_quic_worked,
1070 std::unique_ptr<QuicServerInfoMap> quic_server_info_map,
1071 std::unique_ptr<BrokenAlternativeServiceList>
1072 broken_alternative_service_list,
1073 std::unique_ptr<RecentlyBrokenAlternativeServices>
1074 recently_broken_alternative_services) {
1075 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1076
1077 DCHECK(!is_initialized_);
1078
1079 // Either all of these are nullptr, or none of them are (except the broken alt
1080 // service fields).
1081 if (server_info_map) {
1082 OnServerInfoLoaded(std::move(server_info_map));
1083 OnLastLocalAddressWhenQuicWorkedLoaded(last_local_address_when_quic_worked);
1084 OnQuicServerInfoMapLoaded(std::move(quic_server_info_map));
1085 if (recently_broken_alternative_services) {
1086 DCHECK(broken_alternative_service_list);
1087 OnBrokenAndRecentlyBrokenAlternativeServicesLoaded(
1088 std::move(broken_alternative_service_list),
1089 std::move(recently_broken_alternative_services));
1090 }
1091 }
1092
1093 is_initialized_ = true;
1094
1095 if (queue_write_on_load_) {
1096 // Leaving this as true doesn't actually have any effect, but seems best to
1097 // be safe.
1098 queue_write_on_load_ = false;
1099 MaybeQueueWriteProperties();
1100 }
1101 }
1102
OnServerInfoLoaded(std::unique_ptr<ServerInfoMap> server_info_map)1103 void HttpServerProperties::OnServerInfoLoaded(
1104 std::unique_ptr<ServerInfoMap> server_info_map) {
1105 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1106
1107 // Perform a simple sanity check on loaded data, when DCHECKs are enabled.
1108 #if DCHECK_IS_ON()
1109 if (!use_network_isolation_key_) {
1110 for (auto server_info = server_info_map->begin();
1111 server_info != server_info_map->end(); ++server_info) {
1112 DCHECK(server_info->first.network_isolation_key.IsEmpty());
1113 }
1114 }
1115 #endif // DCHECK_IS_ON()
1116
1117 // Swap in the entries from persisted data. This allows the MRU cache to be
1118 // sorted based on the order of the entries in the newer in-memory cache.
1119 server_info_map_.Swap(*server_info_map);
1120
1121 // Add the entries from the memory cache.
1122 for (auto it = server_info_map->rbegin(); it != server_info_map->rend();
1123 ++it) {
1124 // If there's no corresponding old entry, add the new entry directly.
1125 auto old_entry = server_info_map_.Get(it->first);
1126 if (old_entry == server_info_map_.end()) {
1127 server_info_map_.Put(it->first, std::move(it->second));
1128 continue;
1129 }
1130
1131 // Otherwise, merge the old and new entries. Prefer values from older
1132 // entries.
1133 if (!old_entry->second.supports_spdy.has_value())
1134 old_entry->second.supports_spdy = it->second.supports_spdy;
1135 if (!old_entry->second.alternative_services.has_value())
1136 old_entry->second.alternative_services = it->second.alternative_services;
1137 if (!old_entry->second.server_network_stats.has_value())
1138 old_entry->second.server_network_stats = it->second.server_network_stats;
1139
1140 // |requires_http11| isn't saved to prefs, so the loaded entry should not
1141 // have it set. Unconditionally copy it from the new entry.
1142 DCHECK(!old_entry->second.requires_http11.has_value());
1143 old_entry->second.requires_http11 = it->second.requires_http11;
1144 }
1145
1146 // Attempt to find canonical servers. Canonical suffix only apply to HTTPS.
1147 const uint16_t kCanonicalPort = 443;
1148 const char* kCanonicalScheme = "https";
1149 for (auto it = server_info_map_.begin(); it != server_info_map_.end(); ++it) {
1150 if (!it->second.alternative_services ||
1151 it->first.server.scheme() != kCanonicalScheme) {
1152 continue;
1153 }
1154 const std::string* canonical_suffix =
1155 GetCanonicalSuffix(it->first.server.host());
1156 if (!canonical_suffix)
1157 continue;
1158 ServerInfoMapKey key = CreateServerInfoKey(
1159 url::SchemeHostPort(kCanonicalScheme, *canonical_suffix,
1160 kCanonicalPort),
1161 it->first.network_isolation_key);
1162 // If we already have a valid canonical server, we're done.
1163 if (base::Contains(canonical_alt_svc_map_, key)) {
1164 auto it = server_info_map_.Peek(key);
1165 if (it != server_info_map_.end() &&
1166 it->second.alternative_services.has_value()) {
1167 continue;
1168 }
1169 }
1170 canonical_alt_svc_map_[key] = it->first.server;
1171 }
1172 }
1173
OnLastLocalAddressWhenQuicWorkedLoaded(const IPAddress & last_local_address_when_quic_worked)1174 void HttpServerProperties::OnLastLocalAddressWhenQuicWorkedLoaded(
1175 const IPAddress& last_local_address_when_quic_worked) {
1176 last_local_address_when_quic_worked_ = last_local_address_when_quic_worked;
1177 }
1178
OnQuicServerInfoMapLoaded(std::unique_ptr<QuicServerInfoMap> quic_server_info_map)1179 void HttpServerProperties::OnQuicServerInfoMapLoaded(
1180 std::unique_ptr<QuicServerInfoMap> quic_server_info_map) {
1181 DCHECK_EQ(quic_server_info_map->max_size(), quic_server_info_map_.max_size());
1182
1183 // Add the entries from persisted data.
1184 quic_server_info_map_.Swap(*quic_server_info_map);
1185
1186 // Add the entries from the memory cache.
1187 for (auto it = quic_server_info_map->rbegin();
1188 it != quic_server_info_map->rend(); ++it) {
1189 if (quic_server_info_map_.Get(it->first) == quic_server_info_map_.end()) {
1190 quic_server_info_map_.Put(it->first, it->second);
1191 }
1192 }
1193
1194 // Repopulate |canonical_server_info_map_| to stay in sync with
1195 // |quic_server_info_map_|.
1196 canonical_server_info_map_.clear();
1197 for (auto it = quic_server_info_map_.rbegin();
1198 it != quic_server_info_map_.rend(); ++it) {
1199 UpdateCanonicalServerInfoMap(it->first);
1200 }
1201 }
1202
OnBrokenAndRecentlyBrokenAlternativeServicesLoaded(std::unique_ptr<BrokenAlternativeServiceList> broken_alternative_service_list,std::unique_ptr<RecentlyBrokenAlternativeServices> recently_broken_alternative_services)1203 void HttpServerProperties::OnBrokenAndRecentlyBrokenAlternativeServicesLoaded(
1204 std::unique_ptr<BrokenAlternativeServiceList>
1205 broken_alternative_service_list,
1206 std::unique_ptr<RecentlyBrokenAlternativeServices>
1207 recently_broken_alternative_services) {
1208 broken_alternative_services_.SetBrokenAndRecentlyBrokenAlternativeServices(
1209 std::move(broken_alternative_service_list),
1210 std::move(recently_broken_alternative_services));
1211 }
1212
MaybeQueueWriteProperties()1213 void HttpServerProperties::MaybeQueueWriteProperties() {
1214 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1215
1216 if (prefs_update_timer_.IsRunning() || !properties_manager_)
1217 return;
1218
1219 if (!is_initialized_) {
1220 queue_write_on_load_ = true;
1221 return;
1222 }
1223
1224 prefs_update_timer_.Start(
1225 FROM_HERE, kUpdatePrefsDelay,
1226 base::BindOnce(&HttpServerProperties::WriteProperties,
1227 base::Unretained(this), base::OnceClosure()));
1228 }
1229
WriteProperties(base::OnceClosure callback) const1230 void HttpServerProperties::WriteProperties(base::OnceClosure callback) const {
1231 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1232 DCHECK(properties_manager_);
1233
1234 // |this| shouldn't be waiting to load properties cached to disk when this
1235 // method is invoked, since this method will overwrite any cached properties.
1236 DCHECK(is_initialized_);
1237
1238 // There shouldn't be a queued update when this is run, since this method
1239 // removes the need for any update to be queued.
1240 DCHECK(!prefs_update_timer_.IsRunning());
1241
1242 properties_manager_->WriteToPrefs(
1243 server_info_map_,
1244 base::BindRepeating(&HttpServerProperties::GetCanonicalSuffix,
1245 base::Unretained(this)),
1246 last_local_address_when_quic_worked_, quic_server_info_map_,
1247 broken_alternative_services_.broken_alternative_service_list(),
1248 broken_alternative_services_.recently_broken_alternative_services(),
1249 std::move(callback));
1250 }
1251
1252 } // namespace net
1253