1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http.multipart;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.handler.codec.DecoderResult;
21 import io.netty.handler.codec.http.DefaultFullHttpRequest;
22 import io.netty.handler.codec.http.DefaultHttpContent;
23 import io.netty.handler.codec.http.FullHttpRequest;
24 import io.netty.handler.codec.http.HttpConstants;
25 import io.netty.handler.codec.http.HttpContent;
26 import io.netty.handler.codec.http.HttpHeaders;
27 import io.netty.handler.codec.http.HttpMethod;
28 import io.netty.handler.codec.http.HttpRequest;
29 import io.netty.handler.codec.http.HttpVersion;
30 import io.netty.handler.codec.http.LastHttpContent;
31 import io.netty.handler.stream.ChunkedInput;
32 import io.netty.util.internal.PlatformDependent;
33
34 import java.io.File;
35 import java.io.IOException;
36 import java.io.UnsupportedEncodingException;
37 import java.net.URLEncoder;
38 import java.nio.charset.Charset;
39 import java.util.ArrayList;
40 import java.util.List;
41 import java.util.ListIterator;
42 import java.util.Map;
43 import java.util.regex.Pattern;
44
45 import static io.netty.buffer.Unpooled.wrappedBuffer;
46 import static java.util.AbstractMap.SimpleImmutableEntry;
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
61
62
63
64 public enum EncoderMode {
65
66
67
68
69 RFC1738,
70
71
72
73
74 RFC3986
75 }
76
77 private static final Map.Entry[] percentEncodings;
78
79 static {
80 percentEncodings = new Map.Entry[] {
81 new SimpleImmutableEntry<Pattern, String>(Pattern.compile("\\*"), "%2A"),
82 new SimpleImmutableEntry<Pattern, String>(Pattern.compile("\\+"), "%20"),
83 new SimpleImmutableEntry<Pattern, String>(Pattern.compile("~"), "%7E")
84 };
85 }
86
87
88
89
90 private final HttpDataFactory factory;
91
92
93
94
95 private final HttpRequest request;
96
97
98
99
100 private final Charset charset;
101
102
103
104
105 private boolean isChunked;
106
107
108
109
110 private final List<InterfaceHttpData> bodyListDatas;
111
112
113
114 final List<InterfaceHttpData> multipartHttpDatas;
115
116
117
118
119 private final boolean isMultipart;
120
121
122
123
124 String multipartDataBoundary;
125
126
127
128
129 String multipartMixedBoundary;
130
131
132
133 private boolean headerFinalized;
134
135 private final EncoderMode encoderMode;
136
137
138
139
140
141
142
143
144
145
146
147
148 public HttpPostRequestEncoder(HttpRequest request, boolean multipart) throws ErrorDataEncoderException {
149 this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, multipart,
150 HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738);
151 }
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166 public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart)
167 throws ErrorDataEncoderException {
168 this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738);
169 }
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188 public HttpPostRequestEncoder(
189 HttpDataFactory factory, HttpRequest request, boolean multipart, Charset charset,
190 EncoderMode encoderMode)
191 throws ErrorDataEncoderException {
192 if (factory == null) {
193 throw new NullPointerException("factory");
194 }
195 if (request == null) {
196 throw new NullPointerException("request");
197 }
198 if (charset == null) {
199 throw new NullPointerException("charset");
200 }
201 HttpMethod method = request.getMethod();
202 if (method.equals(HttpMethod.TRACE)) {
203 throw new ErrorDataEncoderException("Cannot create a Encoder if request is a TRACE");
204 }
205 this.request = request;
206 this.charset = charset;
207 this.factory = factory;
208
209 bodyListDatas = new ArrayList<InterfaceHttpData>();
210
211 isLastChunk = false;
212 isLastChunkSent = false;
213 isMultipart = multipart;
214 multipartHttpDatas = new ArrayList<InterfaceHttpData>();
215 this.encoderMode = encoderMode;
216 if (isMultipart) {
217 initDataMultipart();
218 }
219 }
220
221
222
223
224 public void cleanFiles() {
225 factory.cleanRequestHttpDatas(request);
226 }
227
228
229
230
231 private boolean isLastChunk;
232
233
234
235 private boolean isLastChunkSent;
236
237
238
239 private FileUpload currentFileUpload;
240
241
242
243 private boolean duringMixedMode;
244
245
246
247
248 private long globalBodySize;
249
250
251
252
253
254
255 public boolean isMultipart() {
256 return isMultipart;
257 }
258
259
260
261
262 private void initDataMultipart() {
263 multipartDataBoundary = getNewMultipartDelimiter();
264 }
265
266
267
268
269 private void initMixedMultipart() {
270 multipartMixedBoundary = getNewMultipartDelimiter();
271 }
272
273
274
275
276
277 private static String getNewMultipartDelimiter() {
278
279 return Long.toHexString(PlatformDependent.threadLocalRandom().nextLong()).toLowerCase();
280 }
281
282
283
284
285
286
287 public List<InterfaceHttpData> getBodyListAttributes() {
288 return bodyListDatas;
289 }
290
291
292
293
294
295
296
297
298
299 public void setBodyHttpDatas(List<InterfaceHttpData> datas) throws ErrorDataEncoderException {
300 if (datas == null) {
301 throw new NullPointerException("datas");
302 }
303 globalBodySize = 0;
304 bodyListDatas.clear();
305 currentFileUpload = null;
306 duringMixedMode = false;
307 multipartHttpDatas.clear();
308 for (InterfaceHttpData data : datas) {
309 addBodyHttpData(data);
310 }
311 }
312
313
314
315
316
317
318
319
320
321
322
323
324
325 public void addBodyAttribute(String name, String value) throws ErrorDataEncoderException {
326 if (name == null) {
327 throw new NullPointerException("name");
328 }
329 String svalue = value;
330 if (value == null) {
331 svalue = "";
332 }
333 Attribute data = factory.createAttribute(request, name, svalue);
334 addBodyHttpData(data);
335 }
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353 public void addBodyFileUpload(String name, File file, String contentType, boolean isText)
354 throws ErrorDataEncoderException {
355 if (name == null) {
356 throw new NullPointerException("name");
357 }
358 if (file == null) {
359 throw new NullPointerException("file");
360 }
361 String scontentType = contentType;
362 String contentTransferEncoding = null;
363 if (contentType == null) {
364 if (isText) {
365 scontentType = HttpPostBodyUtil.DEFAULT_TEXT_CONTENT_TYPE;
366 } else {
367 scontentType = HttpPostBodyUtil.DEFAULT_BINARY_CONTENT_TYPE;
368 }
369 }
370 if (!isText) {
371 contentTransferEncoding = HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value();
372 }
373 FileUpload fileUpload = factory.createFileUpload(request, name, file.getName(), scontentType,
374 contentTransferEncoding, null, file.length());
375 try {
376 fileUpload.setContent(file);
377 } catch (IOException e) {
378 throw new ErrorDataEncoderException(e);
379 }
380 addBodyHttpData(fileUpload);
381 }
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399 public void addBodyFileUploads(String name, File[] file, String[] contentType, boolean[] isText)
400 throws ErrorDataEncoderException {
401 if (file.length != contentType.length && file.length != isText.length) {
402 throw new NullPointerException("Different array length");
403 }
404 for (int i = 0; i < file.length; i++) {
405 addBodyFileUpload(name, file[i], contentType[i], isText[i]);
406 }
407 }
408
409
410
411
412
413
414
415
416
417 public void addBodyHttpData(InterfaceHttpData data) throws ErrorDataEncoderException {
418 if (headerFinalized) {
419 throw new ErrorDataEncoderException("Cannot add value once finalized");
420 }
421 if (data == null) {
422 throw new NullPointerException("data");
423 }
424 bodyListDatas.add(data);
425 if (!isMultipart) {
426 if (data instanceof Attribute) {
427 Attribute attribute = (Attribute) data;
428 try {
429
430 String key = encodeAttribute(attribute.getName(), charset);
431 String value = encodeAttribute(attribute.getValue(), charset);
432 Attribute newattribute = factory.createAttribute(request, key, value);
433 multipartHttpDatas.add(newattribute);
434 globalBodySize += newattribute.getName().length() + 1 + newattribute.length() + 1;
435 } catch (IOException e) {
436 throw new ErrorDataEncoderException(e);
437 }
438 } else if (data instanceof FileUpload) {
439
440 FileUpload fileUpload = (FileUpload) data;
441
442 String key = encodeAttribute(fileUpload.getName(), charset);
443 String value = encodeAttribute(fileUpload.getFilename(), charset);
444 Attribute newattribute = factory.createAttribute(request, key, value);
445 multipartHttpDatas.add(newattribute);
446 globalBodySize += newattribute.getName().length() + 1 + newattribute.length() + 1;
447 }
448 return;
449 }
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482 if (data instanceof Attribute) {
483 if (duringMixedMode) {
484 InternalAttribute internal = new InternalAttribute(charset);
485 internal.addValue("\r\n--" + multipartMixedBoundary + "--");
486 multipartHttpDatas.add(internal);
487 multipartMixedBoundary = null;
488 currentFileUpload = null;
489 duringMixedMode = false;
490 }
491 InternalAttribute internal = new InternalAttribute(charset);
492 if (!multipartHttpDatas.isEmpty()) {
493
494 internal.addValue("\r\n");
495 }
496 internal.addValue("--" + multipartDataBoundary + "\r\n");
497
498 Attribute attribute = (Attribute) data;
499 internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + HttpPostBodyUtil.FORM_DATA + "; "
500 + HttpPostBodyUtil.NAME + "=\"" + attribute.getName() + "\"\r\n");
501 Charset localcharset = attribute.getCharset();
502 if (localcharset != null) {
503
504 internal.addValue(HttpHeaders.Names.CONTENT_TYPE + ": " +
505 HttpPostBodyUtil.DEFAULT_TEXT_CONTENT_TYPE + "; " +
506 HttpHeaders.Values.CHARSET + '='
507 + localcharset.name() + "\r\n");
508 }
509
510 internal.addValue("\r\n");
511 multipartHttpDatas.add(internal);
512 multipartHttpDatas.add(data);
513 globalBodySize += attribute.length() + internal.size();
514 } else if (data instanceof FileUpload) {
515 FileUpload fileUpload = (FileUpload) data;
516 InternalAttribute internal = new InternalAttribute(charset);
517 if (!multipartHttpDatas.isEmpty()) {
518
519 internal.addValue("\r\n");
520 }
521 boolean localMixed;
522 if (duringMixedMode) {
523 if (currentFileUpload != null && currentFileUpload.getName().equals(fileUpload.getName())) {
524
525
526 localMixed = true;
527 } else {
528
529
530
531
532
533 internal.addValue("--" + multipartMixedBoundary + "--");
534 multipartHttpDatas.add(internal);
535 multipartMixedBoundary = null;
536
537
538 internal = new InternalAttribute(charset);
539 internal.addValue("\r\n");
540 localMixed = false;
541
542 currentFileUpload = fileUpload;
543 duringMixedMode = false;
544 }
545 } else {
546 if (currentFileUpload != null && currentFileUpload.getName().equals(fileUpload.getName())) {
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567 initMixedMultipart();
568 InternalAttribute pastAttribute = (InternalAttribute) multipartHttpDatas.get(multipartHttpDatas
569 .size() - 2);
570
571 globalBodySize -= pastAttribute.size();
572 StringBuilder replacement = new StringBuilder(
573 139 + multipartDataBoundary.length() + multipartMixedBoundary.length() * 2 +
574
575 fileUpload.getFilename().length() + fileUpload.getName().length());
576
577 replacement.append("--")
578 .append(multipartDataBoundary)
579 .append("\r\n")
580
581 .append(HttpPostBodyUtil.CONTENT_DISPOSITION)
582 .append(": ")
583 .append(HttpPostBodyUtil.FORM_DATA)
584 .append("; ")
585 .append(HttpPostBodyUtil.NAME)
586 .append("=\"")
587 .append(fileUpload.getName())
588 .append("\"\r\n")
589
590 .append(HttpHeaders.Names.CONTENT_TYPE)
591 .append(": ")
592 .append(HttpPostBodyUtil.MULTIPART_MIXED)
593 .append("; ")
594 .append(HttpHeaders.Values.BOUNDARY)
595 .append('=')
596 .append(multipartMixedBoundary)
597 .append("\r\n\r\n")
598
599 .append("--")
600 .append(multipartMixedBoundary)
601 .append("\r\n")
602
603 .append(HttpPostBodyUtil.CONTENT_DISPOSITION)
604 .append(": ")
605 .append(HttpPostBodyUtil.ATTACHMENT)
606 .append("; ")
607 .append(HttpPostBodyUtil.FILENAME)
608 .append("=\"")
609 .append(fileUpload.getFilename())
610 .append("\"\r\n");
611
612 pastAttribute.setValue(replacement.toString(), 1);
613 pastAttribute.setValue("", 2);
614
615
616 globalBodySize += pastAttribute.size();
617
618
619
620
621
622 localMixed = true;
623 duringMixedMode = true;
624 } else {
625
626
627
628 localMixed = false;
629 currentFileUpload = fileUpload;
630 duringMixedMode = false;
631 }
632 }
633
634 if (localMixed) {
635
636
637 internal.addValue("--" + multipartMixedBoundary + "\r\n");
638
639 internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + HttpPostBodyUtil.ATTACHMENT + "; "
640 + HttpPostBodyUtil.FILENAME + "=\"" + fileUpload.getFilename() + "\"\r\n");
641 } else {
642 internal.addValue("--" + multipartDataBoundary + "\r\n");
643
644
645 internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + HttpPostBodyUtil.FORM_DATA + "; "
646 + HttpPostBodyUtil.NAME + "=\"" + fileUpload.getName() + "\"; "
647 + HttpPostBodyUtil.FILENAME + "=\"" + fileUpload.getFilename() + "\"\r\n");
648 }
649
650
651
652 internal.addValue(HttpHeaders.Names.CONTENT_TYPE + ": " + fileUpload.getContentType());
653 String contentTransferEncoding = fileUpload.getContentTransferEncoding();
654 if (contentTransferEncoding != null
655 && contentTransferEncoding.equals(HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value())) {
656 internal.addValue("\r\n" + HttpHeaders.Names.CONTENT_TRANSFER_ENCODING + ": "
657 + HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value() + "\r\n\r\n");
658 } else if (fileUpload.getCharset() != null) {
659 internal.addValue("; " + HttpHeaders.Values.CHARSET + '='
660 + fileUpload.getCharset().name() + "\r\n\r\n");
661 } else {
662 internal.addValue("\r\n\r\n");
663 }
664 multipartHttpDatas.add(internal);
665 multipartHttpDatas.add(data);
666 globalBodySize += fileUpload.length() + internal.size();
667 }
668 }
669
670
671
672
673 private ListIterator<InterfaceHttpData> iterator;
674
675
676
677
678
679
680
681
682
683
684
685 public HttpRequest finalizeRequest() throws ErrorDataEncoderException {
686
687 if (!headerFinalized) {
688 if (isMultipart) {
689 InternalAttribute internal = new InternalAttribute(charset);
690 if (duringMixedMode) {
691 internal.addValue("\r\n--" + multipartMixedBoundary + "--");
692 }
693 internal.addValue("\r\n--" + multipartDataBoundary + "--\r\n");
694 multipartHttpDatas.add(internal);
695 multipartMixedBoundary = null;
696 currentFileUpload = null;
697 duringMixedMode = false;
698 globalBodySize += internal.size();
699 }
700 headerFinalized = true;
701 } else {
702 throw new ErrorDataEncoderException("Header already encoded");
703 }
704
705 HttpHeaders headers = request.headers();
706 List<String> contentTypes = headers.getAll(HttpHeaders.Names.CONTENT_TYPE);
707 List<String> transferEncoding = headers.getAll(HttpHeaders.Names.TRANSFER_ENCODING);
708 if (contentTypes != null) {
709 headers.remove(HttpHeaders.Names.CONTENT_TYPE);
710 for (String contentType : contentTypes) {
711
712 String lowercased = contentType.toLowerCase();
713 if (lowercased.startsWith(HttpHeaders.Values.MULTIPART_FORM_DATA) ||
714 lowercased.startsWith(HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) {
715
716 } else {
717 headers.add(HttpHeaders.Names.CONTENT_TYPE, contentType);
718 }
719 }
720 }
721 if (isMultipart) {
722 String value = HttpHeaders.Values.MULTIPART_FORM_DATA + "; " + HttpHeaders.Values.BOUNDARY + '='
723 + multipartDataBoundary;
724 headers.add(HttpHeaders.Names.CONTENT_TYPE, value);
725 } else {
726
727 headers.add(HttpHeaders.Names.CONTENT_TYPE, HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED);
728 }
729
730 long realSize = globalBodySize;
731 if (isMultipart) {
732 iterator = multipartHttpDatas.listIterator();
733 } else {
734 realSize -= 1;
735 iterator = multipartHttpDatas.listIterator();
736 }
737 headers.set(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(realSize));
738 if (realSize > HttpPostBodyUtil.chunkSize || isMultipart) {
739 isChunked = true;
740 if (transferEncoding != null) {
741 headers.remove(HttpHeaders.Names.TRANSFER_ENCODING);
742 for (String v : transferEncoding) {
743 if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) {
744
745 } else {
746 headers.add(HttpHeaders.Names.TRANSFER_ENCODING, v);
747 }
748 }
749 }
750 HttpHeaders.setTransferEncodingChunked(request);
751
752
753 return new WrappedHttpRequest(request);
754 } else {
755
756 HttpContent chunk = nextChunk();
757 if (request instanceof FullHttpRequest) {
758 FullHttpRequest fullRequest = (FullHttpRequest) request;
759 ByteBuf chunkContent = chunk.content();
760 if (fullRequest.content() != chunkContent) {
761 fullRequest.content().clear().writeBytes(chunkContent);
762 chunkContent.release();
763 }
764 return fullRequest;
765 } else {
766 return new WrappedFullHttpRequest(request, chunk);
767 }
768 }
769 }
770
771
772
773
774 public boolean isChunked() {
775 return isChunked;
776 }
777
778
779
780
781
782
783
784
785 @SuppressWarnings("unchecked")
786 private String encodeAttribute(String s, Charset charset) throws ErrorDataEncoderException {
787 if (s == null) {
788 return "";
789 }
790 try {
791 String encoded = URLEncoder.encode(s, charset.name());
792 if (encoderMode == EncoderMode.RFC3986) {
793 for (Map.Entry<Pattern, String> entry : percentEncodings) {
794 String replacement = entry.getValue();
795 encoded = entry.getKey().matcher(encoded).replaceAll(replacement);
796 }
797 }
798 return encoded;
799 } catch (UnsupportedEncodingException e) {
800 throw new ErrorDataEncoderException(charset.name(), e);
801 }
802 }
803
804
805
806
807 private ByteBuf currentBuffer;
808
809
810
811 private InterfaceHttpData currentData;
812
813
814
815 private boolean isKey = true;
816
817
818
819
820
821 private ByteBuf fillByteBuf() {
822 int length = currentBuffer.readableBytes();
823 if (length > HttpPostBodyUtil.chunkSize) {
824 return currentBuffer.readSlice(HttpPostBodyUtil.chunkSize).retain();
825 } else {
826
827 ByteBuf slice = currentBuffer;
828 currentBuffer = null;
829 return slice;
830 }
831 }
832
833
834
835
836
837
838
839
840
841
842
843 private HttpContent encodeNextChunkMultipart(int sizeleft) throws ErrorDataEncoderException {
844 if (currentData == null) {
845 return null;
846 }
847 ByteBuf buffer;
848 if (currentData instanceof InternalAttribute) {
849 buffer = ((InternalAttribute) currentData).toByteBuf();
850 currentData = null;
851 } else {
852 if (currentData instanceof Attribute) {
853 try {
854 buffer = ((Attribute) currentData).getChunk(sizeleft);
855 } catch (IOException e) {
856 throw new ErrorDataEncoderException(e);
857 }
858 } else {
859 try {
860 buffer = ((HttpData) currentData).getChunk(sizeleft);
861 } catch (IOException e) {
862 throw new ErrorDataEncoderException(e);
863 }
864 }
865 if (buffer.capacity() == 0) {
866
867 currentData = null;
868 return null;
869 }
870 }
871 if (currentBuffer == null) {
872 currentBuffer = buffer;
873 } else {
874 currentBuffer = wrappedBuffer(currentBuffer, buffer);
875 }
876 if (currentBuffer.readableBytes() < HttpPostBodyUtil.chunkSize) {
877 currentData = null;
878 return null;
879 }
880 buffer = fillByteBuf();
881 return new DefaultHttpContent(buffer);
882 }
883
884
885
886
887
888
889
890
891
892
893
894 private HttpContent encodeNextChunkUrlEncoded(int sizeleft) throws ErrorDataEncoderException {
895 if (currentData == null) {
896 return null;
897 }
898 int size = sizeleft;
899 ByteBuf buffer;
900
901
902 if (isKey) {
903 String key = currentData.getName();
904 buffer = wrappedBuffer(key.getBytes());
905 isKey = false;
906 if (currentBuffer == null) {
907 currentBuffer = wrappedBuffer(buffer, wrappedBuffer("=".getBytes()));
908
909 size -= buffer.readableBytes() + 1;
910 } else {
911 currentBuffer = wrappedBuffer(currentBuffer, buffer, wrappedBuffer("=".getBytes()));
912
913 size -= buffer.readableBytes() + 1;
914 }
915 if (currentBuffer.readableBytes() >= HttpPostBodyUtil.chunkSize) {
916 buffer = fillByteBuf();
917 return new DefaultHttpContent(buffer);
918 }
919 }
920
921
922 try {
923 buffer = ((HttpData) currentData).getChunk(size);
924 } catch (IOException e) {
925 throw new ErrorDataEncoderException(e);
926 }
927
928
929 ByteBuf delimiter = null;
930 if (buffer.readableBytes() < size) {
931 isKey = true;
932 delimiter = iterator.hasNext() ? wrappedBuffer("&".getBytes()) : null;
933 }
934
935
936 if (buffer.capacity() == 0) {
937 currentData = null;
938 if (currentBuffer == null) {
939 currentBuffer = delimiter;
940 } else {
941 if (delimiter != null) {
942 currentBuffer = wrappedBuffer(currentBuffer, delimiter);
943 }
944 }
945 if (currentBuffer.readableBytes() >= HttpPostBodyUtil.chunkSize) {
946 buffer = fillByteBuf();
947 return new DefaultHttpContent(buffer);
948 }
949 return null;
950 }
951
952
953 if (currentBuffer == null) {
954 if (delimiter != null) {
955 currentBuffer = wrappedBuffer(buffer, delimiter);
956 } else {
957 currentBuffer = buffer;
958 }
959 } else {
960 if (delimiter != null) {
961 currentBuffer = wrappedBuffer(currentBuffer, buffer, delimiter);
962 } else {
963 currentBuffer = wrappedBuffer(currentBuffer, buffer);
964 }
965 }
966
967
968 if (currentBuffer.readableBytes() < HttpPostBodyUtil.chunkSize) {
969 currentData = null;
970 isKey = true;
971 return null;
972 }
973
974 buffer = fillByteBuf();
975 return new DefaultHttpContent(buffer);
976 }
977
978 @Override
979 public void close() throws Exception {
980
981
982 }
983
984
985
986
987
988
989
990
991
992 @Override
993 public HttpContent readChunk(ChannelHandlerContext ctx) throws Exception {
994 if (isLastChunkSent) {
995 return null;
996 } else {
997 return nextChunk();
998 }
999 }
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009 private HttpContent nextChunk() throws ErrorDataEncoderException {
1010 if (isLastChunk) {
1011 isLastChunkSent = true;
1012 return LastHttpContent.EMPTY_LAST_CONTENT;
1013 }
1014 ByteBuf buffer;
1015 int size = HttpPostBodyUtil.chunkSize;
1016
1017 if (currentBuffer != null) {
1018 size -= currentBuffer.readableBytes();
1019 }
1020 if (size <= 0) {
1021
1022 buffer = fillByteBuf();
1023 return new DefaultHttpContent(buffer);
1024 }
1025
1026 if (currentData != null) {
1027
1028 if (isMultipart) {
1029 HttpContent chunk = encodeNextChunkMultipart(size);
1030 if (chunk != null) {
1031 return chunk;
1032 }
1033 } else {
1034 HttpContent chunk = encodeNextChunkUrlEncoded(size);
1035 if (chunk != null) {
1036
1037 return chunk;
1038 }
1039 }
1040 size = HttpPostBodyUtil.chunkSize - currentBuffer.readableBytes();
1041 }
1042 if (!iterator.hasNext()) {
1043 isLastChunk = true;
1044
1045 buffer = currentBuffer;
1046 currentBuffer = null;
1047 return new DefaultHttpContent(buffer);
1048 }
1049 while (size > 0 && iterator.hasNext()) {
1050 currentData = iterator.next();
1051 HttpContent chunk;
1052 if (isMultipart) {
1053 chunk = encodeNextChunkMultipart(size);
1054 } else {
1055 chunk = encodeNextChunkUrlEncoded(size);
1056 }
1057 if (chunk == null) {
1058
1059 size = HttpPostBodyUtil.chunkSize - currentBuffer.readableBytes();
1060 continue;
1061 }
1062
1063 return chunk;
1064 }
1065
1066 isLastChunk = true;
1067 if (currentBuffer == null) {
1068 isLastChunkSent = true;
1069
1070 return LastHttpContent.EMPTY_LAST_CONTENT;
1071 }
1072
1073 buffer = currentBuffer;
1074 currentBuffer = null;
1075 return new DefaultHttpContent(buffer);
1076 }
1077
1078 @Override
1079 public boolean isEndOfInput() throws Exception {
1080 return isLastChunkSent;
1081 }
1082
1083
1084
1085
1086 public static class ErrorDataEncoderException extends Exception {
1087 private static final long serialVersionUID = 5020247425493164465L;
1088
1089 public ErrorDataEncoderException() {
1090 }
1091
1092 public ErrorDataEncoderException(String msg) {
1093 super(msg);
1094 }
1095
1096 public ErrorDataEncoderException(Throwable cause) {
1097 super(cause);
1098 }
1099
1100 public ErrorDataEncoderException(String msg, Throwable cause) {
1101 super(msg, cause);
1102 }
1103 }
1104
1105 private static class WrappedHttpRequest implements HttpRequest {
1106 private final HttpRequest request;
1107 WrappedHttpRequest(HttpRequest request) {
1108 this.request = request;
1109 }
1110
1111 @Override
1112 public HttpRequest setProtocolVersion(HttpVersion version) {
1113 request.setProtocolVersion(version);
1114 return this;
1115 }
1116
1117 @Override
1118 public HttpRequest setMethod(HttpMethod method) {
1119 request.setMethod(method);
1120 return this;
1121 }
1122
1123 @Override
1124 public HttpRequest setUri(String uri) {
1125 request.setUri(uri);
1126 return this;
1127 }
1128
1129 @Override
1130 public HttpMethod getMethod() {
1131 return request.getMethod();
1132 }
1133
1134 @Override
1135 public String getUri() {
1136 return request.getUri();
1137 }
1138
1139 @Override
1140 public HttpVersion getProtocolVersion() {
1141 return request.getProtocolVersion();
1142 }
1143
1144 @Override
1145 public HttpHeaders headers() {
1146 return request.headers();
1147 }
1148
1149 @Override
1150 public DecoderResult getDecoderResult() {
1151 return request.getDecoderResult();
1152 }
1153
1154 @Override
1155 public void setDecoderResult(DecoderResult result) {
1156 request.setDecoderResult(result);
1157 }
1158 }
1159
1160 private static final class WrappedFullHttpRequest extends WrappedHttpRequest implements FullHttpRequest {
1161 private final HttpContent content;
1162 private WrappedFullHttpRequest(HttpRequest request, HttpContent content) {
1163 super(request);
1164 this.content = content;
1165 }
1166
1167 @Override
1168 public FullHttpRequest setProtocolVersion(HttpVersion version) {
1169 super.setProtocolVersion(version);
1170 return this;
1171 }
1172
1173 @Override
1174 public FullHttpRequest setMethod(HttpMethod method) {
1175 super.setMethod(method);
1176 return this;
1177 }
1178
1179 @Override
1180 public FullHttpRequest setUri(String uri) {
1181 super.setUri(uri);
1182 return this;
1183 }
1184
1185 @Override
1186 public FullHttpRequest copy() {
1187 DefaultFullHttpRequest copy = new DefaultFullHttpRequest(
1188 getProtocolVersion(), getMethod(), getUri(), content().copy());
1189 copy.headers().set(headers());
1190 copy.trailingHeaders().set(trailingHeaders());
1191 return copy;
1192 }
1193
1194 @Override
1195 public FullHttpRequest duplicate() {
1196 DefaultFullHttpRequest duplicate = new DefaultFullHttpRequest(
1197 getProtocolVersion(), getMethod(), getUri(), content().duplicate());
1198 duplicate.headers().set(headers());
1199 duplicate.trailingHeaders().set(trailingHeaders());
1200 return duplicate;
1201 }
1202
1203 @Override
1204 public FullHttpRequest retain(int increment) {
1205 content.retain(increment);
1206 return this;
1207 }
1208
1209 @Override
1210 public FullHttpRequest retain() {
1211 content.retain();
1212 return this;
1213 }
1214
1215 @Override
1216 public ByteBuf content() {
1217 return content.content();
1218 }
1219
1220 @Override
1221 public HttpHeaders trailingHeaders() {
1222 if (content instanceof LastHttpContent) {
1223 return ((LastHttpContent) content).trailingHeaders();
1224 } else {
1225 return HttpHeaders.EMPTY_HEADERS;
1226 }
1227 }
1228
1229 @Override
1230 public int refCnt() {
1231 return content.refCnt();
1232 }
1233
1234 @Override
1235 public boolean release() {
1236 return content.release();
1237 }
1238
1239 @Override
1240 public boolean release(int decrement) {
1241 return content.release(decrement);
1242 }
1243 }
1244 }