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

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

Powered by Google App Engine
This is Rietveld 408576698