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