View Javadoc

1   //========================================================================
2   //$Id: HttpConnection.java,v 1.13 2005/11/25 21:01:45 gregwilkins Exp $
3   //Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
4   //------------------------------------------------------------------------
5   //Licensed under the Apache License, Version 2.0 (the "License");
6   //you may not use this file except in compliance with the License.
7   //You may obtain a copy of the License at
8   //http://www.apache.org/licenses/LICENSE-2.0
9   //Unless required by applicable law or agreed to in writing, software
10  //distributed under the License is distributed on an "AS IS" BASIS,
11  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  //See the License for the specific language governing permissions and
13  //limitations under the License.
14  //========================================================================
15  
16  package org.mortbay.jetty;
17  
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.io.PrintWriter;
21  
22  import javax.servlet.ServletInputStream;
23  import javax.servlet.ServletOutputStream;
24  import javax.servlet.http.HttpServletResponse;
25  
26  import org.mortbay.io.Buffer;
27  import org.mortbay.io.Connection;
28  import org.mortbay.io.EndPoint;
29  import org.mortbay.io.RuntimeIOException;
30  import org.mortbay.io.BufferCache.CachedBuffer;
31  import org.mortbay.io.nio.SelectChannelEndPoint;
32  import org.mortbay.log.Log;
33  import org.mortbay.resource.Resource;
34  import org.mortbay.util.QuotedStringTokenizer;
35  import org.mortbay.util.StringUtil;
36  import org.mortbay.util.URIUtil;
37  import org.mortbay.util.ajax.Continuation;
38  
39  /**
40   * <p>
41   * A HttpConnection represents the connection of a HTTP client to the server and
42   * is created by an instance of a {@link Connector}. It's prime function is to
43   * associate {@link Request} and {@link Response} instances with a
44   * {@link EndPoint}.
45   * </p>
46   * <p>
47   * A connection is also the prime mechanism used by jetty to recycle objects
48   * without pooling. The {@link Request},{@link Response}, {@link HttpParser},
49   * {@link HttpGenerator} and {@link HttpFields} instances are all recycled for
50   * the duration of a connection. Where appropriate, allocated buffers are also
51   * kept associated with the connection via the parser and/or generator.
52   * </p>
53   * 
54   * 
55   * @author gregw
56   * 
57   */
58  public class HttpConnection implements Connection
59  {
60      private static int UNKNOWN = -2;
61      private static ThreadLocal __currentConnection = new ThreadLocal();
62  
63      private long _timeStamp = System.currentTimeMillis();
64      private int _requests;
65      private boolean _handling;
66      private boolean _destroy;
67  
68      protected final Connector _connector;
69      protected final EndPoint _endp;
70      protected final Server _server;
71      protected final HttpURI _uri;
72  
73      protected final Parser _parser;
74      protected final HttpFields _requestFields;
75      protected final Request _request;
76      protected ServletInputStream _in;
77  
78      protected final Generator _generator;
79      protected final HttpFields _responseFields;
80      protected final Response _response;
81      protected Output _out;
82      protected OutputWriter _writer;
83      protected PrintWriter _printWriter;
84  
85      int _include;
86  
87      private Object _associatedObject; // associated object
88  
89      private transient int _expect = UNKNOWN;
90      private transient int _version = UNKNOWN;
91      private transient boolean _head = false;
92      private transient boolean _host = false;
93      private transient boolean _delayedHandling = false;
94  
95      /* ------------------------------------------------------------ */
96      public static HttpConnection getCurrentConnection()
97      {
98          return (HttpConnection)__currentConnection.get();
99      }
100 
101     /* ------------------------------------------------------------ */
102     protected static void setCurrentConnection(HttpConnection connection)
103     {
104         __currentConnection.set(connection);
105     }
106 
107     /* ------------------------------------------------------------ */
108     /**
109      * Constructor
110      * 
111      */
112     public HttpConnection(Connector connector, EndPoint endpoint, Server server)
113     {
114         _uri = URIUtil.__CHARSET==StringUtil.__UTF8?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
115         _connector = connector;
116         _endp = endpoint;
117         _parser = new HttpParser(_connector,endpoint,new RequestHandler(),_connector.getHeaderBufferSize(),_connector.getRequestBufferSize());
118         _requestFields = new HttpFields();
119         _responseFields = new HttpFields();
120         _request = new Request(this);
121         _response = new Response(this);
122         _generator = new HttpGenerator(_connector,_endp,_connector.getHeaderBufferSize(),_connector.getResponseBufferSize());
123         _generator.setSendServerVersion(server.getSendServerVersion());
124         _server = server;
125     }
126     
127     protected HttpConnection(Connector connector, EndPoint endpoint, Server server,
128             Parser parser, Generator generator, Request request)
129     {
130         _uri = URIUtil.__CHARSET==StringUtil.__UTF8?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
131         _connector = connector;
132         _endp = endpoint;
133         _parser = parser;
134         _requestFields = new HttpFields();
135         _responseFields = new HttpFields();
136         _request = request;
137         _response = new Response(this);
138         _generator = generator;
139         _generator.setSendServerVersion(server.getSendServerVersion());
140         _server = server;
141     }
142 
143     /* ------------------------------------------------------------ */
144     public void destroy()
145     {
146         synchronized (this)
147         {
148             _destroy = true;
149             if (!_handling)
150             {
151                 if (_parser != null)
152                     _parser.reset(true);
153 
154                 if (_generator != null)
155                     _generator.reset(true);
156 
157                 if (_requestFields != null)
158                     _requestFields.destroy();
159 
160                 if (_responseFields != null)
161                     _responseFields.destroy();
162 
163             }
164         }
165     }
166 
167     /* ------------------------------------------------------------ */
168     /**
169      * @return the parser used by this connection
170      */
171     public Parser getParser()
172     {
173         return _parser;
174     }
175 
176     /* ------------------------------------------------------------ */
177     /**
178      * @return the number of requests handled by this connection
179      */
180     public int getRequests()
181     {
182         return _requests;
183     }
184 
185     /* ------------------------------------------------------------ */
186     /**
187      * @return The time this connection was established.
188      */
189     public long getTimeStamp()
190     {
191         return _timeStamp;
192     }
193 
194     /* ------------------------------------------------------------ */
195     /**
196      * @return Returns the associatedObject.
197      */
198     public Object getAssociatedObject()
199     {
200         return _associatedObject;
201     }
202 
203     /* ------------------------------------------------------------ */
204     /**
205      * @param associatedObject
206      *            The associatedObject to set.
207      */
208     public void setAssociatedObject(Object associatedObject)
209     {
210         _associatedObject = associatedObject;
211     }
212 
213     /* ------------------------------------------------------------ */
214     /**
215      * @return Returns the connector.
216      */
217     public Connector getConnector()
218     {
219         return _connector;
220     }
221 
222     /* ------------------------------------------------------------ */
223     /**
224      * @return Returns the requestFields.
225      */
226     public HttpFields getRequestFields()
227     {
228         return _requestFields;
229     }
230 
231     /* ------------------------------------------------------------ */
232     /**
233      * @return Returns the responseFields.
234      */
235     public HttpFields getResponseFields()
236     {
237         return _responseFields;
238     }
239 
240     /* ------------------------------------------------------------ */
241     /**
242      * @return The result of calling {@link #getConnector}.
243      *         {@link Connector#isConfidential(Request) isCondidential}
244      *         (request), or false if there is no connector.
245      */
246     public boolean isConfidential(Request request)
247     {
248         if (_connector != null)
249             return _connector.isConfidential(request);
250         return false;
251     }
252 
253     /* ------------------------------------------------------------ */
254     /**
255      * Find out if the request is INTEGRAL security.
256      * 
257      * @param request
258      * @return <code>true</code> if there is a {@link #getConnector() connector}
259      *         and it considers <code>request</code> to be
260      *         {@link Connector#isIntegral(Request) integral}
261      */
262     public boolean isIntegral(Request request)
263     {
264         if (_connector != null)
265             return _connector.isIntegral(request);
266         return false;
267     }
268 
269     /* ------------------------------------------------------------ */
270     /**
271      * @return The {@link EndPoint} for this connection.
272      */
273     public EndPoint getEndPoint()
274     {
275         return _endp;
276     }
277 
278     /* ------------------------------------------------------------ */
279     /**
280      * @return <code>false</code> (this method is not yet implemented)
281      */
282     public boolean getResolveNames()
283     {
284         return _connector.getResolveNames();
285     }
286 
287     /* ------------------------------------------------------------ */
288     /**
289      * @return Returns the request.
290      */
291     public Request getRequest()
292     {
293         return _request;
294     }
295 
296     /* ------------------------------------------------------------ */
297     /**
298      * @return Returns the response.
299      */
300     public Response getResponse()
301     {
302         return _response;
303     }
304 
305     /* ------------------------------------------------------------ */
306     /**
307      * @return The input stream for this connection. The stream will be created
308      *         if it does not already exist.
309      */
310     public ServletInputStream getInputStream()
311     {
312         if (_in == null)
313             _in = new HttpParser.Input(((HttpParser)_parser),_connector.getMaxIdleTime());
314         return _in;
315     }
316 
317     /* ------------------------------------------------------------ */
318     /**
319      * @return The output stream for this connection. The stream will be created
320      *         if it does not already exist.
321      */
322     public ServletOutputStream getOutputStream()
323     {
324         if (_out == null)
325             _out = new Output();
326         return _out;
327     }
328 
329     /* ------------------------------------------------------------ */
330     /**
331      * @return A {@link PrintWriter} wrapping the {@link #getOutputStream output
332      *         stream}. The writer is created if it does not already exist.
333      */
334     public PrintWriter getPrintWriter(String encoding)
335     {
336         getOutputStream();
337         if (_writer == null)
338         {
339             _writer = new OutputWriter();
340             _printWriter = new PrintWriter(_writer)
341             {
342                 /* ------------------------------------------------------------ */
343                 /*
344                  * @see java.io.PrintWriter#close()
345                  */
346                 public void close()
347                 {
348                     try
349                     {
350                         out.close();
351                     }
352                     catch (IOException e)
353                     {
354                         Log.debug(e);
355                         setError();
356                     }
357                 }
358 
359             };
360         }
361         _writer.setCharacterEncoding(encoding);
362         return _printWriter;
363     }
364 
365     /* ------------------------------------------------------------ */
366     public boolean isResponseCommitted()
367     {
368         return _generator.isCommitted();
369     }
370 
371     /* ------------------------------------------------------------ */
372     public void handle() throws IOException
373     {
374         // Loop while more in buffer
375         boolean more_in_buffer = true; // assume true until proven otherwise
376         int no_progress = 0;
377 
378         while (more_in_buffer)
379         {
380             try
381             {
382                 synchronized (this)
383                 {
384                     if (_handling)
385                         throw new IllegalStateException(); // TODO delete this
386                                                            // check
387                     _handling = true;
388                 }
389 
390                 setCurrentConnection(this);
391                 long io = 0;
392 
393                 Continuation continuation = _request.getContinuation();
394                 if (continuation != null && continuation.isPending())
395                 {
396                     Log.debug("resume continuation {}",continuation);
397                     if (_request.getMethod() == null)
398                         throw new IllegalStateException();
399                     handleRequest();
400                 }
401                 else
402                 {
403                     // If we are not ended then parse available
404                     if (!_parser.isComplete())
405                         io = _parser.parseAvailable();
406 
407                     // Do we have more generating to do?
408                     // Loop here because some writes may take multiple steps and
409                     // we need to flush them all before potentially blocking in
410                     // the
411                     // next loop.
412                     while (_generator.isCommitted() && !_generator.isComplete())
413                     {
414                         long written = _generator.flush();
415                         io += written;
416                         if (written <= 0)
417                             break;
418                         if (_endp.isBufferingOutput())
419                             _endp.flush();
420                     }
421 
422                     // Flush buffers
423                     if (_endp.isBufferingOutput())
424                     {
425                         _endp.flush();
426                         if (!_endp.isBufferingOutput())
427                             no_progress = 0;
428                     }
429 
430                     if (io > 0)
431                         no_progress = 0;
432                     else if (no_progress++ >= 2)
433                         return;
434                 }
435             }
436             catch (HttpException e)
437             {
438                 if (Log.isDebugEnabled())
439                 {
440                     Log.debug("uri=" + _uri);
441                     Log.debug("fields=" + _requestFields);
442                     Log.debug(e);
443                 }
444                 _generator.sendError(e.getStatus(),e.getReason(),null,true);
445 
446                 _parser.reset(true);
447                 _endp.close();
448                 throw e;
449             }
450             finally
451             {
452                 setCurrentConnection(null);
453 
454                 more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
455 
456                 synchronized (this)
457                 {
458                     _handling = false;
459 
460                     if (_destroy)
461                     {
462                         destroy();
463                         return;
464                     }
465                 }
466 
467                 if (_parser.isComplete() && _generator.isComplete() && !_endp.isBufferingOutput())
468                 {
469                     if (!_generator.isPersistent())
470                     {
471                         _parser.reset(true);
472                         more_in_buffer = false;
473                     }
474 
475                     reset(!more_in_buffer);
476                     no_progress = 0;
477                 }
478 
479                 Continuation continuation = _request.getContinuation();
480                 if (continuation != null && continuation.isPending())
481                 {
482                     break;
483                 }
484                 else if (_generator.isCommitted() && !_generator.isComplete() && _endp instanceof SelectChannelEndPoint) // TODO
485                                                                                                                          // remove
486                                                                                                                          // SelectChannel
487                                                                                                                          // dependency
488                     ((SelectChannelEndPoint)_endp).setWritable(false);
489             }
490         }
491     }
492 
493     /* ------------------------------------------------------------ */
494     public void reset(boolean returnBuffers)
495     {
496         _parser.reset(returnBuffers); // TODO maybe only release when low on
497                                       // resources
498         _requestFields.clear();
499         _request.recycle();
500 
501         _generator.reset(returnBuffers); // TODO maybe only release when low on
502                                          // resources
503         _responseFields.clear();
504         _response.recycle();
505 
506         _uri.clear();
507     }
508 
509     /* ------------------------------------------------------------ */
510     protected void handleRequest() throws IOException
511     {
512         if (_server.isRunning())
513         {
514             boolean retrying = false;
515             boolean error = false;
516             String threadName = null;
517             String info=null;
518             try
519             {
520                 info = URIUtil.canonicalPath(_uri.getDecodedPath());
521                 if (info == null)
522                     throw new HttpException(400);
523                 _request.setPathInfo(info);
524 
525                 if (_out != null)
526                     _out.reopen();
527 
528                 if (Log.isDebugEnabled())
529                 {
530                     threadName = Thread.currentThread().getName();
531                     Thread.currentThread().setName(threadName + " - " + _uri);
532                 }
533 
534                 _connector.customize(_endp,_request);
535 
536                 _server.handle(this);
537             }
538             catch (RetryRequest r)
539             {
540                 if (Log.isDebugEnabled())
541                     Log.ignore(r);
542                 retrying = true;
543             }
544             catch (EofException e)
545             {
546                 Log.ignore(e);
547                 error = true;
548             }
549             catch (HttpException e)
550             {
551                 Log.debug(e);
552                 _request.setHandled(true);
553                 _response.sendError(e.getStatus(),e.getReason());
554                 error = true;
555             }
556             catch (RuntimeIOException e)
557             {
558                 Log.debug(e);
559                 _request.setHandled(true);
560                 error = true;
561             }
562             catch (Throwable e)
563             {
564                 if (e instanceof ThreadDeath)
565                     throw (ThreadDeath)e;
566                 
567                 if (info==null)
568                 {
569                     Log.warn(_uri+": "+e);
570                     Log.debug(e);
571                     _request.setHandled(true);
572                     _generator.sendError(400,null,null,true);
573                 }
574                 else
575                 {
576                     Log.warn(""+_uri,e);
577                     _request.setHandled(true);
578                     _generator.sendError(500,null,null,true);
579                 }
580                 error = true;
581             }
582             finally
583             {
584                 if (threadName != null)
585                     Thread.currentThread().setName(threadName);
586 
587                 if (!retrying)
588                 {
589                     if (_request.getContinuation() != null)
590                     {
591                         Log.debug("continuation still pending {}");
592                         _request.getContinuation().reset();
593                     }
594 
595                     if (_endp.isOpen())
596                     {
597                         if (_generator.isPersistent())
598                             _connector.persist(_endp);
599 
600                         if (error)
601                             _endp.close();
602                         else
603                         {
604                             if (!_response.isCommitted() && !_request.isHandled())
605                                 _response.sendError(HttpServletResponse.SC_NOT_FOUND);
606                             _response.complete();
607                         }
608                     }
609                     else
610                     {
611                         _response.complete(); // TODO ????????????
612                     }
613                 }
614             }
615         }
616     }
617 
618     /* ------------------------------------------------------------ */
619     public void commitResponse(boolean last) throws IOException
620     {
621         if (!_generator.isCommitted())
622         {
623             _generator.setResponse(_response.getStatus(),_response.getReason());
624             try
625             {
626                 _generator.completeHeader(_responseFields,last);
627             }
628             catch(IOException io)
629             {
630                 throw io;
631             }
632             catch(RuntimeException e)
633             {
634                 Log.warn("header full: "+e);
635                 Log.debug(e);
636                 _response.reset();
637                 _generator.reset(true);
638                 _generator.setResponse(HttpStatus.ORDINAL_500_Internal_Server_Error,null);
639                 _generator.completeHeader(_responseFields,HttpGenerator.LAST);
640                 _generator.complete();
641                 throw e;
642             }
643         }
644         if (last)
645             _generator.complete();
646     }
647 
648     /* ------------------------------------------------------------ */
649     public void completeResponse() throws IOException
650     {
651         if (!_generator.isCommitted())
652         {
653             _generator.setResponse(_response.getStatus(),_response.getReason());
654             try
655             {
656                 _generator.completeHeader(_responseFields,HttpGenerator.LAST);
657             }
658             catch(IOException io)
659             {
660                 throw io;
661             }
662             catch(RuntimeException e)
663             {
664                 Log.warn("header full: "+e);
665                 Log.debug(e);
666 
667                 _response.reset();
668                 _generator.reset(true);
669                 _generator.setResponse(HttpStatus.ORDINAL_500_Internal_Server_Error,null);
670                 _generator.completeHeader(_responseFields,HttpGenerator.LAST);
671                 _generator.complete();
672                 throw e;
673             }
674         }
675 
676         _generator.complete();
677     }
678 
679     /* ------------------------------------------------------------ */
680     public void flushResponse() throws IOException
681     {
682         try
683         {
684             commitResponse(HttpGenerator.MORE);
685             _generator.flush();
686         }
687         catch (IOException e)
688         {
689             throw (e instanceof EofException)?e:new EofException(e);
690         }
691     }
692 
693     /* ------------------------------------------------------------ */
694     public Generator getGenerator()
695     {
696         return _generator;
697     }
698 
699     /* ------------------------------------------------------------ */
700     public boolean isIncluding()
701     {
702         return _include > 0;
703     }
704 
705     /* ------------------------------------------------------------ */
706     public void include()
707     {
708         _include++;
709     }
710 
711     /* ------------------------------------------------------------ */
712     public void included()
713     {
714         _include--;
715         if (_out != null)
716             _out.reopen();
717     }
718 
719     /* ------------------------------------------------------------ */
720     public boolean isIdle()
721     {
722         return _generator.isIdle() && (_parser.isIdle() || _delayedHandling);
723     }
724 
725     /* ------------------------------------------------------------ */
726     /* ------------------------------------------------------------ */
727     /* ------------------------------------------------------------ */
728     private class RequestHandler extends HttpParser.EventHandler
729     {
730         private String _charset;
731 
732         /*
733          * 
734          * @see
735          * org.mortbay.jetty.HttpParser.EventHandler#startRequest(org.mortbay
736          * .io.Buffer, org.mortbay.io.Buffer, org.mortbay.io.Buffer)
737          */
738         public void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException
739         {
740             _host = false;
741             _expect = UNKNOWN;
742             _delayedHandling = false;
743             _charset = null;
744 
745             if (_request.getTimeStamp() == 0)
746                 _request.setTimeStamp(System.currentTimeMillis());
747             _request.setMethod(method.toString());
748 
749             try
750             {
751                 _uri.parse(uri.array(),uri.getIndex(),uri.length());
752                 _request.setUri(_uri);
753 
754                 if (version == null)
755                 {
756                     _request.setProtocol(HttpVersions.HTTP_0_9);
757                     _version = HttpVersions.HTTP_0_9_ORDINAL;
758                 }
759                 else
760                 {
761                     version = HttpVersions.CACHE.get(version);
762                     _version = HttpVersions.CACHE.getOrdinal(version);
763                     if (_version <= 0)
764                         _version = HttpVersions.HTTP_1_0_ORDINAL;
765                     _request.setProtocol(version.toString());
766                 }
767 
768                 _head = method == HttpMethods.HEAD_BUFFER; // depends on method
769                                                            // being decached.
770             }
771             catch (Exception e)
772             {
773                 throw new HttpException(HttpStatus.ORDINAL_400_Bad_Request,null,e);
774             }
775         }
776 
777         /*
778          * @see
779          * org.mortbay.jetty.HttpParser.EventHandler#parsedHeaderValue(org.mortbay
780          * .io.Buffer)
781          */
782         public void parsedHeader(Buffer name, Buffer value)
783         {
784             int ho = HttpHeaders.CACHE.getOrdinal(name);
785             switch (ho)
786             {
787                 case HttpHeaders.HOST_ORDINAL:
788                     // TODO check if host matched a host in the URI.
789                     _host = true;
790                     break;
791 
792                 case HttpHeaders.EXPECT_ORDINAL:
793                     value = HttpHeaderValues.CACHE.lookup(value);
794                     _expect = HttpHeaderValues.CACHE.getOrdinal(value);
795                     break;
796 
797                 case HttpHeaders.ACCEPT_ENCODING_ORDINAL:
798                 case HttpHeaders.USER_AGENT_ORDINAL:
799                     value = HttpHeaderValues.CACHE.lookup(value);
800                     break;
801 
802                 case HttpHeaders.CONTENT_TYPE_ORDINAL:
803                     value = MimeTypes.CACHE.lookup(value);
804                     _charset = MimeTypes.getCharsetFromContentType(value);
805                     break;
806 
807                 case HttpHeaders.CONNECTION_ORDINAL:
808                     // looks rather clumsy, but the idea is to optimize for a
809                     // single valued header
810                     int ordinal = HttpHeaderValues.CACHE.getOrdinal(value);
811                     switch (ordinal)
812                     {
813                         case -1:
814                         {
815                             String[] values = value.toString().split(",");
816                             for (int i = 0; values != null && i < values.length; i++)
817                             {
818                                 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim());
819 
820                                 if (cb != null)
821                                 {
822                                     switch (cb.getOrdinal())
823                                     {
824                                         case HttpHeaderValues.CLOSE_ORDINAL:
825                                             _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
826                                             _generator.setPersistent(false);
827                                             break;
828 
829                                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
830                                             if (_version == HttpVersions.HTTP_1_0_ORDINAL)
831                                                 _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
832                                             break;
833                                     }
834                                 }
835                             }
836                             break;
837                         }
838                         case HttpHeaderValues.CLOSE_ORDINAL:
839                             _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
840                             _generator.setPersistent(false);
841                             break;
842 
843                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
844                             if (_version == HttpVersions.HTTP_1_0_ORDINAL)
845                                 _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
846                             break;
847                     }
848             }
849 
850             _requestFields.add(name,value);
851         }
852 
853         /*
854          * @see org.mortbay.jetty.HttpParser.EventHandler#headerComplete()
855          */
856         public void headerComplete() throws IOException
857         {
858             _requests++;
859             _generator.setVersion(_version);
860             switch (_version)
861             {
862                 case HttpVersions.HTTP_0_9_ORDINAL:
863                     break;
864                 case HttpVersions.HTTP_1_0_ORDINAL:
865                     _generator.setHead(_head);
866                     break;
867                 case HttpVersions.HTTP_1_1_ORDINAL:
868                     _generator.setHead(_head);
869 
870                     if (_server.getSendDateHeader())
871                         _responseFields.put(HttpHeaders.DATE_BUFFER,_request.getTimeStampBuffer(),_request.getTimeStamp());
872 
873                     if (!_host)
874                     {
875                         _generator.setResponse(HttpStatus.ORDINAL_400_Bad_Request,null);
876                         _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
877                         _generator.completeHeader(_responseFields,true);
878                         _generator.complete();
879                         return;
880                     }
881 
882                     if (_expect != UNKNOWN)
883                     {
884                         if (_expect == HttpHeaderValues.CONTINUE_ORDINAL)
885                         {
886                             // TODO delay sending 100 response until a read is
887                             // attempted.
888                             if (((HttpParser)_parser).getHeaderBuffer() == null || ((HttpParser)_parser).getHeaderBuffer().length() < 2)
889                             {
890                                 _generator.setResponse(HttpStatus.ORDINAL_100_Continue,null);
891                                 _generator.completeHeader(null,true);
892                                 _generator.complete();
893                                 _generator.reset(false);
894                             }
895                         }
896                         else if (_expect == HttpHeaderValues.PROCESSING_ORDINAL)
897                         {
898                         }
899                         else
900                         {
901                             _generator.sendError(HttpStatus.ORDINAL_417_Expectation_Failed,null,null,true);
902                             return;
903                         }
904                     }
905 
906                     break;
907                 default:
908             }
909 
910             if (_charset != null)
911                 _request.setCharacterEncodingUnchecked(_charset);
912 
913             // Either handle now or wait for first content
914             if (((HttpParser)_parser).getContentLength() <= 0 && !((HttpParser)_parser).isChunking())
915                 handleRequest();
916             else
917                 _delayedHandling = true;
918         }
919 
920         /* ------------------------------------------------------------ */
921         /*
922          * @see org.mortbay.jetty.HttpParser.EventHandler#content(int,
923          * org.mortbay.io.Buffer)
924          */
925         public void content(Buffer ref) throws IOException
926         {
927             if (_delayedHandling)
928             {
929                 _delayedHandling = false;
930                 handleRequest();
931             }
932         }
933 
934         /*
935          * (non-Javadoc)
936          * 
937          * @see org.mortbay.jetty.HttpParser.EventHandler#messageComplete(int)
938          */
939         public void messageComplete(long contentLength) throws IOException
940         {
941             if (_delayedHandling)
942             {
943                 _delayedHandling = false;
944                 handleRequest();
945             }
946         }
947 
948         /*
949          * (non-Javadoc)
950          * 
951          * @see
952          * org.mortbay.jetty.HttpParser.EventHandler#startResponse(org.mortbay
953          * .io.Buffer, int, org.mortbay.io.Buffer)
954          */
955         public void startResponse(Buffer version, int status, Buffer reason)
956         {
957             Log.debug("Bad request!: " + version + " " + status + " " + reason);
958         }
959 
960     }
961 
962     /* ------------------------------------------------------------ */
963     /* ------------------------------------------------------------ */
964     /* ------------------------------------------------------------ */
965     public class Output extends AbstractGenerator.Output
966     {
967         Output()
968         {
969             super((AbstractGenerator)HttpConnection.this._generator,_connector.getMaxIdleTime());
970         }
971 
972         /* ------------------------------------------------------------ */
973         /*
974          * @see java.io.OutputStream#close()
975          */
976         public void close() throws IOException
977         {
978             if (_closed)
979                 return;
980 
981             if (!isIncluding() && !_generator.isCommitted())
982                 commitResponse(HttpGenerator.LAST);
983             else
984                 flushResponse();
985 
986             super.close();
987         }
988 
989         /* ------------------------------------------------------------ */
990         /*
991          * @see java.io.OutputStream#flush()
992          */
993         public void flush() throws IOException
994         {
995             if (!_generator.isCommitted())
996                 commitResponse(HttpGenerator.MORE);
997             super.flush();
998         }
999 
1000         /* ------------------------------------------------------------ */
1001         /*
1002          * @see javax.servlet.ServletOutputStream#print(java.lang.String)
1003          */
1004         public void print(String s) throws IOException
1005         {
1006             if (_closed)
1007                 throw new IOException("Closed");
1008             PrintWriter writer = getPrintWriter(null);
1009             writer.print(s);
1010         }
1011 
1012         /* ------------------------------------------------------------ */
1013         public void sendResponse(Buffer response) throws IOException
1014         {
1015             ((HttpGenerator)_generator).sendResponse(response);
1016         }
1017 
1018         /* ------------------------------------------------------------ */
1019         public void sendContent(Object content) throws IOException
1020         {
1021             Resource resource = null;
1022 
1023             if (_closed)
1024                 throw new IOException("Closed");
1025 
1026             if (_generator.getContentWritten() > 0)
1027                 throw new IllegalStateException("!empty");
1028 
1029             if (content instanceof HttpContent)
1030             {
1031                 HttpContent c = (HttpContent)content;
1032                 Buffer contentType = c.getContentType();
1033                 if (contentType != null && !_responseFields.containsKey(HttpHeaders.CONTENT_TYPE_BUFFER))
1034                 {
1035                     String enc = _response.getSetCharacterEncoding();
1036                     if(enc==null)
1037                         _responseFields.add(HttpHeaders.CONTENT_TYPE_BUFFER, contentType);
1038                     else
1039                     {
1040                         if(contentType instanceof CachedBuffer)
1041                         {
1042                             CachedBuffer content_type = ((CachedBuffer)contentType).getAssociate(enc);
1043                             if(content_type!=null)
1044                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, content_type);
1045                             else
1046                             {
1047                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, 
1048                                         contentType+";charset="+QuotedStringTokenizer.quote(enc,";= "));
1049                             }
1050                         }
1051                         else
1052                         {
1053                             _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, 
1054                                     contentType+";charset="+QuotedStringTokenizer.quote(enc,";= "));
1055                         }
1056                     }
1057                 }
1058                 if (c.getContentLength() > 0)
1059                     _responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER,c.getContentLength());
1060                 Buffer lm = c.getLastModified();
1061                 long lml = c.getResource().lastModified();
1062                 if (lm != null)
1063                     _responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER,lm,lml);
1064                 else if (c.getResource() != null)
1065                 {
1066                     if (lml != -1)
1067                         _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,lml);
1068                 }
1069 
1070                 content = c.getBuffer();
1071                 if (content == null)
1072                     content = c.getInputStream();
1073             }
1074             else if (content instanceof Resource)
1075             {
1076                 resource = (Resource)content;
1077                 _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,resource.lastModified());
1078                 content = resource.getInputStream();
1079             }
1080 
1081             if (content instanceof Buffer)
1082             {
1083                 _generator.addContent((Buffer)content,HttpGenerator.LAST);
1084                 commitResponse(HttpGenerator.LAST);
1085             }
1086             else if (content instanceof InputStream)
1087             {
1088                 InputStream in = (InputStream)content;
1089 
1090                 try
1091                 {
1092                     int max = _generator.prepareUncheckedAddContent();
1093                     Buffer buffer = _generator.getUncheckedBuffer();
1094 
1095                     int len = buffer.readFrom(in,max);
1096 
1097                     while (len >= 0)
1098                     {
1099                         _generator.completeUncheckedAddContent();
1100                         _out.flush();
1101 
1102                         max = _generator.prepareUncheckedAddContent();
1103                         buffer = _generator.getUncheckedBuffer();
1104                         len = buffer.readFrom(in,max);
1105                     }
1106                     _generator.completeUncheckedAddContent();
1107                     _out.flush();
1108                 }
1109                 finally
1110                 {
1111                     if (resource != null)
1112                         resource.release();
1113                     else
1114                         in.close();
1115 
1116                 }
1117             }
1118             else
1119                 throw new IllegalArgumentException("unknown content type?");
1120 
1121         }
1122     }
1123 
1124     /* ------------------------------------------------------------ */
1125     /* ------------------------------------------------------------ */
1126     /* ------------------------------------------------------------ */
1127     public class OutputWriter extends AbstractGenerator.OutputWriter
1128     {
1129         OutputWriter()
1130         {
1131             super(HttpConnection.this._out);
1132         }
1133     }
1134 
1135 }