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