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 */ 017package org.apache.commons.fileupload; 018 019import static java.lang.String.format; 020 021import java.io.ByteArrayOutputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.io.OutputStream; 025import java.io.UnsupportedEncodingException; 026 027import org.apache.commons.fileupload.FileUploadBase.FileUploadIOException; 028import org.apache.commons.fileupload.util.Closeable; 029import org.apache.commons.fileupload.util.Streams; 030 031/** 032 * <p> Low level API for processing file uploads. 033 * 034 * <p> This class can be used to process data streams conforming to MIME 035 * 'multipart' format as defined in 036 * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Arbitrarily 037 * large amounts of data in the stream can be processed under constant 038 * memory usage. 039 * 040 * <p> The format of the stream is defined in the following way:<br> 041 * 042 * <code> 043 * multipart-body := preamble 1*encapsulation close-delimiter epilogue<br> 044 * encapsulation := delimiter body CRLF<br> 045 * delimiter := "--" boundary CRLF<br> 046 * close-delimiter := "--" boundary "--"<br> 047 * preamble := <ignore><br> 048 * epilogue := <ignore><br> 049 * body := header-part CRLF body-part<br> 050 * header-part := 1*header CRLF<br> 051 * header := header-name ":" header-value<br> 052 * header-name := <printable ascii characters except ":"><br> 053 * header-value := <any ascii characters except CR & LF><br> 054 * body-data := <arbitrary data><br> 055 * </code> 056 * 057 * <p>Note that body-data can contain another mulipart entity. There 058 * is limited support for single pass processing of such nested 059 * streams. The nested stream is <strong>required</strong> to have a 060 * boundary token of the same length as the parent stream (see {@link 061 * #setBoundary(byte[])}). 062 * 063 * <p>Here is an example of usage of this class.<br> 064 * 065 * <pre> 066 * try { 067 * MultipartStream multipartStream = new MultipartStream(input, boundary); 068 * boolean nextPart = multipartStream.skipPreamble(); 069 * OutputStream output; 070 * while(nextPart) { 071 * String header = multipartStream.readHeaders(); 072 * // process headers 073 * // create some output stream 074 * multipartStream.readBodyData(output); 075 * nextPart = multipartStream.readBoundary(); 076 * } 077 * } catch(MultipartStream.MalformedStreamException e) { 078 * // the stream failed to follow required syntax 079 * } catch(IOException e) { 080 * // a read or write error occurred 081 * } 082 * </pre> 083 * 084 * @version $Id: MultipartStream.java 1565249 2014-02-06 13:45:33Z ggregory $ 085 */ 086public class MultipartStream { 087 088 /** 089 * Internal class, which is used to invoke the 090 * {@link ProgressListener}. 091 */ 092 public static class ProgressNotifier { 093 094 /** 095 * The listener to invoke. 096 */ 097 private final ProgressListener listener; 098 099 /** 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[], int, 272 * ProgressNotifier)} 273 */ 274 @Deprecated 275 public MultipartStream() { 276 this(null, null, null); 277 } 278 279 /** 280 * <p> Constructs a <code>MultipartStream</code> with a custom size buffer 281 * and no progress notifier. 282 * 283 * <p> Note that the buffer must be at least big enough to contain the 284 * boundary string, plus 4 characters for CR/LF and double dash, plus at 285 * least one byte of data. Too small a buffer size setting will degrade 286 * performance. 287 * 288 * @param input The <code>InputStream</code> to serve as a data source. 289 * @param boundary The token used for dividing the stream into 290 * <code>encapsulations</code>. 291 * @param bufSize The size of the buffer to be used, in bytes. 292 * 293 * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int, 294 * ProgressNotifier)}. 295 */ 296 @Deprecated 297 public MultipartStream(InputStream input, byte[] boundary, int bufSize) { 298 this(input, boundary, bufSize, null); 299 } 300 301 /** 302 * <p> Constructs a <code>MultipartStream</code> with a custom size buffer. 303 * 304 * <p> Note that the buffer must be at least big enough to contain the 305 * boundary string, plus 4 characters for CR/LF and double dash, plus at 306 * least one byte of data. Too small a buffer size setting will degrade 307 * performance. 308 * 309 * @param input The <code>InputStream</code> to serve as a data source. 310 * @param boundary The token used for dividing the stream into 311 * <code>encapsulations</code>. 312 * @param bufSize The size of the buffer to be used, in bytes. 313 * @param pNotifier The notifier, which is used for calling the 314 * progress listener, if any. 315 * 316 * @throws IllegalArgumentException If the buffer size is too small 317 * 318 * @since 1.3.1 319 */ 320 public MultipartStream(InputStream input, 321 byte[] boundary, 322 int bufSize, 323 ProgressNotifier pNotifier) { 324 325 if (boundary == null) { 326 throw new IllegalArgumentException("boundary may not be null"); 327 } 328 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 trailing CR/LF from 335 // body-data tokens. 336 this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length; 337 if (bufSize < this.boundaryLength + 1) { 338 throw new IllegalArgumentException( 339 "The buffer size specified for the MultipartStream is too small"); 340 } 341 this.boundary = new byte[this.boundaryLength]; 342 this.keepRegion = this.boundary.length; 343 344 System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0, 345 BOUNDARY_PREFIX.length); 346 System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, 347 boundary.length); 348 349 head = 0; 350 tail = 0; 351 } 352 353 /** 354 * <p> Constructs a <code>MultipartStream</code> with a default size buffer. 355 * 356 * @param input The <code>InputStream</code> to serve as a data source. 357 * @param boundary The token used for dividing the stream into 358 * <code>encapsulations</code>. 359 * @param pNotifier An object for calling the progress listener, if any. 360 * 361 * 362 * @see #MultipartStream(InputStream, byte[], int, ProgressNotifier) 363 */ 364 MultipartStream(InputStream input, 365 byte[] boundary, 366 ProgressNotifier pNotifier) { 367 this(input, boundary, DEFAULT_BUFSIZE, pNotifier); 368 } 369 370 /** 371 * <p> Constructs a <code>MultipartStream</code> with a default size buffer. 372 * 373 * @param input The <code>InputStream</code> to serve as a data source. 374 * @param boundary The token used for dividing the stream into 375 * <code>encapsulations</code>. 376 * 377 * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int, 378 * ProgressNotifier)}. 379 */ 380 @Deprecated 381 public MultipartStream(InputStream input, 382 byte[] boundary) { 383 this(input, boundary, DEFAULT_BUFSIZE, null); 384 } 385 386 // --------------------------------------------------------- Public methods 387 388 /** 389 * Retrieves the character encoding used when reading the headers of an 390 * individual part. When not specified, or <code>null</code>, the platform 391 * default encoding is used. 392 * 393 * @return The encoding used to read part headers. 394 */ 395 public String getHeaderEncoding() { 396 return headerEncoding; 397 } 398 399 /** 400 * Specifies the character encoding to be used when reading the headers of 401 * individual parts. When not specified, or <code>null</code>, the platform 402 * default encoding is used. 403 * 404 * @param encoding The encoding used to read part headers. 405 */ 406 public void setHeaderEncoding(String encoding) { 407 headerEncoding = encoding; 408 } 409 410 /** 411 * Reads a byte from the <code>buffer</code>, and refills it as 412 * necessary. 413 * 414 * @return The next byte from the input stream. 415 * 416 * @throws IOException if there is no more data available. 417 */ 418 public byte readByte() throws IOException { 419 // Buffer depleted ? 420 if (head == tail) { 421 head = 0; 422 // Refill. 423 tail = input.read(buffer, head, bufSize); 424 if (tail == -1) { 425 // No more data available. 426 throw new IOException("No more data is available"); 427 } 428 if (notifier != null) { 429 notifier.noteBytesRead(tail); 430 } 431 } 432 return buffer[head++]; 433 } 434 435 /** 436 * Skips a <code>boundary</code> token, and checks whether more 437 * <code>encapsulations</code> are contained in the stream. 438 * 439 * @return <code>true</code> if there are more encapsulations in 440 * this stream; <code>false</code> otherwise. 441 * 442 * @throws FileUploadIOException if the bytes read from the stream exceeded the size limits 443 * @throws MalformedStreamException if the stream ends unexpectedly or 444 * fails to follow required syntax. 445 */ 446 public boolean readBoundary() 447 throws FileUploadIOException, MalformedStreamException { 448 byte[] marker = new byte[2]; 449 boolean nextChunk = false; 450 451 head += boundaryLength; 452 try { 453 marker[0] = readByte(); 454 if (marker[0] == LF) { 455 // Work around IE5 Mac bug with input type=image. 456 // Because the boundary delimiter, not including the trailing 457 // CRLF, must not appear within any file (RFC 2046, section 458 // 5.1.1), we know the missing CR is due to a buggy browser 459 // rather than a file containing something similar to a 460 // boundary. 461 return true; 462 } 463 464 marker[1] = readByte(); 465 if (arrayequals(marker, STREAM_TERMINATOR, 2)) { 466 nextChunk = false; 467 } else if (arrayequals(marker, FIELD_SEPARATOR, 2)) { 468 nextChunk = true; 469 } else { 470 throw new MalformedStreamException( 471 "Unexpected characters follow a boundary"); 472 } 473 } catch (FileUploadIOException e) { 474 // wraps a SizeException, re-throw as it will be unwrapped later 475 throw e; 476 } catch (IOException e) { 477 throw new MalformedStreamException("Stream ended unexpectedly"); 478 } 479 return nextChunk; 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 * <p>Reads the <code>header-part</code> of the current 513 * <code>encapsulation</code>. 514 * 515 * <p>Headers are returned verbatim to the input stream, including the 516 * trailing <code>CRLF</code> marker. Parsing is left to the 517 * application. 518 * 519 * <p><strong>TODO</strong> allow limiting maximum header size to 520 * protect against abuse. 521 * 522 * @return The <code>header-part</code> of the current encapsulation. 523 * 524 * @throws FileUploadIOException if the bytes read from the stream exceeded the size limits. 525 * @throws MalformedStreamException if the stream ends unexpectedly. 526 */ 527 public String readHeaders() throws FileUploadIOException, MalformedStreamException { 528 int i = 0; 529 byte b; 530 // to support multi-byte characters 531 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 532 int size = 0; 533 while (i < HEADER_SEPARATOR.length) { 534 try { 535 b = readByte(); 536 } catch (FileUploadIOException e) { 537 // wraps a SizeException, re-throw as it will be unwrapped later 538 throw e; 539 } catch (IOException e) { 540 throw new MalformedStreamException("Stream ended unexpectedly"); 541 } 542 if (++size > HEADER_PART_SIZE_MAX) { 543 throw new MalformedStreamException( 544 format("Header section has more than %s bytes (maybe it is not properly terminated)", 545 Integer.valueOf(HEADER_PART_SIZE_MAX))); 546 } 547 if (b == HEADER_SEPARATOR[i]) { 548 i++; 549 } else { 550 i = 0; 551 } 552 baos.write(b); 553 } 554 555 String headers = null; 556 if (headerEncoding != null) { 557 try { 558 headers = baos.toString(headerEncoding); 559 } catch (UnsupportedEncodingException e) { 560 // Fall back to platform default if specified encoding is not 561 // supported. 562 headers = baos.toString(); 563 } 564 } else { 565 headers = baos.toString(); 566 } 567 568 return headers; 569 } 570 571 /** 572 * <p>Reads <code>body-data</code> from the current 573 * <code>encapsulation</code> and writes its contents into the 574 * output <code>Stream</code>. 575 * 576 * <p>Arbitrary large amounts of data can be processed by this 577 * method using a constant size buffer. (see {@link 578 * #MultipartStream(InputStream,byte[],int, 579 * MultipartStream.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() throws MalformedStreamException, IOException { 617 return readBodyData(null); 618 } 619 620 /** 621 * Finds the beginning of the first <code>encapsulation</code>. 622 * 623 * @return <code>true</code> if an <code>encapsulation</code> was found in 624 * the stream. 625 * 626 * @throws IOException if an i/o error occurs. 627 */ 628 public boolean skipPreamble() throws IOException { 629 // First delimiter may be not preceeded with a CRLF. 630 System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2); 631 boundaryLength = boundary.length - 2; 632 try { 633 // Discard all data up to the delimiter. 634 discardBodyData(); 635 636 // Read boundary - if succeeded, the stream contains an 637 // encapsulation. 638 return readBoundary(); 639 } catch (MalformedStreamException e) { 640 return false; 641 } finally { 642 // Restore delimiter. 643 System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2); 644 boundaryLength = boundary.length; 645 boundary[0] = CR; 646 boundary[1] = LF; 647 } 648 } 649 650 /** 651 * Compares <code>count</code> first bytes in the arrays 652 * <code>a</code> and <code>b</code>. 653 * 654 * @param a The first array to compare. 655 * @param b The second array to compare. 656 * @param count How many bytes should be compared. 657 * 658 * @return <code>true</code> if <code>count</code> first bytes in arrays 659 * <code>a</code> and <code>b</code> are equal. 660 */ 661 public static boolean arrayequals(byte[] a, 662 byte[] b, 663 int count) { 664 for (int i = 0; i < count; i++) { 665 if (a[i] != b[i]) { 666 return false; 667 } 668 } 669 return true; 670 } 671 672 /** 673 * Searches for a byte of specified value in the <code>buffer</code>, 674 * starting at the specified <code>position</code>. 675 * 676 * @param value The value to find. 677 * @param pos The starting position for searching. 678 * 679 * @return The position of byte found, counting from beginning of the 680 * <code>buffer</code>, or <code>-1</code> if not found. 681 */ 682 protected int findByte(byte value, 683 int pos) { 684 for (int i = pos; i < tail; i++) { 685 if (buffer[i] == value) { 686 return i; 687 } 688 } 689 690 return -1; 691 } 692 693 /** 694 * Searches for the <code>boundary</code> in the <code>buffer</code> 695 * region delimited by <code>head</code> and <code>tail</code>. 696 * 697 * @return The position of the boundary found, counting from the 698 * beginning of the <code>buffer</code>, or <code>-1</code> if 699 * not found. 700 */ 701 protected int findSeparator() { 702 int first; 703 int match = 0; 704 int maxpos = tail - boundaryLength; 705 for (first = head; first <= maxpos && match != boundaryLength; first++) { 706 first = findByte(boundary[0], first); 707 if (first == -1 || first > maxpos) { 708 return -1; 709 } 710 for (match = 1; match < boundaryLength; match++) { 711 if (buffer[first + match] != boundary[match]) { 712 break; 713 } 714 } 715 } 716 if (match == boundaryLength) { 717 return first - 1; 718 } 719 return -1; 720 } 721 722 /** 723 * Thrown to indicate that the input stream fails to follow the 724 * required syntax. 725 */ 726 public static class MalformedStreamException extends IOException { 727 728 /** 729 * The UID to use when serializing this instance. 730 */ 731 private static final long serialVersionUID = 6466926458059796677L; 732 733 /** 734 * Constructs a <code>MalformedStreamException</code> with no 735 * detail message. 736 */ 737 public MalformedStreamException() { 738 super(); 739 } 740 741 /** 742 * Constructs an <code>MalformedStreamException</code> with 743 * the specified detail message. 744 * 745 * @param message The detail message. 746 */ 747 public MalformedStreamException(String message) { 748 super(message); 749 } 750 751 } 752 753 /** 754 * Thrown upon attempt of setting an invalid boundary token. 755 */ 756 public static class IllegalBoundaryException extends IOException { 757 758 /** 759 * The UID to use when serializing this instance. 760 */ 761 private static final long serialVersionUID = -161533165102632918L; 762 763 /** 764 * Constructs an <code>IllegalBoundaryException</code> with no 765 * detail message. 766 */ 767 public IllegalBoundaryException() { 768 super(); 769 } 770 771 /** 772 * Constructs an <code>IllegalBoundaryException</code> with 773 * the specified detail message. 774 * 775 * @param message The detail message. 776 */ 777 public IllegalBoundaryException(String message) { 778 super(message); 779 } 780 781 } 782 783 /** 784 * An {@link InputStream} for reading an items contents. 785 */ 786 public class ItemInputStream extends InputStream implements Closeable { 787 788 /** 789 * The number of bytes, which have been read so far. 790 */ 791 private long total; 792 793 /** 794 * The number of bytes, which must be hold, because 795 * they might be a part of the boundary. 796 */ 797 private int pad; 798 799 /** 800 * The current offset in the buffer. 801 */ 802 private int pos; 803 804 /** 805 * Whether the stream is already closed. 806 */ 807 private boolean closed; 808 809 /** 810 * Creates a new instance. 811 */ 812 ItemInputStream() { 813 findSeparator(); 814 } 815 816 /** 817 * Called for finding the separator. 818 */ 819 private void findSeparator() { 820 pos = MultipartStream.this.findSeparator(); 821 if (pos == -1) { 822 if (tail - head > keepRegion) { 823 pad = keepRegion; 824 } else { 825 pad = tail - head; 826 } 827 } 828 } 829 830 /** 831 * Returns the number of bytes, which have been read 832 * by the stream. 833 * 834 * @return Number of bytes, which have been read so far. 835 */ 836 public long getBytesRead() { 837 return total; 838 } 839 840 /** 841 * Returns the number of bytes, which are currently 842 * available, without blocking. 843 * 844 * @throws IOException An I/O error occurs. 845 * @return Number of bytes in the buffer. 846 */ 847 @Override 848 public int available() throws IOException { 849 if (pos == -1) { 850 return tail - head - pad; 851 } 852 return pos - head; 853 } 854 855 /** 856 * Offset when converting negative bytes to integers. 857 */ 858 private static final int BYTE_POSITIVE_OFFSET = 256; 859 860 /** 861 * Returns the next byte in the stream. 862 * 863 * @return The next byte in the stream, as a non-negative 864 * integer, or -1 for EOF. 865 * @throws IOException An I/O error occurred. 866 */ 867 @Override 868 public int read() throws IOException { 869 if (closed) { 870 throw new FileItemStream.ItemSkippedException(); 871 } 872 if (available() == 0 && makeAvailable() == 0) { 873 return -1; 874 } 875 ++total; 876 int b = buffer[head++]; 877 if (b >= 0) { 878 return b; 879 } 880 return b + BYTE_POSITIVE_OFFSET; 881 } 882 883 /** 884 * Reads bytes into the given buffer. 885 * 886 * @param b The destination buffer, where to write to. 887 * @param off Offset of the first byte in the buffer. 888 * @param len Maximum number of bytes to read. 889 * @return Number of bytes, which have been actually read, 890 * or -1 for EOF. 891 * @throws IOException An I/O error occurred. 892 */ 893 @Override 894 public int read(byte[] b, int off, int len) throws IOException { 895 if (closed) { 896 throw new FileItemStream.ItemSkippedException(); 897 } 898 if (len == 0) { 899 return 0; 900 } 901 int res = available(); 902 if (res == 0) { 903 res = makeAvailable(); 904 if (res == 0) { 905 return -1; 906 } 907 } 908 res = Math.min(res, len); 909 System.arraycopy(buffer, head, b, off, res); 910 head += res; 911 total += res; 912 return res; 913 } 914 915 /** 916 * Closes the input stream. 917 * 918 * @throws IOException An I/O error occurred. 919 */ 920 @Override 921 public void close() throws IOException { 922 close(false); 923 } 924 925 /** 926 * Closes the input stream. 927 * 928 * @param pCloseUnderlying Whether to close the underlying stream 929 * (hard close) 930 * @throws IOException An I/O error occurred. 931 */ 932 public void close(boolean pCloseUnderlying) throws IOException { 933 if (closed) { 934 return; 935 } 936 if (pCloseUnderlying) { 937 closed = true; 938 input.close(); 939 } else { 940 for (;;) { 941 int av = available(); 942 if (av == 0) { 943 av = makeAvailable(); 944 if (av == 0) { 945 break; 946 } 947 } 948 skip(av); 949 } 950 } 951 closed = true; 952 } 953 954 /** 955 * Skips the given number of bytes. 956 * 957 * @param bytes Number of bytes to skip. 958 * @return The number of bytes, which have actually been 959 * skipped. 960 * @throws IOException An I/O error occurred. 961 */ 962 @Override 963 public long skip(long bytes) throws IOException { 964 if (closed) { 965 throw new FileItemStream.ItemSkippedException(); 966 } 967 int av = available(); 968 if (av == 0) { 969 av = makeAvailable(); 970 if (av == 0) { 971 return 0; 972 } 973 } 974 long res = Math.min(av, bytes); 975 head += res; 976 return res; 977 } 978 979 /** 980 * Attempts to read more data. 981 * 982 * @return Number of available bytes 983 * @throws IOException An I/O error occurred. 984 */ 985 private int makeAvailable() throws IOException { 986 if (pos != -1) { 987 return 0; 988 } 989 990 // Move the data to the beginning of the buffer. 991 total += tail - head - pad; 992 System.arraycopy(buffer, tail - pad, buffer, 0, pad); 993 994 // Refill buffer with new data. 995 head = 0; 996 tail = pad; 997 998 for (;;) { 999 int bytesRead = input.read(buffer, tail, bufSize - tail); 1000 if (bytesRead == -1) { 1001 // The last pad amount is left in the buffer. 1002 // Boundary can't be in there so signal an error 1003 // condition. 1004 final String msg = "Stream ended unexpectedly"; 1005 throw new MalformedStreamException(msg); 1006 } 1007 if (notifier != null) { 1008 notifier.noteBytesRead(bytesRead); 1009 } 1010 tail += bytesRead; 1011 1012 findSeparator(); 1013 int av = available(); 1014 1015 if (av > 0 || pos != -1) { 1016 return av; 1017 } 1018 } 1019 } 1020 1021 /** 1022 * Returns, whether the stream is closed. 1023 * 1024 * @return True, if the stream is closed, otherwise false. 1025 */ 1026 public boolean isClosed() { 1027 return closed; 1028 } 1029 1030 } 1031 1032}