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 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 || _bufferChunked)
170 throw new IllegalStateException("FULL");
171 }
172
173 _content = content;
174 _contentWritten += content.length();
175
176
177 if (_head)
178 {
179 content.clear();
180 _content=null;
181 }
182 else if (_endp != null && _buffer == null && content.length() > 0 && _last)
183 {
184
185
186 _bypass = true;
187 }
188 else
189 {
190
191 if (_buffer == null)
192 _buffer = _buffers.getBuffer();
193
194
195 int len=_buffer.put(_content);
196 _content.skip(len);
197 if (_content.length() == 0)
198 _content = null;
199 }
200 }
201
202
203
204
205
206
207
208 public void sendResponse(Buffer response) throws IOException
209 {
210 if (_noContent || _state!=STATE_HEADER || _content!=null && _content.length()>0 || _bufferChunked || _head )
211 throw new IllegalStateException();
212
213 _last = true;
214
215 _content = response;
216 _bypass = true;
217 _state = STATE_FLUSHING;
218
219
220 _contentLength =_contentWritten = response.length();
221
222 }
223
224
225
226
227
228
229
230
231
232 public boolean addContent(byte b) throws IOException
233 {
234 if (_noContent)
235 throw new IllegalStateException("NO CONTENT");
236
237 if (_last || _state==STATE_END)
238 {
239 Log.debug("Ignoring extra content {}",Byte.valueOf(b));
240 return false;
241 }
242
243
244 if (_content != null && _content.length()>0 || _bufferChunked)
245 {
246 flushBuffer();
247 if (_content != null && _content.length()>0 || _bufferChunked)
248 throw new IllegalStateException("FULL");
249 }
250
251 _contentWritten++;
252
253
254 if (_head)
255 return false;
256
257
258 if (_buffer == null)
259 _buffer = _buffers.getBuffer();
260
261
262 _buffer.put(b);
263
264 return _buffer.space()<=(_contentLength == HttpTokens.CHUNKED_CONTENT?CHUNK_SPACE:0);
265 }
266
267
268
269
270
271
272
273 public int prepareUncheckedAddContent() throws IOException
274 {
275 if (_noContent)
276 return -1;
277
278 if (_last || _state==STATE_END)
279 return -1;
280
281
282 Buffer content = _content;
283 if (content != null && content.length()>0 || _bufferChunked)
284 {
285 flushBuffer();
286 if (content != null && content.length()>0 || _bufferChunked)
287 throw new IllegalStateException("FULL");
288 }
289
290
291 if (_buffer == null)
292 _buffer = _buffers.getBuffer();
293
294 _contentWritten-=_buffer.length();
295
296
297 if (_head)
298 return Integer.MAX_VALUE;
299
300 return _buffer.space()-(_contentLength == HttpTokens.CHUNKED_CONTENT?CHUNK_SPACE:0);
301 }
302
303
304 public boolean isBufferFull()
305 {
306
307 return super.isBufferFull() || _bufferChunked || _bypass || (_contentLength == HttpTokens.CHUNKED_CONTENT && _buffer != null && _buffer.space() < CHUNK_SPACE);
308 }
309
310
311 public void send1xx(int code) throws IOException
312 {
313 if (_state != STATE_HEADER)
314 return;
315
316 if (code<100||code>199)
317 throw new IllegalArgumentException("!1xx");
318 Status status=__status[code];
319 if (status==null)
320 throw new IllegalArgumentException(code+"?");
321
322
323 if (_header == null)
324 _header = _buffers.getHeader();
325
326 _header.put(status._responseLine);
327 _header.put(HttpTokens.CRLF);
328
329 try
330 {
331
332 while(_header.length()>0)
333 {
334 int len = _endp.flush(_header);
335 if (len<0)
336 throw new EofException();
337 if (len==0)
338 Thread.sleep(100);
339 }
340 }
341 catch(InterruptedException e)
342 {
343 Log.debug(e);
344 throw new InterruptedIOException(e.toString());
345 }
346
347 }
348
349
350 public void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException
351 {
352 if (_state != STATE_HEADER)
353 return;
354
355
356 if (_method==null && _status==0)
357 throw new EofException();
358
359 if (_last && !allContentAdded)
360 throw new IllegalStateException("last?");
361 _last = _last | allContentAdded;
362
363
364 if (_header == null)
365 _header = _buffers.getHeader();
366
367 boolean has_server = false;
368
369 if (_method!=null)
370 {
371 _close = false;
372
373 if (_version == HttpVersions.HTTP_0_9_ORDINAL)
374 {
375 _contentLength = HttpTokens.NO_CONTENT;
376 _header.put(_method);
377 _header.put((byte)' ');
378 _header.put(_uri.getBytes("utf-8"));
379 _header.put(HttpTokens.CRLF);
380 _state = STATE_FLUSHING;
381 _noContent=true;
382 return;
383 }
384 else
385 {
386 _header.put(_method);
387 _header.put((byte)' ');
388 _header.put(_uri.getBytes("utf-8"));
389 _header.put((byte)' ');
390 _header.put(_version==HttpVersions.HTTP_1_0_ORDINAL?HttpVersions.HTTP_1_0_BUFFER:HttpVersions.HTTP_1_1_BUFFER);
391 _header.put(HttpTokens.CRLF);
392 }
393 }
394 else
395 {
396
397 if (_version == HttpVersions.HTTP_0_9_ORDINAL)
398 {
399 _close = true;
400 _contentLength = HttpTokens.EOF_CONTENT;
401 _state = STATE_CONTENT;
402 return;
403 }
404 else
405 {
406 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
407 _close = true;
408
409
410 Status status = _status<__status.length?__status[_status]:null;
411
412 if (status==null)
413 {
414 _header.put(HttpVersions.HTTP_1_1_BUFFER);
415 _header.put((byte) ' ');
416 _header.put((byte) ('0' + _status / 100));
417 _header.put((byte) ('0' + (_status % 100) / 10));
418 _header.put((byte) ('0' + (_status % 10)));
419 _header.put((byte) ' ');
420 if (_reason==null)
421 {
422 _header.put((byte) ('0' + _status / 100));
423 _header.put((byte) ('0' + (_status % 100) / 10));
424 _header.put((byte) ('0' + (_status % 10)));
425 }
426 else
427 _header.put(_reason);
428 _header.put(HttpTokens.CRLF);
429 }
430 else
431 {
432 if (_reason==null)
433 _header.put(status._responseLine);
434 else
435 {
436 _header.put(status._schemeCode);
437 _header.put(_reason);
438 _header.put(HttpTokens.CRLF);
439 }
440 }
441
442 if (_status<200 && _status>=100 )
443 {
444 _noContent=true;
445 _content=null;
446 if (_buffer!=null)
447 _buffer.clear();
448
449 _header.put(HttpTokens.CRLF);
450 _state = STATE_CONTENT;
451 return;
452 }
453
454 if (_status==204 || _status==304)
455 {
456 _noContent=true;
457 _content=null;
458 if (_buffer!=null)
459 _buffer.clear();
460 }
461 }
462 }
463
464
465
466
467 HttpFields.Field content_length = null;
468 HttpFields.Field transfer_encoding = null;
469 boolean keep_alive = false;
470 boolean close=false;
471 StringBuilder connection = null;
472
473 if (fields != null)
474 {
475 int s=fields.size();
476 for (int f=0;f<s;f++)
477 {
478 HttpFields.Field field = fields.getField(f);
479 if (field==null)
480 continue;
481 switch (field.getNameOrdinal())
482 {
483 case HttpHeaders.CONTENT_LENGTH_ORDINAL:
484 content_length = field;
485 _contentLength = field.getLongValue();
486
487 if (_contentLength < _contentWritten || _last && _contentLength != _contentWritten)
488 content_length = null;
489
490
491 field.put(_header);
492 break;
493
494 case HttpHeaders.CONTENT_TYPE_ORDINAL:
495 if (BufferUtil.isPrefix(MimeTypes.MULTIPART_BYTERANGES_BUFFER, field.getValueBuffer())) _contentLength = HttpTokens.SELF_DEFINING_CONTENT;
496
497
498 field.put(_header);
499 break;
500
501 case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
502 if (_version == HttpVersions.HTTP_1_1_ORDINAL) transfer_encoding = field;
503
504 break;
505
506 case HttpHeaders.CONNECTION_ORDINAL:
507 if (_method!=null)
508 field.put(_header);
509
510 int connection_value = field.getValueOrdinal();
511 switch (connection_value)
512 {
513 case -1:
514 {
515 String[] values = field.getValue().split(",");
516 for (int i=0;values!=null && i<values.length;i++)
517 {
518 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim());
519
520 if (cb!=null)
521 {
522 switch(cb.getOrdinal())
523 {
524 case HttpHeaderValues.CLOSE_ORDINAL:
525 close=true;
526 if (_method==null)
527 _close=true;
528 keep_alive=false;
529 if (_close && _method==null && _contentLength == HttpTokens.UNKNOWN_CONTENT)
530 _contentLength = HttpTokens.EOF_CONTENT;
531 break;
532
533 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
534 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
535 {
536 keep_alive = true;
537 if (_method==null)
538 _close = false;
539 }
540 break;
541
542 default:
543 if (connection==null)
544 connection=new StringBuilder();
545 else
546 connection.append(',');
547 connection.append(values[i]);
548 }
549 }
550 else
551 {
552 if (connection==null)
553 connection=new StringBuilder();
554 else
555 connection.append(',');
556 connection.append(values[i]);
557 }
558 }
559
560 break;
561 }
562 case HttpHeaderValues.CLOSE_ORDINAL:
563 {
564 close=true;
565 if (_method==null)
566 _close=true;
567 if (_close && _method==null && _contentLength == HttpTokens.UNKNOWN_CONTENT)
568 _contentLength = HttpTokens.EOF_CONTENT;
569 break;
570 }
571 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
572 {
573 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
574 {
575 keep_alive = true;
576 if (_method==null)
577 _close = false;
578 }
579 break;
580 }
581 default:
582 {
583 if (connection==null)
584 connection=new StringBuilder();
585 else
586 connection.append(',');
587 connection.append(field.getValue());
588 }
589 }
590
591
592 break;
593
594 case HttpHeaders.SERVER_ORDINAL:
595 if (getSendServerVersion())
596 {
597 has_server=true;
598 field.put(_header);
599 }
600 break;
601
602 default:
603
604 field.put(_header);
605 }
606 }
607 }
608
609
610
611
612
613
614
615
616
617
618 switch ((int) _contentLength)
619 {
620 case HttpTokens.UNKNOWN_CONTENT:
621
622
623
624
625 if (_contentWritten == 0 && _method==null && (_status < 200 || _status == 204 || _status == 304))
626 _contentLength = HttpTokens.NO_CONTENT;
627 else if (_last)
628 {
629
630 _contentLength = _contentWritten;
631 if (content_length == null)
632 {
633
634 _header.put(HttpHeaders.CONTENT_LENGTH_BUFFER);
635 _header.put(HttpTokens.COLON);
636 _header.put((byte) ' ');
637 BufferUtil.putDecLong(_header, _contentLength);
638 _header.put(HttpTokens.CRLF);
639 }
640 }
641 else
642 {
643
644 _contentLength = (_close || _version < HttpVersions.HTTP_1_1_ORDINAL ) ? HttpTokens.EOF_CONTENT : HttpTokens.CHUNKED_CONTENT;
645 if (_method!=null && _contentLength==HttpTokens.EOF_CONTENT)
646 {
647 _contentLength=HttpTokens.NO_CONTENT;
648 _noContent=true;
649 }
650 }
651 break;
652
653 case HttpTokens.NO_CONTENT:
654 if (content_length == null && _method==null && _status >= 200 && _status != 204 && _status != 304)
655 _header.put(CONTENT_LENGTH_0);
656 break;
657
658 case HttpTokens.EOF_CONTENT:
659 _close = _method==null;
660 break;
661
662 case HttpTokens.CHUNKED_CONTENT:
663 break;
664
665 default:
666
667 break;
668 }
669
670
671 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
672 {
673
674 if (transfer_encoding != null && HttpHeaderValues.CHUNKED_ORDINAL != transfer_encoding.getValueOrdinal())
675 {
676 String c = transfer_encoding.getValue();
677 if (c.endsWith(HttpHeaderValues.CHUNKED))
678 transfer_encoding.put(_header);
679 else
680 throw new IllegalArgumentException("BAD TE");
681 }
682 else
683 _header.put(TRANSFER_ENCODING_CHUNKED);
684 }
685
686
687 if (_contentLength==HttpTokens.EOF_CONTENT)
688 {
689 keep_alive=false;
690 _close=true;
691 }
692
693 if (_method==null)
694 {
695 if (_close && (close || _version > HttpVersions.HTTP_1_0_ORDINAL))
696 {
697 _header.put(CONNECTION_CLOSE);
698 if (connection!=null)
699 {
700 _header.setPutIndex(_header.putIndex()-2);
701 _header.put((byte)',');
702 _header.put(connection.toString().getBytes());
703 _header.put(CRLF);
704 }
705 }
706 else if (keep_alive)
707 {
708 _header.put(CONNECTION_KEEP_ALIVE);
709 if (connection!=null)
710 {
711 _header.setPutIndex(_header.putIndex()-2);
712 _header.put((byte)',');
713 _header.put(connection.toString().getBytes());
714 _header.put(CRLF);
715 }
716 }
717 else if (connection!=null)
718 {
719 _header.put(CONNECTION_);
720 _header.put(connection.toString().getBytes());
721 _header.put(CRLF);
722 }
723 }
724
725 if (!has_server && _status>199 && getSendServerVersion())
726 _header.put(SERVER);
727
728
729 _header.put(HttpTokens.CRLF);
730
731 _state = STATE_CONTENT;
732
733 }
734
735
736
737
738
739
740
741
742
743 public void complete() throws IOException
744 {
745 if (_state == STATE_END)
746 return;
747
748 super.complete();
749
750 if (_state < STATE_FLUSHING)
751 {
752 _state = STATE_FLUSHING;
753 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
754 _needEOC = true;
755 }
756
757 flushBuffer();
758 }
759
760
761 public long flushBuffer() throws IOException
762 {
763 try
764 {
765 if (_state == STATE_HEADER)
766 throw new IllegalStateException("State==HEADER");
767
768 prepareBuffers();
769
770 if (_endp == null)
771 {
772 if (_needCRLF && _buffer!=null)
773 _buffer.put(HttpTokens.CRLF);
774 if (_needEOC && _buffer!=null && !_head)
775 _buffer.put(LAST_CHUNK);
776 _needCRLF=false;
777 _needEOC=false;
778 return 0;
779 }
780
781 int total= 0;
782
783 int len = -1;
784 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);
785 switch (to_flush)
786 {
787 case 7:
788 throw new IllegalStateException();
789 case 6:
790 len = _endp.flush(_header, _buffer, null);
791 break;
792 case 5:
793 len = _endp.flush(_header, _content, null);
794 break;
795 case 4:
796 len = _endp.flush(_header);
797 break;
798 case 3:
799 throw new IllegalStateException();
800 case 2:
801 len = _endp.flush(_buffer);
802 break;
803 case 1:
804 len = _endp.flush(_content);
805 break;
806 case 0:
807 {
808
809 if (_header != null)
810 _header.clear();
811
812 _bypass = false;
813 _bufferChunked = false;
814
815 if (_buffer != null)
816 {
817 _buffer.clear();
818 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
819 {
820
821 _buffer.setPutIndex(CHUNK_SPACE);
822 _buffer.setGetIndex(CHUNK_SPACE);
823
824
825
826 if (_content != null && _content.length() < _buffer.space() && _state != STATE_FLUSHING)
827 {
828 _buffer.put(_content);
829 _content.clear();
830 _content = null;
831 }
832 }
833 }
834
835
836 if (!_needCRLF && !_needEOC && (_content == null || _content.length() == 0))
837 {
838 if (_state == STATE_FLUSHING)
839 _state = STATE_END;
840 if (_state==STATE_END && _close && _status!=100)
841 _endp.close();
842 }
843 else
844
845 prepareBuffers();
846 }
847 }
848
849 if (len > 0)
850 total+=len;
851
852 return total;
853 }
854 catch (IOException e)
855 {
856 Log.ignore(e);
857 throw (e instanceof EofException) ? e:new EofException(e);
858 }
859 }
860
861
862 private void prepareBuffers()
863 {
864
865 if (!_bufferChunked)
866 {
867
868 if (_content != null && _content.length() > 0 && _buffer != null && _buffer.space() > 0)
869 {
870 int len = _buffer.put(_content);
871 _content.skip(len);
872 if (_content.length() == 0)
873 _content = null;
874 }
875
876
877 if (_contentLength == HttpTokens.CHUNKED_CONTENT)
878 {
879 int size = _buffer == null ? 0 : _buffer.length();
880 if (size > 0)
881 {
882
883 _bufferChunked = true;
884
885
886
887 if (_buffer.getIndex() == CHUNK_SPACE)
888 {
889
890 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
891 _buffer.setGetIndex(_buffer.getIndex() - 2);
892 BufferUtil.prependHexInt(_buffer, size);
893
894 if (_needCRLF)
895 {
896 _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
897 _buffer.setGetIndex(_buffer.getIndex() - 2);
898 _needCRLF = false;
899 }
900 }
901 else
902 {
903
904 if (_needCRLF)
905 {
906 if (_header.length() > 0) throw new IllegalStateException("EOC");
907 _header.put(HttpTokens.CRLF);
908 _needCRLF = false;
909 }
910 BufferUtil.putHexInt(_header, size);
911 _header.put(HttpTokens.CRLF);
912 }
913
914
915 if (_buffer.space() >= 2)
916 _buffer.put(HttpTokens.CRLF);
917 else
918 _needCRLF = true;
919 }
920
921
922 if (_needEOC && (_content == null || _content.length() == 0))
923 {
924 if (_needCRLF)
925 {
926 if (_buffer == null && _header.space() >= 2)
927 {
928 _header.put(HttpTokens.CRLF);
929 _needCRLF = false;
930 }
931 else if (_buffer!=null && _buffer.space() >= 2)
932 {
933 _buffer.put(HttpTokens.CRLF);
934 _needCRLF = false;
935 }
936 }
937
938 if (!_needCRLF && _needEOC)
939 {
940 if (_buffer == null && _header.space() >= LAST_CHUNK.length)
941 {
942 if (!_head)
943 {
944 _header.put(LAST_CHUNK);
945 _bufferChunked=true;
946 }
947 _needEOC = false;
948 }
949 else if (_buffer!=null && _buffer.space() >= LAST_CHUNK.length)
950 {
951 if (!_head)
952 {
953 _buffer.put(LAST_CHUNK);
954 _bufferChunked=true;
955 }
956 _needEOC = false;
957 }
958 }
959 }
960 }
961 }
962
963 if (_content != null && _content.length() == 0)
964 _content = null;
965
966 }
967
968 public int getBytesBuffered()
969 {
970 return(_header==null?0:_header.length())+
971 (_buffer==null?0:_buffer.length())+
972 (_content==null?0:_content.length());
973 }
974
975 public boolean isEmpty()
976 {
977 return (_header==null||_header.length()==0) &&
978 (_buffer==null||_buffer.length()==0) &&
979 (_content==null||_content.length()==0);
980 }
981
982 public String toString()
983 {
984 return "HttpGenerator s="+_state+
985 " h="+(_header==null?"null":_header.length())+
986 " b="+(_buffer==null?"null":_buffer.length())+
987 " c="+(_content==null?"null":_content.length());
988 }
989 }