001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.fileupload;
018    
019    import java.io.ByteArrayOutputStream;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.io.OutputStream;
023    import java.io.UnsupportedEncodingException;
024    
025    import org.apache.commons.fileupload.util.Closeable;
026    import org.apache.commons.fileupload.util.Streams;
027    
028    /**
029     * <p> Low level API for processing file uploads.
030     *
031     * <p> This class can be used to process data streams conforming to MIME
032     * 'multipart' format as defined in
033     * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Arbitrarily
034     * large amounts of data in the stream can be processed under constant
035     * memory usage.
036     *
037     * <p> The format of the stream is defined in the following way:<br>
038     *
039     * <code>
040     *   multipart-body := preamble 1*encapsulation close-delimiter epilogue<br>
041     *   encapsulation := delimiter body CRLF<br>
042     *   delimiter := "--" boundary CRLF<br>
043     *   close-delimiter := "--" boudary "--"<br>
044     *   preamble := &lt;ignore&gt;<br>
045     *   epilogue := &lt;ignore&gt;<br>
046     *   body := header-part CRLF body-part<br>
047     *   header-part := 1*header CRLF<br>
048     *   header := header-name ":" header-value<br>
049     *   header-name := &lt;printable ascii characters except ":"&gt;<br>
050     *   header-value := &lt;any ascii characters except CR & LF&gt;<br>
051     *   body-data := &lt;arbitrary data&gt;<br>
052     * </code>
053     *
054     * <p>Note that body-data can contain another mulipart entity.  There
055     * is limited support for single pass processing of such nested
056     * streams.  The nested stream is <strong>required</strong> to have a
057     * boundary token of the same length as the parent stream (see {@link
058     * #setBoundary(byte[])}).
059     *
060     * <p>Here is an example of usage of this class.<br>
061     *
062     * <pre>
063     *   try {
064     *     MultipartStream multipartStream = new MultipartStream(input, boundary);
065     *     boolean nextPart = multipartStream.skipPreamble();
066     *     OutputStream output;
067     *     while(nextPart) {
068     *       String header = multipartStream.readHeaders();
069     *       // process headers
070     *       // create some output stream
071     *       multipartStream.readBodyData(output);
072     *       nextPart = multipartStream.readBoundary();
073     *     }
074     *   } catch(MultipartStream.MalformedStreamException e) {
075     *     // the stream failed to follow required syntax
076     *   } catch(IOException e) {
077     *     // a read or write error occurred
078     *   }
079     * </pre>
080     *
081     * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
082     * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
083     * @author Sean C. Sullivan
084     *
085     * @version $Id: MultipartStream.java 735374 2009-01-18 02:18:45Z jochen $
086     */
087    public class MultipartStream {
088        /**
089         * Internal class, which is used to invoke the
090         * {@link ProgressListener}.
091         */
092            public static class ProgressNotifier {
093            /** The listener to invoke.
094             */
095            private final ProgressListener listener;
096            /** Number of expected bytes, if known, or -1.
097             */
098            private final long contentLength;
099            /** 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    }