OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
mef
2015/09/25 21:32:19
2015?
pauljensen
2015/09/28 14:18:12
Done.
| |
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 org.chromium.net; | |
6 | |
7 import android.content.Context; | |
8 import android.util.Log; | |
9 import android.util.Pair; | |
10 | |
11 import org.json.JSONArray; | |
12 import org.json.JSONException; | |
13 import org.json.JSONObject; | |
14 | |
15 import java.io.File; | |
16 import java.lang.reflect.Constructor; | |
17 import java.util.concurrent.Executor; | |
18 | |
19 /** | |
20 * An engine to process {@link UrlRequest}s, which uses the best HTTP stack | |
21 * available on the current platform. | |
22 */ | |
23 public abstract class CronetEngine { | |
24 /** | |
25 * A builder for {@link CronetEngine}s, which allows runtime configuration o f | |
26 * {@code CronetEngine}. Configuration options are set on the builder and | |
27 * then {@link #build} is called to create the {@code CronetEngine}. | |
28 */ | |
29 public static class Builder { | |
30 private final JSONObject mConfig; | |
31 private Context mContext; | |
32 | |
33 /** | |
34 * Default config enables SPDY, disables QUIC, SDCH and http cache. | |
35 */ | |
36 public Builder() { | |
37 mConfig = new JSONObject(); | |
38 enableLegacyMode(false); | |
39 enableQUIC(false); | |
40 enableHTTP2(true); | |
41 enableSDCH(false); | |
42 enableHttpCache(HTTP_CACHE_DISABLED, 0); | |
43 } | |
44 | |
45 /** | |
46 * Creates a config from a JSON string, which was serialized using | |
47 * {@link #toString}. | |
48 */ | |
49 public Builder(String json) throws JSONException { | |
50 mConfig = new JSONObject(json); | |
51 } | |
52 | |
53 /** | |
54 * Overrides the user-agent header for all requests. | |
55 * @return the config to facilitate chaining. | |
56 */ | |
57 public Builder setUserAgent(String userAgent) { | |
58 return putString(CronetEngineBuilderList.USER_AGENT, userAgent); | |
59 } | |
60 | |
61 String userAgent() { | |
62 return mConfig.optString(CronetEngineBuilderList.USER_AGENT); | |
63 } | |
64 | |
65 /** | |
66 * Sets directory for HTTP Cache and Cookie Storage. The directory must | |
67 * exist. | |
68 * @param value path to existing directory. | |
69 * @return the config to facilitate chaining. | |
70 */ | |
71 public Builder setStoragePath(String value) { | |
72 if (!new File(value).isDirectory()) { | |
73 throw new IllegalArgumentException( | |
74 "Storage path must be set to existing directory"); | |
75 } | |
76 | |
77 return putString(CronetEngineBuilderList.STORAGE_PATH, value); | |
78 } | |
79 | |
80 String storagePath() { | |
81 return mConfig.optString(CronetEngineBuilderList.STORAGE_PATH); | |
82 } | |
83 | |
84 /** | |
85 * Sets whether falling back to implementation based on system's | |
86 * {@link java.net.HttpURLConnection} implementation is enabled. | |
87 * Defaults to disabled. | |
88 * @return the config to facilitate chaining. | |
89 * @deprecated Not supported by the new API. | |
90 */ | |
91 @Deprecated | |
92 public Builder enableLegacyMode(boolean value) { | |
93 return putBoolean(CronetEngineBuilderList.ENABLE_LEGACY_MODE, value) ; | |
94 } | |
95 | |
96 boolean legacyMode() { | |
97 return mConfig.optBoolean(CronetEngineBuilderList.ENABLE_LEGACY_MODE ); | |
98 } | |
99 | |
100 /** | |
101 * Overrides the name of the native library backing Cronet. | |
102 * @return the config to facilitate chaining. | |
103 */ | |
104 Builder setLibraryName(String libName) { | |
105 return putString(CronetEngineBuilderList.NATIVE_LIBRARY_NAME, libNam e); | |
106 } | |
107 | |
108 String libraryName() { | |
109 return mConfig.optString(CronetEngineBuilderList.NATIVE_LIBRARY_NAME , "cronet"); | |
110 } | |
111 | |
112 /** | |
113 * Sets whether <a href="https://www.chromium.org/quic">QUIC</a> protoco l | |
114 * is enabled. Defaults to disabled. | |
115 * @return the config to facilitate chaining. | |
116 */ | |
117 public Builder enableQUIC(boolean value) { | |
118 return putBoolean(CronetEngineBuilderList.ENABLE_QUIC, value); | |
119 } | |
120 | |
121 /** | |
122 * Sets whether <a href="https://tools.ietf.org/html/rfc7540">HTTP/2</a> | |
123 * protocol is enabled. Defaults to enabled. | |
124 * @return the config to facilitate chaining. | |
mef
2015/09/25 21:32:19
Here and elsewhere:
@return the builder to facili
pauljensen
2015/09/28 14:18:12
Done.
| |
125 */ | |
126 public Builder enableHTTP2(boolean value) { | |
127 return putBoolean(CronetEngineBuilderList.ENABLE_SPDY, value); | |
128 } | |
129 | |
130 /** | |
131 * Sets whether | |
132 * <a | |
133 * href="https://lists.w3.org/Archives/Public/ietf-http-wg/2008JulSep/at t-0441/Shared_Dictionary_Compression_over_HTTP.pdf"> | |
134 * SDCH</a> compression is enabled. Defaults to disabled. | |
135 * @return the config to facilitate chaining. | |
136 */ | |
137 public Builder enableSDCH(boolean value) { | |
138 return putBoolean(CronetEngineBuilderList.ENABLE_SDCH, value); | |
139 } | |
140 | |
141 /** | |
142 * Enables | |
143 * <a href="https://developer.chrome.com/multidevice/data-compression">D ata | |
144 * Reduction Proxy</a>. Defaults to disabled. | |
145 * @param key key to use when authenticating with the proxy. | |
146 * @return the config to facilitate chaining. | |
147 */ | |
148 public Builder enableDataReductionProxy(String key) { | |
149 return (putString(CronetEngineBuilderList.DATA_REDUCTION_PROXY_KEY, key)); | |
150 } | |
151 | |
152 /** | |
153 * Overrides | |
154 * <a href="https://developer.chrome.com/multidevice/data-compression"> | |
155 * Data Reduction Proxy</a> configuration parameters with a primary | |
156 * proxy name, fallback proxy name, and a secure proxy check URL. Proxie s | |
157 * are specified as [scheme://]host[:port]. Used for testing. | |
158 * @param primaryProxy the primary data reduction proxy to use. | |
159 * @param fallbackProxy a fallback data reduction proxy to use. | |
160 * @param secureProxyCheckUrl a URL to fetch to determine if using a sec ure | |
161 * proxy is allowed. | |
162 * @return the config to facilitate chaining. | |
163 * @hide | |
164 */ | |
165 public Builder setDataReductionProxyOptions( | |
166 String primaryProxy, String fallbackProxy, String secureProxyChe ckUrl) { | |
167 if (primaryProxy.isEmpty() || fallbackProxy.isEmpty() | |
168 || secureProxyCheckUrl.isEmpty()) { | |
169 throw new IllegalArgumentException( | |
170 "Primary and fallback proxies and check url must be set" ); | |
171 } | |
172 putString(CronetEngineBuilderList.DATA_REDUCTION_PRIMARY_PROXY, prim aryProxy); | |
173 putString(CronetEngineBuilderList.DATA_REDUCTION_FALLBACK_PROXY, fal lbackProxy); | |
174 putString(CronetEngineBuilderList.DATA_REDUCTION_SECURE_PROXY_CHECK_ URL, | |
175 secureProxyCheckUrl); | |
176 return this; | |
177 } | |
178 | |
179 /** | |
180 * Setting to disable HTTP cache. Some data may still be temporarily sto red in memory. | |
181 * Passed to {@link #enableHttpCache}. | |
182 */ | |
183 public static final int HTTP_CACHE_DISABLED = 0; | |
184 | |
185 /** | |
186 * Setting to enable in-memory HTTP cache, including HTTP data. | |
187 * Passed to {@link #enableHttpCache}. | |
188 */ | |
189 public static final int HTTP_CACHE_IN_MEMORY = 1; | |
190 | |
191 /** | |
192 * Setting to enable on-disk cache, excluding HTTP data. | |
193 * {@link #setStoragePath} must be called prior to passing this constant to | |
194 * {@link #enableHttpCache}. | |
195 */ | |
196 public static final int HTTP_CACHE_DISK_NO_HTTP = 2; | |
197 | |
198 /** | |
199 * Setting to enable on-disk cache, including HTTP data. | |
200 * {@link #setStoragePath} must be called prior to passing this constant to | |
201 * {@link #enableHttpCache}. | |
202 */ | |
203 public static final int HTTP_CACHE_DISK = 3; | |
204 | |
205 /** | |
206 * Enables or disables caching of HTTP data and other information like Q UIC | |
207 * server information. | |
208 * @param cacheMode control location and type of cached data. | |
209 * @param maxSize maximum size used to cache data (advisory and maybe | |
210 * exceeded at times). | |
211 * @return the config to facilitate chaining. | |
212 */ | |
213 public Builder enableHttpCache(int cacheMode, long maxSize) { | |
214 if (cacheMode == HTTP_CACHE_DISK || cacheMode == HTTP_CACHE_DISK_NO_ HTTP) { | |
215 if (storagePath().isEmpty()) { | |
216 throw new IllegalArgumentException("Storage path must be set "); | |
217 } | |
218 } else { | |
219 if (!storagePath().isEmpty()) { | |
220 throw new IllegalArgumentException("Storage path must be emp ty"); | |
221 } | |
222 } | |
223 putBoolean(CronetEngineBuilderList.LOAD_DISABLE_CACHE, | |
224 cacheMode == HTTP_CACHE_DISABLED || cacheMode == HTTP_CACHE_ DISK_NO_HTTP); | |
225 putLong(CronetEngineBuilderList.HTTP_CACHE_MAX_SIZE, maxSize); | |
226 | |
227 switch (cacheMode) { | |
228 case HTTP_CACHE_DISABLED: | |
229 return putString(CronetEngineBuilderList.HTTP_CACHE, | |
230 CronetEngineBuilderList.HTTP_CACHE_DISABLED); | |
231 case HTTP_CACHE_DISK_NO_HTTP: | |
232 case HTTP_CACHE_DISK: | |
233 return putString(CronetEngineBuilderList.HTTP_CACHE, | |
234 CronetEngineBuilderList.HTTP_CACHE_DISK); | |
235 | |
236 case HTTP_CACHE_IN_MEMORY: | |
237 return putString(CronetEngineBuilderList.HTTP_CACHE, | |
238 CronetEngineBuilderList.HTTP_CACHE_MEMORY); | |
239 } | |
240 return this; | |
241 } | |
242 | |
243 /** | |
244 * Adds hint that {@code host} supports QUIC. | |
245 * Note that {@link #enableHttpCache enableHttpCache} | |
246 * ({@link HttpCache#DISK DISK}) is needed to take advantage of 0-RTT | |
247 * connection establishment between sessions. | |
248 * | |
249 * @param host hostname of the server that supports QUIC. | |
250 * @param port host of the server that supports QUIC. | |
251 * @param alternatePort alternate port to use for QUIC. | |
252 * @return the config to facilitate chaining. | |
253 */ | |
254 public Builder addQuicHint(String host, int port, int alternatePort) { | |
255 if (host.contains("/")) { | |
256 throw new IllegalArgumentException("Illegal QUIC Hint Host: " + host); | |
257 } | |
258 try { | |
259 JSONArray quicHints = mConfig.optJSONArray(CronetEngineBuilderLi st.QUIC_HINTS); | |
260 if (quicHints == null) { | |
261 quicHints = new JSONArray(); | |
262 mConfig.put(CronetEngineBuilderList.QUIC_HINTS, quicHints); | |
263 } | |
264 | |
265 JSONObject hint = new JSONObject(); | |
266 hint.put(CronetEngineBuilderList.QUIC_HINT_HOST, host); | |
267 hint.put(CronetEngineBuilderList.QUIC_HINT_PORT, port); | |
268 hint.put(CronetEngineBuilderList.QUIC_HINT_ALT_PORT, alternatePo rt); | |
269 quicHints.put(hint); | |
270 } catch (JSONException e) { | |
271 // Intentionally do nothing. | |
272 } | |
273 return this; | |
274 } | |
275 | |
276 /** | |
277 * Sets experimental QUIC connection options, overwriting any pre-existi ng | |
278 * options. List of options is subject to change. | |
279 * | |
280 * @param quicConnectionOptions comma-separated QUIC options (for exampl e | |
281 * "PACE,IW10") to use if QUIC is enabled. | |
282 * @return the config to facilitate chaining. | |
283 */ | |
284 public Builder setExperimentalQuicConnectionOptions(String quicConnectio nOptions) { | |
285 return putString(CronetEngineBuilderList.QUIC_OPTIONS, quicConnectio nOptions); | |
286 } | |
287 | |
288 /** | |
289 * Get JSON string representation of the config. | |
mef
2015/09/25 21:32:18
builder
pauljensen
2015/09/28 14:18:12
Done.
| |
290 */ | |
291 @Override | |
292 public String toString() { | |
293 return mConfig.toString(); | |
294 } | |
295 | |
296 /** | |
297 * Sets a boolean value in the config. Returns a reference to the same | |
298 * config object, so you can chain put calls together. | |
299 * @return the config to facilitate chaining. | |
300 */ | |
301 private Builder putBoolean(String key, boolean value) { | |
302 try { | |
303 mConfig.put(key, value); | |
304 } catch (JSONException e) { | |
305 // Intentionally do nothing. | |
306 } | |
307 return this; | |
308 } | |
309 | |
310 /** | |
311 * Sets a long value in the config. Returns a reference to the same | |
312 * config object, so you can chain put calls together. | |
313 * @return the config to facilitate chaining. | |
314 */ | |
315 private Builder putLong(String key, long value) { | |
316 try { | |
317 mConfig.put(key, value); | |
318 } catch (JSONException e) { | |
319 // Intentionally do nothing. | |
320 } | |
321 return this; | |
322 } | |
323 | |
324 /** | |
325 * Sets a string value in the config. Returns a reference to the same | |
326 * config object, so you can chain put calls together. | |
327 * @return the config to facilitate chaining. | |
328 */ | |
329 private Builder putString(String key, String value) { | |
330 try { | |
331 mConfig.put(key, value); | |
332 } catch (JSONException e) { | |
333 // Intentionally do nothing. | |
334 } | |
335 return this; | |
336 } | |
337 | |
338 /** | |
339 * Build a {@link CronetEngine} using this builder's configuration. | |
340 */ | |
341 public CronetEngine build(Context context) { | |
342 return createContext(context, this); | |
343 } | |
344 } | |
345 | |
346 private static final String TAG = "UrlRequestFactory"; | |
347 private static final String CRONET_URL_REQUEST_CONTEXT = | |
348 "org.chromium.net.CronetUrlRequestContext"; | |
349 | |
350 /** | |
351 * Creates a {@link UrlRequest} given a {@link Builder}, and starts | |
mef
2015/09/25 21:32:19
I think this should link to UrlRequest.Builder.
pauljensen
2015/09/28 14:18:12
It seems to link properly as "Builder"; the link a
| |
352 * the request, all callbacks go to the {@link UrlRequestListener}. | |
353 */ | |
354 public UrlRequest executeRequest(UrlRequest.Builder builder) { | |
355 final UrlRequest request = createRequest( | |
356 builder.mUrl, builder.mListener, builder.mExecutor, builder.mPri ority); | |
357 if (builder.mMethod != null) { | |
358 request.setHttpMethod(builder.mMethod); | |
359 } | |
360 if (builder.mDisableCache) { | |
361 request.disableCache(); | |
362 } | |
363 for (Pair<String, String> header : builder.mRequestHeaders) { | |
mef
2015/09/25 21:32:19
should we switch from Pair<String, String> to Map.
pauljensen
2015/09/28 14:18:12
CronetUrlRequest uses an ArrayList<Pair<String, St
mef
2015/09/28 17:13:37
Ack. That will change once https://codereview.chro
| |
364 request.addHeader(header.first, header.second); | |
365 } | |
366 if (builder.mUploadDataProvider != null) { | |
367 request.setUploadDataProvider( | |
368 builder.mUploadDataProvider, builder.mUploadDataProviderExec utor); | |
369 } | |
370 request.start(); | |
371 return request; | |
372 } | |
373 | |
374 /** | |
375 * Creates a {@link UrlRequest} object. All callbacks will | |
376 * be called on {@code executor}'s thread. {@code executor} must not run | |
377 * tasks on the current thread to prevent blocking networking operations | |
378 * and causing exceptions during shutdown. Request is given medium priority, | |
379 * see {@link UrlRequest#REQUEST_PRIORITY_MEDIUM}. To specify other | |
380 * priorities see {@link #createRequest(String, UrlRequestListener, | |
381 * Executor, int priority)}. | |
382 * | |
383 * @param url {@link java.net.URL} for the request. | |
384 * @param listener callback class that gets called on different events. | |
385 * @param executor {@link Executor} on which all callbacks will be called. | |
386 * @return new request. | |
387 * @deprecated Use {@link #executeRequest}. | |
388 */ | |
389 @Deprecated | |
390 public abstract UrlRequest createRequest( | |
391 String url, UrlRequestListener listener, Executor executor); | |
392 | |
393 /** | |
394 * Creates a {@link UrlRequest} object. All callbacks will | |
395 * be called on {@code executor}'s thread. {@code executor} must not run | |
396 * tasks on the current thread to prevent blocking networking operations | |
397 * and causing exceptions during shutdown. | |
398 * | |
399 * @param url {@link java.net.URL} for the request. | |
400 * @param listener callback class that gets called on different events. | |
401 * @param executor {@link Executor} on which all callbacks will be called. | |
402 * @param priority priority of the request which should be one of the | |
403 * {@link UrlRequest#REQUEST_PRIORITY_IDLE REQUEST_PRIORITY_*} | |
404 * values. | |
405 * @return new request. | |
406 * @deprecated Use {@link #executeRequest}. | |
407 */ | |
408 @Deprecated | |
409 public abstract UrlRequest createRequest( | |
410 String url, UrlRequestListener listener, Executor executor, int prio rity); | |
411 | |
412 /** | |
413 * @return {@code true} if the engine is enabled. | |
414 */ | |
415 abstract boolean isEnabled(); | |
416 | |
417 /** | |
418 * @return a human-readable version string of the engine. | |
419 */ | |
420 public abstract String getVersionString(); | |
421 | |
422 /** | |
423 * Shuts down the {@link CronetEngine} if there are no active requests, | |
424 * otherwise throws an exception. | |
425 * | |
426 * Cannot be called on network thread - the thread Cronet calls into | |
427 * Executor on (which is different from the thread the Executor invokes | |
428 * callbacks on). May block until all the {@code CronetEngine}'s | |
429 * resources have been cleaned up. | |
430 */ | |
431 public abstract void shutdown(); | |
432 | |
433 /** | |
434 * Starts NetLog logging to a file. The NetLog is useful for debugging. | |
435 * The file can be viewed using a Chrome browser navigated to | |
436 * chrome://net-internals/#import | |
437 * @param fileName the complete file path. It must not be empty. If the file | |
438 * exists, it is truncated before starting. If actively logging, | |
439 * this method is ignored. | |
440 * @param logAll {@code true} to include basic events, user cookies, | |
441 * credentials and all transferred bytes in the log. | |
442 * {@code false} to just include basic events. | |
443 */ | |
444 public abstract void startNetLogToFile(String fileName, boolean logAll); | |
445 | |
446 /** | |
447 * Stops NetLog logging and flushes file to disk. If a logging session is | |
448 * not in progress, this call is ignored. | |
449 */ | |
450 public abstract void stopNetLog(); | |
451 | |
452 /** | |
453 * Creates a {@link CronetEngine} with the given {@link Builder}. | |
454 * @param context Android {@link Context}. | |
455 * @param config engine configuration. | |
456 * @deprecated Use {@link CronetEngine.Builder}. | |
457 */ | |
458 @Deprecated | |
459 public static CronetEngine createContext(Context context, Builder config) { | |
460 CronetEngine cronetEngine = null; | |
461 if (config.userAgent().isEmpty()) { | |
462 config.setUserAgent(UserAgent.from(context)); | |
463 } | |
464 if (!config.legacyMode()) { | |
465 cronetEngine = createCronetEngine(context, config); | |
466 } | |
467 if (cronetEngine == null) { | |
468 // TODO(mef): Fallback to stub implementation. Once stub | |
469 // implementation is available merge with createCronetFactory. | |
470 cronetEngine = createCronetEngine(context, config); | |
471 } | |
472 Log.i(TAG, "Using network stack: " + cronetEngine.getVersionString()); | |
473 return cronetEngine; | |
474 } | |
475 | |
476 private static CronetEngine createCronetEngine(Context context, Builder conf ig) { | |
477 CronetEngine cronetEngine = null; | |
478 try { | |
479 Class<? extends CronetEngine> engineClass = | |
480 CronetEngine.class.getClassLoader() | |
481 .loadClass(CRONET_URL_REQUEST_CONTEXT) | |
482 .asSubclass(CronetEngine.class); | |
483 Constructor<? extends CronetEngine> constructor = | |
484 engineClass.getConstructor(Context.class, Builder.class); | |
485 CronetEngine possibleEngine = constructor.newInstance(context, confi g); | |
486 if (possibleEngine.isEnabled()) { | |
487 cronetEngine = possibleEngine; | |
488 } | |
489 } catch (ClassNotFoundException e) { | |
490 // Leave as null. | |
491 } catch (Exception e) { | |
492 throw new IllegalStateException("Cannot instantiate: " + CRONET_URL_ REQUEST_CONTEXT, e); | |
493 } | |
494 return cronetEngine; | |
495 } | |
496 } | |
OLD | NEW |