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 := <ignore><br>
045 * epilogue := <ignore><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 := <printable ascii characters except ":"><br>
050 * header-value := <any ascii characters except CR & LF><br>
051 * body-data := <arbitrary data><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 }