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

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

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

Powered by Google App Engine
This is Rietveld 408576698