Chromium Code Reviews| 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..ef4383e8f090ff1b90dfd13c813615746fd3dc51 |
| --- /dev/null |
| +++ b/components/cronet/android/java/src/org/chromium/net/CronetEngine.java |
| @@ -0,0 +1,487 @@ |
| +// 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. |
|
xunjieli
2015/10/01 19:00:09
nit: s/http/HTTP.
pauljensen
2015/10/01 23:42:59
Done.
|
| + * @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 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 #executeRequest}. |
| + */ |
| + @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 #executeRequest}. |
| + */ |
| + @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(); |
| + |
| + /** |
| + * 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; |
| + } |
| +} |