View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  package org.apache.commons.fileupload;
    18
    19  import static java.lang.String.format;
    20
    21  import java.io.ByteArrayOutputStream;
    22  import java.io.IOException;
    23  import java.io.InputStream;
    24  import java.io.OutputStream;
    25  import java.io.UnsupportedEncodingException;
    26
    27  import org.apache.commons.fileupload.FileUploadBase.FileUploadIOException;
    28  import org.apache.commons.fileupload.util.Closeable;
    29  import org.apache.commons.fileupload.util.Streams;
    30
    31  /**
    32   * <p> Low level API for processing file uploads.
    33   *
    34   * <p> This class can be used to process data streams conforming to MIME
    35   * 'multipart' format as defined in
    36   * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Arbitrarily
    37   * large amounts of data in the stream can be processed under constant
    38   * memory usage.
    39   *
    40   * <p> The format of the stream is defined in the following way:<br>
    41   *
    42   * <code>
    43   *   multipart-body := preamble 1*encapsulation close-delimiter epilogue<br>
    44   *   encapsulation := delimiter body CRLF<br>
    45   *   delimiter := "--" boundary CRLF<br>
    46   *   close-delimiter := "--" boundary "--"<br>
    47   *   preamble := &lt;ignore&gt;<br>
    48   *   epilogue := &lt;ignore&gt;<br>
    49   *   body := header-part CRLF body-part<br>
    50   *   header-part := 1*header CRLF<br>
    51   *   header := header-name ":" header-value<br>
    52   *   header-name := &lt;printable ascii characters except ":"&gt;<br>
    53   *   header-value := &lt;any ascii characters except CR &amp; LF&gt;<br>
    54   *   body-data := &lt;arbitrary data&gt;<br>
    55   * </code>
    56   *
    57   * <p>Note that body-data can contain another mulipart entity.  There
    58   * is limited support for single pass processing of such nested
    59   * streams.  The nested stream is <strong>required</strong> to have a
    60   * boundary token of the same length as the parent stream (see {@link
    61   * #setBoundary(byte[])}).
    62   *
    63   * <p>Here is an example of usage of this class.<br>
    64   *
    65   * <pre>
    66   *   try {
    67   *     MultipartStream multipartStream = new MultipartStream(input, boundary);
    68   *     boolean nextPart = multipartStream.skipPreamble();
    69   *     OutputStream output;
    70   *     while(nextPart) {
    71   *       String header = multipartStream.readHeaders();
    72   *       // process headers
    73   *       // create some output stream
    74   *       multipartStream.readBodyData(output);
    75   *       nextPart = multipartStream.readBoundary();
    76   *     }
    77   *   } catch(MultipartStream.MalformedStreamException e) {
    78   *     // the stream failed to follow required syntax
    79   *   } catch(IOException e) {
    80   *     // a read or write error occurred
    81   *   }
    82   * </pre>
    83   */
    84  public class MultipartStream {
    85
    86      /**
    87       * Internal class, which is used to invoke the
    88       * {@link ProgressListener}.
    89       */
    90      public static class ProgressNotifier {
    91
    92          /**
    93           * The listener to invoke.
    94           */
    95          private final ProgressListener listener;
    96
    97          /**
    98           * Number of expected bytes, if known, or -1.
    99           */
    100         private final long contentLength;
    101
    102         /**
    103          * Number of bytes, which have been read so far.
    104          */
    105         private long bytesRead;
    106
    107         /**
    108          * Number of items, which have been read so far.
    109          */
    110         private int items;
    111
    112         /**
    113          * Creates a new instance with the given listener
    114          * and content length.
    115          *
    116          * @param pListener The listener to invoke.
    117          * @param pContentLength The expected content length.
    118          */
    119         ProgressNotifier(ProgressListener pListener, long pContentLength) {
    120             listener = pListener;
    121             contentLength = pContentLength;
    122         }
    123
    124         /**
    125          * Called to indicate that bytes have been read.
    126          *
    127          * @param pBytes Number of bytes, which have been read.
    128          */
    129         void noteBytesRead(int pBytes) {
    130             /* Indicates, that the given number of bytes have been read from
    131              * the input stream.
    132              */
    133             bytesRead += pBytes;
    134             notifyListener();
    135         }
    136
    137         /**
    138          * Called to indicate, that a new file item has been detected.
    139          */
    140         void noteItem() {
    141             ++items;
    142             notifyListener();
    143         }
    144
    145         /**
    146          * Called for notifying the listener.
    147          */
    148         private void notifyListener() {
    149             if (listener != null) {
    150                 listener.update(bytesRead, contentLength, items);
    151             }
    152         }
    153
    154     }
    155
    156     // ----------------------------------------------------- Manifest constants
    157
    158     /**
    159      * The Carriage Return ASCII character value.
    160      */
    161     public static final byte CR = 0x0D;
    162
    163     /**
    164      * The Line Feed ASCII character value.
    165      */
    166     public static final byte LF = 0x0A;
    167
    168     /**
    169      * The dash (-) ASCII character value.
    170      */
    171     public static final byte DASH = 0x2D;
    172
    173     /**
    174      * The maximum length of <code>header-part</code> that will be
    175      * processed (10 kilobytes = 10240 bytes.).
    176      */
    177     public static final int HEADER_PART_SIZE_MAX = 10240;
    178
    179     /**
    180      * The default length of the buffer used for processing a request.
    181      */
    182     protected static final int DEFAULT_BUFSIZE = 4096;
    183
    184     /**
    185      * A byte sequence that marks the end of <code>header-part</code>
    186      * (<code>CRLFCRLF</code>).
    187      */
    188     protected static final byte[] HEADER_SEPARATOR = {CR, LF, CR, LF};
    189
    190     /**
    191      * A byte sequence that that follows a delimiter that will be
    192      * followed by an encapsulation (<code>CRLF</code>).
    193      */
    194     protected static final byte[] FIELD_SEPARATOR = {CR, LF};
    195
    196     /**
    197      * A byte sequence that that follows a delimiter of the last
    198      * encapsulation in the stream (<code>--</code>).
    199      */
    200     protected static final byte[] STREAM_TERMINATOR = {DASH, DASH};
    201
    202     /**
    203      * A byte sequence that precedes a boundary (<code>CRLF--</code>).
    204      */
    205     protected static final byte[] BOUNDARY_PREFIX = {CR, LF, DASH, DASH};
    206
    207     // ----------------------------------------------------------- Data members
    208
    209     /**
    210      * The input stream from which data is read.
    211      */
    212     private final InputStream input;
    213
    214     /**
    215      * The length of the boundary token plus the leading <code>CRLF--</code>.
    216      */
    217     private int boundaryLength;
    218
    219     /**
    220      * The amount of data, in bytes, that must be kept in the buffer in order
    221      * to detect delimiters reliably.
    222      */
    223     private final int keepRegion;
    224
    225     /**
    226      * The byte sequence that partitions the stream.
    227      */
    228     private final byte[] boundary;
    229
    230     /**
    231      * The table for Knuth-Morris-Pratt search algorithm.
    232      */
    233     private final int[] boundaryTable;
    234
    235     /**
    236      * The length of the buffer used for processing the request.
    237      */
    238     private final int bufSize;
    239
    240     /**
    241      * The buffer used for processing the request.
    242      */
    243     private final byte[] buffer;
    244
    245     /**
    246      * The index of first valid character in the buffer.
    247      * <br>
    248      * 0 <= head < bufSize
    249      */
    250     private int head;
    251
    252     /**
    253      * The index of last valid character in the buffer + 1.
    254      * <br>
    255      * 0 <= tail <= bufSize
    256      */
    257     private int tail;
    258
    259     /**
    260      * The content encoding to use when reading headers.
    261      */
    262     private String headerEncoding;
    263
    264     /**
    265      * The progress notifier, if any, or null.
    266      */
    267     private final ProgressNotifier notifier;
    268
    269     // ----------------------------------------------------------- Constructors
    270
    271     /**
    272      * Creates a new instance.
    273      *
    274      * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int,
    275      * ProgressNotifier)}
    276      */
    277     @Deprecated
    278     public MultipartStream() {
    279         this(null, null, null);
    280     }
    281
    282     /**
    283      * <p> Constructs a <code>MultipartStream</code> with a custom size buffer
    284      * and no progress notifier.
    285      *
    286      * <p> Note that the buffer must be at least big enough to contain the
    287      * boundary string, plus 4 characters for CR/LF and double dash, plus at
    288      * least one byte of data.  Too small a buffer size setting will degrade
    289      * performance.
    290      *
    291      * @param input    The <code>InputStream</code> to serve as a data source.
    292      * @param boundary The token used for dividing the stream into
    293      *                 <code>encapsulations</code>.
    294      * @param bufSize  The size of the buffer to be used, in bytes.
    295      *
    296      * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int,
    297      * ProgressNotifier)}.
    298      */
    299     @Deprecated
    300     public MultipartStream(InputStream input, byte[] boundary, int bufSize) {
    301         this(input, boundary, bufSize, null);
    302     }
    303
    304     /**
    305      * <p> Constructs a <code>MultipartStream</code> with a custom size buffer.
    306      *
    307      * <p> Note that the buffer must be at least big enough to contain the
    308      * boundary string, plus 4 characters for CR/LF and double dash, plus at
    309      * least one byte of data.  Too small a buffer size setting will degrade
    310      * performance.
    311      *
    312      * @param input    The <code>InputStream</code> to serve as a data source.
    313      * @param boundary The token used for dividing the stream into
    314      *                 <code>encapsulations</code>.
    315      * @param bufSize  The size of the buffer to be used, in bytes.
    316      * @param pNotifier The notifier, which is used for calling the
    317      *                  progress listener, if any.
    318      *
    319      * @throws IllegalArgumentException If the buffer size is too small
    320      *
    321      * @since 1.3.1
    322      */
    323     public MultipartStream(InputStream input,
    324             byte[] boundary,
    325             int bufSize,
    326             ProgressNotifier pNotifier) {
    327
    328         if (boundary == null) {
    329             throw new IllegalArgumentException("boundary may not be null");
    330         }
    331         // We prepend CR/LF to the boundary to chop trailing CR/LF from
    332         // body-data tokens.
    333         this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length;
    334         if (bufSize < this.boundaryLength + 1) {
    335             throw new IllegalArgumentException(
    336                     "The buffer size specified for the MultipartStream is too small");
    337         }
    338
    339         this.input = input;
    340         this.bufSize = Math.max(bufSize, boundaryLength * 2);
    341         this.buffer = new byte[this.bufSize];
    342         this.notifier = pNotifier;
    343
    344         this.boundary = new byte[this.boundaryLength];
    345         this.boundaryTable = new int[this.boundaryLength + 1];
    346         this.keepRegion = this.boundary.length;
    347
    348         System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0,
    349                 BOUNDARY_PREFIX.length);
    350         System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
    351                 boundary.length);
    352         computeBoundaryTable();
    353
    354         head = 0;
    355         tail = 0;
    356     }
    357
    358     /**
    359      * <p> Constructs a <code>MultipartStream</code> with a default size buffer.
    360      *
    361      * @param input    The <code>InputStream</code> to serve as a data source.
    362      * @param boundary The token used for dividing the stream into
    363      *                 <code>encapsulations</code>.
    364      * @param pNotifier An object for calling the progress listener, if any.
    365      *
    366      *
    367      * @see #MultipartStream(InputStream, byte[], int, ProgressNotifier)
    368      */
    369     MultipartStream(InputStream input,
    370             byte[] boundary,
    371             ProgressNotifier pNotifier) {
    372         this(input, boundary, DEFAULT_BUFSIZE, pNotifier);
    373     }
    374
    375     /**
    376      * <p> Constructs a <code>MultipartStream</code> with a default size buffer.
    377      *
    378      * @param input    The <code>InputStream</code> to serve as a data source.
    379      * @param boundary The token used for dividing the stream into
    380      *                 <code>encapsulations</code>.
    381      *
    382      * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int,
    383      *  ProgressNotifier)}.
    384      */
    385     @Deprecated
    386     public MultipartStream(InputStream input,
    387             byte[] boundary) {
    388         this(input, boundary, DEFAULT_BUFSIZE, null);
    389     }
    390
    391     // --------------------------------------------------------- Public methods
    392
    393     /**
    394      * Retrieves the character encoding used when reading the headers of an
    395      * individual part. When not specified, or <code>null</code>, the platform
    396      * default encoding is used.
    397      *
    398      * @return The encoding used to read part headers.
    399      */
    400     public String getHeaderEncoding() {
    401         return headerEncoding;
    402     }
    403
    404     /**
    405      * Specifies the character encoding to be used when reading the headers of
    406      * individual parts. When not specified, or <code>null</code>, the platform
    407      * default encoding is used.
    408      *
    409      * @param encoding The encoding used to read part headers.
    410      */
    411     public void setHeaderEncoding(String encoding) {
    412         headerEncoding = encoding;
    413     }
    414
    415     /**
    416      * Reads a byte from the <code>buffer</code>, and refills it as
    417      * necessary.
    418      *
    419      * @return The next byte from the input stream.
    420      *
    421      * @throws IOException if there is no more data available.
    422      */
    423     public byte readByte() throws IOException {
    424         // Buffer depleted ?
    425         if (head == tail) {
    426             head = 0;
    427             // Refill.
    428             tail = input.read(buffer, head, bufSize);
    429             if (tail == -1) {
    430                 // No more data available.
    431                 throw new IOException("No more data is available");
    432             }
    433             if (notifier != null) {
    434                 notifier.noteBytesRead(tail);
    435             }
    436         }
    437         return buffer[head++];
    438     }
    439
    440     /**
    441      * Skips a <code>boundary</code> token, and checks whether more
    442      * <code>encapsulations</code> are contained in the stream.
    443      *
    444      * @return <code>true</code> if there are more encapsulations in
    445      *         this stream; <code>false</code> otherwise.
    446      *
    447      * @throws FileUploadIOException if the bytes read from the stream exceeded the size limits
    448      * @throws MalformedStreamException if the stream ends unexpectedly or
    449      *                                  fails to follow required syntax.
    450      */
    451     public boolean readBoundary()
    452             throws FileUploadIOException, MalformedStreamException {
    453         byte[] marker = new byte[2];
    454         boolean nextChunk = false;
    455
    456         head += boundaryLength;
    457         try {
    458             marker[0] = readByte();
    459             if (marker[0] == LF) {
    460                 // Work around IE5 Mac bug with input type=image.
    461                 // Because the boundary delimiter, not including the trailing
    462                 // CRLF, must not appear within any file (RFC 2046, section
    463                 // 5.1.1), we know the missing CR is due to a buggy browser
    464                 // rather than a file containing something similar to a
    465                 // boundary.
    466                 return true;
    467             }
    468
    469             marker[1] = readByte();
    470             if (arrayequals(marker, STREAM_TERMINATOR, 2)) {
    471                 nextChunk = false;
    472             } else if (arrayequals(marker, FIELD_SEPARATOR, 2)) {
    473                 nextChunk = true;
    474             } else {
    475                 throw new MalformedStreamException(
    476                 "Unexpected characters follow a boundary");
    477             }
    478         } catch (FileUploadIOException e) {
    479             // wraps a SizeException, re-throw as it will be unwrapped later
    480             throw e;
    481         } catch (IOException e) {
    482             throw new MalformedStreamException("Stream ended unexpectedly");
    483         }
    484         return nextChunk;
    485     }
    486
    487     /**
    488      * <p>Changes the boundary token used for partitioning the stream.
    489      *
    490      * <p>This method allows single pass processing of nested multipart
    491      * streams.
    492      *
    493      * <p>The boundary token of the nested stream is <code>required</code>
    494      * to be of the same length as the boundary token in parent stream.
    495      *
    496      * <p>Restoring the parent stream boundary token after processing of a
    497      * nested stream is left to the application.
    498      *
    499      * @param boundary The boundary to be used for parsing of the nested
    500      *                 stream.
    501      *
    502      * @throws IllegalBoundaryException if the <code>boundary</code>
    503      *                                  has a different length than the one
    504      *                                  being currently parsed.
    505      */
    506     public void setBoundary(byte[] boundary)
    507             throws IllegalBoundaryException {
    508         if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) {
    509             throw new IllegalBoundaryException(
    510             "The length of a boundary token cannot be changed");
    511         }
    512         System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
    513                 boundary.length);
    514         computeBoundaryTable();
    515     }
    516
    517     /**
    518      * Compute the table used for Knuth-Morris-Pratt search algorithm.
    519      */
    520     private void computeBoundaryTable() {
    521         int position = 2;
    522         int candidate = 0;
    523
    524         boundaryTable[0] = -1;
    525         boundaryTable[1] = 0;
    526
    527         while (position <= boundaryLength) {
    528             if (boundary[position - 1] == boundary[candidate]) {
    529                 boundaryTable[position] = candidate + 1;
    530                 candidate++;
    531                 position++;
    532             } else if (candidate > 0) {
    533                 candidate = boundaryTable[candidate];
    534             } else {
    535                 boundaryTable[position] = 0;
    536                 position++;
    537             }
    538         }
    539     }
    540
    541     /**
    542      * <p>Reads the <code>header-part</code> of the current
    543      * <code>encapsulation</code>.
    544      *
    545      * <p>Headers are returned verbatim to the input stream, including the
    546      * trailing <code>CRLF</code> marker. Parsing is left to the
    547      * application.
    548      *
    549      * <p><strong>TODO</strong> allow limiting maximum header size to
    550      * protect against abuse.
    551      *
    552      * @return The <code>header-part</code> of the current encapsulation.
    553      *
    554      * @throws FileUploadIOException if the bytes read from the stream exceeded the size limits.
    555      * @throws MalformedStreamException if the stream ends unexpectedly.
    556      */
    557     public String readHeaders() throws FileUploadIOException, MalformedStreamException {
    558         int i = 0;
    559         byte b;
    560         // to support multi-byte characters
    561         ByteArrayOutputStream baos = new ByteArrayOutputStream();
    562         int size = 0;
    563         while (i < HEADER_SEPARATOR.length) {
    564             try {
    565                 b = readByte();
    566             } catch (FileUploadIOException e) {
    567                 // wraps a SizeException, re-throw as it will be unwrapped later
    568                 throw e;
    569             } catch (IOException e) {
    570                 throw new MalformedStreamException("Stream ended unexpectedly");
    571             }
    572             if (++size > HEADER_PART_SIZE_MAX) {
    573                 throw new MalformedStreamException(
    574                         format("Header section has more than %s bytes (maybe it is not properly terminated)",
    575                                Integer.valueOf(HEADER_PART_SIZE_MAX)));
    576             }
    577             if (b == HEADER_SEPARATOR[i]) {
    578                 i++;
    579             } else {
    580                 i = 0;
    581             }
    582             baos.write(b);
    583         }
    584
    585         String headers = null;
    586         if (headerEncoding != null) {
    587             try {
    588                 headers = baos.toString(headerEncoding);
    589             } catch (UnsupportedEncodingException e) {
    590                 // Fall back to platform default if specified encoding is not
    591                 // supported.
    592                 headers = baos.toString();
    593             }
    594         } else {
    595             headers = baos.toString();
    596         }
    597
    598         return headers;
    599     }
    600
    601     /**
    602      * <p>Reads <code>body-data</code> from the current
    603      * <code>encapsulation</code> and writes its contents into the
    604      * output <code>Stream</code>.
    605      *
    606      * <p>Arbitrary large amounts of data can be processed by this
    607      * method using a constant size buffer. (see {@link
    608      * #MultipartStream(InputStream,byte[],int,
    609      *   MultipartStream.ProgressNotifier) constructor}).
    610      *
    611      * @param output The <code>Stream</code> to write data into. May
    612      *               be null, in which case this method is equivalent
    613      *               to {@link #discardBodyData()}.
    614      *
    615      * @return the amount of data written.
    616      *
    617      * @throws MalformedStreamException if the stream ends unexpectedly.
    618      * @throws IOException              if an i/o error occurs.
    619      */
    620     public int readBodyData(OutputStream output)
    621             throws MalformedStreamException, IOException {
    622         return (int) Streams.copy(newInputStream(), output, false); // N.B. Streams.copy closes the input stream
    623     }
    624
    625     /**
    626      * Creates a new {@link ItemInputStream}.
    627      * @return A new instance of {@link ItemInputStream}.
    628      */
    629     ItemInputStream newInputStream() {
    630         return new ItemInputStream();
    631     }
    632
    633     /**
    634      * <p> Reads <code>body-data</code> from the current
    635      * <code>encapsulation</code> and discards it.
    636      *
    637      * <p>Use this method to skip encapsulations you don't need or don't
    638      * understand.
    639      *
    640      * @return The amount of data discarded.
    641      *
    642      * @throws MalformedStreamException if the stream ends unexpectedly.
    643      * @throws IOException              if an i/o error occurs.
    644      */
    645     public int discardBodyData() throws MalformedStreamException, IOException {
    646         return readBodyData(null);
    647     }
    648
    649     /**
    650      * Finds the beginning of the first <code>encapsulation</code>.
    651      *
    652      * @return <code>true</code> if an <code>encapsulation</code> was found in
    653      *         the stream.
    654      *
    655      * @throws IOException if an i/o error occurs.
    656      */
    657     public boolean skipPreamble() throws IOException {
    658         // First delimiter may be not preceeded with a CRLF.
    659         System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2);
    660         boundaryLength = boundary.length - 2;
    661         computeBoundaryTable();
    662         try {
    663             // Discard all data up to the delimiter.
    664             discardBodyData();
    665
    666             // Read boundary - if succeeded, the stream contains an
    667             // encapsulation.
    668             return readBoundary();
    669         } catch (MalformedStreamException e) {
    670             return false;
    671         } finally {
    672             // Restore delimiter.
    673             System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2);
    674             boundaryLength = boundary.length;
    675             boundary[0] = CR;
    676             boundary[1] = LF;
    677             computeBoundaryTable();
    678         }
    679     }
    680
    681     /**
    682      * Compares <code>count</code> first bytes in the arrays
    683      * <code>a</code> and <code>b</code>.
    684      *
    685      * @param a     The first array to compare.
    686      * @param b     The second array to compare.
    687      * @param count How many bytes should be compared.
    688      *
    689      * @return <code>true</code> if <code>count</code> first bytes in arrays
    690      *         <code>a</code> and <code>b</code> are equal.
    691      */
    692     public static boolean arrayequals(byte[] a,
    693             byte[] b,
    694             int count) {
    695         for (int i = 0; i < count; i++) {
    696             if (a[i] != b[i]) {
    697                 return false;
    698             }
    699         }
    700         return true;
    701     }
    702
    703     /**
    704      * Searches for a byte of specified value in the <code>buffer</code>,
    705      * starting at the specified <code>position</code>.
    706      *
    707      * @param value The value to find.
    708      * @param pos   The starting position for searching.
    709      *
    710      * @return The position of byte found, counting from beginning of the
    711      *         <code>buffer</code>, or <code>-1</code> if not found.
    712      */
    713     protected int findByte(byte value,
    714             int pos) {
    715         for (int i = pos; i < tail; i++) {
    716             if (buffer[i] == value) {
    717                 return i;
    718             }
    719         }
    720
    721         return -1;
    722     }
    723
    724     /**
    725      * Searches for the <code>boundary</code> in the <code>buffer</code>
    726      * region delimited by <code>head</code> and <code>tail</code>.
    727      *
    728      * @return The position of the boundary found, counting from the
    729      *         beginning of the <code>buffer</code>, or <code>-1</code> if
    730      *         not found.
    731      */
    732     protected int findSeparator() {
    733
    734         int bufferPos = this.head;
    735         int tablePos = 0;
    736
    737         while (bufferPos < this.tail) {
    738             while (tablePos >= 0 && buffer[bufferPos] != boundary[tablePos]) {
    739                 tablePos = boundaryTable[tablePos];
    740             }
    741             bufferPos++;
    742             tablePos++;
    743             if (tablePos == boundaryLength) {
    744                 return bufferPos - boundaryLength;
    745             }
    746         }
    747         return -1;
    748     }
    749
    750     /**
    751      * Thrown to indicate that the input stream fails to follow the
    752      * required syntax.
    753      */
    754     public static class MalformedStreamException extends IOException {
    755
    756         /**
    757          * The UID to use when serializing this instance.
    758          */
    759         private static final long serialVersionUID = 6466926458059796677L;
    760
    761         /**
    762          * Constructs a <code>MalformedStreamException</code> with no
    763          * detail message.
    764          */
    765         public MalformedStreamException() {
    766             super();
    767         }
    768
    769         /**
    770          * Constructs an <code>MalformedStreamException</code> with
    771          * the specified detail message.
    772          *
    773          * @param message The detail message.
    774          */
    775         public MalformedStreamException(String message) {
    776             super(message);
    777         }
    778
    779     }
    780
    781     /**
    782      * Thrown upon attempt of setting an invalid boundary token.
    783      */
    784     public static class IllegalBoundaryException extends IOException {
    785
    786         /**
    787          * The UID to use when serializing this instance.
    788          */
    789         private static final long serialVersionUID = -161533165102632918L;
    790
    791         /**
    792          * Constructs an <code>IllegalBoundaryException</code> with no
    793          * detail message.
    794          */
    795         public IllegalBoundaryException() {
    796             super();
    797         }
    798
    799         /**
    800          * Constructs an <code>IllegalBoundaryException</code> with
    801          * the specified detail message.
    802          *
    803          * @param message The detail message.
    804          */
    805         public IllegalBoundaryException(String message) {
    806             super(message);
    807         }
    808
    809     }
    810
    811     /**
    812      * An {@link InputStream} for reading an items contents.
    813      */
    814     public class ItemInputStream extends InputStream implements Closeable {
    815
    816         /**
    817          * The number of bytes, which have been read so far.
    818          */
    819         private long total;
    820
    821         /**
    822          * The number of bytes, which must be hold, because
    823          * they might be a part of the boundary.
    824          */
    825         private int pad;
    826
    827         /**
    828          * The current offset in the buffer.
    829          */
    830         private int pos;
    831
    832         /**
    833          * Whether the stream is already closed.
    834          */
    835         private boolean closed;
    836
    837         /**
    838          * Creates a new instance.
    839          */
    840         ItemInputStream() {
    841             findSeparator();
    842         }
    843
    844         /**
    845          * Called for finding the separator.
    846          */
    847         private void findSeparator() {
    848             pos = MultipartStream.this.findSeparator();
    849             if (pos == -1) {
    850                 if (tail - head > keepRegion) {
    851                     pad = keepRegion;
    852                 } else {
    853                     pad = tail - head;
    854                 }
    855             }
    856         }
    857
    858         /**
    859          * Returns the number of bytes, which have been read
    860          * by the stream.
    861          *
    862          * @return Number of bytes, which have been read so far.
    863          */
    864         public long getBytesRead() {
    865             return total;
    866         }
    867
    868         /**
    869          * Returns the number of bytes, which are currently
    870          * available, without blocking.
    871          *
    872          * @throws IOException An I/O error occurs.
    873          * @return Number of bytes in the buffer.
    874          */
    875         @Override
    876         public int available() throws IOException {
    877             if (pos == -1) {
    878                 return tail - head - pad;
    879             }
    880             return pos - head;
    881         }
    882
    883         /**
    884          * Offset when converting negative bytes to integers.
    885          */
    886         private static final int BYTE_POSITIVE_OFFSET = 256;
    887
    888         /**
    889          * Returns the next byte in the stream.
    890          *
    891          * @return The next byte in the stream, as a non-negative
    892          *   integer, or -1 for EOF.
    893          * @throws IOException An I/O error occurred.
    894          */
    895         @Override
    896         public int read() throws IOException {
    897             if (closed) {
    898                 throw new FileItemStream.ItemSkippedException();
    899             }
    900             if (available() == 0 && makeAvailable() == 0) {
    901                 return -1;
    902             }
    903             ++total;
    904             int b = buffer[head++];
    905             if (b >= 0) {
    906                 return b;
    907             }
    908             return b + BYTE_POSITIVE_OFFSET;
    909         }
    910
    911         /**
    912          * Reads bytes into the given buffer.
    913          *
    914          * @param b The destination buffer, where to write to.
    915          * @param off Offset of the first byte in the buffer.
    916          * @param len Maximum number of bytes to read.
    917          * @return Number of bytes, which have been actually read,
    918          *   or -1 for EOF.
    919          * @throws IOException An I/O error occurred.
    920          */
    921         @Override
    922         public int read(byte[] b, int off, int len) throws IOException {
    923             if (closed) {
    924                 throw new FileItemStream.ItemSkippedException();
    925             }
    926             if (len == 0) {
    927                 return 0;
    928             }
    929             int res = available();
    930             if (res == 0) {
    931                 res = makeAvailable();
    932                 if (res == 0) {
    933                     return -1;
    934                 }
    935             }
    936             res = Math.min(res, len);
    937             System.arraycopy(buffer, head, b, off, res);
    938             head += res;
    939             total += res;
    940             return res;
    941         }
    942
    943         /**
    944          * Closes the input stream.
    945          *
    946          * @throws IOException An I/O error occurred.
    947          */
    948         @Override
    949         public void close() throws IOException {
    950             close(false);
    951         }
    952
    953         /**
    954          * Closes the input stream.
    955          *
    956          * @param pCloseUnderlying Whether to close the underlying stream
    957          *   (hard close)
    958          * @throws IOException An I/O error occurred.
    959          */
    960         public void close(boolean pCloseUnderlying) throws IOException {
    961             if (closed) {
    962                 return;
    963             }
    964             if (pCloseUnderlying) {
    965                 closed = true;
    966                 input.close();
    967             } else {
    968                 for (;;) {
    969                     int av = available();
    970                     if (av == 0) {
    971                         av = makeAvailable();
    972                         if (av == 0) {
    973                             break;
    974                         }
    975                     }
    976                     skip(av);
    977                 }
    978             }
    979             closed = true;
    980         }
    981
    982         /**
    983          * Skips the given number of bytes.
    984          *
    985          * @param bytes Number of bytes to skip.
    986          * @return The number of bytes, which have actually been
    987          *   skipped.
    988          * @throws IOException An I/O error occurred.
    989          */
    990         @Override
    991         public long skip(long bytes) throws IOException {
    992             if (closed) {
    993                 throw new FileItemStream.ItemSkippedException();
    994             }
    995             int av = available();
    996             if (av == 0) {
    997                 av = makeAvailable();
    998                 if (av == 0) {
    999                     return 0;
    1000                 }
    1001             }
    1002             long res = Math.min(av, bytes);
    1003             head += res;
    1004             return res;
    1005         }
    1006
    1007         /**
    1008          * Attempts to read more data.
    1009          *
    1010          * @return Number of available bytes
    1011          * @throws IOException An I/O error occurred.
    1012          */
    1013         private int makeAvailable() throws IOException {
    1014             if (pos != -1) {
    1015                 return 0;
    1016             }
    1017
    1018             // Move the data to the beginning of the buffer.
    1019             total += tail - head - pad;
    1020             System.arraycopy(buffer, tail - pad, buffer, 0, pad);
    1021
    1022             // Refill buffer with new data.
    1023             head = 0;
    1024             tail = pad;
    1025
    1026             for (;;) {
    1027                 int bytesRead = input.read(buffer, tail, bufSize - tail);
    1028                 if (bytesRead == -1) {
    1029                     // The last pad amount is left in the buffer.
    1030                     // Boundary can't be in there so signal an error
    1031                     // condition.
    1032                     final String msg = "Stream ended unexpectedly";
    1033                     throw new MalformedStreamException(msg);
    1034                 }
    1035                 if (notifier != null) {
    1036                     notifier.noteBytesRead(bytesRead);
    1037                 }
    1038                 tail += bytesRead;
    1039
    1040                 findSeparator();
    1041                 int av = available();
    1042
    1043                 if (av > 0 || pos != -1) {
    1044                     return av;
    1045                 }
    1046             }
    1047         }
    1048
    1049         /**
    1050          * Returns, whether the stream is closed.
    1051          *
    1052          * @return True, if the stream is closed, otherwise false.
    1053          */
    1054         @Override
    1055         public boolean isClosed() {
    1056             return closed;
    1057         }
    1058
    1059     }
    1060
    1061 }