%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.turbine.util.upload.MultipartStream |
|
|
1 | package org.apache.turbine.util.upload; |
|
2 | ||
3 | /* |
|
4 | * Copyright 2001-2005 The Apache Software Foundation. |
|
5 | * |
|
6 | * Licensed under the Apache License, Version 2.0 (the "License") |
|
7 | * you may not use this file except in compliance with the License. |
|
8 | * You may obtain a copy of the License at |
|
9 | * |
|
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
11 | * |
|
12 | * Unless required by applicable law or agreed to in writing, software |
|
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
15 | * See the License for the specific language governing permissions and |
|
16 | * limitations under the License. |
|
17 | */ |
|
18 | ||
19 | import java.io.IOException; |
|
20 | import java.io.InputStream; |
|
21 | import java.io.OutputStream; |
|
22 | ||
23 | /** |
|
24 | * This class can be used to process data streams conforming to MIME |
|
25 | * 'multipart' format as defined in <a |
|
26 | * href="http://rf.cs/rfc1521.html">RFC 1251</a>. Arbitrary |
|
27 | * large amouns of data in the stream can be processed under constant |
|
28 | * memory usage. |
|
29 | * |
|
30 | * <p>The format of the stream is defined in the following way:<br> |
|
31 | * |
|
32 | * <code> |
|
33 | * multipart-body := preamble 1*encapsulation close-delimiter epilogue<br> |
|
34 | * encapsulation := delimiter body CRLF<br> |
|
35 | * delimiter := "--" boundary CRLF<br> |
|
36 | * close-delimiter := "--" boudary "--"<br> |
|
37 | * preamble := <ignore><br> |
|
38 | * epilogue := <ignore><br> |
|
39 | * body := header-part CRLF body-part<br> |
|
40 | * header-part := 1*header CRLF<br> |
|
41 | * header := header-name ":" header-value<br> |
|
42 | * header-name := <printable ascii characters except ":"><br> |
|
43 | * header-value := <any ascii characters except CR & LF><br> |
|
44 | * body-data := <arbitrary data><br> |
|
45 | * </code> |
|
46 | * |
|
47 | * <p>Note that body-data can contain another mulipart entity. There |
|
48 | * is limited support for single pass processing of such nested |
|
49 | * streams. The nested stream is <strong>required</strong> to have a |
|
50 | * boundary token of the same length as the parent stream (see {@link |
|
51 | * #setBoundary(byte[])}). |
|
52 | * |
|
53 | * <p>Here is an exaple of usage of this class.<br> |
|
54 | * |
|
55 | * <pre> |
|
56 | * try { |
|
57 | * MultipartStream multipartStream = new MultipartStream(input, |
|
58 | * boundary); |
|
59 | * boolean nextPart = malitPartStream.skipPreamble(); |
|
60 | * OutputStream output; |
|
61 | * while(nextPart) { |
|
62 | * header = chunks.readHeader(); |
|
63 | * // process headers |
|
64 | * // create some output stream |
|
65 | * multipartStream.readBodyPart(output); |
|
66 | * nextPart = multipartStream.readBoundary(); |
|
67 | * } |
|
68 | * } catch(MultipartStream.MalformedStreamException e) { |
|
69 | * // the stream failed to follow required syntax |
|
70 | * } catch(IOException) { |
|
71 | * // a read or write error occurred |
|
72 | * } |
|
73 | * </pre> |
|
74 | * |
|
75 | * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a> |
|
76 | * @version $Id: MultipartStream.java 278822 2005-09-05 19:53:05Z henning $ |
|
77 | * @deprecated use commons-fileupload instead |
|
78 | */ |
|
79 | public class MultipartStream |
|
80 | { |
|
81 | /** |
|
82 | * The maximum lenght of <code>header-part</code> that will be |
|
83 | * processed (10 kilobytes = 10240 bytes.) |
|
84 | */ |
|
85 | public static final int HEADER_PART_SIZE_MAX = 10240; |
|
86 | ||
87 | /** The stream were data is read from. */ |
|
88 | protected InputStream input; |
|
89 | ||
90 | /** |
|
91 | * The lenght of boundary token plus leading <code>CRLF--</code>. |
|
92 | */ |
|
93 | protected int boundaryLength; |
|
94 | ||
95 | /** |
|
96 | * The amount of data that must be kept in the buffer in order to |
|
97 | * detect delimiters reliably. |
|
98 | */ |
|
99 | protected int keepRegion; |
|
100 | ||
101 | /** A byte sequence that partitions the stream. */ |
|
102 | protected byte[] boundary; |
|
103 | ||
104 | /** The lenght of the buffer used for processing. */ |
|
105 | protected int bufSize; |
|
106 | ||
107 | /** The default lenght of the buffer used for processing. */ |
|
108 | protected static final int DEFAULT_BUFSIZE = 4096; |
|
109 | ||
110 | /** The buffer used for processing. */ |
|
111 | protected byte[] buffer; |
|
112 | ||
113 | /** |
|
114 | * The index of first valid character in the buffer. |
|
115 | * |
|
116 | * 0 <= head < bufSize |
|
117 | */ |
|
118 | protected int head; |
|
119 | ||
120 | /** |
|
121 | * The index of last valid characer in the buffer + 1. |
|
122 | * |
|
123 | * 0 <= tail <= bufSize |
|
124 | */ |
|
125 | protected int tail; |
|
126 | ||
127 | /** |
|
128 | * A byte sequence that marks the end of <code>header-part</code> |
|
129 | * (<code>CRLFCRLF</code>). |
|
130 | */ |
|
131 | 0 | protected static final byte[] HEADER_SEPARATOR = {0x0D, 0x0A, 0x0D, 0x0A}; |
132 | ||
133 | /** |
|
134 | * A byte sequence that that follows a delimiter that will be |
|
135 | * followed by an encapsulation (<code>CRLF</code>). |
|
136 | */ |
|
137 | 0 | protected static final byte[] FIELD_SEPARATOR = {0x0D, 0x0A}; |
138 | ||
139 | /** |
|
140 | * A byte sequence that that follows a delimiter of the last |
|
141 | * encapsulation in the stream (<code>--</code>). |
|
142 | */ |
|
143 | 0 | protected static final byte[] STREAM_TERMINATOR = {0x2D, 0x2D}; |
144 | ||
145 | /** |
|
146 | * Constructs a MultipartStream with a custom size buffer. |
|
147 | * |
|
148 | * <p>Note that the buffer must be at least big enough to contain |
|
149 | * the boundary string, plus 4 characters for CR/LF and double |
|
150 | * dash, plus at least one byte of data. Too small buffer size |
|
151 | * setting will degrade performance. |
|
152 | * |
|
153 | * @param input The <code>InputStream</code> to serve as a data |
|
154 | * source. |
|
155 | * @param boundary The token used for dividing the stream into |
|
156 | * <code>encapsulations</code>. |
|
157 | * @param bufSize The size of the buffer to be used in bytes. |
|
158 | * @exception MalformedStreamException. |
|
159 | * @exception IOException. |
|
160 | */ |
|
161 | public MultipartStream(InputStream input, |
|
162 | byte[] boundary, |
|
163 | int bufSize) |
|
164 | throws MalformedStreamException, |
|
165 | IOException |
|
166 | 0 | { |
167 | 0 | this.input = input; |
168 | 0 | this.bufSize = bufSize; |
169 | 0 | this.buffer = new byte[bufSize]; |
170 | ||
171 | // We prepend CR/LF to the boundary to chop trailng CR/LF from |
|
172 | // body-data tokens. |
|
173 | 0 | this.boundary = new byte[boundary.length + 4]; |
174 | 0 | this.boundaryLength = boundary.length + 4; |
175 | 0 | this.keepRegion = boundary.length + 3; |
176 | 0 | this.boundary[0] = 0x0D; |
177 | 0 | this.boundary[1] = 0x0A; |
178 | 0 | this.boundary[2] = 0x2D; |
179 | 0 | this.boundary[3] = 0x2D; |
180 | 0 | System.arraycopy(boundary, 0, this.boundary, 4, boundary.length); |
181 | ||
182 | 0 | head = 0; |
183 | 0 | tail = 0; |
184 | 0 | } |
185 | ||
186 | /** |
|
187 | * Constructs a MultipartStream with a defalut size buffer. |
|
188 | * |
|
189 | * @param input The <code>InputStream</code> to serve as a data |
|
190 | * source. |
|
191 | * @param boundary The token used for dividing the stream into |
|
192 | * <code>encapsulations</code>. |
|
193 | * @exception IOException. |
|
194 | */ |
|
195 | public MultipartStream(InputStream input, |
|
196 | byte[] boundary) |
|
197 | throws IOException |
|
198 | { |
|
199 | 0 | this(input, boundary, DEFAULT_BUFSIZE); |
200 | 0 | } |
201 | ||
202 | /** |
|
203 | * Reads a byte from the <code>buffer</code>, and refills it as |
|
204 | * neccessary. |
|
205 | * |
|
206 | * @return Next byte from the input stream. |
|
207 | * @exception IOException, if there isn't any more data available. |
|
208 | */ |
|
209 | public byte readByte() |
|
210 | throws IOException |
|
211 | { |
|
212 | // Buffer depleted ? |
|
213 | 0 | if (head == tail) |
214 | { |
|
215 | 0 | head = 0; |
216 | // Refill. |
|
217 | 0 | tail = input.read(buffer, head, bufSize); |
218 | 0 | if (tail == -1) |
219 | { |
|
220 | // No more data available. |
|
221 | 0 | throw new IOException("No more data is available"); |
222 | } |
|
223 | } |
|
224 | 0 | return buffer[head++]; |
225 | } |
|
226 | ||
227 | /** |
|
228 | * Skips a <code>boundary</code> token, and checks wether more |
|
229 | * <code>encapsulations</code> are contained in the stream. |
|
230 | * |
|
231 | * @return <code>True</code> if there are more encapsulations in |
|
232 | * this stream. |
|
233 | * @exception MalformedStreamException if the stream ends |
|
234 | * unexpecetedly or fails to follow required syntax. |
|
235 | */ |
|
236 | public boolean readBoundary() |
|
237 | throws MalformedStreamException |
|
238 | { |
|
239 | 0 | byte[] marker = new byte[2]; |
240 | 0 | boolean nextChunk = false; |
241 | ||
242 | 0 | head += boundaryLength; |
243 | try |
|
244 | { |
|
245 | 0 | marker[0] = readByte(); |
246 | 0 | marker[1] = readByte(); |
247 | 0 | if (arrayequals(marker, STREAM_TERMINATOR, 2)) |
248 | { |
|
249 | 0 | nextChunk = false; |
250 | } |
|
251 | 0 | else if (arrayequals(marker, FIELD_SEPARATOR, 2)) |
252 | { |
|
253 | 0 | nextChunk = true; |
254 | } |
|
255 | else |
|
256 | { |
|
257 | 0 | throw new MalformedStreamException("Unexpected characters follow a boundary"); |
258 | } |
|
259 | } |
|
260 | 0 | catch (IOException e) |
261 | { |
|
262 | 0 | throw new MalformedStreamException("Stream ended unexpectedly"); |
263 | 0 | } |
264 | 0 | return nextChunk; |
265 | } |
|
266 | ||
267 | /** |
|
268 | * Changes the boundary token used for partitioning the stream. |
|
269 | * |
|
270 | * <p>This method allows single pass processing of nested |
|
271 | * multipart streams. |
|
272 | * |
|
273 | * <p>The boundary token of the nested stream is |
|
274 | * <code>required</code> to be of the same length as the boundary |
|
275 | * token in parent stream. |
|
276 | * |
|
277 | * <p>Restoring parent stream boundary token after processing of a |
|
278 | * nested stream is left ot the application. <br> |
|
279 | * |
|
280 | * @param boundary A boundary to be used for parsing of the nested |
|
281 | * stream. |
|
282 | * @exception IllegalBoundaryException, if <code>boundary</code> |
|
283 | * has diffrent lenght than the one being currently in use. |
|
284 | */ |
|
285 | public void setBoundary(byte[] boundary) |
|
286 | throws IllegalBoundaryException |
|
287 | { |
|
288 | 0 | if (boundary.length != boundaryLength - 4) |
289 | { |
|
290 | 0 | throw new IllegalBoundaryException("The length of a boundary token can not be changed"); |
291 | } |
|
292 | 0 | System.arraycopy(boundary, 0, this.boundary, 4, boundary.length); |
293 | 0 | } |
294 | ||
295 | /** |
|
296 | * <p>Reads <code>header-part</code> of the current |
|
297 | * <code>encapsulation</code> |
|
298 | * |
|
299 | * <p>Headers are returned verbatim to the input stream, including |
|
300 | * traling <code>CRLF</code> marker. Parsing is left to the |
|
301 | * application. |
|
302 | * |
|
303 | * <p><strong>TODO</strong> allow limiting maximum header size to |
|
304 | * protect against abuse.<br> |
|
305 | * |
|
306 | * @return <code>header-part</code> of the current encapsulation. |
|
307 | * @exception MalformedStreamException, if the stream ends |
|
308 | * unexpecetedly. |
|
309 | */ |
|
310 | public String readHeaders() |
|
311 | throws MalformedStreamException |
|
312 | { |
|
313 | 0 | int i = 0; |
314 | 0 | byte b[] = new byte[1]; |
315 | 0 | StringBuffer buf = new StringBuffer(); |
316 | 0 | int sizeMax = HEADER_PART_SIZE_MAX; |
317 | 0 | int size = 0; |
318 | 0 | while (i < 4) |
319 | { |
|
320 | try |
|
321 | { |
|
322 | 0 | b[0] = readByte(); |
323 | } |
|
324 | 0 | catch (IOException e) |
325 | { |
|
326 | 0 | throw new MalformedStreamException("Stream ended unexpectedly"); |
327 | 0 | } |
328 | 0 | size++; |
329 | 0 | if (b[0] == HEADER_SEPARATOR[i]) |
330 | { |
|
331 | 0 | i++; |
332 | } |
|
333 | else |
|
334 | { |
|
335 | 0 | i = 0; |
336 | } |
|
337 | 0 | if (size <= sizeMax) |
338 | { |
|
339 | 0 | buf.append(new String(b)); |
340 | } |
|
341 | } |
|
342 | 0 | return buf.toString(); |
343 | } |
|
344 | ||
345 | /** |
|
346 | * Reads <code>body-data</code> from the current |
|
347 | * <code>encapsulation</code> and writes its contents into the |
|
348 | * output <code>Stream</code>. |
|
349 | * |
|
350 | * <p>Arbitrary large amouts of data can be processed by this |
|
351 | * method using a constant size buffer. (see {@link |
|
352 | * #MultipartStream(InputStream,byte[],int) constructor}). |
|
353 | * |
|
354 | * @param output The <code>Stream</code> to write data into. |
|
355 | * @return the amount of data written. |
|
356 | * @exception MalformedStreamException |
|
357 | * @exception IOException |
|
358 | */ |
|
359 | public int readBodyData(OutputStream output) |
|
360 | throws MalformedStreamException, |
|
361 | IOException |
|
362 | { |
|
363 | 0 | boolean done = false; |
364 | int pad; |
|
365 | int pos; |
|
366 | int bytesRead; |
|
367 | 0 | int total = 0; |
368 | 0 | while (!done) |
369 | { |
|
370 | // Is boundary token present somewere in the buffer? |
|
371 | 0 | pos = findSeparator(); |
372 | 0 | if (pos != -1) |
373 | { |
|
374 | // Write the rest of the data before the boundary. |
|
375 | 0 | output.write(buffer, head, pos - head); |
376 | 0 | total += pos - head; |
377 | 0 | head = pos; |
378 | 0 | done = true; |
379 | } |
|
380 | else |
|
381 | { |
|
382 | // Determine how much data should be kept in the |
|
383 | // buffer. |
|
384 | 0 | if (tail - head > keepRegion) |
385 | { |
|
386 | 0 | pad = keepRegion; |
387 | } |
|
388 | else |
|
389 | { |
|
390 | 0 | pad = tail - head; |
391 | } |
|
392 | // Write out the data belonging to the body-data. |
|
393 | 0 | output.write(buffer, head, tail - head - pad); |
394 | ||
395 | // Move the data to the beging of the buffer. |
|
396 | 0 | total += tail - head - pad; |
397 | 0 | System.arraycopy(buffer, tail - pad, buffer, 0, pad); |
398 | ||
399 | // Refill buffer with new data. |
|
400 | 0 | head = 0; |
401 | 0 | bytesRead = input.read(buffer, pad, bufSize - pad); |
402 | ||
403 | // [pprrrrrrr] |
|
404 | 0 | if (bytesRead != -1) |
405 | { |
|
406 | 0 | tail = pad + bytesRead; |
407 | } |
|
408 | else |
|
409 | { |
|
410 | // The last pad amount is left in the buffer. |
|
411 | // Boundary can't be in there so write out the |
|
412 | // data you have and signal an error condition. |
|
413 | 0 | output.write(buffer, 0, pad); |
414 | 0 | output.flush(); |
415 | 0 | total += pad; |
416 | 0 | throw new MalformedStreamException("Stream ended unexpectedly"); |
417 | } |
|
418 | } |
|
419 | } |
|
420 | 0 | output.flush(); |
421 | 0 | return total; |
422 | } |
|
423 | ||
424 | /** |
|
425 | * Reads <code>body-data</code> from the current |
|
426 | * <code>encapsulation</code> and discards it. |
|
427 | * |
|
428 | * <p>Use this method to skip encapsulations you don't need or |
|
429 | * don't understand. |
|
430 | * |
|
431 | * @return The amount of data discarded. |
|
432 | * @exception MalformedStreamException |
|
433 | * @exception IOException |
|
434 | */ |
|
435 | public int discardBodyData() |
|
436 | throws MalformedStreamException, |
|
437 | IOException |
|
438 | { |
|
439 | 0 | boolean done = false; |
440 | int pad; |
|
441 | int pos; |
|
442 | int bytesRead; |
|
443 | 0 | int total = 0; |
444 | 0 | while (!done) |
445 | { |
|
446 | // Is boundary token present somewere in the buffer? |
|
447 | 0 | pos = findSeparator(); |
448 | 0 | if (pos != -1) |
449 | { |
|
450 | // Write the rest of the data before the boundary. |
|
451 | 0 | total += pos - head; |
452 | 0 | head = pos; |
453 | 0 | done = true; |
454 | } |
|
455 | else |
|
456 | { |
|
457 | // Determine how much data should be kept in the |
|
458 | // buffer. |
|
459 | 0 | if (tail - head > keepRegion) |
460 | { |
|
461 | 0 | pad = keepRegion; |
462 | } |
|
463 | else |
|
464 | { |
|
465 | 0 | pad = tail - head; |
466 | } |
|
467 | 0 | total += tail - head - pad; |
468 | ||
469 | // Move the data to the beging of the buffer. |
|
470 | 0 | System.arraycopy(buffer, tail - pad, buffer, 0, pad); |
471 | ||
472 | // Refill buffer with new data. |
|
473 | 0 | head = 0; |
474 | 0 | bytesRead = input.read(buffer, pad, bufSize - pad); |
475 | ||
476 | // [pprrrrrrr] |
|
477 | 0 | if (bytesRead != -1) |
478 | { |
|
479 | 0 | tail = pad + bytesRead; |
480 | } |
|
481 | else |
|
482 | { |
|
483 | // The last pad amount is left in the buffer. |
|
484 | // Boundary can't be in there so signal an error |
|
485 | // condition. |
|
486 | 0 | total += pad; |
487 | 0 | throw new MalformedStreamException("Stream ended unexpectedly"); |
488 | } |
|
489 | } |
|
490 | } |
|
491 | 0 | return total; |
492 | } |
|
493 | ||
494 | /** |
|
495 | * Finds the beginning of the first <code>encapsulation</code>. |
|
496 | * |
|
497 | * @return <code>True</code> if an <code>encapsulation</code> was |
|
498 | * found in the stream. |
|
499 | * @exception IOException |
|
500 | */ |
|
501 | public boolean skipPreamble() |
|
502 | throws IOException |
|
503 | { |
|
504 | // First delimiter may be not preceeded with a CRLF. |
|
505 | 0 | System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2); |
506 | 0 | boundaryLength = boundary.length - 2; |
507 | try |
|
508 | { |
|
509 | // Discard all data up to the delimiter. |
|
510 | 0 | discardBodyData(); |
511 | ||
512 | // Read boundary - if succeded, the stream contains an |
|
513 | // encapsulation. |
|
514 | 0 | return readBoundary(); |
515 | } |
|
516 | 0 | catch (MalformedStreamException e) |
517 | { |
|
518 | 0 | return false; |
519 | } |
|
520 | finally |
|
521 | { |
|
522 | // Restore delimiter. |
|
523 | 0 | System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2); |
524 | 0 | boundaryLength = boundary.length; |
525 | 0 | boundary[0] = 0x0D; |
526 | 0 | boundary[1] = 0x0A; |
527 | } |
|
528 | } |
|
529 | ||
530 | /** |
|
531 | * Compares <code>count</code> first bytes in the arrays |
|
532 | * <code>a</code> and <code>b</code>. |
|
533 | * |
|
534 | * @param a The first array to compare. |
|
535 | * @param b The second array to compare. |
|
536 | * @param count How many bytes should be compared. |
|
537 | * @return <code>true</code> if <code>count</code> first bytes in |
|
538 | * arrays <code>a</code> and <code>b</code> are equal. |
|
539 | */ |
|
540 | public static boolean arrayequals(byte[] a, |
|
541 | byte[] b, |
|
542 | int count) |
|
543 | { |
|
544 | 0 | for (int i = 0; i < count; i++) |
545 | { |
|
546 | 0 | if (a[i] != b[i]) |
547 | { |
|
548 | 0 | return false; |
549 | } |
|
550 | } |
|
551 | 0 | return true; |
552 | } |
|
553 | ||
554 | /** |
|
555 | * Searches a byte of specified value in the <code>buffer</code> |
|
556 | * starting at specified <code>position</code>. |
|
557 | * |
|
558 | * @param value the value to find. |
|
559 | * @param pos The starting position for searching. |
|
560 | * @return The position of byte found, counting from beginning of |
|
561 | * the <code>buffer</code>, or <code>-1</code> if not found. |
|
562 | */ |
|
563 | protected int findByte(byte value, |
|
564 | int pos) |
|
565 | { |
|
566 | 0 | for (int i = pos; i < tail; i++) |
567 | 0 | if (buffer[i] == value) |
568 | 0 | return i; |
569 | ||
570 | 0 | return -1; |
571 | } |
|
572 | ||
573 | /** |
|
574 | * Searches the <code>boundary</code> in <code>buffer</code> |
|
575 | * region delimited by <code>head</code> and <code>tail</code>. |
|
576 | * |
|
577 | * @return The position of the boundary found, counting from |
|
578 | * beginning of the <code>buffer</code>, or <code>-1</code> if not |
|
579 | * found. |
|
580 | */ |
|
581 | protected int findSeparator() |
|
582 | { |
|
583 | int first; |
|
584 | 0 | int match = 0; |
585 | 0 | int maxpos = tail - boundaryLength; |
586 | 0 | for (first = head; |
587 | 0 | (first <= maxpos) && (match != boundaryLength); |
588 | 0 | first++) |
589 | { |
|
590 | 0 | first = findByte(boundary[0], first); |
591 | 0 | if (first == -1 || (first > maxpos)) |
592 | 0 | return -1; |
593 | 0 | for (match = 1; match < boundaryLength; match++) |
594 | { |
|
595 | 0 | if (buffer[first + match] != boundary[match]) |
596 | 0 | break; |
597 | } |
|
598 | } |
|
599 | 0 | if (match == boundaryLength) |
600 | { |
|
601 | 0 | return first - 1; |
602 | } |
|
603 | 0 | return -1; |
604 | } |
|
605 | ||
606 | /** |
|
607 | * Thrown to indicate that the input stream fails to follow the |
|
608 | * required syntax. |
|
609 | */ |
|
610 | public class MalformedStreamException |
|
611 | extends IOException |
|
612 | { |
|
613 | /** Serial Version UID */ |
|
614 | private static final long serialVersionUID = 3813694874163574138L; |
|
615 | ||
616 | /** |
|
617 | * Constructs a <code>MalformedStreamException</code> with no |
|
618 | * detail message. |
|
619 | */ |
|
620 | public MalformedStreamException() |
|
621 | { |
|
622 | super(); |
|
623 | } |
|
624 | ||
625 | /** |
|
626 | * Constructs an <code>MalformedStreamException</code> with |
|
627 | * the specified detail message. |
|
628 | * |
|
629 | * @param message The detail message. |
|
630 | */ |
|
631 | public MalformedStreamException(String message) |
|
632 | { |
|
633 | super(message); |
|
634 | } |
|
635 | } |
|
636 | ||
637 | /** |
|
638 | * Thrown upon attempt of setting an invalid boundary token. |
|
639 | */ |
|
640 | public class IllegalBoundaryException |
|
641 | extends IOException |
|
642 | { |
|
643 | /** Serial Version UID */ |
|
644 | private static final long serialVersionUID = -2883885421190362860L; |
|
645 | ||
646 | /** |
|
647 | * Constructs an <code>IllegalBoundaryException</code> with no |
|
648 | * detail message. |
|
649 | */ |
|
650 | public IllegalBoundaryException() |
|
651 | { |
|
652 | super(); |
|
653 | } |
|
654 | ||
655 | /** |
|
656 | * Constructs an <code>IllegalBoundaryException</code> with |
|
657 | * the specified detail message. |
|
658 | * |
|
659 | * @param message The detail message. |
|
660 | */ |
|
661 | public IllegalBoundaryException(String message) |
|
662 | { |
|
663 | super(message); |
|
664 | } |
|
665 | } |
|
666 | ||
667 | /*------------------------------------------------------------- |
|
668 | ||
669 | // These are the methods that were used to debug this stuff. |
|
670 | ||
671 | // Dump data. |
|
672 | protected void dump() |
|
673 | { |
|
674 | System.out.println("01234567890"); |
|
675 | byte[] temp = new byte[buffer.length]; |
|
676 | for(int i=0; i<buffer.length; i++) |
|
677 | { |
|
678 | if(buffer[i] == 0x0D || buffer[i] == 0x0A) |
|
679 | { |
|
680 | temp[i] = 0x21; |
|
681 | } |
|
682 | else |
|
683 | { |
|
684 | temp[i] = buffer[i]; |
|
685 | } |
|
686 | } |
|
687 | System.out.println(new String(temp)); |
|
688 | int i; |
|
689 | for(i=0; i<head; i++) |
|
690 | System.out.print(" "); |
|
691 | System.out.println("h"); |
|
692 | for(i=0; i<tail; i++) |
|
693 | System.out.print(" "); |
|
694 | System.out.println("t"); |
|
695 | System.out.flush(); |
|
696 | } |
|
697 | ||
698 | // Main routine, for testing purposes only. |
|
699 | // |
|
700 | // @param args A String[] with the command line arguments. |
|
701 | // @exception Exception, a generic exception. |
|
702 | public static void main( String[] args ) |
|
703 | throws Exception |
|
704 | { |
|
705 | File boundaryFile = new File("boundary.dat"); |
|
706 | int boundarySize = (int)boundaryFile.length(); |
|
707 | byte[] boundary = new byte[boundarySize]; |
|
708 | FileInputStream input = new FileInputStream(boundaryFile); |
|
709 | input.read(boundary,0,boundarySize); |
|
710 | ||
711 | input = new FileInputStream("multipart.dat"); |
|
712 | MultipartStream chunks = new MultipartStream(input, boundary); |
|
713 | ||
714 | int i = 0; |
|
715 | String header; |
|
716 | OutputStream output; |
|
717 | boolean nextChunk = chunks.skipPreamble(); |
|
718 | while(nextChunk) |
|
719 | { |
|
720 | header = chunks.readHeaders(); |
|
721 | System.out.println("!"+header+"!"); |
|
722 | System.out.println("wrote part"+i+".dat"); |
|
723 | output = new FileOutputStream("part"+(i++)+".dat"); |
|
724 | chunks.readBodyData(output); |
|
725 | nextChunk = chunks.readBoundary(); |
|
726 | } |
|
727 | } |
|
728 | ||
729 | */ |
|
730 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |