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.put(_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.put(_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.put(_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.put(_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.put(_header);
645 }
646 break;
647
648 default:
649
650 field.put(_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.put(_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 _state = STATE_END;
894 if (_state==STATE_END && _persistent != null && !_persistent && _status!=100 && _method==null)
895 _endp.shutdownOutput();
896 }
897 else
898
899 prepareBuffers();
900 }
901 }
902
903 if (len > 0)
904 total+=len;
905
906 return total;
907 }
908 catch (IOException e)
909 {
910 Log.ignore(e);
911 throw (e instanceof EofException) ? e:new EofException(e);
912 }
913 }
914
915
916 private void prepareBuffers()
917 {
918
919 if (!_bufferChunked)
920 {
921
922 if (!_bypass && _content != null && _content.length() > 0 && _buffer != null && _buffer.space() > 0)
923 {
924 int len = _buffer.put(_content);
925 _content.skip(len);
926 if (_content.length() == 0)
927 _content = null;
928 }
929
930
931 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
932 {
933 if ((_buffer==null||_buffer.length()==0) && _content!=null)
934 {
935
936 int size = _content.length();
937 _bufferChunked = true;
938
939
940 if (_needCRLF)
941 {
942 if (_header.length() > 0) throw new IllegalStateException("EOC");
943 _header.put(HttpTokens.CRLF);
944 _needCRLF = false;
945 }
946
947 BufferUtil.putHexInt(_header, size);
948 _header.put(HttpTokens.CRLF);
949
950
951 _needCRLF=true;
952 }
953 else if (_buffer!=null)
954 {
955 int size = _buffer.length();
956 if (size > 0)
957 {
958
959 _bufferChunked = true;
960
961
962
963 if (_buffer.getIndex() == CHUNK_SPACE)
964 {
965
966 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
967 _buffer.setGetIndex(_buffer.getIndex() - 2);
968 BufferUtil.prependHexInt(_buffer, size);
969
970 if (_needCRLF)
971 {
972 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
973 _buffer.setGetIndex(_buffer.getIndex() - 2);
974 _needCRLF = false;
975 }
976 }
977 else
978 {
979
980 if (_needCRLF)
981 {
982 if (_header.length() > 0) throw new IllegalStateException("EOC");
983 _header.put(HttpTokens.CRLF);
984 _needCRLF = false;
985 }
986 BufferUtil.putHexInt(_header, size);
987 _header.put(HttpTokens.CRLF);
988 }
989
990
991 if (_buffer.space() >= 2)
992 _buffer.put(HttpTokens.CRLF);
993 else
994 _needCRLF = true;
995 }
996 }
997
998
999 if (_needEOC && (_content == null || _content.length() == 0))
1000 {
1001 if (_needCRLF)
1002 {
1003 if (_buffer == null && _header.space() >= 2)
1004 {
1005 _header.put(HttpTokens.CRLF);
1006 _needCRLF = false;
1007 }
1008 else if (_buffer!=null && _buffer.space() >= 2)
1009 {
1010 _buffer.put(HttpTokens.CRLF);
1011 _needCRLF = false;
1012 }
1013 }
1014
1015 if (!_needCRLF && _needEOC)
1016 {
1017 if (_buffer == null && _header.space() >= LAST_CHUNK.length)
1018 {
1019 if (!_head)
1020 {
1021 _header.put(LAST_CHUNK);
1022 _bufferChunked=true;
1023 }
1024 _needEOC = false;
1025 }
1026 else if (_buffer!=null && _buffer.space() >= LAST_CHUNK.length)
1027 {
1028 if (!_head)
1029 {
1030 _buffer.put(LAST_CHUNK);
1031 _bufferChunked=true;
1032 }
1033 _needEOC = false;
1034 }
1035 }
1036 }
1037 }
1038 }
1039
1040 if (_content != null && _content.length() == 0)
1041 _content = null;
1042
1043 }
1044
1045 public int getBytesBuffered()
1046 {
1047 return(_header==null?0:_header.length())+
1048 (_buffer==null?0:_buffer.length())+
1049 (_content==null?0:_content.length());
1050 }
1051
1052 public boolean isEmpty()
1053 {
1054 return (_header==null||_header.length()==0) &&
1055 (_buffer==null||_buffer.length()==0) &&
1056 (_content==null||_content.length()==0);
1057 }
1058
1059 @Override
1060 public String toString()
1061 {
1062 return "HttpGenerator s="+_state+
1063 " h="+(_header==null?"null":_header.length())+
1064 " b="+(_buffer==null?"null":_buffer.length())+
1065 " c="+(_content==null?"null":_content.length());
1066 }
1067 }