1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.http;
15
16 import java.io.IOException;
17 import java.io.InterruptedIOException;
18
19 import org.eclipse.jetty.io.Buffer;
20 import org.eclipse.jetty.io.BufferCache.CachedBuffer;
21 import org.eclipse.jetty.io.BufferUtil;
22 import org.eclipse.jetty.io.Buffers;
23 import org.eclipse.jetty.io.ByteArrayBuffer;
24 import org.eclipse.jetty.io.EndPoint;
25 import org.eclipse.jetty.io.EofException;
26 import org.eclipse.jetty.util.StringUtil;
27 import org.eclipse.jetty.util.log.Log;
28 import org.eclipse.jetty.util.log.Logger;
29
30
31
32
33
34
35
36
37 public class HttpGenerator extends AbstractGenerator
38 {
39 private static final Logger LOG = Log.getLogger(HttpGenerator.class);
40
41
42 private static class Status
43 {
44 Buffer _reason;
45 Buffer _schemeCode;
46 Buffer _responseLine;
47 }
48 private static final Status[] __status = new Status[HttpStatus.MAX_CODE+1];
49 static
50 {
51 int versionLength=HttpVersions.HTTP_1_1_BUFFER.length();
52
53 for (int i=0;i<__status.length;i++)
54 {
55 HttpStatus.Code code = HttpStatus.getCode(i);
56 if (code==null)
57 continue;
58 String reason=code.getMessage();
59 byte[] bytes=new byte[versionLength+5+reason.length()+2];
60 HttpVersions.HTTP_1_1_BUFFER.peek(0,bytes, 0, versionLength);
61 bytes[versionLength+0]=' ';
62 bytes[versionLength+1]=(byte)('0'+i/100);
63 bytes[versionLength+2]=(byte)('0'+(i%100)/10);
64 bytes[versionLength+3]=(byte)('0'+(i%10));
65 bytes[versionLength+4]=' ';
66 for (int j=0;j<reason.length();j++)
67 bytes[versionLength+5+j]=(byte)reason.charAt(j);
68 bytes[versionLength+5+reason.length()]=HttpTokens.CARRIAGE_RETURN;
69 bytes[versionLength+6+reason.length()]=HttpTokens.LINE_FEED;
70
71 __status[i] = new Status();
72 __status[i]._reason=new ByteArrayBuffer(bytes,versionLength+5,bytes.length-versionLength-7,Buffer.IMMUTABLE);
73 __status[i]._schemeCode=new ByteArrayBuffer(bytes,0,versionLength+5,Buffer.IMMUTABLE);
74 __status[i]._responseLine=new ByteArrayBuffer(bytes,0,bytes.length,Buffer.IMMUTABLE);
75 }
76 }
77
78
79 public static Buffer getReasonBuffer(int code)
80 {
81 Status status = code<__status.length?__status[code]:null;
82 if (status!=null)
83 return status._reason;
84 return null;
85 }
86
87
88
89 private static final byte[] LAST_CHUNK =
90 { (byte) '0', (byte) '\015', (byte) '\012', (byte) '\015', (byte) '\012'};
91 private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012");
92 private static final byte[] CONNECTION_KEEP_ALIVE = StringUtil.getBytes("Connection: keep-alive\015\012");
93 private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012");
94 private static final byte[] CONNECTION_ = StringUtil.getBytes("Connection: ");
95 private static final byte[] CRLF = StringUtil.getBytes("\015\012");
96 private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012");
97 private static byte[] SERVER = StringUtil.getBytes("Server: Jetty(7.0.x)\015\012");
98
99
100 private static final int CHUNK_SPACE = 12;
101
102 public static void setServerVersion(String version)
103 {
104 SERVER=StringUtil.getBytes("Server: Jetty("+version+")\015\012");
105 }
106
107
108 private boolean _bypass = false;
109 private boolean _needCRLF = false;
110 private boolean _needEOC = false;
111 private boolean _bufferChunked = false;
112
113
114
115
116
117
118
119
120
121 public HttpGenerator(Buffers buffers, EndPoint io)
122 {
123 super(buffers,io);
124 }
125
126
127 @Override
128 public void reset(boolean returnBuffers)
129 {
130 super.reset(returnBuffers);
131 _bypass = false;
132 _needCRLF = false;
133 _needEOC = false;
134 _bufferChunked=false;
135 _method=null;
136 _uri=null;
137 _noContent=false;
138 }
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153 public void addContent(Buffer content, boolean last) throws IOException
154 {
155 if (_noContent)
156 throw new IllegalStateException("NO CONTENT");
157
158 if (_last || _state==STATE_END)
159 {
160 LOG.debug("Ignoring extra content {}",content);
161 content.clear();
162 return;
163 }
164 _last = last;
165
166
167 if (_content!=null && _content.length()>0 || _bufferChunked)
168 {
169 if (!_endp.isOpen())
170 throw new EofException();
171 flushBuffer();
172 if (_content != null && _content.length()>0)
173 {
174 Buffer nc=_buffers.getBuffer(_content.length()+content.length());
175 nc.put(_content);
176 nc.put(content);
177 content=nc;
178 }
179 }
180
181 _content = content;
182 _contentWritten += content.length();
183
184
185 if (_head)
186 {
187 content.clear();
188 _content=null;
189 }
190 else if (_endp != null && (_buffer==null || _buffer.length()==0) && _content.length() > 0 && (_last || isCommitted() && _content.length()>1024))
191 {
192 _bypass = true;
193 }
194 else if (!_bufferChunked)
195 {
196
197 if (_buffer == null)
198 _buffer = _buffers.getBuffer();
199
200
201 int len=_buffer.put(_content);
202 _content.skip(len);
203 if (_content.length() == 0)
204 _content = null;
205 }
206 }
207
208
209
210
211
212
213
214 public void sendResponse(Buffer response) throws IOException
215 {
216 if (_noContent || _state!=STATE_HEADER || _content!=null && _content.length()>0 || _bufferChunked || _head )
217 throw new IllegalStateException();
218
219 _last = true;
220
221 _content = response;
222 _bypass = true;
223 _state = STATE_FLUSHING;
224
225
226 _contentLength =_contentWritten = response.length();
227
228 }
229
230
231
232
233
234
235
236
237
238 public boolean addContent(byte b) throws IOException
239 {
240 if (_noContent)
241 throw new IllegalStateException("NO CONTENT");
242
243 if (_last || _state==STATE_END)
244 {
245 LOG.debug("Ignoring extra content {}",Byte.valueOf(b));
246 return false;
247 }
248
249
250 if (_content != null && _content.length()>0 || _bufferChunked)
251 {
252 flushBuffer();
253 if (_content != null && _content.length()>0 || _bufferChunked)
254 throw new IllegalStateException("FULL");
255 }
256
257 _contentWritten++;
258
259
260 if (_head)
261 return false;
262
263
264 if (_buffer == null)
265 _buffer = _buffers.getBuffer();
266
267
268 _buffer.put(b);
269
270 return _buffer.space()<=(_contentLength == HttpTokens.CHUNKED_CONTENT?CHUNK_SPACE:0);
271 }
272
273
274
275
276
277
278
279 @Override
280 public int prepareUncheckedAddContent() throws IOException
281 {
282 if (_noContent)
283 return -1;
284
285 if (_last || _state==STATE_END)
286 return -1;
287
288
289 Buffer content = _content;
290 if (content != null && content.length()>0 || _bufferChunked)
291 {
292 flushBuffer();
293 if (content != null && content.length()>0 || _bufferChunked)
294 throw new IllegalStateException("FULL");
295 }
296
297
298 if (_buffer == null)
299 _buffer = _buffers.getBuffer();
300
301 _contentWritten-=_buffer.length();
302
303
304 if (_head)
305 return Integer.MAX_VALUE;
306
307 return _buffer.space()-(_contentLength == HttpTokens.CHUNKED_CONTENT?CHUNK_SPACE:0);
308 }
309
310
311 @Override
312 public boolean isBufferFull()
313 {
314
315 return super.isBufferFull() || _bufferChunked || _bypass || (_contentLength == HttpTokens.CHUNKED_CONTENT && _buffer != null && _buffer.space() < CHUNK_SPACE);
316 }
317
318
319 public void send1xx(int code) throws IOException
320 {
321 if (_state != STATE_HEADER)
322 return;
323
324 if (code<100||code>199)
325 throw new IllegalArgumentException("!1xx");
326 Status status=__status[code];
327 if (status==null)
328 throw new IllegalArgumentException(code+"?");
329
330
331 if (_header == null)
332 _header = _buffers.getHeader();
333
334 _header.put(status._responseLine);
335 _header.put(HttpTokens.CRLF);
336
337 try
338 {
339
340 while(_header.length()>0)
341 {
342 int len = _endp.flush(_header);
343 if (len<0)
344 throw new EofException();
345 if (len==0)
346 Thread.sleep(100);
347 }
348 }
349 catch(InterruptedException e)
350 {
351 LOG.debug(e);
352 throw new InterruptedIOException(e.toString());
353 }
354 }
355
356
357 @Override
358 public boolean isRequest()
359 {
360 return _method!=null;
361 }
362
363
364 @Override
365 public boolean isResponse()
366 {
367 return _method==null;
368 }
369
370
371 @Override
372 public void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException
373 {
374 if (_state != STATE_HEADER)
375 return;
376
377
378 if (isResponse() && _status==0)
379 throw new EofException();
380
381 if (_last && !allContentAdded)
382 throw new IllegalStateException("last?");
383 _last = _last | allContentAdded;
384
385
386 if (_header == null)
387 _header = _buffers.getHeader();
388
389 boolean has_server = false;
390
391 try
392 {
393 if (isRequest())
394 {
395 _persistent=true;
396
397 if (_version == HttpVersions.HTTP_0_9_ORDINAL)
398 {
399 _contentLength = HttpTokens.NO_CONTENT;
400 _header.put(_method);
401 _header.put((byte)' ');
402 _header.put(_uri.getBytes("utf-8"));
403 _header.put(HttpTokens.CRLF);
404 _state = STATE_FLUSHING;
405 _noContent=true;
406 return;
407 }
408 else
409 {
410 _header.put(_method);
411 _header.put((byte)' ');
412 _header.put(_uri.getBytes("utf-8"));
413 _header.put((byte)' ');
414 _header.put(_version==HttpVersions.HTTP_1_0_ORDINAL?HttpVersions.HTTP_1_0_BUFFER:HttpVersions.HTTP_1_1_BUFFER);
415 _header.put(HttpTokens.CRLF);
416 }
417 }
418 else
419 {
420
421
422 if (_version == HttpVersions.HTTP_0_9_ORDINAL)
423 {
424 _persistent = false;
425 _contentLength = HttpTokens.EOF_CONTENT;
426 _state = STATE_CONTENT;
427 return;
428 }
429 else
430 {
431 if (_persistent==null)
432 _persistent= (_version > HttpVersions.HTTP_1_0_ORDINAL);
433
434
435 Status status = _status<__status.length?__status[_status]:null;
436
437 if (status==null)
438 {
439 _header.put(HttpVersions.HTTP_1_1_BUFFER);
440 _header.put((byte) ' ');
441 _header.put((byte) ('0' + _status / 100));
442 _header.put((byte) ('0' + (_status % 100) / 10));
443 _header.put((byte) ('0' + (_status % 10)));
444 _header.put((byte) ' ');
445 if (_reason==null)
446 {
447 _header.put((byte) ('0' + _status / 100));
448 _header.put((byte) ('0' + (_status % 100) / 10));
449 _header.put((byte) ('0' + (_status % 10)));
450 }
451 else
452 _header.put(_reason);
453 _header.put(HttpTokens.CRLF);
454 }
455 else
456 {
457 if (_reason==null)
458 _header.put(status._responseLine);
459 else
460 {
461 _header.put(status._schemeCode);
462 _header.put(_reason);
463 _header.put(HttpTokens.CRLF);
464 }
465 }
466
467 if (_status<200 && _status>=100 )
468 {
469 _noContent=true;
470 _content=null;
471 if (_buffer!=null)
472 _buffer.clear();
473
474
475 if (_status!=101 )
476 {
477 _header.put(HttpTokens.CRLF);
478 _state = STATE_CONTENT;
479 return;
480 }
481 }
482 else if (_status==204 || _status==304)
483 {
484 _noContent=true;
485 _content=null;
486 if (_buffer!=null)
487 _buffer.clear();
488 }
489 }
490 }
491
492
493 if (_status>=200 && _date!=null)
494 {
495 _header.put(HttpHeaders.DATE_BUFFER);
496 _header.put((byte)':');
497 _header.put((byte)' ');
498 _header.put(_date);
499 _header.put(CRLF);
500 }
501
502
503 HttpFields.Field content_length = null;
504 HttpFields.Field transfer_encoding = null;
505 boolean keep_alive = false;
506 boolean close=false;
507 boolean content_type=false;
508 StringBuilder connection = null;
509
510 if (fields != null)
511 {
512 int s=fields.size();
513 for (int f=0;f<s;f++)
514 {
515 HttpFields.Field field = fields.getField(f);
516 if (field==null)
517 continue;
518
519 switch (field.getNameOrdinal())
520 {
521 case HttpHeaders.CONTENT_LENGTH_ORDINAL:
522 content_length = field;
523 _contentLength = field.getLongValue();
524
525 if (_contentLength < _contentWritten || _last && _contentLength != _contentWritten)
526 content_length = null;
527
528
529 field.putTo(_header);
530 break;
531
532 case HttpHeaders.CONTENT_TYPE_ORDINAL:
533 if (BufferUtil.isPrefix(MimeTypes.MULTIPART_BYTERANGES_BUFFER, field.getValueBuffer())) _contentLength = HttpTokens.SELF_DEFINING_CONTENT;
534
535
536 content_type=true;
537 field.putTo(_header);
538 break;
539
540 case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
541 if (_version == HttpVersions.HTTP_1_1_ORDINAL)
542 transfer_encoding = field;
543
544 break;
545
546 case HttpHeaders.CONNECTION_ORDINAL:
547 if (isRequest())
548 field.putTo(_header);
549
550 int connection_value = field.getValueOrdinal();
551 switch (connection_value)
552 {
553 case -1:
554 {
555 String[] values = field.getValue().split(",");
556 for (int i=0;values!=null && i<values.length;i++)
557 {
558 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim());
559
560 if (cb!=null)
561 {
562 switch(cb.getOrdinal())
563 {
564 case HttpHeaderValues.CLOSE_ORDINAL:
565 close=true;
566 if (isResponse())
567 _persistent=false;
568 keep_alive=false;
569 if (!_persistent && isResponse() && _contentLength == HttpTokens.UNKNOWN_CONTENT)
570 _contentLength = HttpTokens.EOF_CONTENT;
571 break;
572
573 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
574 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
575 {
576 keep_alive = true;
577 if (isResponse())
578 _persistent = true;
579 }
580 break;
581
582 default:
583 if (connection==null)
584 connection=new StringBuilder();
585 else
586 connection.append(',');
587 connection.append(values[i]);
588 }
589 }
590 else
591 {
592 if (connection==null)
593 connection=new StringBuilder();
594 else
595 connection.append(',');
596 connection.append(values[i]);
597 }
598 }
599
600 break;
601 }
602 case HttpHeaderValues.UPGRADE_ORDINAL:
603 {
604
605 if (isResponse())
606 {
607 field.putTo(_header);
608 continue;
609 }
610 }
611 case HttpHeaderValues.CLOSE_ORDINAL:
612 {
613 close=true;
614 if (isResponse())
615 _persistent=false;
616 if (!_persistent && isResponse() && _contentLength == HttpTokens.UNKNOWN_CONTENT)
617 _contentLength = HttpTokens.EOF_CONTENT;
618 break;
619 }
620 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
621 {
622 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
623 {
624 keep_alive = true;
625 if (isResponse())
626 _persistent=true;
627 }
628 break;
629 }
630 default:
631 {
632 if (connection==null)
633 connection=new StringBuilder();
634 else
635 connection.append(',');
636 connection.append(field.getValue());
637 }
638 }
639
640
641 break;
642
643 case HttpHeaders.SERVER_ORDINAL:
644 if (getSendServerVersion())
645 {
646 has_server=true;
647 field.putTo(_header);
648 }
649 break;
650
651 default:
652
653 field.putTo(_header);
654 }
655 }
656 }
657
658
659
660
661
662
663
664
665
666
667 switch ((int) _contentLength)
668 {
669 case HttpTokens.UNKNOWN_CONTENT:
670
671
672
673
674 if (_contentWritten == 0 && isResponse() && (_status < 200 || _status == 204 || _status == 304))
675 _contentLength = HttpTokens.NO_CONTENT;
676 else if (_last)
677 {
678
679 _contentLength = _contentWritten;
680 if (content_length == null && (isResponse() || _contentLength>0 || content_type ))
681 {
682
683 _header.put(HttpHeaders.CONTENT_LENGTH_BUFFER);
684 _header.put(HttpTokens.COLON);
685 _header.put((byte) ' ');
686 BufferUtil.putDecLong(_header, _contentLength);
687 _header.put(HttpTokens.CRLF);
688 }
689 }
690 else
691 {
692
693 _contentLength = (!_persistent || _version < HttpVersions.HTTP_1_1_ORDINAL ) ? HttpTokens.EOF_CONTENT : HttpTokens.CHUNKED_CONTENT;
694 if (isRequest() && _contentLength==HttpTokens.EOF_CONTENT)
695 {
696 _contentLength=HttpTokens.NO_CONTENT;
697 _noContent=true;
698 }
699 }
700 break;
701
702 case HttpTokens.NO_CONTENT:
703 if (content_length == null && isResponse() && _status >= 200 && _status != 204 && _status != 304)
704 _header.put(CONTENT_LENGTH_0);
705 break;
706
707 case HttpTokens.EOF_CONTENT:
708 _persistent = isRequest();
709 break;
710
711 case HttpTokens.CHUNKED_CONTENT:
712 break;
713
714 default:
715
716 break;
717 }
718
719
720 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
721 {
722
723 if (transfer_encoding != null && HttpHeaderValues.CHUNKED_ORDINAL != transfer_encoding.getValueOrdinal())
724 {
725 String c = transfer_encoding.getValue();
726 if (c.endsWith(HttpHeaderValues.CHUNKED))
727 transfer_encoding.putTo(_header);
728 else
729 throw new IllegalArgumentException("BAD TE");
730 }
731 else
732 _header.put(TRANSFER_ENCODING_CHUNKED);
733 }
734
735
736 if (_contentLength==HttpTokens.EOF_CONTENT)
737 {
738 keep_alive=false;
739 _persistent=false;
740 }
741
742 if (isResponse())
743 {
744 if (!_persistent && (close || _version > HttpVersions.HTTP_1_0_ORDINAL))
745 {
746 _header.put(CONNECTION_CLOSE);
747 if (connection!=null)
748 {
749 _header.setPutIndex(_header.putIndex()-2);
750 _header.put((byte)',');
751 _header.put(connection.toString().getBytes());
752 _header.put(CRLF);
753 }
754 }
755 else if (keep_alive)
756 {
757 _header.put(CONNECTION_KEEP_ALIVE);
758 if (connection!=null)
759 {
760 _header.setPutIndex(_header.putIndex()-2);
761 _header.put((byte)',');
762 _header.put(connection.toString().getBytes());
763 _header.put(CRLF);
764 }
765 }
766 else if (connection!=null)
767 {
768 _header.put(CONNECTION_);
769 _header.put(connection.toString().getBytes());
770 _header.put(CRLF);
771 }
772 }
773
774 if (!has_server && _status>199 && getSendServerVersion())
775 _header.put(SERVER);
776
777
778 _header.put(HttpTokens.CRLF);
779
780 _state = STATE_CONTENT;
781
782 }
783 catch(ArrayIndexOutOfBoundsException e)
784 {
785 throw new RuntimeException("Header>"+_header.capacity(),e);
786 }
787 }
788
789
790
791
792
793
794
795
796
797 @Override
798 public void complete() throws IOException
799 {
800 if (_state == STATE_END)
801 return;
802
803 super.complete();
804
805 if (_state < STATE_FLUSHING)
806 {
807 _state = STATE_FLUSHING;
808 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
809 _needEOC = true;
810 }
811
812 flushBuffer();
813 }
814
815
816 @Override
817 public long flushBuffer() throws IOException
818 {
819 try
820 {
821 if (_state == STATE_HEADER)
822 throw new IllegalStateException("State==HEADER");
823
824 prepareBuffers();
825
826 if (_endp == null)
827 {
828 if (_needCRLF && _buffer!=null)
829 _buffer.put(HttpTokens.CRLF);
830 if (_needEOC && _buffer!=null && !_head)
831 _buffer.put(LAST_CHUNK);
832 _needCRLF=false;
833 _needEOC=false;
834 return 0;
835 }
836
837 int total= 0;
838
839 int len = -1;
840 int to_flush = ((_header != null && _header.length() > 0)?4:0) | ((_buffer != null && _buffer.length() > 0)?2:0) | ((_bypass && _content != null && _content.length() > 0)?1:0);
841 switch (to_flush)
842 {
843 case 7:
844 throw new IllegalStateException();
845 case 6:
846 len = _endp.flush(_header, _buffer, null);
847 break;
848 case 5:
849 len = _endp.flush(_header, _content, null);
850 break;
851 case 4:
852 len = _endp.flush(_header);
853 break;
854 case 3:
855 len = _endp.flush(_buffer, _content, null);
856 break;
857 case 2:
858 len = _endp.flush(_buffer);
859 break;
860 case 1:
861 len = _endp.flush(_content);
862 break;
863 case 0:
864 {
865
866 if (_header != null)
867 _header.clear();
868
869 _bypass = false;
870 _bufferChunked = false;
871
872 if (_buffer != null)
873 {
874 _buffer.clear();
875 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
876 {
877
878 _buffer.setPutIndex(CHUNK_SPACE);
879 _buffer.setGetIndex(CHUNK_SPACE);
880
881
882
883 if (_content != null && _content.length() < _buffer.space() && _state != STATE_FLUSHING)
884 {
885 _buffer.put(_content);
886 _content.clear();
887 _content=null;
888 }
889 }
890 }
891
892
893 if (!_needCRLF && !_needEOC && (_content==null || _content.length()==0))
894 {
895 if (_state == STATE_FLUSHING)
896 {
897 _state = STATE_END;
898 }
899
900 if (_state==STATE_END && _persistent != null && !_persistent && _status!=100 && _method==null)
901 {
902 _endp.shutdownOutput();
903 }
904 }
905 else
906
907 prepareBuffers();
908 }
909 }
910
911 if (len > 0)
912 total+=len;
913
914 return total;
915 }
916 catch (IOException e)
917 {
918 LOG.ignore(e);
919 throw (e instanceof EofException) ? e:new EofException(e);
920 }
921 }
922
923
924 private void prepareBuffers()
925 {
926
927 if (!_bufferChunked)
928 {
929
930 if (!_bypass && _content != null && _content.length() > 0 && _buffer != null && _buffer.space() > 0)
931 {
932 int len = _buffer.put(_content);
933 _content.skip(len);
934 if (_content.length() == 0)
935 _content = null;
936 }
937
938
939 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
940 {
941 if ((_buffer==null||_buffer.length()==0) && _content!=null)
942 {
943
944 int size = _content.length();
945 _bufferChunked = true;
946
947
948 if (_needCRLF)
949 {
950 if (_header.length() > 0) throw new IllegalStateException("EOC");
951 _header.put(HttpTokens.CRLF);
952 _needCRLF = false;
953 }
954
955 BufferUtil.putHexInt(_header, size);
956 _header.put(HttpTokens.CRLF);
957
958
959 _needCRLF=true;
960 }
961 else if (_buffer!=null)
962 {
963 int size = _buffer.length();
964 if (size > 0)
965 {
966
967 _bufferChunked = true;
968
969
970
971 if (_buffer.getIndex() == CHUNK_SPACE)
972 {
973
974 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
975 _buffer.setGetIndex(_buffer.getIndex() - 2);
976 BufferUtil.prependHexInt(_buffer, size);
977
978 if (_needCRLF)
979 {
980 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
981 _buffer.setGetIndex(_buffer.getIndex() - 2);
982 _needCRLF = false;
983 }
984 }
985 else
986 {
987
988 if (_needCRLF)
989 {
990 if (_header.length() > 0) throw new IllegalStateException("EOC");
991 _header.put(HttpTokens.CRLF);
992 _needCRLF = false;
993 }
994 BufferUtil.putHexInt(_header, size);
995 _header.put(HttpTokens.CRLF);
996 }
997
998
999 if (_buffer.space() >= 2)
1000 _buffer.put(HttpTokens.CRLF);
1001 else
1002 _needCRLF = true;
1003 }
1004 }
1005
1006
1007 if (_needEOC && (_content == null || _content.length() == 0))
1008 {
1009 if (_needCRLF)
1010 {
1011 if (_buffer == null && _header.space() >= 2)
1012 {
1013 _header.put(HttpTokens.CRLF);
1014 _needCRLF = false;
1015 }
1016 else if (_buffer!=null && _buffer.space() >= 2)
1017 {
1018 _buffer.put(HttpTokens.CRLF);
1019 _needCRLF = false;
1020 }
1021 }
1022
1023 if (!_needCRLF && _needEOC)
1024 {
1025 if (_buffer == null && _header.space() >= LAST_CHUNK.length)
1026 {
1027 if (!_head)
1028 {
1029 _header.put(LAST_CHUNK);
1030 _bufferChunked=true;
1031 }
1032 _needEOC = false;
1033 }
1034 else if (_buffer!=null && _buffer.space() >= LAST_CHUNK.length)
1035 {
1036 if (!_head)
1037 {
1038 _buffer.put(LAST_CHUNK);
1039 _bufferChunked=true;
1040 }
1041 _needEOC = false;
1042 }
1043 }
1044 }
1045 }
1046 }
1047
1048 if (_content != null && _content.length() == 0)
1049 _content = null;
1050
1051 }
1052
1053 public int getBytesBuffered()
1054 {
1055 return(_header==null?0:_header.length())+
1056 (_buffer==null?0:_buffer.length())+
1057 (_content==null?0:_content.length());
1058 }
1059
1060 public boolean isEmpty()
1061 {
1062 return (_header==null||_header.length()==0) &&
1063 (_buffer==null||_buffer.length()==0) &&
1064 (_content==null||_content.length()==0);
1065 }
1066
1067 @Override
1068 public String toString()
1069 {
1070 return "HttpGenerator s="+_state+
1071 " h="+(_header==null?"null":_header.length())+
1072 " b="+(_buffer==null?"null":_buffer.length())+
1073 " c="+(_content==null?"null":_content.length());
1074 }
1075 }