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