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.DefaultHttpChunk;
21 import org.jboss.netty.handler.codec.http.HttpChunk;
22 import org.jboss.netty.handler.codec.http.HttpConstants;
23 import org.jboss.netty.handler.codec.http.HttpHeaders;
24 import org.jboss.netty.handler.codec.http.HttpMethod;
25 import org.jboss.netty.handler.codec.http.HttpRequest;
26 import org.jboss.netty.handler.stream.ChunkedInput;
27
28 import java.io.File;
29 import java.io.IOException;
30 import java.io.UnsupportedEncodingException;
31 import java.net.URLEncoder;
32 import java.nio.charset.Charset;
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.ListIterator;
37 import java.util.Map;
38 import java.util.Random;
39 import java.util.regex.Pattern;
40
41
42
43
44 public class HttpPostRequestEncoder implements ChunkedInput {
45
46
47
48
49 public enum EncoderMode {
50
51
52
53
54 RFC1738,
55
56
57
58
59 RFC3986
60 }
61
62 private static final Map<Pattern, String> percentEncodings = new HashMap<Pattern, String>();
63
64 static {
65 percentEncodings.put(Pattern.compile("\\*"), "%2A");
66 percentEncodings.put(Pattern.compile("\\+"), "%20");
67 percentEncodings.put(Pattern.compile("%7E"), "~");
68 }
69
70
71
72
73 private final HttpDataFactory factory;
74
75
76
77
78 private final HttpRequest request;
79
80
81
82
83 private final Charset charset;
84
85
86
87
88 private boolean isChunked;
89
90
91
92
93 private final List<InterfaceHttpData> bodyListDatas;
94
95
96
97 private final List<InterfaceHttpData> multipartHttpDatas;
98
99
100
101
102 private final boolean isMultipart;
103
104
105
106
107 private String multipartDataBoundary;
108
109
110
111
112
113 private String multipartMixedBoundary;
114
115
116
117 private boolean headerFinalized;
118
119 private final EncoderMode encoderMode;
120
121
122
123
124
125
126
127
128 public HttpPostRequestEncoder(HttpRequest request, boolean multipart)
129 throws ErrorDataEncoderException {
130 this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE),
131 request, multipart, HttpConstants.DEFAULT_CHARSET);
132 }
133
134
135
136
137
138
139
140
141
142 public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart)
143 throws ErrorDataEncoderException {
144 this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET);
145 }
146
147
148
149
150
151
152
153
154
155
156 public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request,
157 boolean multipart, Charset charset) throws ErrorDataEncoderException {
158 this(factory, request, multipart, charset, EncoderMode.RFC1738);
159 }
160
161
162
163
164
165
166
167
168
169
170
171 public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart,
172 Charset charset, EncoderMode encoderMode) throws ErrorDataEncoderException {
173 if (factory == null) {
174 throw new NullPointerException("factory");
175 }
176 if (request == null) {
177 throw new NullPointerException("request");
178 }
179 if (charset == null) {
180 throw new NullPointerException("charset");
181 }
182 if (request.getMethod() != HttpMethod.POST) {
183 throw new ErrorDataEncoderException("Cannot create a Encoder if not a POST");
184 }
185 this.request = request;
186 this.charset = charset;
187 this.factory = factory;
188 this.encoderMode = encoderMode;
189
190 bodyListDatas = new ArrayList<InterfaceHttpData>();
191
192 isLastChunk = false;
193 isLastChunkSent = false;
194 isMultipart = multipart;
195 multipartHttpDatas = new ArrayList<InterfaceHttpData>();
196 if (isMultipart) {
197 initDataMultipart();
198 }
199 }
200
201
202
203 public void cleanFiles() {
204 factory.cleanRequestHttpDatas(request);
205 }
206
207
208
209
210 private boolean isLastChunk;
211
212
213
214 private boolean isLastChunkSent;
215
216
217
218 private FileUpload currentFileUpload;
219
220
221
222 private boolean duringMixedMode;
223
224
225
226
227 private long globalBodySize;
228
229
230
231
232
233 public boolean isMultipart() {
234 return isMultipart;
235 }
236
237
238
239
240 private void initDataMultipart() {
241 multipartDataBoundary = getNewMultipartDelimiter();
242 }
243
244
245
246
247 private void initMixedMultipart() {
248 multipartMixedBoundary = getNewMultipartDelimiter();
249 }
250
251
252
253
254
255 private static String getNewMultipartDelimiter() {
256
257 Random random = new Random();
258 return Long.toHexString(random.nextLong()).toLowerCase();
259 }
260
261
262
263
264
265
266 public List<InterfaceHttpData> getBodyListAttributes() {
267 return bodyListDatas;
268 }
269
270
271
272
273
274
275 public void setBodyHttpDatas(List<InterfaceHttpData> datas)
276 throws ErrorDataEncoderException {
277 if (datas == null) {
278 throw new NullPointerException("datas");
279 }
280 globalBodySize = 0;
281 bodyListDatas.clear();
282 currentFileUpload = null;
283 duringMixedMode = false;
284 multipartHttpDatas.clear();
285 for (InterfaceHttpData data: datas) {
286 addBodyHttpData(data);
287 }
288 }
289
290
291
292
293
294
295
296
297 public void addBodyAttribute(String name, String value)
298 throws ErrorDataEncoderException {
299 if (name == null) {
300 throw new NullPointerException("name");
301 }
302 String svalue = value;
303 if (value == null) {
304 svalue = "";
305 }
306 Attribute data = factory.createAttribute(request, name, svalue);
307 addBodyHttpData(data);
308 }
309
310
311
312
313
314
315
316
317
318
319 public void addBodyFileUpload(String name, File file, String contentType, boolean isText)
320 throws ErrorDataEncoderException {
321 if (name == null) {
322 throw new NullPointerException("name");
323 }
324 if (file == null) {
325 throw new NullPointerException("file");
326 }
327 String scontentType = contentType;
328 String contentTransferEncoding = null;
329 if (contentType == null) {
330 if (isText) {
331 scontentType = HttpPostBodyUtil.DEFAULT_TEXT_CONTENT_TYPE;
332 } else {
333 scontentType = HttpPostBodyUtil.DEFAULT_BINARY_CONTENT_TYPE;
334 }
335 }
336 if (!isText) {
337 contentTransferEncoding = HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value();
338 }
339 FileUpload fileUpload = factory.createFileUpload(request, name, file.getName(),
340 scontentType, contentTransferEncoding, null, file.length());
341 try {
342 fileUpload.setContent(file);
343 } catch (IOException e) {
344 throw new ErrorDataEncoderException(e);
345 }
346 addBodyHttpData(fileUpload);
347 }
348
349
350
351
352
353
354
355
356
357
358 public void addBodyFileUploads(String name, File[] file, String[] contentType, boolean[] isText)
359 throws ErrorDataEncoderException {
360 if (file.length != contentType.length && file.length != isText.length) {
361 throw new NullPointerException("Different array length");
362 }
363 for (int i = 0; i < file.length; i++) {
364 addBodyFileUpload(name, file[i], contentType[i], isText[i]);
365 }
366 }
367
368
369
370
371
372
373 public void addBodyHttpData(InterfaceHttpData data)
374 throws ErrorDataEncoderException {
375 if (headerFinalized) {
376 throw new ErrorDataEncoderException("Cannot add value once finalized");
377 }
378 if (data == null) {
379 throw new NullPointerException("data");
380 }
381 bodyListDatas.add(data);
382 if (! isMultipart) {
383 if (data instanceof Attribute) {
384 Attribute attribute = (Attribute) data;
385 try {
386
387 String key = encodeAttribute(attribute.getName(), charset);
388 String value = encodeAttribute(attribute.getValue(), charset);
389 Attribute newattribute = factory.createAttribute(request, key, value);
390 multipartHttpDatas.add(newattribute);
391 globalBodySize += newattribute.getName().length() + 1 +
392 newattribute.length() + 1;
393 } catch (IOException e) {
394 throw new ErrorDataEncoderException(e);
395 }
396 } else if (data instanceof FileUpload) {
397
398 FileUpload fileUpload = (FileUpload) data;
399
400 String key = encodeAttribute(fileUpload.getName(), charset);
401 String value = encodeAttribute(fileUpload.getFilename(), charset);
402 Attribute newattribute = factory.createAttribute(request, key, value);
403 multipartHttpDatas.add(newattribute);
404 globalBodySize += newattribute.getName().length() + 1 +
405 newattribute.length() + 1;
406 }
407 return;
408 }
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441 if (data instanceof Attribute) {
442 if (duringMixedMode) {
443 InternalAttribute internal = new InternalAttribute(charset);
444 internal.addValue("\r\n--" + multipartMixedBoundary + "--");
445 multipartHttpDatas.add(internal);
446 multipartMixedBoundary = null;
447 currentFileUpload = null;
448 duringMixedMode = false;
449 }
450 InternalAttribute internal = new InternalAttribute(charset);
451 if (!multipartHttpDatas.isEmpty()) {
452
453 internal.addValue("\r\n");
454 }
455 internal.addValue("--" + multipartDataBoundary + "\r\n");
456
457 Attribute attribute = (Attribute) data;
458 internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " +
459 HttpPostBodyUtil.FORM_DATA + "; " +
460 HttpPostBodyUtil.NAME + "=\"" + attribute.getName() + "\"\r\n");
461 Charset localcharset = attribute.getCharset();
462 if (localcharset != null) {
463
464 internal.addValue(HttpHeaders.Names.CONTENT_TYPE + ": " +
465 HttpPostBodyUtil.DEFAULT_TEXT_CONTENT_TYPE + "; " +
466 HttpHeaders.Values.CHARSET + '=' + localcharset + "\r\n");
467 }
468
469 internal.addValue("\r\n");
470 multipartHttpDatas.add(internal);
471 multipartHttpDatas.add(data);
472 globalBodySize += attribute.length() + internal.size();
473 } else if (data instanceof FileUpload) {
474 FileUpload fileUpload = (FileUpload) data;
475 InternalAttribute internal = new InternalAttribute(charset);
476 if (!multipartHttpDatas.isEmpty()) {
477
478 internal.addValue("\r\n");
479 }
480 boolean localMixed;
481 if (duringMixedMode) {
482 if (currentFileUpload != null &&
483 currentFileUpload.getName().equals(fileUpload.getName())) {
484
485
486 localMixed = true;
487 } else {
488
489
490
491
492 internal.addValue("--" + multipartMixedBoundary + "--");
493 multipartHttpDatas.add(internal);
494 multipartMixedBoundary = null;
495
496 internal = new InternalAttribute(charset);
497 internal.addValue("\r\n");
498 localMixed = false;
499
500 currentFileUpload = fileUpload;
501 duringMixedMode = false;
502 }
503 } else {
504 if (currentFileUpload != null &&
505 currentFileUpload.getName().equals(fileUpload.getName())) {
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524 initMixedMultipart();
525 InternalAttribute pastAttribute =
526 (InternalAttribute) multipartHttpDatas.get(multipartHttpDatas.size() - 2);
527
528 globalBodySize -= pastAttribute.size();
529 String replacement = HttpPostBodyUtil.CONTENT_DISPOSITION + ": " +
530 HttpPostBodyUtil.FORM_DATA + "; " + HttpPostBodyUtil.NAME + "=\"" +
531 fileUpload.getName() + "\"\r\n";
532 replacement += HttpHeaders.Names.CONTENT_TYPE + ": " +
533 HttpPostBodyUtil.MULTIPART_MIXED + "; " + HttpHeaders.Values.BOUNDARY +
534 '=' + multipartMixedBoundary + "\r\n\r\n";
535 replacement += "--" + multipartMixedBoundary + "\r\n";
536 replacement += HttpPostBodyUtil.CONTENT_DISPOSITION + ": " +
537 HttpPostBodyUtil.FILE + "; " + HttpPostBodyUtil.FILENAME + "=\"" +
538 fileUpload.getFilename() + "\"\r\n";
539 pastAttribute.setValue(replacement, 1);
540
541 globalBodySize += pastAttribute.size();
542
543
544
545
546 localMixed = true;
547 duringMixedMode = true;
548 } else {
549
550
551 localMixed = false;
552 currentFileUpload = fileUpload;
553 duringMixedMode = false;
554 }
555 }
556
557 if (localMixed) {
558
559
560 internal.addValue("--" + multipartMixedBoundary + "\r\n");
561
562 internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " +
563 HttpPostBodyUtil.FILE + "; " + HttpPostBodyUtil.FILENAME + "=\"" +
564 fileUpload.getFilename() +
565 "\"\r\n");
566 } else {
567 internal.addValue("--" + multipartDataBoundary + "\r\n");
568
569 internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " +
570 HttpPostBodyUtil.FORM_DATA + "; " + HttpPostBodyUtil.NAME + "=\"" +
571 fileUpload.getName() + "\"; " +
572 HttpPostBodyUtil.FILENAME + "=\"" +
573 fileUpload.getFilename() +
574 "\"\r\n");
575 }
576
577
578
579 internal.addValue(HttpHeaders.Names.CONTENT_TYPE + ": " +
580 fileUpload.getContentType());
581 String contentTransferEncoding = fileUpload.getContentTransferEncoding();
582 if (contentTransferEncoding != null &&
583 contentTransferEncoding.equals(
584 HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value())) {
585 internal.addValue("\r\n" + HttpHeaders.Names.CONTENT_TRANSFER_ENCODING +
586 ": " + HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value() +
587 "\r\n\r\n");
588 } else if (fileUpload.getCharset() != null) {
589 internal.addValue("; " + HttpHeaders.Values.CHARSET + '=' +
590 fileUpload.getCharset() + "\r\n\r\n");
591 } else {
592 internal.addValue("\r\n\r\n");
593 }
594 multipartHttpDatas.add(internal);
595 multipartHttpDatas.add(data);
596 globalBodySize += fileUpload.length() + internal.size();
597 }
598 }
599
600
601
602
603 private ListIterator<InterfaceHttpData> iterator;
604
605
606
607
608
609
610
611
612
613
614
615
616 public HttpRequest finalizeRequest() throws ErrorDataEncoderException {
617
618 if (! headerFinalized) {
619 if (isMultipart) {
620 InternalAttribute internal = new InternalAttribute(charset);
621 if (duringMixedMode) {
622 internal.addValue("\r\n--" + multipartMixedBoundary + "--");
623 }
624 internal.addValue("\r\n--" + multipartDataBoundary + "--\r\n");
625 multipartHttpDatas.add(internal);
626 multipartMixedBoundary = null;
627 currentFileUpload = null;
628 duringMixedMode = false;
629 globalBodySize += internal.size();
630 }
631 headerFinalized = true;
632 } else {
633 throw new ErrorDataEncoderException("Header already encoded");
634 }
635 List<String> contentTypes = request.getHeaders(HttpHeaders.Names.CONTENT_TYPE);
636 List<String> transferEncoding =
637 request.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING);
638 if (contentTypes != null) {
639 request.removeHeader(HttpHeaders.Names.CONTENT_TYPE);
640 for (String contentType: contentTypes) {
641
642 if (contentType.toLowerCase().startsWith(
643 HttpHeaders.Values.MULTIPART_FORM_DATA)) {
644
645 } else if (contentType.toLowerCase().startsWith(
646 HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) {
647
648 } else {
649 request.addHeader(HttpHeaders.Names.CONTENT_TYPE, contentType);
650 }
651 }
652 }
653 if (isMultipart) {
654 String value = HttpHeaders.Values.MULTIPART_FORM_DATA + "; " +
655 HttpHeaders.Values.BOUNDARY + '=' + multipartDataBoundary;
656 request.addHeader(HttpHeaders.Names.CONTENT_TYPE, value);
657 } else {
658
659 request.addHeader(HttpHeaders.Names.CONTENT_TYPE,
660 HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED);
661 }
662
663 long realSize = globalBodySize;
664 if (isMultipart) {
665 iterator = multipartHttpDatas.listIterator();
666 } else {
667 realSize -= 1;
668 iterator = multipartHttpDatas.listIterator();
669 }
670 request.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String
671 .valueOf(realSize));
672 if (realSize > HttpPostBodyUtil.chunkSize || isMultipart) {
673 isChunked = true;
674 if (transferEncoding != null) {
675 request.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
676 for (String v: transferEncoding) {
677 if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) {
678
679 } else {
680 request.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, v);
681 }
682 }
683 }
684 request.addHeader(HttpHeaders.Names.TRANSFER_ENCODING,
685 HttpHeaders.Values.CHUNKED);
686 request.setContent(ChannelBuffers.EMPTY_BUFFER);
687 } else {
688
689 HttpChunk chunk = nextChunk();
690 request.setContent(chunk.getContent());
691 }
692 return request;
693 }
694
695
696
697
698 public boolean isChunked() {
699 return isChunked;
700 }
701
702
703
704
705
706
707 private String encodeAttribute(String s, Charset charset)
708 throws ErrorDataEncoderException {
709 if (s == null) {
710 return "";
711 }
712 try {
713 String encoded = URLEncoder.encode(s, charset.name());
714 if (encoderMode == EncoderMode.RFC3986) {
715 for (Map.Entry<Pattern, String> entry : percentEncodings.entrySet()) {
716 String replacement = entry.getValue();
717 encoded = entry.getKey().matcher(encoded).replaceAll(replacement);
718 }
719 }
720 return encoded;
721 } catch (UnsupportedEncodingException e) {
722 throw new ErrorDataEncoderException(charset.name(), e);
723 }
724 }
725
726
727
728
729 private ChannelBuffer currentBuffer;
730
731
732
733 private InterfaceHttpData currentData;
734
735
736
737 private boolean isKey = true;
738
739
740
741
742
743
744 private ChannelBuffer fillChannelBuffer() {
745 int length = currentBuffer.readableBytes();
746 if (length > HttpPostBodyUtil.chunkSize) {
747 ChannelBuffer slice =
748 currentBuffer.slice(currentBuffer.readerIndex(), HttpPostBodyUtil.chunkSize);
749 currentBuffer.skipBytes(HttpPostBodyUtil.chunkSize);
750 return slice;
751 } else {
752
753 ChannelBuffer slice = currentBuffer;
754 currentBuffer = null;
755 return slice;
756 }
757 }
758
759
760
761
762
763
764
765
766
767
768 private HttpChunk encodeNextChunkMultipart(int sizeleft) throws ErrorDataEncoderException {
769 if (currentData == null) {
770 return null;
771 }
772 ChannelBuffer buffer;
773 if (currentData instanceof InternalAttribute) {
774
775 buffer = ((InternalAttribute) currentData).toChannelBuffer();
776 currentData = null;
777 } else {
778 if (currentData instanceof Attribute) {
779 try {
780 buffer = ((Attribute) currentData).getChunk(sizeleft);
781 } catch (IOException e) {
782 throw new ErrorDataEncoderException(e);
783 }
784 } else {
785 try {
786 buffer = ((HttpData) currentData).getChunk(sizeleft);
787 } catch (IOException e) {
788 throw new ErrorDataEncoderException(e);
789 }
790 }
791 if (buffer.capacity() == 0) {
792
793 currentData = null;
794 return null;
795 }
796 }
797 if (currentBuffer == null) {
798 currentBuffer = buffer;
799 } else {
800 currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer,
801 buffer);
802 }
803 if (currentBuffer.readableBytes() < HttpPostBodyUtil.chunkSize) {
804 currentData = null;
805 return null;
806 }
807 buffer = fillChannelBuffer();
808 return new DefaultHttpChunk(buffer);
809 }
810
811
812
813
814
815
816
817
818
819
820 private HttpChunk encodeNextChunkUrlEncoded(int sizeleft) throws ErrorDataEncoderException {
821 if (currentData == null) {
822 return null;
823 }
824 int size = sizeleft;
825 ChannelBuffer buffer;
826 if (isKey) {
827
828 String key = currentData.getName();
829 buffer = ChannelBuffers.wrappedBuffer(key.getBytes());
830 isKey = false;
831 if (currentBuffer == null) {
832 currentBuffer = ChannelBuffers.wrappedBuffer(
833 buffer, ChannelBuffers.wrappedBuffer("=".getBytes()));
834
835 size -= buffer.readableBytes() + 1;
836 } else {
837 currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer,
838 buffer, ChannelBuffers.wrappedBuffer("=".getBytes()));
839
840 size -= buffer.readableBytes() + 1;
841 }
842 if (currentBuffer.readableBytes() >= HttpPostBodyUtil.chunkSize) {
843 buffer = fillChannelBuffer();
844 return new DefaultHttpChunk(buffer);
845 }
846 }
847 try {
848 buffer = ((HttpData) currentData).getChunk(size);
849 } catch (IOException e) {
850 throw new ErrorDataEncoderException(e);
851 }
852 ChannelBuffer delimiter = null;
853 if (buffer.readableBytes() < size) {
854
855 isKey = true;
856 delimiter = iterator.hasNext() ?
857 ChannelBuffers.wrappedBuffer("&".getBytes()) :
858 null;
859 }
860 if (buffer.capacity() == 0) {
861
862 currentData = null;
863 if (currentBuffer == null) {
864 currentBuffer = delimiter;
865 } else {
866 if (delimiter != null) {
867 currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer,
868 delimiter);
869 }
870 }
871 if (currentBuffer.readableBytes() >= HttpPostBodyUtil.chunkSize) {
872 buffer = fillChannelBuffer();
873 return new DefaultHttpChunk(buffer);
874 }
875 return null;
876 }
877 if (currentBuffer == null) {
878 if (delimiter != null) {
879 currentBuffer = ChannelBuffers.wrappedBuffer(buffer,
880 delimiter);
881 } else {
882 currentBuffer = buffer;
883 }
884 } else {
885 if (delimiter != null) {
886 currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer,
887 buffer, delimiter);
888 } else {
889 currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer,
890 buffer);
891 }
892 }
893 if (currentBuffer.readableBytes() < HttpPostBodyUtil.chunkSize) {
894
895 currentData = null;
896 isKey = true;
897 return null;
898 }
899 buffer = fillChannelBuffer();
900
901 return new DefaultHttpChunk(buffer);
902 }
903
904 public void close() throws Exception {
905
906 }
907
908
909
910
911
912
913
914
915 public HttpChunk nextChunk() throws ErrorDataEncoderException {
916 if (isLastChunk) {
917 isLastChunkSent = true;
918 return new DefaultHttpChunk(ChannelBuffers.EMPTY_BUFFER);
919 }
920 ChannelBuffer buffer;
921 int size = HttpPostBodyUtil.chunkSize;
922
923 if (currentBuffer != null) {
924 size -= currentBuffer.readableBytes();
925 }
926 if (size <= 0) {
927
928 buffer = fillChannelBuffer();
929 return new DefaultHttpChunk(buffer);
930 }
931
932 if (currentData != null) {
933
934 if (isMultipart) {
935 HttpChunk chunk = encodeNextChunkMultipart(size);
936 if (chunk != null) {
937 return chunk;
938 }
939 } else {
940 HttpChunk chunk = encodeNextChunkUrlEncoded(size);
941 if (chunk != null) {
942
943 return chunk;
944 }
945 }
946 size = HttpPostBodyUtil.chunkSize - currentBuffer.readableBytes();
947 }
948 if (! iterator.hasNext()) {
949 isLastChunk = true;
950
951 buffer = currentBuffer;
952 currentBuffer = null;
953 return new DefaultHttpChunk(buffer);
954 }
955 while (size > 0 && iterator.hasNext()) {
956 currentData = iterator.next();
957 HttpChunk chunk;
958 if (isMultipart) {
959 chunk = encodeNextChunkMultipart(size);
960 } else {
961 chunk = encodeNextChunkUrlEncoded(size);
962 }
963 if (chunk == null) {
964
965 size = HttpPostBodyUtil.chunkSize - currentBuffer.readableBytes();
966 continue;
967 }
968
969 return chunk;
970 }
971
972 isLastChunk = true;
973 if (currentBuffer == null) {
974 isLastChunkSent = true;
975
976 return new DefaultHttpChunk(ChannelBuffers.EMPTY_BUFFER);
977 }
978
979 buffer = currentBuffer;
980 currentBuffer = null;
981 return new DefaultHttpChunk(buffer);
982 }
983
984 public boolean isEndOfInput() throws Exception {
985 return isLastChunkSent;
986 }
987
988 public boolean hasNextChunk() throws Exception {
989 return !isLastChunkSent;
990 }
991
992
993
994
995 public static class ErrorDataEncoderException extends Exception {
996 private static final long serialVersionUID = 5020247425493164465L;
997
998 public ErrorDataEncoderException() {
999 }
1000
1001 public ErrorDataEncoderException(String msg) {
1002 super(msg);
1003 }
1004
1005 public ErrorDataEncoderException(Throwable cause) {
1006 super(cause);
1007 }
1008
1009 public ErrorDataEncoderException(String msg, Throwable cause) {
1010 super(msg, cause);
1011 }
1012 }
1013 }