1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.handler.codec.http.multipart;
17
18 import org.jboss.netty.buffer.ChannelBuffer;
19 import org.jboss.netty.buffer.ChannelBuffers;
20 import org.jboss.netty.handler.codec.http.HttpChunk;
21 import org.jboss.netty.handler.codec.http.HttpConstants;
22 import org.jboss.netty.handler.codec.http.HttpHeaders;
23 import org.jboss.netty.handler.codec.http.HttpRequest;
24 import org.jboss.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadNoBackArrayException;
25 import org.jboss.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadOptimize;
26 import org.jboss.netty.handler.codec.http.multipart.HttpPostBodyUtil.TransferEncodingMechanism;
27 import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException;
28 import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException;
29 import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.MultiPartStatus;
30 import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.NotEnoughDataDecoderException;
31 import org.jboss.netty.util.internal.CaseIgnoringComparator;
32 import org.jboss.netty.util.internal.StringUtil;
33
34 import java.io.IOException;
35 import java.nio.charset.Charset;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.TreeMap;
40
41
42
43
44 public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequestDecoder {
45
46
47
48 private final HttpDataFactory factory;
49
50
51
52
53 private final HttpRequest request;
54
55
56
57
58 private Charset charset;
59
60
61
62
63 private boolean isLastChunk;
64
65
66
67
68 private final List<InterfaceHttpData> bodyListHttpData = new ArrayList<InterfaceHttpData>();
69
70
71
72
73 private final Map<String, List<InterfaceHttpData>> bodyMapHttpData = new TreeMap<String, List<InterfaceHttpData>>(
74 CaseIgnoringComparator.INSTANCE);
75
76
77
78
79 private ChannelBuffer undecodedChunk;
80
81
82
83
84 private int bodyListHttpDataRank;
85
86
87
88
89 private String multipartDataBoundary;
90
91
92
93
94
95 private String multipartMixedBoundary;
96
97
98
99
100 private MultiPartStatus currentStatus = MultiPartStatus.NOTSTARTED;
101
102
103
104
105 private Map<String, Attribute> currentFieldAttributes;
106
107
108
109
110 private FileUpload currentFileUpload;
111
112
113
114
115 private Attribute currentAttribute;
116
117
118
119
120
121
122
123 public HttpPostMultipartRequestDecoder(HttpRequest request) throws ErrorDataDecoderException {
124 this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE),
125 request, HttpConstants.DEFAULT_CHARSET);
126 }
127
128
129
130
131
132
133
134
135 public HttpPostMultipartRequestDecoder(HttpDataFactory factory, HttpRequest request)
136 throws ErrorDataDecoderException {
137 this(factory, request, HttpConstants.DEFAULT_CHARSET);
138 }
139
140
141
142
143
144
145
146
147
148 public HttpPostMultipartRequestDecoder(HttpDataFactory factory, HttpRequest request,
149 Charset charset) throws ErrorDataDecoderException {
150 if (factory == null) {
151 throw new NullPointerException("factory");
152 }
153 if (request == null) {
154 throw new NullPointerException("request");
155 }
156 if (charset == null) {
157 throw new NullPointerException("charset");
158 }
159 this.request = request;
160 this.charset = charset;
161 this.factory = factory;
162
163 setMultipart(this.request.headers().get(HttpHeaders.Names.CONTENT_TYPE));
164 if (!this.request.isChunked()) {
165 undecodedChunk = this.request.getContent();
166 isLastChunk = true;
167 parseBody();
168 }
169 }
170
171
172
173
174 private void setMultipart(String contentType) throws ErrorDataDecoderException {
175 String[] dataBoundary = HttpPostRequestDecoder.getMultipartDataBoundary(contentType);
176 if (dataBoundary != null) {
177 multipartDataBoundary = dataBoundary[0];
178 if (dataBoundary.length > 1 && dataBoundary[1] != null) {
179 charset = Charset.forName(dataBoundary[1]);
180 }
181 } else {
182 multipartDataBoundary = null;
183 }
184 currentStatus = MultiPartStatus.HEADERDELIMITER;
185 }
186
187 public boolean isMultipart() {
188 return true;
189 }
190
191 public List<InterfaceHttpData> getBodyHttpDatas()
192 throws NotEnoughDataDecoderException {
193 if (!isLastChunk) {
194 throw new NotEnoughDataDecoderException();
195 }
196 return bodyListHttpData;
197 }
198
199 public List<InterfaceHttpData> getBodyHttpDatas(String name)
200 throws NotEnoughDataDecoderException {
201 if (!isLastChunk) {
202 throw new NotEnoughDataDecoderException();
203 }
204 return bodyMapHttpData.get(name);
205 }
206
207 public InterfaceHttpData getBodyHttpData(String name)
208 throws NotEnoughDataDecoderException {
209 if (!isLastChunk) {
210 throw new NotEnoughDataDecoderException();
211 }
212 List<InterfaceHttpData> list = bodyMapHttpData.get(name);
213 if (list != null) {
214 return list.get(0);
215 }
216 return null;
217 }
218
219 public void offer(HttpChunk chunk) throws ErrorDataDecoderException {
220 ChannelBuffer chunked = chunk.getContent();
221 if (undecodedChunk == null) {
222 undecodedChunk = chunked;
223 } else {
224
225
226 undecodedChunk = ChannelBuffers.wrappedBuffer(
227 undecodedChunk, chunked);
228 }
229 if (chunk.isLast()) {
230 isLastChunk = true;
231 }
232 parseBody();
233 }
234
235 public boolean hasNext() throws EndOfDataDecoderException {
236 if (currentStatus == MultiPartStatus.EPILOGUE) {
237
238 if (bodyListHttpDataRank >= bodyListHttpData.size()) {
239 throw new EndOfDataDecoderException();
240 }
241 }
242 return !bodyListHttpData.isEmpty() && bodyListHttpDataRank < bodyListHttpData.size();
243 }
244
245 public InterfaceHttpData next() throws EndOfDataDecoderException {
246 if (hasNext()) {
247 return bodyListHttpData.get(bodyListHttpDataRank++);
248 }
249 return null;
250 }
251
252
253
254
255
256
257 private void parseBody() throws ErrorDataDecoderException {
258 if (currentStatus == MultiPartStatus.PREEPILOGUE ||
259 currentStatus == MultiPartStatus.EPILOGUE) {
260 if (isLastChunk) {
261 currentStatus = MultiPartStatus.EPILOGUE;
262 }
263 return;
264 }
265 parseBodyMultipart();
266 }
267
268
269
270
271 private void addHttpData(InterfaceHttpData data) {
272 if (data == null) {
273 return;
274 }
275 List<InterfaceHttpData> datas = bodyMapHttpData.get(data.getName());
276 if (datas == null) {
277 datas = new ArrayList<InterfaceHttpData>(1);
278 bodyMapHttpData.put(data.getName(), datas);
279 }
280 datas.add(data);
281 bodyListHttpData.add(data);
282 }
283
284
285
286
287
288
289 private void parseBodyMultipart() throws ErrorDataDecoderException {
290 if (undecodedChunk == null || undecodedChunk.readableBytes() == 0) {
291
292 return;
293 }
294 InterfaceHttpData data = decodeMultipart(currentStatus);
295 while (data != null) {
296 addHttpData(data);
297 if (currentStatus == MultiPartStatus.PREEPILOGUE ||
298 currentStatus == MultiPartStatus.EPILOGUE) {
299 break;
300 }
301 data = decodeMultipart(currentStatus);
302 }
303 }
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320 private InterfaceHttpData decodeMultipart(MultiPartStatus state)
321 throws ErrorDataDecoderException {
322 switch (state) {
323 case NOTSTARTED:
324 throw new ErrorDataDecoderException(
325 "Should not be called with the current status");
326 case PREAMBLE:
327
328 throw new ErrorDataDecoderException(
329 "Should not be called with the current status");
330 case HEADERDELIMITER: {
331
332 return findMultipartDelimiter(multipartDataBoundary,
333 MultiPartStatus.DISPOSITION, MultiPartStatus.PREEPILOGUE);
334 }
335 case DISPOSITION: {
336
337
338
339
340
341
342
343
344
345 return findMultipartDisposition();
346 }
347 case FIELD: {
348
349 Charset localCharset = null;
350 Attribute charsetAttribute = currentFieldAttributes
351 .get(HttpHeaders.Values.CHARSET);
352 if (charsetAttribute != null) {
353 try {
354 localCharset = Charset.forName(charsetAttribute.getValue());
355 } catch (IOException e) {
356 throw new ErrorDataDecoderException(e);
357 }
358 }
359 Attribute nameAttribute = currentFieldAttributes
360 .get(HttpPostBodyUtil.NAME);
361 if (currentAttribute == null) {
362 try {
363 currentAttribute = factory.createAttribute(request,
364 cleanString(nameAttribute.getValue()));
365 } catch (NullPointerException e) {
366 throw new ErrorDataDecoderException(e);
367 } catch (IllegalArgumentException e) {
368 throw new ErrorDataDecoderException(e);
369 } catch (IOException e) {
370 throw new ErrorDataDecoderException(e);
371 }
372 if (localCharset != null) {
373 currentAttribute.setCharset(localCharset);
374 }
375 }
376
377 try {
378 loadFieldMultipart(multipartDataBoundary);
379 } catch (NotEnoughDataDecoderException e) {
380 return null;
381 }
382 Attribute finalAttribute = currentAttribute;
383 currentAttribute = null;
384 currentFieldAttributes = null;
385
386 currentStatus = MultiPartStatus.HEADERDELIMITER;
387 return finalAttribute;
388 }
389 case FILEUPLOAD: {
390
391 return getFileUpload(multipartDataBoundary);
392 }
393 case MIXEDDELIMITER: {
394
395
396 return findMultipartDelimiter(multipartMixedBoundary,
397 MultiPartStatus.MIXEDDISPOSITION,
398 MultiPartStatus.HEADERDELIMITER);
399 }
400 case MIXEDDISPOSITION: {
401 return findMultipartDisposition();
402 }
403 case MIXEDFILEUPLOAD: {
404
405 return getFileUpload(multipartMixedBoundary);
406 }
407 case PREEPILOGUE:
408 return null;
409 case EPILOGUE:
410 return null;
411 default:
412 throw new ErrorDataDecoderException("Shouldn't reach here.");
413 }
414 }
415
416
417
418
419
420 void skipControlCharacters() throws NotEnoughDataDecoderException {
421 SeekAheadOptimize sao;
422 try {
423 sao = new SeekAheadOptimize(undecodedChunk);
424 } catch (SeekAheadNoBackArrayException e) {
425 try {
426 skipControlCharactersStandard();
427 } catch (IndexOutOfBoundsException e1) {
428 throw new NotEnoughDataDecoderException(e1);
429 }
430 return;
431 }
432
433 while (sao.pos < sao.limit) {
434 char c = (char) (sao.bytes[sao.pos ++] & 0xFF);
435 if (!Character.isISOControl(c) && !Character.isWhitespace(c)) {
436 sao.setReadPosition(1);
437 return;
438 }
439 }
440 throw new NotEnoughDataDecoderException("Access out of bounds");
441 }
442 void skipControlCharactersStandard() {
443 for (;;) {
444 char c = (char) undecodedChunk.readUnsignedByte();
445 if (!Character.isISOControl(c) && !Character.isWhitespace(c)) {
446 undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1);
447 break;
448 }
449 }
450 }
451
452
453
454
455
456
457
458
459
460 private InterfaceHttpData findMultipartDelimiter(String delimiter,
461 MultiPartStatus dispositionStatus,
462 MultiPartStatus closeDelimiterStatus)
463 throws ErrorDataDecoderException {
464
465 int readerIndex = undecodedChunk.readerIndex();
466 try {
467 skipControlCharacters();
468 } catch (NotEnoughDataDecoderException e1) {
469 undecodedChunk.readerIndex(readerIndex);
470 return null;
471 }
472 skipOneLine();
473 String newline;
474 try {
475 newline = readDelimiter(delimiter);
476 } catch (NotEnoughDataDecoderException e) {
477 undecodedChunk.readerIndex(readerIndex);
478 return null;
479 }
480 if (newline.equals(delimiter)) {
481 currentStatus = dispositionStatus;
482 return decodeMultipart(dispositionStatus);
483 }
484 if (newline.equals(delimiter + "--")) {
485
486 currentStatus = closeDelimiterStatus;
487 if (currentStatus == MultiPartStatus.HEADERDELIMITER) {
488
489
490 currentFieldAttributes = null;
491 return decodeMultipart(MultiPartStatus.HEADERDELIMITER);
492 }
493 return null;
494 }
495 undecodedChunk.readerIndex(readerIndex);
496 throw new ErrorDataDecoderException("No Multipart delimiter found");
497 }
498
499
500
501
502
503
504 private InterfaceHttpData findMultipartDisposition()
505 throws ErrorDataDecoderException {
506 int readerIndex = undecodedChunk.readerIndex();
507 if (currentStatus == MultiPartStatus.DISPOSITION) {
508 currentFieldAttributes = new TreeMap<String, Attribute>(
509 CaseIgnoringComparator.INSTANCE);
510 }
511
512 while (!skipOneLine()) {
513 String newline;
514 try {
515 skipControlCharacters();
516 newline = readLine();
517 } catch (NotEnoughDataDecoderException e) {
518 undecodedChunk.readerIndex(readerIndex);
519 return null;
520 }
521 String[] contents = splitMultipartHeader(newline);
522 if (contents[0].equalsIgnoreCase(HttpPostBodyUtil.CONTENT_DISPOSITION)) {
523 boolean checkSecondArg;
524 if (currentStatus == MultiPartStatus.DISPOSITION) {
525 checkSecondArg = contents[1]
526 .equalsIgnoreCase(HttpPostBodyUtil.FORM_DATA);
527 } else {
528 checkSecondArg = contents[1]
529 .equalsIgnoreCase(HttpPostBodyUtil.ATTACHMENT) ||
530 contents[1]
531 .equalsIgnoreCase(HttpPostBodyUtil.FILE);
532 }
533 if (checkSecondArg) {
534
535 for (int i = 2; i < contents.length; i ++) {
536 String[] values = StringUtil.split(contents[i], '=', 2);
537 Attribute attribute;
538 try {
539 String name = cleanString(values[0]);
540 String value = values[1];
541
542
543 if (HttpPostBodyUtil.FILENAME.equals(name)) {
544
545 value = value.substring(1, value.length() - 1);
546 } else {
547
548 value = cleanString(value);
549 }
550 attribute = factory.createAttribute(request, name, value);
551 } catch (NullPointerException e) {
552 throw new ErrorDataDecoderException(e);
553 } catch (IllegalArgumentException e) {
554 throw new ErrorDataDecoderException(e);
555 }
556 currentFieldAttributes.put(attribute.getName(),
557 attribute);
558 }
559 }
560 } else if (contents[0]
561 .equalsIgnoreCase(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING)) {
562 Attribute attribute;
563 try {
564 attribute = factory.createAttribute(request,
565 HttpHeaders.Names.CONTENT_TRANSFER_ENCODING,
566 cleanString(contents[1]));
567 } catch (NullPointerException e) {
568 throw new ErrorDataDecoderException(e);
569 } catch (IllegalArgumentException e) {
570 throw new ErrorDataDecoderException(e);
571 }
572 currentFieldAttributes.put(
573 HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, attribute);
574 } else if (contents[0]
575 .equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH)) {
576 Attribute attribute;
577 try {
578 attribute = factory.createAttribute(request,
579 HttpHeaders.Names.CONTENT_LENGTH,
580 cleanString(contents[1]));
581 } catch (NullPointerException e) {
582 throw new ErrorDataDecoderException(e);
583 } catch (IllegalArgumentException e) {
584 throw new ErrorDataDecoderException(e);
585 }
586 currentFieldAttributes.put(HttpHeaders.Names.CONTENT_LENGTH,
587 attribute);
588 } else if (contents[0].equalsIgnoreCase(HttpHeaders.Names.CONTENT_TYPE)) {
589
590 if (contents[1].equalsIgnoreCase(HttpPostBodyUtil.MULTIPART_MIXED)) {
591 if (currentStatus == MultiPartStatus.DISPOSITION) {
592 String values = StringUtil.substringAfter(contents[2], '=');
593 multipartMixedBoundary = "--" + values;
594 currentStatus = MultiPartStatus.MIXEDDELIMITER;
595 return decodeMultipart(MultiPartStatus.MIXEDDELIMITER);
596 } else {
597 throw new ErrorDataDecoderException(
598 "Mixed Multipart found in a previous Mixed Multipart");
599 }
600 } else {
601 for (int i = 1; i < contents.length; i ++) {
602 if (contents[i].toLowerCase().startsWith(
603 HttpHeaders.Values.CHARSET)) {
604 String values = StringUtil.substringAfter(contents[i], '=');
605 Attribute attribute;
606 try {
607 attribute = factory.createAttribute(request,
608 HttpHeaders.Values.CHARSET,
609 cleanString(values));
610 } catch (NullPointerException e) {
611 throw new ErrorDataDecoderException(e);
612 } catch (IllegalArgumentException e) {
613 throw new ErrorDataDecoderException(e);
614 }
615 currentFieldAttributes.put(HttpHeaders.Values.CHARSET,
616 attribute);
617 } else {
618 Attribute attribute;
619 try {
620 attribute = factory.createAttribute(request,
621 cleanString(contents[0]), contents[i]);
622 } catch (NullPointerException e) {
623 throw new ErrorDataDecoderException(e);
624 } catch (IllegalArgumentException e) {
625 throw new ErrorDataDecoderException(e);
626 }
627 currentFieldAttributes.put(attribute.getName(),
628 attribute);
629 }
630 }
631 }
632 } else {
633 throw new ErrorDataDecoderException("Unknown Params: " +
634 newline);
635 }
636 }
637
638 Attribute filenameAttribute = currentFieldAttributes
639 .get(HttpPostBodyUtil.FILENAME);
640 if (currentStatus == MultiPartStatus.DISPOSITION) {
641 if (filenameAttribute != null) {
642
643 currentStatus = MultiPartStatus.FILEUPLOAD;
644
645 return decodeMultipart(MultiPartStatus.FILEUPLOAD);
646 } else {
647
648 currentStatus = MultiPartStatus.FIELD;
649
650 return decodeMultipart(MultiPartStatus.FIELD);
651 }
652 } else {
653 if (filenameAttribute != null) {
654
655 currentStatus = MultiPartStatus.MIXEDFILEUPLOAD;
656
657 return decodeMultipart(MultiPartStatus.MIXEDFILEUPLOAD);
658 } else {
659
660 throw new ErrorDataDecoderException("Filename not found");
661 }
662 }
663 }
664
665
666
667
668
669
670
671 private InterfaceHttpData getFileUpload(String delimiter)
672 throws ErrorDataDecoderException {
673
674
675 Attribute encoding = currentFieldAttributes
676 .get(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING);
677 Charset localCharset = charset;
678
679 TransferEncodingMechanism mechanism = TransferEncodingMechanism.BIT7;
680 if (encoding != null) {
681 String code;
682 try {
683 code = encoding.getValue().toLowerCase();
684 } catch (IOException e) {
685 throw new ErrorDataDecoderException(e);
686 }
687 if (code.equals(HttpPostBodyUtil.TransferEncodingMechanism.BIT7.value())) {
688 localCharset = HttpPostBodyUtil.US_ASCII;
689 } else if (code.equals(HttpPostBodyUtil.TransferEncodingMechanism.BIT8.value())) {
690 localCharset = HttpPostBodyUtil.ISO_8859_1;
691 mechanism = TransferEncodingMechanism.BIT8;
692 } else if (code
693 .equals(HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value())) {
694
695 mechanism = TransferEncodingMechanism.BINARY;
696 } else {
697 throw new ErrorDataDecoderException(
698 "TransferEncoding Unknown: " + code);
699 }
700 }
701 Attribute charsetAttribute = currentFieldAttributes
702 .get(HttpHeaders.Values.CHARSET);
703 if (charsetAttribute != null) {
704 try {
705 localCharset = Charset.forName(charsetAttribute.getValue());
706 } catch (IOException e) {
707 throw new ErrorDataDecoderException(e);
708 }
709 }
710 if (currentFileUpload == null) {
711 Attribute filenameAttribute = currentFieldAttributes
712 .get(HttpPostBodyUtil.FILENAME);
713 Attribute nameAttribute = currentFieldAttributes
714 .get(HttpPostBodyUtil.NAME);
715 Attribute contentTypeAttribute = currentFieldAttributes
716 .get(HttpHeaders.Names.CONTENT_TYPE);
717 if (contentTypeAttribute == null) {
718
719 contentTypeAttribute = new MemoryAttribute(HttpHeaders.Names.CONTENT_TYPE);
720 try {
721 contentTypeAttribute.setValue(HttpPostBodyUtil.DEFAULT_BINARY_CONTENT_TYPE);
722 } catch (IOException e) {
723 throw new ErrorDataDecoderException(
724 "Content-Type is absent but required, and cannot be reverted to default");
725 }
726 }
727 Attribute lengthAttribute = currentFieldAttributes
728 .get(HttpHeaders.Names.CONTENT_LENGTH);
729 long size;
730 try {
731 size = lengthAttribute != null? Long.parseLong(lengthAttribute
732 .getValue()) : 0L;
733 } catch (IOException e) {
734 throw new ErrorDataDecoderException(e);
735 } catch (NumberFormatException e) {
736 size = 0;
737 }
738 try {
739 currentFileUpload = factory.createFileUpload(
740 request,
741 cleanString(nameAttribute.getValue()), cleanString(filenameAttribute.getValue()),
742 contentTypeAttribute.getValue(), mechanism.value(),
743 localCharset, size);
744 } catch (NullPointerException e) {
745 throw new ErrorDataDecoderException(e);
746 } catch (IllegalArgumentException e) {
747 throw new ErrorDataDecoderException(e);
748 } catch (IOException e) {
749 throw new ErrorDataDecoderException(e);
750 }
751 }
752
753 try {
754 readFileUploadByteMultipart(delimiter);
755 } catch (NotEnoughDataDecoderException e) {
756
757
758
759 return null;
760 }
761 if (currentFileUpload.isCompleted()) {
762
763 if (currentStatus == MultiPartStatus.FILEUPLOAD) {
764 currentStatus = MultiPartStatus.HEADERDELIMITER;
765 currentFieldAttributes = null;
766 } else {
767 currentStatus = MultiPartStatus.MIXEDDELIMITER;
768 cleanMixedAttributes();
769 }
770 FileUpload fileUpload = currentFileUpload;
771 currentFileUpload = null;
772 return fileUpload;
773 }
774
775
776
777 return null;
778 }
779
780 public void cleanFiles() {
781 factory.cleanRequestHttpDatas(request);
782 }
783
784 public void removeHttpDataFromClean(InterfaceHttpData data) {
785 factory.removeHttpDataFromClean(request, data);
786 }
787
788
789
790
791 private void cleanMixedAttributes() {
792 currentFieldAttributes.remove(HttpHeaders.Values.CHARSET);
793 currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_LENGTH);
794 currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING);
795 currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_TYPE);
796 currentFieldAttributes.remove(HttpPostBodyUtil.FILENAME);
797 }
798
799
800
801
802
803
804
805 private String readLineStandard() throws NotEnoughDataDecoderException {
806 int readerIndex = undecodedChunk.readerIndex();
807 try {
808 ChannelBuffer line = ChannelBuffers.dynamicBuffer(64);
809
810 while (undecodedChunk.readable()) {
811 byte nextByte = undecodedChunk.readByte();
812 if (nextByte == HttpConstants.CR) {
813
814 nextByte = undecodedChunk.getByte(undecodedChunk.readerIndex());
815 if (nextByte == HttpConstants.LF) {
816
817 undecodedChunk.readByte();
818 return line.toString(charset);
819 } else {
820
821 line.writeByte(HttpConstants.CR);
822 }
823 } else if (nextByte == HttpConstants.LF) {
824 return line.toString(charset);
825 } else {
826 line.writeByte(nextByte);
827 }
828 }
829 } catch (IndexOutOfBoundsException e) {
830 undecodedChunk.readerIndex(readerIndex);
831 throw new NotEnoughDataDecoderException(e);
832 }
833 undecodedChunk.readerIndex(readerIndex);
834 throw new NotEnoughDataDecoderException();
835 }
836
837
838
839
840
841
842 private String readLine() throws NotEnoughDataDecoderException {
843 SeekAheadOptimize sao;
844 try {
845 sao = new SeekAheadOptimize(undecodedChunk);
846 } catch (SeekAheadNoBackArrayException e1) {
847 return readLineStandard();
848 }
849 int readerIndex = undecodedChunk.readerIndex();
850 try {
851 ChannelBuffer line = ChannelBuffers.dynamicBuffer(64);
852 while (sao.pos < sao.limit) {
853 byte nextByte = sao.bytes[sao.pos ++];
854 if (nextByte == HttpConstants.CR) {
855 if (sao.pos < sao.limit) {
856 nextByte = sao.bytes[sao.pos ++];
857 if (nextByte == HttpConstants.LF) {
858 sao.setReadPosition(0);
859 return line.toString(charset);
860 } else {
861
862 sao.pos--;
863 line.writeByte(HttpConstants.CR);
864 }
865 } else {
866 line.writeByte(nextByte);
867 }
868 } else if (nextByte == HttpConstants.LF) {
869 sao.setReadPosition(0);
870 return line.toString(charset);
871 } else {
872 line.writeByte(nextByte);
873 }
874 }
875 } catch (IndexOutOfBoundsException e) {
876 undecodedChunk.readerIndex(readerIndex);
877 throw new NotEnoughDataDecoderException(e);
878 }
879 undecodedChunk.readerIndex(readerIndex);
880 throw new NotEnoughDataDecoderException();
881 }
882
883
884
885
886
887
888
889
890
891
892
893
894 private String readDelimiterStandard(String delimiter) throws NotEnoughDataDecoderException {
895 int readerIndex = undecodedChunk.readerIndex();
896 try {
897 StringBuilder sb = new StringBuilder(64);
898 int delimiterPos = 0;
899 int len = delimiter.length();
900 while (undecodedChunk.readable() && delimiterPos < len) {
901 byte nextByte = undecodedChunk.readByte();
902 if (nextByte == delimiter.charAt(delimiterPos)) {
903 delimiterPos++;
904 sb.append((char) nextByte);
905 } else {
906
907 undecodedChunk.readerIndex(readerIndex);
908 throw new NotEnoughDataDecoderException();
909 }
910 }
911
912 if (undecodedChunk.readable()) {
913 byte nextByte = undecodedChunk.readByte();
914
915 if (nextByte == HttpConstants.CR) {
916 nextByte = undecodedChunk.readByte();
917 if (nextByte == HttpConstants.LF) {
918 return sb.toString();
919 } else {
920
921
922 undecodedChunk.readerIndex(readerIndex);
923 throw new NotEnoughDataDecoderException();
924 }
925 } else if (nextByte == HttpConstants.LF) {
926 return sb.toString();
927 } else if (nextByte == '-') {
928 sb.append('-');
929
930 nextByte = undecodedChunk.readByte();
931 if (nextByte == '-') {
932 sb.append('-');
933
934 if (undecodedChunk.readable()) {
935 nextByte = undecodedChunk.readByte();
936 if (nextByte == HttpConstants.CR) {
937 nextByte = undecodedChunk.readByte();
938 if (nextByte == HttpConstants.LF) {
939 return sb.toString();
940 } else {
941
942
943 undecodedChunk.readerIndex(readerIndex);
944 throw new NotEnoughDataDecoderException();
945 }
946 } else if (nextByte == HttpConstants.LF) {
947 return sb.toString();
948 } else {
949
950
951 undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1);
952 return sb.toString();
953 }
954 }
955
956
957
958 return sb.toString();
959 }
960
961
962 }
963 }
964 } catch (IndexOutOfBoundsException e) {
965 undecodedChunk.readerIndex(readerIndex);
966 throw new NotEnoughDataDecoderException(e);
967 }
968 undecodedChunk.readerIndex(readerIndex);
969 throw new NotEnoughDataDecoderException();
970 }
971
972
973
974
975
976
977
978
979
980
981
982 private String readDelimiter(String delimiter) throws NotEnoughDataDecoderException {
983 SeekAheadOptimize sao;
984 try {
985 sao = new SeekAheadOptimize(undecodedChunk);
986 } catch (SeekAheadNoBackArrayException e1) {
987 return readDelimiterStandard(delimiter);
988 }
989 int readerIndex = undecodedChunk.readerIndex();
990 int delimiterPos = 0;
991 int len = delimiter.length();
992 try {
993 StringBuilder sb = new StringBuilder(64);
994
995 while (sao.pos < sao.limit && delimiterPos < len) {
996 byte nextByte = sao.bytes[sao.pos ++];
997 if (nextByte == delimiter.charAt(delimiterPos)) {
998 delimiterPos++;
999 sb.append((char) nextByte);
1000 } else {
1001
1002 undecodedChunk.readerIndex(readerIndex);
1003 throw new NotEnoughDataDecoderException();
1004 }
1005 }
1006
1007 if (sao.pos < sao.limit) {
1008 byte nextByte = sao.bytes[sao.pos ++];
1009 if (nextByte == HttpConstants.CR) {
1010
1011 if (sao.pos < sao.limit) {
1012 nextByte = sao.bytes[sao.pos ++];
1013 if (nextByte == HttpConstants.LF) {
1014 sao.setReadPosition(0);
1015 return sb.toString();
1016 } else {
1017
1018
1019 undecodedChunk.readerIndex(readerIndex);
1020 throw new NotEnoughDataDecoderException();
1021 }
1022 } else {
1023
1024
1025 undecodedChunk.readerIndex(readerIndex);
1026 throw new NotEnoughDataDecoderException();
1027 }
1028 } else if (nextByte == HttpConstants.LF) {
1029
1030 sao.setReadPosition(0);
1031 return sb.toString();
1032 } else if (nextByte == '-') {
1033 sb.append('-');
1034
1035 if (sao.pos < sao.limit) {
1036 nextByte = sao.bytes[sao.pos ++];
1037 if (nextByte == '-') {
1038 sb.append('-');
1039
1040 if (sao.pos < sao.limit) {
1041 nextByte = sao.bytes[sao.pos++];
1042 if (nextByte == HttpConstants.CR) {
1043 if (sao.pos < sao.limit) {
1044 nextByte = sao.bytes[sao.pos++];
1045 if (nextByte == HttpConstants.LF) {
1046 sao.setReadPosition(0);
1047 return sb.toString();
1048 } else {
1049
1050
1051 undecodedChunk.readerIndex(readerIndex);
1052 throw new NotEnoughDataDecoderException();
1053 }
1054 } else {
1055
1056
1057 undecodedChunk.readerIndex(readerIndex);
1058 throw new NotEnoughDataDecoderException();
1059 }
1060 } else if (nextByte == HttpConstants.LF) {
1061 sao.setReadPosition(0);
1062 return sb.toString();
1063 } else {
1064
1065
1066 sao.setReadPosition(1);
1067 return sb.toString();
1068 }
1069 }
1070
1071
1072
1073 sao.setReadPosition(0);
1074 return sb.toString();
1075 }
1076
1077
1078 }
1079 }
1080 }
1081 } catch (IndexOutOfBoundsException e) {
1082 undecodedChunk.readerIndex(readerIndex);
1083 throw new NotEnoughDataDecoderException(e);
1084 }
1085 undecodedChunk.readerIndex(readerIndex);
1086 throw new NotEnoughDataDecoderException();
1087 }
1088
1089
1090
1091
1092
1093
1094
1095
1096 private void readFileUploadByteMultipartStandard(String delimiter)
1097 throws NotEnoughDataDecoderException, ErrorDataDecoderException {
1098 int readerIndex = undecodedChunk.readerIndex();
1099
1100 boolean newLine = true;
1101 int index = 0;
1102 int lastPosition = undecodedChunk.readerIndex();
1103 boolean found = false;
1104 while (undecodedChunk.readable()) {
1105 byte nextByte = undecodedChunk.readByte();
1106 if (newLine) {
1107
1108 if (nextByte == delimiter.codePointAt(index)) {
1109 index ++;
1110 if (delimiter.length() == index) {
1111 found = true;
1112 break;
1113 }
1114 continue;
1115 } else {
1116 newLine = false;
1117 index = 0;
1118
1119 if (nextByte == HttpConstants.CR) {
1120 if (undecodedChunk.readable()) {
1121 nextByte = undecodedChunk.readByte();
1122 if (nextByte == HttpConstants.LF) {
1123 newLine = true;
1124 index = 0;
1125 lastPosition = undecodedChunk.readerIndex() - 2;
1126 } else {
1127
1128 lastPosition = undecodedChunk.readerIndex() - 1;
1129
1130
1131 undecodedChunk.readerIndex(lastPosition);
1132 }
1133 }
1134 } else if (nextByte == HttpConstants.LF) {
1135 newLine = true;
1136 index = 0;
1137 lastPosition = undecodedChunk.readerIndex() - 1;
1138 } else {
1139
1140 lastPosition = undecodedChunk.readerIndex();
1141 }
1142 }
1143 } else {
1144
1145 if (nextByte == HttpConstants.CR) {
1146 if (undecodedChunk.readable()) {
1147 nextByte = undecodedChunk.readByte();
1148 if (nextByte == HttpConstants.LF) {
1149 newLine = true;
1150 index = 0;
1151 lastPosition = undecodedChunk.readerIndex() - 2;
1152 } else {
1153
1154 lastPosition = undecodedChunk.readerIndex() - 1;
1155
1156
1157 undecodedChunk.readerIndex(lastPosition);
1158 }
1159 }
1160 } else if (nextByte == HttpConstants.LF) {
1161 newLine = true;
1162 index = 0;
1163 lastPosition = undecodedChunk.readerIndex() - 1;
1164 } else {
1165
1166 lastPosition = undecodedChunk.readerIndex();
1167 }
1168 }
1169 }
1170 ChannelBuffer buffer = undecodedChunk.slice(readerIndex, lastPosition -
1171 readerIndex);
1172 if (found) {
1173
1174 try {
1175 currentFileUpload.addContent(buffer, true);
1176
1177 undecodedChunk.readerIndex(lastPosition);
1178 } catch (IOException e) {
1179 throw new ErrorDataDecoderException(e);
1180 }
1181 } else {
1182
1183 try {
1184 currentFileUpload.addContent(buffer, false);
1185
1186 undecodedChunk.readerIndex(lastPosition);
1187 throw new NotEnoughDataDecoderException();
1188 } catch (IOException e) {
1189 throw new ErrorDataDecoderException(e);
1190 }
1191 }
1192 }
1193
1194
1195
1196
1197
1198
1199
1200
1201 private void readFileUploadByteMultipart(String delimiter)
1202 throws NotEnoughDataDecoderException, ErrorDataDecoderException {
1203 SeekAheadOptimize sao;
1204 try {
1205 sao = new SeekAheadOptimize(undecodedChunk);
1206 } catch (SeekAheadNoBackArrayException e1) {
1207 readFileUploadByteMultipartStandard(delimiter);
1208 return;
1209 }
1210 int readerIndex = undecodedChunk.readerIndex();
1211
1212 boolean newLine = true;
1213 int index = 0;
1214 int lastrealpos = sao.pos;
1215 int lastPosition;
1216 boolean found = false;
1217
1218 while (sao.pos < sao.limit) {
1219 byte nextByte = sao.bytes[sao.pos ++];
1220 if (newLine) {
1221
1222 if (nextByte == delimiter.codePointAt(index)) {
1223 index ++;
1224 if (delimiter.length() == index) {
1225 found = true;
1226 break;
1227 }
1228 continue;
1229 } else {
1230 newLine = false;
1231 index = 0;
1232
1233 if (nextByte == HttpConstants.CR) {
1234 if (sao.pos < sao.limit) {
1235 nextByte = sao.bytes[sao.pos ++];
1236 if (nextByte == HttpConstants.LF) {
1237 newLine = true;
1238 index = 0;
1239 lastrealpos = sao.pos - 2;
1240 } else {
1241
1242 sao.pos--;
1243
1244
1245 lastrealpos = sao.pos;
1246 }
1247 }
1248 } else if (nextByte == HttpConstants.LF) {
1249 newLine = true;
1250 index = 0;
1251 lastrealpos = sao.pos - 1;
1252 } else {
1253
1254 lastrealpos = sao.pos;
1255 }
1256 }
1257 } else {
1258
1259 if (nextByte == HttpConstants.CR) {
1260 if (sao.pos < sao.limit) {
1261 nextByte = sao.bytes[sao.pos ++];
1262 if (nextByte == HttpConstants.LF) {
1263 newLine = true;
1264 index = 0;
1265 lastrealpos = sao.pos - 2;
1266 } else {
1267
1268 sao.pos--;
1269
1270
1271 lastrealpos = sao.pos;
1272 }
1273 }
1274 } else if (nextByte == HttpConstants.LF) {
1275 newLine = true;
1276 index = 0;
1277 lastrealpos = sao.pos - 1;
1278 } else {
1279
1280 lastrealpos = sao.pos;
1281 }
1282 }
1283 }
1284 lastPosition = sao.getReadPosition(lastrealpos);
1285 ChannelBuffer buffer = undecodedChunk.slice(readerIndex, lastPosition - readerIndex);
1286 if (found) {
1287
1288 try {
1289 currentFileUpload.addContent(buffer, true);
1290
1291 undecodedChunk.readerIndex(lastPosition);
1292 } catch (IOException e) {
1293 throw new ErrorDataDecoderException(e);
1294 }
1295 } else {
1296
1297 try {
1298 currentFileUpload.addContent(buffer, false);
1299
1300 undecodedChunk.readerIndex(lastPosition);
1301 throw new NotEnoughDataDecoderException();
1302 } catch (IOException e) {
1303 throw new ErrorDataDecoderException(e);
1304 }
1305 }
1306 }
1307
1308
1309
1310
1311
1312
1313 private void loadFieldMultipartStandard(String delimiter)
1314 throws NotEnoughDataDecoderException, ErrorDataDecoderException {
1315 int readerIndex = undecodedChunk.readerIndex();
1316 try {
1317
1318 boolean newLine = true;
1319 int index = 0;
1320 int lastPosition = undecodedChunk.readerIndex();
1321 boolean found = false;
1322 while (undecodedChunk.readable()) {
1323 byte nextByte = undecodedChunk.readByte();
1324 if (newLine) {
1325
1326 if (nextByte == delimiter.codePointAt(index)) {
1327 index ++;
1328 if (delimiter.length() == index) {
1329 found = true;
1330 break;
1331 }
1332 continue;
1333 } else {
1334 newLine = false;
1335 index = 0;
1336
1337 if (nextByte == HttpConstants.CR) {
1338 if (undecodedChunk.readable()) {
1339 nextByte = undecodedChunk.readByte();
1340 if (nextByte == HttpConstants.LF) {
1341 newLine = true;
1342 index = 0;
1343 lastPosition = undecodedChunk.readerIndex() - 2;
1344 } else {
1345
1346 lastPosition = undecodedChunk.readerIndex() - 1;
1347 undecodedChunk.readerIndex(lastPosition);
1348 }
1349 } else {
1350 lastPosition = undecodedChunk.readerIndex() - 1;
1351 }
1352 } else if (nextByte == HttpConstants.LF) {
1353 newLine = true;
1354 index = 0;
1355 lastPosition = undecodedChunk.readerIndex() - 1;
1356 } else {
1357 lastPosition = undecodedChunk.readerIndex();
1358 }
1359 }
1360 } else {
1361
1362 if (nextByte == HttpConstants.CR) {
1363 if (undecodedChunk.readable()) {
1364 nextByte = undecodedChunk.readByte();
1365 if (nextByte == HttpConstants.LF) {
1366 newLine = true;
1367 index = 0;
1368 lastPosition = undecodedChunk.readerIndex() - 2;
1369 } else {
1370
1371 lastPosition = undecodedChunk.readerIndex() - 1;
1372 undecodedChunk.readerIndex(lastPosition);
1373 }
1374 } else {
1375 lastPosition = undecodedChunk.readerIndex() - 1;
1376 }
1377 } else if (nextByte == HttpConstants.LF) {
1378 newLine = true;
1379 index = 0;
1380 lastPosition = undecodedChunk.readerIndex() - 1;
1381 } else {
1382 lastPosition = undecodedChunk.readerIndex();
1383 }
1384 }
1385 }
1386 if (found) {
1387
1388
1389
1390 try {
1391 currentAttribute.addContent(
1392 undecodedChunk.slice(readerIndex, lastPosition - readerIndex),
1393 true);
1394 } catch (IOException e) {
1395 throw new ErrorDataDecoderException(e);
1396 }
1397 undecodedChunk.readerIndex(lastPosition);
1398 } else {
1399 try {
1400 currentAttribute.addContent(
1401 undecodedChunk.slice(readerIndex, lastPosition - readerIndex),
1402 false);
1403 } catch (IOException e) {
1404 throw new ErrorDataDecoderException(e);
1405 }
1406 undecodedChunk.readerIndex(lastPosition);
1407 throw new NotEnoughDataDecoderException();
1408 }
1409 } catch (IndexOutOfBoundsException e) {
1410 undecodedChunk.readerIndex(readerIndex);
1411 throw new NotEnoughDataDecoderException(e);
1412 }
1413 }
1414
1415
1416
1417
1418
1419
1420 private void loadFieldMultipart(String delimiter)
1421 throws NotEnoughDataDecoderException, ErrorDataDecoderException {
1422 SeekAheadOptimize sao;
1423 try {
1424 sao = new SeekAheadOptimize(undecodedChunk);
1425 } catch (SeekAheadNoBackArrayException e1) {
1426 loadFieldMultipartStandard(delimiter);
1427 return;
1428 }
1429 int readerIndex = undecodedChunk.readerIndex();
1430 try {
1431
1432 boolean newLine = true;
1433 int index = 0;
1434 int lastPosition;
1435 int lastrealpos = sao.pos;
1436 boolean found = false;
1437
1438 while (sao.pos < sao.limit) {
1439 byte nextByte = sao.bytes[sao.pos ++];
1440 if (newLine) {
1441
1442 if (nextByte == delimiter.codePointAt(index)) {
1443 index ++;
1444 if (delimiter.length() == index) {
1445 found = true;
1446 break;
1447 }
1448 continue;
1449 } else {
1450 newLine = false;
1451 index = 0;
1452
1453 if (nextByte == HttpConstants.CR) {
1454 if (sao.pos < sao.limit) {
1455 nextByte = sao.bytes[sao.pos ++];
1456 if (nextByte == HttpConstants.LF) {
1457 newLine = true;
1458 index = 0;
1459 lastrealpos = sao.pos - 2;
1460 } else {
1461
1462 sao.pos--;
1463 lastrealpos = sao.pos;
1464 }
1465 }
1466 } else if (nextByte == HttpConstants.LF) {
1467 newLine = true;
1468 index = 0;
1469 lastrealpos = sao.pos - 1;
1470 } else {
1471 lastrealpos = sao.pos;
1472 }
1473 }
1474 } else {
1475
1476 if (nextByte == HttpConstants.CR) {
1477 if (sao.pos < sao.limit) {
1478 nextByte = sao.bytes[sao.pos ++];
1479 if (nextByte == HttpConstants.LF) {
1480 newLine = true;
1481 index = 0;
1482 lastrealpos = sao.pos - 2;
1483 } else {
1484
1485 sao.pos--;
1486 lastrealpos = sao.pos;
1487 }
1488 }
1489 } else if (nextByte == HttpConstants.LF) {
1490 newLine = true;
1491 index = 0;
1492 lastrealpos = sao.pos - 1;
1493 } else {
1494 lastrealpos = sao.pos;
1495 }
1496 }
1497 }
1498 lastPosition = sao.getReadPosition(lastrealpos);
1499 if (found) {
1500
1501
1502
1503 try {
1504 currentAttribute.addContent(
1505 undecodedChunk.slice(readerIndex, lastPosition - readerIndex), true);
1506 } catch (IOException e) {
1507 throw new ErrorDataDecoderException(e);
1508 }
1509 undecodedChunk.readerIndex(lastPosition);
1510 } else {
1511 try {
1512 currentAttribute.addContent(
1513 undecodedChunk.slice(readerIndex, lastPosition - readerIndex), false);
1514 } catch (IOException e) {
1515 throw new ErrorDataDecoderException(e);
1516 }
1517 undecodedChunk.readerIndex(lastPosition);
1518 throw new NotEnoughDataDecoderException();
1519 }
1520 } catch (IndexOutOfBoundsException e) {
1521 undecodedChunk.readerIndex(readerIndex);
1522 throw new NotEnoughDataDecoderException(e);
1523 }
1524 }
1525
1526
1527
1528
1529
1530 private static String cleanString(String field) {
1531 StringBuilder sb = new StringBuilder(field.length());
1532 for (int i = 0; i < field.length(); i ++) {
1533 char nextChar = field.charAt(i);
1534 if (nextChar == HttpConstants.COLON) {
1535 sb.append(HttpConstants.SP);
1536 } else if (nextChar == HttpConstants.COMMA) {
1537 sb.append(HttpConstants.SP);
1538 } else if (nextChar == HttpConstants.EQUALS) {
1539 sb.append(HttpConstants.SP);
1540 } else if (nextChar == HttpConstants.SEMICOLON) {
1541 sb.append(HttpConstants.SP);
1542 } else if (nextChar == HttpConstants.HT) {
1543 sb.append(HttpConstants.SP);
1544 } else if (nextChar == HttpConstants.DOUBLE_QUOTE) {
1545
1546 } else {
1547 sb.append(nextChar);
1548 }
1549 }
1550 return sb.toString().trim();
1551 }
1552
1553
1554
1555
1556
1557 private boolean skipOneLine() {
1558 if (!undecodedChunk.readable()) {
1559 return false;
1560 }
1561 byte nextByte = undecodedChunk.readByte();
1562 if (nextByte == HttpConstants.CR) {
1563 if (!undecodedChunk.readable()) {
1564 undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1);
1565 return false;
1566 }
1567 nextByte = undecodedChunk.readByte();
1568 if (nextByte == HttpConstants.LF) {
1569 return true;
1570 }
1571 undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 2);
1572 return false;
1573 }
1574 if (nextByte == HttpConstants.LF) {
1575 return true;
1576 }
1577 undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1);
1578 return false;
1579 }
1580
1581
1582
1583
1584
1585
1586 private static String[] splitMultipartHeader(String sb) {
1587 ArrayList<String> headers = new ArrayList<String>(1);
1588 int nameStart;
1589 int nameEnd;
1590 int colonEnd;
1591 int valueStart;
1592 int valueEnd;
1593 nameStart = HttpPostBodyUtil.findNonWhitespace(sb, 0);
1594 for (nameEnd = nameStart; nameEnd < sb.length(); nameEnd ++) {
1595 char ch = sb.charAt(nameEnd);
1596 if (ch == ':' || Character.isWhitespace(ch)) {
1597 break;
1598 }
1599 }
1600 for (colonEnd = nameEnd; colonEnd < sb.length(); colonEnd ++) {
1601 if (sb.charAt(colonEnd) == ':') {
1602 colonEnd ++;
1603 break;
1604 }
1605 }
1606 valueStart = HttpPostBodyUtil.findNonWhitespace(sb, colonEnd);
1607 valueEnd = HttpPostBodyUtil.findEndOfString(sb);
1608 headers.add(sb.substring(nameStart, nameEnd));
1609 String svalue = sb.substring(valueStart, valueEnd);
1610 String[] values;
1611 if (svalue.indexOf(';') >= 0) {
1612 values = splitMultipartHeaderValues(svalue);
1613 } else {
1614 values = StringUtil.split(svalue, ',');
1615 }
1616 for (String value: values) {
1617 headers.add(value.trim());
1618 }
1619 String[] array = new String[headers.size()];
1620 for (int i = 0; i < headers.size(); i ++) {
1621 array[i] = headers.get(i);
1622 }
1623 return array;
1624 }
1625
1626
1627
1628
1629
1630
1631 private static String[] splitMultipartHeaderValues(String svalue) {
1632 List<String> values = new ArrayList<String>(1);
1633 boolean inQuote = false;
1634 boolean escapeNext = false;
1635 int start = 0;
1636 for (int i = 0; i < svalue.length(); i++) {
1637 char c = svalue.charAt(i);
1638 if (inQuote) {
1639 if (escapeNext) {
1640 escapeNext = false;
1641 } else {
1642 if (c == '\\') {
1643 escapeNext = true;
1644 } else if (c == '"') {
1645 inQuote = false;
1646 }
1647 }
1648 } else {
1649 if (c == '"') {
1650 inQuote = true;
1651 } else if (c == ';') {
1652 values.add(svalue.substring(start, i));
1653 start = i + 1;
1654 }
1655 }
1656 }
1657 values.add(svalue.substring(start));
1658 return values.toArray(new String[values.size()]);
1659 }
1660 }