• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 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 package android.net.http;
6 
7 import android.annotation.SuppressLint;
8 import android.content.Context;
9 import android.net.Network;
10 
11 import org.chromium.net.ExperimentalCronetEngine;
12 import org.chromium.net.ICronetEngineBuilder;
13 import org.chromium.net.ApiVersion;
14 
15 import androidx.annotation.NonNull;
16 import androidx.annotation.Nullable;
17 
18 import com.android.internal.annotations.VisibleForTesting;
19 
20 import java.io.IOException;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.net.URL;
24 import java.net.URLConnection;
25 import java.net.URLStreamHandlerFactory;
26 import java.time.Instant;
27 import java.util.Set;
28 import java.util.concurrent.Executor;
29 
30 import javax.net.ssl.HttpsURLConnection;
31 
32 /**
33  * An engine to process {@link UrlRequest}s, which uses the best HTTP stack available on the current
34  * platform. An instance of this class can be created using {@link Builder}.
35  */
36 // SuppressLint: Making the HttpEngine AutoCloseable indicates to the developers that it's
37 // expected to be used in a try-with-resource clause. This in turn promotes local, narrowly
38 // scoped instances of HttpEngine. That's the exact opposite of how HttpEngine is supposed
39 // to be used - it should live in an application-wide scope and be reused multiple times across
40 // the lifespan of the app.
41 @SuppressLint("NotCloseable")
42 public abstract class HttpEngine {
43 
44     /**
45      * {@hide}
46      */
HttpEngine()47     protected HttpEngine() {}
48 
49     /**
50      * Returns a new {@link Builder} object that facilitates creating a {@link HttpEngine}.
51      *
52      * {@hide}
53      */
54     @NonNull
builder(@onNull Context context)55     public static Builder builder(@NonNull Context context) {
56         return new Builder(context);
57     }
58 
59     /**
60      * A builder for {@link HttpEngine}s, which allows runtime configuration of
61      * {@link HttpEngine}. Configuration options are set on the builder and
62      * then {@link #build} is called to create the {@link HttpEngine}.
63      */
64     // NOTE(kapishnikov): In order to avoid breaking the existing API clients, all future methods
65     // added to this class and other API classes must have default implementation.
66     // SuppressLint: Builder can not be final since ExperimentalHttpEngine.Builder inherit this
67     // Builder.
68     @SuppressLint("StaticFinalBuilder")
69     public static class Builder {
70 
71         /**
72          * Reference to the actual builder implementation. {@hide exclude from JavaDoc}.
73          */
74         protected final IHttpEngineBuilder mBuilderDelegate;
75 
76         /**
77          * Constructs a {@link Builder} object that facilitates creating a
78          * {@link HttpEngine}. The default configuration enables HTTP/2 and
79          * QUIC, but disables the HTTP cache.
80          *
81          * @param context Android {@link Context}, which is used by {@link Builder} to retrieve the
82          * application context. A reference to only the application context will be kept, so as to
83          * avoid extending the lifetime of {@code context} unnecessarily.
84          */
Builder(@onNull Context context)85         public Builder(@NonNull Context context) {
86             this(createBuilderDelegate(context));
87         }
88 
89         /**
90          * Constructs {@link Builder} with a given delegate that provides the actual implementation
91          * of the {@code Builder} methods. This constructor is used only by the internal
92          * implementation.
93          *
94          * @param builderDelegate delegate that provides the actual implementation.
95          *
96          * {@hide}
97          */
98         @VisibleForTesting
Builder(@onNull IHttpEngineBuilder builderDelegate)99         public Builder(@NonNull IHttpEngineBuilder builderDelegate) {
100             mBuilderDelegate = builderDelegate;
101         }
102 
103         /**
104          * Constructs a default User-Agent string including the system build version, model and id,
105          * and the HTTP stack version.
106          *
107          * @return User-Agent string.
108          */
109         // SuppressLint: API to get default user agent that could include system build version,
110         // model, Id, and Cronet version.
111         @NonNull @SuppressLint("GetterOnBuilder")
getDefaultUserAgent()112         public String getDefaultUserAgent() {
113             return mBuilderDelegate.getDefaultUserAgent();
114         }
115 
116         /**
117          * Overrides the User-Agent header for all requests. An explicitly set User-Agent header
118          * (set using {@link UrlRequest.Builder#addHeader}) will override a value set using this
119          * function.
120          *
121          * @param userAgent the User-Agent string to use for all requests.
122          * @return the builder to facilitate chaining.
123          */
124         // SuppressLint: Value is passed to JNI code and maintained by JNI code after build
125         @NonNull @SuppressLint("MissingGetterMatchingBuilder")
setUserAgent(@onNull String userAgent)126         public Builder setUserAgent(@NonNull String userAgent) {
127             mBuilderDelegate.setUserAgent(userAgent);
128             return this;
129         }
130 
131         /**
132          * Sets directory for HTTP Cache and Cookie Storage. The directory must
133          * exist.
134          * <p>
135          * <b>NOTE:</b> Do not use the same storage directory with more than one
136          * {@link HttpEngine} at a time. Access to the storage directory does
137          * not support concurrent access by multiple {@link HttpEngine} instances.
138          *
139          * @param value path to existing directory.
140          * @return the builder to facilitate chaining.
141          */
142         // SuppressLint: Value is passed to JNI code and maintained by JNI code after build
143         @NonNull @SuppressLint("MissingGetterMatchingBuilder")
setStoragePath(@onNull String value)144         public Builder setStoragePath(@NonNull String value) {
145             mBuilderDelegate.setStoragePath(value);
146             return this;
147         }
148 
149         /**
150          * Sets whether <a href="https://www.chromium.org/quic">QUIC</a> protocol
151          * is enabled. Defaults to enabled.
152          *
153          * @param value {@code true} to enable QUIC, {@code false} to disable.
154          * @return the builder to facilitate chaining.
155          */
156         // SuppressLint: Value is passed to JNI code and maintained by JNI code after build
157         @NonNull @SuppressLint("MissingGetterMatchingBuilder")
setEnableQuic(boolean value)158         public Builder setEnableQuic(boolean value) {
159             mBuilderDelegate.setEnableQuic(value);
160             return this;
161         }
162 
163         /**
164          * Sets whether <a href="https://tools.ietf.org/html/rfc7540">HTTP/2</a> protocol is
165          * enabled. Defaults to enabled.
166          *
167          * @param value {@code true} to enable HTTP/2, {@code false} to disable.
168          * @return the builder to facilitate chaining.
169          */
170         // SuppressLint: Value is passed to JNI code and maintained by JNI code after build
171         @NonNull @SuppressLint("MissingGetterMatchingBuilder")
setEnableHttp2(boolean value)172         public Builder setEnableHttp2(boolean value) {
173             mBuilderDelegate.setEnableHttp2(value);
174             return this;
175         }
176 
177         /**
178          * Sets whether <a href="https://tools.ietf.org/html/rfc7932">Brotli</a> compression is
179          * enabled. If enabled, Brotli will be advertised in Accept-Encoding request headers.
180          * Defaults to disabled.
181          *
182          * @param value {@code true} to enable Brotli, {@code false} to disable.
183          * @return the builder to facilitate chaining.
184          */
185         // SuppressLint: Value is passed to JNI code and maintained by JNI code after build
186         @NonNull @SuppressLint("MissingGetterMatchingBuilder")
setEnableBrotli(boolean value)187         public Builder setEnableBrotli(boolean value) {
188             mBuilderDelegate.setEnableBrotli(value);
189             return this;
190         }
191 
192         /**
193          * Setting to disable HTTP cache. Some data may still be temporarily stored in memory.
194          * Passed to {@link #setEnableHttpCache}.
195          */
196         public static final int HTTP_CACHE_DISABLED = 0;
197 
198         /**
199          * Setting to enable in-memory HTTP cache, including HTTP data.
200          * Passed to {@link #setEnableHttpCache}.
201          */
202         public static final int HTTP_CACHE_IN_MEMORY = 1;
203 
204         /**
205          * Setting to enable on-disk cache, excluding HTTP data.
206          * {@link #setStoragePath} must be called prior to passing this constant to
207          * {@link #setEnableHttpCache}.
208          */
209         public static final int HTTP_CACHE_DISK_NO_HTTP = 2;
210 
211         /**
212          * Setting to enable on-disk cache, including HTTP data.
213          * {@link #setStoragePath} must be called prior to passing this constant to
214          * {@link #setEnableHttpCache}.
215          */
216         public static final int HTTP_CACHE_DISK = 3;
217 
218         /**
219          * Enables or disables caching of HTTP data and other information like QUIC server
220          * information.
221          *
222          * @param cacheMode control location and type of cached data. Must be one of {@link
223          * #HTTP_CACHE_DISABLED HTTP_CACHE_*}.
224          * @param maxSize maximum size in bytes used to cache data (advisory and maybe exceeded at
225          * times).
226          * @return the builder to facilitate chaining.
227          */
228         // SuppressLint: Value is passed to JNI code and maintained by JNI code after build
229         @NonNull @SuppressLint("MissingGetterMatchingBuilder")
setEnableHttpCache(int cacheMode, long maxSize)230         public Builder setEnableHttpCache(int cacheMode, long maxSize) {
231             mBuilderDelegate.setEnableHttpCache(cacheMode, maxSize);
232             return this;
233         }
234 
235         /**
236          * Adds hint that {@code host} supports QUIC.
237          * Note that {@link #setEnableHttpCache enableHttpCache}
238          * ({@link #HTTP_CACHE_DISK}) is needed to take advantage of 0-RTT
239          * connection establishment between sessions.
240          *
241          * @param host hostname of the server that supports QUIC.
242          * @param port host of the server that supports QUIC.
243          * @param alternatePort alternate port to use for QUIC.
244          * @return the builder to facilitate chaining.
245          */
246         // SuppressLint: Value is passed to JNI code and maintained by JNI code after build
247         @NonNull @SuppressLint("MissingGetterMatchingBuilder")
addQuicHint(@onNull String host, int port, int alternatePort)248         public Builder addQuicHint(@NonNull String host, int port, int alternatePort) {
249             mBuilderDelegate.addQuicHint(host, port, alternatePort);
250             return this;
251         }
252 
253         /**
254          * Pins a set of public keys for a given host. By pinning a set of public keys, {@code
255          * pinsSha256}, communication with {@code hostName} is required to authenticate with a
256          * certificate with a public key from the set of pinned ones. An app can pin the public key
257          * of the root certificate, any of the intermediate certificates or the end-entry
258          * certificate. Authentication will fail and secure communication will not be established if
259          * none of the public keys is present in the host's certificate chain, even if the host
260          * attempts to authenticate with a certificate allowed by the device's trusted store of
261          * certificates.
262          *
263          * <p>Calling this method multiple times with the same host name overrides the previously
264          * set pins for the host.
265          *
266          * <p>More information about the public key pinning can be found in <a
267          * href="https://tools.ietf.org/html/rfc7469">RFC 7469</a>.
268          *
269          * @param hostName name of the host to which the public keys should be pinned. A host that
270          * consists only of digits and the dot character is treated as invalid.
271          * @param pinsSha256 a set of pins. Each pin is the SHA-256 cryptographic hash of the
272          * DER-encoded ASN.1 representation of the Subject Public Key Info (SPKI) of the host's
273          * X.509 certificate. Use {@link java.security.cert.Certificate#getPublicKey()
274          * Certificate.getPublicKey()} and {@link java.security.Key#getEncoded() Key.getEncoded()}
275          * to obtain DER-encoded ASN.1 representation of the SPKI. Although, the method does not
276          * mandate the presence of the backup pin that can be used if the control of the primary
277          * private key has been lost, it is highly recommended to supply one.
278          * @param includeSubdomains indicates whether the pinning policy should be applied to
279          *                          subdomains of {@code hostName}.
280          * @param expirationInstant specifies the expiration instant for the pins.
281          * @return the builder to facilitate chaining.
282          * @throws NullPointerException if any of the input parameters are {@code null}.
283          * @throws IllegalArgumentException if the given host name is invalid or {@code pinsSha256}
284          * contains a byte array that does not represent a valid SHA-256 hash.
285          */
286         // SuppressLint: Value is passed to JNI code and maintained by JNI code after build
287         @NonNull @SuppressLint("MissingGetterMatchingBuilder")
addPublicKeyPins(@onNull String hostName, @NonNull Set<byte[]> pinsSha256, boolean includeSubdomains, @NonNull Instant expirationInstant)288         public Builder addPublicKeyPins(@NonNull String hostName, @NonNull Set<byte[]> pinsSha256,
289                 boolean includeSubdomains, @NonNull Instant expirationInstant) {
290             mBuilderDelegate.addPublicKeyPins(
291                     hostName, pinsSha256, includeSubdomains, expirationInstant);
292             return this;
293         }
294 
295         /**
296          * Enables or disables public key pinning bypass for local trust anchors. Disabling the
297          * bypass for local trust anchors is highly discouraged since it may prohibit the app from
298          * communicating with the pinned hosts. E.g., a user may want to send all traffic through an
299          * SSL enabled proxy by changing the device proxy settings and adding the proxy certificate
300          * to the list of local trust anchor. Disabling the bypass will most likely prevent the app
301          * from sending any traffic to the pinned hosts. For more information see 'How does key
302          * pinning interact with local proxies and filters?' at
303          * https://www.chromium.org/Home/chromium-security/security-faq
304          *
305          * @param value {@code true} to enable the bypass, {@code false} to disable.
306          * @return the builder to facilitate chaining.
307          */
308         // SuppressLint: Value is passed to JNI code and maintained by JNI code after build
309         @NonNull @SuppressLint("MissingGetterMatchingBuilder")
setEnablePublicKeyPinningBypassForLocalTrustAnchors(boolean value)310         public Builder setEnablePublicKeyPinningBypassForLocalTrustAnchors(boolean value) {
311             mBuilderDelegate.setEnablePublicKeyPinningBypassForLocalTrustAnchors(value);
312             return this;
313         }
314 
315         /**
316          * Configures the behavior of the HTTP stack when using QUIC. For more details, see
317          * documentation of {@link QuicOptions} and the individual methods
318          * of {@link QuicOptions.Builder}.
319          *
320          * <p>Only relevant if {@link #setEnableQuic(boolean)} is enabled.
321          *
322          * @return the builder to facilitate chaining.
323          */
324         // SuppressLint: Value is passed to JNI code and maintained by JNI code after build
325         @NonNull @SuppressLint("MissingGetterMatchingBuilder")
326         @QuicOptions.Experimental
setQuicOptions(@onNull QuicOptions quicOptions)327         public Builder setQuicOptions(@NonNull QuicOptions quicOptions) {
328             mBuilderDelegate.setQuicOptions(quicOptions);
329             return this;
330         }
331 
332         /**
333          * Configures the behavior of hostname lookup. For more details, see documentation
334          * of {@link DnsOptions} and the individual methods of {@link DnsOptions.Builder}.
335          *
336          * <p>Only relevant if {@link #setEnableQuic(boolean)} is enabled.
337          *
338          * @return the builder to facilitate chaining.
339          */
340         // SuppressLint: Value is passed to JNI code and maintained by JNI code after build
341         @NonNull @SuppressLint("MissingGetterMatchingBuilder")
342         @DnsOptions.Experimental
setDnsOptions(@onNull DnsOptions dnsOptions)343         public Builder setDnsOptions(@NonNull DnsOptions dnsOptions) {
344             mBuilderDelegate.setDnsOptions(dnsOptions);
345             return this;
346         }
347 
348         /**
349          * Configures the behavior of connection migration. For more details, see documentation
350          * of {@link ConnectionMigrationOptions} and the individual methods of {@link
351          * ConnectionMigrationOptions.Builder}.
352          *
353          * <p>Only relevant if {@link #setEnableQuic(boolean)} is enabled.
354          *
355          * @return the builder to facilitate chaining.
356          */
357         // SuppressLint: Value is passed to JNI code and maintained by JNI code after build
358         @NonNull @SuppressLint("MissingGetterMatchingBuilder")
359         @ConnectionMigrationOptions.Experimental
setConnectionMigrationOptions( @onNull ConnectionMigrationOptions connectionMigrationOptions)360         public Builder setConnectionMigrationOptions(
361                 @NonNull ConnectionMigrationOptions connectionMigrationOptions) {
362             mBuilderDelegate.setConnectionMigrationOptions(connectionMigrationOptions);
363             return this;
364         }
365 
366         /**
367          * Build a {@link HttpEngine} using this builder's configuration.
368          * @return constructed {@link HttpEngine}.
369          */
370         @NonNull
build()371         public HttpEngine build() {
372             return mBuilderDelegate.build();
373         }
374 
375         /**
376          * Creates an implementation of {@link IHttpEngineBuilder} that can be used
377          * to delegate the builder calls to.
378          *
379          * @param context Android Context to use.
380          * @return the created {@link IHttpEngineBuilder}.
381          */
createBuilderDelegate(Context context)382         private static IHttpEngineBuilder createBuilderDelegate(Context context) {
383             try {
384                 Class<?> cronetClazz = context.getClassLoader().loadClass(
385                         "android.net.connectivity.org.chromium.net.impl.NativeCronetEngineBuilderImpl");
386                 Class<?> aospClazz = context.getClassLoader().loadClass(
387                         "android.net.http.CronetEngineBuilderWrapper");
388 
389                 ICronetEngineBuilder cronetBuilderImpl = (ICronetEngineBuilder)
390                         cronetClazz.getConstructor(Context.class).newInstance(context);
391                 IHttpEngineBuilder aospBuilderImpl = (IHttpEngineBuilder)
392                         aospClazz.getConstructor(ExperimentalCronetEngine.Builder.class).newInstance(new ExperimentalCronetEngine.Builder(cronetBuilderImpl));
393                 return aospBuilderImpl;
394             } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
395                 throw new IllegalArgumentException(e);
396             }
397         }
398     }
399 
400     /**
401      * @return a human-readable version string of the engine.
402      */
403     @NonNull
getVersionString()404     public static String getVersionString() {
405         return ApiVersion.getCronetVersion();
406     }
407 
408     /**
409      * Shuts down the {@link HttpEngine} if there are no active requests,
410      * otherwise throws an exception.
411      *
412      * Cannot be called on network thread - the thread the HTTP stack calls into
413      * Executor on (which is different from the thread the Executor invokes
414      * callbacks on). May block until all the {@link HttpEngine} resources have been cleaned up.
415      */
shutdown()416     public abstract void shutdown();
417 
418     /**
419      * Binds the engine to the specified network. All requests created through this engine
420      * will use the network associated to this handle. If this network disconnects all requests will
421      * fail, the exact error will depend on the stage of request processing when the network
422      * disconnects.
423      *
424      * @param network the network to bind the engine to. Specify {@code null} to unbind.
425      */
bindToNetwork(@ullable Network network)426     public void bindToNetwork(@Nullable Network network) {}
427 
428     /**
429      * Establishes a new connection to the resource specified by the {@link URL} {@code url}.
430      * <p>
431      * <b>Note:</b> This {@link java.net.HttpURLConnection} implementation is subject to certain
432      * limitations, see {@link #createUrlStreamHandlerFactory} for details.
433      *
434      * @param url URL of resource to connect to.
435      * @return an {@link java.net.HttpURLConnection} instance implemented
436      *     by this {@link HttpEngine}.
437      * @throws IOException if an error occurs while opening the connection.
438      */
439     // SuppressLint since this is for interface parity with j.n.URLConnection
440     @SuppressLint("AndroidUri") @NonNull
openConnection( @uppressLint"AndroidUri") @onNull URL url)441     public abstract URLConnection openConnection(
442             @SuppressLint("AndroidUri") @NonNull URL url) throws IOException;
443 
444     /**
445      * Creates a {@link URLStreamHandlerFactory} to handle HTTP and HTTPS
446      * traffic. An instance of this class can be installed via
447      * {@link URL#setURLStreamHandlerFactory} thus using this {@link HttpEngine} by default for
448      * all requests created via {@link URL#openConnection}.
449      * <p>
450      * This {@link java.net.HttpURLConnection} implementation does not implement all features
451      * offered by the API:
452      * <ul>
453      * <li>the HTTP cache installed via
454      *     {@link HttpResponseCache#install(java.io.File, long) HttpResponseCache.install()}</li>
455      * <li>the HTTP authentication method installed via
456      *     {@link java.net.Authenticator#setDefault}</li>
457      * <li>the HTTP cookie storage installed via {@link java.net.CookieHandler#setDefault}</li>
458      * </ul>
459      * <p>
460      * While we support and encourages requests using the HTTPS protocol, we don't provide support
461      * for the {@link HttpsURLConnection} API. This lack of support also includes not using certain
462      * HTTPS features provided via {@link HttpsURLConnection}:
463      * <ul>
464      * <li>the HTTPS hostname verifier installed via {@link
465      *   HttpsURLConnection#setDefaultHostnameVerifier(javax.net.ssl.HostnameVerifier)
466      *     HttpsURLConnection.setDefaultHostnameVerifier()}</li>
467      * <li>the HTTPS socket factory installed via {@link
468      *   HttpsURLConnection#setDefaultSSLSocketFactory(javax.net.ssl.SSLSocketFactory)
469      *     HttpsURLConnection.setDefaultSSLSocketFactory()}</li>
470      * </ul>
471      *
472      * @return an {@link URLStreamHandlerFactory} instance implemented by this
473      *         {@link HttpEngine}.
474      */
475     // SuppressLint since this is for interface parity with j.n.URLStreamHandlerFactory
476     @SuppressLint("AndroidUri") @NonNull
createUrlStreamHandlerFactory()477     public abstract URLStreamHandlerFactory createUrlStreamHandlerFactory();
478 
479     /**
480      * Creates a builder for {@link UrlRequest}. All callbacks for
481      * generated {@link UrlRequest} objects will be invoked on
482      * {@code executor}'s threads. {@code executor} must not run tasks on the
483      * thread calling {@link Executor#execute} to prevent blocking networking
484      * operations and causing exceptions during shutdown.
485      *
486      * @param url URL for the generated requests.
487      * @param executor {@link Executor} on which all callbacks will be invoked.
488      * @param callback callback object that gets invoked on different events.
489      */
490     @NonNull
newUrlRequestBuilder( @onNull String url, @NonNull Executor executor, @NonNull UrlRequest.Callback callback)491     public abstract UrlRequest.Builder newUrlRequestBuilder(
492             @NonNull String url, @NonNull Executor executor, @NonNull UrlRequest.Callback callback);
493 
494     /**
495      * Creates a builder for {@link BidirectionalStream} objects. All callbacks for
496      * generated {@code BidirectionalStream} objects will be invoked on
497      * {@code executor}. {@code executor} must not run tasks on the
498      * current thread, otherwise the networking operations may block and exceptions
499      * may be thrown at shutdown time.
500      *
501      * @param url URL for the generated streams.
502      * @param executor the {@link Executor} on which {@code callback} methods will be invoked.
503      * @param callback the {@link BidirectionalStream.Callback} object that gets invoked upon
504      * different events occurring.
505      *
506      * @return the created builder.
507      */
508     @NonNull
newBidirectionalStreamBuilder( @onNull String url, @NonNull Executor executor, @NonNull BidirectionalStream.Callback callback)509     public abstract BidirectionalStream.Builder newBidirectionalStreamBuilder(
510             @NonNull String url, @NonNull Executor executor,
511             @NonNull BidirectionalStream.Callback callback);
512 
513 }
514