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

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

Issue 586143002: Initial implementation of Cronet Async API. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Renamed UrlRequestFactory into UrlRequestContext, addressed some comments. Created 6 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.net; 5 package org.chromium.net;
6 6
7 import android.util.Log;
8
9 import org.chromium.base.CalledByNative;
10 import org.chromium.base.JNINamespace;
11
12 import java.net.URL;
13 import java.nio.ByteBuffer;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.concurrent.Executor;
19
7 /** 20 /**
8 * UrlRequest using Chromium HTTP stack implementation. 21 * UrlRequest using Chromium HTTP stack implementation.
9 */ 22 */
10 public class CronetUrlRequest implements UrlRequest { 23 @JNINamespace("cronet")
24 final class CronetUrlRequest implements UrlRequest {
25 /** Native adapter object, owned by UrlRequest. */
26 private long mUrlRequestAdapter;
27 private final CronetUrlRequestContext mRequestContext;
28 private final List<String> mUrlChain = new ArrayList<String>();
29 private final int mPriority;
30 private final UrlRequestListener mListener;
31 private final Executor mExecutor;
32 private NativeResponseInfo mResponseInfo;
33 private boolean mStarted = false;
34 private boolean mCanceled = false;
35 private OnDataReceivedRunnable mOnDataReceivedTask;
36
37 final class OnDataReceivedRunnable implements Runnable {
38 ByteBuffer mByteBuffer;
39 public void run() {
40 if (isCanceled()) {
41 return;
42 }
43 try {
44 mListener.onDataReceived(CronetUrlRequest.this,
45 mResponseInfo, mByteBuffer);
46 mByteBuffer = null;
47 if (!isCanceled()) {
48 nativeReceiveData(mUrlRequestAdapter);
49 }
50 } catch (Exception e) {
51 onCalledByNativeException(e);
52 }
53 }
54 }
55
56 final class NativeResponseInfo implements ExtendedResponseInfo {
57 private final String[] mUrlChain;
58 private final int mHttpStatusCode;
59 private final ResponseHeadersMap mAllHeaders = new ResponseHeadersMap();
60 private final boolean mWasCached;
61 private final String mNegotiatedProtocol;
62 private long mTotalReceivedBytes = 0;
63
64 NativeResponseInfo(String[] urlChain, int httpStatusCode,
65 boolean wasCached, String negotiatedProtocol) {
66 mUrlChain = urlChain;
67 mHttpStatusCode = httpStatusCode;
68 mWasCached = wasCached;
69 mNegotiatedProtocol = negotiatedProtocol;
70 }
71
72 @Override
73 public String getUrl() {
74 return mUrlChain[mUrlChain.length - 1];
75 }
76
77 @Override
78 public String[] getUrlChain() {
79 return mUrlChain;
80 }
81
82 @Override
83 public int getHttpStatusCode() {
84 return mHttpStatusCode;
85 }
86
87 @Override
88 public Map<String, List<String>> getAllHeaders() {
89 return mAllHeaders;
90 }
91
92 @Override
93 public boolean wasCached() {
94 return mWasCached;
95 }
96
97 @Override
98 public String getNegotiatedProtocol() {
99 return mNegotiatedProtocol;
100 }
101
102 @Override
103 public long getTotalReceivedBytes() {
104 return mTotalReceivedBytes;
105 }
106 };
107
108 CronetUrlRequest(CronetUrlRequestContext requestContext,
109 String url, int priority,
110 UrlRequestListener listener,
111 Executor executor) {
112 if (requestContext == null) {
113 throw new NullPointerException("Context is required");
114 }
115 if (url == null) {
116 throw new NullPointerException("URL is required");
117 }
118 if (listener == null) {
119 throw new NullPointerException("Listener is required");
120 }
121 if (executor == null) {
122 throw new NullPointerException("Executor is required");
123 }
124
125 mRequestContext = requestContext;
126 mUrlChain.add(url);
127 mPriority = convertRequestPriority(priority);
128 mUrlRequestAdapter = nativeCreateRequestAdapter(
129 mRequestContext.getCronetUrlRequestContextAdapter(),
130 url,
131 mPriority);
132 mListener = listener;
133 mExecutor = executor;
134 }
135
11 @Override 136 @Override
12 public void setHttpMethod(String method) { 137 public void setHttpMethod(String method) {
13 138 checkNotStarted();
mmenke 2014/10/21 16:17:54 Should also check that we're not done (mUrlRequest
mef 2014/10/21 21:27:47 Done.
139 if (method == null) {
140 throw new NullPointerException("method is required");
141 }
142 nativeSetHttpMethod(mUrlRequestAdapter, method);
14 } 143 }
15 144
16 @Override 145 @Override
17 public void addHeader(String header, String value) { 146 public void addHeader(String header, String value) {
18 147 checkNotStarted();
148 if (header == null || value == null) {
149 throw new NullPointerException("Invalid header");
150 }
151 nativeAddHeader(mUrlRequestAdapter, header, value);
19 } 152 }
20 153
21 @Override 154 @Override
22 public void start(UrlRequestListener listener) { 155 public void start() {
23 156 checkNotStarted();
157 mStarted = true;
158 nativeStart(mUrlRequestAdapter);
24 } 159 }
25 160
26 @Override 161 @Override
27 public void cancel() { 162 public void cancel() {
mmenke 2014/10/21 16:17:54 If already cancelled, should either do nothing or
mef 2014/10/21 21:27:47 Done.
28 163 mCanceled = true;
164 nativeCancel(mUrlRequestAdapter);
mmenke 2014/10/21 16:17:53 Perhaps get rid of onCanceled and just destroy the
mef 2014/10/21 21:27:47 Per offline discussion we can't do that.
29 } 165 }
30 166
31 @Override 167 @Override
32 public boolean isCanceled() { 168 public boolean isCanceled() {
33 return false; 169 return mCanceled;
34 } 170 }
35 171
36 @Override 172 @Override
37 public void pause() { 173 public void pause() {
38 174 throw new UnsupportedOperationException("Not implemented yet");
39 } 175 }
40 176
41 @Override 177 @Override
42 public boolean isPaused() { 178 public boolean isPaused() {
43 return false; 179 return false;
44 } 180 }
45 181
46 @Override 182 @Override
47 public void resume() { 183 public void resume() {
48 184 throw new UnsupportedOperationException("Not implemented yet");
185 }
186
187 private static int convertRequestPriority(int priority) {
188 switch (priority) {
189 case REQUEST_PRIORITY_IDLE:
190 return ChromiumUrlRequestPriority.IDLE;
191 case REQUEST_PRIORITY_LOWEST:
192 return ChromiumUrlRequestPriority.LOWEST;
193 case REQUEST_PRIORITY_LOW:
194 return ChromiumUrlRequestPriority.LOW;
195 case REQUEST_PRIORITY_MEDIUM:
196 return ChromiumUrlRequestPriority.MEDIUM;
197 case REQUEST_PRIORITY_HIGHEST:
198 return ChromiumUrlRequestPriority.HIGHEST;
199 default:
200 return ChromiumUrlRequestPriority.MEDIUM;
201 }
202 }
203
204 private NativeResponseInfo prepareResponseInfo() {
205 NativeResponseInfo responseInfo = new NativeResponseInfo(
206 mUrlChain.toArray(new String[mUrlChain.size()]),
207 nativeGetHttpStatusCode(mUrlRequestAdapter),
208 nativeGetWasCached(mUrlRequestAdapter),
209 nativeGetNegotiatedProtocol(mUrlRequestAdapter));
210 nativeGetAllHeaders(mUrlRequestAdapter, responseInfo.mAllHeaders);
211 return responseInfo;
212 }
213
214 private void checkNotStarted() {
215 if (mStarted) {
216 throw new IllegalStateException("Request is already started.");
217 }
218 }
219
220 private void destroyRequestAdapter() {
221 if (mUrlRequestAdapter != 0) {
222 nativeDestroyRequestAdapter(mUrlRequestAdapter);
223 mUrlRequestAdapter = 0;
224 } else {
225 throw new IllegalStateException("Request is already destroyed.");
226 }
227 }
228
229 /**
230 * If @CalledByNative method throws an exception, request gets cancelled
231 * and exception could be retrieved from request using getException().
232 */
233 private void onCalledByNativeException(Exception e) {
234 UrlRequestException requestError = new UrlRequestException(
235 "CalledByNative method has thrown an exception", e);
236 Log.e(CronetUrlRequestContext.LOG_TAG,
237 "Exception in CalledByNative method", e);
238 try {
239 mListener.onError(this, mResponseInfo, requestError);
240 cancel();
241 } catch (Exception cancelException) {
242 Log.e(CronetUrlRequestContext.LOG_TAG,
243 "Exception trying to cancel request", cancelException);
244 }
245 }
246
247 // Private methods called by the native code.
248
249 /**
250 * Called before following redirects. The redirect will automatically be
251 * followed, unless the request is paused or cancelled during this
252 * callback. If the redirect response has a body, it will be ignored.
253 * This will only be called between start and onResponseStarted.
254 *
255 * @param newLocation Location where request is redirected.
256 */
257 @SuppressWarnings("unused")
258 @CalledByNative
259 private void onRedirect(final String newLocation) {
260 mUrlChain.add(newLocation);
261 final NativeResponseInfo responseInfo = prepareResponseInfo();
262 Runnable task = new Runnable() {
263 public void run() {
mmenke 2014/10/21 16:17:53 If cancelled, should do nothing. Hrm...Suddenly o
mef 2014/10/21 21:27:47 Done.
264 try {
265 mListener.onRedirect(CronetUrlRequest.this, responseInfo,
266 new URL(newLocation));
267 if (!isCanceled()) {
268 nativeFollowDeferredRedirect(mUrlRequestAdapter);
269 }
270 } catch (Exception e) {
271 onCalledByNativeException(e);
272 }
273 }
274 };
275 mExecutor.execute(task);
276 }
277
278 /**
279 * Called when the final set of headers, after all redirects,
280 * is received. Can only be called once for each request.
281 */
282 @SuppressWarnings("unused")
283 @CalledByNative
284 private void onResponseStarted() {
285 mResponseInfo = prepareResponseInfo();
286 Runnable task = new Runnable() {
287 public void run() {
288 try {
mmenke 2014/10/21 16:17:54 Check we aren't cancelled.
mef 2014/10/21 21:27:47 Done.
289 mListener.onResponseStarted(CronetUrlRequest.this,
290 mResponseInfo);
291 if (!isCanceled()) {
292 nativeReceiveData(mUrlRequestAdapter);
293 }
294 } catch (Exception e) {
295 onCalledByNativeException(e);
296 }
297 }
298 };
299 mExecutor.execute(task);
300 }
301
302 /**
303 * Called whenever data is received. The ByteBuffer remains
304 * valid only until listener callback. Or if the callback
305 * pauses the request, it remains valid until the request is resumed.
306 * Cancelling the request also invalidates the buffer.
307 *
308 * @param byteBuffer Received data.
309 */
310 @SuppressWarnings("unused")
311 @CalledByNative
312 private void onDataReceived(final ByteBuffer byteBuffer) {
313 if (mOnDataReceivedTask == null) {
314 mOnDataReceivedTask = new OnDataReceivedRunnable();
mmenke 2014/10/21 16:17:54 Why is this not inlined, but the others are? I'm
mef 2014/10/21 21:27:47 clm@ has expressed a concern that onDataReceived i
315 }
316 mOnDataReceivedTask.mByteBuffer = byteBuffer;
317 mExecutor.execute(mOnDataReceivedTask);
318 }
319
320 /**
321 * Called when request is canceled, no callbacks are called.
322 */
323 @SuppressWarnings("unused")
324 @CalledByNative
325 private void onCanceled() {
326 Runnable task = new Runnable() {
327 public void run() {
328 destroyRequestAdapter();
329 }
330 };
331 mExecutor.execute(task);
332 }
333
334 /**
335 * Called when request is complete, no callbacks will be called afterwards.
336 */
337 @SuppressWarnings("unused")
338 @CalledByNative
339 private void onComplete() {
340 mResponseInfo.mTotalReceivedBytes =
341 nativeGetTotalReceivedBytes(mUrlRequestAdapter);
342 Runnable task = new Runnable() {
343 public void run() {
344 if (isCanceled()) {
345 return;
346 }
347 try {
348 mListener.onComplete(CronetUrlRequest.this, mResponseInfo);
349 destroyRequestAdapter();
350 } catch (Exception e) {
351 onCalledByNativeException(e);
352 }
353 }
354 };
355 mExecutor.execute(task);
356 }
357
358 /**
359 * Called when error has occured, no callbacks will be called afterwards.
360 *
361 * @param nativeError native net error code.
362 * @param errorString textual representation of the error code.
363 */
364 @SuppressWarnings("unused")
365 @CalledByNative
366 private void onError(final int nativeError, final String errorString) {
367 Runnable task = new Runnable() {
368 public void run() {
369 if (isCanceled()) {
370 return;
371 }
372 try {
373 UrlRequestException requestError = new UrlRequestException(
374 "Exception in CronetUrlRequest: " + errorString,
375 nativeError);
376 mListener.onError(CronetUrlRequest.this,
377 mResponseInfo,
378 requestError);
379 destroyRequestAdapter();
380 } catch (Exception e) {
381 onCalledByNativeException(e);
382 }
383 }
384 };
385 mExecutor.execute(task);
386 }
387
388 /**
389 * Appends header |name| with value |value| to |headersMap|.
390 */
391 @SuppressWarnings("unused")
392 @CalledByNative
393 private void onAppendResponseHeader(ResponseHeadersMap headersMap,
394 String name, String value) {
395 try {
396 if (!headersMap.containsKey(name)) {
397 headersMap.put(name, new ArrayList<String>());
398 }
399 headersMap.get(name).add(value);
400 } catch (Exception e) {
401 onCalledByNativeException(e);
402 }
403 }
404
405 // Native methods are implemented in cronet_url_request.cc.
406
407 private native long nativeCreateRequestAdapter(
408 long urlRequestContextAdapter, String url, int priority);
409
410 private native void nativeAddHeader(long urlRequestAdapter, String name,
411 String value);
412
413 private native void nativeSetHttpMethod(long urlRequestAdapter,
414 String method);
415
416 private native void nativeStart(long urlRequestAdapter);
417
418 private native void nativeCancel(long urlRequestAdapter);
419
420 private native void nativeDestroyRequestAdapter(long urlRequestAdapter);
421
422 private native void nativeFollowDeferredRedirect(long urlRequestAdapter);
423
424 private native void nativeReceiveData(long urlRequestAdapter);
425
426 private native int nativeGetHttpStatusCode(long urlRequestAdapter);
427
428 private native void nativeGetAllHeaders(long urlRequestAdapter,
429 ResponseHeadersMap headers);
430
431 private native String nativeGetNegotiatedProtocol(long urlRequestAdapter);
432
433 private native boolean nativeGetWasCached(long urlRequestAdapter);
434
435 private native long nativeGetTotalReceivedBytes(long urlRequestAdapter);
436
437 // Explicit class to work around JNI-generator generics confusion.
438 private class ResponseHeadersMap extends HashMap<String, List<String>> {
49 } 439 }
50 } 440 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698