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

Unified 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, 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 side-by-side diff with in-line comments
Download patch
Index: components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java
index 3a7776dc8520cbabc219fde0c6515e7d1499e808..ff029b7bd16b1cb3413f606f0fdf697e71c4e7c4 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java
+++ b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java
@@ -4,38 +4,165 @@
package org.chromium.net;
+import android.util.Log;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
/**
* UrlRequest using Chromium HTTP stack implementation.
*/
-public class CronetUrlRequest implements UrlRequest {
+@JNINamespace("cronet")
+final class CronetUrlRequest implements UrlRequest {
+ /**
+ * Native adapter object, owned by UrlRequest.
+ */
+ private long mUrlRequestAdapter;
+ private final CronetUrlRequestContext mRequestContext;
+ private final String mUrl;
+ private final int mPriority;
+ private final UrlRequestListener mListener;
+ private final Executor mExecutor;
+ private NativeResponseInfo mResponseInfo;
+ private boolean mCanceled = false;
+ private OnDataReceivedRunnable mOnDataReceivedTask;
+
+ final class OnDataReceivedRunnable implements Runnable {
+ ByteBuffer mByteBuffer;
+ public void run() {
+ try {
+ mListener.onDataReceived(CronetUrlRequest.this,
+ mResponseInfo, mByteBuffer);
+ mByteBuffer = null;
+ if (!isCanceled())
+ nativeReceiveData(mUrlRequestAdapter);
+ } catch (Exception e) {
+ onCalledByNativeException(e);
+ }
+ }
+ }
+
+ final class NativeResponseInfo implements ResponseInfo {
+ private final List<String> mUrlChain = new ArrayList<String>();
+ private final int mHttpStatusCode;
+ private final ResponseHeadersMap mAllHeaders = new ResponseHeadersMap();
+ private final boolean mWasCached;
+ private final String mNegotiatedProtocol;
+ 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.
+
+ NativeResponseInfo(int httpStatusCode, boolean wasCached,
+ String negotiatedProtocol) {
+ mHttpStatusCode = httpStatusCode;
+ mWasCached = wasCached;
+ mNegotiatedProtocol = negotiatedProtocol;
+ }
+
+ @Override
+ public String getUrl() {
+ return mUrlChain.get(mUrlChain.size() - 1);
+ }
+
+ @Override
+ public String[] getUrlChain() {
+ return mUrlChain.toArray(new String[mUrlChain.size()]);
+ }
+
+ @Override
+ public int getHttpStatusCode() {
+ return mHttpStatusCode;
+ }
+
+ @Override
+ public Map<String, List<String>> getAllHeaders() {
+ return mAllHeaders;
+ }
+
+ @Override
+ public boolean wasCached() {
+ return mWasCached;
+ }
+
+ @Override
+ public String getNegotiatedProtocol() {
+ return mNegotiatedProtocol;
+ }
+
+ @Override
+ public long getTotalReceivedBytes() {
+ return mTotalReceivedBytes;
+ }
+ };
+
+ CronetUrlRequest(CronetUrlRequestContext requestContext,
+ String url, int priority,
+ UrlRequestListener listener,
+ Executor executor) {
+ if (requestContext == null) {
+ throw new NullPointerException("Context is required");
+ }
+ if (url == null) {
+ throw new NullPointerException("URL is required");
+ }
+ if (listener == null) {
+ throw new NullPointerException("Listener is required");
+ }
+ if (executor == null) {
+ throw new NullPointerException("Executor is required");
+ }
+
+ mRequestContext = requestContext;
+ mUrl = url;
+ mPriority = convertRequestPriority(priority);
+ mUrlRequestAdapter = nativeCreateRequestAdapter(
+ mRequestContext.getCronetUrlRequestContextAdapter(),
+ mUrl,
+ mPriority);
+ mListener = listener;
+ mExecutor = executor;
+ }
+
@Override
public void setHttpMethod(String method) {
-
+ if (method == null) {
+ throw new NullPointerException("method is required");
+ }
+ nativeSetHttpMethod(mUrlRequestAdapter, method);
}
@Override
public void addHeader(String header, String value) {
-
+ if (header == null || value == null) {
+ throw new NullPointerException("Invalid header");
+ }
+ nativeAddHeader(mUrlRequestAdapter, header, value);
}
@Override
- public void start(UrlRequestListener listener) {
-
+ public void start() {
+ nativeStart(mUrlRequestAdapter);
}
@Override
public void cancel() {
-
+ nativeCancel(mUrlRequestAdapter);
}
@Override
public boolean isCanceled() {
- return false;
+ return mCanceled;
}
@Override
public void pause() {
-
+ throw new UnsupportedOperationException("Not implemented yet");
}
@Override
@@ -45,6 +172,233 @@ public class CronetUrlRequest implements UrlRequest {
@Override
public void resume() {
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+ private static int convertRequestPriority(int priority) {
+ switch (priority) {
+ case REQUEST_PRIORITY_IDLE:
+ return ChromiumUrlRequestPriority.IDLE;
+ case REQUEST_PRIORITY_LOWEST:
+ return ChromiumUrlRequestPriority.LOWEST;
+ case REQUEST_PRIORITY_LOW:
+ return ChromiumUrlRequestPriority.LOW;
+ case REQUEST_PRIORITY_MEDIUM:
+ return ChromiumUrlRequestPriority.MEDIUM;
+ case REQUEST_PRIORITY_HIGHEST:
+ return ChromiumUrlRequestPriority.HIGHEST;
+ default:
+ return ChromiumUrlRequestPriority.MEDIUM;
+ }
}
+
+ private void prepareResponseInfo() {
+ if (mResponseInfo != null)
+ return;
+ mResponseInfo = new NativeResponseInfo(
+ nativeGetHttpStatusCode(mUrlRequestAdapter),
+ nativeGetWasCached(mUrlRequestAdapter),
+ nativeGetNegotiatedProtocol(mUrlRequestAdapter));
+ mResponseInfo.mUrlChain.add(mUrl);
+ nativeGetAllHeaders(mUrlRequestAdapter, mResponseInfo.mAllHeaders);
+ }
+
+ // Private methods called by native library.
+
+ /**
+ * If @CalledByNative method throws an exception, request gets cancelled
+ * and exception could be retrieved from request using getException().
+ */
+ private void onCalledByNativeException(Exception e) {
+ UrlRequestException requestError = new UrlRequestException(
+ "CalledByNative method has thrown an exception", e);
+ Log.e(CronetUrlRequestContext.LOG_TAG,
+ "Exception in CalledByNative method", e);
+ try {
+ mListener.onError(this, mResponseInfo, requestError);
+ cancel();
+ } catch (Exception cancelException) {
+ Log.e(CronetUrlRequestContext.LOG_TAG,
+ "Exception trying to cancel request", cancelException);
+ }
+ }
+
+ /**
+ * Called before following redirects. The redirect will automatically be
+ * followed, unless the request is paused or cancelled during this
+ * callback. If the redirect response has a body, it will be ignored.
+ * This will only be called between start and onResponseStarted.
+ *
+ * @param info Response information.
+ * @param newLocation Location where request is redirected.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private void onRedirect(final String newLocation) {
+ prepareResponseInfo();
+ mResponseInfo.mUrlChain.add(newLocation);
+ Runnable task = new Runnable() {
+ public void run() {
+ try {
+ mListener.onRedirect(CronetUrlRequest.this, mResponseInfo,
+ new URL(newLocation));
+ if (!isCanceled())
+ nativeFollowDeferredRedirect(mUrlRequestAdapter);
+ } catch (Exception e) {
+ onCalledByNativeException(e);
+ }
+ }
+ };
+ mExecutor.execute(task);
+ }
+
+ /**
+ * Called when the final set of headers, after all redirects,
+ * is received. Can only be called once for each request.
+ *
+ * @param info Response information.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private void onResponseStarted() {
+ prepareResponseInfo();
+ Runnable task = new Runnable() {
+ public void run() {
+ try {
+ mListener.onResponseStarted(CronetUrlRequest.this,
+ mResponseInfo);
+ if (!isCanceled())
+ nativeReceiveData(mUrlRequestAdapter);
+ } catch (Exception e) {
+ onCalledByNativeException(e);
+ }
+ }
+ };
+ mExecutor.execute(task);
+ }
+
+ /**
+ * Called whenever data is received. The ByteBuffer remains
+ * valid only until listener callback. Or if the callback
+ * pauses the request, it remains valid until the request is resumed.
+ * Cancelling the request also invalidates the buffer.
+ *
+ * @param byteBuffer Received data.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private void onDataReceived(final ByteBuffer byteBuffer) {
+ if (mOnDataReceivedTask == null)
+ mOnDataReceivedTask = new OnDataReceivedRunnable();
+ mOnDataReceivedTask.mByteBuffer = byteBuffer;
+ mExecutor.execute(mOnDataReceivedTask);
+ }
+
+ /**
+ * Called when request is complete, no callbacks will be called afterwards.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private void onComplete(boolean canceled) {
+ mCanceled = canceled;
+ Runnable task = new Runnable() {
+ public void run() {
+ try {
+ mListener.onComplete(CronetUrlRequest.this, mResponseInfo);
+ nativeDestroyRequestAdapter(mUrlRequestAdapter);
+ } catch (Exception e) {
+ onCalledByNativeException(e);
+ }
+ }
+ };
+ mExecutor.execute(task);
+ }
+
+ /**
+ * Called when error has occured, no callbacks will be called afterwards.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private void onError(int error) {
+ Runnable task = new Runnable() {
+ public void run() {
+ try {
+ UrlRequestException requestError = new UrlRequestException(
+ "Exception in CronetUrlRequest ", null);
+ mListener.onError(CronetUrlRequest.this,
+ mResponseInfo,
+ requestError);
+ nativeDestroyRequestAdapter(mUrlRequestAdapter);
+ } catch (Exception e) {
+ onCalledByNativeException(e);
+ }
+ }
+ };
+ mExecutor.execute(task);
+ }
+
+ /**
+ * Appends header |name| with value |value| to |headersMap|.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private void onAppendResponseHeader(ResponseHeadersMap headersMap,
+ String name, String value) {
+ try {
+ if (!headersMap.containsKey(name)) {
+ headersMap.put(name, new ArrayList<String>());
+ }
+ headersMap.get(name).add(value);
+ } catch (Exception e) {
+ onCalledByNativeException(e);
+ }
+ }
+
+ // Native methods are implemented in cronet_url_request.cc.
+
+ private native long nativeCreateRequestAdapter(
+ long urlRequestContextAdapter, String url, int priority);
+
+ private native void nativeAddHeader(long urlRequestAdapter, String name,
+ String value);
+
+ private native void nativeSetHttpMethod(long urlRequestAdapter,
+ String method);
+
+ private native void nativeStart(long urlRequestAdapter);
+
+ private native void nativeCancel(long urlRequestAdapter);
+
+ private native void nativeDestroyRequestAdapter(long urlRequestAdapter);
+
+ private native void nativeFollowDeferredRedirect(long urlRequestAdapter);
+
+ private native void nativeReceiveData(long urlRequestAdapter);
+
+ // TODO(mef): Remove unused methods (if any).
+
+ private native int nativeGetErrorCode(long urlRequestAdapter);
+
+ private native int nativeGetHttpStatusCode(long urlRequestAdapter);
+
+ private native String nativeGetErrorString(long urlRequestAdapter);
+
+ private native String nativeGetContentType(long urlRequestAdapter);
+
+ private native long nativeGetContentLength(long urlRequestAdapter);
+
+ private native String nativeGetHeader(long urlRequestAdapter, String name);
+
+ private native void nativeGetAllHeaders(long urlRequestAdapter,
+ ResponseHeadersMap headers);
+
+ private native String nativeGetNegotiatedProtocol(long urlRequestAdapter);
+
+ private native boolean nativeGetWasCached(long urlRequestAdapter);
+
+ // Explicit class to work around JNI-generator generics confusion.
+ private class ResponseHeadersMap extends HashMap<String, List<String>> {
+ }
+
+
}

Powered by Google App Engine
This is Rietveld 408576698