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 }