Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 */ |
| 23 @JNINamespace("cronet") | |
| 10 public class CronetUrlRequest implements UrlRequest { | 24 public class CronetUrlRequest implements UrlRequest { |
|
Charles
2014/09/20 00:47:34
Final?
mef
2014/09/22 17:12:13
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 ResponseInfo mResponseInfo; | |
| 35 private boolean mCanceled = false; | |
| 36 | |
| 37 class NativeResponseInfo implements ResponseInfo { | |
|
Charles
2014/09/20 00:47:34
Final
mef
2014/09/22 17:12:13
Done.
| |
| 38 @Override | |
| 39 public URL getUrl() { | |
| 40 return null; | |
| 41 } | |
| 42 | |
| 43 @Override | |
| 44 public URL[] getUrlChain() { | |
| 45 return null; | |
| 46 } | |
| 47 | |
| 48 @Override | |
| 49 public int getHttpStatusCode() { | |
| 50 return 0; | |
| 51 } | |
| 52 | |
| 53 @Override | |
| 54 public Map<String, List<String>> getAllHeaders() { | |
| 55 return null; | |
| 56 } | |
| 57 | |
| 58 @Override | |
| 59 public boolean wasCached() { | |
| 60 return false; | |
| 61 } | |
| 62 | |
| 63 @Override | |
| 64 public boolean wasFetchedOverSPDY() { | |
|
Charles
2014/09/20 00:47:34
I actually prefer getting the negotiated protocol
mef
2014/09/22 17:12:12
sgtm
| |
| 65 return false; | |
| 66 } | |
| 67 | |
| 68 @Override | |
| 69 public boolean wasFetchedOverQUIC() { | |
| 70 return false; | |
| 71 } | |
| 72 }; | |
| 73 | |
| 74 CronetUrlRequest(CronetUrlRequestContext requestContext, | |
| 75 String url, int priority, | |
| 76 UrlRequestListener listener, | |
| 77 Executor executor) { | |
| 78 if (requestContext == null) { | |
| 79 throw new NullPointerException("Context is required"); | |
| 80 } | |
| 81 if (url == null) { | |
| 82 throw new NullPointerException("URL is required"); | |
| 83 } | |
| 84 if (listener == null) { | |
| 85 throw new NullPointerException("Listener is required"); | |
| 86 } | |
| 87 if (executor == null) { | |
| 88 throw new NullPointerException("Executor is required"); | |
| 89 } | |
| 90 | |
| 91 mRequestContext = requestContext; | |
| 92 mUrl = url; | |
| 93 mPriority = convertRequestPriority(priority); | |
| 94 mUrlRequestAdapter = nativeCreateRequestAdapter( | |
| 95 mRequestContext.getCronetUrlRequestContextAdapter(), | |
|
Charles
2014/09/20 00:47:34
These params all fit on one line
mef
2014/09/22 17:12:13
Acknowledged.
mef
2014/09/23 18:42:14
Actually, they don't fit into 80 chars, which is o
| |
| 96 mUrl, | |
| 97 mPriority); | |
| 98 mListener = listener; | |
| 99 mExecutor = executor; | |
| 100 } | |
| 101 | |
| 11 @Override | 102 @Override |
| 12 public void setHttpMethod(String method) { | 103 public void setHttpMethod(String method) { |
| 13 | 104 if (method == null) { |
| 105 throw new NullPointerException("method is required"); | |
| 106 } | |
| 107 nativeSetHttpMethod(mUrlRequestAdapter, method); | |
| 14 } | 108 } |
| 15 | 109 |
| 16 @Override | 110 @Override |
| 17 public void addHeader(String header, String value) { | 111 public void addHeader(String header, String value) { |
| 18 | 112 if (header == null || value == null) { |
| 113 throw new NullPointerException("Invalid header"); | |
| 114 } | |
| 115 nativeAddHeader(mUrlRequestAdapter, header, value); | |
| 19 } | 116 } |
| 20 | 117 |
| 21 @Override | 118 @Override |
| 22 public void start(UrlRequestListener listener) { | 119 public void start() { |
|
Charles
2014/09/20 00:47:33
These methods should have threading policy attache
mef
2014/09/22 17:12:12
Is there some way to formally declare threading po
Charles
2014/09/22 21:22:54
There is, but it's not standard and the compiler d
| |
| 23 | 120 nativeStart(mUrlRequestAdapter); |
| 24 } | 121 } |
| 25 | 122 |
| 26 @Override | 123 @Override |
| 27 public void cancel() { | 124 public void cancel() { |
| 28 | 125 mCanceled = true; |
| 126 nativeDestroyRequestAdapter(mUrlRequestAdapter); | |
| 29 } | 127 } |
| 30 | 128 |
| 31 @Override | 129 @Override |
| 32 public boolean isCanceled() { | 130 public boolean isCanceled() { |
| 33 return false; | 131 return mCanceled; |
| 34 } | 132 } |
| 35 | 133 |
| 36 @Override | 134 @Override |
| 37 public void pause() { | 135 public void pause() { |
| 38 | 136 |
| 39 } | 137 } |
| 40 | 138 |
| 41 @Override | 139 @Override |
| 42 public boolean isPaused() { | 140 public boolean isPaused() { |
| 43 return false; | 141 return false; |
| 44 } | 142 } |
| 45 | 143 |
| 46 @Override | 144 @Override |
| 47 public void resume() { | 145 public void resume() { |
| 48 | 146 |
| 49 } | 147 } |
| 148 | |
| 149 private static int convertRequestPriority(int priority) { | |
| 150 switch (priority) { | |
| 151 case REQUEST_PRIORITY_IDLE: | |
| 152 return ChromiumUrlRequestPriority.IDLE; | |
| 153 case REQUEST_PRIORITY_LOWEST: | |
| 154 return ChromiumUrlRequestPriority.LOWEST; | |
| 155 case REQUEST_PRIORITY_LOW: | |
| 156 return ChromiumUrlRequestPriority.LOW; | |
| 157 case REQUEST_PRIORITY_MEDIUM: | |
| 158 return ChromiumUrlRequestPriority.MEDIUM; | |
| 159 case REQUEST_PRIORITY_HIGHEST: | |
| 160 return ChromiumUrlRequestPriority.HIGHEST; | |
| 161 default: | |
| 162 return ChromiumUrlRequestPriority.MEDIUM; | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 private void prepareResponseInfo() { | |
| 167 | |
| 168 } | |
| 169 | |
| 170 | |
| 171 // Private methods called by native library. | |
| 172 | |
| 173 /** | |
| 174 * If @CalledByNative method throws an exception, request gets cancelled | |
| 175 * and exception could be retrieved from request using getException(). | |
| 176 */ | |
| 177 private void onCalledByNativeException(Exception e) { | |
|
Charles
2014/09/20 00:47:34
Needs @CalledByNative annotation
mef
2014/09/22 17:12:12
It is not called by native, it merely handles exce
| |
| 178 UrlRequestException requestError = new UrlRequestException( | |
| 179 "CalledByNative method has thrown an exception", e); | |
| 180 Log.e(CronetUrlRequestContext.LOG_TAG, | |
| 181 "Exception in CalledByNative method", e); | |
| 182 try { | |
| 183 mListener.onError(this, requestError); | |
| 184 cancel(); | |
| 185 } catch (Exception cancel_exception) { | |
|
Charles
2014/09/20 00:47:33
Use java style naming of exception
mef
2014/09/22 17:12:13
Done.
| |
| 186 Log.e(CronetUrlRequestContext.LOG_TAG, | |
| 187 "Exception trying to cancel request", cancel_exception); | |
| 188 } | |
| 189 } | |
| 190 | |
| 191 /** | |
| 192 * Called before following redirects. The redirect will automatically be | |
| 193 * followed, unless the request is paused or cancelled during this | |
| 194 * callback. If the redirect response has a body, it will be ignored. | |
| 195 * This will only be called between start and onResponseStarted. | |
| 196 * | |
| 197 * @param info Response information. | |
| 198 * @param newLocation Location where request is redirected. | |
| 199 */ | |
| 200 @SuppressWarnings("unused") | |
| 201 @CalledByNative | |
| 202 private void onRedirect(final String newLocation) { | |
| 203 prepareResponseInfo(); | |
| 204 Runnable task = new Runnable() { | |
| 205 public void run() { | |
| 206 try { | |
| 207 mListener.onRedirect(CronetUrlRequest.this, mResponseInfo, | |
| 208 new URL(newLocation)); | |
| 209 if (!isCanceled()) | |
| 210 nativeFollowDeferredRedirect(mUrlRequestAdapter); | |
| 211 } catch (Exception e) { | |
| 212 onCalledByNativeException(e); | |
| 213 } | |
| 214 } | |
| 215 }; | |
| 216 mExecutor.execute(task); | |
| 217 } | |
| 218 | |
| 219 /** | |
| 220 * Called when the final set of headers, after all redirects, | |
| 221 * is received. Can only be called once for each request. | |
| 222 * | |
| 223 * @param info Response information. | |
| 224 */ | |
| 225 @SuppressWarnings("unused") | |
| 226 @CalledByNative | |
| 227 private void onResponseStarted() { | |
| 228 prepareResponseInfo(); | |
| 229 Runnable task = new Runnable() { | |
| 230 public void run() { | |
| 231 try { | |
| 232 mListener.onResponseStarted(CronetUrlRequest.this, | |
| 233 mResponseInfo); | |
| 234 if (!isCanceled()) | |
| 235 nativeReceiveData(mUrlRequestAdapter); | |
| 236 } catch (Exception e) { | |
| 237 onCalledByNativeException(e); | |
| 238 } | |
| 239 } | |
| 240 }; | |
| 241 mExecutor.execute(task); | |
| 242 } | |
| 243 | |
| 244 /** | |
| 245 * Called whenever data is received. The ByteBuffer remains | |
| 246 * valid only for the duration of the callback. Or if the callback | |
| 247 * pauses the request, it remains valid until the request is resumed. | |
| 248 * Cancelling the request also invalidates the buffer. | |
| 249 * | |
| 250 * @param byteBuffer Received data. | |
| 251 */ | |
| 252 @SuppressWarnings("unused") | |
| 253 @CalledByNative | |
| 254 private void onDataReceived(final ByteBuffer byteBuffer) { | |
| 255 Runnable task = new Runnable() { | |
|
Charles
2014/09/20 00:47:34
Is there any way to avoid allocating this every ti
mef
2014/09/22 17:12:12
I guess I can keep onDataReceivedRunnable as a mem
Charles
2014/09/22 21:22:53
That would work.
On 2014/09/22 17:12:12, mef wrot
mef
2014/09/23 18:42:14
Done.
| |
| 256 public void run() { | |
| 257 try { | |
| 258 mListener.onDataReceived(CronetUrlRequest.this, byteBuffer); | |
| 259 if (!isCanceled()) | |
| 260 nativeReceiveData(mUrlRequestAdapter); | |
| 261 } catch (Exception e) { | |
| 262 onCalledByNativeException(e); | |
| 263 } | |
| 264 } | |
| 265 }; | |
| 266 mExecutor.execute(task); | |
| 267 } | |
| 268 | |
| 269 /** | |
| 270 * Called when request is complete, no callbacks will be called afterwards. | |
| 271 */ | |
| 272 @SuppressWarnings("unused") | |
| 273 @CalledByNative | |
| 274 private void onComplete() { | |
| 275 Runnable task = new Runnable() { | |
| 276 public void run() { | |
| 277 try { | |
| 278 mListener.onComplete(CronetUrlRequest.this); | |
| 279 nativeDestroyRequestAdapter(mUrlRequestAdapter); | |
| 280 } catch (Exception e) { | |
| 281 onCalledByNativeException(e); | |
| 282 } | |
| 283 } | |
| 284 }; | |
| 285 mExecutor.execute(task); | |
| 286 } | |
| 287 | |
| 288 /** | |
| 289 * Called when error has occured, no callbacks will be called afterwards. | |
| 290 */ | |
| 291 @SuppressWarnings("unused") | |
| 292 @CalledByNative | |
| 293 private void onError(int error) { | |
| 294 Runnable task = new Runnable() { | |
| 295 public void run() { | |
| 296 try { | |
| 297 UrlRequestException requestError = new UrlRequestException( | |
| 298 "Exception in CronetUrlRequest ", null); | |
| 299 mListener.onError(CronetUrlRequest.this, requestError); | |
| 300 nativeDestroyRequestAdapter(mUrlRequestAdapter); | |
| 301 } catch (Exception e) { | |
| 302 onCalledByNativeException(e); | |
|
Charles
2014/09/20 00:47:34
Given that this is happening on the other thread,
mef
2014/09/22 17:12:12
Good question. What should happen if listener thro
Charles
2014/09/22 21:22:53
I think the current behavior is correct, except th
mef
2014/09/23 18:42:14
Done.
| |
| 303 } | |
| 304 } | |
| 305 }; | |
| 306 mExecutor.execute(task); | |
|
mef
2014/09/22 17:12:13
Should we execute it inside of try...catch to hand
| |
| 307 } | |
| 308 | |
| 309 /** | |
| 310 * Appends header |name| with value |value| to |headersMap|. | |
| 311 */ | |
| 312 @SuppressWarnings("unused") | |
| 313 @CalledByNative | |
| 314 private void onAppendResponseHeader(ResponseHeadersMap headersMap, | |
| 315 String name, String value) { | |
| 316 try { | |
| 317 if (!headersMap.containsKey(name)) { | |
| 318 headersMap.put(name, new ArrayList<String>()); | |
| 319 } | |
| 320 headersMap.get(name).add(value); | |
| 321 } catch (Exception e) { | |
| 322 onCalledByNativeException(e); | |
| 323 } | |
| 324 } | |
| 325 | |
| 326 // Native methods are implemented in cronet_url_request.cc. | |
| 327 | |
| 328 private native long nativeCreateRequestAdapter( | |
| 329 long urlRequestContextAdapter, String url, int priority); | |
| 330 | |
| 331 private native void nativeAddHeader(long urlRequestAdapter, String name, | |
| 332 String value); | |
| 333 | |
| 334 private native void nativeSetHttpMethod(long urlRequestAdapter, | |
| 335 String method); | |
| 336 | |
| 337 private native void nativeStart(long urlRequestAdapter); | |
| 338 | |
| 339 private native void nativeCancel(long urlRequestAdapter); | |
| 340 | |
| 341 private native void nativeDestroyRequestAdapter(long urlRequestAdapter); | |
| 342 | |
| 343 private native void nativeFollowDeferredRedirect(long urlRequestAdapter); | |
| 344 | |
| 345 private native void nativeReceiveData(long urlRequestAdapter); | |
| 346 | |
| 347 // TODO(mef): Remove unused methods (if any). | |
| 348 | |
| 349 private native int nativeGetErrorCode(long urlRequestAdapter); | |
| 350 | |
| 351 private native int nativeGetHttpStatusCode(long urlRequestAdapter); | |
| 352 | |
| 353 private native String nativeGetErrorString(long urlRequestAdapter); | |
| 354 | |
| 355 private native String nativeGetContentType(long urlRequestAdapter); | |
| 356 | |
| 357 private native long nativeGetContentLength(long urlRequestAdapter); | |
| 358 | |
| 359 private native String nativeGetHeader(long urlRequestAdapter, String name); | |
| 360 | |
| 361 private native void nativeGetAllHeaders(long urlRequestAdapter, | |
| 362 ResponseHeadersMap headers); | |
| 363 | |
| 364 private native String nativeGetNegotiatedProtocol(long urlRequestAdapter); | |
| 365 | |
| 366 // Explicit class to work around JNI-generator generics confusion. | |
| 367 private class ResponseHeadersMap extends HashMap<String, List<String>> { | |
| 368 } | |
| 369 | |
| 370 | |
| 50 } | 371 } |
| OLD | NEW |