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