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.FilterInputStream;
008import java.io.IOException;
009import javax.servlet.ServletInputStream;
010
011/**
012 * A <code>PartInputStream</code> filters a <code>ServletInputStream</code>, 
013 * providing access to a single MIME part contained with in which ends with 
014 * the boundary specified.  It uses buffering to provide maximum performance.
015 * <p>
016 * Note the <code>readLine</code> method of <code>ServletInputStream</code>
017 * has the annoying habit of adding a \r\n to the end of the last line.  Since
018 * we want a byte-for-byte transfer, we have to cut those chars. This means 
019 * that we must always maintain at least 2 characters in our buffer to allow 
020 * us to trim when necessary.
021 * 
022 * @author Geoff Soutter
023 * @author Jason Hunter
024 * @version 1.4, 2002/11/01, fix for "unexpected end of part" caused by
025 *                           boundary newlines split across buffers
026 * @version 1.3, 2001/05/21, fix to handle boundaries crossing 64K mark
027 * @version 1.2, 2001/02/07, added read(byte[]) implementation for safety
028 * @version 1.1, 2000/11/26, fixed available() to never return negative
029 * @version 1.0, 2000/10/27, initial revision
030 */
031public class PartInputStream extends FilterInputStream {
032  /** boundary which "ends" the stream */
033  private String boundary;
034  
035  /** our buffer */
036  private byte [] buf = new byte[64*1024];  // 64k
037  
038  /** number of bytes we've read into the buffer */
039  private int count; 
040  
041  /** current position in the buffer */
042  private int pos;
043  
044  /** flag that indicates if we have encountered the boundary */
045  private boolean eof;
046    
047  /**
048   * Creates a <code>PartInputStream</code> which stops at the specified
049   * boundary from a <code>ServletInputStream<code>.
050   * 
051   * @param in  a servlet input stream.
052   * @param boundary the MIME boundary to stop at.
053   */
054  PartInputStream(ServletInputStream in, 
055                  String boundary) throws IOException {
056    super(in);
057    this.boundary = boundary;
058  }
059
060  /**
061   * Fill up our buffer from the underlying input stream, and check for the 
062   * boundary that signifies end-of-file. Users of this method must ensure 
063   * that they leave exactly 2 characters in the buffer before calling this 
064   * method (except the first time), so that we may only use these characters
065   * if a boundary is not found in the first line read.
066   * 
067   * @exception  IOException  if an I/O error occurs.
068   */
069  private void fill() throws IOException
070  {
071    if (eof)
072      return;
073    
074    // as long as we are not just starting up
075    if (count > 0)
076    {
077      // if the caller left the requisite amount spare in the buffer
078      if (count - pos == 2) {
079        // copy it back to the start of the buffer
080        System.arraycopy(buf, pos, buf, 0, count - pos);
081        count -= pos;
082        pos = 0;
083      } else {
084        // should never happen, but just in case
085        throw new IllegalStateException("fill() detected illegal buffer state");
086      }
087    }
088    
089    // Try and fill the entire buffer, starting at count, line by line
090    // but never read so close to the end that we might split a boundary
091    // Thanks to Tony Chu, tony.chu@brio.com, for the -2 suggestion.
092    int read = 0;
093    int boundaryLength = boundary.length();
094    int maxRead = buf.length - boundaryLength - 2;  // -2 is for /r/n
095    while (count < maxRead) {
096      // read a line
097      read = ((ServletInputStream)in).readLine(buf, count, buf.length - count);
098      // check for eof and boundary
099      if (read == -1) {
100        throw new IOException("unexpected end of part");
101      } else {
102        if (read >= boundaryLength) {
103          eof = true;
104          for (int i=0; i < boundaryLength; i++) {
105            if (boundary.charAt(i) != buf[count + i]) {
106              // Not the boundary!
107              eof = false;
108              break;
109            }
110          }
111          if (eof) {
112            break;
113          }
114        }
115      }
116      // success
117      count += read;
118    }
119  }
120  
121  /**
122   * See the general contract of the <code>read</code>
123   * method of <code>InputStream</code>.
124   * <p>
125   * Returns <code>-1</code> (end of file) when the MIME 
126   * boundary of this part is encountered.
127   *
128   * @return     the next byte of data, or <code>-1</code> if the end of the
129   *             stream is reached.
130   * @exception  IOException  if an I/O error occurs.
131   */
132  public int read() throws IOException {
133    if (count - pos <= 2) {
134      fill();
135      if (count - pos <= 2) {
136        return -1;
137      }
138    }
139    return buf[pos++] & 0xff;
140  }
141
142  /**
143   * See the general contract of the <code>read</code>
144   * method of <code>InputStream</code>.
145   * <p>
146   * Returns <code>-1</code> (end of file) when the MIME
147   * boundary of this part is encountered.
148   *
149   * @param      b     the buffer into which the data is read.
150   * @return     the total number of bytes read into the buffer, or
151   *             <code>-1</code> if there is no more data because the end
152   *             of the stream has been reached.
153   * @exception  IOException  if an I/O error occurs.
154   */
155  public int read(byte b[]) throws IOException {
156    return read(b, 0, b.length);
157  }
158
159  /**
160   * See the general contract of the <code>read</code>
161   * method of <code>InputStream</code>.
162   * <p>
163   * Returns <code>-1</code> (end of file) when the MIME 
164   * boundary of this part is encountered.
165   *
166   * @param      b     the buffer into which the data is read.
167   * @param      off   the start offset of the data.
168   * @param      len   the maximum number of bytes read.
169   * @return     the total number of bytes read into the buffer, or
170   *             <code>-1</code> if there is no more data because the end
171   *             of the stream has been reached.
172   * @exception  IOException  if an I/O error occurs.
173   */
174  public int read(byte b[], int off, int len) throws IOException
175  {
176    int total = 0;
177    if (len == 0) {
178      return 0;
179    }
180
181    int avail = count - pos - 2;
182    if (avail <= 0) {
183      fill();
184      avail = count - pos - 2;
185      if(avail <= 0) {
186        return -1;
187      }
188    }
189    int copy = Math.min(len, avail);
190    System.arraycopy(buf, pos, b, off, copy);
191    pos += copy;
192    total += copy;
193      
194    while (total < len) {
195      fill();
196      avail = count - pos - 2;
197      if(avail <= 0) {
198        return total;
199      }
200      copy = Math.min(len - total, avail);
201      System.arraycopy(buf, pos, b, off + total, copy);
202      pos += copy;
203      total += copy;
204    }
205    return total;
206  }
207
208  /**
209   * Returns the number of bytes that can be read from this input stream
210   * without blocking.  This is a standard <code>InputStream</code> idiom
211   * to deal with buffering gracefully, and is not same as the length of the
212   * part arriving in this stream.
213   *
214   * @return     the number of bytes that can be read from the input stream
215   *             without blocking.
216   * @exception  IOException  if an I/O error occurs.
217   */
218  public int available() throws IOException {
219    int avail = (count - pos - 2) + in.available();
220    // Never return a negative value
221    return (avail < 0 ? 0 : avail);
222  }
223
224  /**
225   * Closes this input stream and releases any system resources 
226   * associated with the stream. 
227   * <p>
228   * This method will read any unread data in the MIME part so that the next 
229   * part starts an an expected place in the parent <code>InputStream</code>.
230   * Note that if the client code forgets to call this method on error,
231   * <code>MultipartParser</code> will call it automatically if you call 
232   * <code>readNextPart()</code>.
233   *
234   * @exception  IOException  if an I/O error occurs.
235   */
236  public void close() throws IOException {
237    if (!eof) {
238      while (read(buf, 0, buf.length) != -1)
239        ; // do nothing
240    }
241  }
242}