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