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