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 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 && content.length() > 0 && _last)
188 {
189
190
191 _bypass = true;
192 }
193 else if (!_bufferChunked)
194 {
195
196 if (_buffer == null)
197 _buffer = _buffers.getBuffer();
198
199
200 int len=_buffer.put(_content);
201 _content.skip(len);
202 if (_content.length() == 0)
203 _content = null;
204 }
205 }
206
207
208
209
210
211
212
213 public void sendResponse(Buffer response) throws IOException
214 {
215 if (_noContent || _state!=STATE_HEADER || _content!=null && _content.length()>0 || _bufferChunked || _head )
216 throw new IllegalStateException();
217
218 _last = true;
219
220 _content = response;
221 _bypass = true;
222 _state = STATE_FLUSHING;
223
224
225 _contentLength =_contentWritten = response.length();
226
227 }
228
229
230
231
232
233
234
235
236
237 public boolean addContent(byte b) throws IOException
238 {
239 if (_noContent)
240 throw new IllegalStateException("NO CONTENT");
241
242 if (_last || _state==STATE_END)
243 {
244 Log.debug("Ignoring extra content {}",Byte.valueOf(b));
245 return false;
246 }
247
248
249 if (_content != null && _content.length()>0 || _bufferChunked)
250 {
251 flushBuffer();
252 if (_content != null && _content.length()>0 || _bufferChunked)
253 throw new IllegalStateException("FULL");
254 }
255
256 _contentWritten++;
257
258
259 if (_head)
260 return false;
261
262
263 if (_buffer == null)
264 _buffer = _buffers.getBuffer();
265
266
267 _buffer.put(b);
268
269 return _buffer.space()<=(_contentLength == HttpTokens.CHUNKED_CONTENT?CHUNK_SPACE:0);
270 }
271
272
273
274
275
276
277
278 @Override
279 public int prepareUncheckedAddContent() throws IOException
280 {
281 if (_noContent)
282 return -1;
283
284 if (_last || _state==STATE_END)
285 return -1;
286
287
288 Buffer content = _content;
289 if (content != null && content.length()>0 || _bufferChunked)
290 {
291 flushBuffer();
292 if (content != null && content.length()>0 || _bufferChunked)
293 throw new IllegalStateException("FULL");
294 }
295
296
297 if (_buffer == null)
298 _buffer = _buffers.getBuffer();
299
300 _contentWritten-=_buffer.length();
301
302
303 if (_head)
304 return Integer.MAX_VALUE;
305
306 return _buffer.space()-(_contentLength == HttpTokens.CHUNKED_CONTENT?CHUNK_SPACE:0);
307 }
308
309
310 @Override
311 public boolean isBufferFull()
312 {
313
314 return super.isBufferFull() || _bufferChunked || _bypass || (_contentLength == HttpTokens.CHUNKED_CONTENT && _buffer != null && _buffer.space() < CHUNK_SPACE);
315 }
316
317
318 public void send1xx(int code) throws IOException
319 {
320 if (_state != STATE_HEADER)
321 return;
322
323 if (code<100||code>199)
324 throw new IllegalArgumentException("!1xx");
325 Status status=__status[code];
326 if (status==null)
327 throw new IllegalArgumentException(code+"?");
328
329
330 if (_header == null)
331 _header = _buffers.getHeader();
332
333 _header.put(status._responseLine);
334 _header.put(HttpTokens.CRLF);
335
336 try
337 {
338
339 while(_header.length()>0)
340 {
341 int len = _endp.flush(_header);
342 if (len<0)
343 throw new EofException();
344 if (len==0)
345 Thread.sleep(100);
346 }
347 }
348 catch(InterruptedException e)
349 {
350 Log.debug(e);
351 throw new InterruptedIOException(e.toString());
352 }
353 }
354
355
356 @Override
357 public boolean isRequest()
358 {
359 return _method!=null;
360 }
361
362
363 @Override
364 public boolean isResponse()
365 {
366 return _method==null;
367 }
368
369
370 @Override
371 public void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException
372 {
373 if (_state != STATE_HEADER)
374 return;
375
376
377 if (isResponse() && _status==0)
378 throw new EofException();
379
380 if (_last && !allContentAdded)
381 throw new IllegalStateException("last?");
382 _last = _last | allContentAdded;
383
384
385 if (_header == null)
386 _header = _buffers.getHeader();
387
388 boolean has_server = false;
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
781
782
783
784
785
786
787
788
789 @Override
790 public void complete() throws IOException
791 {
792 if (_state == STATE_END)
793 return;
794
795 super.complete();
796
797 if (_state < STATE_FLUSHING)
798 {
799 _state = STATE_FLUSHING;
800 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
801 _needEOC = true;
802 }
803
804 flushBuffer();
805 }
806
807
808 @Override
809 public long flushBuffer() throws IOException
810 {
811 try
812 {
813 if (_state == STATE_HEADER)
814 throw new IllegalStateException("State==HEADER");
815
816 prepareBuffers();
817
818 if (_endp == null)
819 {
820 if (_needCRLF && _buffer!=null)
821 _buffer.put(HttpTokens.CRLF);
822 if (_needEOC && _buffer!=null && !_head)
823 _buffer.put(LAST_CHUNK);
824 _needCRLF=false;
825 _needEOC=false;
826 return 0;
827 }
828
829 int total= 0;
830
831 int len = -1;
832 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);
833 switch (to_flush)
834 {
835 case 7:
836 throw new IllegalStateException();
837 case 6:
838 len = _endp.flush(_header, _buffer, null);
839 break;
840 case 5:
841 len = _endp.flush(_header, _content, null);
842 break;
843 case 4:
844 len = _endp.flush(_header);
845 break;
846 case 3:
847 throw new IllegalStateException();
848 case 2:
849 len = _endp.flush(_buffer);
850 break;
851 case 1:
852 len = _endp.flush(_content);
853 break;
854 case 0:
855 {
856
857 if (_header != null)
858 _header.clear();
859
860 _bypass = false;
861 _bufferChunked = false;
862
863 if (_buffer != null)
864 {
865 _buffer.clear();
866 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
867 {
868
869 _buffer.setPutIndex(CHUNK_SPACE);
870 _buffer.setGetIndex(CHUNK_SPACE);
871
872
873
874 if (_content != null && _content.length() < _buffer.space() && _state != STATE_FLUSHING)
875 {
876 _buffer.put(_content);
877 _content.clear();
878 _content = null;
879 }
880 }
881 }
882
883
884 if (!_needCRLF && !_needEOC && (_content == null || _content.length() == 0))
885 {
886 if (_state == STATE_FLUSHING)
887 _state = STATE_END;
888 if (_state==STATE_END && !_persistent && _status!=100)
889 _endp.close();
890 }
891 else
892
893 prepareBuffers();
894 }
895 }
896
897 if (len > 0)
898 total+=len;
899
900 return total;
901 }
902 catch (IOException e)
903 {
904 Log.ignore(e);
905 throw (e instanceof EofException) ? e:new EofException(e);
906 }
907 }
908
909
910 private void prepareBuffers()
911 {
912
913 if (!_bufferChunked)
914 {
915
916 if (_content != null && _content.length() > 0 && _buffer != null && _buffer.space() > 0)
917 {
918 int len = _buffer.put(_content);
919 _content.skip(len);
920 if (_content.length() == 0)
921 _content = null;
922 }
923
924
925 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
926 {
927 int size = _buffer == null ? 0 : _buffer.length();
928 if (size > 0)
929 {
930
931 _bufferChunked = true;
932
933
934
935 if (_buffer.getIndex() == CHUNK_SPACE)
936 {
937
938 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
939 _buffer.setGetIndex(_buffer.getIndex() - 2);
940 BufferUtil.prependHexInt(_buffer, size);
941
942 if (_needCRLF)
943 {
944 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
945 _buffer.setGetIndex(_buffer.getIndex() - 2);
946 _needCRLF = false;
947 }
948 }
949 else
950 {
951
952 if (_needCRLF)
953 {
954 if (_header.length() > 0) throw new IllegalStateException("EOC");
955 _header.put(HttpTokens.CRLF);
956 _needCRLF = false;
957 }
958 BufferUtil.putHexInt(_header, size);
959 _header.put(HttpTokens.CRLF);
960 }
961
962
963 if (_buffer.space() >= 2)
964 _buffer.put(HttpTokens.CRLF);
965 else
966 _needCRLF = true;
967 }
968
969
970 if (_needEOC && (_content == null || _content.length() == 0))
971 {
972 if (_needCRLF)
973 {
974 if (_buffer == null && _header.space() >= 2)
975 {
976 _header.put(HttpTokens.CRLF);
977 _needCRLF = false;
978 }
979 else if (_buffer!=null && _buffer.space() >= 2)
980 {
981 _buffer.put(HttpTokens.CRLF);
982 _needCRLF = false;
983 }
984 }
985
986 if (!_needCRLF && _needEOC)
987 {
988 if (_buffer == null && _header.space() >= LAST_CHUNK.length)
989 {
990 if (!_head)
991 {
992 _header.put(LAST_CHUNK);
993 _bufferChunked=true;
994 }
995 _needEOC = false;
996 }
997 else if (_buffer!=null && _buffer.space() >= LAST_CHUNK.length)
998 {
999 if (!_head)
1000 {
1001 _buffer.put(LAST_CHUNK);
1002 _bufferChunked=true;
1003 }
1004 _needEOC = false;
1005 }
1006 }
1007 }
1008 }
1009 }
1010
1011 if (_content != null && _content.length() == 0)
1012 _content = null;
1013
1014 }
1015
1016 public int getBytesBuffered()
1017 {
1018 return(_header==null?0:_header.length())+
1019 (_buffer==null?0:_buffer.length())+
1020 (_content==null?0:_content.length());
1021 }
1022
1023 public boolean isEmpty()
1024 {
1025 return (_header==null||_header.length()==0) &&
1026 (_buffer==null||_buffer.length()==0) &&
1027 (_content==null||_content.length()==0);
1028 }
1029
1030 @Override
1031 public String toString()
1032 {
1033 return "HttpGenerator s="+_state+
1034 " h="+(_header==null?"null":_header.length())+
1035 " b="+(_buffer==null?"null":_buffer.length())+
1036 " c="+(_content==null?"null":_content.length());
1037 }
1038 }