Index: net/cronet/android/java/src/org/chromium/net/UrlRequest.java |
diff --git a/net/cronet/android/java/src/org/chromium/net/UrlRequest.java b/net/cronet/android/java/src/org/chromium/net/UrlRequest.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b44e4a2190a247c6bbac7588b5be2885c26c846f |
--- /dev/null |
+++ b/net/cronet/android/java/src/org/chromium/net/UrlRequest.java |
@@ -0,0 +1,286 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.net; |
+ |
+import org.apache.http.conn.ConnectTimeoutException; |
+import org.chromium.base.AccessedByNative; |
+import org.chromium.base.CalledByNative; |
+ |
+import java.io.IOException; |
+import java.net.MalformedURLException; |
+import java.net.URL; |
+import java.net.UnknownHostException; |
+import java.nio.ByteBuffer; |
+import java.nio.channels.WritableByteChannel; |
+import java.util.HashMap; |
+import java.util.Map; |
+import java.util.Map.Entry; |
+ |
+/** |
+ * Network request using the native http stack implementation. |
+ */ |
+public class UrlRequest { |
+ |
+ /* |
+ * Must match definitions in the native source. |
+ */ |
+ public static final int REQUEST_PRIORITY_IDLE = 0; |
+ public static final int REQUEST_PRIORITY_LOWEST = 1; |
+ public static final int REQUEST_PRIORITY_LOW = 2; |
+ public static final int REQUEST_PRIORITY_MEDIUM = 3; |
+ public static final int REQUEST_PRIORITY_HIGHEST = 4; |
+ |
+ /* |
+ * Must match definitions in the native source. |
+ */ |
+ private static final int SUCCESS = 0; |
+ private static final int UNKNOWN_ERROR = 1; |
+ private static final int MALFORMED_URL_ERROR = 2; |
+ private static final int CONNECTION_TIMED_OUT = 3; |
+ private static final int UNKNOWN_HOST = 4; |
+ |
+ private final UrlRequestContext mRequestContext; |
+ private final String mUrl; |
+ private final int mPriority; |
+ private final Map<String, String> mHeaders; |
+ private final WritableByteChannel mSink; |
+ |
+ private Map<String, String> mAdditionalHeaders; |
+ private String mPostBodyContentType; |
+ private byte[] mPostBody; |
+ private WritableByteChannel mOutputChannel; |
+ private IOException mSinkException; |
+ private boolean mStarted; |
+ private boolean mCanceled; |
+ private boolean mRecycled; |
+ private String mContentType; |
+ private long mContentLength; |
+ |
+ /** |
+ * This field is accessed exclusively from the native layer. |
+ */ |
+ @AccessedByNative |
+ @SuppressWarnings("unused") |
+ private long mRequest; |
+ |
+ /** |
+ * Constructor. |
+ * |
+ * @param requestContext The context. |
+ * @param url The URL. |
+ * @param priority Request priority, e.g. {@link #REQUEST_PRIORITY_MEDIUM}. |
+ * @param headers HTTP headers. |
+ * @param sink The output channel into which downloaded content will be written. |
+ */ |
+ public UrlRequest(UrlRequestContext requestContext, String url, int priority, |
+ Map<String, String> headers, WritableByteChannel sink) { |
+ mRequestContext = requestContext; |
+ mUrl = url; |
+ mPriority = priority; |
+ mHeaders = headers; |
+ mSink = sink; |
+ nativeInit(mRequestContext, mUrl, mPriority); |
+ } |
+ |
+ /** |
+ * Adds a request header. |
+ */ |
+ public void addHeader(String header, String value) { |
+ validateNotStarted(); |
+ if (mAdditionalHeaders == null) { |
+ mAdditionalHeaders = new HashMap<String, String>(); |
+ } |
+ mAdditionalHeaders.put(header, value); |
+ } |
+ |
+ /** |
+ * Sets data to upload as part of a POST request. |
+ * |
+ * @param contentType MIME type of the post content or null if this is not a POST. |
+ * @param data The content that needs to be uploaded if this is a POST request. |
+ */ |
+ public void setUploadData(String contentType, byte[] data) { |
+ validateNotStarted(); |
+ mPostBodyContentType = contentType; |
+ mPostBody = data; |
+ } |
+ |
+ public WritableByteChannel getSink() { |
+ return mSink; |
+ } |
+ |
+ public synchronized void start() { |
+ if (mCanceled) { |
+ return; |
+ } |
+ |
+ validateNotStarted(); |
+ validateNotRecycled(); |
+ |
+ if (mPostBody != null && mPostBody.length > 0) { |
+ nativeSetPostData(mPostBodyContentType, mPostBody); |
+ } |
+ |
+ if (mHeaders != null && !mHeaders.isEmpty()) { |
+ for (Entry<String, String> entry : mHeaders.entrySet()) { |
+ nativeAddHeader(entry.getKey(), entry.getValue()); |
+ } |
+ } |
+ |
+ if (mAdditionalHeaders != null && !mAdditionalHeaders.isEmpty()) { |
+ for (Entry<String, String> entry : mAdditionalHeaders.entrySet()) { |
+ nativeAddHeader(entry.getKey(), entry.getValue()); |
+ } |
+ } |
+ |
+ nativeStart(); |
+ } |
+ |
+ public synchronized void cancel() { |
+ if (mCanceled) { |
+ return; |
+ } |
+ |
+ mCanceled = true; |
+ |
+ if (!mRecycled) { |
+ nativeCancel(); |
+ } |
+ } |
+ |
+ public synchronized boolean isCanceled() { |
+ return mCanceled; |
+ } |
+ |
+ public synchronized boolean isRecycled() { |
+ return mRecycled; |
+ } |
+ |
+ /** |
+ * Returns an exception if any, or null if the request was completed successfully. |
+ */ |
+ public IOException getException() { |
+ if (mSinkException != null) { |
+ return mSinkException; |
+ } |
+ |
+ validateNotRecycled(); |
+ |
+ int errorCode = nativeGetErrorCode(); |
+ switch (errorCode) { |
+ case SUCCESS: |
+ return null; |
+ case UNKNOWN_ERROR: |
+ return new IOException(nativeGetErrorString()); |
+ case MALFORMED_URL_ERROR: |
+ return new MalformedURLException("Malformed URL: " + mUrl); |
+ case CONNECTION_TIMED_OUT: |
+ return new ConnectTimeoutException("Connection timed out"); |
+ case UNKNOWN_HOST: |
+ String host; |
+ try { |
+ host = new URL(mUrl).getHost(); |
+ } catch (MalformedURLException e) { |
+ host = mUrl; |
+ } |
+ return new UnknownHostException("Unknown host: " + host); |
+ default: |
+ throw new IllegalStateException("Unrecognized error code: " + errorCode); |
+ } |
+ } |
+ |
+ public native int getHttpStatusCode(); |
+ |
+ /** |
+ * Content length as reported by the server. May be -1 or incorrect if the server returns the |
+ * wrong number, which happens even with Google servers. |
+ */ |
+ public long getContentLength() { |
+ return mContentLength; |
+ } |
+ |
+ public String getContentType() { |
+ return mContentType; |
+ } |
+ |
+ /** |
+ * A callback invoked when the first chunk of the response has arrived. |
+ */ |
+ @CalledByNative |
+ protected void onResponseStarted() { |
+ mContentType = nativeGetContentType(); |
+ mContentLength = nativeGetContentLength(); |
+ } |
+ |
+ /** |
+ * A callback invoked when the response has been fully consumed. |
+ */ |
+ protected void onRequestComplete() { |
+ } |
+ |
+ /** |
+ * Consumes a portion of the response. |
+ * |
+ * @param byteBuffer The ByteBuffer to append. Must be a direct buffer, and no references to it |
+ * may be retained after the method ends, as it wraps code managed on the native heap. |
+ */ |
+ @CalledByNative |
+ protected void onBytesRead(ByteBuffer byteBuffer) { |
+ try { |
+ while (byteBuffer.hasRemaining()) { |
+ mSink.write(byteBuffer); |
+ } |
+ } catch (IOException e) { |
+ mSinkException = e; |
+ cancel(); |
+ } |
+ } |
+ |
+ /** |
+ * Notifies the listener, releases native data structures. |
+ */ |
+ @SuppressWarnings("unused") |
+ @CalledByNative |
+ private synchronized void finish() { |
+ if (mRecycled) { |
+ return; |
+ } |
+ try { |
+ mSink.close(); |
+ } catch (IOException e) { |
+ // Ignore |
+ } |
+ onRequestComplete(); |
+ nativeRecycle(); |
+ mRecycled = true; |
+ } |
+ |
+ private void validateNotRecycled() { |
+ if (mRecycled) { |
+ throw new IllegalStateException("Accessing recycled request"); |
+ } |
+ } |
+ |
+ private void validateNotStarted() { |
+ if (mStarted) { |
+ throw new IllegalStateException("Request already started"); |
+ } |
+ } |
+ |
+ public String getUrl() { |
+ return mUrl; |
+ } |
+ |
+ private native void nativeInit(UrlRequestContext requestContext, String url, int priority); |
+ private native void nativeAddHeader(String name, String value); |
+ private native void nativeSetPostData(String contentType, byte[] content); |
+ private native void nativeStart(); |
+ private native void nativeCancel(); |
+ private native void nativeRecycle(); |
+ private native int nativeGetErrorCode(); |
+ private native String nativeGetErrorString(); |
+ private native String nativeGetContentType(); |
+ private native long nativeGetContentLength(); |
+} |