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