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