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

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

Powered by Google App Engine
This is Rietveld 408576698