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