blob: a643a1456230dc0d57adbba85a7644b460518b5a [file] [log] [blame]
// 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.
*/
public 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(UrlRequestContext requestContext,
String url, int priority, Map<String, String> headers,
HttpUrlRequestListener listener) {
this(requestContext, url, priority, headers,
new ChunkedWritableByteChannel(), listener);
mBufferFullResponse = true;
}
public ChromiumUrlRequest(UrlRequestContext requestContext,
String url, int priority, Map<String, String> headers,
WritableByteChannel sink, HttpUrlRequestListener listener) {
super(requestContext, url, convertRequestPriority(priority), headers,
sink);
mListener = listener;
}
private static int convertRequestPriority(int priority) {
switch (priority) {
case HttpUrlRequest.REQUEST_PRIORITY_IDLE:
return UrlRequestPriority.IDLE;
case HttpUrlRequest.REQUEST_PRIORITY_LOWEST:
return UrlRequestPriority.LOWEST;
case HttpUrlRequest.REQUEST_PRIORITY_LOW:
return UrlRequestPriority.LOW;
case HttpUrlRequest.REQUEST_PRIORITY_MEDIUM:
return UrlRequestPriority.MEDIUM;
case HttpUrlRequest.REQUEST_PRIORITY_HIGHEST:
return UrlRequestPriority.HIGHEST;
default:
return UrlRequestPriority.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;
}
}
mListener.onResponseStarted(this);
}
@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();
}
}