ChromiumUrlRequest.java revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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
5package org.chromium.net;
6
7import java.io.IOException;
8import java.nio.ByteBuffer;
9import java.nio.channels.WritableByteChannel;
10import java.util.Map;
11
12/**
13 * Network request using the native http stack implementation.
14 */
15class 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(ChromiumUrlRequestContext 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(ChromiumUrlRequestContext 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