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

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

Powered by Google App Engine
This is Rietveld 408576698