View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses.
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   * HttpGenerator. Builds HTTP Messages.
32   *
33   *
34   *
35   */
36  public class HttpGenerator extends AbstractGenerator
37  {
38      // Build cache of response lines for status
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      // common _content
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      // other statics
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     // data
105     private boolean _bypass = false; // True if _content buffer can be written directly to endp and bypass the content buffer
106     private boolean _needCRLF = false;
107     private boolean _needEOC = false;
108     private boolean _bufferChunked = false;
109 
110 
111     /* ------------------------------------------------------------------------------- */
112     /**
113      * Constructor.
114      *
115      * @param buffers buffer pool
116      * @param io the end point to use
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      * Add content.
142      *
143      * @param content
144      * @param last
145      * @throws IllegalArgumentException if <code>content</code> is {@link Buffer#isImmutable immutable}.
146      * @throws IllegalStateException If the request is not expecting any more content,
147      *   or if the buffers are full and cannot be flushed.
148      * @throws IOException if there is a problem flushing the buffers.
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         // Handle any unfinished business?
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         // Handle the _content
182         if (_head)
183         {
184             content.clear();
185             _content=null;
186         }
187         else if (_endp != null && _buffer == null && content.length() > 0 && _last)
188         {
189             // TODO - use bypass in more cases.
190             // Make _content a direct buffer
191             _bypass = true;
192         }
193         else if (!_bufferChunked)
194         {
195             // Yes - so we better check we have a buffer
196             if (_buffer == null)
197                 _buffer = _buffers.getBuffer();
198 
199             // Copy _content to buffer;
200             int len=_buffer.put(_content);
201             _content.skip(len);
202             if (_content.length() == 0)
203                 _content = null;
204         }
205     }
206 
207     /* ------------------------------------------------------------ */
208     /**
209      * send complete response.
210      *
211      * @param response
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         // TODO this is not exactly right, but should do.
225         _contentLength =_contentWritten = response.length();
226 
227     }
228 
229     /* ------------------------------------------------------------ */
230     /**
231      * Add content.
232      *
233      * @param b byte
234      * @return true if the buffers are full
235      * @throws IOException
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         // Handle any unfinished business?
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         // Handle the _content
259         if (_head)
260             return false;
261 
262         // we better check we have a buffer
263         if (_buffer == null)
264             _buffer = _buffers.getBuffer();
265 
266         // Copy _content to buffer;
267         _buffer.put(b);
268 
269         return _buffer.space()<=(_contentLength == HttpTokens.CHUNKED_CONTENT?CHUNK_SPACE:0);
270     }
271 
272     /* ------------------------------------------------------------ */
273     /** Prepare buffer for unchecked writes.
274      * Prepare the generator buffer to receive unchecked writes
275      * @return the available space in the buffer.
276      * @throws IOException
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         // Handle any unfinished business?
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         // we better check we have a buffer
297         if (_buffer == null)
298             _buffer = _buffers.getBuffer();
299 
300         _contentWritten-=_buffer.length();
301 
302         // Handle the _content
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         // Should we flush the buffers?
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         // get a header buffer
330         if (_header == null)
331             _header = _buffers.getHeader();
332 
333         _header.put(status._responseLine);
334         _header.put(HttpTokens.CRLF);
335 
336         try
337         {
338             // nasty semi busy flush!
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         // handle a reset
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         // get a header buffer
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")); // TODO WRONG!
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")); // TODO WRONG!
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                 // Responses
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                     // add response line
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                         // end the header.
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             // Add headers
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             // key field values
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                             // write the field to the header buffer
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                             // write the field to the header buffer
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                             // Do NOT add yet!
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                                     // special case for websocket connection ordering
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                             // Do NOT add yet!
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                             // write the field to the header buffer
652                             field.put(_header);
653                     }
654                 }
655             }
656 
657             // Calculate how to end _content and connection, _content length and transfer encoding
658             // settings.
659             // From RFC 2616 4.4:
660             // 1. No body for 1xx, 204, 304 & HEAD response
661             // 2. Force _content-length?
662             // 3. If Transfer-Encoding!=identity && HTTP/1.1 && !HttpConnection==close then chunk
663             // 4. Content-Length
664             // 5. multipart/byteranges
665             // 6. close
666             switch ((int) _contentLength)
667             {
668                 case HttpTokens.UNKNOWN_CONTENT:
669                     // It may be that we have no _content, or perhaps _content just has not been
670                     // written yet?
671 
672                     // Response known not to have a body
673                     if (_contentWritten == 0 && isResponse() && (_status < 200 || _status == 204 || _status == 304))
674                         _contentLength = HttpTokens.NO_CONTENT;
675                     else if (_last)
676                     {
677                         // we have seen all the _content there is
678                         _contentLength = _contentWritten;
679                         if (content_length == null && (isResponse() || _contentLength>0 || content_type ))
680                         {
681                             // known length but not actually set.
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                         // No idea, so we must assume that a body is coming
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                     // TODO - maybe allow forced chunking by setting te ???
715                     break;
716             }
717 
718             // Add transfer_encoding if needed
719             if (_contentLength == HttpTokens.CHUNKED_CONTENT)
720             {
721                 // try to use user supplied encoding as it may have other values.
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             // Handle connection if need be
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             // end the header.
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      * Complete the message.
793      *
794      * @throws IOException
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(); // should never happen!
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(); // should never happen!
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                     // Nothing more we can write now.
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                             // reserve some space for the chunk header
876                             _buffer.setPutIndex(CHUNK_SPACE);
877                             _buffer.setGetIndex(CHUNK_SPACE);
878 
879                             // Special case handling for small left over buffer from
880                             // an addContent that caused a buffer flush.
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                     // Are we completely finished for now?
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                         // Try to prepare more to write.
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         // if we are not flushing an existing chunk
920         if (!_bufferChunked)
921         {
922             // Refill buffer if possible
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             // Chunk buffer if need be
932             if (_contentLength == HttpTokens.CHUNKED_CONTENT)
933             {
934                 int size = _buffer == null ? 0 : _buffer.length();
935                 if (size > 0)
936                 {
937                     // Prepare a chunk!
938                     _bufferChunked = true;
939 
940                     // Did we leave space at the start of the buffer.
941                     //noinspection ConstantConditions
942                     if (_buffer.getIndex() == CHUNK_SPACE)
943                     {
944                         // Oh yes, goodie! let's use it then!
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                         // No space so lets use the header buffer.
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                     // Add end chunk trailer.
970                     if (_buffer.space() >= 2)
971                         _buffer.put(HttpTokens.CRLF);
972                     else
973                         _needCRLF = true;
974                 }
975 
976                 // If we need EOC and everything written
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 }