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.BufferUtil;
21 import org.eclipse.jetty.io.Buffers;
22 import org.eclipse.jetty.io.ByteArrayBuffer;
23 import org.eclipse.jetty.io.EndPoint;
24 import org.eclipse.jetty.io.EofException;
25 import org.eclipse.jetty.io.BufferCache.CachedBuffer;
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
119 public HttpGenerator(Buffers buffers, EndPoint io)
120 {
121 super(buffers,io);
122 }
123
124
125 @Override
126 public void reset(boolean returnBuffers)
127 {
128 super.reset(returnBuffers);
129 _bypass = false;
130 _needCRLF = false;
131 _needEOC = false;
132 _bufferChunked=false;
133 _method=null;
134 _uri=null;
135 _noContent=false;
136 }
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151 public void addContent(Buffer content, boolean last) throws IOException
152 {
153 if (_noContent)
154 throw new IllegalStateException("NO CONTENT");
155
156 if (_last || _state==STATE_END)
157 {
158 Log.debug("Ignoring extra content {}",content);
159 content.clear();
160 return;
161 }
162 _last = last;
163
164
165 if (_content!=null && _content.length()>0 || _bufferChunked)
166 {
167 if (!_endp.isOpen())
168 throw new EofException();
169 flushBuffer();
170 if (_content != null && _content.length()>0)
171 {
172 Buffer nc=_buffers.getBuffer(_content.length()+content.length());
173 nc.put(_content);
174 nc.put(content);
175 _content=nc;
176 }
177 }
178
179 _content = content;
180 _contentWritten += content.length();
181
182
183 if (_head)
184 {
185 content.clear();
186 _content=null;
187 }
188 else if (_endp != null && _buffer == null && content.length() > 0 && _last)
189 {
190
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
358 @Override
359 public void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException
360 {
361 if (_state != STATE_HEADER)
362 return;
363
364
365 if (_method==null && _status==0)
366 throw new EofException();
367
368 if (_last && !allContentAdded)
369 throw new IllegalStateException("last?");
370 _last = _last | allContentAdded;
371
372
373 if (_header == null)
374 _header = _buffers.getHeader();
375
376 boolean has_server = false;
377
378 if (_method!=null)
379 {
380 _close = false;
381
382 if (_version == HttpVersions.HTTP_0_9_ORDINAL)
383 {
384 _contentLength = HttpTokens.NO_CONTENT;
385 _header.put(_method);
386 _header.put((byte)' ');
387 _header.put(_uri.getBytes("utf-8"));
388 _header.put(HttpTokens.CRLF);
389 _state = STATE_FLUSHING;
390 _noContent=true;
391 return;
392 }
393 else
394 {
395 _header.put(_method);
396 _header.put((byte)' ');
397 _header.put(_uri.getBytes("utf-8"));
398 _header.put((byte)' ');
399 _header.put(_version==HttpVersions.HTTP_1_0_ORDINAL?HttpVersions.HTTP_1_0_BUFFER:HttpVersions.HTTP_1_1_BUFFER);
400 _header.put(HttpTokens.CRLF);
401 }
402 }
403 else
404 {
405
406 if (_version == HttpVersions.HTTP_0_9_ORDINAL)
407 {
408 _close = true;
409 _contentLength = HttpTokens.EOF_CONTENT;
410 _state = STATE_CONTENT;
411 return;
412 }
413 else
414 {
415 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
416 _close = true;
417
418
419 Status status = _status<__status.length?__status[_status]:null;
420
421 if (status==null)
422 {
423 _header.put(HttpVersions.HTTP_1_1_BUFFER);
424 _header.put((byte) ' ');
425 _header.put((byte) ('0' + _status / 100));
426 _header.put((byte) ('0' + (_status % 100) / 10));
427 _header.put((byte) ('0' + (_status % 10)));
428 _header.put((byte) ' ');
429 if (_reason==null)
430 {
431 _header.put((byte) ('0' + _status / 100));
432 _header.put((byte) ('0' + (_status % 100) / 10));
433 _header.put((byte) ('0' + (_status % 10)));
434 }
435 else
436 _header.put(_reason);
437 _header.put(HttpTokens.CRLF);
438 }
439 else
440 {
441 if (_reason==null)
442 _header.put(status._responseLine);
443 else
444 {
445 _header.put(status._schemeCode);
446 _header.put(_reason);
447 _header.put(HttpTokens.CRLF);
448 }
449 }
450
451 if (_status<200 && _status>=100 )
452 {
453 _noContent=true;
454 _content=null;
455 if (_buffer!=null)
456 _buffer.clear();
457
458
459 if (_status!=101 )
460 {
461 _header.put(HttpTokens.CRLF);
462 _state = STATE_CONTENT;
463 return;
464 }
465 }
466 else if (_status==204 || _status==304)
467 {
468 _noContent=true;
469 _content=null;
470 if (_buffer!=null)
471 _buffer.clear();
472 }
473 }
474 }
475
476
477 if (_status>=200 && _date!=null)
478 {
479 _header.put(HttpHeaders.DATE_BUFFER);
480 _header.put((byte)':');
481 _header.put((byte)' ');
482 _header.put(_date);
483 _header.put(CRLF);
484 }
485
486
487 HttpFields.Field content_length = null;
488 HttpFields.Field transfer_encoding = null;
489 boolean keep_alive = false;
490 boolean close=false;
491 boolean content_type=false;
492 StringBuilder connection = null;
493
494 if (fields != null)
495 {
496 int s=fields.size();
497 for (int f=0;f<s;f++)
498 {
499 HttpFields.Field field = fields.getField(f);
500 if (field==null)
501 continue;
502
503 switch (field.getNameOrdinal())
504 {
505 case HttpHeaders.CONTENT_LENGTH_ORDINAL:
506 content_length = field;
507 _contentLength = field.getLongValue();
508
509 if (_contentLength < _contentWritten || _last && _contentLength != _contentWritten)
510 content_length = null;
511
512
513 field.put(_header);
514 break;
515
516 case HttpHeaders.CONTENT_TYPE_ORDINAL:
517 if (BufferUtil.isPrefix(MimeTypes.MULTIPART_BYTERANGES_BUFFER, field.getValueBuffer())) _contentLength = HttpTokens.SELF_DEFINING_CONTENT;
518
519
520 content_type=true;
521 field.put(_header);
522 break;
523
524 case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
525 if (_version == HttpVersions.HTTP_1_1_ORDINAL)
526 transfer_encoding = field;
527
528 break;
529
530 case HttpHeaders.CONNECTION_ORDINAL:
531 if (_method!=null)
532 field.put(_header);
533
534 int connection_value = field.getValueOrdinal();
535 switch (connection_value)
536 {
537 case -1:
538 {
539 String[] values = field.getValue().split(",");
540 for (int i=0;values!=null && i<values.length;i++)
541 {
542 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim());
543
544 if (cb!=null)
545 {
546 switch(cb.getOrdinal())
547 {
548 case HttpHeaderValues.CLOSE_ORDINAL:
549 close=true;
550 if (_method==null)
551 _close=true;
552 keep_alive=false;
553 if (_close && _method==null && _contentLength == HttpTokens.UNKNOWN_CONTENT)
554 _contentLength = HttpTokens.EOF_CONTENT;
555 break;
556
557 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
558 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
559 {
560 keep_alive = true;
561 if (_method==null)
562 _close = false;
563 }
564 break;
565
566 default:
567 if (connection==null)
568 connection=new StringBuilder();
569 else
570 connection.append(',');
571 connection.append(values[i]);
572 }
573 }
574 else
575 {
576 if (connection==null)
577 connection=new StringBuilder();
578 else
579 connection.append(',');
580 connection.append(values[i]);
581 }
582 }
583
584 break;
585 }
586 case HttpHeaderValues.UPGRADE_ORDINAL:
587 {
588
589 if (_method==null)
590 {
591 field.put(_header);
592 continue;
593 }
594 }
595 case HttpHeaderValues.CLOSE_ORDINAL:
596 {
597 close=true;
598 if (_method==null)
599 _close=true;
600 if (_close && _method==null && _contentLength == HttpTokens.UNKNOWN_CONTENT)
601 _contentLength = HttpTokens.EOF_CONTENT;
602 break;
603 }
604 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
605 {
606 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
607 {
608 keep_alive = true;
609 if (_method==null)
610 _close = false;
611 }
612 break;
613 }
614 default:
615 {
616 if (connection==null)
617 connection=new StringBuilder();
618 else
619 connection.append(',');
620 connection.append(field.getValue());
621 }
622 }
623
624
625 break;
626
627 case HttpHeaders.SERVER_ORDINAL:
628 if (getSendServerVersion())
629 {
630 has_server=true;
631 field.put(_header);
632 }
633 break;
634
635 default:
636
637 field.put(_header);
638 }
639 }
640 }
641
642
643
644
645
646
647
648
649
650
651 switch ((int) _contentLength)
652 {
653 case HttpTokens.UNKNOWN_CONTENT:
654
655
656
657
658 if (_contentWritten == 0 && _method==null && (_status < 200 || _status == 204 || _status == 304))
659 _contentLength = HttpTokens.NO_CONTENT;
660 else if (_last)
661 {
662
663 _contentLength = _contentWritten;
664 if (content_length == null && (_method==null || _contentLength>0 || content_type ))
665 {
666
667 _header.put(HttpHeaders.CONTENT_LENGTH_BUFFER);
668 _header.put(HttpTokens.COLON);
669 _header.put((byte) ' ');
670 BufferUtil.putDecLong(_header, _contentLength);
671 _header.put(HttpTokens.CRLF);
672 }
673 }
674 else
675 {
676
677 _contentLength = (_close || _version < HttpVersions.HTTP_1_1_ORDINAL ) ? HttpTokens.EOF_CONTENT : HttpTokens.CHUNKED_CONTENT;
678 if (_method!=null && _contentLength==HttpTokens.EOF_CONTENT)
679 {
680 _contentLength=HttpTokens.NO_CONTENT;
681 _noContent=true;
682 }
683 }
684 break;
685
686 case HttpTokens.NO_CONTENT:
687 if (content_length == null && _method==null && _status >= 200 && _status != 204 && _status != 304)
688 _header.put(CONTENT_LENGTH_0);
689 break;
690
691 case HttpTokens.EOF_CONTENT:
692 _close = _method==null;
693 break;
694
695 case HttpTokens.CHUNKED_CONTENT:
696 break;
697
698 default:
699
700 break;
701 }
702
703
704 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
705 {
706
707 if (transfer_encoding != null && HttpHeaderValues.CHUNKED_ORDINAL != transfer_encoding.getValueOrdinal())
708 {
709 String c = transfer_encoding.getValue();
710 if (c.endsWith(HttpHeaderValues.CHUNKED))
711 transfer_encoding.put(_header);
712 else
713 throw new IllegalArgumentException("BAD TE");
714 }
715 else
716 _header.put(TRANSFER_ENCODING_CHUNKED);
717 }
718
719
720 if (_contentLength==HttpTokens.EOF_CONTENT)
721 {
722 keep_alive=false;
723 _close=true;
724 }
725
726 if (_method==null)
727 {
728 if (_close && (close || _version > HttpVersions.HTTP_1_0_ORDINAL))
729 {
730 _header.put(CONNECTION_CLOSE);
731 if (connection!=null)
732 {
733 _header.setPutIndex(_header.putIndex()-2);
734 _header.put((byte)',');
735 _header.put(connection.toString().getBytes());
736 _header.put(CRLF);
737 }
738 }
739 else if (keep_alive)
740 {
741 _header.put(CONNECTION_KEEP_ALIVE);
742 if (connection!=null)
743 {
744 _header.setPutIndex(_header.putIndex()-2);
745 _header.put((byte)',');
746 _header.put(connection.toString().getBytes());
747 _header.put(CRLF);
748 }
749 }
750 else if (connection!=null)
751 {
752 _header.put(CONNECTION_);
753 _header.put(connection.toString().getBytes());
754 _header.put(CRLF);
755 }
756 }
757
758 if (!has_server && _status>199 && getSendServerVersion())
759 _header.put(SERVER);
760
761
762 _header.put(HttpTokens.CRLF);
763
764 _state = STATE_CONTENT;
765
766 }
767
768
769
770
771
772
773
774
775
776 @Override
777 public void complete() throws IOException
778 {
779 if (_state == STATE_END)
780 return;
781
782 super.complete();
783
784 if (_state < STATE_FLUSHING)
785 {
786 _state = STATE_FLUSHING;
787 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
788 _needEOC = true;
789 }
790
791 flushBuffer();
792 }
793
794
795 @Override
796 public long flushBuffer() throws IOException
797 {
798 try
799 {
800 if (_state == STATE_HEADER)
801 throw new IllegalStateException("State==HEADER");
802
803 prepareBuffers();
804
805 if (_endp == null)
806 {
807 if (_needCRLF && _buffer!=null)
808 _buffer.put(HttpTokens.CRLF);
809 if (_needEOC && _buffer!=null && !_head)
810 _buffer.put(LAST_CHUNK);
811 _needCRLF=false;
812 _needEOC=false;
813 return 0;
814 }
815
816 int total= 0;
817
818 int len = -1;
819 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);
820 switch (to_flush)
821 {
822 case 7:
823 throw new IllegalStateException();
824 case 6:
825 len = _endp.flush(_header, _buffer, null);
826 break;
827 case 5:
828 len = _endp.flush(_header, _content, null);
829 break;
830 case 4:
831 len = _endp.flush(_header);
832 break;
833 case 3:
834 throw new IllegalStateException();
835 case 2:
836 len = _endp.flush(_buffer);
837 break;
838 case 1:
839 len = _endp.flush(_content);
840 break;
841 case 0:
842 {
843
844 if (_header != null)
845 _header.clear();
846
847 _bypass = false;
848 _bufferChunked = false;
849
850 if (_buffer != null)
851 {
852 _buffer.clear();
853 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
854 {
855
856 _buffer.setPutIndex(CHUNK_SPACE);
857 _buffer.setGetIndex(CHUNK_SPACE);
858
859
860
861 if (_content != null && _content.length() < _buffer.space() && _state != STATE_FLUSHING)
862 {
863 _buffer.put(_content);
864 _content.clear();
865 _content = null;
866 }
867 }
868 }
869
870
871 if (!_needCRLF && !_needEOC && (_content == null || _content.length() == 0))
872 {
873 if (_state == STATE_FLUSHING)
874 _state = STATE_END;
875 if (_state==STATE_END && _close && _status!=100)
876 _endp.close();
877 }
878 else
879
880 prepareBuffers();
881 }
882 }
883
884 if (len > 0)
885 total+=len;
886
887 return total;
888 }
889 catch (IOException e)
890 {
891 Log.ignore(e);
892 throw (e instanceof EofException) ? e:new EofException(e);
893 }
894 }
895
896
897 private void prepareBuffers()
898 {
899
900 if (!_bufferChunked)
901 {
902
903 if (_content != null && _content.length() > 0 && _buffer != null && _buffer.space() > 0)
904 {
905 int len = _buffer.put(_content);
906 _content.skip(len);
907 if (_content.length() == 0)
908 _content = null;
909 }
910
911
912 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
913 {
914 int size = _buffer == null ? 0 : _buffer.length();
915 if (size > 0)
916 {
917
918 _bufferChunked = true;
919
920
921
922 if (_buffer.getIndex() == CHUNK_SPACE)
923 {
924
925 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
926 _buffer.setGetIndex(_buffer.getIndex() - 2);
927 BufferUtil.prependHexInt(_buffer, size);
928
929 if (_needCRLF)
930 {
931 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
932 _buffer.setGetIndex(_buffer.getIndex() - 2);
933 _needCRLF = false;
934 }
935 }
936 else
937 {
938
939 if (_needCRLF)
940 {
941 if (_header.length() > 0) throw new IllegalStateException("EOC");
942 _header.put(HttpTokens.CRLF);
943 _needCRLF = false;
944 }
945 BufferUtil.putHexInt(_header, size);
946 _header.put(HttpTokens.CRLF);
947 }
948
949
950 if (_buffer.space() >= 2)
951 _buffer.put(HttpTokens.CRLF);
952 else
953 _needCRLF = true;
954 }
955
956
957 if (_needEOC && (_content == null || _content.length() == 0))
958 {
959 if (_needCRLF)
960 {
961 if (_buffer == null && _header.space() >= 2)
962 {
963 _header.put(HttpTokens.CRLF);
964 _needCRLF = false;
965 }
966 else if (_buffer!=null && _buffer.space() >= 2)
967 {
968 _buffer.put(HttpTokens.CRLF);
969 _needCRLF = false;
970 }
971 }
972
973 if (!_needCRLF && _needEOC)
974 {
975 if (_buffer == null && _header.space() >= LAST_CHUNK.length)
976 {
977 if (!_head)
978 {
979 _header.put(LAST_CHUNK);
980 _bufferChunked=true;
981 }
982 _needEOC = false;
983 }
984 else if (_buffer!=null && _buffer.space() >= LAST_CHUNK.length)
985 {
986 if (!_head)
987 {
988 _buffer.put(LAST_CHUNK);
989 _bufferChunked=true;
990 }
991 _needEOC = false;
992 }
993 }
994 }
995 }
996 }
997
998 if (_content != null && _content.length() == 0)
999 _content = null;
1000
1001 }
1002
1003 public int getBytesBuffered()
1004 {
1005 return(_header==null?0:_header.length())+
1006 (_buffer==null?0:_buffer.length())+
1007 (_content==null?0:_content.length());
1008 }
1009
1010 public boolean isEmpty()
1011 {
1012 return (_header==null||_header.length()==0) &&
1013 (_buffer==null||_buffer.length()==0) &&
1014 (_content==null||_content.length()==0);
1015 }
1016
1017 @Override
1018 public String toString()
1019 {
1020 return "HttpGenerator s="+_state+
1021 " h="+(_header==null?"null":_header.length())+
1022 " b="+(_buffer==null?"null":_buffer.length())+
1023 " c="+(_content==null?"null":_content.length());
1024 }
1025 }