Home | History | Annotate | Download | only in http
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package org.apache.harmony.luni.internal.net.www.protocol.http;
     18 
     19 import java.io.IOException;
     20 import java.io.InputStream;
     21 import java.io.OutputStream;
     22 import java.net.CacheRequest;
     23 
     24 /**
     25  * An input stream for the body of an HTTP response.
     26  *
     27  * <p>Since a single socket's input stream may be used to read multiple HTTP
     28  * responses from the same server, subclasses shouldn't close the socket stream.
     29  *
     30  * <p>A side effect of reading an HTTP response is that the response cache
     31  * is populated. If the stream is closed early, that cache entry will be
     32  * invalidated.
     33  */
     34 abstract class AbstractHttpInputStream extends InputStream {
     35     protected final InputStream in;
     36     protected final HttpURLConnectionImpl httpURLConnection;
     37     protected final CacheRequest cacheRequest;
     38     protected final OutputStream cacheOut;
     39     protected boolean closed;
     40     private byte[] skipBuffer;
     41 
     42     AbstractHttpInputStream(InputStream in, HttpURLConnectionImpl httpURLConnection,
     43             CacheRequest cacheRequest) throws IOException {
     44         this.in = in;
     45         this.httpURLConnection = httpURLConnection;
     46         this.cacheRequest = cacheRequest;
     47         this.cacheOut = cacheRequest != null ? cacheRequest.getBody() : null;
     48     }
     49 
     50     /**
     51      * read() is implemented using read(byte[], int, int) so subclasses only
     52      * need to override the latter.
     53      */
     54     @Override public final int read() throws IOException {
     55         byte[] buffer = new byte[1];
     56         int count = read(buffer, 0, 1);
     57         return count == -1 ? -1 : buffer[0] & 0xff;
     58     }
     59 
     60     /**
     61      * skip(long) is implemented using read(byte[], int, int) so subclasses
     62      * only need to override the latter.
     63      */
     64     @Override public final long skip(long n) throws IOException {
     65         if (skipBuffer == null) {
     66             skipBuffer = new byte[4096];
     67         }
     68         long total = 0;
     69         while (total < n) {
     70             // Calling read() ensures the skipped bytes make it into the response cache.
     71             int count = read(skipBuffer, 0, (int) Math.min(n - total, skipBuffer.length));
     72             if (count == -1) {
     73                 break;
     74             }
     75             total += count;
     76         }
     77         return total;
     78     }
     79 
     80     protected final void checkBounds(byte[] buffer, int offset, int count) {
     81         if (offset < 0 || offset > buffer.length || count < 0 || buffer.length - offset < count) {
     82             throw new ArrayIndexOutOfBoundsException(
     83                     "offset=" + offset + ", buffer.length=" + buffer.length + ", count=" + count);
     84         }
     85     }
     86 
     87     protected final void checkNotClosed() throws IOException {
     88         if (closed) {
     89             throw new IOException("stream closed");
     90         }
     91     }
     92 
     93     protected final void cacheWrite(byte[] buffer, int offset, int count) throws IOException {
     94         if (cacheOut != null) {
     95             cacheOut.write(buffer, offset, count);
     96         }
     97     }
     98 
     99     /**
    100      * Closes the cache entry and makes the socket available for reuse. This
    101      * should be invoked when the end of the body has been reached.
    102      */
    103     protected final void endOfInput(boolean reuseSocket) throws IOException {
    104         if (cacheRequest != null) {
    105             cacheOut.close();
    106         }
    107         httpURLConnection.releaseSocket(reuseSocket);
    108     }
    109 
    110     /**
    111      * Calls abort on the cache entry and disconnects the socket. This
    112      * should be invoked when the connection is closed unexpectedly to
    113      * invalidate the cache entry and to prevent the HTTP connection from
    114      * being reused. HTTP messages are sent in serial so whenever a message
    115      * cannot be read to completion, subsequent messages cannot be read
    116      * either and the connection must be discarded.
    117      *
    118      * <p>An earlier implementation skipped the remaining bytes, but this
    119      * requires that the entire transfer be completed. If the intention was
    120      * to cancel the transfer, closing the connection is the only solution.
    121      */
    122     protected final void unexpectedEndOfInput() {
    123         if (cacheRequest != null) {
    124             cacheRequest.abort();
    125         }
    126         httpURLConnection.releaseSocket(false);
    127     }
    128 }
    129