##### Coverage Report - org.apache.commons.fileupload.MultipartStream

Classes in this File | Line Coverage | Branch Coverage | Complexity
MultipartStream |
76% | 107/139
77% | 37/48
| 3.210526315789474;3,211
MultipartStream$IllegalBoundaryException |
0% | 0/4
N/A | N/A
| 3.210526315789474;3,211
MultipartStream$ItemInputStream |
80% | 65/81
73% | 34/46
| 3.210526315789474;3,211
MultipartStream$MalformedStreamException |
50% | 2/4
N/A | N/A
| 3.210526315789474;3,211
MultipartStream$ProgressNotifier |
100% | 13/13
100% | 2/2
| 3.210526315789474;3,211

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