Home » HttpComponents-Core-4.0.1 » org.apache.http.io » [javadoc | source]

    1   /*
    2    * $HeadURL: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpcore/tags/4.0-alpha2/src/java/org/apache/http/io/ContentLengthInputStream.java $
    3    * $Revision: 390883 $
    4    * $Date: 2006-04-02 20:39:50 +0200 (Sun, 02 Apr 2006) $
    5    *
    6    * ====================================================================
    7    *
    8    *  Copyright 1999-2006 The Apache Software Foundation
    9    *
   10    *  Licensed under the Apache License, Version 2.0 (the "License");
   11    *  you may not use this file except in compliance with the License.
   12    *  You may obtain a copy of the License at
   13    *
   14    *      http://www.apache.org/licenses/LICENSE-2.0
   15    *
   16    *  Unless required by applicable law or agreed to in writing, software
   17    *  distributed under the License is distributed on an "AS IS" BASIS,
   18    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   19    *  See the License for the specific language governing permissions and
   20    *  limitations under the License.
   21    * ====================================================================
   22    *
   23    * This software consists of voluntary contributions made by many
   24    * individuals on behalf of the Apache Software Foundation.  For more
   25    * information on the Apache Software Foundation, please see
   26    * <http://www.apache.org/>.
   27    *
   28    */
   29   
   30   package org.apache.http.io;
   31   
   32   import java.io.IOException;
   33   import java.io.InputStream;
   34   
   35   /**
   36    * <p>
   37    * This class cuts the wrapped InputStream off after a specified number of bytes.
   38    * </p>
   39    * <p>
   40    * Note that this class NEVER closes the underlying stream, even when close
   41    * gets called.  Instead, it will read until the "end" of its chunking on close,
   42    * which allows for the seamless invocation of subsequent HTTP 1.1 calls, while
   43    * not requiring the client to remember to read the entire contents of the
   44    * response.
   45    * </p>
   46    * <p>Implementation note: Choices abound. One approach would pass
   47    * through the {@link InputStream#mark} and {@link InputStream#reset} calls to
   48    * the underlying stream.  That's tricky, though, because you then have to
   49    * start duplicating the work of keeping track of how much a reset rewinds.
   50    * Further, you have to watch out for the "readLimit", and since the semantics
   51    * for the readLimit leave room for differing implementations, you might get
   52    * into a lot of trouble.</p>
   53    *
   54    * <p>Alternatively, you could make this class extend {@link java.io.BufferedInputStream}
   55    * and then use the protected members of that class to avoid duplicated effort.
   56    * That solution has the side effect of adding yet another possible layer of
   57    * buffering.</p>
   58    *
   59    * <p>Then, there is the simple choice, which this takes - simply don't
   60    * support {@link InputStream#mark} and {@link InputStream#reset}.  That choice
   61    * has the added benefit of keeping this class very simple.</p>
   62    *
   63    * @author Ortwin Glueck
   64    * @author Eric Johnson
   65    * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
   66    * @since 2.0
   67    */
   68   public class ContentLengthInputStream extends InputStream {
   69       
   70       private static int BUFFER_SIZE = 2048;
   71       /**
   72        * The maximum number of bytes that can be read from the stream. Subsequent
   73        * read operations will return -1.
   74        */
   75       private long contentLength;
   76   
   77       /** The current position */
   78       private long pos = 0;
   79   
   80       /** True if the stream is closed. */
   81       private boolean closed = false;
   82   
   83       /**
   84        * Wrapped input stream that all calls are delegated to.
   85        */
   86       private HttpDataReceiver in = null;
   87   
   88       /**
   89        * Creates a new length limited stream
   90        *
   91        * @param in The stream to wrap
   92        * @param contentLength The maximum number of bytes that can be read from
   93        * the stream. Subsequent read operations will return -1.
   94        * 
   95        * @since 3.0
   96        */
   97       public ContentLengthInputStream(final HttpDataReceiver in, long contentLength) {
   98           super();
   99           if (in == null) {
  100               throw new IllegalArgumentException("Input stream may not be null");
  101           }
  102           if (contentLength < 0) {
  103               throw new IllegalArgumentException("Content length may not be negative");
  104           }
  105           this.in = in;
  106           this.contentLength = contentLength;
  107       }
  108   
  109       /**
  110        * <p>Reads until the end of the known length of content.</p>
  111        *
  112        * <p>Does not close the underlying socket input, but instead leaves it
  113        * primed to parse the next response.</p>
  114        * @throws IOException If an IO problem occurs.
  115        */
  116       public void close() throws IOException {
  117           if (!closed) {
  118               try {
  119                   byte buffer[] = new byte[BUFFER_SIZE];
  120                   while (read(buffer) >= 0) {
  121                   }
  122               } finally {
  123                   // close after above so that we don't throw an exception trying
  124                   // to read after closed!
  125                   closed = true;
  126               }
  127           }
  128       }
  129   
  130   
  131       /**
  132        * Read the next byte from the stream
  133        * @return The next byte or -1 if the end of stream has been reached.
  134        * @throws IOException If an IO problem occurs
  135        * @see java.io.InputStream#read()
  136        */
  137       public int read() throws IOException {
  138           if (closed) {
  139               throw new IOException("Attempted read from closed stream.");
  140           }
  141   
  142           if (pos >= contentLength) {
  143               return -1;
  144           }
  145           pos++;
  146           return this.in.read();
  147       }
  148   
  149       /**
  150        * Does standard {@link InputStream#read(byte[], int, int)} behavior, but
  151        * also notifies the watcher when the contents have been consumed.
  152        *
  153        * @param b     The byte array to fill.
  154        * @param off   Start filling at this position.
  155        * @param len   The number of bytes to attempt to read.
  156        * @return The number of bytes read, or -1 if the end of content has been
  157        *  reached.
  158        *
  159        * @throws java.io.IOException Should an error occur on the wrapped stream.
  160        */
  161       public int read (byte[] b, int off, int len) throws java.io.IOException {
  162           if (closed) {
  163               throw new IOException("Attempted read from closed stream.");
  164           }
  165   
  166           if (pos >= contentLength) {
  167               return -1;
  168           }
  169   
  170           if (pos + len > contentLength) {
  171               len = (int) (contentLength - pos);
  172           }
  173           int count = this.in.read(b, off, len);
  174           pos += count;
  175           return count;
  176       }
  177   
  178   
  179       /**
  180        * Read more bytes from the stream.
  181        * @param b The byte array to put the new data in.
  182        * @return The number of bytes read into the buffer.
  183        * @throws IOException If an IO problem occurs
  184        * @see java.io.InputStream#read(byte[])
  185        */
  186       public int read(byte[] b) throws IOException {
  187           return read(b, 0, b.length);
  188       }
  189   
  190       /**
  191        * Skips and discards a number of bytes from the input stream.
  192        * @param n The number of bytes to skip.
  193        * @return The actual number of bytes skipped. <= 0 if no bytes
  194        * are skipped.
  195        * @throws IOException If an error occurs while skipping bytes.
  196        * @see InputStream#skip(long)
  197        */
  198       public long skip(long n) throws IOException {
  199           if (n <= 0) {
  200               return 0;
  201           }
  202           byte[] buffer = new byte[BUFFER_SIZE];
  203           // make sure we don't skip more bytes than are 
  204           // still available
  205           long remaining = Math.min(n, this.contentLength - this.pos); 
  206           // skip and keep track of the bytes actually skipped
  207           long count = 0;
  208           while (remaining > 0) {
  209               int l = read(buffer, 0, (int)Math.min(BUFFER_SIZE, remaining));
  210               if (l == -1) {
  211                   break;
  212               }
  213               count += l;
  214               remaining -= l;
  215           }
  216           this.pos += count;
  217           return count;
  218       }
  219   }

Home » HttpComponents-Core-4.0.1 » org.apache.http.io » [javadoc | source]