Home | History | Annotate | Download | only in net
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 package org.chromium.net;
      6 
      7 import java.io.IOException;
      8 import java.nio.ByteBuffer;
      9 import java.nio.channels.WritableByteChannel;
     10 import java.util.Map;
     11 
     12 /**
     13  * Network request using the native http stack implementation.
     14  */
     15 class ChromiumUrlRequest extends UrlRequest implements HttpUrlRequest {
     16 
     17     private final HttpUrlRequestListener mListener;
     18 
     19     private boolean mBufferFullResponse;
     20 
     21     private long mOffset;
     22 
     23     private long mContentLength;
     24 
     25     private long mContentLengthLimit;
     26 
     27     private boolean mCancelIfContentLengthOverLimit;
     28 
     29     private boolean mContentLengthOverLimit;
     30 
     31     private boolean mSkippingToOffset;
     32 
     33     private long mSize;
     34 
     35     public ChromiumUrlRequest(UrlRequestContext requestContext,
     36             String url, int priority, Map<String, String> headers,
     37             HttpUrlRequestListener listener) {
     38         this(requestContext, url, priority, headers,
     39                 new ChunkedWritableByteChannel(), listener);
     40         mBufferFullResponse = true;
     41     }
     42 
     43     public ChromiumUrlRequest(UrlRequestContext requestContext,
     44             String url, int priority, Map<String, String> headers,
     45             WritableByteChannel sink, HttpUrlRequestListener listener) {
     46         super(requestContext, url, convertRequestPriority(priority), headers,
     47                 sink);
     48         mListener = listener;
     49     }
     50 
     51     private static int convertRequestPriority(int priority) {
     52         switch (priority) {
     53             case HttpUrlRequest.REQUEST_PRIORITY_IDLE:
     54                 return UrlRequestPriority.IDLE;
     55             case HttpUrlRequest.REQUEST_PRIORITY_LOWEST:
     56                 return UrlRequestPriority.LOWEST;
     57             case HttpUrlRequest.REQUEST_PRIORITY_LOW:
     58                 return UrlRequestPriority.LOW;
     59             case HttpUrlRequest.REQUEST_PRIORITY_MEDIUM:
     60                 return UrlRequestPriority.MEDIUM;
     61             case HttpUrlRequest.REQUEST_PRIORITY_HIGHEST:
     62                 return UrlRequestPriority.HIGHEST;
     63             default:
     64                 return UrlRequestPriority.MEDIUM;
     65         }
     66     }
     67 
     68     @Override
     69     public void setOffset(long offset) {
     70         mOffset = offset;
     71         if (offset != 0) {
     72             addHeader("Range", "bytes=" + offset + "-");
     73         }
     74     }
     75 
     76     @Override
     77     public long getContentLength() {
     78         return mContentLength;
     79     }
     80 
     81     @Override
     82     public void setContentLengthLimit(long limit, boolean cancelEarly) {
     83         mContentLengthLimit = limit;
     84         mCancelIfContentLengthOverLimit = cancelEarly;
     85     }
     86 
     87     @Override
     88     protected void onResponseStarted() {
     89         super.onResponseStarted();
     90 
     91         mContentLength = super.getContentLength();
     92         if (mContentLengthLimit > 0 && mContentLength > mContentLengthLimit
     93                 && mCancelIfContentLengthOverLimit) {
     94             onContentLengthOverLimit();
     95             return;
     96         }
     97 
     98         if (mBufferFullResponse && mContentLength != -1
     99                 && !mContentLengthOverLimit) {
    100             ((ChunkedWritableByteChannel)getSink()).setCapacity(
    101                     (int)mContentLength);
    102         }
    103 
    104         if (mOffset != 0) {
    105             // The server may ignore the request for a byte range.
    106             if (super.getHttpStatusCode() == 200) {
    107                 if (mContentLength != -1) {
    108                     mContentLength -= mOffset;
    109                 }
    110                 mSkippingToOffset = true;
    111             } else {
    112                 mSize = mOffset;
    113             }
    114         }
    115         mListener.onResponseStarted(this);
    116     }
    117 
    118     @Override
    119     protected void onBytesRead(ByteBuffer buffer) {
    120         if (mContentLengthOverLimit) {
    121             return;
    122         }
    123 
    124         int size = buffer.remaining();
    125         mSize += size;
    126         if (mSkippingToOffset) {
    127             if (mSize <= mOffset) {
    128                 return;
    129             } else {
    130                 mSkippingToOffset = false;
    131                 buffer.position((int)(mOffset - (mSize - size)));
    132             }
    133         }
    134 
    135         if (mContentLengthLimit != 0 && mSize > mContentLengthLimit) {
    136             buffer.limit(size - (int)(mSize - mContentLengthLimit));
    137             super.onBytesRead(buffer);
    138             onContentLengthOverLimit();
    139             return;
    140         }
    141 
    142         super.onBytesRead(buffer);
    143     }
    144 
    145     private void onContentLengthOverLimit() {
    146         mContentLengthOverLimit = true;
    147         cancel();
    148     }
    149 
    150     @Override
    151     protected void onRequestComplete() {
    152         mListener.onRequestComplete(this);
    153     }
    154 
    155     @Override
    156     public int getHttpStatusCode() {
    157         int httpStatusCode = super.getHttpStatusCode();
    158 
    159         // TODO(mef): Investigate the following:
    160         // If we have been able to successfully resume a previously interrupted
    161         // download,
    162         // the status code will be 206, not 200. Since the rest of the
    163         // application is
    164         // expecting 200 to indicate success, we need to fake it.
    165         if (httpStatusCode == 206) {
    166             httpStatusCode = 200;
    167         }
    168         return httpStatusCode;
    169     }
    170 
    171     @Override
    172     public IOException getException() {
    173         IOException ex = super.getException();
    174         if (ex == null && mContentLengthOverLimit) {
    175             ex = new ResponseTooLargeException();
    176         }
    177         return ex;
    178     }
    179 
    180     @Override
    181     public ByteBuffer getByteBuffer() {
    182         return ((ChunkedWritableByteChannel)getSink()).getByteBuffer();
    183     }
    184 
    185     @Override
    186     public byte[] getResponseAsBytes() {
    187         return ((ChunkedWritableByteChannel)getSink()).getBytes();
    188     }
    189 }
    190