mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/ContentLengthInputStream.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     1 /*
     2  * ====================================================================
     3  * Licensed to the Apache Software Foundation (ASF) under one
     4  * or more contributor license agreements.  See the NOTICE file
     5  * distributed with this work for additional information
     6  * regarding copyright ownership.  The ASF licenses this file
     7  * to you under the Apache License, Version 2.0 (the
     8  * "License"); you may not use this file except in compliance
     9  * with the License.  You may obtain a copy of the License at
    10  *
    11  *   http://www.apache.org/licenses/LICENSE-2.0
    12  *
    13  * Unless required by applicable law or agreed to in writing,
    14  * software distributed under the License is distributed on an
    15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    16  * KIND, either express or implied.  See the License for the
    17  * specific language governing permissions and limitations
    18  * under the License.
    19  * ====================================================================
    20  *
    21  * This software consists of voluntary contributions made by many
    22  * individuals on behalf of the Apache Software Foundation.  For more
    23  * information on the Apache Software Foundation, please see
    24  * <http://www.apache.org/>.
    25  *
    26  */
    28 package ch.boye.httpclientandroidlib.impl.io;
    30 import java.io.IOException;
    31 import java.io.InputStream;
    33 import ch.boye.httpclientandroidlib.ConnectionClosedException;
    34 import ch.boye.httpclientandroidlib.io.BufferInfo;
    35 import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
    37 /**
    38  * Input stream that cuts off after a defined number of bytes. This class
    39  * is used to receive content of HTTP messages where the end of the content
    40  * entity is determined by the value of the <code>Content-Length header</code>.
    41  * Entities transferred using this stream can be maximum {@link Long#MAX_VALUE}
    42  * long.
    43  * <p>
    44  * Note that this class NEVER closes the underlying stream, even when close
    45  * gets called.  Instead, it will read until the "end" of its limit on
    46  * close, which allows for the seamless execution of subsequent HTTP 1.1
    47  * requests, while not requiring the client to remember to read the entire
    48  * contents of the response.
    49  *
    50  *
    51  * @since 4.0
    52  */
    53 public class ContentLengthInputStream extends InputStream {
    55     private static final int BUFFER_SIZE = 2048;
    56     /**
    57      * The maximum number of bytes that can be read from the stream. Subsequent
    58      * read operations will return -1.
    59      */
    60     private long contentLength;
    62     /** The current position */
    63     private long pos = 0;
    65     /** True if the stream is closed. */
    66     private boolean closed = false;
    68     /**
    69      * Wrapped input stream that all calls are delegated to.
    70      */
    71     private SessionInputBuffer in = null;
    73     /**
    74      * Wraps a session input buffer and cuts off output after a defined number
    75      * of bytes.
    76      *
    77      * @param in The session input buffer
    78      * @param contentLength The maximum number of bytes that can be read from
    79      * the stream. Subsequent read operations will return -1.
    80      */
    81     public ContentLengthInputStream(final SessionInputBuffer in, long contentLength) {
    82         super();
    83         if (in == null) {
    84             throw new IllegalArgumentException("Input stream may not be null");
    85         }
    86         if (contentLength < 0) {
    87             throw new IllegalArgumentException("Content length may not be negative");
    88         }
    89         this.in = in;
    90         this.contentLength = contentLength;
    91     }
    93     /**
    94      * <p>Reads until the end of the known length of content.</p>
    95      *
    96      * <p>Does not close the underlying socket input, but instead leaves it
    97      * primed to parse the next response.</p>
    98      * @throws IOException If an IO problem occurs.
    99      */
   100     public void close() throws IOException {
   101         if (!closed) {
   102             try {
   103                 if (pos < contentLength) {
   104                     byte buffer[] = new byte[BUFFER_SIZE];
   105                     while (read(buffer) >= 0) {
   106                     }
   107                 }
   108             } finally {
   109                 // close after above so that we don't throw an exception trying
   110                 // to read after closed!
   111                 closed = true;
   112             }
   113         }
   114     }
   116     public int available() throws IOException {
   117         if (this.in instanceof BufferInfo) {
   118             int len = ((BufferInfo) this.in).length();
   119             return Math.min(len, (int) (this.contentLength - this.pos));
   120         } else {
   121             return 0;
   122         }
   123     }
   125     /**
   126      * Read the next byte from the stream
   127      * @return The next byte or -1 if the end of stream has been reached.
   128      * @throws IOException If an IO problem occurs
   129      * @see java.io.InputStream#read()
   130      */
   131     public int read() throws IOException {
   132         if (closed) {
   133             throw new IOException("Attempted read from closed stream.");
   134         }
   136         if (pos >= contentLength) {
   137             return -1;
   138         }
   139         int b = this.in.read();
   140         if (b == -1) {
   141             if (pos < contentLength) {
   142                 throw new ConnectionClosedException(
   143                         "Premature end of Content-Length delimited message body (expected: "
   144                         + contentLength + "; received: " + pos);
   145             }
   146         } else {
   147             pos++;
   148         }
   149         return b;
   150     }
   152     /**
   153      * Does standard {@link InputStream#read(byte[], int, int)} behavior, but
   154      * also notifies the watcher when the contents have been consumed.
   155      *
   156      * @param b     The byte array to fill.
   157      * @param off   Start filling at this position.
   158      * @param len   The number of bytes to attempt to read.
   159      * @return The number of bytes read, or -1 if the end of content has been
   160      *  reached.
   161      *
   162      * @throws java.io.IOException Should an error occur on the wrapped stream.
   163      */
   164     public int read (byte[] b, int off, int len) throws java.io.IOException {
   165         if (closed) {
   166             throw new IOException("Attempted read from closed stream.");
   167         }
   169         if (pos >= contentLength) {
   170             return -1;
   171         }
   173         if (pos + len > contentLength) {
   174             len = (int) (contentLength - pos);
   175         }
   176         int count = this.in.read(b, off, len);
   177         if (count == -1 && pos < contentLength) {
   178             throw new ConnectionClosedException(
   179                     "Premature end of Content-Length delimited message body (expected: "
   180                     + contentLength + "; received: " + pos);
   181         }
   182         if (count > 0) {
   183             pos += count;
   184         }
   185         return count;
   186     }
   189     /**
   190      * Read more bytes from the stream.
   191      * @param b The byte array to put the new data in.
   192      * @return The number of bytes read into the buffer.
   193      * @throws IOException If an IO problem occurs
   194      * @see java.io.InputStream#read(byte[])
   195      */
   196     public int read(byte[] b) throws IOException {
   197         return read(b, 0, b.length);
   198     }
   200     /**
   201      * Skips and discards a number of bytes from the input stream.
   202      * @param n The number of bytes to skip.
   203      * @return The actual number of bytes skipped. <= 0 if no bytes
   204      * are skipped.
   205      * @throws IOException If an error occurs while skipping bytes.
   206      * @see InputStream#skip(long)
   207      */
   208     public long skip(long n) throws IOException {
   209         if (n <= 0) {
   210             return 0;
   211         }
   212         byte[] buffer = new byte[BUFFER_SIZE];
   213         // make sure we don't skip more bytes than are
   214         // still available
   215         long remaining = Math.min(n, this.contentLength - this.pos);
   216         // skip and keep track of the bytes actually skipped
   217         long count = 0;
   218         while (remaining > 0) {
   219             int l = read(buffer, 0, (int)Math.min(BUFFER_SIZE, remaining));
   220             if (l == -1) {
   221                 break;
   222             }
   223             count += l;
   224             remaining -= l;
   225         }
   226         return count;
   227     }
   228 }

mercurial