Coverage Report - org.apache.commons.fileupload.MultipartStream
 
Classes in this File Line Coverage Branch Coverage Complexity
MultipartStream
76%
107/139
77%
37/48
3,211
MultipartStream$IllegalBoundaryException
0%
0/4
N/A
3,211
MultipartStream$ItemInputStream
80%
65/81
73%
34/46
3,211
MultipartStream$MalformedStreamException
50%
2/4
N/A
3,211
MultipartStream$ProgressNotifier
100%
13/13
100%
2/2
3,211
 
 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  66303117
 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  38
         ProgressNotifier(ProgressListener pListener, long pContentLength) {
 120  38
             listener = pListener;
 121  38
             contentLength = pContentLength;
 122  38
         }
 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  8506
             bytesRead += pBytes;
 134  8506
             notifyListener();
 135  8506
         }
 136  
 
 137  
         /**
 138  
          * Called to indicate, that a new file item has been detected.
 139  
          */
 140  
         void noteItem() {
 141  3189
             ++items;
 142  3189
             notifyListener();
 143  3189
         }
 144  
 
 145  
         /**
 146  
          * Called for notifying the listener.
 147  
          */
 148  
         private void notifyListener() {
 149  11695
             if (listener != null) {
 150  5212
                 listener.update(bytesRead, contentLength, items);
 151  
             }
 152  11695
         }
 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  1
     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  1
     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  1
     protected static final byte[] STREAM_TERMINATOR = {DASH, DASH};
 201  
 
 202  
     /**
 203  
      * A byte sequence that precedes a boundary (<code>CRLF--</code>).
 204  
      */
 205  1
     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 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  0
         this(null, null, null);
 280  0
     }
 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  0
         this(input, boundary, bufSize, null);
 302  0
     }
 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  38
             ProgressNotifier pNotifier) {
 327  
 
 328  38
         if (boundary == null) {
 329  0
             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  38
         this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length;
 334  38
         if (bufSize < this.boundaryLength + 1) {
 335  1
             throw new IllegalArgumentException(
 336  
                     "The buffer size specified for the MultipartStream is too small");
 337  
         }
 338  
 
 339  37
         this.input = input;
 340  37
         this.bufSize = Math.max(bufSize, boundaryLength * 2);
 341  37
         this.buffer = new byte[this.bufSize];
 342  37
         this.notifier = pNotifier;
 343  
 
 344  37
         this.boundary = new byte[this.boundaryLength];
 345  37
         this.boundaryTable = new int[this.boundaryLength + 1];
 346  37
         this.keepRegion = this.boundary.length;
 347  
 
 348  37
         System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0,
 349  
                 BOUNDARY_PREFIX.length);
 350  37
         System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
 351  
                 boundary.length);
 352  37
         computeBoundaryTable();
 353  
 
 354  37
         head = 0;
 355  37
         tail = 0;
 356  37
     }
 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  36
         this(input, boundary, DEFAULT_BUFSIZE, pNotifier);
 373  36
     }
 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  0
         this(input, boundary, DEFAULT_BUFSIZE, null);
 389  0
     }
 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  0
         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  35
         headerEncoding = encoding;
 413  35
     }
 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  170443
         if (head == tail) {
 426  71
             head = 0;
 427  
             // Refill.
 428  71
             tail = input.read(buffer, head, bufSize);
 429  71
             if (tail == -1) {
 430  
                 // No more data available.
 431  0
                 throw new IOException("No more data is available");
 432  
             }
 433  71
             if (notifier != null) {
 434  71
                 notifier.noteBytesRead(tail);
 435  
             }
 436  
         }
 437  170443
         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  3226
         byte[] marker = new byte[2];
 454  3226
         boolean nextChunk = false;
 455  
 
 456  3226
         head += boundaryLength;
 457  
         try {
 458  3226
             marker[0] = readByte();
 459  3226
             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  4
                 return true;
 467  
             }
 468  
 
 469  3222
             marker[1] = readByte();
 470  3222
             if (arrayequals(marker, STREAM_TERMINATOR, 2)) {
 471  32
                 nextChunk = false;
 472  3190
             } else if (arrayequals(marker, FIELD_SEPARATOR, 2)) {
 473  3190
                 nextChunk = true;
 474  
             } else {
 475  0
                 throw new MalformedStreamException(
 476  
                 "Unexpected characters follow a boundary");
 477  
             }
 478  0
         } catch (FileUploadIOException e) {
 479  
             // wraps a SizeException, re-throw as it will be unwrapped later
 480  0
             throw e;
 481  0
         } catch (IOException e) {
 482  0
             throw new MalformedStreamException("Stream ended unexpectedly");
 483  3222
         }
 484  3222
         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  8
         if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) {
 509  0
             throw new IllegalBoundaryException(
 510  
             "The length of a boundary token can not be changed");
 511  
         }
 512  8
         System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
 513  
                 boundary.length);
 514  8
         computeBoundaryTable();
 515  8
     }
 516  
 
 517  
     /**
 518  
      * Compute the table used for Knuth-Morris-Pratt search algorithm.
 519  
      */
 520  
     private void computeBoundaryTable() {
 521  6499
         int position = 2;
 522  6499
         int candidate = 0;
 523  
 
 524  6499
         boundaryTable[0] = -1;
 525  6499
         boundaryTable[1] = 0;
 526  
 
 527  77883
         while (position <= boundaryLength) {
 528  71384
             if (boundary[position - 1] == boundary[candidate]) {
 529  12872
                 boundaryTable[position] = candidate + 1;
 530  12872
                 candidate++;
 531  12872
                 position++;
 532  58512
             } else if (candidate > 0) {
 533  12872
                 candidate = boundaryTable[candidate];
 534  
             } else {
 535  45640
                 boundaryTable[position] = 0;
 536  45640
                 position++;
 537  
             }
 538  
         }
 539  6499
     }
 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  3194
         int i = 0;
 559  
         byte b;
 560  
         // to support multi-byte characters
 561  3194
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 562  3194
         int size = 0;
 563  167189
         while (i < HEADER_SEPARATOR.length) {
 564  
             try {
 565  163995
                 b = readByte();
 566  0
             } catch (FileUploadIOException e) {
 567  
                 // wraps a SizeException, re-throw as it will be unwrapped later
 568  0
                 throw e;
 569  0
             } catch (IOException e) {
 570  0
                 throw new MalformedStreamException("Stream ended unexpectedly");
 571  163995
             }
 572  163995
             if (++size > HEADER_PART_SIZE_MAX) {
 573  0
                 throw new MalformedStreamException(
 574  0
                         format("Header section has more than %s bytes (maybe it is not properly terminated)",
 575  0
                                Integer.valueOf(HEADER_PART_SIZE_MAX)));
 576  
             }
 577  163995
             if (b == HEADER_SEPARATOR[i]) {
 578  12884
                 i++;
 579  
             } else {
 580  151111
                 i = 0;
 581  
             }
 582  163995
             baos.write(b);
 583  
         }
 584  
 
 585  3194
         String headers = null;
 586  3194
         if (headerEncoding != null) {
 587  
             try {
 588  0
                 headers = baos.toString(headerEncoding);
 589  0
             } catch (UnsupportedEncodingException e) {
 590  
                 // Fall back to platform default if specified encoding is not
 591  
                 // supported.
 592  0
                 headers = baos.toString();
 593  0
             }
 594  
         } else {
 595  3194
             headers = baos.toString();
 596  
         }
 597  
 
 598  3194
         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  3227
         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  6417
         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  3227
         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  3227
         System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2);
 660  3227
         boundaryLength = boundary.length - 2;
 661  3227
         computeBoundaryTable();
 662  
         try {
 663  
             // Discard all data up to the delimiter.
 664  3227
             discardBodyData();
 665  
 
 666  
             // Read boundary - if succeeded, the stream contains an
 667  
             // encapsulation.
 668  6452
             return readBoundary();
 669  0
         } catch (MalformedStreamException e) {
 670  0
             return false;
 671  
         } finally {
 672  
             // Restore delimiter.
 673  3227
             System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2);
 674  3227
             boundaryLength = boundary.length;
 675  3227
             boundary[0] = CR;
 676  3227
             boundary[1] = LF;
 677  3227
             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  12856
         for (int i = 0; i < count; i++) {
 696  9634
             if (a[i] != b[i]) {
 697  3190
                 return false;
 698  
             }
 699  
         }
 700  3222
         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  0
         for (int i = pos; i < tail; i++) {
 716  0
             if (buffer[i] == value) {
 717  0
                 return i;
 718  
             }
 719  
         }
 720  
 
 721  0
         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  14852
         int bufferPos = this.head;
 735  14852
         int tablePos = 0;
 736  
 
 737  34380737
         while (bufferPos < this.tail) {
 738  68680442
             while (tablePos >= 0 && buffer[bufferPos] != boundary[tablePos]) {
 739  34308143
                 tablePos = boundaryTable[tablePos];
 740  
             }
 741  34372299
             bufferPos++;
 742  34372299
             tablePos++;
 743  34372299
             if (tablePos == boundaryLength) {
 744  6414
                 return bufferPos - boundaryLength;
 745  
             }
 746  
         }
 747  8438
         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  0
             super();
 767  0
         }
 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  2
             super(message);
 777  2
         }
 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  0
             super();
 797  0
         }
 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  0
             super(message);
 807  0
         }
 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  6417
         ItemInputStream() {
 841  6417
             findSeparator();
 842  6417
         }
 843  
 
 844  
         /**
 845  
          * Called for finding the separator.
 846  
          */
 847  
         private void findSeparator() {
 848  14852
             pos = MultipartStream.this.findSeparator();
 849  14852
             if (pos == -1) {
 850  8438
                 if (tail - head > keepRegion) {
 851  8374
                     pad = keepRegion;
 852  
                 } else {
 853  64
                     pad = tail - head;
 854  
                 }
 855  
             }
 856  14852
         }
 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  0
             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  17069664
             if (pos == -1) {
 878  14948729
                 return tail - head - pad;
 879  
             }
 880  2120935
             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  17039872
             if (closed) {
 898  0
                 throw new FileItemStream.ItemSkippedException();
 899  
             }
 900  17039872
             if (available() == 0 && makeAvailable() == 0) {
 901  1024
                 return -1;
 902  
             }
 903  17038848
             ++total;
 904  17038848
             int b = buffer[head++];
 905  17038848
             if (b >= 0) {
 906  8552192
                 return b;
 907  
             }
 908  8486656
             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  14944
             if (closed) {
 924  0
                 throw new FileItemStream.ItemSkippedException();
 925  
             }
 926  14944
             if (len == 0) {
 927  0
                 return 0;
 928  
             }
 929  14944
             int res = available();
 930  14944
             if (res == 0) {
 931  9612
                 res = makeAvailable();
 932  9609
                 if (res == 0) {
 933  5386
                     return -1;
 934  
                 }
 935  
             }
 936  9555
             res = Math.min(res, len);
 937  9555
             System.arraycopy(buffer, head, b, off, res);
 938  9555
             head += res;
 939  9555
             total += res;
 940  9555
             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  9599
             close(false);
 951  9596
         }
 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  9601
             if (closed) {
 962  3186
                 return;
 963  
             }
 964  6415
             if (pCloseUnderlying) {
 965  2
                 closed = true;
 966  2
                 input.close();
 967  
             } else {
 968  
                 for (;;) {
 969  6413
                     int av = available();
 970  6413
                     if (av == 0) {
 971  6413
                         av = makeAvailable();
 972  6410
                         if (av == 0) {
 973  6410
                             break;
 974  
                         }
 975  
                     }
 976  0
                     skip(av);
 977  0
                 }
 978  
             }
 979  6412
             closed = true;
 980  6412
         }
 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  0
             if (closed) {
 993  0
                 throw new FileItemStream.ItemSkippedException();
 994  
             }
 995  0
             int av = available();
 996  0
             if (av == 0) {
 997  0
                 av = makeAvailable();
 998  0
                 if (av == 0) {
 999  0
                     return 0;
 1000  
                 }
 1001  
             }
 1002  0
             long res = Math.min(av, bytes);
 1003  0
             head += res;
 1004  0
             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  21221
             if (pos != -1) {
 1015  12786
                 return 0;
 1016  
             }
 1017  
 
 1018  
             // Move the data to the beginning of the buffer.
 1019  8435
             total += tail - head - pad;
 1020  8435
             System.arraycopy(buffer, tail - pad, buffer, 0, pad);
 1021  
 
 1022  
             // Refill buffer with new data.
 1023  8435
             head = 0;
 1024  8435
             tail = pad;
 1025  
 
 1026  
             for (;;) {
 1027  8441
                 int bytesRead = input.read(buffer, tail, bufSize - tail);
 1028  8437
                 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  2
                     final String msg = "Stream ended unexpectedly";
 1033  2
                     throw new MalformedStreamException(msg);
 1034  
                 }
 1035  8435
                 if (notifier != null) {
 1036  8435
                     notifier.noteBytesRead(bytesRead);
 1037  
                 }
 1038  8435
                 tail += bytesRead;
 1039  
 
 1040  8435
                 findSeparator();
 1041  8435
                 int av = available();
 1042  
 
 1043  8435
                 if (av > 0 || pos != -1) {
 1044  8429
                     return av;
 1045  
                 }
 1046  6
             }
 1047  
         }
 1048  
 
 1049  
         /**
 1050  
          * Returns, whether the stream is closed.
 1051  
          *
 1052  
          * @return True, if the stream is closed, otherwise false.
 1053  
          */
 1054  
         public boolean isClosed() {
 1055  3184
             return closed;
 1056  
         }
 1057  
 
 1058  
     }
 1059  
 
 1060  
 }