diff options
author | Patrick Rohr <prohr@google.com> | 2023-06-02 11:51:16 -0700 |
---|---|---|
committer | Patrick Rohr <prohr@google.com> | 2023-06-02 11:51:51 -0700 |
commit | 034f8d104a19aeee914aac965a6b784df2c2a2fa (patch) | |
tree | 99eb6b11299acf189c460e49519fe877b8631e48 | |
parent | a11c5517ad79b6b1a64bdf3caaf8df6cae838426 (diff) | |
parent | d12afe756882b2521faa0b33cbd4813fcea04c22 (diff) | |
download | cronet-034f8d104a19aeee914aac965a6b784df2c2a2fa.tar.gz |
Merge branch 'upstream-import' into upstream-staging
Test: none
Change-Id: Iaf1c6309cec514da486fb3f16085339e7576ea93
10 files changed, 466 insertions, 308 deletions
diff --git a/build/util/LASTCHANGE b/build/util/LASTCHANGE index f23425f93..a20739da7 100644 --- a/build/util/LASTCHANGE +++ b/build/util/LASTCHANGE @@ -1,2 +1,2 @@ -LASTCHANGE=c499d7ea22c8b2dba278465a5df7b86a8efa4e64-refs/branch-heads/5735@{#970} +LASTCHANGE=8154150933b488112e0d3876e132d1d2b5e54d4b-refs/branch-heads/5735@{#997} LASTCHANGE_YEAR=2023 diff --git a/build/util/LASTCHANGE.committime b/build/util/LASTCHANGE.committime index 0e443f24c..f70183f0e 100644 --- a/build/util/LASTCHANGE.committime +++ b/build/util/LASTCHANGE.committime @@ -1 +1 @@ -1684896280
\ No newline at end of file +1684972835
\ No newline at end of file diff --git a/chrome/VERSION b/chrome/VERSION index 05ef68145..f8eac5cc2 100644 --- a/chrome/VERSION +++ b/chrome/VERSION @@ -1,4 +1,4 @@ MAJOR=114 MINOR=0 BUILD=5735 -PATCH=53 +PATCH=84 diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn index 265dfeb54..3f4c0b51b 100644 --- a/components/cronet/android/BUILD.gn +++ b/components/cronet/android/BUILD.gn @@ -247,6 +247,7 @@ android_library("cronet_api_java") { "api/src/android/net/http/DnsOptions.java", "api/src/android/net/http/ExperimentalBidirectionalStream.java", "api/src/android/net/http/ExperimentalHttpEngine.java", + "api/src/android/net/http/ExperimentalOptionsTranslatingHttpEngineBuilder.java", "api/src/android/net/http/ExperimentalUrlRequest.java", "api/src/android/net/http/HeaderBlock.java", "api/src/android/net/http/IHttpEngineBuilder.java", diff --git a/components/cronet/android/api/src/android/net/http/ExperimentalHttpEngine.java b/components/cronet/android/api/src/android/net/http/ExperimentalHttpEngine.java index f525ad497..c894bce1a 100644 --- a/components/cronet/android/api/src/android/net/http/ExperimentalHttpEngine.java +++ b/components/cronet/android/api/src/android/net/http/ExperimentalHttpEngine.java @@ -9,20 +9,14 @@ import static android.net.http.DnsOptions.DNS_OPTION_ENABLED; import static android.net.http.DnsOptions.DNS_OPTION_UNSPECIFIED; import android.content.Context; -import android.net.http.DnsOptions.StaleDnsOptions; import androidx.annotation.VisibleForTesting; -import org.json.JSONException; -import org.json.JSONObject; - import java.io.IOException; import java.net.Proxy; import java.net.URL; import java.net.URLConnection; import java.time.Instant; -import java.util.ArrayList; -import java.util.List; import java.util.Set; import java.util.concurrent.Executor; @@ -91,10 +85,6 @@ public abstract class ExperimentalHttpEngine extends HttpEngine { * Experimental features may be deprecated in the future. Use at your own risk. */ public static class Builder extends HttpEngine.Builder { - private JSONObject mParsedExperimentalOptions; - private final List<ExperimentalOptionsPatch> mExperimentalOptionsPatches = - new ArrayList<>(); - /** * Constructs a {@link Builder} object that facilitates creating a {@link HttpEngine}. The * default configuration enables HTTP/2 and disables QUIC, SDCH and the HTTP cache. @@ -126,11 +116,7 @@ public abstract class ExperimentalHttpEngine extends HttpEngine { * @return the builder to facilitate chaining. */ public Builder setExperimentalOptions(String options) { - if (options == null || options.isEmpty()) { - mParsedExperimentalOptions = null; - } else { - mParsedExperimentalOptions = parseExperimentalOptions(options); - } + mBuilderDelegate.setExperimentalOptions(options); return this; } @@ -209,255 +195,21 @@ public abstract class ExperimentalHttpEngine extends HttpEngine { @Override @QuicOptions.Experimental public Builder setQuicOptions(QuicOptions options) { - // If the delegate builder supports enabling connection migration directly, just use it - if (mBuilderDelegate.getSupportedConfigOptions().contains( - IHttpEngineBuilder.QUIC_OPTIONS)) { - mBuilderDelegate.setQuicOptions(options); - return this; - } - - // If not, we'll have to work around it by modifying the experimental options JSON. - mExperimentalOptionsPatches.add((experimentalOptions) -> { - JSONObject quicOptions = createDefaultIfAbsent(experimentalOptions, "QUIC"); - - // Note: using the experimental APIs always overwrites what's in the experimental - // JSON, even though "repeated" fields could in theory be additive. - if (!options.getAllowedQuicHosts().isEmpty()) { - quicOptions.put( - "host_whitelist", String.join(",", options.getAllowedQuicHosts())); - } - if (!options.getEnabledQuicVersions().isEmpty()) { - quicOptions.put( - "quic_version", String.join(",", options.getEnabledQuicVersions())); - } - if (!options.getConnectionOptions().isEmpty()) { - quicOptions.put( - "connection_options", String.join(",", options.getConnectionOptions())); - } - if (!options.getClientConnectionOptions().isEmpty()) { - quicOptions.put("client_connection_options", - String.join(",", options.getClientConnectionOptions())); - } - if (!options.getExtraQuicheFlags().isEmpty()) { - quicOptions.put( - "set_quic_flags", String.join(",", options.getExtraQuicheFlags())); - } - - if (options.hasInMemoryServerConfigsCacheSize()) { - quicOptions.put("max_server_configs_stored_in_properties", - options.getInMemoryServerConfigsCacheSize()); - } - - if (options.getHandshakeUserAgent() != null) { - quicOptions.put("user_agent_id", options.getHandshakeUserAgent()); - } - - if (options.getRetryWithoutAltSvcOnQuicErrors() != null) { - quicOptions.put("retry_without_alt_svc_on_quic_errors", - options.getRetryWithoutAltSvcOnQuicErrors()); - } - - if (options.getEnableTlsZeroRtt() != null) { - quicOptions.put("disable_tls_zero_rtt", !options.getEnableTlsZeroRtt()); - } - - if (options.getPreCryptoHandshakeIdleTimeout() != null) { - quicOptions.put("max_idle_time_before_crypto_handshake_seconds", - options.getPreCryptoHandshakeIdleTimeout().toSeconds()); - } - - if (options.getCryptoHandshakeTimeout() != null) { - quicOptions.put("max_time_before_crypto_handshake_seconds", - options.getCryptoHandshakeTimeout().toSeconds()); - } - - if (options.getIdleConnectionTimeout() != null) { - quicOptions.put("idle_connection_timeout_seconds", - options.getIdleConnectionTimeout().toSeconds()); - } - - if (options.getRetransmittableOnWireTimeout() != null) { - quicOptions.put("retransmittable_on_wire_timeout_milliseconds", - options.getRetransmittableOnWireTimeout().toMillis()); - } - - if (options.getCloseSessionsOnIpChange() != null) { - quicOptions.put( - "close_sessions_on_ip_change", options.getCloseSessionsOnIpChange()); - } - - if (options.getGoawaySessionsOnIpChange() != null) { - quicOptions.put( - "goaway_sessions_on_ip_change", options.getGoawaySessionsOnIpChange()); - } - - if (options.getInitialBrokenServicePeriod() != null) { - quicOptions.put("initial_delay_for_broken_alternative_service_seconds", - options.getInitialBrokenServicePeriod().toSeconds()); - } - - if (options.getIncreaseBrokenServicePeriodExponentially() != null) { - quicOptions.put("exponential_backoff_on_initial_delay", - options.getIncreaseBrokenServicePeriodExponentially()); - } - - if (options.getDelayJobsWithAvailableSpdySession() != null) { - quicOptions.put("delay_main_job_with_available_spdy_session", - options.getDelayJobsWithAvailableSpdySession()); - } - }); + super.setQuicOptions(options); return this; } @Override @DnsOptions.Experimental public Builder setDnsOptions(DnsOptions options) { - // If the delegate builder supports enabling connection migration directly, just use it - if (mBuilderDelegate.getSupportedConfigOptions().contains( - IHttpEngineBuilder.DNS_OPTIONS)) { - mBuilderDelegate.setDnsOptions(options); - return this; - } - - // If not, we'll have to work around it by modifying the experimental options JSON. - mExperimentalOptionsPatches.add((experimentalOptions) -> { - JSONObject asyncDnsOptions = createDefaultIfAbsent(experimentalOptions, "AsyncDNS"); - - if (options.getUseHttpStackDnsResolver() != DNS_OPTION_UNSPECIFIED) { - asyncDnsOptions.put("enable", - options.getUseHttpStackDnsResolver() == DNS_OPTION_ENABLED); - } - - JSONObject staleDnsOptions = createDefaultIfAbsent(experimentalOptions, "StaleDNS"); - - if (options.getStaleDns() != DNS_OPTION_UNSPECIFIED) { - staleDnsOptions.put("enable", - options.getStaleDns() == DNS_OPTION_ENABLED); - } - - if (options.getPersistHostCache() != DNS_OPTION_UNSPECIFIED) { - staleDnsOptions.put("persist_to_disk", - options.getPersistHostCache() == DNS_OPTION_ENABLED); - } - - if (options.getPersistHostCachePeriod() != null) { - staleDnsOptions.put( - "persist_delay_ms", options.getPersistHostCachePeriod().toMillis()); - } - - if (options.getStaleDnsOptions() != null) { - StaleDnsOptions staleDnsOptionsJava = options.getStaleDnsOptions(); - - if (staleDnsOptionsJava.getAllowCrossNetworkUsage() - != DNS_OPTION_UNSPECIFIED) { - staleDnsOptions.put("allow_other_network", - staleDnsOptionsJava.getAllowCrossNetworkUsage() - == DNS_OPTION_ENABLED); - } - - if (staleDnsOptionsJava.getFreshLookupTimeout() != null) { - staleDnsOptions.put( - "delay_ms", staleDnsOptionsJava.getFreshLookupTimeout().toMillis()); - } - - if (staleDnsOptionsJava.getUseStaleOnNameNotResolved() - != DNS_OPTION_UNSPECIFIED) { - staleDnsOptions.put("use_stale_on_name_not_resolved", - staleDnsOptionsJava.getUseStaleOnNameNotResolved() - == DNS_OPTION_ENABLED); - } - - if (staleDnsOptionsJava.getMaxExpiredDelay() != null) { - staleDnsOptions.put("max_expired_time_ms", - staleDnsOptionsJava.getMaxExpiredDelay().toMillis()); - } - } - - JSONObject quicOptions = createDefaultIfAbsent(experimentalOptions, "QUIC"); - if (options.getPreestablishConnectionsToStaleDnsResults() - != DNS_OPTION_UNSPECIFIED) { - quicOptions.put("race_stale_dns_on_connection", - options.getPreestablishConnectionsToStaleDnsResults() - == DNS_OPTION_ENABLED); - } - }); + super.setDnsOptions(options); return this; } @Override @ConnectionMigrationOptions.Experimental public Builder setConnectionMigrationOptions(ConnectionMigrationOptions options) { - // If the delegate builder supports enabling connection migration directly, just use it - if (mBuilderDelegate.getSupportedConfigOptions().contains( - IHttpEngineBuilder.CONNECTION_MIGRATION_OPTIONS)) { - mBuilderDelegate.setConnectionMigrationOptions(options); - return this; - } - - // If not, we'll have to work around it by modifying the experimental options JSON. - mExperimentalOptionsPatches.add((experimentalOptions) -> { - JSONObject quicOptions = createDefaultIfAbsent(experimentalOptions, "QUIC"); - - if (options.getDefaultNetworkMigration() != MIGRATION_OPTION_UNSPECIFIED) { - quicOptions.put("migrate_sessions_on_network_change_v2", - options.getDefaultNetworkMigration() - == MIGRATION_OPTION_ENABLED); - } - if (options.getAllowServerMigration() != null) { - quicOptions.put("allow_server_migration", options.getAllowServerMigration()); - } - if (options.getMigrateIdleConnections() != null) { - quicOptions.put("migrate_idle_sessions", options.getMigrateIdleConnections()); - } - if (options.getIdleMigrationPeriod() != null) { - quicOptions.put("idle_session_migration_period_seconds", - options.getIdleMigrationPeriod().toSeconds()); - } - if (options.getMaxTimeOnNonDefaultNetwork() != null) { - quicOptions.put("max_time_on_non_default_network_seconds", - options.getMaxTimeOnNonDefaultNetwork().toSeconds()); - } - if (options.getMaxPathDegradingNonDefaultMigrationsCount() != null) { - quicOptions.put("max_migrations_to_non_default_network_on_path_degrading", - options.getMaxPathDegradingNonDefaultMigrationsCount()); - } - if (options.getMaxWriteErrorNonDefaultNetworkMigrationsCount() != null) { - quicOptions.put("max_migrations_to_non_default_network_on_write_error", - options.getMaxWriteErrorNonDefaultNetworkMigrationsCount()); - } - if (options.getPathDegradationMigration() != MIGRATION_OPTION_UNSPECIFIED) { - boolean pathDegradationValue = (options.getPathDegradationMigration() - == MIGRATION_OPTION_ENABLED); - - boolean skipPortMigrationFlag = false; - - if (options.getAllowNonDefaultNetworkUsage() - != MIGRATION_OPTION_UNSPECIFIED) { - boolean nonDefaultNetworkValue = - (options.getAllowNonDefaultNetworkUsage() - == MIGRATION_OPTION_ENABLED); - if (!pathDegradationValue && nonDefaultNetworkValue) { - // Misconfiguration which doesn't translate easily to the JSON flags - throw new IllegalArgumentException( - "Unable to turn on non-default network usage without path " - + "degradation migration!"); - } else if (pathDegradationValue && nonDefaultNetworkValue) { - // Both values being true results in the non-default network migration - // being enabled. - quicOptions.put("migrate_sessions_early_v2", true); - quicOptions.put("retry_on_alternate_network_before_handshake", true); - skipPortMigrationFlag = true; - } else { - quicOptions.put("migrate_sessions_early_v2", false); - } - } - - if (!skipPortMigrationFlag) { - quicOptions.put("allow_port_migration", pathDegradationValue); - } - } - }); - + super.setConnectionMigrationOptions(options); return this; } @@ -488,53 +240,8 @@ public abstract class ExperimentalHttpEngine extends HttpEngine { @Override public ExperimentalHttpEngine build() { - if (mParsedExperimentalOptions == null && mExperimentalOptionsPatches.isEmpty()) { - return mBuilderDelegate.build(); - } - - if (mParsedExperimentalOptions == null) { - mParsedExperimentalOptions = new JSONObject(); - } - - for (ExperimentalOptionsPatch patch : mExperimentalOptionsPatches) { - try { - patch.applyTo(mParsedExperimentalOptions); - } catch (JSONException e) { - throw new IllegalStateException("Unable to apply JSON patch!", e); - } - } - - mBuilderDelegate.setExperimentalOptions(mParsedExperimentalOptions.toString()); return mBuilderDelegate.build(); } - - private static JSONObject parseExperimentalOptions(String jsonString) { - try { - return new JSONObject(jsonString); - } catch (JSONException e) { - throw new IllegalArgumentException("Experimental options parsing failed", e); - } - } - - private static JSONObject createDefaultIfAbsent(JSONObject jsonObject, String key) { - JSONObject object = jsonObject.optJSONObject(key); - if (object == null) { - object = new JSONObject(); - try { - jsonObject.put(key, object); - } catch (JSONException e) { - throw new IllegalArgumentException( - "Failed adding a default object for key [" + key + "]", e); - } - } - - return object; - } - - @FunctionalInterface - private interface ExperimentalOptionsPatch { - void applyTo(JSONObject experimentalOptions) throws JSONException; - } } @Override diff --git a/components/cronet/android/api/src/android/net/http/ExperimentalOptionsTranslatingCronetEngineBuilder.java b/components/cronet/android/api/src/android/net/http/ExperimentalOptionsTranslatingCronetEngineBuilder.java new file mode 100644 index 000000000..6efc222b4 --- /dev/null +++ b/components/cronet/android/api/src/android/net/http/ExperimentalOptionsTranslatingCronetEngineBuilder.java @@ -0,0 +1,430 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +package android.net.http; + +import androidx.annotation.VisibleForTesting; + +import org.json.JSONException; +import org.json.JSONObject; + +import org.chromium.net.DnsOptions.StaleDnsOptions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * An implementation of IHttpEngineBuilder which handles translation of configuration options to + * json-based experimental options, if necessary. + * + * <p>{@hide internal class} + */ +final class ExperimentalOptionsTranslatingHttpEngineBuilder extends IHttpEngineBuilder { + private static final Set<Integer> SUPPORTED_OPTIONS = Collections.unmodifiableSet( + new HashSet(Arrays.asList(IHttpEngineBuilder.CONNECTION_MIGRATION_OPTIONS, + IHttpEngineBuilder.DNS_OPTIONS, IHttpEngineBuilder.QUIC_OPTIONS))); + + private JSONObject mParsedExperimentalOptions; + private final List<ExperimentalOptionsPatch> mExperimentalOptionsPatches = new ArrayList<>(); + + private final IHttpEngineBuilder mDelegate; + + ExperimentalOptionsTranslatingHttpEngineBuilder(IHttpEngineBuilder delegate) { + this.mDelegate = delegate; + } + + @Override + public IHttpEngineBuilder setQuicOptions(QuicOptions options) { + // If the delegate builder supports enabling connection migration directly, just use it + if (mDelegate.getSupportedConfigOptions().contains(IHttpEngineBuilder.QUIC_OPTIONS)) { + mDelegate.setQuicOptions(options); + return this; + } + + // If not, we'll have to work around it by modifying the experimental options JSON. + mExperimentalOptionsPatches.add((experimentalOptions) -> { + JSONObject quicOptions = createDefaultIfAbsent(experimentalOptions, "QUIC"); + + // Note: using the experimental APIs always overwrites what's in the experimental + // JSON, even though "repeated" fields could in theory be additive. + if (!options.getQuicHostAllowlist().isEmpty()) { + quicOptions.put("host_whitelist", String.join(",", options.getQuicHostAllowlist())); + } + if (!options.getEnabledQuicVersions().isEmpty()) { + quicOptions.put("quic_version", String.join(",", options.getEnabledQuicVersions())); + } + if (!options.getConnectionOptions().isEmpty()) { + quicOptions.put( + "connection_options", String.join(",", options.getConnectionOptions())); + } + if (!options.getClientConnectionOptions().isEmpty()) { + quicOptions.put("client_connection_options", + String.join(",", options.getClientConnectionOptions())); + } + if (!options.getExtraQuicheFlags().isEmpty()) { + quicOptions.put("set_quic_flags", String.join(",", options.getExtraQuicheFlags())); + } + + if (options.getInMemoryServerConfigsCacheSize() != null) { + quicOptions.put("max_server_configs_stored_in_properties", + options.getInMemoryServerConfigsCacheSize()); + } + + if (options.getHandshakeUserAgent() != null) { + quicOptions.put("user_agent_id", options.getHandshakeUserAgent()); + } + + if (options.getRetryWithoutAltSvcOnQuicErrors() != null) { + quicOptions.put("retry_without_alt_svc_on_quic_errors", + options.getRetryWithoutAltSvcOnQuicErrors()); + } + + if (options.getEnableTlsZeroRtt() != null) { + quicOptions.put("disable_tls_zero_rtt", !options.getEnableTlsZeroRtt()); + } + + if (options.getPreCryptoHandshakeIdleTimeoutSeconds() != null) { + quicOptions.put("max_idle_time_before_crypto_handshake_seconds", + options.getPreCryptoHandshakeIdleTimeoutSeconds()); + } + + if (options.getCryptoHandshakeTimeoutSeconds() != null) { + quicOptions.put("max_time_before_crypto_handshake_seconds", + options.getCryptoHandshakeTimeoutSeconds()); + } + + if (options.getIdleConnectionTimeoutSeconds() != null) { + quicOptions.put("idle_connection_timeout_seconds", + options.getIdleConnectionTimeoutSeconds()); + } + + if (options.getRetransmittableOnWireTimeoutMillis() != null) { + quicOptions.put("retransmittable_on_wire_timeout_milliseconds", + options.getRetransmittableOnWireTimeoutMillis()); + } + + if (options.getCloseSessionsOnIpChange() != null) { + quicOptions.put( + "close_sessions_on_ip_change", options.getCloseSessionsOnIpChange()); + } + + if (options.getGoawaySessionsOnIpChange() != null) { + quicOptions.put( + "goaway_sessions_on_ip_change", options.getGoawaySessionsOnIpChange()); + } + + if (options.getInitialBrokenServicePeriodSeconds() != null) { + quicOptions.put("initial_delay_for_broken_alternative_service_seconds", + options.getInitialBrokenServicePeriodSeconds()); + } + + if (options.getIncreaseBrokenServicePeriodExponentially() != null) { + quicOptions.put("exponential_backoff_on_initial_delay", + options.getIncreaseBrokenServicePeriodExponentially()); + } + + if (options.getDelayJobsWithAvailableSpdySession() != null) { + quicOptions.put("delay_main_job_with_available_spdy_session", + options.getDelayJobsWithAvailableSpdySession()); + } + }); + return this; + } + + @Override + public IHttpEngineBuilder setDnsOptions(DnsOptions options) { + // If the delegate builder supports enabling connection migration directly, just use it + if (mDelegate.getSupportedConfigOptions().contains(IHttpEngineBuilder.DNS_OPTIONS)) { + mDelegate.setDnsOptions(options); + return this; + } + + // If not, we'll have to work around it by modifying the experimental options JSON. + mExperimentalOptionsPatches.add((experimentalOptions) -> { + JSONObject asyncDnsOptions = createDefaultIfAbsent(experimentalOptions, "AsyncDNS"); + + if (options.getUseBuiltInDnsResolver() != null) { + asyncDnsOptions.put("enable", options.getUseBuiltInDnsResolver()); + } + + JSONObject staleDnsOptions = createDefaultIfAbsent(experimentalOptions, "StaleDNS"); + + if (options.getEnableStaleDns() != null) { + staleDnsOptions.put("enable", options.getEnableStaleDns()); + } + + if (options.getPersistHostCache() != null) { + staleDnsOptions.put("persist_to_disk", options.getPersistHostCache()); + } + + if (options.getPersistHostCachePeriodMillis() != null) { + staleDnsOptions.put("persist_delay_ms", options.getPersistHostCachePeriodMillis()); + } + + if (options.getStaleDnsOptions() != null) { + StaleDnsOptions staleDnsOptionsJava = options.getStaleDnsOptions(); + + if (staleDnsOptionsJava.getAllowCrossNetworkUsage() != null) { + staleDnsOptions.put( + "allow_other_network", staleDnsOptionsJava.getAllowCrossNetworkUsage()); + } + + if (staleDnsOptionsJava.getFreshLookupTimeoutMillis() != null) { + staleDnsOptions.put( + "delay_ms", staleDnsOptionsJava.getFreshLookupTimeoutMillis()); + } + + if (staleDnsOptionsJava.getUseStaleOnNameNotResolved() != null) { + staleDnsOptions.put("use_stale_on_name_not_resolved", + staleDnsOptionsJava.getUseStaleOnNameNotResolved()); + } + + if (staleDnsOptionsJava.getMaxExpiredDelayMillis() != null) { + staleDnsOptions.put( + "max_expired_time_ms", staleDnsOptionsJava.getMaxExpiredDelayMillis()); + } + } + + JSONObject quicOptions = createDefaultIfAbsent(experimentalOptions, "QUIC"); + if (options.getPreestablishConnectionsToStaleDnsResults() != null) { + quicOptions.put("race_stale_dns_on_connection", + options.getPreestablishConnectionsToStaleDnsResults()); + } + }); + return this; + } + + @Override + public IHttpEngineBuilder setConnectionMigrationOptions(ConnectionMigrationOptions options) { + // If the delegate builder supports enabling connection migration directly, just use it + if (mDelegate.getSupportedConfigOptions().contains( + IHttpEngineBuilder.CONNECTION_MIGRATION_OPTIONS)) { + mDelegate.setConnectionMigrationOptions(options); + return this; + } + + // If not, we'll have to work around it by modifying the experimental options JSON. + mExperimentalOptionsPatches.add((experimentalOptions) -> { + JSONObject quicOptions = createDefaultIfAbsent(experimentalOptions, "QUIC"); + + if (options.getEnableDefaultNetworkMigration() != null) { + quicOptions.put("migrate_sessions_on_network_change_v2", + options.getEnableDefaultNetworkMigration()); + } + if (options.getAllowServerMigration() != null) { + quicOptions.put("allow_server_migration", options.getAllowServerMigration()); + } + if (options.getMigrateIdleConnections() != null) { + quicOptions.put("migrate_idle_sessions", options.getMigrateIdleConnections()); + } + if (options.getIdleMigrationPeriodSeconds() != null) { + quicOptions.put("idle_session_migration_period_seconds", + options.getIdleMigrationPeriodSeconds()); + } + if (options.getRetryPreHandshakeErrorsOnAlternateNetwork() != null) { + quicOptions.put("retry_on_alternate_network_before_handshake", + options.getRetryPreHandshakeErrorsOnAlternateNetwork()); + } + if (options.getMaxTimeOnNonDefaultNetworkSeconds() != null) { + quicOptions.put("max_time_on_non_default_network_seconds", + options.getMaxTimeOnNonDefaultNetworkSeconds()); + } + if (options.getMaxPathDegradingEagerMigrationsCount() != null) { + quicOptions.put("max_migrations_to_non_default_network_on_path_degrading", + options.getMaxPathDegradingEagerMigrationsCount()); + } + if (options.getMaxWriteErrorEagerMigrationsCount() != null) { + quicOptions.put("max_migrations_to_non_default_network_on_write_error", + options.getMaxWriteErrorEagerMigrationsCount()); + } + if (options.getEnablePathDegradationMigration() != null) { + boolean pathDegradationValue = options.getEnablePathDegradationMigration(); + + boolean skipPortMigrationFlag = false; + + if (options.getAllowNonDefaultNetworkUsage() != null) { + boolean nonDefaultNetworkValue = options.getAllowNonDefaultNetworkUsage(); + if (!pathDegradationValue && nonDefaultNetworkValue) { + // Misconfiguration which doesn't translate easily to the JSON flags + throw new IllegalArgumentException( + "Unable to turn on non-default network usage without path " + + "degradation migration!"); + } else if (pathDegradationValue && nonDefaultNetworkValue) { + // Both values being true results in the non-default network migration + // being enabled. + quicOptions.put("migrate_sessions_early_v2", true); + skipPortMigrationFlag = true; + } else { + quicOptions.put("migrate_sessions_early_v2", false); + } + } + + if (!skipPortMigrationFlag) { + quicOptions.put("allow_port_migration", pathDegradationValue); + } + } + }); + + return this; + } + + @Override + public IHttpEngineBuilder setExperimentalOptions(String options) { + if (options == null || options.isEmpty()) { + mParsedExperimentalOptions = null; + } else { + mParsedExperimentalOptions = parseExperimentalOptions(options); + } + return this; + } + + @Override + protected Set<Integer> getSupportedConfigOptions() { + return SUPPORTED_OPTIONS; + } + + @Override + public ExperimentalHttpEngine build() { + if (mParsedExperimentalOptions == null && mExperimentalOptionsPatches.isEmpty()) { + return mDelegate.build(); + } + + if (mParsedExperimentalOptions == null) { + mParsedExperimentalOptions = new JSONObject(); + } + + for (ExperimentalOptionsPatch patch : mExperimentalOptionsPatches) { + try { + patch.applyTo(mParsedExperimentalOptions); + } catch (JSONException e) { + throw new IllegalStateException("Unable to apply JSON patch!", e); + } + } + + mDelegate.setExperimentalOptions(mParsedExperimentalOptions.toString()); + return mDelegate.build(); + } + + private static JSONObject parseExperimentalOptions(String jsonString) { + try { + return new JSONObject(jsonString); + } catch (JSONException e) { + throw new IllegalArgumentException("Experimental options parsing failed", e); + } + } + + private static JSONObject createDefaultIfAbsent(JSONObject jsonObject, String key) { + JSONObject object = jsonObject.optJSONObject(key); + if (object == null) { + object = new JSONObject(); + try { + jsonObject.put(key, object); + } catch (JSONException e) { + throw new IllegalArgumentException( + "Failed adding a default object for key [" + key + "]", e); + } + } + + return object; + } + + @VisibleForTesting + IHttpEngineBuilder getDelegate() { + return mDelegate; + } + + @FunctionalInterface + private interface ExperimentalOptionsPatch { + void applyTo(JSONObject experimentalOptions) throws JSONException; + } + + // Delegating-only methods + @Override + public IHttpEngineBuilder addPublicKeyPins(String hostName, Set<byte[]> pinsSha256, + boolean includeSubdomains, Date expirationDate) { + mDelegate.addPublicKeyPins(hostName, pinsSha256, includeSubdomains, expirationDate); + return this; + } + + @Override + public IHttpEngineBuilder addQuicHint(String host, int port, int alternatePort) { + mDelegate.addQuicHint(host, port, alternatePort); + return this; + } + + @Override + public IHttpEngineBuilder enableHttp2(boolean value) { + mDelegate.enableHttp2(value); + return this; + } + + @Override + public IHttpEngineBuilder enableHttpCache(int cacheMode, long maxSize) { + mDelegate.enableHttpCache(cacheMode, maxSize); + return this; + } + + @Override + public IHttpEngineBuilder enablePublicKeyPinningBypassForLocalTrustAnchors(boolean value) { + mDelegate.enablePublicKeyPinningBypassForLocalTrustAnchors(value); + return this; + } + + @Override + public IHttpEngineBuilder enableQuic(boolean value) { + mDelegate.enableQuic(value); + return this; + } + + @Override + public IHttpEngineBuilder enableSdch(boolean value) { + mDelegate.enableSdch(value); + return this; + } + + @Override + public IHttpEngineBuilder enableBrotli(boolean value) { + mDelegate.enableBrotli(value); + return this; + } + + @Override + public IHttpEngineBuilder setLibraryLoader(HttpEngine.Builder.LibraryLoader loader) { + mDelegate.setLibraryLoader(loader); + return this; + } + + @Override + public IHttpEngineBuilder setStoragePath(String value) { + mDelegate.setStoragePath(value); + return this; + } + + @Override + public IHttpEngineBuilder setUserAgent(String userAgent) { + mDelegate.setUserAgent(userAgent); + return this; + } + + @Override + public String getDefaultUserAgent() { + return mDelegate.getDefaultUserAgent(); + } + + @Override + public IHttpEngineBuilder enableNetworkQualityEstimator(boolean value) { + mDelegate.enableNetworkQualityEstimator(value); + return this; + } + + @Override + public IHttpEngineBuilder setThreadPriority(int priority) { + mDelegate.setThreadPriority(priority); + return this; + } +} diff --git a/components/cronet/android/api/src/android/net/http/HttpEngine.java b/components/cronet/android/api/src/android/net/http/HttpEngine.java index 3687180e4..ec3b04c40 100644 --- a/components/cronet/android/api/src/android/net/http/HttpEngine.java +++ b/components/cronet/android/api/src/android/net/http/HttpEngine.java @@ -160,7 +160,13 @@ public abstract class HttpEngine { * {@hide} */ Builder(@NonNull IHttpEngineBuilder builderDelegate) { - mBuilderDelegate = builderDelegate; + if (builderDelegate instanceof ExperimentalOptionsTranslatingHttpEngineBuilder) { + // Already wrapped at the top level, no need to do it again + mBuilderDelegate = builderDelegate; + } else { + mBuilderDelegate = + new ExperimentalOptionsTranslatingHttpEngineBuilder(builderDelegate); + } } /** diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java index 80c246482..e48cb8a0c 100644 --- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java +++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java @@ -51,7 +51,6 @@ import org.chromium.net.CronetTestRule.RequiresMinAndroidApi; import org.chromium.net.CronetTestRule.RequiresMinApi; import org.chromium.net.NetworkChangeNotifierAutoDetect.ConnectivityManagerDelegate; import org.chromium.net.TestUrlRequestCallback.ResponseStep; -import org.chromium.net.impl.CronetEngineBuilderImpl; import org.chromium.net.impl.CronetLibraryLoader; import org.chromium.net.impl.CronetUrlRequestContext; @@ -1482,7 +1481,7 @@ public class CronetUrlRequestContextTest { builder.setEnablePublicKeyPinningBypassForLocalTrustAnchors(false); CronetUrlRequestContextTestJni.get().verifyUrlRequestContextConfig( CronetUrlRequestContext.createNativeUrlRequestContextConfig( - (CronetEngineBuilderImpl) builder.getBuilderDelegate()), + CronetTestUtil.getCronetEngineBuilderImpl(builder)), getTestStorage(getContext())); } @@ -1505,7 +1504,7 @@ public class CronetUrlRequestContextTest { builder.setEnablePublicKeyPinningBypassForLocalTrustAnchors(false); CronetUrlRequestContextTestJni.get().verifyUrlRequestContextQuicOffConfig( CronetUrlRequestContext.createNativeUrlRequestContextConfig( - (CronetEngineBuilderImpl) builder.getBuilderDelegate()), + CronetTestUtil.getCronetEngineBuilderImpl(builder)), getTestStorage(getContext())); } diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/ExperimentalOptionsTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/ExperimentalOptionsTest.java index 2a78dbd4e..c0f9af289 100644 --- a/components/cronet/android/test/javatests/src/org/chromium/net/ExperimentalOptionsTest.java +++ b/components/cronet/android/test/javatests/src/org/chromium/net/ExperimentalOptionsTest.java @@ -511,10 +511,23 @@ public class ExperimentalOptionsTest { @Test @MediumTest @OnlyRunNativeCronet - public void testExperimentalOptions_allSet() throws Exception { + public void testExperimentalOptions_allSet_viaExperimentalEngine() throws Exception { MockCronetBuilderImpl mockBuilderImpl = MockCronetBuilderImpl.withoutNativeSetterSupport(); - mBuilder = new ExperimentalHttpEngine.Builder(mockBuilderImpl); + testExperimentalOptionsAllSetImpl( + new ExperimentalHttpEngine.Builder(mockBuilderImpl), mockBuilderImpl); + } + + @Test + @MediumTest + @OnlyRunNativeCronet + public void testExperimentalOptions_allSet_viaNonExperimentalEngine() throws Exception { + MockCronetBuilderImpl mockBuilderImpl = MockCronetBuilderImpl.withoutNativeSetterSupport(); + testExperimentalOptionsAllSetImpl( + new CronetEngine.Builder(mockBuilderImpl), mockBuilderImpl); + } + private static void testExperimentalOptionsAllSetImpl( + CronetEngine.Builder builder, MockCronetBuilderImpl mockBuilderImpl) throws Exception { QuicOptions quicOptions = QuicOptions.builder() .addAllowedQuicHost("quicHost1.com") @@ -601,7 +614,7 @@ public class ExperimentalOptionsTest { toTelephoneKeyboardSequence("badPathErr")) .build(); - mBuilder.setDnsOptions(dnsOptions) + builder.setDnsOptions(dnsOptions) .setConnectionMigrationOptions(connectionMigrationOptions) .setQuicOptions(quicOptions) .build(); diff --git a/components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java b/components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java index e6954c871..271b02672 100644 --- a/components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java +++ b/components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java @@ -99,7 +99,9 @@ public class CronetTestUtil { public static CronetEngineBuilderImpl getCronetEngineBuilderImpl( ExperimentalHttpEngine.Builder builder) { - return (CronetEngineBuilderImpl) builder.getBuilderDelegate(); + return (CronetEngineBuilderImpl) ((ExperimentalOptionsTranslatingHttpEngineBuilder) + builder.getBuilderDelegate()) + .getDelegate(); } /** |