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