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 |
index 5f8c76b30d8102b5374183d6f324f6c2286eed03..81787c3706697a261d03962678ac56d3f64e4e35 100644 |
--- a/net/cronet/android/java/src/org/chromium/net/UrlRequest.java |
+++ b/net/cronet/android/java/src/org/chromium/net/UrlRequest.java |
@@ -24,404 +24,407 @@ import java.util.concurrent.Semaphore; |
* Network request using the native http stack implementation. |
*/ |
public class UrlRequest { |
- private static final class ContextLock { |
- } |
- |
- /** |
- * 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 static final int UPLOAD_BYTE_BUFFER_SIZE = 32768; |
- |
- // TODO(mef) : Rename to follow Google Java style guide: |
- // In Google Style special prefixes or suffixes, like those seen in the |
- // examples name_, mName, s_name and kName, are not used. |
- 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 boolean mPostBodySet; |
- private ReadableByteChannel mPostBodyChannel; |
- private WritableByteChannel mOutputChannel; |
- private IOException mSinkException; |
- private volatile boolean mStarted; |
- private volatile boolean mCanceled; |
- private volatile boolean mRecycled; |
- private volatile boolean mFinished; |
- private String mContentType; |
- private long mContentLength; |
- private Semaphore mAppendChunkSemaphore; |
- private final ContextLock mLock; |
- |
- /** |
- * 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; |
- mLock = new ContextLock(); |
- nativeInit(mRequestContext, mUrl, mPriority); |
- mPostBodySet = false; |
- } |
- |
- /** |
- * Adds a request header. |
- */ |
- public void addHeader(String header, String value) { |
- validateNotStarted(); |
- if (mAdditionalHeaders == null) { |
- mAdditionalHeaders = new HashMap<String, String>(); |
+ private static final class ContextLock { |
} |
- 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) { |
- synchronized (mLock) { |
- validateNotStarted(); |
- validatePostBodyNotSet(); |
- nativeSetPostData(contentType, data); |
- mPostBodySet = true; |
+ |
+ /** |
+ * 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 static final int UPLOAD_BYTE_BUFFER_SIZE = 32768; |
+ |
+ // TODO(mef) : Rename to follow Google Java style guide: |
+ // In Google Style special prefixes or suffixes, like those seen in the |
+ // examples name_, mName, s_name and kName, are not used. |
+ 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 boolean mPostBodySet; |
+ private ReadableByteChannel mPostBodyChannel; |
+ private WritableByteChannel mOutputChannel; |
+ private IOException mSinkException; |
+ private volatile boolean mStarted; |
+ private volatile boolean mCanceled; |
+ private volatile boolean mRecycled; |
+ private volatile boolean mFinished; |
+ private String mContentType; |
+ private long mContentLength; |
+ private Semaphore mAppendChunkSemaphore; |
+ private final ContextLock mLock; |
+ |
+ /** |
+ * 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; |
+ mLock = new ContextLock(); |
+ nativeInit(mRequestContext, mUrl, mPriority); |
+ mPostBodySet = false; |
} |
- } |
- |
- /** |
- * Sets a readable byte channel to upload as part of a POST request. |
- * |
- * @param contentType MIME type of the post content or null if this is not a |
- * POST request. |
- * @param channel The channel to read to read upload data from if this is a |
- * POST request. |
- */ |
- public void setUploadChannel(String contentType, |
- ReadableByteChannel channel) { |
- synchronized (mLock) { |
- validateNotStarted(); |
- validatePostBodyNotSet(); |
- nativeBeginChunkedUpload(contentType); |
- mPostBodyChannel = channel; |
- mPostBodySet = true; |
+ |
+ /** |
+ * Adds a request header. |
+ */ |
+ public void addHeader(String header, String value) { |
+ validateNotStarted(); |
+ if (mAdditionalHeaders == null) { |
+ mAdditionalHeaders = new HashMap<String, String>(); |
+ } |
+ mAdditionalHeaders.put(header, value); |
} |
- mAppendChunkSemaphore = new Semaphore(0); |
- } |
- public WritableByteChannel getSink() { |
- return mSink; |
- } |
+ /** |
+ * 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) { |
+ synchronized (mLock) { |
+ validateNotStarted(); |
+ validatePostBodyNotSet(); |
+ nativeSetPostData(contentType, data); |
+ mPostBodySet = true; |
+ } |
+ } |
- public void start() { |
- synchronized (mLock) { |
- if (mCanceled) { |
- return; |
- } |
+ /** |
+ * Sets a readable byte channel to upload as part of a POST request. |
+ * |
+ * @param contentType MIME type of the post content or null if this is not a |
+ * POST request. |
+ * @param channel The channel to read to read upload data from if this is a |
+ * POST request. |
+ */ |
+ public void setUploadChannel(String contentType, |
+ ReadableByteChannel channel) { |
+ synchronized (mLock) { |
+ validateNotStarted(); |
+ validatePostBodyNotSet(); |
+ nativeBeginChunkedUpload(contentType); |
+ mPostBodyChannel = channel; |
+ mPostBodySet = true; |
+ } |
+ mAppendChunkSemaphore = new Semaphore(0); |
+ } |
- validateNotStarted(); |
- validateNotRecycled(); |
+ public WritableByteChannel getSink() { |
+ return mSink; |
+ } |
+ |
+ public void start() { |
+ synchronized (mLock) { |
+ if (mCanceled) { |
+ return; |
+ } |
+ |
+ validateNotStarted(); |
+ validateNotRecycled(); |
- mStarted = true; |
+ mStarted = true; |
- if (mHeaders != null && !mHeaders.isEmpty()) { |
- for (Entry<String, String> entry : mHeaders.entrySet()) { |
- nativeAddHeader(entry.getKey(), entry.getValue()); |
+ 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(); |
} |
- } |
- if (mAdditionalHeaders != null && !mAdditionalHeaders.isEmpty()) { |
- for (Entry<String, String> entry : mAdditionalHeaders.entrySet()) { |
- nativeAddHeader(entry.getKey(), entry.getValue()); |
+ if (mPostBodyChannel != null) { |
+ uploadFromChannel(mPostBodyChannel); |
} |
- } |
+ } |
- nativeStart(); |
+ /** |
+ * Uploads data from a {@code ReadableByteChannel} using chunked transfer |
+ * encoding. The native call to append a chunk is asynchronous so a |
+ * semaphore is used to delay writing into the buffer again until chromium |
+ * is finished with it. |
+ * |
+ * @param channel the channel to read data from. |
+ */ |
+ private void uploadFromChannel(ReadableByteChannel channel) { |
+ ByteBuffer buffer = ByteBuffer.allocateDirect(UPLOAD_BYTE_BUFFER_SIZE); |
+ |
+ // The chromium API requires us to specify in advance if a chunk is the |
+ // last one. This extra ByteBuffer is needed to peek ahead and check for |
+ // the end of the channel. |
+ ByteBuffer checkForEnd = ByteBuffer.allocate(1); |
+ |
+ try { |
+ boolean lastChunk; |
+ do { |
+ // First dump in the one byte we read to check for the end of |
+ // the channel. (The first time through the loop the checkForEnd |
+ // buffer will be empty). |
+ checkForEnd.flip(); |
+ buffer.clear(); |
+ buffer.put(checkForEnd); |
+ checkForEnd.clear(); |
+ |
+ channel.read(buffer); |
+ lastChunk = channel.read(checkForEnd) <= 0; |
+ buffer.flip(); |
+ nativeAppendChunk(buffer, buffer.limit(), lastChunk); |
+ |
+ if (lastChunk) { |
+ break; |
+ } |
+ |
+ // Acquire permit before writing to the buffer again to ensure |
+ // chromium is done with it. |
+ mAppendChunkSemaphore.acquire(); |
+ } while (!lastChunk && !mFinished); |
+ } catch (IOException e) { |
+ mSinkException = e; |
+ cancel(); |
+ } catch (InterruptedException e) { |
+ mSinkException = new IOException(e); |
+ cancel(); |
+ } finally { |
+ try { |
+ mPostBodyChannel.close(); |
+ } catch (IOException ignore) { |
+ ; |
+ } |
+ } |
} |
- if (mPostBodyChannel != null) { |
- uploadFromChannel(mPostBodyChannel); |
+ public void cancel() { |
+ synchronized (mLock) { |
+ if (mCanceled) { |
+ return; |
+ } |
+ |
+ mCanceled = true; |
+ |
+ if (!mRecycled) { |
+ nativeCancel(); |
+ } |
+ } |
} |
- } |
- |
- /** |
- * Uploads data from a {@code ReadableByteChannel} using chunked transfer |
- * encoding. |
- * |
- * The native call to append a chunk is asynchronous so a semaphore is used |
- * to delay writing into the buffer again until chromium is finished with it. |
- * |
- * @param channel the channel to read data from. |
- */ |
- private void uploadFromChannel(ReadableByteChannel channel) { |
- ByteBuffer buffer = ByteBuffer.allocateDirect(UPLOAD_BYTE_BUFFER_SIZE); |
- |
- // The chromium API requires us to specify in advance if a chunk is the last |
- // one. This extra ByteBuffer is needed to peek ahead and check for the end |
- // of the channel. |
- ByteBuffer checkForEnd = ByteBuffer.allocate(1); |
- |
- try { |
- boolean lastChunk; |
- do { |
- // First dump in the one byte we read to check for the end of the |
- // channel. (The first time through the loop the checkForEnd buffer will |
- // be empty). |
- checkForEnd.flip(); |
- buffer.clear(); |
- buffer.put(checkForEnd); |
- checkForEnd.clear(); |
- |
- channel.read(buffer); |
- lastChunk = channel.read(checkForEnd) <= 0; |
- buffer.flip(); |
- nativeAppendChunk(buffer, buffer.limit(), lastChunk); |
- |
- if (lastChunk) { |
- break; |
+ |
+ public boolean isCanceled() { |
+ synchronized (mLock) { |
+ return mCanceled; |
} |
+ } |
- // Acquire permit before writing to the buffer again to ensure chromium |
- // is done with it. |
- mAppendChunkSemaphore.acquire(); |
- |
- } while (!lastChunk && !mFinished); |
- |
- } catch (IOException e) { |
- mSinkException = e; |
- cancel(); |
- } catch (InterruptedException e) { |
- mSinkException = new IOException(e); |
- cancel(); |
- } finally { |
- try { |
- mPostBodyChannel.close(); |
- } catch (IOException ignore) { |
- ; |
- } |
+ public boolean isRecycled() { |
+ synchronized (mLock) { |
+ return mRecycled; |
+ } |
} |
- } |
- public void cancel() { |
- synchronized (mLock) { |
- if (mCanceled) { |
- return; |
- } |
+ /** |
+ * Returns an exception if any, or null if the request was completed |
+ * successfully. |
+ */ |
+ public IOException getException() { |
+ if (mSinkException != null) { |
+ return mSinkException; |
+ } |
- mCanceled = true; |
+ 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); |
+ } |
+ } |
- if (!mRecycled) { |
- nativeCancel(); |
- } |
+ 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; |
} |
- } |
- public boolean isCanceled() { |
- synchronized (mLock) { |
- return mCanceled; |
+ /** |
+ * A callback invoked when appending a chunk to the request has completed. |
+ */ |
+ @CalledByNative |
+ protected void onAppendChunkCompleted() { |
+ mAppendChunkSemaphore.release(); |
} |
- } |
- public boolean isRecycled() { |
- synchronized (mLock) { |
- return mRecycled; |
+ /** |
+ * A callback invoked when the first chunk of the response has arrived. |
+ */ |
+ @CalledByNative |
+ protected void onResponseStarted() { |
+ mContentType = nativeGetContentType(); |
+ mContentLength = nativeGetContentLength(); |
} |
- } |
- |
- /** |
- * Returns an exception if any, or null if the request was completed |
- * successfully. |
- */ |
- public IOException getException() { |
- if (mSinkException != null) { |
- return mSinkException; |
+ |
+ /** |
+ * A callback invoked when the response has been fully consumed. |
+ */ |
+ protected void onRequestComplete() { |
} |
- 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; |
+ /** |
+ * 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 { |
- host = new URL(mUrl).getHost(); |
- } catch (MalformedURLException e) { |
- host = mUrl; |
+ while (byteBuffer.hasRemaining()) { |
+ mSink.write(byteBuffer); |
+ } |
+ } catch (IOException e) { |
+ mSinkException = e; |
+ cancel(); |
} |
- 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 appending a chunk to the request has completed. |
- */ |
- @CalledByNative |
- protected void onAppendChunkCompleted() { |
- mAppendChunkSemaphore.release(); |
- } |
- |
- /** |
- * 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 void finish() { |
+ synchronized (mLock) { |
+ mFinished = true; |
+ if (mAppendChunkSemaphore != null) { |
+ mAppendChunkSemaphore.release(); |
+ } |
+ |
+ if (mRecycled) { |
+ return; |
+ } |
+ try { |
+ mSink.close(); |
+ } catch (IOException e) { |
+ // Ignore |
+ } |
+ onRequestComplete(); |
+ nativeRecycle(); |
+ mRecycled = true; |
+ } |
} |
- } |
- |
- /** |
- * Notifies the listener, releases native data structures. |
- */ |
- @SuppressWarnings("unused") |
- @CalledByNative |
- private void finish() { |
- synchronized (mLock) { |
- mFinished = true; |
- if (mAppendChunkSemaphore != null) { |
- mAppendChunkSemaphore.release(); |
- } |
- |
- 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 validateNotRecycled() { |
- if (mRecycled) { |
- throw new IllegalStateException("Accessing recycled request"); |
+ private void validateNotStarted() { |
+ if (mStarted) { |
+ throw new IllegalStateException("Request already started"); |
+ } |
} |
- } |
- private void validateNotStarted() { |
- if (mStarted) { |
- throw new IllegalStateException("Request already started"); |
+ private void validatePostBodyNotSet() { |
+ if (mPostBodySet) { |
+ throw new IllegalStateException("Post Body already set"); |
+ } |
} |
- } |
- private void validatePostBodyNotSet() { |
- if (mPostBodySet) { |
- throw new IllegalStateException("Post Body already set"); |
+ public String getUrl() { |
+ return mUrl; |
} |
- } |
- |
- 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 nativeBeginChunkedUpload(String contentType); |
- private native void nativeAppendChunk(ByteBuffer chunk, |
- int chunkSize, |
- boolean isLastChunk); |
- 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(); |
+ |
+ 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 nativeBeginChunkedUpload(String contentType); |
+ |
+ private native void nativeAppendChunk(ByteBuffer chunk, int chunkSize, |
+ boolean isLastChunk); |
+ |
+ 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(); |
} |