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