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

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

Powered by Google App Engine
This is Rietveld 408576698