• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/task/sequenced_task_runner.h"
29 #include "base/task/single_thread_task_runner.h"
30 #include "base/threading/thread_restrictions.h"
31 #include "base/time/time.h"
32 #include "base/values.h"
33 #include "build/build_config.h"
34 #include "components/cronet/cronet_global_state.h"
35 #include "components/cronet/cronet_prefs_manager.h"
36 #include "components/cronet/host_cache_persistence_manager.h"
37 #include "components/cronet/url_request_context_config.h"
38 #include "net/base/ip_address.h"
39 #include "net/base/load_flags.h"
40 #include "net/base/logging_network_change_observer.h"
41 #include "net/base/net_errors.h"
42 #include "net/base/network_delegate_impl.h"
43 #include "net/base/network_isolation_key.h"
44 #include "net/base/url_util.h"
45 #include "net/cert/caching_cert_verifier.h"
46 #include "net/cert/cert_verifier.h"
47 #include "net/cert/x509_certificate.h"
48 #include "net/cookies/cookie_inclusion_status.h"
49 #include "net/cookies/cookie_monster.h"
50 #include "net/cookies/cookie_setting_override.h"
51 #include "net/first_party_sets/first_party_set_metadata.h"
52 #include "net/http/http_auth_handler_factory.h"
53 #include "net/http/transport_security_state.h"
54 #include "net/log/file_net_log_observer.h"
55 #include "net/log/net_log_util.h"
56 #include "net/net_buildflags.h"
57 #include "net/nqe/network_quality_estimator_params.h"
58 #include "net/proxy_resolution/proxy_config_service_fixed.h"
59 #include "net/proxy_resolution/proxy_resolution_service.h"
60 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
61 #include "net/url_request/url_request_context.h"
62 #include "net/url_request/url_request_context_builder.h"
63 #include "net/url_request/url_request_context_getter.h"
64 #include "net/url_request/url_request_interceptor.h"
65 
66 #if BUILDFLAG(ENABLE_REPORTING)
67 #include "net/network_error_logging/network_error_logging_service.h"
68 #include "net/reporting/reporting_service.h"
69 #endif  // BUILDFLAG(ENABLE_REPORTING)
70 
71 namespace {
72 
73 // This class wraps a NetLog that also contains network change events.
74 class NetLogWithNetworkChangeEvents {
75  public:
NetLogWithNetworkChangeEvents()76   NetLogWithNetworkChangeEvents() : net_log_(net::NetLog::Get()) {}
77 
78   NetLogWithNetworkChangeEvents(const NetLogWithNetworkChangeEvents&) = delete;
79   NetLogWithNetworkChangeEvents& operator=(
80       const NetLogWithNetworkChangeEvents&) = delete;
81 
net_log()82   net::NetLog* net_log() { return net_log_; }
83   // This function registers with the NetworkChangeNotifier and so must be
84   // called *after* the NetworkChangeNotifier is created. Should only be
85   // called on the init thread as it is not thread-safe and the init thread is
86   // the thread the NetworkChangeNotifier is created on. This function is
87   // not thread-safe because accesses to |net_change_logger_| are not atomic.
88   // There might be multiple CronetEngines each with a network thread so
89   // so the init thread is used. |g_net_log_| also outlives the network threads
90   // so it would be unsafe to receive callbacks on the network threads without
91   // a complicated thread-safe reference-counting system to control callback
92   // registration.
EnsureInitializedOnInitThread()93   void EnsureInitializedOnInitThread() {
94     DCHECK(cronet::OnInitThread());
95     if (net_change_logger_)
96       return;
97     net_change_logger_ =
98         std::make_unique<net::LoggingNetworkChangeObserver>(net_log_);
99   }
100 
101  private:
102   raw_ptr<net::NetLog> net_log_;
103   // LoggingNetworkChangeObserver logs network change events to a NetLog.
104   // This class bundles one LoggingNetworkChangeObserver with one NetLog,
105   // so network change event are logged just once in the NetLog.
106   std::unique_ptr<net::LoggingNetworkChangeObserver> net_change_logger_;
107 };
108 
109 // Use a global NetLog instance. See crbug.com/486120.
110 static base::LazyInstance<NetLogWithNetworkChangeEvents>::Leaky g_net_log =
111     LAZY_INSTANCE_INITIALIZER;
112 
113 class BasicNetworkDelegate : public net::NetworkDelegateImpl {
114  public:
BasicNetworkDelegate()115   BasicNetworkDelegate() {}
116 
117   BasicNetworkDelegate(const BasicNetworkDelegate&) = delete;
118   BasicNetworkDelegate& operator=(const BasicNetworkDelegate&) = delete;
119 
~BasicNetworkDelegate()120   ~BasicNetworkDelegate() override {}
121 
122  private:
123   // net::NetworkDelegate implementation.
OnAnnotateAndMoveUserBlockedCookies(const net::URLRequest & request,const net::FirstPartySetMetadata & first_party_set_metadata,net::CookieAccessResultList & maybe_included_cookies,net::CookieAccessResultList & excluded_cookies)124   bool OnAnnotateAndMoveUserBlockedCookies(
125       const net::URLRequest& request,
126       const net::FirstPartySetMetadata& first_party_set_metadata,
127       net::CookieAccessResultList& maybe_included_cookies,
128       net::CookieAccessResultList& excluded_cookies) override {
129     // Disallow sending cookies by default.
130     ExcludeAllCookies(net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES,
131                       maybe_included_cookies, excluded_cookies);
132     return false;
133   }
134 
OnCanSetCookie(const net::URLRequest & request,const net::CanonicalCookie & cookie,net::CookieOptions * options,const net::FirstPartySetMetadata & first_party_set_metadata,net::CookieInclusionStatus * inclusion_status)135   bool OnCanSetCookie(
136       const net::URLRequest& request,
137       const net::CanonicalCookie& cookie,
138       net::CookieOptions* options,
139       const net::FirstPartySetMetadata& first_party_set_metadata,
140       net::CookieInclusionStatus* inclusion_status) override {
141     // Disallow saving cookies by default.
142     return false;
143   }
144 };
145 
146 // Helper function to make a net::URLRequestContext aware of a QUIC hint.
SetQuicHint(net::URLRequestContext * context,const cronet::URLRequestContextConfig::QuicHint * quic_hint)147 void SetQuicHint(net::URLRequestContext* context,
148                  const cronet::URLRequestContextConfig::QuicHint* quic_hint) {
149   if (quic_hint->host.empty()) {
150     LOG(ERROR) << "Empty QUIC hint host: " << quic_hint->host;
151     return;
152   }
153 
154   url::CanonHostInfo host_info;
155   std::string canon_host(net::CanonicalizeHost(quic_hint->host, &host_info));
156   if (!host_info.IsIPAddress() &&
157       !net::IsCanonicalizedHostCompliant(canon_host)) {
158     LOG(ERROR) << "Invalid QUIC hint host: " << quic_hint->host;
159     return;
160   }
161 
162   if (quic_hint->port <= std::numeric_limits<uint16_t>::min() ||
163       quic_hint->port > std::numeric_limits<uint16_t>::max()) {
164     LOG(ERROR) << "Invalid QUIC hint port: " << quic_hint->port;
165     return;
166   }
167 
168   if (quic_hint->alternate_port <= std::numeric_limits<uint16_t>::min() ||
169       quic_hint->alternate_port > std::numeric_limits<uint16_t>::max()) {
170     LOG(ERROR) << "Invalid QUIC hint alternate port: "
171                << quic_hint->alternate_port;
172     return;
173   }
174 
175   url::SchemeHostPort quic_server("https", canon_host, quic_hint->port);
176   net::AlternativeService alternative_service(
177       net::kProtoQUIC, "", static_cast<uint16_t>(quic_hint->alternate_port));
178   context->http_server_properties()->SetQuicAlternativeService(
179       quic_server, net::NetworkAnonymizationKey(), alternative_service,
180       base::Time::Max(), quic::ParsedQuicVersionVector());
181 }
182 
183 // net::NetworkChangeNotifier doesn't provide an API to query if a specific
184 // network has become disconnected. For these network though, it will return
185 // CONNECTION_UNKNOWN as their connection type. This should be a good enough
186 // approximation for the time being.
IsNetworkNoLongerConnected(net::handles::NetworkHandle network)187 bool IsNetworkNoLongerConnected(net::handles::NetworkHandle network) {
188   return net::NetworkChangeNotifier::GetNetworkConnectionType(network) ==
189          net::NetworkChangeNotifier::CONNECTION_UNKNOWN;
190 }
191 
192 }  // namespace
193 
194 namespace cronet {
195 
CronetContext(std::unique_ptr<URLRequestContextConfig> context_config,std::unique_ptr<Callback> callback,scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)196 CronetContext::CronetContext(
197     std::unique_ptr<URLRequestContextConfig> context_config,
198     std::unique_ptr<Callback> callback,
199     scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
200     : bidi_stream_detect_broken_connection_(
201           context_config->bidi_stream_detect_broken_connection),
202       heartbeat_interval_(context_config->heartbeat_interval),
203       enable_telemetry_(context_config->enable_telemetry),
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, GURL::EmptyGURL());
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 
MaybeDestroyURLRequestContext(net::handles::NetworkHandle network)678 void CronetContext::MaybeDestroyURLRequestContext(
679     net::handles::NetworkHandle network) {
680   DCHECK(IsOnNetworkThread());
681   network_tasks_->MaybeDestroyURLRequestContext(network);
682 }
683 
default_load_flags() const684 int CronetContext::default_load_flags() const {
685   return default_load_flags_;
686 }
687 
GetFileThread()688 base::Thread* CronetContext::GetFileThread() {
689   DCHECK(OnInitThread());
690   if (!file_thread_) {
691     file_thread_ = std::make_unique<base::Thread>("Network File Thread");
692     file_thread_->Start();
693   }
694   return file_thread_.get();
695 }
696 
OnEffectiveConnectionTypeChanged(net::EffectiveConnectionType effective_connection_type)697 void CronetContext::NetworkTasks::OnEffectiveConnectionTypeChanged(
698     net::EffectiveConnectionType effective_connection_type) {
699   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
700   callback_->OnEffectiveConnectionTypeChanged(effective_connection_type);
701 }
702 
OnRTTOrThroughputEstimatesComputed(base::TimeDelta http_rtt,base::TimeDelta transport_rtt,int32_t downstream_throughput_kbps)703 void CronetContext::NetworkTasks::OnRTTOrThroughputEstimatesComputed(
704     base::TimeDelta http_rtt,
705     base::TimeDelta transport_rtt,
706     int32_t downstream_throughput_kbps) {
707   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
708 
709   int32_t http_rtt_ms = http_rtt.InMilliseconds() <= INT32_MAX
710                             ? static_cast<int32_t>(http_rtt.InMilliseconds())
711                             : INT32_MAX;
712   int32_t transport_rtt_ms =
713       transport_rtt.InMilliseconds() <= INT32_MAX
714           ? static_cast<int32_t>(transport_rtt.InMilliseconds())
715           : INT32_MAX;
716 
717   callback_->OnRTTOrThroughputEstimatesComputed(http_rtt_ms, transport_rtt_ms,
718                                                 downstream_throughput_kbps);
719 }
720 
OnRTTObservation(int32_t rtt_ms,const base::TimeTicks & timestamp,net::NetworkQualityObservationSource source)721 void CronetContext::NetworkTasks::OnRTTObservation(
722     int32_t rtt_ms,
723     const base::TimeTicks& timestamp,
724     net::NetworkQualityObservationSource source) {
725   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
726 
727   callback_->OnRTTObservation(
728       rtt_ms, (timestamp - base::TimeTicks::UnixEpoch()).InMilliseconds(),
729       source);
730 }
731 
OnThroughputObservation(int32_t throughput_kbps,const base::TimeTicks & timestamp,net::NetworkQualityObservationSource source)732 void CronetContext::NetworkTasks::OnThroughputObservation(
733     int32_t throughput_kbps,
734     const base::TimeTicks& timestamp,
735     net::NetworkQualityObservationSource source) {
736   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
737 
738   callback_->OnThroughputObservation(
739       throughput_kbps,
740       (timestamp - base::TimeTicks::UnixEpoch()).InMilliseconds(), source);
741 }
742 
OnNetworkDisconnected(net::handles::NetworkHandle network)743 void CronetContext::NetworkTasks::OnNetworkDisconnected(
744     net::handles::NetworkHandle network) {
745   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
746 
747   if (!contexts_.contains(network))
748     return;
749 
750   auto& context = contexts_[network];
751   // After `network` disconnects, we can delete the URLRequestContext
752   // associated with it only if it has no pending URLRequests.
753   // If there are, their destruction procedure will take care of destroying
754   // this context (see MaybeDestroyURLRequestContext for more info).
755   if (context->url_requests()->size() == 0)
756     contexts_.erase(network);
757 }
758 
OnNetworkConnected(net::handles::NetworkHandle network)759 void CronetContext::NetworkTasks::OnNetworkConnected(
760     net::handles::NetworkHandle network) {
761   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
762 }
OnNetworkSoonToDisconnect(net::handles::NetworkHandle network)763 void CronetContext::NetworkTasks::OnNetworkSoonToDisconnect(
764     net::handles::NetworkHandle network) {
765   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
766 }
OnNetworkMadeDefault(net::handles::NetworkHandle network)767 void CronetContext::NetworkTasks::OnNetworkMadeDefault(
768     net::handles::NetworkHandle network) {
769   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
770 }
771 
StartNetLog(const base::FilePath & file_path,bool include_socket_bytes)772 void CronetContext::NetworkTasks::StartNetLog(const base::FilePath& file_path,
773                                               bool include_socket_bytes) {
774   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
775 
776   // Do nothing if already logging to a file.
777   if (net_log_file_observer_)
778     return;
779 
780   net::NetLogCaptureMode capture_mode =
781       include_socket_bytes ? net::NetLogCaptureMode::kEverything
782                            : net::NetLogCaptureMode::kDefault;
783   net_log_file_observer_ = net::FileNetLogObserver::CreateUnbounded(
784       file_path, capture_mode, /*constants=*/nullptr);
785   std::set<net::URLRequestContext*> contexts;
786   for (auto& iter : contexts_)
787     contexts.insert(iter.second.get());
788   CreateNetLogEntriesForActiveObjects(contexts, net_log_file_observer_.get());
789   net_log_file_observer_->StartObserving(g_net_log.Get().net_log());
790 }
791 
StartNetLogToBoundedFile(const std::string & dir_path,bool include_socket_bytes,int size)792 void CronetContext::NetworkTasks::StartNetLogToBoundedFile(
793     const std::string& dir_path,
794     bool include_socket_bytes,
795     int size) {
796   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
797 
798   // Do nothing if already logging to a directory.
799   if (net_log_file_observer_) {
800     return;
801   }
802 
803   // TODO(eroman): The cronet API passes a directory here. But it should now
804   // just pass a file path.
805 #if BUILDFLAG(IS_WIN)
806   base::FilePath file_path(base::FilePath::FromUTF8Unsafe(dir_path));
807 #else
808   base::FilePath file_path(dir_path);
809 #endif
810   file_path = file_path.AppendASCII("netlog.json");
811 
812   {
813     base::ScopedAllowBlocking allow_blocking;
814     if (!base::PathIsWritable(file_path)) {
815       LOG(ERROR) << "Path is not writable: " << file_path.value();
816     }
817   }
818 
819   net::NetLogCaptureMode capture_mode =
820       include_socket_bytes ? net::NetLogCaptureMode::kEverything
821                            : net::NetLogCaptureMode::kDefault;
822   net_log_file_observer_ = net::FileNetLogObserver::CreateBounded(
823       file_path, size, capture_mode, /*constants=*/nullptr);
824 
825   std::set<net::URLRequestContext*> contexts;
826   for (auto& iter : contexts_)
827     contexts.insert(iter.second.get());
828   CreateNetLogEntriesForActiveObjects(contexts, net_log_file_observer_.get());
829 
830   net_log_file_observer_->StartObserving(g_net_log.Get().net_log());
831 }
832 
StopNetLog()833 void CronetContext::NetworkTasks::StopNetLog() {
834   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
835 
836   if (!net_log_file_observer_)
837     return;
838   net_log_file_observer_->StopObserving(
839       base::Value::ToUniquePtrValue(GetNetLogInfo()),
840       base::BindOnce(&CronetContext::NetworkTasks::StopNetLogCompleted,
841                      base::Unretained(this)));
842   net_log_file_observer_.reset();
843 }
844 
StopNetLogCompleted()845 void CronetContext::NetworkTasks::StopNetLogCompleted() {
846   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
847   callback_->OnStopNetLogCompleted();
848 }
849 
GetNetLogInfo() const850 base::Value CronetContext::NetworkTasks::GetNetLogInfo() const {
851   base::Value::Dict net_info;
852   for (auto& iter : contexts_)
853     net_info.Set(base::NumberToString(iter.first),
854                  net::GetNetInfo(iter.second.get()));
855   if (!effective_experimental_options_.empty()) {
856     net_info.Set("cronetExperimentalParams",
857                  effective_experimental_options_.Clone());
858   }
859   return base::Value(std::move(net_info));
860 }
861 
862 }  // namespace cronet
863