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
254
255 private void checkMultipart(String contentType)
256 throws ErrorDataDecoderException {
257
258 String[] headerContentType = splitHeaderContentType(contentType);
259 if (headerContentType[0].toLowerCase().startsWith(
260 HttpHeaders.Values.MULTIPART_FORM_DATA) &&
261 headerContentType[1].toLowerCase().startsWith(
262 HttpHeaders.Values.BOUNDARY)) {
263 String[] boundary = headerContentType[1].split("=");
264 if (boundary.length != 2) {
265 throw new ErrorDataDecoderException("Needs a boundary value");
266 }
267 multipartDataBoundary = "--" + boundary[1];
268 isMultipart = true;
269 currentStatus = MultiPartStatus.HEADERDELIMITER;
270 } else {
271 isMultipart = false;
272 }
273 }
274
275
276
277
278
279 public boolean isMultipart() {
280 return isMultipart;
281 }
282
283
284
285
286
287
288
289
290
291
292 public List<InterfaceHttpData> getBodyHttpDatas()
293 throws NotEnoughDataDecoderException {
294 if (!isLastChunk) {
295 throw new NotEnoughDataDecoderException();
296 }
297 return bodyListHttpData;
298 }
299
300
301
302
303
304
305
306
307
308
309
310 public List<InterfaceHttpData> getBodyHttpDatas(String name)
311 throws NotEnoughDataDecoderException {
312 if (!isLastChunk) {
313 throw new NotEnoughDataDecoderException();
314 }
315 return bodyMapHttpData.get(name);
316 }
317
318
319
320
321
322
323
324
325
326
327
328 public InterfaceHttpData getBodyHttpData(String name)
329 throws NotEnoughDataDecoderException {
330 if (!isLastChunk) {
331 throw new NotEnoughDataDecoderException();
332 }
333 List<InterfaceHttpData> list = bodyMapHttpData.get(name);
334 if (list != null) {
335 return list.get(0);
336 }
337 return null;
338 }
339
340
341
342
343
344
345
346 public void offer(HttpChunk chunk) throws ErrorDataDecoderException {
347 ChannelBuffer chunked = chunk.getContent();
348 if (undecodedChunk == null) {
349 undecodedChunk = chunked;
350 } else {
351
352
353 undecodedChunk = ChannelBuffers.wrappedBuffer(
354 undecodedChunk, chunked);
355 }
356 if (chunk.isLast()) {
357 isLastChunk = true;
358 }
359 parseBody();
360 }
361
362
363
364
365
366
367
368
369
370 public boolean hasNext() throws EndOfDataDecoderException {
371 if (currentStatus == MultiPartStatus.EPILOGUE) {
372
373 if (bodyListHttpDataRank >= bodyListHttpData.size()) {
374 throw new EndOfDataDecoderException();
375 }
376 }
377 return !bodyListHttpData.isEmpty() && bodyListHttpDataRank < bodyListHttpData.size();
378 }
379
380
381
382
383
384
385
386
387 public InterfaceHttpData next() throws EndOfDataDecoderException {
388 if (hasNext()) {
389 return bodyListHttpData.get(bodyListHttpDataRank++);
390 }
391 return null;
392 }
393
394
395
396
397
398
399 private void parseBody() throws ErrorDataDecoderException {
400 if (currentStatus == MultiPartStatus.PREEPILOGUE ||
401 currentStatus == MultiPartStatus.EPILOGUE) {
402 if (isLastChunk) {
403 currentStatus = MultiPartStatus.EPILOGUE;
404 }
405 return;
406 }
407 if (isMultipart) {
408 parseBodyMultipart();
409 } else {
410 parseBodyAttributes();
411 }
412 }
413
414
415
416
417
418 private void addHttpData(InterfaceHttpData data) {
419 if (data == null) {
420 return;
421 }
422 List<InterfaceHttpData> datas = bodyMapHttpData.get(data.getName());
423 if (datas == null) {
424 datas = new ArrayList<InterfaceHttpData>(1);
425 bodyMapHttpData.put(data.getName(), datas);
426 }
427 datas.add(data);
428 bodyListHttpData.add(data);
429 }
430
431
432
433
434
435
436
437
438 private void parseBodyAttributesStandard() throws ErrorDataDecoderException {
439 int firstpos = undecodedChunk.readerIndex();
440 int currentpos = firstpos;
441 int equalpos = firstpos;
442 int ampersandpos = firstpos;
443 if (currentStatus == MultiPartStatus.NOTSTARTED) {
444 currentStatus = MultiPartStatus.DISPOSITION;
445 }
446 boolean contRead = true;
447 try {
448 while (undecodedChunk.readable() && contRead) {
449 char read = (char) undecodedChunk.readUnsignedByte();
450 currentpos++;
451 switch (currentStatus) {
452 case DISPOSITION:
453 if (read == '=') {
454 currentStatus = MultiPartStatus.FIELD;
455 equalpos = currentpos - 1;
456 String key = decodeAttribute(
457 undecodedChunk.toString(firstpos, equalpos - firstpos, charset),
458 charset);
459 currentAttribute = factory.createAttribute(request, key);
460 firstpos = currentpos;
461 } else if (read == '&') {
462 currentStatus = MultiPartStatus.DISPOSITION;
463 ampersandpos = currentpos - 1;
464 String key = decodeAttribute(
465 undecodedChunk.toString(firstpos, ampersandpos - firstpos, charset), charset);
466 currentAttribute = factory.createAttribute(request, key);
467 currentAttribute.setValue("");
468 addHttpData(currentAttribute);
469 currentAttribute = null;
470 firstpos = currentpos;
471 contRead = true;
472 }
473 break;
474 case FIELD:
475 if (read == '&') {
476 currentStatus = MultiPartStatus.DISPOSITION;
477 ampersandpos = currentpos - 1;
478 setFinalBuffer(undecodedChunk.slice(firstpos, ampersandpos - firstpos));
479 firstpos = currentpos;
480 contRead = true;
481 } else if (read == HttpConstants.CR) {
482 if (undecodedChunk.readable()) {
483 read = (char) undecodedChunk.readUnsignedByte();
484 currentpos++;
485 if (read == HttpConstants.LF) {
486 currentStatus = MultiPartStatus.PREEPILOGUE;
487 ampersandpos = currentpos - 2;
488 setFinalBuffer(
489 undecodedChunk.slice(firstpos, ampersandpos - firstpos));
490 firstpos = currentpos;
491 contRead = false;
492 } else {
493
494 contRead = false;
495 throw new ErrorDataDecoderException("Bad end of line");
496 }
497 } else {
498 currentpos--;
499 }
500 } else if (read == HttpConstants.LF) {
501 currentStatus = MultiPartStatus.PREEPILOGUE;
502 ampersandpos = currentpos - 1;
503 setFinalBuffer(
504 undecodedChunk.slice(firstpos, ampersandpos - firstpos));
505 firstpos = currentpos;
506 contRead = false;
507 }
508 break;
509 default:
510
511 contRead = false;
512 }
513 }
514 if (isLastChunk && currentAttribute != null) {
515
516 ampersandpos = currentpos;
517 if (ampersandpos > firstpos) {
518 setFinalBuffer(
519 undecodedChunk.slice(firstpos, ampersandpos - firstpos));
520 } else if (! currentAttribute.isCompleted()) {
521 setFinalBuffer(ChannelBuffers.EMPTY_BUFFER);
522 }
523 firstpos = currentpos;
524 currentStatus = MultiPartStatus.EPILOGUE;
525 return;
526 }
527 if (contRead && currentAttribute != null) {
528
529 if (currentStatus == MultiPartStatus.FIELD) {
530 currentAttribute.addContent(
531 undecodedChunk.slice(firstpos, currentpos - firstpos),
532 false);
533 firstpos = currentpos;
534 }
535 undecodedChunk.readerIndex(firstpos);
536 } else {
537
538 }
539 } catch (ErrorDataDecoderException e) {
540
541 undecodedChunk.readerIndex(firstpos);
542 throw e;
543 } catch (IOException e) {
544
545 undecodedChunk.readerIndex(firstpos);
546 throw new ErrorDataDecoderException(e);
547 }
548 }
549
550
551
552
553
554
555
556
557 private void parseBodyAttributes() throws ErrorDataDecoderException {
558 SeekAheadOptimize sao = null;
559 try {
560 sao = new SeekAheadOptimize(undecodedChunk);
561 } catch (SeekAheadNoBackArrayException e1) {
562 parseBodyAttributesStandard();
563 return;
564 }
565 int firstpos = undecodedChunk.readerIndex();
566 int currentpos = firstpos;
567 int equalpos = firstpos;
568 int ampersandpos = firstpos;
569 if (currentStatus == MultiPartStatus.NOTSTARTED) {
570 currentStatus = MultiPartStatus.DISPOSITION;
571 }
572 boolean contRead = true;
573 try {
574 loop:
575 while (sao.pos < sao.limit) {
576 char read = (char) (sao.bytes[sao.pos ++] & 0xFF);
577 currentpos ++;
578 switch (currentStatus) {
579 case DISPOSITION:
580 if (read == '=') {
581 currentStatus = MultiPartStatus.FIELD;
582 equalpos = currentpos - 1;
583 String key = decodeAttribute(
584 undecodedChunk.toString(firstpos, equalpos - firstpos, charset),
585 charset);
586 currentAttribute = factory.createAttribute(request, key);
587 firstpos = currentpos;
588 } else if (read == '&') {
589 currentStatus = MultiPartStatus.DISPOSITION;
590 ampersandpos = currentpos - 1;
591 String key = decodeAttribute(
592 undecodedChunk.toString(firstpos, ampersandpos - firstpos, charset), charset);
593 currentAttribute = factory.createAttribute(request, key);
594 currentAttribute.setValue("");
595 addHttpData(currentAttribute);
596 currentAttribute = null;
597 firstpos = currentpos;
598 contRead = true;
599 }
600 break;
601 case FIELD:
602 if (read == '&') {
603 currentStatus = MultiPartStatus.DISPOSITION;
604 ampersandpos = currentpos - 1;
605 setFinalBuffer(undecodedChunk.slice(firstpos, ampersandpos - firstpos));
606 firstpos = currentpos;
607 contRead = true;
608 } else if (read == HttpConstants.CR) {
609 if (sao.pos < sao.limit) {
610 read = (char) (sao.bytes[sao.pos ++] & 0xFF);
611 currentpos++;
612 if (read == HttpConstants.LF) {
613 currentStatus = MultiPartStatus.PREEPILOGUE;
614 ampersandpos = currentpos - 2;
615 sao.setReadPosition(0);
616 setFinalBuffer(
617 undecodedChunk.slice(firstpos, ampersandpos - firstpos));
618 firstpos = currentpos;
619 contRead = false;
620 break loop;
621 } else {
622
623 sao.setReadPosition(0);
624 contRead = false;
625 throw new ErrorDataDecoderException("Bad end of line");
626 }
627 } else {
628 if (sao.limit > 0) {
629 currentpos --;
630 }
631 }
632 } else if (read == HttpConstants.LF) {
633 currentStatus = MultiPartStatus.PREEPILOGUE;
634 ampersandpos = currentpos - 1;
635 sao.setReadPosition(0);
636 setFinalBuffer(
637 undecodedChunk.slice(firstpos, ampersandpos - firstpos));
638 firstpos = currentpos;
639 contRead = false;
640 break loop;
641 }
642 break;
643 default:
644
645 sao.setReadPosition(0);
646 contRead = false;
647 break loop;
648 }
649 }
650 if (isLastChunk && currentAttribute != null) {
651
652 ampersandpos = currentpos;
653 if (ampersandpos > firstpos) {
654 setFinalBuffer(
655 undecodedChunk.slice(firstpos, ampersandpos - firstpos));
656 } else if (! currentAttribute.isCompleted()) {
657 setFinalBuffer(ChannelBuffers.EMPTY_BUFFER);
658 }
659 firstpos = currentpos;
660 currentStatus = MultiPartStatus.EPILOGUE;
661 return;
662 }
663 if (contRead && currentAttribute != null) {
664
665 if (currentStatus == MultiPartStatus.FIELD) {
666 currentAttribute.addContent(
667 undecodedChunk.slice(firstpos, currentpos - firstpos),
668 false);
669 firstpos = currentpos;
670 }
671 undecodedChunk.readerIndex(firstpos);
672 } else {
673
674 }
675 } catch (ErrorDataDecoderException e) {
676
677 undecodedChunk.readerIndex(firstpos);
678 throw e;
679 } catch (IOException e) {
680
681 undecodedChunk.readerIndex(firstpos);
682 throw new ErrorDataDecoderException(e);
683 }
684 }
685
686 private void setFinalBuffer(ChannelBuffer buffer) throws ErrorDataDecoderException, IOException {
687 currentAttribute.addContent(buffer, true);
688 String value = decodeAttribute(
689 currentAttribute.getChannelBuffer().toString(charset),
690 charset);
691 currentAttribute.setValue(value);
692 addHttpData(currentAttribute);
693 currentAttribute = null;
694 }
695
696
697
698
699
700
701
702
703 private static String decodeAttribute(String s, Charset charset)
704 throws ErrorDataDecoderException {
705 if (s == null) {
706 return "";
707 }
708 try {
709 return URLDecoder.decode(s, charset.name());
710 } catch (UnsupportedEncodingException e) {
711 throw new ErrorDataDecoderException(charset.toString(), e);
712 } catch (IllegalArgumentException e) {
713 throw new ErrorDataDecoderException("Bad string: '" + s + '\'', e);
714 }
715 }
716
717
718
719
720
721
722 private void parseBodyMultipart() throws ErrorDataDecoderException {
723 if (undecodedChunk == null || undecodedChunk.readableBytes() == 0) {
724
725 return;
726 }
727 InterfaceHttpData data = decodeMultipart(currentStatus);
728 while (data != null) {
729 addHttpData(data);
730 if (currentStatus == MultiPartStatus.PREEPILOGUE ||
731 currentStatus == MultiPartStatus.EPILOGUE) {
732 break;
733 }
734 data = decodeMultipart(currentStatus);
735 }
736 }
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754 private InterfaceHttpData decodeMultipart(MultiPartStatus state)
755 throws ErrorDataDecoderException {
756 switch (state) {
757 case NOTSTARTED:
758 throw new ErrorDataDecoderException(
759 "Should not be called with the current status");
760 case PREAMBLE:
761
762 throw new ErrorDataDecoderException(
763 "Should not be called with the current status");
764 case HEADERDELIMITER: {
765
766 return findMultipartDelimiter(multipartDataBoundary,
767 MultiPartStatus.DISPOSITION, MultiPartStatus.PREEPILOGUE);
768 }
769 case DISPOSITION: {
770
771
772
773
774
775
776
777
778
779 return findMultipartDisposition();
780 }
781 case FIELD: {
782
783 Charset localCharset = null;
784 Attribute charsetAttribute = currentFieldAttributes
785 .get(HttpHeaders.Values.CHARSET);
786 if (charsetAttribute != null) {
787 try {
788 localCharset = Charset.forName(charsetAttribute.getValue());
789 } catch (IOException e) {
790 throw new ErrorDataDecoderException(e);
791 }
792 }
793 Attribute nameAttribute = currentFieldAttributes
794 .get(HttpPostBodyUtil.NAME);
795 if (currentAttribute == null) {
796 try {
797 currentAttribute = factory.createAttribute(request, nameAttribute
798 .getValue());
799 } catch (NullPointerException e) {
800 throw new ErrorDataDecoderException(e);
801 } catch (IllegalArgumentException e) {
802 throw new ErrorDataDecoderException(e);
803 } catch (IOException e) {
804 throw new ErrorDataDecoderException(e);
805 }
806 if (localCharset != null) {
807 currentAttribute.setCharset(localCharset);
808 }
809 }
810
811 try {
812 loadFieldMultipart(multipartDataBoundary);
813 } catch (NotEnoughDataDecoderException e) {
814 return null;
815 }
816 Attribute finalAttribute = currentAttribute;
817 currentAttribute = null;
818 currentFieldAttributes = null;
819
820 currentStatus = MultiPartStatus.HEADERDELIMITER;
821 return finalAttribute;
822 }
823 case FILEUPLOAD: {
824
825 return getFileUpload(multipartDataBoundary);
826 }
827 case MIXEDDELIMITER: {
828
829
830 return findMultipartDelimiter(multipartMixedBoundary,
831 MultiPartStatus.MIXEDDISPOSITION,
832 MultiPartStatus.HEADERDELIMITER);
833 }
834 case MIXEDDISPOSITION: {
835 return findMultipartDisposition();
836 }
837 case MIXEDFILEUPLOAD: {
838
839 return getFileUpload(multipartMixedBoundary);
840 }
841 case PREEPILOGUE:
842 return null;
843 case EPILOGUE:
844 return null;
845 default:
846 throw new ErrorDataDecoderException("Shouldn't reach here.");
847 }
848 }
849
850
851
852
853
854 void skipControlCharacters() throws NotEnoughDataDecoderException {
855 SeekAheadOptimize sao = null;
856 try {
857 sao = new SeekAheadOptimize(undecodedChunk);
858 } catch (SeekAheadNoBackArrayException e) {
859 try {
860 skipControlCharactersStandard();
861 } catch (IndexOutOfBoundsException e1) {
862 throw new NotEnoughDataDecoderException(e1);
863 }
864 return;
865 }
866
867 while (sao.pos < sao.limit) {
868 char c = (char) (sao.bytes[sao.pos ++] & 0xFF);
869 if (!Character.isISOControl(c) && !Character.isWhitespace(c)) {
870 sao.setReadPosition(1);
871 return;
872 }
873 }
874 throw new NotEnoughDataDecoderException("Access out of bounds");
875 }
876 void skipControlCharactersStandard() {
877 for (;;) {
878 char c = (char) undecodedChunk.readUnsignedByte();
879 if (!Character.isISOControl(c) && !Character.isWhitespace(c)) {
880 undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1);
881 break;
882 }
883 }
884 }
885
886
887
888
889
890
891
892
893
894 private InterfaceHttpData findMultipartDelimiter(String delimiter,
895 MultiPartStatus dispositionStatus,
896 MultiPartStatus closeDelimiterStatus)
897 throws ErrorDataDecoderException {
898
899 int readerIndex = undecodedChunk.readerIndex();
900 try {
901 skipControlCharacters();
902 } catch (NotEnoughDataDecoderException e1) {
903 undecodedChunk.readerIndex(readerIndex);
904 return null;
905 }
906 skipOneLine();
907 String newline;
908 try {
909 newline = readDelimiter(delimiter);
910 } catch (NotEnoughDataDecoderException e) {
911 undecodedChunk.readerIndex(readerIndex);
912 return null;
913 }
914 if (newline.equals(delimiter)) {
915 currentStatus = dispositionStatus;
916 return decodeMultipart(dispositionStatus);
917 } else if (newline.equals(delimiter + "--")) {
918
919 currentStatus = closeDelimiterStatus;
920 if (currentStatus == MultiPartStatus.HEADERDELIMITER) {
921
922
923 currentFieldAttributes = null;
924 return decodeMultipart(MultiPartStatus.HEADERDELIMITER);
925 }
926 return null;
927 }
928 undecodedChunk.readerIndex(readerIndex);
929 throw new ErrorDataDecoderException("No Multipart delimiter found");
930 }
931
932
933
934
935
936
937 private InterfaceHttpData findMultipartDisposition()
938 throws ErrorDataDecoderException {
939 int readerIndex = undecodedChunk.readerIndex();
940 if (currentStatus == MultiPartStatus.DISPOSITION) {
941 currentFieldAttributes = new TreeMap<String, Attribute>(
942 CaseIgnoringComparator.INSTANCE);
943 }
944
945 while (!skipOneLine()) {
946 String newline;
947 try {
948 skipControlCharacters();
949 newline = readLine();
950 } catch (NotEnoughDataDecoderException e) {
951 undecodedChunk.readerIndex(readerIndex);
952 return null;
953 }
954 String[] contents = splitMultipartHeader(newline);
955 if (contents[0].equalsIgnoreCase(HttpPostBodyUtil.CONTENT_DISPOSITION)) {
956 boolean checkSecondArg = false;
957 if (currentStatus == MultiPartStatus.DISPOSITION) {
958 checkSecondArg = contents[1]
959 .equalsIgnoreCase(HttpPostBodyUtil.FORM_DATA);
960 } else {
961 checkSecondArg = contents[1]
962 .equalsIgnoreCase(HttpPostBodyUtil.ATTACHMENT) ||
963 contents[1]
964 .equalsIgnoreCase(HttpPostBodyUtil.FILE);
965 }
966 if (checkSecondArg) {
967
968 for (int i = 2; i < contents.length; i ++) {
969 String[] values = StringUtil.split(contents[i], '=');
970 Attribute attribute;
971 try {
972 attribute = factory.createAttribute(request, values[0].trim(),
973 decodeAttribute(cleanString(values[1]), charset));
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 contents[0].trim(),
1045 decodeAttribute(cleanString(contents[i]), charset));
1046 } catch (NullPointerException e) {
1047 throw new ErrorDataDecoderException(e);
1048 } catch (IllegalArgumentException e) {
1049 throw new ErrorDataDecoderException(e);
1050 }
1051 currentFieldAttributes.put(attribute.getName(),
1052 attribute);
1053 }
1054 }
1055 }
1056 } else {
1057 throw new ErrorDataDecoderException("Unknown Params: " +
1058 newline);
1059 }
1060 }
1061
1062 Attribute filenameAttribute = currentFieldAttributes
1063 .get(HttpPostBodyUtil.FILENAME);
1064 if (currentStatus == MultiPartStatus.DISPOSITION) {
1065 if (filenameAttribute != null) {
1066
1067 currentStatus = MultiPartStatus.FILEUPLOAD;
1068
1069 return decodeMultipart(MultiPartStatus.FILEUPLOAD);
1070 } else {
1071
1072 currentStatus = MultiPartStatus.FIELD;
1073
1074 return decodeMultipart(MultiPartStatus.FIELD);
1075 }
1076 } else {
1077 if (filenameAttribute != null) {
1078
1079 currentStatus = MultiPartStatus.MIXEDFILEUPLOAD;
1080
1081 return decodeMultipart(MultiPartStatus.MIXEDFILEUPLOAD);
1082 } else {
1083
1084 throw new ErrorDataDecoderException("Filename not found");
1085 }
1086 }
1087 }
1088
1089
1090
1091
1092
1093
1094
1095 private InterfaceHttpData getFileUpload(String delimiter)
1096 throws ErrorDataDecoderException {
1097
1098
1099 Attribute encoding = currentFieldAttributes
1100 .get(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING);
1101 Charset localCharset = charset;
1102
1103 TransferEncodingMechanism mechanism = TransferEncodingMechanism.BIT7;
1104 if (encoding != null) {
1105 String code;
1106 try {
1107 code = encoding.getValue().toLowerCase();
1108 } catch (IOException e) {
1109 throw new ErrorDataDecoderException(e);
1110 }
1111 if (code.equals(HttpPostBodyUtil.TransferEncodingMechanism.BIT7.value())) {
1112 localCharset = HttpPostBodyUtil.US_ASCII;
1113 } else if (code.equals(HttpPostBodyUtil.TransferEncodingMechanism.BIT8.value())) {
1114 localCharset = HttpPostBodyUtil.ISO_8859_1;
1115 mechanism = TransferEncodingMechanism.BIT8;
1116 } else if (code
1117 .equals(HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value())) {
1118
1119 mechanism = TransferEncodingMechanism.BINARY;
1120 } else {
1121 throw new ErrorDataDecoderException(
1122 "TransferEncoding Unknown: " + code);
1123 }
1124 }
1125 Attribute charsetAttribute = currentFieldAttributes
1126 .get(HttpHeaders.Values.CHARSET);
1127 if (charsetAttribute != null) {
1128 try {
1129 localCharset = Charset.forName(charsetAttribute.getValue());
1130 } catch (IOException e) {
1131 throw new ErrorDataDecoderException(e);
1132 }
1133 }
1134 if (currentFileUpload == null) {
1135 Attribute filenameAttribute = currentFieldAttributes
1136 .get(HttpPostBodyUtil.FILENAME);
1137 Attribute nameAttribute = currentFieldAttributes
1138 .get(HttpPostBodyUtil.NAME);
1139 Attribute contentTypeAttribute = currentFieldAttributes
1140 .get(HttpHeaders.Names.CONTENT_TYPE);
1141 if (contentTypeAttribute == null) {
1142 throw new ErrorDataDecoderException(
1143 "Content-Type is absent but required");
1144 }
1145 Attribute lengthAttribute = currentFieldAttributes
1146 .get(HttpHeaders.Names.CONTENT_LENGTH);
1147 long size;
1148 try {
1149 size = lengthAttribute != null? Long.parseLong(lengthAttribute
1150 .getValue()) : 0L;
1151 } catch (IOException e) {
1152 throw new ErrorDataDecoderException(e);
1153 } catch (NumberFormatException e) {
1154 size = 0;
1155 }
1156 try {
1157 currentFileUpload = factory.createFileUpload(
1158 request,
1159 nameAttribute.getValue(), filenameAttribute.getValue(),
1160 contentTypeAttribute.getValue(), mechanism.value(),
1161 localCharset, size);
1162 } catch (NullPointerException e) {
1163 throw new ErrorDataDecoderException(e);
1164 } catch (IllegalArgumentException e) {
1165 throw new ErrorDataDecoderException(e);
1166 } catch (IOException e) {
1167 throw new ErrorDataDecoderException(e);
1168 }
1169 }
1170
1171 try {
1172 readFileUploadByteMultipart(delimiter);
1173 } catch (NotEnoughDataDecoderException e) {
1174
1175
1176
1177 return null;
1178 }
1179 if (currentFileUpload.isCompleted()) {
1180
1181 if (currentStatus == MultiPartStatus.FILEUPLOAD) {
1182 currentStatus = MultiPartStatus.HEADERDELIMITER;
1183 currentFieldAttributes = null;
1184 } else {
1185 currentStatus = MultiPartStatus.MIXEDDELIMITER;
1186 cleanMixedAttributes();
1187 }
1188 FileUpload fileUpload = currentFileUpload;
1189 currentFileUpload = null;
1190 return fileUpload;
1191 }
1192
1193
1194
1195 return null;
1196 }
1197
1198
1199
1200
1201 public void cleanFiles() {
1202 factory.cleanRequestHttpDatas(request);
1203 }
1204
1205
1206
1207
1208 public void removeHttpDataFromClean(InterfaceHttpData data) {
1209 factory.removeHttpDataFromClean(request, data);
1210 }
1211
1212
1213
1214
1215 private void cleanMixedAttributes() {
1216 currentFieldAttributes.remove(HttpHeaders.Values.CHARSET);
1217 currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_LENGTH);
1218 currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING);
1219 currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_TYPE);
1220 currentFieldAttributes.remove(HttpPostBodyUtil.FILENAME);
1221 }
1222
1223
1224
1225
1226
1227
1228
1229 private String readLineStandard() throws NotEnoughDataDecoderException {
1230 int readerIndex = undecodedChunk.readerIndex();
1231 try {
1232 StringBuilder sb = new StringBuilder(64);
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 sb.toString();
1239 }
1240 } else if (nextByte == HttpConstants.LF) {
1241 return sb.toString();
1242 } else {
1243 sb.append((char) 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 = null;
1261 try {
1262 sao = new SeekAheadOptimize(undecodedChunk);
1263 } catch (SeekAheadNoBackArrayException e1) {
1264 return readLineStandard();
1265 }
1266 int readerIndex = undecodedChunk.readerIndex();
1267 try {
1268 StringBuilder sb = new StringBuilder(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 sb.toString();
1277 }
1278 } else {
1279 sb.append((char) nextByte);
1280 }
1281 } else if (nextByte == HttpConstants.LF) {
1282 sao.setReadPosition(0);
1283 return sb.toString();
1284 } else {
1285 sb.append((char) 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((char) nextByte);
1342
1343 nextByte = undecodedChunk.readByte();
1344 if (nextByte == '-') {
1345 sb.append((char) nextByte);
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 = null;
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((char) nextByte);
1442
1443 if (sao.pos < sao.limit) {
1444 nextByte = sao.bytes[sao.pos ++];
1445 if (nextByte == '-') {
1446 sb.append((char) nextByte);
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
1500 private void readFileUploadByteMultipartStandard(String delimiter)
1501 throws NotEnoughDataDecoderException, ErrorDataDecoderException {
1502 int readerIndex = undecodedChunk.readerIndex();
1503
1504 boolean newLine = true;
1505 int index = 0;
1506 int lastPosition = undecodedChunk.readerIndex();
1507 boolean found = false;
1508 while (undecodedChunk.readable()) {
1509 byte nextByte = undecodedChunk.readByte();
1510 if (newLine) {
1511
1512 if (nextByte == delimiter.codePointAt(index)) {
1513 index ++;
1514 if (delimiter.length() == index) {
1515 found = true;
1516 break;
1517 }
1518 continue;
1519 } else {
1520 newLine = false;
1521 index = 0;
1522
1523 if (nextByte == HttpConstants.CR) {
1524 if (undecodedChunk.readable()) {
1525 nextByte = undecodedChunk.readByte();
1526 if (nextByte == HttpConstants.LF) {
1527 newLine = true;
1528 index = 0;
1529 lastPosition = undecodedChunk.readerIndex() - 2;
1530 }
1531 }
1532 } else if (nextByte == HttpConstants.LF) {
1533 newLine = true;
1534 index = 0;
1535 lastPosition = undecodedChunk.readerIndex() - 1;
1536 } else {
1537
1538 lastPosition = undecodedChunk.readerIndex();
1539 }
1540 }
1541 } else {
1542
1543 if (nextByte == HttpConstants.CR) {
1544 if (undecodedChunk.readable()) {
1545 nextByte = undecodedChunk.readByte();
1546 if (nextByte == HttpConstants.LF) {
1547 newLine = true;
1548 index = 0;
1549 lastPosition = undecodedChunk.readerIndex() - 2;
1550 }
1551 }
1552 } else if (nextByte == HttpConstants.LF) {
1553 newLine = true;
1554 index = 0;
1555 lastPosition = undecodedChunk.readerIndex() - 1;
1556 } else {
1557
1558 lastPosition = undecodedChunk.readerIndex();
1559 }
1560 }
1561 }
1562 ChannelBuffer buffer = undecodedChunk.slice(readerIndex, lastPosition -
1563 readerIndex);
1564 if (found) {
1565
1566 try {
1567 currentFileUpload.addContent(buffer, true);
1568
1569 undecodedChunk.readerIndex(lastPosition);
1570 } catch (IOException e) {
1571 throw new ErrorDataDecoderException(e);
1572 }
1573 } else {
1574
1575 try {
1576 currentFileUpload.addContent(buffer, false);
1577
1578 undecodedChunk.readerIndex(lastPosition);
1579 throw new NotEnoughDataDecoderException();
1580 } catch (IOException e) {
1581 throw new ErrorDataDecoderException(e);
1582 }
1583 }
1584 }
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594 private void readFileUploadByteMultipart(String delimiter)
1595 throws NotEnoughDataDecoderException, ErrorDataDecoderException {
1596 SeekAheadOptimize sao = null;
1597 try {
1598 sao = new SeekAheadOptimize(undecodedChunk);
1599 } catch (SeekAheadNoBackArrayException e1) {
1600 readFileUploadByteMultipartStandard(delimiter);
1601 return;
1602 }
1603 int readerIndex = undecodedChunk.readerIndex();
1604
1605 boolean newLine = true;
1606 int index = 0;
1607 int lastrealpos = sao.pos;
1608 int lastPosition = undecodedChunk.readerIndex();
1609 boolean found = false;
1610
1611 while (sao.pos < sao.limit) {
1612 byte nextByte = sao.bytes[sao.pos ++];
1613 if (newLine) {
1614
1615 if (nextByte == delimiter.codePointAt(index)) {
1616 index ++;
1617 if (delimiter.length() == index) {
1618 found = true;
1619 break;
1620 }
1621 continue;
1622 } else {
1623 newLine = false;
1624 index = 0;
1625
1626 if (nextByte == HttpConstants.CR) {
1627 if (sao.pos < sao.limit) {
1628 nextByte = sao.bytes[sao.pos ++];
1629 if (nextByte == HttpConstants.LF) {
1630 newLine = true;
1631 index = 0;
1632 lastrealpos = sao.pos - 2;
1633 }
1634 }
1635 } else if (nextByte == HttpConstants.LF) {
1636 newLine = true;
1637 index = 0;
1638 lastrealpos = sao.pos - 1;
1639 } else {
1640
1641 lastrealpos = sao.pos;
1642 }
1643 }
1644 } else {
1645
1646 if (nextByte == HttpConstants.CR) {
1647 if (sao.pos < sao.limit) {
1648 nextByte = sao.bytes[sao.pos ++];
1649 if (nextByte == HttpConstants.LF) {
1650 newLine = true;
1651 index = 0;
1652 lastrealpos = sao.pos - 2;
1653 }
1654 }
1655 } else if (nextByte == HttpConstants.LF) {
1656 newLine = true;
1657 index = 0;
1658 lastrealpos = sao.pos - 1;
1659 } else {
1660
1661 lastrealpos = sao.pos;
1662 }
1663 }
1664 }
1665 lastPosition = sao.getReadPosition(lastrealpos);
1666 ChannelBuffer buffer = undecodedChunk.slice(readerIndex, lastPosition - readerIndex);
1667 if (found) {
1668
1669 try {
1670 currentFileUpload.addContent(buffer, true);
1671
1672 undecodedChunk.readerIndex(lastPosition);
1673 } catch (IOException e) {
1674 throw new ErrorDataDecoderException(e);
1675 }
1676 } else {
1677
1678 try {
1679 currentFileUpload.addContent(buffer, false);
1680
1681 undecodedChunk.readerIndex(lastPosition);
1682 throw new NotEnoughDataDecoderException();
1683 } catch (IOException e) {
1684 throw new ErrorDataDecoderException(e);
1685 }
1686 }
1687 }
1688
1689
1690
1691
1692
1693
1694 private void loadFieldMultipartStandard(String delimiter)
1695 throws NotEnoughDataDecoderException, ErrorDataDecoderException {
1696 int readerIndex = undecodedChunk.readerIndex();
1697 try {
1698
1699 boolean newLine = true;
1700 int index = 0;
1701 int lastPosition = undecodedChunk.readerIndex();
1702 boolean found = false;
1703 while (undecodedChunk.readable()) {
1704 byte nextByte = undecodedChunk.readByte();
1705 if (newLine) {
1706
1707 if (nextByte == delimiter.codePointAt(index)) {
1708 index ++;
1709 if (delimiter.length() == index) {
1710 found = true;
1711 break;
1712 }
1713 continue;
1714 } else {
1715 newLine = false;
1716 index = 0;
1717
1718 if (nextByte == HttpConstants.CR) {
1719 if (undecodedChunk.readable()) {
1720 nextByte = undecodedChunk.readByte();
1721 if (nextByte == HttpConstants.LF) {
1722 newLine = true;
1723 index = 0;
1724 lastPosition = undecodedChunk.readerIndex() - 2;
1725 }
1726 }
1727 } else if (nextByte == HttpConstants.LF) {
1728 newLine = true;
1729 index = 0;
1730 lastPosition = undecodedChunk.readerIndex() - 1;
1731 } else {
1732 lastPosition = undecodedChunk.readerIndex();
1733 }
1734 }
1735 } else {
1736
1737 if (nextByte == HttpConstants.CR) {
1738 if (undecodedChunk.readable()) {
1739 nextByte = undecodedChunk.readByte();
1740 if (nextByte == HttpConstants.LF) {
1741 newLine = true;
1742 index = 0;
1743 lastPosition = undecodedChunk.readerIndex() - 2;
1744 }
1745 }
1746 } else if (nextByte == HttpConstants.LF) {
1747 newLine = true;
1748 index = 0;
1749 lastPosition = undecodedChunk.readerIndex() - 1;
1750 } else {
1751 lastPosition = undecodedChunk.readerIndex();
1752 }
1753 }
1754 }
1755 if (found) {
1756
1757
1758
1759 try {
1760 currentAttribute.addContent(
1761 undecodedChunk.slice(readerIndex, lastPosition - readerIndex),
1762 true);
1763 } catch (IOException e) {
1764 throw new ErrorDataDecoderException(e);
1765 }
1766 undecodedChunk.readerIndex(lastPosition);
1767 } else {
1768 try {
1769 currentAttribute.addContent(
1770 undecodedChunk.slice(readerIndex, lastPosition - readerIndex),
1771 false);
1772 } catch (IOException e) {
1773 throw new ErrorDataDecoderException(e);
1774 }
1775 undecodedChunk.readerIndex(lastPosition);
1776 throw new NotEnoughDataDecoderException();
1777 }
1778 } catch (IndexOutOfBoundsException e) {
1779 undecodedChunk.readerIndex(readerIndex);
1780 throw new NotEnoughDataDecoderException(e);
1781 }
1782 }
1783
1784
1785
1786
1787
1788
1789 private void loadFieldMultipart(String delimiter)
1790 throws NotEnoughDataDecoderException, ErrorDataDecoderException {
1791 SeekAheadOptimize sao = null;
1792 try {
1793 sao = new SeekAheadOptimize(undecodedChunk);
1794 } catch (SeekAheadNoBackArrayException e1) {
1795 loadFieldMultipartStandard(delimiter);
1796 return;
1797 }
1798 int readerIndex = undecodedChunk.readerIndex();
1799 try {
1800
1801 boolean newLine = true;
1802 int index = 0;
1803 int lastPosition = undecodedChunk.readerIndex();
1804 int lastrealpos = sao.pos;
1805 boolean found = false;
1806
1807 while (sao.pos < sao.limit) {
1808 byte nextByte = sao.bytes[sao.pos ++];
1809 if (newLine) {
1810
1811 if (nextByte == delimiter.codePointAt(index)) {
1812 index ++;
1813 if (delimiter.length() == index) {
1814 found = true;
1815 break;
1816 }
1817 continue;
1818 } else {
1819 newLine = false;
1820 index = 0;
1821
1822 if (nextByte == HttpConstants.CR) {
1823 if (sao.pos < sao.limit) {
1824 nextByte = sao.bytes[sao.pos ++];
1825 if (nextByte == HttpConstants.LF) {
1826 newLine = true;
1827 index = 0;
1828 lastrealpos = sao.pos - 2;
1829 }
1830 }
1831 } else if (nextByte == HttpConstants.LF) {
1832 newLine = true;
1833 index = 0;
1834 lastrealpos = sao.pos - 1;
1835 } else {
1836 lastrealpos = sao.pos;
1837 }
1838 }
1839 } else {
1840
1841 if (nextByte == HttpConstants.CR) {
1842 if (sao.pos < sao.limit) {
1843 nextByte = sao.bytes[sao.pos ++];
1844 if (nextByte == HttpConstants.LF) {
1845 newLine = true;
1846 index = 0;
1847 lastrealpos = sao.pos - 2;
1848 }
1849 }
1850 } else if (nextByte == HttpConstants.LF) {
1851 newLine = true;
1852 index = 0;
1853 lastrealpos = sao.pos - 1;
1854 } else {
1855 lastrealpos = sao.pos;
1856 }
1857 }
1858 }
1859 lastPosition = sao.getReadPosition(lastrealpos);
1860 if (found) {
1861
1862
1863
1864 try {
1865 currentAttribute.addContent(
1866 undecodedChunk.slice(readerIndex, lastPosition - readerIndex), true);
1867 } catch (IOException e) {
1868 throw new ErrorDataDecoderException(e);
1869 }
1870 undecodedChunk.readerIndex(lastPosition);
1871 } else {
1872 try {
1873 currentAttribute.addContent(
1874 undecodedChunk.slice(readerIndex, lastPosition - readerIndex), false);
1875 } catch (IOException e) {
1876 throw new ErrorDataDecoderException(e);
1877 }
1878 undecodedChunk.readerIndex(lastPosition);
1879 throw new NotEnoughDataDecoderException();
1880 }
1881 } catch (IndexOutOfBoundsException e) {
1882 undecodedChunk.readerIndex(readerIndex);
1883 throw new NotEnoughDataDecoderException(e);
1884 }
1885 }
1886
1887
1888
1889
1890
1891 private static String cleanString(String field) {
1892 StringBuilder sb = new StringBuilder(field.length());
1893 int i = 0;
1894 for (i = 0; i < field.length(); i ++) {
1895 char nextChar = field.charAt(i);
1896 if (nextChar == HttpConstants.COLON) {
1897 sb.append(HttpConstants.SP);
1898 } else if (nextChar == HttpConstants.COMMA) {
1899 sb.append(HttpConstants.SP);
1900 } else if (nextChar == HttpConstants.EQUALS) {
1901 sb.append(HttpConstants.SP);
1902 } else if (nextChar == HttpConstants.SEMICOLON) {
1903 sb.append(HttpConstants.SP);
1904 } else if (nextChar == HttpConstants.HT) {
1905 sb.append(HttpConstants.SP);
1906 } else if (nextChar == HttpConstants.DOUBLE_QUOTE) {
1907
1908 } else {
1909 sb.append(nextChar);
1910 }
1911 }
1912 return sb.toString().trim();
1913 }
1914
1915
1916
1917
1918
1919 private boolean skipOneLine() {
1920 if (!undecodedChunk.readable()) {
1921 return false;
1922 }
1923 byte nextByte = undecodedChunk.readByte();
1924 if (nextByte == HttpConstants.CR) {
1925 if (!undecodedChunk.readable()) {
1926 undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1);
1927 return false;
1928 }
1929 nextByte = undecodedChunk.readByte();
1930 if (nextByte == HttpConstants.LF) {
1931 return true;
1932 }
1933 undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 2);
1934 return false;
1935 } else if (nextByte == HttpConstants.LF) {
1936 return true;
1937 }
1938 undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1);
1939 return false;
1940 }
1941
1942
1943
1944
1945
1946
1947 private static String[] splitHeaderContentType(String sb) {
1948 int size = sb.length();
1949 int aStart;
1950 int aEnd;
1951 int bStart;
1952 int bEnd;
1953 aStart = HttpPostBodyUtil.findNonWhitespace(sb, 0);
1954 aEnd = HttpPostBodyUtil.findWhitespace(sb, aStart);
1955 if (aEnd >= size) {
1956 return new String[] { sb, "" };
1957 }
1958 if (sb.charAt(aEnd) == ';') {
1959 aEnd --;
1960 }
1961 bStart = HttpPostBodyUtil.findNonWhitespace(sb, aEnd);
1962 bEnd = HttpPostBodyUtil.findEndOfString(sb);
1963 return new String[] { sb.substring(aStart, aEnd),
1964 sb.substring(bStart, bEnd) };
1965 }
1966
1967
1968
1969
1970
1971
1972
1973 private static String[] splitMultipartHeader(String sb) {
1974 ArrayList<String> headers = new ArrayList<String>(1);
1975 int nameStart;
1976 int nameEnd;
1977 int colonEnd;
1978 int valueStart;
1979 int valueEnd;
1980 nameStart = HttpPostBodyUtil.findNonWhitespace(sb, 0);
1981 for (nameEnd = nameStart; nameEnd < sb.length(); nameEnd ++) {
1982 char ch = sb.charAt(nameEnd);
1983 if (ch == ':' || Character.isWhitespace(ch)) {
1984 break;
1985 }
1986 }
1987 for (colonEnd = nameEnd; colonEnd < sb.length(); colonEnd ++) {
1988 if (sb.charAt(colonEnd) == ':') {
1989 colonEnd ++;
1990 break;
1991 }
1992 }
1993 valueStart = HttpPostBodyUtil.findNonWhitespace(sb, colonEnd);
1994 valueEnd = HttpPostBodyUtil.findEndOfString(sb);
1995 headers.add(sb.substring(nameStart, nameEnd));
1996 String svalue = sb.substring(valueStart, valueEnd);
1997 String[] values = null;
1998 if (svalue.indexOf(';') >= 0) {
1999 values = StringUtil.split(svalue, ';');
2000 } else {
2001 values = StringUtil.split(svalue, ',');
2002 }
2003 for (String value: values) {
2004 headers.add(value.trim());
2005 }
2006 String[] array = new String[headers.size()];
2007 for (int i = 0; i < headers.size(); i ++) {
2008 array[i] = headers.get(i);
2009 }
2010 return array;
2011 }
2012
2013
2014
2015
2016
2017 public static class NotEnoughDataDecoderException extends Exception {
2018
2019
2020 private static final long serialVersionUID = -7846841864603865638L;
2021
2022
2023
2024 public NotEnoughDataDecoderException() {
2025 }
2026
2027
2028
2029
2030 public NotEnoughDataDecoderException(String msg) {
2031 super(msg);
2032 }
2033
2034
2035
2036
2037 public NotEnoughDataDecoderException(Throwable cause) {
2038 super(cause);
2039 }
2040
2041
2042
2043
2044
2045 public NotEnoughDataDecoderException(String msg, Throwable cause) {
2046 super(msg, cause);
2047 }
2048 }
2049
2050
2051
2052
2053 public static class EndOfDataDecoderException extends Exception {
2054
2055
2056 private static final long serialVersionUID = 1336267941020800769L;
2057
2058 }
2059
2060
2061
2062
2063 public static class ErrorDataDecoderException extends Exception {
2064
2065
2066 private static final long serialVersionUID = 5020247425493164465L;
2067
2068
2069
2070 public ErrorDataDecoderException() {
2071 }
2072
2073
2074
2075
2076 public ErrorDataDecoderException(String msg) {
2077 super(msg);
2078 }
2079
2080
2081
2082
2083 public ErrorDataDecoderException(Throwable cause) {
2084 super(cause);
2085 }
2086
2087
2088
2089
2090
2091 public ErrorDataDecoderException(String msg, Throwable cause) {
2092 super(msg, cause);
2093 }
2094 }
2095
2096
2097
2098
2099 public static class IncompatibleDataDecoderException extends Exception {
2100
2101
2102 private static final long serialVersionUID = -953268047926250267L;
2103
2104
2105
2106 public IncompatibleDataDecoderException() {
2107 }
2108
2109
2110
2111
2112 public IncompatibleDataDecoderException(String msg) {
2113 super(msg);
2114 }
2115
2116
2117
2118
2119 public IncompatibleDataDecoderException(Throwable cause) {
2120 super(cause);
2121 }
2122
2123
2124
2125
2126
2127 public IncompatibleDataDecoderException(String msg, Throwable cause) {
2128 super(msg, cause);
2129 }
2130 }
2131 }