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