xref: /aosp_15_r20/external/cronet/components/cronet/url_request_context_config.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/url_request_context_config.h"
6 
7 #include <memory>
8 #include <type_traits>
9 #include <utility>
10 
11 #include "base/containers/contains.h"
12 #include "base/json/json_reader.h"
13 #include "base/json/json_writer.h"
14 #include "base/logging.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_piece.h"
18 #include "base/strings/string_split.h"
19 #include "base/task/sequenced_task_runner.h"
20 #include "base/values.h"
21 #include "build/build_config.h"
22 #include "components/cronet/stale_host_resolver.h"
23 #include "net/base/address_family.h"
24 #include "net/cert/caching_cert_verifier.h"
25 #include "net/cert/cert_verifier.h"
26 #include "net/cert/cert_verify_proc.h"
27 #include "net/cert/multi_threaded_cert_verifier.h"
28 #include "net/dns/context_host_resolver.h"
29 #include "net/dns/host_resolver.h"
30 #include "net/dns/mapped_host_resolver.h"
31 #include "net/http/http_network_session.h"
32 #include "net/http/http_server_properties.h"
33 #include "net/log/net_log.h"
34 #include "net/nqe/network_quality_estimator_params.h"
35 #include "net/quic/set_quic_flag.h"
36 #include "net/socket/ssl_client_socket.h"
37 #include "net/ssl/ssl_key_logger_impl.h"
38 #include "net/third_party/quiche/src/quiche/quic/core/quic_packets.h"
39 #include "net/third_party/quiche/src/quiche/quic/core/quic_tag.h"
40 #include "net/url_request/url_request_context_builder.h"
41 #include "url/origin.h"
42 
43 #if BUILDFLAG(ENABLE_REPORTING)
44 #include "net/reporting/reporting_policy.h"
45 #endif  // BUILDFLAG(ENABLE_REPORTING)
46 
47 namespace cronet {
48 
49 namespace {
50 
51 // Name of disk cache directory.
52 const base::FilePath::CharType kDiskCacheDirectoryName[] =
53     FILE_PATH_LITERAL("disk_cache");
54 const char kQuicFieldTrialName[] = "QUIC";
55 const char kQuicConnectionOptions[] = "connection_options";
56 const char kQuicClientConnectionOptions[] = "client_connection_options";
57 const char kQuicStoreServerConfigsInProperties[] =
58     "store_server_configs_in_properties";
59 const char kQuicMaxServerConfigsStoredInProperties[] =
60     "max_server_configs_stored_in_properties";
61 const char kQuicIdleConnectionTimeoutSeconds[] =
62     "idle_connection_timeout_seconds";
63 const char kQuicMaxTimeBeforeCryptoHandshakeSeconds[] =
64     "max_time_before_crypto_handshake_seconds";
65 const char kQuicMaxIdleTimeBeforeCryptoHandshakeSeconds[] =
66     "max_idle_time_before_crypto_handshake_seconds";
67 const char kQuicCloseSessionsOnIpChange[] = "close_sessions_on_ip_change";
68 const char kQuicGoAwaySessionsOnIpChange[] = "goaway_sessions_on_ip_change";
69 const char kQuicAllowServerMigration[] = "allow_server_migration";
70 const char kQuicMigrateSessionsOnNetworkChangeV2[] =
71     "migrate_sessions_on_network_change_v2";
72 const char kQuicMigrateIdleSessions[] = "migrate_idle_sessions";
73 const char kQuicRetransmittableOnWireTimeoutMilliseconds[] =
74     "retransmittable_on_wire_timeout_milliseconds";
75 const char kQuicIdleSessionMigrationPeriodSeconds[] =
76     "idle_session_migration_period_seconds";
77 const char kQuicMaxTimeOnNonDefaultNetworkSeconds[] =
78     "max_time_on_non_default_network_seconds";
79 const char kQuicMaxMigrationsToNonDefaultNetworkOnWriteError[] =
80     "max_migrations_to_non_default_network_on_write_error";
81 const char kQuicMaxMigrationsToNonDefaultNetworkOnPathDegrading[] =
82     "max_migrations_to_non_default_network_on_path_degrading";
83 const char kQuicMigrateSessionsEarlyV2[] = "migrate_sessions_early_v2";
84 const char kQuicRetryOnAlternateNetworkBeforeHandshake[] =
85     "retry_on_alternate_network_before_handshake";
86 const char kQuicHostWhitelist[] = "host_whitelist";
87 const char kQuicVersion[] = "quic_version";
88 const char kQuicFlags[] = "set_quic_flags";
89 const char kRetryWithoutAltSvcOnQuicErrors[] =
90     "retry_without_alt_svc_on_quic_errors";
91 const char kInitialDelayForBrokenAlternativeServiceSeconds[] =
92     "initial_delay_for_broken_alternative_service_seconds";
93 const char kExponentialBackoffOnInitialDelay[] =
94     "exponential_backoff_on_initial_delay";
95 const char kDelayMainJobWithAvailableSpdySession[] =
96     "delay_main_job_with_available_spdy_session";
97 
98 // AsyncDNS experiment dictionary name.
99 const char kAsyncDnsFieldTrialName[] = "AsyncDNS";
100 // Name of boolean to enable AsyncDNS experiment.
101 const char kAsyncDnsEnable[] = "enable";
102 
103 // Stale DNS (StaleHostResolver) experiment dictionary name.
104 const char kStaleDnsFieldTrialName[] = "StaleDNS";
105 // Name of boolean to enable stale DNS experiment.
106 const char kStaleDnsEnable[] = "enable";
107 // Name of integer delay in milliseconds before a stale DNS result will be
108 // used.
109 const char kStaleDnsDelayMs[] = "delay_ms";
110 // Name of integer maximum age (past expiration) in milliseconds of a stale DNS
111 // result that will be used, or 0 for no limit.
112 const char kStaleDnsMaxExpiredTimeMs[] = "max_expired_time_ms";
113 // Name of integer maximum times each stale DNS result can be used, or 0 for no
114 // limit.
115 const char kStaleDnsMaxStaleUses[] = "max_stale_uses";
116 // Name of boolean to allow stale DNS results from other networks to be used on
117 // the current network.
118 const char kStaleDnsAllowOtherNetwork[] = "allow_other_network";
119 // Name of boolean to enable persisting the DNS cache to disk.
120 const char kStaleDnsPersist[] = "persist_to_disk";
121 // Name of integer minimum time in milliseconds between writes to disk for DNS
122 // cache persistence. Default value is one minute. Only relevant if
123 // "persist_to_disk" is true.
124 const char kStaleDnsPersistTimer[] = "persist_delay_ms";
125 // Name of boolean to allow use of stale DNS results when network resolver
126 // returns ERR_NAME_NOT_RESOLVED.
127 const char kStaleDnsUseStaleOnNameNotResolved[] =
128     "use_stale_on_name_not_resolved";
129 
130 // Rules to override DNS resolution. Intended for testing.
131 // See explanation of format in net/dns/mapped_host_resolver.h.
132 const char kHostResolverRulesFieldTrialName[] = "HostResolverRules";
133 const char kHostResolverRules[] = "host_resolver_rules";
134 
135 // NetworkQualityEstimator (NQE) experiment dictionary name.
136 const char kNetworkQualityEstimatorFieldTrialName[] = "NetworkQualityEstimator";
137 
138 // Network Error Logging experiment dictionary name.
139 const char kNetworkErrorLoggingFieldTrialName[] = "NetworkErrorLogging";
140 // Name of boolean to enable Reporting API.
141 const char kNetworkErrorLoggingEnable[] = "enable";
142 // Name of list of preloaded "Report-To" headers.
143 const char kNetworkErrorLoggingPreloadedReportToHeaders[] =
144     "preloaded_report_to_headers";
145 // Name of list of preloaded "NEL" headers.
146 const char kNetworkErrorLoggingPreloadedNELHeaders[] = "preloaded_nel_headers";
147 // Name of key (for above two lists) for header origin.
148 const char kNetworkErrorLoggingOrigin[] = "origin";
149 // Name of key (for above two lists) for header value.
150 const char kNetworkErrorLoggingValue[] = "value";
151 
152 // Disable IPv6 when on WiFi. This is a workaround for a known issue on certain
153 // Android phones, and should not be necessary when not on one of those devices.
154 // See https://crbug.com/696569 for details.
155 const char kDisableIPv6OnWifi[] = "disable_ipv6_on_wifi";
156 
157 const char kSSLKeyLogFile[] = "ssl_key_log_file";
158 
159 const char kAllowPortMigration[] = "allow_port_migration";
160 
161 const char kDisableTlsZeroRtt[] = "disable_tls_zero_rtt";
162 
163 // Whether SPDY sessions should be closed or marked as going away upon relevant
164 // network changes. When not specified, /net behavior varies depending on the
165 // underlying OS.
166 const char kSpdyGoAwayOnIpChange[] = "spdy_go_away_on_ip_change";
167 
168 // Whether the connection status of all bidirectional streams (created through
169 // the Cronet engine) should be monitored.
170 // The value must be an integer (> 0) and will be interpreted as a suggestion
171 // for the period of the heartbeat signal. See
172 // SpdySession#EnableBrokenConnectionDetection for more info.
173 const char kBidiStreamDetectBrokenConnection[] =
174     "bidi_stream_detect_broken_connection";
175 
176 const char kUseDnsHttpsSvcbFieldTrialName[] = "UseDnsHttpsSvcb";
177 const char kUseDnsHttpsSvcbUseAlpn[] = "use_alpn";
178 
179 // Serializes a base::Value into a string that can be used as the value of
180 // JFV-encoded HTTP header [1].  If |value| is a list, we remove the outermost
181 // [] delimiters from the result.
182 //
183 // [1] https://tools.ietf.org/html/draft-reschke-http-jfv
SerializeJFVHeader(const base::Value & value)184 std::string SerializeJFVHeader(const base::Value& value) {
185   std::string result;
186   if (!base::JSONWriter::Write(value, &result))
187     return std::string();
188   if (value.is_list()) {
189     DCHECK(result.size() >= 2);
190     return result.substr(1, result.size() - 2);
191   }
192   return result;
193 }
194 
195 std::vector<URLRequestContextConfig::PreloadedNelAndReportingHeader>
ParseNetworkErrorLoggingHeaders(const base::Value::List & preloaded_headers_config)196 ParseNetworkErrorLoggingHeaders(
197     const base::Value::List& preloaded_headers_config) {
198   std::vector<URLRequestContextConfig::PreloadedNelAndReportingHeader> result;
199   for (const auto& preloaded_header_config : preloaded_headers_config) {
200     if (!preloaded_header_config.is_dict())
201       continue;
202 
203     const std::string* origin_config =
204         preloaded_header_config.GetDict().FindString(
205             kNetworkErrorLoggingOrigin);
206     if (!origin_config)
207       continue;
208     GURL origin_url(*origin_config);
209     if (!origin_url.is_valid())
210       continue;
211     auto origin = url::Origin::Create(origin_url);
212 
213     auto* value =
214         preloaded_header_config.GetDict().Find(kNetworkErrorLoggingValue);
215     if (!value)
216       continue;
217 
218     result.push_back(URLRequestContextConfig::PreloadedNelAndReportingHeader(
219         origin, SerializeJFVHeader(*value)));
220   }
221   return result;
222 }
223 
224 // Applies |f| to the value contained by |maybe|, returns empty optional
225 // otherwise.
226 template <typename T, typename F>
map(std::optional<T> maybe,F && f)227 auto map(std::optional<T> maybe, F&& f) {
228   if (!maybe)
229     return std::optional<std::invoke_result_t<F, T>>();
230   return std::optional<std::invoke_result_t<F, T>>(f(maybe.value()));
231 }
232 
233 }  // namespace
234 
QuicHint(const std::string & host,int port,int alternate_port)235 URLRequestContextConfig::QuicHint::QuicHint(const std::string& host,
236                                             int port,
237                                             int alternate_port)
238     : host(host), port(port), alternate_port(alternate_port) {}
239 
~QuicHint()240 URLRequestContextConfig::QuicHint::~QuicHint() {}
241 
Pkp(const std::string & host,bool include_subdomains,const base::Time & expiration_date)242 URLRequestContextConfig::Pkp::Pkp(const std::string& host,
243                                   bool include_subdomains,
244                                   const base::Time& expiration_date)
245     : host(host),
246       include_subdomains(include_subdomains),
247       expiration_date(expiration_date) {}
248 
~Pkp()249 URLRequestContextConfig::Pkp::~Pkp() {}
250 
251 URLRequestContextConfig::PreloadedNelAndReportingHeader::
PreloadedNelAndReportingHeader(const url::Origin & origin,std::string value)252     PreloadedNelAndReportingHeader(const url::Origin& origin, std::string value)
253     : origin(origin), value(std::move(value)) {}
254 
255 URLRequestContextConfig::PreloadedNelAndReportingHeader::
256     ~PreloadedNelAndReportingHeader() = default;
257 
URLRequestContextConfig(bool enable_quic,bool enable_spdy,bool enable_brotli,HttpCacheType http_cache,int http_cache_max_size,bool load_disable_cache,const std::string & storage_path,const std::string & accept_language,const std::string & user_agent,base::Value::Dict experimental_options,std::unique_ptr<net::CertVerifier> mock_cert_verifier,bool enable_network_quality_estimator,bool bypass_public_key_pinning_for_local_trust_anchors,std::optional<double> network_thread_priority)258 URLRequestContextConfig::URLRequestContextConfig(
259     bool enable_quic,
260     bool enable_spdy,
261     bool enable_brotli,
262     HttpCacheType http_cache,
263     int http_cache_max_size,
264     bool load_disable_cache,
265     const std::string& storage_path,
266     const std::string& accept_language,
267     const std::string& user_agent,
268     base::Value::Dict experimental_options,
269     std::unique_ptr<net::CertVerifier> mock_cert_verifier,
270     bool enable_network_quality_estimator,
271     bool bypass_public_key_pinning_for_local_trust_anchors,
272     std::optional<double> network_thread_priority)
273     : enable_quic(enable_quic),
274       enable_spdy(enable_spdy),
275       enable_brotli(enable_brotli),
276       http_cache(http_cache),
277       http_cache_max_size(http_cache_max_size),
278       load_disable_cache(load_disable_cache),
279       storage_path(storage_path),
280       accept_language(accept_language),
281       user_agent(user_agent),
282       mock_cert_verifier(std::move(mock_cert_verifier)),
283       enable_network_quality_estimator(enable_network_quality_estimator),
284       bypass_public_key_pinning_for_local_trust_anchors(
285           bypass_public_key_pinning_for_local_trust_anchors),
286       effective_experimental_options(experimental_options.Clone()),
287       experimental_options(std::move(experimental_options)),
288       network_thread_priority(network_thread_priority),
289       bidi_stream_detect_broken_connection(false),
290       heartbeat_interval(base::Seconds(0)) {
291   SetContextConfigExperimentalOptions();
292 }
293 
~URLRequestContextConfig()294 URLRequestContextConfig::~URLRequestContextConfig() {}
295 
296 // static
297 std::unique_ptr<URLRequestContextConfig>
CreateURLRequestContextConfig(bool enable_quic,bool enable_spdy,bool enable_brotli,HttpCacheType http_cache,int http_cache_max_size,bool load_disable_cache,const std::string & storage_path,const std::string & accept_language,const std::string & user_agent,const std::string & unparsed_experimental_options,std::unique_ptr<net::CertVerifier> mock_cert_verifier,bool enable_network_quality_estimator,bool bypass_public_key_pinning_for_local_trust_anchors,std::optional<double> network_thread_priority)298 URLRequestContextConfig::CreateURLRequestContextConfig(
299     bool enable_quic,
300     bool enable_spdy,
301     bool enable_brotli,
302     HttpCacheType http_cache,
303     int http_cache_max_size,
304     bool load_disable_cache,
305     const std::string& storage_path,
306     const std::string& accept_language,
307     const std::string& user_agent,
308     const std::string& unparsed_experimental_options,
309     std::unique_ptr<net::CertVerifier> mock_cert_verifier,
310     bool enable_network_quality_estimator,
311     bool bypass_public_key_pinning_for_local_trust_anchors,
312     std::optional<double> network_thread_priority) {
313   std::optional<base::Value::Dict> experimental_options =
314       ParseExperimentalOptions(unparsed_experimental_options);
315   if (!experimental_options) {
316     // For the time being maintain backward compatibility by only failing to
317     // parse when DCHECKs are enabled.
318     if (ExperimentalOptionsParsingIsAllowedToFail())
319       return nullptr;
320     else
321       experimental_options = base::Value::Dict();
322   }
323   return base::WrapUnique(new URLRequestContextConfig(
324       enable_quic, enable_spdy, enable_brotli, http_cache, http_cache_max_size,
325       load_disable_cache, storage_path, accept_language, user_agent,
326       std::move(experimental_options).value(), std::move(mock_cert_verifier),
327       enable_network_quality_estimator,
328       bypass_public_key_pinning_for_local_trust_anchors,
329       network_thread_priority));
330 }
331 
332 // static
333 std::optional<base::Value::Dict>
ParseExperimentalOptions(std::string unparsed_experimental_options)334 URLRequestContextConfig::ParseExperimentalOptions(
335     std::string unparsed_experimental_options) {
336   // From a user perspective no experimental options means an empty string. The
337   // underlying code instead expects and empty dictionary. Normalize this.
338   if (unparsed_experimental_options.empty())
339     unparsed_experimental_options = "{}";
340   DVLOG(1) << "Experimental Options:" << unparsed_experimental_options;
341   auto parsed_json = base::JSONReader::ReadAndReturnValueWithError(
342       unparsed_experimental_options);
343   if (!parsed_json.has_value()) {
344     LOG(ERROR) << "Parsing experimental options failed: '"
345                << unparsed_experimental_options << "', error "
346                << parsed_json.error().message;
347     return std::nullopt;
348   }
349 
350   base::Value::Dict* experimental_options_dict = parsed_json->GetIfDict();
351   if (!experimental_options_dict) {
352     LOG(ERROR) << "Experimental options string is not a dictionary: "
353                << *parsed_json;
354     return std::nullopt;
355   }
356 
357   return std::move(*experimental_options_dict);
358 }
359 
SetContextConfigExperimentalOptions()360 void URLRequestContextConfig::SetContextConfigExperimentalOptions() {
361   const base::Value* heartbeat_interval_value =
362       experimental_options.Find(kBidiStreamDetectBrokenConnection);
363   if (heartbeat_interval_value) {
364     if (!heartbeat_interval_value->is_int()) {
365       LOG(ERROR) << "\"" << kBidiStreamDetectBrokenConnection
366                  << "\" config params \"" << heartbeat_interval_value
367                  << "\" is not an int";
368       experimental_options.Remove(kBidiStreamDetectBrokenConnection);
369       effective_experimental_options.Remove(kBidiStreamDetectBrokenConnection);
370     } else {
371       int heartbeat_interval_secs = heartbeat_interval_value->GetInt();
372       heartbeat_interval = base::Seconds(heartbeat_interval_secs);
373       bidi_stream_detect_broken_connection = heartbeat_interval_secs > 0;
374       experimental_options.Remove(kBidiStreamDetectBrokenConnection);
375     }
376   }
377 }
378 
SetContextBuilderExperimentalOptions(net::URLRequestContextBuilder * context_builder,net::HttpNetworkSessionParams * session_params,net::QuicParams * quic_params,net::handles::NetworkHandle bound_network)379 void URLRequestContextConfig::SetContextBuilderExperimentalOptions(
380     net::URLRequestContextBuilder* context_builder,
381     net::HttpNetworkSessionParams* session_params,
382     net::QuicParams* quic_params,
383     net::handles::NetworkHandle bound_network) {
384   bool async_dns_enable = false;
385   bool stale_dns_enable = false;
386   bool host_resolver_rules_enable = false;
387   bool disable_ipv6_on_wifi = false;
388   bool nel_enable = false;
389   bool is_network_bound = bound_network != net::handles::kInvalidNetworkHandle;
390   std::optional<net::HostResolver::HttpsSvcbOptions> https_svcb_options;
391 
392   StaleHostResolver::StaleOptions stale_dns_options;
393   const std::string* host_resolver_rules_string;
394 
395   for (auto iter = experimental_options.begin();
396        iter != experimental_options.end(); ++iter) {
397     if (iter->first == kQuicFieldTrialName) {
398       if (!iter->second.is_dict()) {
399         LOG(ERROR) << "Quic config params \"" << iter->second
400                    << "\" is not a dictionary value";
401         effective_experimental_options.Remove(iter->first);
402         continue;
403       }
404 
405       const base::Value::Dict& quic_args = iter->second.GetDict();
406       const std::string* quic_version_string =
407           quic_args.FindString(kQuicVersion);
408       if (quic_version_string) {
409         quic::ParsedQuicVersionVector supported_versions =
410             quic::ParseQuicVersionVectorString(*quic_version_string);
411         quic::ParsedQuicVersionVector filtered_versions;
412         quic::ParsedQuicVersionVector obsolete_versions =
413             net::ObsoleteQuicVersions();
414         for (const quic::ParsedQuicVersion& version : supported_versions) {
415           if (!base::Contains(obsolete_versions, version)) {
416             filtered_versions.push_back(version);
417           }
418         }
419         if (!filtered_versions.empty()) {
420           quic_params->supported_versions = filtered_versions;
421         }
422       }
423 
424       const std::string* quic_connection_options =
425           quic_args.FindString(kQuicConnectionOptions);
426       if (quic_connection_options) {
427         quic_params->connection_options =
428             quic::ParseQuicTagVector(*quic_connection_options);
429       }
430 
431       const std::string* quic_client_connection_options =
432           quic_args.FindString(kQuicClientConnectionOptions);
433       if (quic_client_connection_options) {
434         quic_params->client_connection_options =
435             quic::ParseQuicTagVector(*quic_client_connection_options);
436       }
437 
438       // TODO(rtenneti): Delete this option after apps stop using it.
439       // Added this for backward compatibility.
440       if (quic_args.FindBool(kQuicStoreServerConfigsInProperties)
441               .value_or(false)) {
442         quic_params->max_server_configs_stored_in_properties =
443             net::kDefaultMaxQuicServerEntries;
444       }
445 
446       quic_params->max_server_configs_stored_in_properties =
447           static_cast<size_t>(
448               quic_args.FindInt(kQuicMaxServerConfigsStoredInProperties)
449                   .value_or(
450                       quic_params->max_server_configs_stored_in_properties));
451 
452       quic_params->idle_connection_timeout =
453           map(quic_args.FindInt(kQuicIdleConnectionTimeoutSeconds),
454               base::Seconds<int>)
455               .value_or(quic_params->idle_connection_timeout);
456 
457       quic_params->max_time_before_crypto_handshake =
458           map(quic_args.FindInt(kQuicMaxTimeBeforeCryptoHandshakeSeconds),
459               base::Seconds<int>)
460               .value_or(quic_params->max_time_before_crypto_handshake);
461 
462       quic_params->max_idle_time_before_crypto_handshake =
463           map(quic_args.FindInt(kQuicMaxIdleTimeBeforeCryptoHandshakeSeconds),
464               base::Seconds<int>)
465               .value_or(quic_params->max_idle_time_before_crypto_handshake);
466 
467       quic_params->close_sessions_on_ip_change =
468           quic_args.FindBool(kQuicCloseSessionsOnIpChange)
469               .value_or(quic_params->close_sessions_on_ip_change);
470 
471       quic_params->goaway_sessions_on_ip_change =
472           quic_args.FindBool(kQuicGoAwaySessionsOnIpChange)
473               .value_or(quic_params->goaway_sessions_on_ip_change);
474       quic_params->allow_server_migration =
475           quic_args.FindBool(kQuicAllowServerMigration)
476               .value_or(quic_params->allow_server_migration);
477 
478       std::optional<bool> quic_migrate_sessions_on_network_change_v2_in =
479           quic_args.FindBool(kQuicMigrateSessionsOnNetworkChangeV2);
480       if (quic_migrate_sessions_on_network_change_v2_in.has_value()) {
481         quic_params->migrate_sessions_on_network_change_v2 =
482             quic_migrate_sessions_on_network_change_v2_in.value();
483         quic_params->max_time_on_non_default_network =
484             map(quic_args.FindInt(kQuicMaxTimeOnNonDefaultNetworkSeconds),
485                 base::Seconds<int>)
486                 .value_or(quic_params->max_time_on_non_default_network);
487         quic_params->max_migrations_to_non_default_network_on_write_error =
488             quic_args.FindInt(kQuicMaxMigrationsToNonDefaultNetworkOnWriteError)
489                 .value_or(
490                     quic_params
491                         ->max_migrations_to_non_default_network_on_write_error);
492         quic_params->max_migrations_to_non_default_network_on_path_degrading =
493             quic_args
494                 .FindInt(kQuicMaxMigrationsToNonDefaultNetworkOnPathDegrading)
495                 .value_or(
496                     quic_params
497                         ->max_migrations_to_non_default_network_on_path_degrading);
498       }
499 
500       std::optional<bool> quic_migrate_idle_sessions_in =
501           quic_args.FindBool(kQuicMigrateIdleSessions);
502       if (quic_migrate_idle_sessions_in.has_value()) {
503         quic_params->migrate_idle_sessions =
504             quic_migrate_idle_sessions_in.value();
505         quic_params->idle_session_migration_period =
506             map(quic_args.FindInt(kQuicIdleSessionMigrationPeriodSeconds),
507                 base::Seconds<int>)
508                 .value_or(quic_params->idle_session_migration_period);
509       }
510 
511       quic_params->migrate_sessions_early_v2 =
512           quic_args.FindBool(kQuicMigrateSessionsEarlyV2)
513               .value_or(quic_params->migrate_sessions_early_v2);
514 
515       quic_params->retransmittable_on_wire_timeout =
516           map(quic_args.FindInt(kQuicRetransmittableOnWireTimeoutMilliseconds),
517               base::Milliseconds<int>)
518               .value_or(quic_params->retransmittable_on_wire_timeout);
519 
520       quic_params->retry_on_alternate_network_before_handshake =
521           quic_args.FindBool(kQuicRetryOnAlternateNetworkBeforeHandshake)
522               .value_or(
523                   quic_params->retry_on_alternate_network_before_handshake);
524 
525       quic_params->allow_port_migration =
526           quic_args.FindBool(kAllowPortMigration)
527               .value_or(quic_params->allow_port_migration);
528 
529       quic_params->retry_without_alt_svc_on_quic_errors =
530           quic_args.FindBool(kRetryWithoutAltSvcOnQuicErrors)
531               .value_or(quic_params->retry_without_alt_svc_on_quic_errors);
532 
533       quic_params->initial_delay_for_broken_alternative_service = map(
534           quic_args.FindInt(kInitialDelayForBrokenAlternativeServiceSeconds),
535           base::Seconds<int>);
536 
537       quic_params->exponential_backoff_on_initial_delay =
538           quic_args.FindBool(kExponentialBackoffOnInitialDelay);
539 
540       quic_params->delay_main_job_with_available_spdy_session =
541           quic_args.FindBool(kDelayMainJobWithAvailableSpdySession)
542               .value_or(
543                   quic_params->delay_main_job_with_available_spdy_session);
544 
545       quic_params->disable_tls_zero_rtt =
546           quic_args.FindBool(kDisableTlsZeroRtt)
547               .value_or(quic_params->disable_tls_zero_rtt);
548 
549       const std::string* quic_host_allowlist =
550           quic_args.FindString(kQuicHostWhitelist);
551       if (quic_host_allowlist) {
552         std::vector<std::string> host_vector =
553             base::SplitString(*quic_host_allowlist, ",", base::TRIM_WHITESPACE,
554                               base::SPLIT_WANT_ALL);
555         session_params->quic_host_allowlist.clear();
556         for (const std::string& host : host_vector) {
557           session_params->quic_host_allowlist.insert(host);
558         }
559       }
560 
561       const std::string* quic_flags = quic_args.FindString(kQuicFlags);
562       if (quic_flags) {
563         for (const auto& flag :
564              base::SplitString(*quic_flags, ",", base::TRIM_WHITESPACE,
565                                base::SPLIT_WANT_ALL)) {
566           std::vector<std::string> tokens = base::SplitString(
567               flag, "=", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
568           if (tokens.size() != 2)
569             continue;
570           net::SetQuicFlagByName(tokens[0], tokens[1]);
571         }
572       }
573     } else if (iter->first == kAsyncDnsFieldTrialName) {
574       if (!iter->second.is_dict()) {
575         LOG(ERROR) << "\"" << iter->first << "\" config params \""
576                    << iter->second << "\" is not a dictionary value";
577         effective_experimental_options.Remove(iter->first);
578         continue;
579       }
580       const base::Value::Dict& async_dns_args = iter->second.GetDict();
581       async_dns_enable =
582           async_dns_args.FindBool(kAsyncDnsEnable).value_or(async_dns_enable);
583     } else if (iter->first == kStaleDnsFieldTrialName) {
584       if (!iter->second.is_dict()) {
585         LOG(ERROR) << "\"" << iter->first << "\" config params \""
586                    << iter->second << "\" is not a dictionary value";
587         effective_experimental_options.Remove(iter->first);
588         continue;
589       }
590       const base::Value::Dict& stale_dns_args = iter->second.GetDict();
591       stale_dns_enable =
592           stale_dns_args.FindBool(kStaleDnsEnable).value_or(false);
593 
594       if (stale_dns_enable) {
595         stale_dns_options.delay = map(stale_dns_args.FindInt(kStaleDnsDelayMs),
596                                       base::Milliseconds<int>)
597                                       .value_or(stale_dns_options.delay);
598         stale_dns_options.max_expired_time =
599             map(stale_dns_args.FindInt(kStaleDnsMaxExpiredTimeMs),
600                 base::Milliseconds<int>)
601                 .value_or(stale_dns_options.max_expired_time);
602         stale_dns_options.max_stale_uses =
603             stale_dns_args.FindInt(kStaleDnsMaxStaleUses)
604                 .value_or(stale_dns_options.max_stale_uses);
605         stale_dns_options.allow_other_network =
606             stale_dns_args.FindBool(kStaleDnsAllowOtherNetwork)
607                 .value_or(stale_dns_options.allow_other_network);
608         enable_host_cache_persistence =
609             stale_dns_args.FindBool(kStaleDnsPersist)
610                 .value_or(enable_host_cache_persistence);
611         host_cache_persistence_delay_ms =
612             stale_dns_args.FindInt(kStaleDnsPersistTimer)
613                 .value_or(host_cache_persistence_delay_ms);
614         stale_dns_options.use_stale_on_name_not_resolved =
615             stale_dns_args.FindBool(kStaleDnsUseStaleOnNameNotResolved)
616                 .value_or(stale_dns_options.use_stale_on_name_not_resolved);
617       }
618     } else if (iter->first == kHostResolverRulesFieldTrialName) {
619       if (!iter->second.is_dict()) {
620         LOG(ERROR) << "\"" << iter->first << "\" config params \""
621                    << iter->second << "\" is not a dictionary value";
622         effective_experimental_options.Remove(iter->first);
623         continue;
624       }
625       const base::Value::Dict& host_resolver_rules_args =
626           iter->second.GetDict();
627       host_resolver_rules_string =
628           host_resolver_rules_args.FindString(kHostResolverRules);
629       host_resolver_rules_enable = !!host_resolver_rules_string;
630     } else if (iter->first == kUseDnsHttpsSvcbFieldTrialName) {
631       if (!iter->second.is_dict()) {
632         LOG(ERROR) << "\"" << iter->first << "\" config params \""
633                    << iter->second << "\" is not a dictionary value";
634         effective_experimental_options.Remove(iter->first);
635         continue;
636       }
637       const base::Value::Dict& args = iter->second.GetDict();
638       https_svcb_options = net::HostResolver::HttpsSvcbOptions::FromDict(args);
639       session_params->use_dns_https_svcb_alpn =
640           args.FindBool(kUseDnsHttpsSvcbUseAlpn)
641               .value_or(session_params->use_dns_https_svcb_alpn);
642     } else if (iter->first == kNetworkErrorLoggingFieldTrialName) {
643       if (!iter->second.is_dict()) {
644         LOG(ERROR) << "\"" << iter->first << "\" config params \""
645                    << iter->second << "\" is not a dictionary value";
646         effective_experimental_options.Remove(iter->first);
647         continue;
648       }
649       const base::Value::Dict& nel_args = iter->second.GetDict();
650       nel_enable =
651           nel_args.FindBool(kNetworkErrorLoggingEnable).value_or(nel_enable);
652 
653       const auto* preloaded_report_to_headers_config =
654           nel_args.FindList(kNetworkErrorLoggingPreloadedReportToHeaders);
655       if (preloaded_report_to_headers_config) {
656         preloaded_report_to_headers = ParseNetworkErrorLoggingHeaders(
657             *preloaded_report_to_headers_config);
658       }
659 
660       const auto* preloaded_nel_headers_config =
661           nel_args.FindList(kNetworkErrorLoggingPreloadedNELHeaders);
662       if (preloaded_nel_headers_config) {
663         preloaded_nel_headers =
664             ParseNetworkErrorLoggingHeaders(*preloaded_nel_headers_config);
665       }
666     } else if (iter->first == kDisableIPv6OnWifi) {
667       if (!iter->second.is_bool()) {
668         LOG(ERROR) << "\"" << iter->first << "\" config params \""
669                    << iter->second << "\" is not a bool";
670         effective_experimental_options.Remove(iter->first);
671         continue;
672       }
673       disable_ipv6_on_wifi = iter->second.GetBool();
674     } else if (iter->first == kSSLKeyLogFile) {
675       if (iter->second.is_string()) {
676         base::FilePath ssl_key_log_file(
677             base::FilePath::FromUTF8Unsafe(iter->second.GetString()));
678         if (!ssl_key_log_file.empty()) {
679           // SetSSLKeyLogger is only safe to call before any SSLClientSockets
680           // are created. This should not be used if there are multiple
681           // CronetEngine.
682           // TODO(xunjieli): Expose this as a stable API after crbug.com/458365
683           // is resolved.
684           net::SSLClientSocket::SetSSLKeyLogger(
685               std::make_unique<net::SSLKeyLoggerImpl>(ssl_key_log_file));
686         }
687       }
688     } else if (iter->first == kNetworkQualityEstimatorFieldTrialName) {
689       if (!iter->second.is_dict()) {
690         LOG(ERROR) << "\"" << iter->first << "\" config params \""
691                    << iter->second << "\" is not a dictionary value";
692         effective_experimental_options.Remove(iter->first);
693         continue;
694       }
695 
696       const base::Value::Dict& nqe_args = iter->second.GetDict();
697       const std::string* nqe_option =
698           nqe_args.FindString(net::kForceEffectiveConnectionType);
699       if (nqe_option) {
700         nqe_forced_effective_connection_type =
701             net::GetEffectiveConnectionTypeForName(*nqe_option);
702         if (!nqe_option->empty() && !nqe_forced_effective_connection_type) {
703           LOG(ERROR) << "\"" << nqe_option
704                      << "\" is not a valid effective connection type value";
705         }
706       }
707     } else if (iter->first == kSpdyGoAwayOnIpChange) {
708       if (!iter->second.is_bool()) {
709         LOG(ERROR) << "\"" << iter->first << "\" config params \""
710                    << iter->second << "\" is not a bool";
711         effective_experimental_options.Remove(iter->first);
712         continue;
713       }
714       session_params->spdy_go_away_on_ip_change = iter->second.GetBool();
715     } else {
716       LOG(WARNING) << "Unrecognized Cronet experimental option \""
717                    << iter->first << "\" with params \"" << iter->second;
718       effective_experimental_options.Remove(iter->first);
719     }
720   }
721 
722   if (async_dns_enable || stale_dns_enable || host_resolver_rules_enable ||
723       disable_ipv6_on_wifi || is_network_bound || https_svcb_options) {
724     net::HostResolver::ManagerOptions host_resolver_manager_options;
725     host_resolver_manager_options.insecure_dns_client_enabled =
726         async_dns_enable;
727     host_resolver_manager_options.check_ipv6_on_wifi = !disable_ipv6_on_wifi;
728     if (https_svcb_options) {
729       host_resolver_manager_options.https_svcb_options = https_svcb_options;
730     }
731 
732     if (!is_network_bound) {
733       std::unique_ptr<net::HostResolver> host_resolver;
734       // TODO(crbug.com/40614970): Consider using a shared HostResolverManager
735       // for Cronet HostResolvers.
736       if (stale_dns_enable) {
737         DCHECK(!disable_ipv6_on_wifi);
738         host_resolver = std::make_unique<StaleHostResolver>(
739             net::HostResolver::CreateStandaloneContextResolver(
740                 net::NetLog::Get(), std::move(host_resolver_manager_options)),
741             stale_dns_options);
742       } else {
743         host_resolver = net::HostResolver::CreateStandaloneResolver(
744             net::NetLog::Get(), std::move(host_resolver_manager_options));
745       }
746       if (host_resolver_rules_enable) {
747         std::unique_ptr<net::MappedHostResolver> remapped_resolver(
748             new net::MappedHostResolver(std::move(host_resolver)));
749         remapped_resolver->SetRulesFromString(*host_resolver_rules_string);
750         host_resolver = std::move(remapped_resolver);
751       }
752       context_builder->set_host_resolver(std::move(host_resolver));
753     } else {
754       // `stale_dns_enable` and `host_resolver_rules_enable` are purposefully
755       // ignored. Implementing them requires instantiating a special
756       // HostResolver that wraps the real underlying resolver: that isn't
757       // possible at the moment for network-bound contexts as they create a
758       // special HostResolver internally and don't expose that.
759       context_builder->BindToNetwork(bound_network,
760                                      std::move(host_resolver_manager_options));
761     }
762   }
763 
764 #if BUILDFLAG(ENABLE_REPORTING)
765   if (nel_enable) {
766     auto policy = net::ReportingPolicy::Create();
767 
768     // Apps (like Cronet embedders) are generally allowed to run in the
769     // background, even across network changes, so use more relaxed privacy
770     // settings than when Reporting is running in the browser.
771     policy->persist_reports_across_restarts = true;
772     policy->persist_clients_across_restarts = true;
773     policy->persist_reports_across_network_changes = true;
774     policy->persist_clients_across_network_changes = true;
775 
776     context_builder->set_reporting_policy(std::move(policy));
777     context_builder->set_network_error_logging_enabled(true);
778   }
779 #endif  // BUILDFLAG(ENABLE_REPORTING)
780 }
781 
ConfigureURLRequestContextBuilder(net::URLRequestContextBuilder * context_builder,net::handles::NetworkHandle bound_network)782 void URLRequestContextConfig::ConfigureURLRequestContextBuilder(
783     net::URLRequestContextBuilder* context_builder,
784     net::handles::NetworkHandle bound_network) {
785   std::string config_cache;
786   if (http_cache != DISABLED) {
787     net::URLRequestContextBuilder::HttpCacheParams cache_params;
788     if (http_cache == DISK && !storage_path.empty()) {
789       cache_params.type = net::URLRequestContextBuilder::HttpCacheParams::DISK;
790       cache_params.path = base::FilePath::FromUTF8Unsafe(storage_path)
791                               .Append(kDiskCacheDirectoryName);
792     } else {
793       cache_params.type =
794           net::URLRequestContextBuilder::HttpCacheParams::IN_MEMORY;
795     }
796     cache_params.max_size = http_cache_max_size;
797     context_builder->EnableHttpCache(cache_params);
798   } else {
799     context_builder->DisableHttpCache();
800   }
801   context_builder->set_accept_language(accept_language);
802   context_builder->set_user_agent(user_agent);
803   net::HttpNetworkSessionParams session_params;
804   session_params.enable_http2 = enable_spdy;
805   session_params.enable_quic = enable_quic;
806   auto quic_context = std::make_unique<net::QuicContext>();
807   if (enable_quic) {
808     quic_context->params()->goaway_sessions_on_ip_change = false;
809     // Explicitly disable network-change migration on Cronet. This is tracked
810     // at crbug.com/1430096.
811     quic_context->params()->migrate_sessions_on_network_change_v2 = false;
812   }
813 
814   SetContextBuilderExperimentalOptions(context_builder, &session_params,
815                                        quic_context->params(), bound_network);
816 
817   context_builder->set_http_network_session_params(session_params);
818   context_builder->set_quic_context(std::move(quic_context));
819 
820   if (mock_cert_verifier)
821     context_builder->SetCertVerifier(std::move(mock_cert_verifier));
822   // TODO(mef): Use |config| to set cookies.
823 }
824 
URLRequestContextConfigBuilder()825 URLRequestContextConfigBuilder::URLRequestContextConfigBuilder() {}
~URLRequestContextConfigBuilder()826 URLRequestContextConfigBuilder::~URLRequestContextConfigBuilder() {}
827 
828 std::unique_ptr<URLRequestContextConfig>
Build()829 URLRequestContextConfigBuilder::Build() {
830   return URLRequestContextConfig::CreateURLRequestContextConfig(
831       enable_quic, enable_spdy, enable_brotli, http_cache, http_cache_max_size,
832       load_disable_cache, storage_path, accept_language, user_agent,
833       experimental_options, std::move(mock_cert_verifier),
834       enable_network_quality_estimator,
835       bypass_public_key_pinning_for_local_trust_anchors,
836       network_thread_priority);
837 }
838 
839 }  // namespace cronet
840