xref: /aosp_15_r20/external/cronet/components/cronet/cronet_context.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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