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