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