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