Index: components/cronet/android/java/src/org/chromium/net/CronetEngine.java |
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetEngine.java b/components/cronet/android/java/src/org/chromium/net/CronetEngine.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2de7e8d001ef96e49d84d07f5305d474fdbcc03e |
--- /dev/null |
+++ b/components/cronet/android/java/src/org/chromium/net/CronetEngine.java |
@@ -0,0 +1,563 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.net; |
+ |
+import android.content.Context; |
+import android.util.Log; |
+ |
+import org.json.JSONArray; |
+import org.json.JSONException; |
+import org.json.JSONObject; |
+ |
+import java.io.File; |
+import java.lang.reflect.Constructor; |
+import java.util.concurrent.Executor; |
+ |
+/** |
+ * An engine to process {@link UrlRequest}s, which uses the best HTTP stack |
+ * available on the current platform. |
+ */ |
+public abstract class CronetEngine { |
+ /** |
+ * A builder for {@link CronetEngine}s, which allows runtime configuration of |
+ * {@code CronetEngine}. Configuration options are set on the builder and |
+ * then {@link #build} is called to create the {@code CronetEngine}. |
+ */ |
+ public static class Builder { |
+ private final JSONObject mConfig; |
+ private final Context mContext; |
+ |
+ /** |
+ * Default config enables SPDY, disables QUIC, SDCH and HTTP cache. |
+ * @param context Android {@link Context} for engine to use. |
+ */ |
+ public Builder(Context context) { |
+ mConfig = new JSONObject(); |
+ mContext = context; |
+ enableLegacyMode(false); |
+ enableQUIC(false); |
+ enableHTTP2(true); |
+ enableSDCH(false); |
+ enableHttpCache(HTTP_CACHE_DISABLED, 0); |
+ } |
+ |
+ /** |
+ * Creates a config from a JSON string, which was serialized using |
+ * {@link #toString}. |
+ * |
+ * @param context Android {@link Context} for engine to use. |
+ * @param json JSON string of configuration parameters, which was |
+ * serialized using {@link #toString}. |
+ */ |
+ public Builder(Context context, String json) throws JSONException { |
+ mConfig = new JSONObject(json); |
+ mContext = context; |
+ } |
+ |
+ /** |
+ * Overrides the user-agent header for all requests. |
+ * @return the builder to facilitate chaining. |
+ */ |
+ public Builder setUserAgent(String userAgent) { |
+ return putString(CronetEngineBuilderList.USER_AGENT, userAgent); |
+ } |
+ |
+ String userAgent() { |
+ return mConfig.optString(CronetEngineBuilderList.USER_AGENT); |
+ } |
+ |
+ /** |
+ * Sets directory for HTTP Cache and Cookie Storage. The directory must |
+ * exist. |
+ * @param value path to existing directory. |
+ * @return the builder to facilitate chaining. |
+ */ |
+ public Builder setStoragePath(String value) { |
+ if (!new File(value).isDirectory()) { |
+ throw new IllegalArgumentException( |
+ "Storage path must be set to existing directory"); |
+ } |
+ |
+ return putString(CronetEngineBuilderList.STORAGE_PATH, value); |
+ } |
+ |
+ String storagePath() { |
+ return mConfig.optString(CronetEngineBuilderList.STORAGE_PATH); |
+ } |
+ |
+ /** |
+ * Sets whether falling back to implementation based on system's |
+ * {@link java.net.HttpURLConnection} implementation is enabled. |
+ * Defaults to disabled. |
+ * @return the builder to facilitate chaining. |
+ * @deprecated Not supported by the new API. |
+ */ |
+ @Deprecated |
+ public Builder enableLegacyMode(boolean value) { |
+ return putBoolean(CronetEngineBuilderList.ENABLE_LEGACY_MODE, value); |
+ } |
+ |
+ boolean legacyMode() { |
+ return mConfig.optBoolean(CronetEngineBuilderList.ENABLE_LEGACY_MODE); |
+ } |
+ |
+ /** |
+ * Overrides the name of the native library backing Cronet. |
+ * @return the builder to facilitate chaining. |
+ */ |
+ Builder setLibraryName(String libName) { |
+ return putString(CronetEngineBuilderList.NATIVE_LIBRARY_NAME, libName); |
+ } |
+ |
+ String libraryName() { |
+ return mConfig.optString(CronetEngineBuilderList.NATIVE_LIBRARY_NAME, "cronet"); |
+ } |
+ |
+ /** |
+ * Sets whether <a href="https://www.chromium.org/quic">QUIC</a> protocol |
+ * is enabled. Defaults to disabled. |
+ * @return the builder to facilitate chaining. |
+ */ |
+ public Builder enableQUIC(boolean value) { |
+ return putBoolean(CronetEngineBuilderList.ENABLE_QUIC, value); |
+ } |
+ |
+ /** |
+ * Sets whether <a href="https://tools.ietf.org/html/rfc7540">HTTP/2</a> |
+ * protocol is enabled. Defaults to enabled. |
+ * @return the builder to facilitate chaining. |
+ */ |
+ public Builder enableHTTP2(boolean value) { |
+ return putBoolean(CronetEngineBuilderList.ENABLE_SPDY, value); |
+ } |
+ |
+ /** |
+ * Sets whether |
+ * <a |
+ * href="https://lists.w3.org/Archives/Public/ietf-http-wg/2008JulSep/att-0441/Shared_Dictionary_Compression_over_HTTP.pdf"> |
+ * SDCH</a> compression is enabled. Defaults to disabled. |
+ * @return the builder to facilitate chaining. |
+ */ |
+ public Builder enableSDCH(boolean value) { |
+ return putBoolean(CronetEngineBuilderList.ENABLE_SDCH, value); |
+ } |
+ |
+ /** |
+ * Enables |
+ * <a href="https://developer.chrome.com/multidevice/data-compression">Data |
+ * Reduction Proxy</a>. Defaults to disabled. |
+ * @param key key to use when authenticating with the proxy. |
+ * @return the builder to facilitate chaining. |
+ */ |
+ public Builder enableDataReductionProxy(String key) { |
+ return (putString(CronetEngineBuilderList.DATA_REDUCTION_PROXY_KEY, key)); |
+ } |
+ |
+ /** |
+ * Overrides |
+ * <a href="https://developer.chrome.com/multidevice/data-compression"> |
+ * Data Reduction Proxy</a> configuration parameters with a primary |
+ * proxy name, fallback proxy name, and a secure proxy check URL. Proxies |
+ * are specified as [scheme://]host[:port]. Used for testing. |
+ * @param primaryProxy the primary data reduction proxy to use. |
+ * @param fallbackProxy a fallback data reduction proxy to use. |
+ * @param secureProxyCheckUrl a URL to fetch to determine if using a secure |
+ * proxy is allowed. |
+ * @return the builder to facilitate chaining. |
+ * @hide |
+ */ |
+ public Builder setDataReductionProxyOptions( |
+ String primaryProxy, String fallbackProxy, String secureProxyCheckUrl) { |
+ if (primaryProxy.isEmpty() || fallbackProxy.isEmpty() |
+ || secureProxyCheckUrl.isEmpty()) { |
+ throw new IllegalArgumentException( |
+ "Primary and fallback proxies and check url must be set"); |
+ } |
+ putString(CronetEngineBuilderList.DATA_REDUCTION_PRIMARY_PROXY, primaryProxy); |
+ putString(CronetEngineBuilderList.DATA_REDUCTION_FALLBACK_PROXY, fallbackProxy); |
+ putString(CronetEngineBuilderList.DATA_REDUCTION_SECURE_PROXY_CHECK_URL, |
+ secureProxyCheckUrl); |
+ return this; |
+ } |
+ |
+ /** |
+ * Setting to disable HTTP cache. Some data may still be temporarily stored in memory. |
+ * Passed to {@link #enableHttpCache}. |
+ */ |
+ public static final int HTTP_CACHE_DISABLED = 0; |
+ |
+ /** |
+ * Setting to enable in-memory HTTP cache, including HTTP data. |
+ * Passed to {@link #enableHttpCache}. |
+ */ |
+ public static final int HTTP_CACHE_IN_MEMORY = 1; |
+ |
+ /** |
+ * Setting to enable on-disk cache, excluding HTTP data. |
+ * {@link #setStoragePath} must be called prior to passing this constant to |
+ * {@link #enableHttpCache}. |
+ */ |
+ public static final int HTTP_CACHE_DISK_NO_HTTP = 2; |
+ |
+ /** |
+ * Setting to enable on-disk cache, including HTTP data. |
+ * {@link #setStoragePath} must be called prior to passing this constant to |
+ * {@link #enableHttpCache}. |
+ */ |
+ public static final int HTTP_CACHE_DISK = 3; |
+ |
+ /** |
+ * Enables or disables caching of HTTP data and other information like QUIC |
+ * server information. |
+ * @param cacheMode control location and type of cached data. |
+ * @param maxSize maximum size in bytes used to cache data (advisory and maybe |
+ * exceeded at times). |
+ * @return the builder to facilitate chaining. |
+ */ |
+ public Builder enableHttpCache(int cacheMode, long maxSize) { |
+ if (cacheMode == HTTP_CACHE_DISK || cacheMode == HTTP_CACHE_DISK_NO_HTTP) { |
+ if (storagePath().isEmpty()) { |
+ throw new IllegalArgumentException("Storage path must be set"); |
+ } |
+ } else { |
+ if (!storagePath().isEmpty()) { |
+ throw new IllegalArgumentException("Storage path must be empty"); |
+ } |
+ } |
+ putBoolean(CronetEngineBuilderList.LOAD_DISABLE_CACHE, |
+ cacheMode == HTTP_CACHE_DISABLED || cacheMode == HTTP_CACHE_DISK_NO_HTTP); |
+ putLong(CronetEngineBuilderList.HTTP_CACHE_MAX_SIZE, maxSize); |
+ |
+ switch (cacheMode) { |
+ case HTTP_CACHE_DISABLED: |
+ return putString(CronetEngineBuilderList.HTTP_CACHE, |
+ CronetEngineBuilderList.HTTP_CACHE_DISABLED); |
+ case HTTP_CACHE_DISK_NO_HTTP: |
+ case HTTP_CACHE_DISK: |
+ return putString(CronetEngineBuilderList.HTTP_CACHE, |
+ CronetEngineBuilderList.HTTP_CACHE_DISK); |
+ |
+ case HTTP_CACHE_IN_MEMORY: |
+ return putString(CronetEngineBuilderList.HTTP_CACHE, |
+ CronetEngineBuilderList.HTTP_CACHE_MEMORY); |
+ } |
+ return this; |
+ } |
+ |
+ /** |
+ * Adds hint that {@code host} supports QUIC. |
+ * Note that {@link #enableHttpCache enableHttpCache} |
+ * ({@link HttpCache#DISK DISK}) is needed to take advantage of 0-RTT |
+ * connection establishment between sessions. |
+ * |
+ * @param host hostname of the server that supports QUIC. |
+ * @param port host of the server that supports QUIC. |
+ * @param alternatePort alternate port to use for QUIC. |
+ * @return the builder to facilitate chaining. |
+ */ |
+ public Builder addQuicHint(String host, int port, int alternatePort) { |
+ if (host.contains("/")) { |
+ throw new IllegalArgumentException("Illegal QUIC Hint Host: " + host); |
+ } |
+ try { |
+ JSONArray quicHints = mConfig.optJSONArray(CronetEngineBuilderList.QUIC_HINTS); |
+ if (quicHints == null) { |
+ quicHints = new JSONArray(); |
+ mConfig.put(CronetEngineBuilderList.QUIC_HINTS, quicHints); |
+ } |
+ |
+ JSONObject hint = new JSONObject(); |
+ hint.put(CronetEngineBuilderList.QUIC_HINT_HOST, host); |
+ hint.put(CronetEngineBuilderList.QUIC_HINT_PORT, port); |
+ hint.put(CronetEngineBuilderList.QUIC_HINT_ALT_PORT, alternatePort); |
+ quicHints.put(hint); |
+ } catch (JSONException e) { |
+ // Intentionally do nothing. |
+ } |
+ return this; |
+ } |
+ |
+ /** |
+ * Sets experimental QUIC connection options, overwriting any pre-existing |
+ * options. List of options is subject to change. |
+ * |
+ * @param quicConnectionOptions comma-separated QUIC options (for example |
+ * "PACE,IW10") to use if QUIC is enabled. |
+ * @return the builder to facilitate chaining. |
+ */ |
+ public Builder setExperimentalQuicConnectionOptions(String quicConnectionOptions) { |
+ return putString(CronetEngineBuilderList.QUIC_OPTIONS, quicConnectionOptions); |
+ } |
+ |
+ /** |
+ * Get JSON string representation of the builder. |
+ */ |
+ @Override |
+ public String toString() { |
+ return mConfig.toString(); |
+ } |
+ |
+ /** |
+ * Returns {@link Context} for builder. |
+ * |
+ * @return {@link Context} for builder. |
+ */ |
+ Context getContext() { |
+ return mContext; |
+ } |
+ |
+ /** |
+ * Sets a boolean value in the config. Returns a reference to the same |
+ * config object, so you can chain put calls together. |
+ * @return the builder to facilitate chaining. |
+ */ |
+ private Builder putBoolean(String key, boolean value) { |
+ try { |
+ mConfig.put(key, value); |
+ } catch (JSONException e) { |
+ // Intentionally do nothing. |
+ } |
+ return this; |
+ } |
+ |
+ /** |
+ * Sets a long value in the config. Returns a reference to the same |
+ * config object, so you can chain put calls together. |
+ * @return the builder to facilitate chaining. |
+ */ |
+ private Builder putLong(String key, long value) { |
+ try { |
+ mConfig.put(key, value); |
+ } catch (JSONException e) { |
+ // Intentionally do nothing. |
+ } |
+ return this; |
+ } |
+ |
+ /** |
+ * Sets a string value in the config. Returns a reference to the same |
+ * config object, so you can chain put calls together. |
+ * @return the builder to facilitate chaining. |
+ */ |
+ private Builder putString(String key, String value) { |
+ try { |
+ mConfig.put(key, value); |
+ } catch (JSONException e) { |
+ // Intentionally do nothing. |
+ } |
+ return this; |
+ } |
+ |
+ /** |
+ * Build a {@link CronetEngine} using this builder's configuration. |
+ */ |
+ public CronetEngine build() { |
+ return createContext(this); |
+ } |
+ } |
+ |
+ private static final String TAG = "UrlRequestFactory"; |
+ private static final String CRONET_URL_REQUEST_CONTEXT = |
+ "org.chromium.net.CronetUrlRequestContext"; |
+ |
+ /** |
+ * Creates a {@link UrlRequest} object. All callbacks will |
+ * be called on {@code executor}'s thread. {@code executor} must not run |
+ * tasks on the current thread to prevent blocking networking operations |
+ * and causing exceptions during shutdown. Request is given medium priority, |
+ * see {@link UrlRequest#REQUEST_PRIORITY_MEDIUM}. To specify other |
+ * priorities see {@link #createRequest(String, UrlRequestListener, |
+ * Executor, int priority)}. |
+ * |
+ * @param url {@link java.net.URL} for the request. |
+ * @param listener callback class that gets called on different events. |
+ * @param executor {@link Executor} on which all callbacks will be called. |
+ * @return new request. |
+ * @deprecated Use {@link Builder#build}. |
+ */ |
+ @Deprecated |
+ public abstract UrlRequest createRequest( |
+ String url, UrlRequestListener listener, Executor executor); |
+ |
+ /** |
+ * Creates a {@link UrlRequest} object. All callbacks will |
+ * be called on {@code executor}'s thread. {@code executor} must not run |
+ * tasks on the current thread to prevent blocking networking operations |
+ * and causing exceptions during shutdown. |
+ * |
+ * @param url {@link java.net.URL} for the request. |
+ * @param listener callback class that gets called on different events. |
+ * @param executor {@link Executor} on which all callbacks will be called. |
+ * @param priority priority of the request which should be one of the |
+ * {@link UrlRequest#REQUEST_PRIORITY_IDLE REQUEST_PRIORITY_*} |
+ * values. |
+ * @return new request. |
+ * @deprecated Use {@link Builder#build}. |
+ */ |
+ @Deprecated |
+ public abstract UrlRequest createRequest( |
+ String url, UrlRequestListener listener, Executor executor, int priority); |
+ |
+ /** |
+ * @return {@code true} if the engine is enabled. |
+ */ |
+ abstract boolean isEnabled(); |
+ |
+ /** |
+ * @return a human-readable version string of the engine. |
+ */ |
+ public abstract String getVersionString(); |
+ |
+ /** |
+ * Shuts down the {@link CronetEngine} if there are no active requests, |
+ * otherwise throws an exception. |
+ * |
+ * Cannot be called on network thread - the thread Cronet calls into |
+ * Executor on (which is different from the thread the Executor invokes |
+ * callbacks on). May block until all the {@code CronetEngine}'s |
+ * resources have been cleaned up. |
+ */ |
+ public abstract void shutdown(); |
+ |
+ /** |
+ * Starts NetLog logging to a file. The NetLog is useful for debugging. |
+ * The file can be viewed using a Chrome browser navigated to |
+ * chrome://net-internals/#import |
+ * @param fileName the complete file path. It must not be empty. If the file |
+ * exists, it is truncated before starting. If actively logging, |
+ * this method is ignored. |
+ * @param logAll {@code true} to include basic events, user cookies, |
+ * credentials and all transferred bytes in the log. |
+ * {@code false} to just include basic events. |
+ */ |
+ public abstract void startNetLogToFile(String fileName, boolean logAll); |
+ |
+ /** |
+ * Stops NetLog logging and flushes file to disk. If a logging session is |
+ * not in progress, this call is ignored. |
+ */ |
+ public abstract void stopNetLog(); |
+ |
+ /** |
+ * Enables the network quality estimator, which collects and reports |
+ * measurements of round trip time (RTT) and downstream throughput at |
+ * various layers of the network stack. After enabling the estimator, |
+ * listeners of RTT and throughput can be added with |
+ * {@link #addRttListener} and {@link #addThroughputListener} and |
+ * removed with {@link #removeRttListener} and |
+ * {@link #removeThroughputListener}. The estimator uses memory and CPU |
+ * only when enabled. |
+ * @param executor an executor that will be used to notified all |
+ * added RTT and throughput listeners. |
+ * @deprecated not really deprecated but hidden for now as it's a prototype. |
+ */ |
+ @Deprecated public abstract void enableNetworkQualityEstimator(Executor executor); |
+ |
+ /** |
+ * Enables the network quality estimator for testing. This must be called |
+ * before round trip time and throughput listeners are added. Set both |
+ * boolean parameters to false for default behavior. |
+ * @param useLocalHostRequests include requests to localhost in estimates. |
+ * @param useSmallerResponses include small responses in throughput estimates. |
+ * @param executor an {@link java.util.concurrent.Executor} on which all |
+ * listeners will be called. |
+ * @deprecated not really deprecated but hidden for now as it's a prototype. |
+ */ |
+ @Deprecated |
+ abstract void enableNetworkQualityEstimatorForTesting( |
+ boolean useLocalHostRequests, boolean useSmallerResponses, Executor executor); |
+ |
+ /** |
+ * Registers a listener that gets called whenever the network quality |
+ * estimator witnesses a sample round trip time. This must be called |
+ * after {@link #enableNetworkQualityEstimator}, and with throw an |
+ * exception otherwise. Round trip times may be recorded at various layers |
+ * of the network stack, including TCP, QUIC, and at the URL request layer. |
+ * The listener is called on the {@link java.util.concurrent.Executor} that |
+ * is passed to {@link #enableNetworkQualityEstimator}. |
+ * @param listener the listener of round trip times. |
+ * @deprecated not really deprecated but hidden for now as it's a prototype. |
+ */ |
+ @Deprecated public abstract void addRttListener(NetworkQualityRttListener listener); |
+ |
+ /** |
+ * Removes a listener of round trip times if previously registered with |
+ * {@link #addRttListener}. This should be called after a |
+ * {@link NetworkQualityRttListener} is added in order to stop receiving |
+ * observations. |
+ * @param listener the listener of round trip times. |
+ * @deprecated not really deprecated but hidden for now as it's a prototype. |
+ */ |
+ @Deprecated public abstract void removeRttListener(NetworkQualityRttListener listener); |
+ |
+ /** |
+ * Registers a listener that gets called whenever the network quality |
+ * estimator witnesses a sample throughput measurement. This must be called |
+ * after {@link #enableNetworkQualityEstimator}. Throughput observations |
+ * are computed by measuring bytes read over the active network interface |
+ * at times when at least one URL response is being received. The listener |
+ * is called on the {@link java.util.concurrent.Executor} that is passed to |
+ * {@link #enableNetworkQualityEstimator}. |
+ * @param listener the listener of throughput. |
+ * @deprecated not really deprecated but hidden for now as it's a prototype. |
+ */ |
+ @Deprecated |
+ public abstract void addThroughputListener(NetworkQualityThroughputListener listener); |
+ |
+ /** |
+ * Removes a listener of throughput. This should be called after a |
+ * {@link NetworkQualityThroughputListener} is added with |
+ * {@link #addThroughputListener} in order to stop receiving observations. |
+ * @param listener the listener of throughput. |
+ * @deprecated not really deprecated but hidden for now as it's a prototype. |
+ */ |
+ @Deprecated |
+ public abstract void removeThroughputListener(NetworkQualityThroughputListener listener); |
+ |
+ /** |
+ * Creates a {@link CronetEngine} with the given {@link Builder}. |
+ * @param context Android {@link Context}. |
+ * @param config engine configuration. |
+ * @deprecated Use {@link CronetEngine.Builder}. |
+ */ |
+ @Deprecated |
+ public static CronetEngine createContext(Builder config) { |
+ CronetEngine cronetEngine = null; |
+ if (config.userAgent().isEmpty()) { |
+ config.setUserAgent(UserAgent.from(config.getContext())); |
+ } |
+ if (!config.legacyMode()) { |
+ cronetEngine = createCronetEngine(config); |
+ } |
+ if (cronetEngine == null) { |
+ // TODO(mef): Fallback to stub implementation. Once stub |
+ // implementation is available merge with createCronetFactory. |
+ cronetEngine = createCronetEngine(config); |
+ } |
+ Log.i(TAG, "Using network stack: " + cronetEngine.getVersionString()); |
+ return cronetEngine; |
+ } |
+ |
+ private static CronetEngine createCronetEngine(Builder config) { |
+ CronetEngine cronetEngine = null; |
+ try { |
+ Class<? extends CronetEngine> engineClass = |
+ CronetEngine.class.getClassLoader() |
+ .loadClass(CRONET_URL_REQUEST_CONTEXT) |
+ .asSubclass(CronetEngine.class); |
+ Constructor<? extends CronetEngine> constructor = |
+ engineClass.getConstructor(Builder.class); |
+ CronetEngine possibleEngine = constructor.newInstance(config); |
+ if (possibleEngine.isEnabled()) { |
+ cronetEngine = possibleEngine; |
+ } |
+ } catch (ClassNotFoundException e) { |
+ // Leave as null. |
+ } catch (Exception e) { |
+ throw new IllegalStateException("Cannot instantiate: " + CRONET_URL_REQUEST_CONTEXT, e); |
+ } |
+ return cronetEngine; |
+ } |
+} |