001// Copyright (C) 1999-2001 by Jason Hunter <jhunter_AT_acm_DOT_org>.
002// All rights reserved.  Use of this class is limited.
003// Please see the LICENSE for more information.
004 
005package com.oreilly.servlet.multipart;
006
007import java.io.IOException;
008
009import javax.servlet.ServletInputStream;
010
011/**
012 * A <code>LimitedServletInputStream</code> wraps another 
013 * <code>ServletInputStream</code> in order to keep track of how many bytes 
014 * have been read and detect when the Content-Length limit has been reached. 
015 * This is necessary since some servlet containers are slow to notice the end 
016 * of stream and cause the client code to hang if it tries to read past it.
017 * 
018 * @author Jason Hunter
019 * @author Geoff Soutter
020 * @version 1.0, 2000/10/27, initial revision
021 */
022public class LimitedServletInputStream extends ServletInputStream {
023  
024  /** input stream we are filtering */
025  private ServletInputStream in;
026  
027  /** number of bytes to read before giving up */
028  private int totalExpected;
029  
030  /** number of bytes we have currently read */
031  private int totalRead = 0;
032  
033  /**
034   * Creates a <code>LimitedServletInputStream</code> with the specified
035   * length limit that wraps the provided <code>ServletInputStream</code>.
036   */
037  public LimitedServletInputStream(ServletInputStream in, int totalExpected) {
038    this.in = in;
039    this.totalExpected = totalExpected;
040  }
041
042  /**
043   * Implement length limitation on top of the <code>readLine</code> method of
044   * the wrapped <code>ServletInputStream</code>.
045   *
046   * @param b    an array of bytes into which data is read.
047   * @param off  an integer specifying the character at which
048   *        this method begins reading.
049   * @param len  an integer specifying the maximum number of 
050   *        bytes to read.
051   * @return     an integer specifying the actual number of bytes 
052   *        read, or -1 if the end of the stream is reached.
053   * @exception  IOException  if an I/O error occurs.
054   */
055  public int readLine(byte b[], int off, int len) throws IOException {
056    int result, left = totalExpected - totalRead;
057    if (left <= 0) {
058      return -1;
059    } else {
060      result = ((ServletInputStream)in).readLine(b, off, Math.min(left, len));
061    }
062    if (result > 0) {
063      totalRead += result;
064    }
065    return result;    
066  }
067
068  /**
069   * Implement length limitation on top of the <code>read</code> method of 
070   * the wrapped <code>ServletInputStream</code>.
071   *
072   * @return     the next byte of data, or <code>-1</code> if the end of the
073   *             stream is reached.
074   * @exception  IOException  if an I/O error occurs.
075   */
076  public int read() throws IOException {
077    if (totalRead >= totalExpected) {
078      return -1;
079    }
080
081    int result = in.read();
082    if (result != -1) {
083      totalRead++;
084    }
085    return result;
086  }
087  
088  /**
089   * Implement length limitation on top of the <code>read</code> method of 
090   * the wrapped <code>ServletInputStream</code>.
091   *
092   * @param      b     destination buffer.
093   * @param      off   offset at which to start storing bytes.
094   * @param      len   maximum number of bytes to read.
095   * @return     the number of bytes read, or <code>-1</code> if the end of
096   *             the stream has been reached.
097   * @exception  IOException  if an I/O error occurs.
098   */
099  public int read( byte b[], int off, int len ) throws IOException {
100    int result, left = totalExpected - totalRead;
101    if (left <= 0) {
102      return -1;
103    } else {
104      result = in.read(b, off, Math.min(left, len));
105    }
106    if (result > 0) {
107      totalRead += result;
108    }
109    return result;    
110  }
111}