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