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,
787 cleanString(nameAttribute.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 String name = cleanString(values[0]);
963 String value = values[1];
964
965
966 if (HttpPostBodyUtil.FILENAME.equals(name)) {
967
968 value = value.substring(1, value.length() - 1);
969 } else {
970
971 value = cleanString(value);
972 }
973 attribute = factory.createAttribute(request, name, value);
974 } catch (NullPointerException e) {
975 throw new ErrorDataDecoderException(e);
976 } catch (IllegalArgumentException e) {
977 throw new ErrorDataDecoderException(e);
978 }
979 currentFieldAttributes.put(attribute.getName(),
980 attribute);
981 }
982 }
983 } else if (contents[0]
984 .equalsIgnoreCase(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING)) {
985 Attribute attribute;
986 try {
987 attribute = factory.createAttribute(request,
988 HttpHeaders.Names.CONTENT_TRANSFER_ENCODING,
989 cleanString(contents[1]));
990 } catch (NullPointerException e) {
991 throw new ErrorDataDecoderException(e);
992 } catch (IllegalArgumentException e) {
993 throw new ErrorDataDecoderException(e);
994 }
995 currentFieldAttributes.put(
996 HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, attribute);
997 } else if (contents[0]
998 .equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH)) {
999 Attribute attribute;
1000 try {
1001 attribute = factory.createAttribute(request,
1002 HttpHeaders.Names.CONTENT_LENGTH,
1003 cleanString(contents[1]));
1004 } catch (NullPointerException e) {
1005 throw new ErrorDataDecoderException(e);
1006 } catch (IllegalArgumentException e) {
1007 throw new ErrorDataDecoderException(e);
1008 }
1009 currentFieldAttributes.put(HttpHeaders.Names.CONTENT_LENGTH,
1010 attribute);
1011 } else if (contents[0].equalsIgnoreCase(HttpHeaders.Names.CONTENT_TYPE)) {
1012
1013 if (contents[1].equalsIgnoreCase(HttpPostBodyUtil.MULTIPART_MIXED)) {
1014 if (currentStatus == MultiPartStatus.DISPOSITION) {
1015 String[] values = StringUtil.split(contents[2], '=');
1016 multipartMixedBoundary = "--" + values[1];
1017 currentStatus = MultiPartStatus.MIXEDDELIMITER;
1018 return decodeMultipart(MultiPartStatus.MIXEDDELIMITER);
1019 } else {
1020 throw new ErrorDataDecoderException(
1021 "Mixed Multipart found in a previous Mixed Multipart");
1022 }
1023 } else {
1024 for (int i = 1; i < contents.length; i ++) {
1025 if (contents[i].toLowerCase().startsWith(
1026 HttpHeaders.Values.CHARSET)) {
1027 String[] values = StringUtil.split(contents[i], '=');
1028 Attribute attribute;
1029 try {
1030 attribute = factory.createAttribute(request,
1031 HttpHeaders.Values.CHARSET,
1032 cleanString(values[1]));
1033 } catch (NullPointerException e) {
1034 throw new ErrorDataDecoderException(e);
1035 } catch (IllegalArgumentException e) {
1036 throw new ErrorDataDecoderException(e);
1037 }
1038 currentFieldAttributes.put(HttpHeaders.Values.CHARSET,
1039 attribute);
1040 } else {
1041 Attribute attribute;
1042 try {
1043 attribute = factory.createAttribute(request,
1044 cleanString(contents[0]), contents[i]);
1045 } catch (NullPointerException e) {
1046 throw new ErrorDataDecoderException(e);
1047 } catch (IllegalArgumentException e) {
1048 throw new ErrorDataDecoderException(e);
1049 }
1050 currentFieldAttributes.put(attribute.getName(),
1051 attribute);
1052 }
1053 }
1054 }
1055 } else {
1056 throw new ErrorDataDecoderException("Unknown Params: " +
1057 newline);
1058 }
1059 }
1060
1061 Attribute filenameAttribute = currentFieldAttributes
1062 .get(HttpPostBodyUtil.FILENAME);
1063 if (currentStatus == MultiPartStatus.DISPOSITION) {
1064 if (filenameAttribute != null) {
1065
1066 currentStatus = MultiPartStatus.FILEUPLOAD;
1067
1068 return decodeMultipart(MultiPartStatus.FILEUPLOAD);
1069 } else {
1070
1071 currentStatus = MultiPartStatus.FIELD;
1072
1073 return decodeMultipart(MultiPartStatus.FIELD);
1074 }
1075 } else {
1076 if (filenameAttribute != null) {
1077
1078 currentStatus = MultiPartStatus.MIXEDFILEUPLOAD;
1079
1080 return decodeMultipart(MultiPartStatus.MIXEDFILEUPLOAD);
1081 } else {
1082
1083 throw new ErrorDataDecoderException("Filename not found");
1084 }
1085 }
1086 }
1087
1088
1089
1090
1091
1092
1093
1094 private InterfaceHttpData getFileUpload(String delimiter)
1095 throws ErrorDataDecoderException {
1096
1097
1098 Attribute encoding = currentFieldAttributes
1099 .get(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING);
1100 Charset localCharset = charset;
1101
1102 TransferEncodingMechanism mechanism = TransferEncodingMechanism.BIT7;
1103 if (encoding != null) {
1104 String code;
1105 try {
1106 code = encoding.getValue().toLowerCase();
1107 } catch (IOException e) {
1108 throw new ErrorDataDecoderException(e);
1109 }
1110 if (code.equals(HttpPostBodyUtil.TransferEncodingMechanism.BIT7.value())) {
1111 localCharset = HttpPostBodyUtil.US_ASCII;
1112 } else if (code.equals(HttpPostBodyUtil.TransferEncodingMechanism.BIT8.value())) {
1113 localCharset = HttpPostBodyUtil.ISO_8859_1;
1114 mechanism = TransferEncodingMechanism.BIT8;
1115 } else if (code
1116 .equals(HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value())) {
1117
1118 mechanism = TransferEncodingMechanism.BINARY;
1119 } else {
1120 throw new ErrorDataDecoderException(
1121 "TransferEncoding Unknown: " + code);
1122 }
1123 }
1124 Attribute charsetAttribute = currentFieldAttributes
1125 .get(HttpHeaders.Values.CHARSET);
1126 if (charsetAttribute != null) {
1127 try {
1128 localCharset = Charset.forName(charsetAttribute.getValue());
1129 } catch (IOException e) {
1130 throw new ErrorDataDecoderException(e);
1131 }
1132 }
1133 if (currentFileUpload == null) {
1134 Attribute filenameAttribute = currentFieldAttributes
1135 .get(HttpPostBodyUtil.FILENAME);
1136 Attribute nameAttribute = currentFieldAttributes
1137 .get(HttpPostBodyUtil.NAME);
1138 Attribute contentTypeAttribute = currentFieldAttributes
1139 .get(HttpHeaders.Names.CONTENT_TYPE);
1140 if (contentTypeAttribute == null) {
1141 throw new ErrorDataDecoderException(
1142 "Content-Type is absent but required");
1143 }
1144 Attribute lengthAttribute = currentFieldAttributes
1145 .get(HttpHeaders.Names.CONTENT_LENGTH);
1146 long size;
1147 try {
1148 size = lengthAttribute != null? Long.parseLong(lengthAttribute
1149 .getValue()) : 0L;
1150 } catch (IOException e) {
1151 throw new ErrorDataDecoderException(e);
1152 } catch (NumberFormatException e) {
1153 size = 0;
1154 }
1155 try {
1156 currentFileUpload = factory.createFileUpload(
1157 request,
1158 cleanString(nameAttribute.getValue()), cleanString(filenameAttribute.getValue()),
1159 contentTypeAttribute.getValue(), mechanism.value(),
1160 localCharset, size);
1161 } catch (NullPointerException e) {
1162 throw new ErrorDataDecoderException(e);
1163 } catch (IllegalArgumentException e) {
1164 throw new ErrorDataDecoderException(e);
1165 } catch (IOException e) {
1166 throw new ErrorDataDecoderException(e);
1167 }
1168 }
1169
1170 try {
1171 readFileUploadByteMultipart(delimiter);
1172 } catch (NotEnoughDataDecoderException e) {
1173
1174
1175
1176 return null;
1177 }
1178 if (currentFileUpload.isCompleted()) {
1179
1180 if (currentStatus == MultiPartStatus.FILEUPLOAD) {
1181 currentStatus = MultiPartStatus.HEADERDELIMITER;
1182 currentFieldAttributes = null;
1183 } else {
1184 currentStatus = MultiPartStatus.MIXEDDELIMITER;
1185 cleanMixedAttributes();
1186 }
1187 FileUpload fileUpload = currentFileUpload;
1188 currentFileUpload = null;
1189 return fileUpload;
1190 }
1191
1192
1193
1194 return null;
1195 }
1196
1197
1198
1199
1200 public void cleanFiles() {
1201 factory.cleanRequestHttpDatas(request);
1202 }
1203
1204
1205
1206
1207 public void removeHttpDataFromClean(InterfaceHttpData data) {
1208 factory.removeHttpDataFromClean(request, data);
1209 }
1210
1211
1212
1213
1214 private void cleanMixedAttributes() {
1215 currentFieldAttributes.remove(HttpHeaders.Values.CHARSET);
1216 currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_LENGTH);
1217 currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING);
1218 currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_TYPE);
1219 currentFieldAttributes.remove(HttpPostBodyUtil.FILENAME);
1220 }
1221
1222
1223
1224
1225
1226
1227
1228 private String readLineStandard() throws NotEnoughDataDecoderException {
1229 int readerIndex = undecodedChunk.readerIndex();
1230 try {
1231 ChannelBuffer line = ChannelBuffers.dynamicBuffer(64);
1232
1233 while (undecodedChunk.readable()) {
1234 byte nextByte = undecodedChunk.readByte();
1235 if (nextByte == HttpConstants.CR) {
1236 nextByte = undecodedChunk.readByte();
1237 if (nextByte == HttpConstants.LF) {
1238 return line.toString(charset);
1239 }
1240 } else if (nextByte == HttpConstants.LF) {
1241 return line.toString(charset);
1242 } else {
1243 line.writeByte(nextByte);
1244 }
1245 }
1246 } catch (IndexOutOfBoundsException e) {
1247 undecodedChunk.readerIndex(readerIndex);
1248 throw new NotEnoughDataDecoderException(e);
1249 }
1250 undecodedChunk.readerIndex(readerIndex);
1251 throw new NotEnoughDataDecoderException();
1252 }
1253
1254
1255
1256
1257
1258
1259 private String readLine() throws NotEnoughDataDecoderException {
1260 SeekAheadOptimize sao;
1261 try {
1262 sao = new SeekAheadOptimize(undecodedChunk);
1263 } catch (SeekAheadNoBackArrayException e1) {
1264 return readLineStandard();
1265 }
1266 int readerIndex = undecodedChunk.readerIndex();
1267 try {
1268 ChannelBuffer line = ChannelBuffers.dynamicBuffer(64);
1269 while (sao.pos < sao.limit) {
1270 byte nextByte = sao.bytes[sao.pos ++];
1271 if (nextByte == HttpConstants.CR) {
1272 if (sao.pos < sao.limit) {
1273 nextByte = sao.bytes[sao.pos ++];
1274 if (nextByte == HttpConstants.LF) {
1275 sao.setReadPosition(0);
1276 return line.toString(charset);
1277 }
1278 } else {
1279 line.writeByte(nextByte);
1280 }
1281 } else if (nextByte == HttpConstants.LF) {
1282 sao.setReadPosition(0);
1283 return line.toString(charset);
1284 } else {
1285 line.writeByte(nextByte);
1286 }
1287 }
1288 } catch (IndexOutOfBoundsException e) {
1289 undecodedChunk.readerIndex(readerIndex);
1290 throw new NotEnoughDataDecoderException(e);
1291 }
1292 undecodedChunk.readerIndex(readerIndex);
1293 throw new NotEnoughDataDecoderException();
1294 }
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307 private String readDelimiterStandard(String delimiter) throws NotEnoughDataDecoderException {
1308 int readerIndex = undecodedChunk.readerIndex();
1309 try {
1310 StringBuilder sb = new StringBuilder(64);
1311 int delimiterPos = 0;
1312 int len = delimiter.length();
1313 while (undecodedChunk.readable() && delimiterPos < len) {
1314 byte nextByte = undecodedChunk.readByte();
1315 if (nextByte == delimiter.charAt(delimiterPos)) {
1316 delimiterPos++;
1317 sb.append((char) nextByte);
1318 } else {
1319
1320 undecodedChunk.readerIndex(readerIndex);
1321 throw new NotEnoughDataDecoderException();
1322 }
1323 }
1324
1325 if (undecodedChunk.readable()) {
1326 byte nextByte = undecodedChunk.readByte();
1327
1328 if (nextByte == HttpConstants.CR) {
1329 nextByte = undecodedChunk.readByte();
1330 if (nextByte == HttpConstants.LF) {
1331 return sb.toString();
1332 } else {
1333
1334
1335 undecodedChunk.readerIndex(readerIndex);
1336 throw new NotEnoughDataDecoderException();
1337 }
1338 } else if (nextByte == HttpConstants.LF) {
1339 return sb.toString();
1340 } else if (nextByte == '-') {
1341 sb.append('-');
1342
1343 nextByte = undecodedChunk.readByte();
1344 if (nextByte == '-') {
1345 sb.append('-');
1346
1347 if (undecodedChunk.readable()) {
1348 nextByte = undecodedChunk.readByte();
1349 if (nextByte == HttpConstants.CR) {
1350 nextByte = undecodedChunk.readByte();
1351 if (nextByte == HttpConstants.LF) {
1352 return sb.toString();
1353 } else {
1354
1355
1356 undecodedChunk.readerIndex(readerIndex);
1357 throw new NotEnoughDataDecoderException();
1358 }
1359 } else if (nextByte == HttpConstants.LF) {
1360 return sb.toString();
1361 } else {
1362
1363
1364 undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1);
1365 return sb.toString();
1366 }
1367 }
1368
1369
1370
1371 return sb.toString();
1372 }
1373
1374
1375 }
1376 }
1377 } catch (IndexOutOfBoundsException e) {
1378 undecodedChunk.readerIndex(readerIndex);
1379 throw new NotEnoughDataDecoderException(e);
1380 }
1381 undecodedChunk.readerIndex(readerIndex);
1382 throw new NotEnoughDataDecoderException();
1383 }
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395 private String readDelimiter(String delimiter) throws NotEnoughDataDecoderException {
1396 SeekAheadOptimize sao;
1397 try {
1398 sao = new SeekAheadOptimize(undecodedChunk);
1399 } catch (SeekAheadNoBackArrayException e1) {
1400 return readDelimiterStandard(delimiter);
1401 }
1402 int readerIndex = undecodedChunk.readerIndex();
1403 int delimiterPos = 0;
1404 int len = delimiter.length();
1405 try {
1406 StringBuilder sb = new StringBuilder(64);
1407
1408 while (sao.pos < sao.limit && delimiterPos < len) {
1409 byte nextByte = sao.bytes[sao.pos ++];
1410 if (nextByte == delimiter.charAt(delimiterPos)) {
1411 delimiterPos++;
1412 sb.append((char) nextByte);
1413 } else {
1414
1415 undecodedChunk.readerIndex(readerIndex);
1416 throw new NotEnoughDataDecoderException();
1417 }
1418 }
1419
1420 if (sao.pos < sao.limit) {
1421 byte nextByte = sao.bytes[sao.pos ++];
1422 if (nextByte == HttpConstants.CR) {
1423
1424 if (sao.pos < sao.limit) {
1425 nextByte = sao.bytes[sao.pos ++];
1426 if (nextByte == HttpConstants.LF) {
1427 sao.setReadPosition(0);
1428 return sb.toString();
1429 }
1430 } else {
1431
1432
1433 undecodedChunk.readerIndex(readerIndex);
1434 throw new NotEnoughDataDecoderException();
1435 }
1436 } else if (nextByte == HttpConstants.LF) {
1437
1438 sao.setReadPosition(0);
1439 return sb.toString();
1440 } else if (nextByte == '-') {
1441 sb.append('-');
1442
1443 if (sao.pos < sao.limit) {
1444 nextByte = sao.bytes[sao.pos ++];
1445 if (nextByte == '-') {
1446 sb.append('-');
1447
1448 if (sao.pos < sao.limit) {
1449 nextByte = sao.bytes[sao.pos++];
1450 if (nextByte == HttpConstants.CR) {
1451 if (sao.pos < sao.limit) {
1452 nextByte = sao.bytes[sao.pos++];
1453 if (nextByte == HttpConstants.LF) {
1454 sao.setReadPosition(0);
1455 return sb.toString();
1456 }
1457 } else {
1458
1459
1460 undecodedChunk.readerIndex(readerIndex);
1461 throw new NotEnoughDataDecoderException();
1462 }
1463 } else if (nextByte == HttpConstants.LF) {
1464 sao.setReadPosition(0);
1465 return sb.toString();
1466 } else {
1467
1468
1469 sao.setReadPosition(1);
1470 return sb.toString();
1471 }
1472 }
1473
1474
1475
1476 sao.setReadPosition(0);
1477 return sb.toString();
1478 }
1479
1480
1481 }
1482 }
1483 }
1484 } catch (IndexOutOfBoundsException e) {
1485 undecodedChunk.readerIndex(readerIndex);
1486 throw new NotEnoughDataDecoderException(e);
1487 }
1488 undecodedChunk.readerIndex(readerIndex);
1489 throw new NotEnoughDataDecoderException();
1490 }
1491
1492
1493
1494
1495
1496
1497
1498
1499 private void readFileUploadByteMultipartStandard(String delimiter)
1500 throws NotEnoughDataDecoderException, ErrorDataDecoderException {
1501 int readerIndex = undecodedChunk.readerIndex();
1502
1503 boolean newLine = true;
1504 int index = 0;
1505 int lastPosition = undecodedChunk.readerIndex();
1506 boolean found = false;
1507 while (undecodedChunk.readable()) {
1508 byte nextByte = undecodedChunk.readByte();
1509 if (newLine) {
1510
1511 if (nextByte == delimiter.codePointAt(index)) {
1512 index ++;
1513 if (delimiter.length() == index) {
1514 found = true;
1515 break;
1516 }
1517 continue;
1518 } else {
1519 newLine = false;
1520 index = 0;
1521
1522 if (nextByte == HttpConstants.CR) {
1523 if (undecodedChunk.readable()) {
1524 nextByte = undecodedChunk.readByte();
1525 if (nextByte == HttpConstants.LF) {
1526 newLine = true;
1527 index = 0;
1528 lastPosition = undecodedChunk.readerIndex() - 2;
1529 } else {
1530
1531 lastPosition = undecodedChunk.readerIndex() - 1;
1532
1533
1534 undecodedChunk.readerIndex(lastPosition);
1535 }
1536 }
1537 } else if (nextByte == HttpConstants.LF) {
1538 newLine = true;
1539 index = 0;
1540 lastPosition = undecodedChunk.readerIndex() - 1;
1541 } else {
1542
1543 lastPosition = undecodedChunk.readerIndex();
1544 }
1545 }
1546 } else {
1547
1548 if (nextByte == HttpConstants.CR) {
1549 if (undecodedChunk.readable()) {
1550 nextByte = undecodedChunk.readByte();
1551 if (nextByte == HttpConstants.LF) {
1552 newLine = true;
1553 index = 0;
1554 lastPosition = undecodedChunk.readerIndex() - 2;
1555 } else {
1556
1557 lastPosition = undecodedChunk.readerIndex() - 1;
1558
1559
1560 undecodedChunk.readerIndex(lastPosition);
1561 }
1562 }
1563 } else if (nextByte == HttpConstants.LF) {
1564 newLine = true;
1565 index = 0;
1566 lastPosition = undecodedChunk.readerIndex() - 1;
1567 } else {
1568
1569 lastPosition = undecodedChunk.readerIndex();
1570 }
1571 }
1572 }
1573 ChannelBuffer buffer = undecodedChunk.slice(readerIndex, lastPosition -
1574 readerIndex);
1575 if (found) {
1576
1577 try {
1578 currentFileUpload.addContent(buffer, true);
1579
1580 undecodedChunk.readerIndex(lastPosition);
1581 } catch (IOException e) {
1582 throw new ErrorDataDecoderException(e);
1583 }
1584 } else {
1585
1586 try {
1587 currentFileUpload.addContent(buffer, false);
1588
1589 undecodedChunk.readerIndex(lastPosition);
1590 throw new NotEnoughDataDecoderException();
1591 } catch (IOException e) {
1592 throw new ErrorDataDecoderException(e);
1593 }
1594 }
1595 }
1596
1597
1598
1599
1600
1601
1602
1603
1604 private void readFileUploadByteMultipart(String delimiter)
1605 throws NotEnoughDataDecoderException, ErrorDataDecoderException {
1606 SeekAheadOptimize sao;
1607 try {
1608 sao = new SeekAheadOptimize(undecodedChunk);
1609 } catch (SeekAheadNoBackArrayException e1) {
1610 readFileUploadByteMultipartStandard(delimiter);
1611 return;
1612 }
1613 int readerIndex = undecodedChunk.readerIndex();
1614
1615 boolean newLine = true;
1616 int index = 0;
1617 int lastrealpos = sao.pos;
1618 int lastPosition;
1619 boolean found = false;
1620
1621 while (sao.pos < sao.limit) {
1622 byte nextByte = sao.bytes[sao.pos ++];
1623 if (newLine) {
1624
1625 if (nextByte == delimiter.codePointAt(index)) {
1626 index ++;
1627 if (delimiter.length() == index) {
1628 found = true;
1629 break;
1630 }
1631 continue;
1632 } else {
1633 newLine = false;
1634 index = 0;
1635
1636 if (nextByte == HttpConstants.CR) {
1637 if (sao.pos < sao.limit) {
1638 nextByte = sao.bytes[sao.pos ++];
1639 if (nextByte == HttpConstants.LF) {
1640 newLine = true;
1641 index = 0;
1642 lastrealpos = sao.pos - 2;
1643 } else {
1644
1645 sao.pos--;
1646
1647
1648 lastrealpos = sao.pos;
1649 }
1650 }
1651 } else if (nextByte == HttpConstants.LF) {
1652 newLine = true;
1653 index = 0;
1654 lastrealpos = sao.pos - 1;
1655 } else {
1656
1657 lastrealpos = sao.pos;
1658 }
1659 }
1660 } else {
1661
1662 if (nextByte == HttpConstants.CR) {
1663 if (sao.pos < sao.limit) {
1664 nextByte = sao.bytes[sao.pos ++];
1665 if (nextByte == HttpConstants.LF) {
1666 newLine = true;
1667 index = 0;
1668 lastrealpos = sao.pos - 2;
1669 } else {
1670
1671 sao.pos--;
1672
1673
1674 lastrealpos = sao.pos;
1675 }
1676 }
1677 } else if (nextByte == HttpConstants.LF) {
1678 newLine = true;
1679 index = 0;
1680 lastrealpos = sao.pos - 1;
1681 } else {
1682
1683 lastrealpos = sao.pos;
1684 }
1685 }
1686 }
1687 lastPosition = sao.getReadPosition(lastrealpos);
1688 ChannelBuffer buffer = undecodedChunk.slice(readerIndex, lastPosition - readerIndex);
1689 if (found) {
1690
1691 try {
1692 currentFileUpload.addContent(buffer, true);
1693
1694 undecodedChunk.readerIndex(lastPosition);
1695 } catch (IOException e) {
1696 throw new ErrorDataDecoderException(e);
1697 }
1698 } else {
1699
1700 try {
1701 currentFileUpload.addContent(buffer, false);
1702
1703 undecodedChunk.readerIndex(lastPosition);
1704 throw new NotEnoughDataDecoderException();
1705 } catch (IOException e) {
1706 throw new ErrorDataDecoderException(e);
1707 }
1708 }
1709 }
1710
1711
1712
1713
1714
1715
1716 private void loadFieldMultipartStandard(String delimiter)
1717 throws NotEnoughDataDecoderException, ErrorDataDecoderException {
1718 int readerIndex = undecodedChunk.readerIndex();
1719 try {
1720
1721 boolean newLine = true;
1722 int index = 0;
1723 int lastPosition = undecodedChunk.readerIndex();
1724 boolean found = false;
1725 while (undecodedChunk.readable()) {
1726 byte nextByte = undecodedChunk.readByte();
1727 if (newLine) {
1728
1729 if (nextByte == delimiter.codePointAt(index)) {
1730 index ++;
1731 if (delimiter.length() == index) {
1732 found = true;
1733 break;
1734 }
1735 continue;
1736 } else {
1737 newLine = false;
1738 index = 0;
1739
1740 if (nextByte == HttpConstants.CR) {
1741 if (undecodedChunk.readable()) {
1742 nextByte = undecodedChunk.readByte();
1743 if (nextByte == HttpConstants.LF) {
1744 newLine = true;
1745 index = 0;
1746 lastPosition = undecodedChunk.readerIndex() - 2;
1747 }
1748 }
1749 } else if (nextByte == HttpConstants.LF) {
1750 newLine = true;
1751 index = 0;
1752 lastPosition = undecodedChunk.readerIndex() - 1;
1753 } else {
1754 lastPosition = undecodedChunk.readerIndex();
1755 }
1756 }
1757 } else {
1758
1759 if (nextByte == HttpConstants.CR) {
1760 if (undecodedChunk.readable()) {
1761 nextByte = undecodedChunk.readByte();
1762 if (nextByte == HttpConstants.LF) {
1763 newLine = true;
1764 index = 0;
1765 lastPosition = undecodedChunk.readerIndex() - 2;
1766 }
1767 }
1768 } else if (nextByte == HttpConstants.LF) {
1769 newLine = true;
1770 index = 0;
1771 lastPosition = undecodedChunk.readerIndex() - 1;
1772 } else {
1773 lastPosition = undecodedChunk.readerIndex();
1774 }
1775 }
1776 }
1777 if (found) {
1778
1779
1780
1781 try {
1782 currentAttribute.addContent(
1783 undecodedChunk.slice(readerIndex, lastPosition - readerIndex),
1784 true);
1785 } catch (IOException e) {
1786 throw new ErrorDataDecoderException(e);
1787 }
1788 undecodedChunk.readerIndex(lastPosition);
1789 } else {
1790 try {
1791 currentAttribute.addContent(
1792 undecodedChunk.slice(readerIndex, lastPosition - readerIndex),
1793 false);
1794 } catch (IOException e) {
1795 throw new ErrorDataDecoderException(e);
1796 }
1797 undecodedChunk.readerIndex(lastPosition);
1798 throw new NotEnoughDataDecoderException();
1799 }
1800 } catch (IndexOutOfBoundsException e) {
1801 undecodedChunk.readerIndex(readerIndex);
1802 throw new NotEnoughDataDecoderException(e);
1803 }
1804 }
1805
1806
1807
1808
1809
1810
1811 private void loadFieldMultipart(String delimiter)
1812 throws NotEnoughDataDecoderException, ErrorDataDecoderException {
1813 SeekAheadOptimize sao;
1814 try {
1815 sao = new SeekAheadOptimize(undecodedChunk);
1816 } catch (SeekAheadNoBackArrayException e1) {
1817 loadFieldMultipartStandard(delimiter);
1818 return;
1819 }
1820 int readerIndex = undecodedChunk.readerIndex();
1821 try {
1822
1823 boolean newLine = true;
1824 int index = 0;
1825 int lastPosition;
1826 int lastrealpos = sao.pos;
1827 boolean found = false;
1828
1829 while (sao.pos < sao.limit) {
1830 byte nextByte = sao.bytes[sao.pos ++];
1831 if (newLine) {
1832
1833 if (nextByte == delimiter.codePointAt(index)) {
1834 index ++;
1835 if (delimiter.length() == index) {
1836 found = true;
1837 break;
1838 }
1839 continue;
1840 } else {
1841 newLine = false;
1842 index = 0;
1843
1844 if (nextByte == HttpConstants.CR) {
1845 if (sao.pos < sao.limit) {
1846 nextByte = sao.bytes[sao.pos ++];
1847 if (nextByte == HttpConstants.LF) {
1848 newLine = true;
1849 index = 0;
1850 lastrealpos = sao.pos - 2;
1851 }
1852 }
1853 } else if (nextByte == HttpConstants.LF) {
1854 newLine = true;
1855 index = 0;
1856 lastrealpos = sao.pos - 1;
1857 } else {
1858 lastrealpos = sao.pos;
1859 }
1860 }
1861 } else {
1862
1863 if (nextByte == HttpConstants.CR) {
1864 if (sao.pos < sao.limit) {
1865 nextByte = sao.bytes[sao.pos ++];
1866 if (nextByte == HttpConstants.LF) {
1867 newLine = true;
1868 index = 0;
1869 lastrealpos = sao.pos - 2;
1870 }
1871 }
1872 } else if (nextByte == HttpConstants.LF) {
1873 newLine = true;
1874 index = 0;
1875 lastrealpos = sao.pos - 1;
1876 } else {
1877 lastrealpos = sao.pos;
1878 }
1879 }
1880 }
1881 lastPosition = sao.getReadPosition(lastrealpos);
1882 if (found) {
1883
1884
1885
1886 try {
1887 currentAttribute.addContent(
1888 undecodedChunk.slice(readerIndex, lastPosition - readerIndex), true);
1889 } catch (IOException e) {
1890 throw new ErrorDataDecoderException(e);
1891 }
1892 undecodedChunk.readerIndex(lastPosition);
1893 } else {
1894 try {
1895 currentAttribute.addContent(
1896 undecodedChunk.slice(readerIndex, lastPosition - readerIndex), false);
1897 } catch (IOException e) {
1898 throw new ErrorDataDecoderException(e);
1899 }
1900 undecodedChunk.readerIndex(lastPosition);
1901 throw new NotEnoughDataDecoderException();
1902 }
1903 } catch (IndexOutOfBoundsException e) {
1904 undecodedChunk.readerIndex(readerIndex);
1905 throw new NotEnoughDataDecoderException(e);
1906 }
1907 }
1908
1909
1910
1911
1912
1913 private static String cleanString(String field) {
1914 StringBuilder sb = new StringBuilder(field.length());
1915 for (int i = 0; i < field.length(); i ++) {
1916 char nextChar = field.charAt(i);
1917 if (nextChar == HttpConstants.COLON) {
1918 sb.append(HttpConstants.SP);
1919 } else if (nextChar == HttpConstants.COMMA) {
1920 sb.append(HttpConstants.SP);
1921 } else if (nextChar == HttpConstants.EQUALS) {
1922 sb.append(HttpConstants.SP);
1923 } else if (nextChar == HttpConstants.SEMICOLON) {
1924 sb.append(HttpConstants.SP);
1925 } else if (nextChar == HttpConstants.HT) {
1926 sb.append(HttpConstants.SP);
1927 } else if (nextChar == HttpConstants.DOUBLE_QUOTE) {
1928
1929 } else {
1930 sb.append(nextChar);
1931 }
1932 }
1933 return sb.toString().trim();
1934 }
1935
1936
1937
1938
1939
1940 private boolean skipOneLine() {
1941 if (!undecodedChunk.readable()) {
1942 return false;
1943 }
1944 byte nextByte = undecodedChunk.readByte();
1945 if (nextByte == HttpConstants.CR) {
1946 if (!undecodedChunk.readable()) {
1947 undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1);
1948 return false;
1949 }
1950 nextByte = undecodedChunk.readByte();
1951 if (nextByte == HttpConstants.LF) {
1952 return true;
1953 }
1954 undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 2);
1955 return false;
1956 }
1957 if (nextByte == HttpConstants.LF) {
1958 return true;
1959 }
1960 undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1);
1961 return false;
1962 }
1963
1964
1965
1966
1967
1968 private static String[] splitHeaderContentType(String sb) {
1969 int size = sb.length();
1970 int aStart;
1971 int aEnd;
1972 int bStart;
1973 int bEnd;
1974 aStart = HttpPostBodyUtil.findNonWhitespace(sb, 0);
1975 aEnd = HttpPostBodyUtil.findWhitespace(sb, aStart);
1976 if (aEnd >= size) {
1977 return new String[] { sb, "" };
1978 }
1979 if (sb.charAt(aEnd) == ';') {
1980 aEnd --;
1981 }
1982 bStart = HttpPostBodyUtil.findNonWhitespace(sb, aEnd);
1983 bEnd = HttpPostBodyUtil.findEndOfString(sb);
1984 return new String[] { sb.substring(aStart, aEnd),
1985 sb.substring(bStart, bEnd) };
1986 }
1987
1988
1989
1990
1991
1992
1993 private static String[] splitMultipartHeader(String sb) {
1994 ArrayList<String> headers = new ArrayList<String>(1);
1995 int nameStart;
1996 int nameEnd;
1997 int colonEnd;
1998 int valueStart;
1999 int valueEnd;
2000 nameStart = HttpPostBodyUtil.findNonWhitespace(sb, 0);
2001 for (nameEnd = nameStart; nameEnd < sb.length(); nameEnd ++) {
2002 char ch = sb.charAt(nameEnd);
2003 if (ch == ':' || Character.isWhitespace(ch)) {
2004 break;
2005 }
2006 }
2007 for (colonEnd = nameEnd; colonEnd < sb.length(); colonEnd ++) {
2008 if (sb.charAt(colonEnd) == ':') {
2009 colonEnd ++;
2010 break;
2011 }
2012 }
2013 valueStart = HttpPostBodyUtil.findNonWhitespace(sb, colonEnd);
2014 valueEnd = HttpPostBodyUtil.findEndOfString(sb);
2015 headers.add(sb.substring(nameStart, nameEnd));
2016 String svalue = sb.substring(valueStart, valueEnd);
2017 String[] values;
2018 if (svalue.indexOf(';') >= 0) {
2019 values = StringUtil.split(svalue, ';');
2020 } else {
2021 values = StringUtil.split(svalue, ',');
2022 }
2023 for (String value: values) {
2024 headers.add(value.trim());
2025 }
2026 String[] array = new String[headers.size()];
2027 for (int i = 0; i < headers.size(); i ++) {
2028 array[i] = headers.get(i);
2029 }
2030 return array;
2031 }
2032
2033
2034
2035
2036
2037 public static class NotEnoughDataDecoderException extends Exception {
2038 private static final long serialVersionUID = -7846841864603865638L;
2039
2040 public NotEnoughDataDecoderException() {
2041 }
2042
2043 public NotEnoughDataDecoderException(String msg) {
2044 super(msg);
2045 }
2046
2047 public NotEnoughDataDecoderException(Throwable cause) {
2048 super(cause);
2049 }
2050
2051 public NotEnoughDataDecoderException(String msg, Throwable cause) {
2052 super(msg, cause);
2053 }
2054 }
2055
2056
2057
2058
2059 public static class EndOfDataDecoderException extends Exception {
2060 private static final long serialVersionUID = 1336267941020800769L;
2061 }
2062
2063
2064
2065
2066 public static class ErrorDataDecoderException extends Exception {
2067 private static final long serialVersionUID = 5020247425493164465L;
2068
2069 public ErrorDataDecoderException() {
2070 }
2071
2072 public ErrorDataDecoderException(String msg) {
2073 super(msg);
2074 }
2075
2076 public ErrorDataDecoderException(Throwable cause) {
2077 super(cause);
2078 }
2079
2080 public ErrorDataDecoderException(String msg, Throwable cause) {
2081 super(msg, cause);
2082 }
2083 }
2084
2085
2086
2087
2088 public static class IncompatibleDataDecoderException extends Exception {
2089 private static final long serialVersionUID = -953268047926250267L;
2090
2091 public IncompatibleDataDecoderException() {
2092 }
2093
2094 public IncompatibleDataDecoderException(String msg) {
2095 super(msg);
2096 }
2097
2098 public IncompatibleDataDecoderException(Throwable cause) {
2099 super(cause);
2100 }
2101
2102 public IncompatibleDataDecoderException(String msg, Throwable cause) {
2103 super(msg, cause);
2104 }
2105 }
2106 }