Index: net/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java |
diff --git a/net/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java b/net/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c64b6d69956aefb6ae5bf868491ce2aa9d419fdf |
--- /dev/null |
+++ b/net/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java |
@@ -0,0 +1,194 @@ |
+// 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 java.io.IOException; |
+import java.nio.ByteBuffer; |
+import java.nio.channels.WritableByteChannel; |
+import java.util.Map; |
+ |
+/** |
+ * Network request using the native http stack implementation. |
+ */ |
+class ChromiumUrlRequest extends UrlRequest implements HttpUrlRequest { |
+ |
+ private final HttpUrlRequestListener mListener; |
+ |
+ private boolean mBufferFullResponse; |
+ |
+ private long mOffset; |
+ |
+ private long mContentLength; |
+ |
+ private long mContentLengthLimit; |
+ |
+ private boolean mCancelIfContentLengthOverLimit; |
+ |
+ private boolean mContentLengthOverLimit; |
+ |
+ private boolean mSkippingToOffset; |
+ |
+ private long mSize; |
+ |
+ public ChromiumUrlRequest(ChromiumUrlRequestContext requestContext, |
+ String url, int priority, Map<String, String> headers, |
+ HttpUrlRequestListener listener) { |
+ this(requestContext, url, priority, headers, |
+ new ChunkedWritableByteChannel(), listener); |
+ mBufferFullResponse = true; |
+ } |
+ |
+ public ChromiumUrlRequest(ChromiumUrlRequestContext requestContext, |
+ String url, int priority, Map<String, String> headers, |
+ WritableByteChannel sink, HttpUrlRequestListener listener) { |
+ super(requestContext, url, convertRequestPriority(priority), headers, |
+ sink); |
+ if (requestContext == null) { |
+ throw new NullPointerException("Context is required"); |
+ } |
+ if (url == null) { |
+ throw new NullPointerException("URL is required"); |
+ } |
+ mListener = listener; |
+ } |
+ |
+ private static int convertRequestPriority(int priority) { |
+ switch (priority) { |
+ case HttpUrlRequest.REQUEST_PRIORITY_IDLE: |
+ return UrlRequest.REQUEST_PRIORITY_IDLE; |
+ case HttpUrlRequest.REQUEST_PRIORITY_LOWEST: |
+ return UrlRequest.REQUEST_PRIORITY_LOWEST; |
+ case HttpUrlRequest.REQUEST_PRIORITY_LOW: |
+ return UrlRequest.REQUEST_PRIORITY_LOW; |
+ case HttpUrlRequest.REQUEST_PRIORITY_MEDIUM: |
+ return UrlRequest.REQUEST_PRIORITY_MEDIUM; |
+ case HttpUrlRequest.REQUEST_PRIORITY_HIGHEST: |
+ return UrlRequest.REQUEST_PRIORITY_HIGHEST; |
+ default: |
+ return UrlRequest.REQUEST_PRIORITY_MEDIUM; |
+ } |
+ } |
+ |
+ @Override |
+ public void setOffset(long offset) { |
+ mOffset = offset; |
+ if (offset != 0) { |
+ addHeader("Range", "bytes=" + offset + "-"); |
+ } |
+ } |
+ |
+ @Override |
+ public long getContentLength() { |
+ return mContentLength; |
+ } |
+ |
+ @Override |
+ public void setContentLengthLimit(long limit, boolean cancelEarly) { |
+ mContentLengthLimit = limit; |
+ mCancelIfContentLengthOverLimit = cancelEarly; |
+ } |
+ |
+ @Override |
+ protected void onResponseStarted() { |
+ super.onResponseStarted(); |
+ |
+ mContentLength = super.getContentLength(); |
+ if (mContentLengthLimit > 0 && mContentLength > mContentLengthLimit |
+ && mCancelIfContentLengthOverLimit) { |
+ onContentLengthOverLimit(); |
+ return; |
+ } |
+ |
+ if (mBufferFullResponse && mContentLength != -1 |
+ && !mContentLengthOverLimit) { |
+ ((ChunkedWritableByteChannel)getSink()).setCapacity( |
+ (int)mContentLength); |
+ } |
+ |
+ if (mOffset != 0) { |
+ // The server may ignore the request for a byte range. |
+ if (super.getHttpStatusCode() == 200) { |
+ if (mContentLength != -1) { |
+ mContentLength -= mOffset; |
+ } |
+ mSkippingToOffset = true; |
+ } else { |
+ mSize = mOffset; |
+ } |
+ } |
+ } |
+ |
+ @Override |
+ protected void onBytesRead(ByteBuffer buffer) { |
+ if (mContentLengthOverLimit) { |
+ return; |
+ } |
+ |
+ int size = buffer.remaining(); |
+ mSize += size; |
+ if (mSkippingToOffset) { |
+ if (mSize <= mOffset) { |
+ return; |
+ } else { |
+ mSkippingToOffset = false; |
+ buffer.position((int)(mOffset - (mSize - size))); |
+ } |
+ } |
+ |
+ if (mContentLengthLimit != 0 && mSize > mContentLengthLimit) { |
+ buffer.limit(size - (int)(mSize - mContentLengthLimit)); |
+ super.onBytesRead(buffer); |
+ onContentLengthOverLimit(); |
+ return; |
+ } |
+ |
+ super.onBytesRead(buffer); |
+ } |
+ |
+ private void onContentLengthOverLimit() { |
+ mContentLengthOverLimit = true; |
+ cancel(); |
+ } |
+ |
+ @Override |
+ protected void onRequestComplete() { |
+ mListener.onRequestComplete(this); |
+ } |
+ |
+ @Override |
+ public int getHttpStatusCode() { |
+ int httpStatusCode = super.getHttpStatusCode(); |
+ |
+ // TODO(mef): Investigate the following: |
+ // If we have been able to successfully resume a previously interrupted |
+ // download, |
+ // the status code will be 206, not 200. Since the rest of the |
+ // application is |
+ // expecting 200 to indicate success, we need to fake it. |
+ if (httpStatusCode == 206) { |
+ httpStatusCode = 200; |
+ } |
+ return httpStatusCode; |
+ } |
+ |
+ @Override |
+ public IOException getException() { |
+ IOException ex = super.getException(); |
+ if (ex == null && mContentLengthOverLimit) { |
+ ex = new ResponseTooLargeException(); |
+ } |
+ return ex; |
+ } |
+ |
+ @Override |
+ public ByteBuffer getByteBuffer() { |
+ return ((ChunkedWritableByteChannel)getSink()).getByteBuffer(); |
+ } |
+ |
+ @Override |
+ public byte[] getResponseAsBytes() { |
+ return ((ChunkedWritableByteChannel)getSink()).getBytes(); |
+ } |
+} |