Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(405)

Side by Side Diff: components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java

Issue 2339223002: Cronet API Refactoring (Closed)
Patch Set: Rebase & Conflict Resolution Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 package org.chromium.net.impl;
5
6 import android.content.Context;
7 import android.support.annotation.IntDef;
8 import android.support.annotation.VisibleForTesting;
9 import android.util.Log;
10
11 import static org.chromium.net.CronetEngine.Builder.HTTP_CACHE_DISABLED;
12 import static org.chromium.net.CronetEngine.Builder.HTTP_CACHE_DISK;
13 import static org.chromium.net.CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP;
14 import static org.chromium.net.CronetEngine.Builder.HTTP_CACHE_IN_MEMORY;
15
16 import org.chromium.net.CronetEngine;
17 import org.chromium.net.ExperimentalCronetEngine;
18 import org.chromium.net.ICronetEngineBuilder;
19
20 import java.io.File;
21 import java.lang.annotation.Retention;
22 import java.lang.annotation.RetentionPolicy;
23 import java.lang.reflect.Constructor;
24 import java.net.IDN;
25 import java.util.Date;
26 import java.util.HashSet;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.regex.Pattern;
31
32 /**
33 * Implementation of {@link ICronetEngineBuilder}.
34 */
35 public class CronetEngineBuilderImpl extends ICronetEngineBuilder {
36 /**
37 * A hint that a host supports QUIC.
38 */
39 public static class QuicHint {
40 // The host.
41 final String mHost;
42 // Port of the server that supports QUIC.
43 final int mPort;
44 // Alternate protocol port.
45 final int mAlternatePort;
46
47 QuicHint(String host, int port, int alternatePort) {
48 mHost = host;
49 mPort = port;
50 mAlternatePort = alternatePort;
51 }
52 }
53
54 /**
55 * A public key pin.
56 */
57 public static class Pkp {
58 // Host to pin for.
59 final String mHost;
60 // Array of SHA-256 hashes of keys.
61 final byte[][] mHashes;
62 // Should pin apply to subdomains?
63 final boolean mIncludeSubdomains;
64 // When the pin expires.
65 final Date mExpirationDate;
66
67 Pkp(String host, byte[][] hashes, boolean includeSubdomains, Date expira tionDate) {
68 mHost = host;
69 mHashes = hashes;
70 mIncludeSubdomains = includeSubdomains;
71 mExpirationDate = expirationDate;
72 }
73 }
74
75 private static final String TAG = "CronetEngineBuilder";
76 private static final String NATIVE_CRONET_IMPL_CLASS =
77 "org.chromium.net.impl.CronetUrlRequestContext";
78 private static final String JAVA_CRONET_IMPL_CLASS = "org.chromium.net.impl. JavaCronetEngine";
79 private static final Pattern INVALID_PKP_HOST_NAME = Pattern.compile("^[0-9\ \.]*$");
80
81 // Private fields are simply storage of configuration for the resulting Cron etEngine.
82 // See setters below for verbose descriptions.
83 private final Context mContext;
84 private final List<QuicHint> mQuicHints = new LinkedList<>();
85 private final List<Pkp> mPkps = new LinkedList<>();
86 private boolean mPublicKeyPinningBypassForLocalTrustAnchorsEnabled;
87 private String mUserAgent;
88 private String mStoragePath;
89 private boolean mLegacyModeEnabled;
90 private CronetEngine.Builder.LibraryLoader mLibraryLoader;
91 private String mLibraryName;
92 private boolean mQuicEnabled;
93 private boolean mHttp2Enabled;
94 private boolean mSdchEnabled;
95 private String mDataReductionProxyKey;
96 private String mDataReductionProxyPrimaryProxy;
97 private String mDataReductionProxyFallbackProxy;
98 private String mDataReductionProxySecureProxyCheckUrl;
99 private boolean mDisableCache;
100 private int mHttpCacheMode;
101 private long mHttpCacheMaxSize;
102 private String mExperimentalOptions;
103 private long mMockCertVerifier;
104 private boolean mNetworkQualityEstimatorEnabled;
105 private String mCertVerifierData;
106
107 /**
108 * Default config enables SPDY, disables QUIC, SDCH and HTTP cache.
109 * @param context Android {@link Context} for engine to use.
110 */
111 public CronetEngineBuilderImpl(Context context) {
112 mContext = context;
113 setLibraryName("cronet");
114 enableLegacyMode(false);
115 enableQuic(false);
116 enableHttp2(true);
117 enableSdch(false);
118 enableHttpCache(HTTP_CACHE_DISABLED, 0);
119 enableNetworkQualityEstimator(false);
120 enablePublicKeyPinningBypassForLocalTrustAnchors(true);
121 }
122
123 @Override
124 public String getDefaultUserAgent() {
125 return UserAgent.from(mContext);
126 }
127
128 @Override
129 public CronetEngineBuilderImpl setUserAgent(String userAgent) {
130 mUserAgent = userAgent;
131 return this;
132 }
133
134 String getUserAgent() {
135 return mUserAgent;
136 }
137
138 @Override
139 public CronetEngineBuilderImpl setStoragePath(String value) {
140 if (!new File(value).isDirectory()) {
141 throw new IllegalArgumentException("Storage path must be set to exis ting directory");
142 }
143 mStoragePath = value;
144 return this;
145 }
146
147 String storagePath() {
148 return mStoragePath;
149 }
150
151 @Override
152 public CronetEngineBuilderImpl enableLegacyMode(boolean value) {
153 mLegacyModeEnabled = value;
154 return this;
155 }
156
157 private boolean legacyMode() {
158 return mLegacyModeEnabled;
159 }
160
161 /**
162 * Overrides the name of the native library backing Cronet.
163 * @param libName the name of the native library backing Cronet.
164 * @return the builder to facilitate chaining.
165 */
166 public CronetEngineBuilderImpl setLibraryName(String libName) {
167 mLibraryName = libName;
168 return this;
169 }
170
171 String libraryName() {
172 return mLibraryName;
173 }
174
175 @Override
176 public CronetEngineBuilderImpl setLibraryLoader(CronetEngine.Builder.Library Loader loader) {
177 mLibraryLoader = loader;
178 return this;
179 }
180
181 CronetEngine.Builder.LibraryLoader libraryLoader() {
182 return mLibraryLoader;
183 }
184
185 @Override
186 public CronetEngineBuilderImpl enableQuic(boolean value) {
187 mQuicEnabled = value;
188 return this;
189 }
190
191 boolean quicEnabled() {
192 return mQuicEnabled;
193 }
194
195 /**
196 * Constructs default QUIC User Agent Id string including application name
197 * and Cronet version. Returns empty string if QUIC is not enabled.
198 *
199 * @return QUIC User Agent ID string.
200 */
201 String getDefaultQuicUserAgentId() {
202 return mQuicEnabled ? UserAgent.getQuicUserAgentIdFrom(mContext) : "";
203 }
204
205 @Override
206 public CronetEngineBuilderImpl enableHttp2(boolean value) {
207 mHttp2Enabled = value;
208 return this;
209 }
210
211 boolean http2Enabled() {
212 return mHttp2Enabled;
213 }
214
215 @Override
216 public CronetEngineBuilderImpl enableSdch(boolean value) {
217 mSdchEnabled = value;
218 return this;
219 }
220
221 boolean sdchEnabled() {
222 return mSdchEnabled;
223 }
224
225 @Override
226 public CronetEngineBuilderImpl enableDataReductionProxy(String key) {
227 mDataReductionProxyKey = key;
228 return this;
229 }
230
231 String dataReductionProxyKey() {
232 return mDataReductionProxyKey;
233 }
234
235 @Override
236 public CronetEngineBuilderImpl setDataReductionProxyOptions(
237 String primaryProxy, String fallbackProxy, String secureProxyCheckUr l) {
238 if (primaryProxy.isEmpty() || fallbackProxy.isEmpty() || secureProxyChec kUrl.isEmpty()) {
239 throw new IllegalArgumentException(
240 "Primary and fallback proxies and check url must be set");
241 }
242 mDataReductionProxyPrimaryProxy = primaryProxy;
243 mDataReductionProxyFallbackProxy = fallbackProxy;
244 mDataReductionProxySecureProxyCheckUrl = secureProxyCheckUrl;
245 return this;
246 }
247
248 String dataReductionProxyPrimaryProxy() {
249 return mDataReductionProxyPrimaryProxy;
250 }
251
252 String dataReductionProxyFallbackProxy() {
253 return mDataReductionProxyFallbackProxy;
254 }
255
256 String dataReductionProxySecureProxyCheckUrl() {
257 return mDataReductionProxySecureProxyCheckUrl;
258 }
259
260 @IntDef({
261 HTTP_CACHE_DISABLED, HTTP_CACHE_IN_MEMORY, HTTP_CACHE_DISK_NO_HTTP, HTTP_CACHE_DISK,
262 })
263 @Retention(RetentionPolicy.SOURCE)
264 public @interface HttpCacheSetting {}
265
266 @Override
267 public CronetEngineBuilderImpl enableHttpCache(@HttpCacheSetting int cacheMo de, long maxSize) {
268 if (cacheMode == HTTP_CACHE_DISK || cacheMode == HTTP_CACHE_DISK_NO_HTTP ) {
269 if (storagePath() == null) {
270 throw new IllegalArgumentException("Storage path must be set");
271 }
272 } else {
273 if (storagePath() != null) {
274 throw new IllegalArgumentException("Storage path must not be set ");
275 }
276 }
277 mDisableCache = (cacheMode == HTTP_CACHE_DISABLED || cacheMode == HTTP_C ACHE_DISK_NO_HTTP);
278 mHttpCacheMaxSize = maxSize;
279
280 switch (cacheMode) {
281 case HTTP_CACHE_DISABLED:
282 mHttpCacheMode = HttpCacheType.DISABLED;
283 break;
284 case HTTP_CACHE_DISK_NO_HTTP:
285 case HTTP_CACHE_DISK:
286 mHttpCacheMode = HttpCacheType.DISK;
287 break;
288 case HTTP_CACHE_IN_MEMORY:
289 mHttpCacheMode = HttpCacheType.MEMORY;
290 break;
291 default:
292 throw new IllegalArgumentException("Unknown cache mode");
293 }
294 return this;
295 }
296
297 boolean cacheDisabled() {
298 return mDisableCache;
299 }
300
301 long httpCacheMaxSize() {
302 return mHttpCacheMaxSize;
303 }
304
305 int httpCacheMode() {
306 return mHttpCacheMode;
307 }
308
309 @Override
310 public CronetEngineBuilderImpl addQuicHint(String host, int port, int altern atePort) {
311 if (host.contains("/")) {
312 throw new IllegalArgumentException("Illegal QUIC Hint Host: " + host );
313 }
314 mQuicHints.add(new QuicHint(host, port, alternatePort));
315 return this;
316 }
317
318 List<QuicHint> quicHints() {
319 return mQuicHints;
320 }
321
322 @Override
323 public CronetEngineBuilderImpl addPublicKeyPins(String hostName, Set<byte[]> pinsSha256,
324 boolean includeSubdomains, Date expirationDate) {
325 if (hostName == null) {
326 throw new NullPointerException("The hostname cannot be null");
327 }
328 if (pinsSha256 == null) {
329 throw new NullPointerException("The set of SHA256 pins cannot be nul l");
330 }
331 if (expirationDate == null) {
332 throw new NullPointerException("The pin expiration date cannot be nu ll");
333 }
334 String idnHostName = validateHostNameForPinningAndConvert(hostName);
335 // Convert the pin to BASE64 encoding. The hash set will eliminate dupli cations.
336 Set<byte[]> hashes = new HashSet<>(pinsSha256.size());
337 for (byte[] pinSha256 : pinsSha256) {
338 if (pinSha256 == null || pinSha256.length != 32) {
339 throw new IllegalArgumentException("Public key pin is invalid");
340 }
341 hashes.add(pinSha256);
342 }
343 // Add new element to PKP list.
344 mPkps.add(new Pkp(idnHostName, hashes.toArray(new byte[hashes.size()][]) , includeSubdomains,
345 expirationDate));
346 return this;
347 }
348
349 /**
350 * Returns list of public key pins.
351 * @return list of public key pins.
352 */
353 List<Pkp> publicKeyPins() {
354 return mPkps;
355 }
356
357 @Override
358 public CronetEngineBuilderImpl enablePublicKeyPinningBypassForLocalTrustAnch ors(boolean value) {
359 mPublicKeyPinningBypassForLocalTrustAnchorsEnabled = value;
360 return this;
361 }
362
363 boolean publicKeyPinningBypassForLocalTrustAnchorsEnabled() {
364 return mPublicKeyPinningBypassForLocalTrustAnchorsEnabled;
365 }
366
367 /**
368 * Checks whether a given string represents a valid host name for PKP and co nverts it
369 * to ASCII Compatible Encoding representation according to RFC 1122, RFC 11 23 and
370 * RFC 3490. This method is more restrictive than required by RFC 7469. Thus , a host
371 * that contains digits and the dot character only is considered invalid.
372 *
373 * Note: Currently Cronet doesn't have native implementation of host name va lidation that
374 * can be used. There is code that parses a provided URL but doesn't e nsure its
375 * correctness. The implementation relies on {@code getaddrinfo} funct ion.
376 *
377 * @param hostName host name to check and convert.
378 * @return true if the string is a valid host name.
379 * @throws IllegalArgumentException if the the given string does not represe nt a valid
380 * hostname.
381 */
382 private static String validateHostNameForPinningAndConvert(String hostName)
383 throws IllegalArgumentException {
384 if (INVALID_PKP_HOST_NAME.matcher(hostName).matches()) {
385 throw new IllegalArgumentException("Hostname " + hostName + " is ill egal."
386 + " A hostname should not consist of digits and/or dots only .");
387 }
388 // Workaround for crash, see crbug.com/634914
389 if (hostName.length() > 255) {
390 throw new IllegalArgumentException("Hostname " + hostName + " is too long."
391 + " The name of the host does not comply with RFC 1122 and R FC 1123.");
392 }
393 try {
394 return IDN.toASCII(hostName, IDN.USE_STD3_ASCII_RULES);
395 } catch (IllegalArgumentException ex) {
396 throw new IllegalArgumentException("Hostname " + hostName + " is ill egal."
397 + " The name of the host does not comply with RFC 1122 and R FC 1123.");
398 }
399 }
400
401 @Override
402 public CronetEngineBuilderImpl setExperimentalOptions(String options) {
403 mExperimentalOptions = options;
404 return this;
405 }
406
407 public String experimentalOptions() {
408 return mExperimentalOptions;
409 }
410
411 /**
412 * Sets a native MockCertVerifier for testing. See
413 * {@code MockCertVerifier.createMockCertVerifier} for a method that
414 * can be used to create a MockCertVerifier.
415 * @param mockCertVerifier pointer to native MockCertVerifier.
416 * @return the builder to facilitate chaining.
417 */
418 @VisibleForTesting
419 public CronetEngineBuilderImpl setMockCertVerifierForTesting(long mockCertVe rifier) {
420 mMockCertVerifier = mockCertVerifier;
421 return this;
422 }
423
424 long mockCertVerifier() {
425 return mMockCertVerifier;
426 }
427
428 /**
429 * @return true if the network quality estimator has been enabled for
430 * this builder.
431 */
432 boolean networkQualityEstimatorEnabled() {
433 return mNetworkQualityEstimatorEnabled;
434 }
435
436 @Override
437 public CronetEngineBuilderImpl setCertVerifierData(String certVerifierData) {
438 mCertVerifierData = certVerifierData;
439 return this;
440 }
441
442 @Override
443 public CronetEngineBuilderImpl enableNetworkQualityEstimator(boolean value) {
444 mNetworkQualityEstimatorEnabled = value;
445 return this;
446 }
447
448 String certVerifierData() {
449 return mCertVerifierData;
450 }
451
452 /**
453 * Returns {@link Context} for builder.
454 *
455 * @return {@link Context} for builder.
456 */
457 Context getContext() {
458 return mContext;
459 }
460
461 @Override
462 public ExperimentalCronetEngine build() {
463 final ClassLoader loader = getClass().getClassLoader();
464 if (getUserAgent() == null) {
465 setUserAgent(getDefaultUserAgent());
466 }
467
468 ExperimentalCronetEngine cronetEngine = null;
469 if (!legacyMode()) {
470 cronetEngine = createNativeCronetEngine(loader);
471 }
472 if (cronetEngine == null) {
473 cronetEngine = createJavaCronetEngine(loader);
474 }
475
476 if (cronetEngine == null) {
477 Log.i(TAG,
478 "Class loader " + loader + " couldn't find any Cronet engine implementation.");
479 } else {
480 Log.i(TAG, loader.toString() + " found Cronet engine implementation "
481 + cronetEngine.getClass() + ". Network stack version "
482 + cronetEngine.getVersionString());
483 // Clear MOCK_CERT_VERIFIER reference if there is any, since
484 // the ownership has been transferred to the engine.
485 mMockCertVerifier = 0;
486 }
487 return cronetEngine;
488 }
489
490 private ExperimentalCronetEngine createNativeCronetEngine(ClassLoader loader ) {
491 return createCronetEngine(loader, NATIVE_CRONET_IMPL_CLASS, this);
492 }
493
494 private ExperimentalCronetEngine createJavaCronetEngine(ClassLoader loader) {
495 return createCronetEngine(loader, JAVA_CRONET_IMPL_CLASS, getUserAgent() );
496 }
497
498 private ExperimentalCronetEngine createCronetEngine(
499 ClassLoader loader, String name, Object argument) {
500 ExperimentalCronetEngine cronetEngine = null;
501 try {
502 Class<? extends ExperimentalCronetEngine> engineClass =
503 loader.loadClass(name).asSubclass(ExperimentalCronetEngine.c lass);
504 Constructor<? extends ExperimentalCronetEngine> constructor =
505 engineClass.getConstructor(argument.getClass());
506 cronetEngine = constructor.newInstance(argument);
507 } catch (ClassNotFoundException e) {
508 // Leave as null.
509 Log.i(TAG, "Class loader " + loader + " cannot find Cronet engine im plementation: "
510 + name + ". Will try to find an alternative implemen tation.");
511 } catch (Exception e) {
512 throw new IllegalStateException("Cannot instantiate: " + name, e);
513 }
514 return cronetEngine;
515 }
516 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698