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