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