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