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