1 // Copyright 2014 The Chromium Authors
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 "components/cronet/cronet_context.h"
6
7 #include <limits.h>
8 #include <stddef.h>
9 #include <stdint.h>
10
11 #include <limits>
12 #include <map>
13 #include <memory>
14 #include <set>
15 #include <utility>
16
17 #include "base/base64.h"
18 #include "base/files/file_path.h"
19 #include "base/files/file_util.h"
20 #include "base/files/scoped_file.h"
21 #include "base/functional/bind.h"
22 #include "base/functional/callback.h"
23 #include "base/lazy_instance.h"
24 #include "base/logging.h"
25 #include "base/memory/raw_ptr.h"
26 #include "base/message_loop/message_pump_type.h"
27 #include "base/metrics/statistics_recorder.h"
28 #include "base/synchronization/waitable_event.h"
29 #include "base/task/sequenced_task_runner.h"
30 #include "base/task/single_thread_task_runner.h"
31 #include "base/threading/thread_restrictions.h"
32 #include "base/time/time.h"
33 #include "base/values.h"
34 #include "build/build_config.h"
35 #include "components/cronet/cronet_global_state.h"
36 #include "components/cronet/cronet_prefs_manager.h"
37 #include "components/cronet/host_cache_persistence_manager.h"
38 #include "components/cronet/url_request_context_config.h"
39 #include "net/base/ip_address.h"
40 #include "net/base/load_flags.h"
41 #include "net/base/logging_network_change_observer.h"
42 #include "net/base/net_errors.h"
43 #include "net/base/network_delegate_impl.h"
44 #include "net/base/network_isolation_key.h"
45 #include "net/base/url_util.h"
46 #include "net/cert/caching_cert_verifier.h"
47 #include "net/cert/cert_verifier.h"
48 #include "net/cert/x509_certificate.h"
49 #include "net/cookies/cookie_inclusion_status.h"
50 #include "net/cookies/cookie_monster.h"
51 #include "net/cookies/cookie_setting_override.h"
52 #include "net/first_party_sets/first_party_set_metadata.h"
53 #include "net/http/http_auth_handler_factory.h"
54 #include "net/http/transport_security_state.h"
55 #include "net/log/file_net_log_observer.h"
56 #include "net/log/net_log_util.h"
57 #include "net/net_buildflags.h"
58 #include "net/nqe/network_quality_estimator_params.h"
59 #include "net/proxy_resolution/proxy_config_service_fixed.h"
60 #include "net/proxy_resolution/proxy_resolution_service.h"
61 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
62 #include "net/url_request/url_request_context.h"
63 #include "net/url_request/url_request_context_builder.h"
64 #include "net/url_request/url_request_context_getter.h"
65 #include "net/url_request/url_request_interceptor.h"
66
67 #if BUILDFLAG(ENABLE_REPORTING)
68 #include "net/network_error_logging/network_error_logging_service.h"
69 #include "net/reporting/reporting_service.h"
70 #endif // BUILDFLAG(ENABLE_REPORTING)
71
72 namespace {
73
74 // This class wraps a NetLog that also contains network change events.
75 class NetLogWithNetworkChangeEvents {
76 public:
NetLogWithNetworkChangeEvents()77 NetLogWithNetworkChangeEvents() : net_log_(net::NetLog::Get()) {}
78
79 NetLogWithNetworkChangeEvents(const NetLogWithNetworkChangeEvents&) = delete;
80 NetLogWithNetworkChangeEvents& operator=(
81 const NetLogWithNetworkChangeEvents&) = delete;
82
net_log()83 net::NetLog* net_log() { return net_log_; }
84 // This function registers with the NetworkChangeNotifier and so must be
85 // called *after* the NetworkChangeNotifier is created. Should only be
86 // called on the init thread as it is not thread-safe and the init thread is
87 // the thread the NetworkChangeNotifier is created on. This function is
88 // not thread-safe because accesses to |net_change_logger_| are not atomic.
89 // There might be multiple CronetEngines each with a network thread so
90 // so the init thread is used. |g_net_log_| also outlives the network threads
91 // so it would be unsafe to receive callbacks on the network threads without
92 // a complicated thread-safe reference-counting system to control callback
93 // registration.
EnsureInitializedOnInitThread()94 void EnsureInitializedOnInitThread() {
95 DCHECK(cronet::OnInitThread());
96 if (net_change_logger_)
97 return;
98 net_change_logger_ =
99 std::make_unique<net::LoggingNetworkChangeObserver>(net_log_);
100 }
101
102 private:
103 raw_ptr<net::NetLog> net_log_;
104 // LoggingNetworkChangeObserver logs network change events to a NetLog.
105 // This class bundles one LoggingNetworkChangeObserver with one NetLog,
106 // so network change event are logged just once in the NetLog.
107 std::unique_ptr<net::LoggingNetworkChangeObserver> net_change_logger_;
108 };
109
110 // Use a global NetLog instance. See crbug.com/486120.
111 static base::LazyInstance<NetLogWithNetworkChangeEvents>::Leaky g_net_log =
112 LAZY_INSTANCE_INITIALIZER;
113
114 class BasicNetworkDelegate : public net::NetworkDelegateImpl {
115 public:
BasicNetworkDelegate()116 BasicNetworkDelegate() {}
117
118 BasicNetworkDelegate(const BasicNetworkDelegate&) = delete;
119 BasicNetworkDelegate& operator=(const BasicNetworkDelegate&) = delete;
120
~BasicNetworkDelegate()121 ~BasicNetworkDelegate() override {}
122
123 private:
124 // net::NetworkDelegate implementation.
OnAnnotateAndMoveUserBlockedCookies(const net::URLRequest & request,const net::FirstPartySetMetadata & first_party_set_metadata,net::CookieAccessResultList & maybe_included_cookies,net::CookieAccessResultList & excluded_cookies)125 bool OnAnnotateAndMoveUserBlockedCookies(
126 const net::URLRequest& request,
127 const net::FirstPartySetMetadata& first_party_set_metadata,
128 net::CookieAccessResultList& maybe_included_cookies,
129 net::CookieAccessResultList& excluded_cookies) override {
130 // Disallow sending cookies by default.
131 ExcludeAllCookies(net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES,
132 maybe_included_cookies, excluded_cookies);
133 return false;
134 }
135
OnCanSetCookie(const net::URLRequest & request,const net::CanonicalCookie & cookie,net::CookieOptions * options,const net::FirstPartySetMetadata & first_party_set_metadata,net::CookieInclusionStatus * inclusion_status)136 bool OnCanSetCookie(
137 const net::URLRequest& request,
138 const net::CanonicalCookie& cookie,
139 net::CookieOptions* options,
140 const net::FirstPartySetMetadata& first_party_set_metadata,
141 net::CookieInclusionStatus* inclusion_status) override {
142 // Disallow saving cookies by default.
143 return false;
144 }
145 };
146
147 // Helper function to make a net::URLRequestContext aware of a QUIC hint.
SetQuicHint(net::URLRequestContext * context,const cronet::URLRequestContextConfig::QuicHint * quic_hint)148 void SetQuicHint(net::URLRequestContext* context,
149 const cronet::URLRequestContextConfig::QuicHint* quic_hint) {
150 if (quic_hint->host.empty()) {
151 LOG(ERROR) << "Empty QUIC hint host: " << quic_hint->host;
152 return;
153 }
154
155 url::CanonHostInfo host_info;
156 std::string canon_host(net::CanonicalizeHost(quic_hint->host, &host_info));
157 if (!host_info.IsIPAddress() &&
158 !net::IsCanonicalizedHostCompliant(canon_host)) {
159 LOG(ERROR) << "Invalid QUIC hint host: " << quic_hint->host;
160 return;
161 }
162
163 if (quic_hint->port <= std::numeric_limits<uint16_t>::min() ||
164 quic_hint->port > std::numeric_limits<uint16_t>::max()) {
165 LOG(ERROR) << "Invalid QUIC hint port: " << quic_hint->port;
166 return;
167 }
168
169 if (quic_hint->alternate_port <= std::numeric_limits<uint16_t>::min() ||
170 quic_hint->alternate_port > std::numeric_limits<uint16_t>::max()) {
171 LOG(ERROR) << "Invalid QUIC hint alternate port: "
172 << quic_hint->alternate_port;
173 return;
174 }
175
176 url::SchemeHostPort quic_server("https", canon_host, quic_hint->port);
177 net::AlternativeService alternative_service(
178 net::kProtoQUIC, "", static_cast<uint16_t>(quic_hint->alternate_port));
179 context->http_server_properties()->SetQuicAlternativeService(
180 quic_server, net::NetworkAnonymizationKey(), alternative_service,
181 base::Time::Max(), quic::ParsedQuicVersionVector());
182 }
183
184 // net::NetworkChangeNotifier doesn't provide an API to query if a specific
185 // network has become disconnected. For these network though, it will return
186 // CONNECTION_UNKNOWN as their connection type. This should be a good enough
187 // approximation for the time being.
IsNetworkNoLongerConnected(net::handles::NetworkHandle network)188 bool IsNetworkNoLongerConnected(net::handles::NetworkHandle network) {
189 return net::NetworkChangeNotifier::GetNetworkConnectionType(network) ==
190 net::NetworkChangeNotifier::CONNECTION_UNKNOWN;
191 }
192
193 } // namespace
194
195 namespace cronet {
196
CronetContext(std::unique_ptr<URLRequestContextConfig> context_config,std::unique_ptr<Callback> callback,scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)197 CronetContext::CronetContext(
198 std::unique_ptr<URLRequestContextConfig> context_config,
199 std::unique_ptr<Callback> callback,
200 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
201 : bidi_stream_detect_broken_connection_(
202 context_config->bidi_stream_detect_broken_connection),
203 heartbeat_interval_(context_config->heartbeat_interval),
204 default_load_flags_(
205 net::LOAD_NORMAL |
206 (context_config->load_disable_cache ? net::LOAD_DISABLE_CACHE : 0)),
207 network_tasks_(
208 new NetworkTasks(std::move(context_config), std::move(callback))),
209 network_task_runner_(network_task_runner) {
210 if (!network_task_runner_) {
211 network_thread_ = std::make_unique<base::Thread>("network");
212 base::Thread::Options options;
213 options.message_pump_type = base::MessagePumpType::IO;
214 network_thread_->StartWithOptions(std::move(options));
215 network_task_runner_ = network_thread_->task_runner();
216 }
217 }
218
~CronetContext()219 CronetContext::~CronetContext() {
220 DCHECK(!GetNetworkTaskRunner()->BelongsToCurrentThread());
221 GetNetworkTaskRunner()->DeleteSoon(FROM_HERE, network_tasks_.get());
222 }
223
NetworkTasks(std::unique_ptr<URLRequestContextConfig> context_config,std::unique_ptr<CronetContext::Callback> callback)224 CronetContext::NetworkTasks::NetworkTasks(
225 std::unique_ptr<URLRequestContextConfig> context_config,
226 std::unique_ptr<CronetContext::Callback> callback)
227 : default_context_(nullptr),
228 is_default_context_initialized_(false),
229 context_config_(std::move(context_config)),
230 callback_(std::move(callback)) {
231 DETACH_FROM_THREAD(network_thread_checker_);
232 }
233
~NetworkTasks()234 CronetContext::NetworkTasks::~NetworkTasks() {
235 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
236 callback_->OnDestroyNetworkThread();
237
238 if (cronet_prefs_manager_)
239 cronet_prefs_manager_->PrepareForShutdown();
240
241 if (network_quality_estimator_) {
242 network_quality_estimator_->RemoveRTTObserver(this);
243 network_quality_estimator_->RemoveThroughputObserver(this);
244 network_quality_estimator_->RemoveEffectiveConnectionTypeObserver(this);
245 network_quality_estimator_->RemoveRTTAndThroughputEstimatesObserver(this);
246 }
247
248 if (net::NetworkChangeNotifier::AreNetworkHandlesSupported())
249 net::NetworkChangeNotifier::RemoveNetworkObserver(this);
250 }
251
InitRequestContextOnInitThread()252 void CronetContext::InitRequestContextOnInitThread() {
253 DCHECK(OnInitThread());
254 // Cannot create this inside Initialize because Android requires this to be
255 // created on the JNI thread.
256 auto proxy_config_service =
257 cronet::CreateProxyConfigService(GetNetworkTaskRunner());
258 g_net_log.Get().EnsureInitializedOnInitThread();
259 GetNetworkTaskRunner()->PostTask(
260 FROM_HERE,
261 base::BindOnce(&CronetContext::NetworkTasks::Initialize,
262 base::Unretained(network_tasks_), GetNetworkTaskRunner(),
263 GetFileThread()->task_runner(),
264 std::move(proxy_config_service)));
265 }
266
ConfigureNetworkQualityEstimatorForTesting(bool use_local_host_requests,bool use_smaller_responses,bool disable_offline_check)267 void CronetContext::NetworkTasks::ConfigureNetworkQualityEstimatorForTesting(
268 bool use_local_host_requests,
269 bool use_smaller_responses,
270 bool disable_offline_check) {
271 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
272 network_quality_estimator_->SetUseLocalHostRequestsForTesting(
273 use_local_host_requests);
274 network_quality_estimator_->SetUseSmallResponsesForTesting(
275 use_smaller_responses);
276 network_quality_estimator_->DisableOfflineCheckForTesting(
277 disable_offline_check);
278 }
279
ConfigureNetworkQualityEstimatorForTesting(bool use_local_host_requests,bool use_smaller_responses,bool disable_offline_check)280 void CronetContext::ConfigureNetworkQualityEstimatorForTesting(
281 bool use_local_host_requests,
282 bool use_smaller_responses,
283 bool disable_offline_check) {
284 PostTaskToNetworkThread(
285 FROM_HERE,
286 base::BindOnce(&CronetContext::NetworkTasks::
287 ConfigureNetworkQualityEstimatorForTesting,
288 base::Unretained(network_tasks_), use_local_host_requests,
289 use_smaller_responses, disable_offline_check));
290 }
291
URLRequestContextExistsForTesting(net::handles::NetworkHandle network)292 bool CronetContext::URLRequestContextExistsForTesting(
293 net::handles::NetworkHandle network) {
294 DCHECK(IsOnNetworkThread());
295 return network_tasks_->URLRequestContextExistsForTesting(network); // IN-TEST
296 }
297
ProvideRTTObservations(bool should)298 void CronetContext::NetworkTasks::ProvideRTTObservations(bool should) {
299 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
300 if (!network_quality_estimator_)
301 return;
302 if (should) {
303 network_quality_estimator_->AddRTTObserver(this);
304 } else {
305 network_quality_estimator_->RemoveRTTObserver(this);
306 }
307 }
308
ProvideRTTObservations(bool should)309 void CronetContext::ProvideRTTObservations(bool should) {
310 PostTaskToNetworkThread(
311 FROM_HERE,
312 base::BindOnce(&CronetContext::NetworkTasks::ProvideRTTObservations,
313 base::Unretained(network_tasks_), should));
314 }
315
ProvideThroughputObservations(bool should)316 void CronetContext::NetworkTasks::ProvideThroughputObservations(bool should) {
317 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
318 if (!network_quality_estimator_)
319 return;
320 if (should) {
321 network_quality_estimator_->AddThroughputObserver(this);
322 } else {
323 network_quality_estimator_->RemoveThroughputObserver(this);
324 }
325 }
326
ProvideThroughputObservations(bool should)327 void CronetContext::ProvideThroughputObservations(bool should) {
328 PostTaskToNetworkThread(
329 FROM_HERE,
330 base::BindOnce(
331 &CronetContext::NetworkTasks::ProvideThroughputObservations,
332 base::Unretained(network_tasks_), should));
333 }
334
SpawnNetworkBoundURLRequestContextForTesting(net::handles::NetworkHandle network)335 void CronetContext::NetworkTasks::SpawnNetworkBoundURLRequestContextForTesting(
336 net::handles::NetworkHandle network) {
337 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
338 DCHECK(!contexts_.contains(network));
339 contexts_[network] = BuildNetworkBoundURLRequestContext(network);
340 }
341
URLRequestContextExistsForTesting(net::handles::NetworkHandle network)342 bool CronetContext::NetworkTasks::URLRequestContextExistsForTesting(
343 net::handles::NetworkHandle network) {
344 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
345 return contexts_.contains(network);
346 }
347
348 std::unique_ptr<net::URLRequestContext>
BuildDefaultURLRequestContext(std::unique_ptr<net::ProxyConfigService> proxy_config_service)349 CronetContext::NetworkTasks::BuildDefaultURLRequestContext(
350 std::unique_ptr<net::ProxyConfigService> proxy_config_service) {
351 DCHECK(!network_quality_estimator_);
352 DCHECK(!cronet_prefs_manager_);
353 net::URLRequestContextBuilder context_builder;
354 context_config_->ConfigureURLRequestContextBuilder(&context_builder);
355 SetSharedURLRequestContextBuilderConfig(&context_builder);
356
357 context_builder.set_proxy_resolution_service(
358 cronet::CreateProxyResolutionService(std::move(proxy_config_service),
359 g_net_log.Get().net_log()));
360
361 if (context_config_->enable_network_quality_estimator) {
362 std::unique_ptr<net::NetworkQualityEstimatorParams> nqe_params =
363 std::make_unique<net::NetworkQualityEstimatorParams>(
364 std::map<std::string, std::string>());
365 if (context_config_->nqe_forced_effective_connection_type) {
366 nqe_params->SetForcedEffectiveConnectionType(
367 context_config_->nqe_forced_effective_connection_type.value());
368 }
369
370 network_quality_estimator_ = std::make_unique<net::NetworkQualityEstimator>(
371 std::move(nqe_params), g_net_log.Get().net_log());
372 network_quality_estimator_->AddEffectiveConnectionTypeObserver(this);
373 network_quality_estimator_->AddRTTAndThroughputEstimatesObserver(this);
374
375 context_builder.set_network_quality_estimator(
376 network_quality_estimator_.get());
377 }
378
379 // Set up pref file if storage path is specified.
380 if (!context_config_->storage_path.empty()) {
381 #if BUILDFLAG(IS_WIN)
382 base::FilePath storage_path(
383 base::FilePath::FromUTF8Unsafe(context_config_->storage_path));
384 #else
385 base::FilePath storage_path(context_config_->storage_path);
386 #endif
387 // Currently only the default context uses a PrefManager, this means that
388 // contexts for specific networks do not maintain state between restarts.
389 // Part of that is by design, part of that is due to CronetPrefsManager's
390 // current interface: it assumes that a single URLRequestContext exists
391 // and, under that assumption, mixes NQE, HostCache, and
392 // HttpServerProperties management persistence. The former two should
393 // apply only to the default context, while the latter could also be
394 // applied to network-bound contexts.
395 // TODO(stefanoduo): Decouple CronetPrefManager management of NQE,
396 // HostCache and HttpServerProperties and apply HttpServerProperties to
397 // network bound contexts.
398 cronet_prefs_manager_ = std::make_unique<CronetPrefsManager>(
399 context_config_->storage_path, network_task_runner_, file_task_runner_,
400 context_config_->enable_network_quality_estimator,
401 context_config_->enable_host_cache_persistence,
402 g_net_log.Get().net_log(), &context_builder);
403 }
404
405 auto context = context_builder.Build();
406
407 // Set up host cache persistence if it's enabled. Happens after building the
408 // URLRequestContext to get access to the HostCache.
409 if (context_config_->enable_host_cache_persistence && cronet_prefs_manager_) {
410 net::HostCache* host_cache = context->host_resolver()->GetHostCache();
411 cronet_prefs_manager_->SetupHostCachePersistence(
412 host_cache, context_config_->host_cache_persistence_delay_ms,
413 g_net_log.Get().net_log());
414 }
415
416 SetSharedURLRequestContextConfig(context.get());
417 return context;
418 }
419
420 std::unique_ptr<net::URLRequestContext>
BuildNetworkBoundURLRequestContext(net::handles::NetworkHandle network)421 CronetContext::NetworkTasks::BuildNetworkBoundURLRequestContext(
422 net::handles::NetworkHandle network) {
423 net::URLRequestContextBuilder context_builder;
424 context_config_->ConfigureURLRequestContextBuilder(&context_builder, network);
425 SetSharedURLRequestContextBuilderConfig(&context_builder);
426
427 // On Android, Cronet doesn't handle PAC URL processing, instead it defers
428 // that to the OS (which sets up a local proxy configured correctly w.r.t.
429 // Android settings). See crbug.com/432539.
430 // TODO(stefanoduo): Confirm if we can keep using this configuration for
431 // requests bound to a network (otherwise we might have to query that
432 // network's LinkProperties#getHttpProxy).
433 // Until then don't support proxies when a network is specified.
434 context_builder.set_proxy_config_service(
435 std::make_unique<net::ProxyConfigServiceFixed>(
436 net::ProxyConfigWithAnnotation::CreateDirect()));
437
438 auto context = context_builder.Build();
439 SetSharedURLRequestContextConfig(context.get());
440 return context;
441 }
442
SetSharedURLRequestContextBuilderConfig(net::URLRequestContextBuilder * context_builder)443 void CronetContext::NetworkTasks::SetSharedURLRequestContextBuilderConfig(
444 net::URLRequestContextBuilder* context_builder) {
445 context_builder->set_network_delegate(
446 std::make_unique<BasicNetworkDelegate>());
447 context_builder->set_net_log(g_net_log.Get().net_log());
448
449 // Explicitly disable the persister for Cronet to avoid persistence of dynamic
450 // HPKP. This is a safety measure ensuring that nobody enables the persistence
451 // of HPKP by specifying transport_security_persister_file_path in the future.
452 context_builder->set_transport_security_persister_file_path(base::FilePath());
453
454 // Disable net::CookieStore.
455 context_builder->SetCookieStore(nullptr);
456
457 context_builder->set_check_cleartext_permitted(true);
458 context_builder->set_enable_brotli(context_config_->enable_brotli);
459 }
460
SetSharedURLRequestContextConfig(net::URLRequestContext * context)461 void CronetContext::NetworkTasks::SetSharedURLRequestContextConfig(
462 net::URLRequestContext* context) {
463 if (context_config_->enable_quic) {
464 for (const auto& quic_hint : context_config_->quic_hints)
465 SetQuicHint(context, quic_hint.get());
466 }
467
468 // Iterate through PKP configuration for every host.
469 for (const auto& pkp : context_config_->pkp_list) {
470 // Add the host pinning.
471 context->transport_security_state()->AddHPKP(
472 pkp->host, pkp->expiration_date, pkp->include_subdomains,
473 pkp->pin_hashes);
474 }
475
476 context->transport_security_state()
477 ->SetEnablePublicKeyPinningBypassForLocalTrustAnchors(
478 context_config_->bypass_public_key_pinning_for_local_trust_anchors);
479
480 #if BUILDFLAG(ENABLE_REPORTING)
481 if (context->reporting_service()) {
482 for (const auto& preloaded_header :
483 context_config_->preloaded_report_to_headers) {
484 context->reporting_service()->ProcessReportToHeader(
485 preloaded_header.origin, net::NetworkAnonymizationKey(),
486 preloaded_header.value);
487 }
488 }
489
490 if (context->network_error_logging_service()) {
491 for (const auto& preloaded_header :
492 context_config_->preloaded_nel_headers) {
493 context->network_error_logging_service()->OnHeader(
494 net::NetworkAnonymizationKey(), preloaded_header.origin,
495 net::IPAddress(), preloaded_header.value);
496 }
497 }
498 #endif // BUILDFLAG(ENABLE_REPORTING)
499 }
500
Initialize(scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,scoped_refptr<base::SequencedTaskRunner> file_task_runner,std::unique_ptr<net::ProxyConfigService> proxy_config_service)501 void CronetContext::NetworkTasks::Initialize(
502 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
503 scoped_refptr<base::SequencedTaskRunner> file_task_runner,
504 std::unique_ptr<net::ProxyConfigService> proxy_config_service) {
505 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
506 DCHECK(!is_default_context_initialized_);
507
508 network_task_runner_ = network_task_runner;
509 file_task_runner_ = file_task_runner;
510 if (context_config_->network_thread_priority)
511 SetNetworkThreadPriorityOnNetworkThread(
512 context_config_->network_thread_priority.value());
513 base::DisallowBlocking();
514 effective_experimental_options_ =
515 context_config_->effective_experimental_options.Clone();
516
517 const net::handles::NetworkHandle default_network =
518 net::handles::kInvalidNetworkHandle;
519 contexts_[default_network] =
520 BuildDefaultURLRequestContext(std::move(proxy_config_service));
521 default_context_ = contexts_[default_network].get();
522
523 if (net::NetworkChangeNotifier::AreNetworkHandlesSupported())
524 net::NetworkChangeNotifier::AddNetworkObserver(this);
525
526 callback_->OnInitNetworkThread();
527 is_default_context_initialized_ = true;
528
529 if (context_config_->enable_network_quality_estimator &&
530 cronet_prefs_manager_) {
531 cronet_prefs_manager_->SetupNqePersistence(
532 network_quality_estimator_.get());
533 }
534
535 while (!tasks_waiting_for_context_.empty()) {
536 std::move(tasks_waiting_for_context_.front()).Run();
537 tasks_waiting_for_context_.pop();
538 }
539 }
540
GetURLRequestContext(net::handles::NetworkHandle network)541 net::URLRequestContext* CronetContext::NetworkTasks::GetURLRequestContext(
542 net::handles::NetworkHandle network) {
543 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
544 DCHECK(is_default_context_initialized_);
545
546 if (network == net::handles::kInvalidNetworkHandle)
547 return default_context_;
548
549 // Non-default contexts are created on the fly.
550 if (contexts_.find(network) == contexts_.end())
551 contexts_[network] = BuildNetworkBoundURLRequestContext(network);
552 return contexts_[network].get();
553 }
554
MaybeDestroyURLRequestContext(net::handles::NetworkHandle network)555 void CronetContext::NetworkTasks::MaybeDestroyURLRequestContext(
556 net::handles::NetworkHandle network) {
557 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
558
559 // Default network context is never deleted.
560 if (network == net::handles::kInvalidNetworkHandle)
561 return;
562 if (!contexts_.contains(network))
563 return;
564
565 auto& context = contexts_[network];
566 // For a URLRequestContext to be destroyed, two conditions must be satisfied:
567 // 1. The network associated to that context must be no longer connected
568 // 2. There must be no URLRequests associated to that context
569 if (context->url_requests()->size() == 0 &&
570 IsNetworkNoLongerConnected(network)) {
571 contexts_.erase(network);
572 }
573 }
574
575 // Request context getter for CronetContext.
576 class CronetContext::ContextGetter : public net::URLRequestContextGetter {
577 public:
ContextGetter(CronetContext * cronet_context)578 explicit ContextGetter(CronetContext* cronet_context)
579 : cronet_context_(cronet_context) {
580 DCHECK(cronet_context_);
581 }
582
583 ContextGetter(const ContextGetter&) = delete;
584 ContextGetter& operator=(const ContextGetter&) = delete;
585
GetURLRequestContext()586 net::URLRequestContext* GetURLRequestContext() override {
587 return cronet_context_->GetURLRequestContext();
588 }
589
GetNetworkTaskRunner() const590 scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
591 const override {
592 return cronet_context_->GetNetworkTaskRunner();
593 }
594
595 private:
596 // Must be called on the network thread.
~ContextGetter()597 ~ContextGetter() override { DCHECK(cronet_context_->IsOnNetworkThread()); }
598
599 // CronetContext associated with this ContextGetter.
600 const raw_ptr<CronetContext> cronet_context_;
601 };
602
CreateURLRequestContextGetter()603 net::URLRequestContextGetter* CronetContext::CreateURLRequestContextGetter() {
604 DCHECK(IsOnNetworkThread());
605 return new ContextGetter(this);
606 }
607
GetURLRequestContext(net::handles::NetworkHandle network)608 net::URLRequestContext* CronetContext::GetURLRequestContext(
609 net::handles::NetworkHandle network) {
610 DCHECK(IsOnNetworkThread());
611 return network_tasks_->GetURLRequestContext(network);
612 }
613
PostTaskToNetworkThread(const base::Location & posted_from,base::OnceClosure callback)614 void CronetContext::PostTaskToNetworkThread(const base::Location& posted_from,
615 base::OnceClosure callback) {
616 GetNetworkTaskRunner()->PostTask(
617 posted_from,
618 base::BindOnce(&CronetContext::NetworkTasks::RunTaskAfterContextInit,
619 base::Unretained(network_tasks_), std::move(callback)));
620 }
621
RunTaskAfterContextInit(base::OnceClosure task_to_run_after_context_init)622 void CronetContext::NetworkTasks::RunTaskAfterContextInit(
623 base::OnceClosure task_to_run_after_context_init) {
624 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
625 if (is_default_context_initialized_) {
626 DCHECK(tasks_waiting_for_context_.empty());
627 std::move(task_to_run_after_context_init).Run();
628 return;
629 }
630 tasks_waiting_for_context_.push(std::move(task_to_run_after_context_init));
631 }
632
IsOnNetworkThread() const633 bool CronetContext::IsOnNetworkThread() const {
634 return GetNetworkTaskRunner()->BelongsToCurrentThread();
635 }
636
637 scoped_refptr<base::SingleThreadTaskRunner>
GetNetworkTaskRunner() const638 CronetContext::GetNetworkTaskRunner() const {
639 return network_task_runner_;
640 }
641
StartNetLogToFile(const std::string & file_name,bool log_all)642 bool CronetContext::StartNetLogToFile(const std::string& file_name,
643 bool log_all) {
644 #if BUILDFLAG(IS_WIN)
645 base::FilePath file_path(base::FilePath::FromUTF8Unsafe(file_name));
646 #else
647 base::FilePath file_path(file_name);
648 #endif
649 base::ScopedFILE file(base::OpenFile(file_path, "w"));
650 if (!file) {
651 LOG(ERROR) << "Failed to open NetLog file for writing.";
652 return false;
653 }
654 PostTaskToNetworkThread(
655 FROM_HERE,
656 base::BindOnce(&CronetContext::NetworkTasks::StartNetLog,
657 base::Unretained(network_tasks_), file_path, log_all));
658 return true;
659 }
660
StartNetLogToDisk(const std::string & dir_name,bool log_all,int max_size)661 void CronetContext::StartNetLogToDisk(const std::string& dir_name,
662 bool log_all,
663 int max_size) {
664 PostTaskToNetworkThread(
665 FROM_HERE,
666 base::BindOnce(&CronetContext::NetworkTasks::StartNetLogToBoundedFile,
667 base::Unretained(network_tasks_), dir_name, log_all,
668 max_size));
669 }
670
StopNetLog()671 void CronetContext::StopNetLog() {
672 DCHECK(!GetNetworkTaskRunner()->BelongsToCurrentThread());
673 PostTaskToNetworkThread(
674 FROM_HERE, base::BindOnce(&CronetContext::NetworkTasks::StopNetLog,
675 base::Unretained(network_tasks_)));
676 }
677
FlushWritePropertiesForTesting()678 void CronetContext::FlushWritePropertiesForTesting() {
679 base::WaitableEvent wait_for_callback;
680 network_task_runner_->PostTask(
681 FROM_HERE,
682 base::BindOnce(
683 [](NetworkTasks* network_tasks, base::OnceClosure callback) {
684 network_tasks
685 ->GetURLRequestContext(net::handles::kInvalidNetworkHandle)
686 ->http_server_properties()
687 ->FlushWritePropertiesForTesting( // IN-TEST
688 std::move(callback));
689 },
690 network_tasks_,
691 base::BindOnce(&base::WaitableEvent::Signal,
692 base::Unretained(&wait_for_callback))));
693 wait_for_callback.Wait();
694 }
695
MaybeDestroyURLRequestContext(net::handles::NetworkHandle network)696 void CronetContext::MaybeDestroyURLRequestContext(
697 net::handles::NetworkHandle network) {
698 DCHECK(IsOnNetworkThread());
699 network_tasks_->MaybeDestroyURLRequestContext(network);
700 }
701
default_load_flags() const702 int CronetContext::default_load_flags() const {
703 return default_load_flags_;
704 }
705
GetFileThread()706 base::Thread* CronetContext::GetFileThread() {
707 DCHECK(OnInitThread());
708 if (!file_thread_) {
709 file_thread_ = std::make_unique<base::Thread>("Network File Thread");
710 file_thread_->Start();
711 }
712 return file_thread_.get();
713 }
714
OnEffectiveConnectionTypeChanged(net::EffectiveConnectionType effective_connection_type)715 void CronetContext::NetworkTasks::OnEffectiveConnectionTypeChanged(
716 net::EffectiveConnectionType effective_connection_type) {
717 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
718 callback_->OnEffectiveConnectionTypeChanged(effective_connection_type);
719 }
720
OnRTTOrThroughputEstimatesComputed(base::TimeDelta http_rtt,base::TimeDelta transport_rtt,int32_t downstream_throughput_kbps)721 void CronetContext::NetworkTasks::OnRTTOrThroughputEstimatesComputed(
722 base::TimeDelta http_rtt,
723 base::TimeDelta transport_rtt,
724 int32_t downstream_throughput_kbps) {
725 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
726
727 int32_t http_rtt_ms = http_rtt.InMilliseconds() <= INT32_MAX
728 ? static_cast<int32_t>(http_rtt.InMilliseconds())
729 : INT32_MAX;
730 int32_t transport_rtt_ms =
731 transport_rtt.InMilliseconds() <= INT32_MAX
732 ? static_cast<int32_t>(transport_rtt.InMilliseconds())
733 : INT32_MAX;
734
735 callback_->OnRTTOrThroughputEstimatesComputed(http_rtt_ms, transport_rtt_ms,
736 downstream_throughput_kbps);
737 }
738
OnRTTObservation(int32_t rtt_ms,const base::TimeTicks & timestamp,net::NetworkQualityObservationSource source)739 void CronetContext::NetworkTasks::OnRTTObservation(
740 int32_t rtt_ms,
741 const base::TimeTicks& timestamp,
742 net::NetworkQualityObservationSource source) {
743 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
744
745 callback_->OnRTTObservation(
746 rtt_ms, (timestamp - base::TimeTicks::UnixEpoch()).InMilliseconds(),
747 source);
748 }
749
OnThroughputObservation(int32_t throughput_kbps,const base::TimeTicks & timestamp,net::NetworkQualityObservationSource source)750 void CronetContext::NetworkTasks::OnThroughputObservation(
751 int32_t throughput_kbps,
752 const base::TimeTicks& timestamp,
753 net::NetworkQualityObservationSource source) {
754 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
755
756 callback_->OnThroughputObservation(
757 throughput_kbps,
758 (timestamp - base::TimeTicks::UnixEpoch()).InMilliseconds(), source);
759 }
760
OnNetworkDisconnected(net::handles::NetworkHandle network)761 void CronetContext::NetworkTasks::OnNetworkDisconnected(
762 net::handles::NetworkHandle network) {
763 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
764
765 if (!contexts_.contains(network))
766 return;
767
768 auto& context = contexts_[network];
769 // After `network` disconnects, we can delete the URLRequestContext
770 // associated with it only if it has no pending URLRequests.
771 // If there are, their destruction procedure will take care of destroying
772 // this context (see MaybeDestroyURLRequestContext for more info).
773 if (context->url_requests()->size() == 0)
774 contexts_.erase(network);
775 }
776
OnNetworkConnected(net::handles::NetworkHandle network)777 void CronetContext::NetworkTasks::OnNetworkConnected(
778 net::handles::NetworkHandle network) {
779 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
780 }
OnNetworkSoonToDisconnect(net::handles::NetworkHandle network)781 void CronetContext::NetworkTasks::OnNetworkSoonToDisconnect(
782 net::handles::NetworkHandle network) {
783 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
784 }
OnNetworkMadeDefault(net::handles::NetworkHandle network)785 void CronetContext::NetworkTasks::OnNetworkMadeDefault(
786 net::handles::NetworkHandle network) {
787 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
788 }
789
StartNetLog(const base::FilePath & file_path,bool include_socket_bytes)790 void CronetContext::NetworkTasks::StartNetLog(const base::FilePath& file_path,
791 bool include_socket_bytes) {
792 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
793
794 // Do nothing if already logging to a file.
795 if (net_log_file_observer_)
796 return;
797
798 net::NetLogCaptureMode capture_mode =
799 include_socket_bytes ? net::NetLogCaptureMode::kEverything
800 : net::NetLogCaptureMode::kDefault;
801 net_log_file_observer_ = net::FileNetLogObserver::CreateUnbounded(
802 file_path, capture_mode, /*constants=*/nullptr);
803 std::set<net::URLRequestContext*> contexts;
804 for (auto& iter : contexts_)
805 contexts.insert(iter.second.get());
806 CreateNetLogEntriesForActiveObjects(contexts, net_log_file_observer_.get());
807 net_log_file_observer_->StartObserving(g_net_log.Get().net_log());
808 }
809
StartNetLogToBoundedFile(const std::string & dir_path,bool include_socket_bytes,int size)810 void CronetContext::NetworkTasks::StartNetLogToBoundedFile(
811 const std::string& dir_path,
812 bool include_socket_bytes,
813 int size) {
814 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
815
816 // Do nothing if already logging to a directory.
817 if (net_log_file_observer_) {
818 return;
819 }
820
821 // TODO(eroman): The cronet API passes a directory here. But it should now
822 // just pass a file path.
823 #if BUILDFLAG(IS_WIN)
824 base::FilePath file_path(base::FilePath::FromUTF8Unsafe(dir_path));
825 #else
826 base::FilePath file_path(dir_path);
827 #endif
828 file_path = file_path.AppendASCII("netlog.json");
829
830 {
831 base::ScopedAllowBlocking allow_blocking;
832 if (!base::PathIsWritable(file_path)) {
833 LOG(ERROR) << "Path is not writable: " << file_path.value();
834 }
835 }
836
837 net::NetLogCaptureMode capture_mode =
838 include_socket_bytes ? net::NetLogCaptureMode::kEverything
839 : net::NetLogCaptureMode::kDefault;
840 net_log_file_observer_ = net::FileNetLogObserver::CreateBounded(
841 file_path, size, capture_mode, /*constants=*/nullptr);
842
843 std::set<net::URLRequestContext*> contexts;
844 for (auto& iter : contexts_)
845 contexts.insert(iter.second.get());
846 CreateNetLogEntriesForActiveObjects(contexts, net_log_file_observer_.get());
847
848 net_log_file_observer_->StartObserving(g_net_log.Get().net_log());
849 }
850
StopNetLog()851 void CronetContext::NetworkTasks::StopNetLog() {
852 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
853
854 if (!net_log_file_observer_)
855 return;
856 net_log_file_observer_->StopObserving(
857 base::Value::ToUniquePtrValue(GetNetLogInfo()),
858 base::BindOnce(&CronetContext::NetworkTasks::StopNetLogCompleted,
859 base::Unretained(this)));
860 net_log_file_observer_.reset();
861 }
862
StopNetLogCompleted()863 void CronetContext::NetworkTasks::StopNetLogCompleted() {
864 DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
865 callback_->OnStopNetLogCompleted();
866 }
867
GetNetLogInfo() const868 base::Value CronetContext::NetworkTasks::GetNetLogInfo() const {
869 base::Value::Dict net_info;
870 for (auto& iter : contexts_)
871 net_info.Set(base::NumberToString(iter.first),
872 net::GetNetInfo(iter.second.get()));
873 if (!effective_experimental_options_.empty()) {
874 net_info.Set("cronetExperimentalParams",
875 effective_experimental_options_.Clone());
876 }
877 return base::Value(std::move(net_info));
878 }
879
880 } // namespace cronet
881