Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/mortbay/http/HttpConnection.java


1   // ========================================================================
2   // Copyright (c) 1999-2003 Mort Bay Consulting (Australia) Pty. Ltd.
3   // $Id: HttpConnection.java,v 1.67 2003/11/22 16:06:01 gregwilkins Exp $
4   // ========================================================================
5   
6   package org.mortbay.http;
7   
8   import java.io.IOException;
9   import java.io.InputStream;
10  import java.io.OutputStream;
11  import java.net.InetAddress;
12  import java.net.Socket;
13  import java.util.Enumeration;
14  import java.util.List;
15  
16  import org.apache.commons.logging.Log;
17  import org.apache.commons.logging.LogFactory;
18  import org.mortbay.util.InetAddrPort;
19  import org.mortbay.util.LineInput;
20  import org.mortbay.util.LogSupport;
21  import org.mortbay.util.OutputObserver;
22  import org.mortbay.util.StringUtil;
23  
24  
25  /* ------------------------------------------------------------ */
26  /** A HTTP Connection.
27   * This class provides the generic HTTP handling for
28   * a connection to a HTTP server. An instance of HttpConnection
29   * is normally created by a HttpListener and then given control
30   * in order to run the protocol handling before and after passing
31   * a request to the HttpServer of the HttpListener.
32   *
33   * This class is not synchronized as it should only ever be known
34   * to a single thread.
35   *
36   * @see HttpListener
37   * @see HttpServer
38   * @version $Id: HttpConnection.java,v 1.67 2003/11/22 16:06:01 gregwilkins Exp $
39   * @author Greg Wilkins (gregw)
40   */
41  public class HttpConnection
42      implements OutputObserver
43  {
44      private static Log log = LogFactory.getLog(HttpConnection.class);
45  
46      /* ------------------------------------------------------------ */
47      private static ThreadLocal __threadConnection=new ThreadLocal();
48  
49  
50      /** Support for FRC2068 Continues.
51       * If true, then 100 Continues will be sent when expected or for POST requests. If false, 100 Continues will
52       * only be sent if expected. Can be configured with the org.mortbay.http.HttpConnection.2068Continue system
53       * property.
54       */
55      private static boolean __2068_Continues=Boolean.getBoolean("org.mortbay.http.HttpConnection.2068Continue");
56      
57      /* ------------------------------------------------------------ */
58      protected HttpRequest _request;
59      protected HttpResponse _response;
60      protected boolean _persistent;
61      protected boolean _keepAlive;
62  
63      private HttpListener _listener;
64      private HttpInputStream _inputStream;
65      private HttpOutputStream _outputStream;
66      private boolean _close;
67      private int _dotVersion;
68      private boolean _firstWrite;
69      private Thread _handlingThread;
70      
71      private InetAddress _remoteInetAddress;
72      private String _remoteAddr;
73      private String _remoteHost;
74      private HttpServer _httpServer;
75      private Object _connection;
76      private boolean _throttled ;
77      
78      private boolean _statsOn;
79      private long _tmpTime;
80      private long _openTime;
81      private long _reqTime;
82      private int _requests;
83      private Object _object;
84      private HttpTunnel _tunnel ;
85      private boolean _resolveRemoteHost;
86      
87      /* ------------------------------------------------------------ */
88      /** Constructor.
89       * @param listener The listener that created this connection.
90       * @param remoteAddr The address of the remote end or null.
91       * @param in InputStream to read request(s) from.
92       * @param out OutputputStream to write response(s) to.
93       * @param connection The underlying connection object, most likely
94       * a socket. This is not used by HttpConnection other than to make
95       * it available via getConnection().
96       */
97      public HttpConnection(HttpListener listener,
98                            InetAddress remoteAddr,
99                            InputStream in,
100                           OutputStream out,
101                           Object connection)
102     {
103         if(log.isDebugEnabled())log.debug("new HttpConnection: "+connection);
104         _listener=listener;
105         _remoteInetAddress=remoteAddr;
106         int bufferSize=listener==null?4096:listener.getBufferSize();
107         int reserveSize=listener==null?512:listener.getBufferReserve();
108         _inputStream=new HttpInputStream(in,bufferSize);
109         _outputStream=new HttpOutputStream(out,bufferSize,reserveSize);
110         _outputStream.addObserver(this);
111         _firstWrite=false;
112         if (_listener!=null)
113             _httpServer=_listener.getHttpServer();
114         _connection=connection;
115         
116         _statsOn=_httpServer!=null && _httpServer.getStatsOn();
117         if (_statsOn)
118         {
119             _openTime=System.currentTimeMillis();
120             _httpServer.statsOpenConnection();
121         }
122         _reqTime=0;
123         _requests=0;
124         
125         _request = new HttpRequest(this);
126         _response = new HttpResponse(this);
127 
128         _resolveRemoteHost =
129             _listener!=null &&
130             _listener.getHttpServer().getResolveRemoteHost();
131     }
132 
133     /* ------------------------------------------------------------ */
134     /** Get the ThreadLocal HttpConnection.
135      * The ThreadLocal HttpConnection is set by the handle() method.
136      * @return HttpConnection for this thread.
137      */
138     static HttpConnection getHttpConnection()
139     {
140         return (HttpConnection)__threadConnection.get();
141     }
142     
143     /* ------------------------------------------------------------ */
144     /** Get the Remote address.
145      * @return the remote address
146      */
147     public InetAddress getRemoteInetAddress()
148     {
149         return _remoteInetAddress;
150     }
151 
152     /* ------------------------------------------------------------ */
153     /** Get the Remote address.
154      * @return the remote host name
155      */
156     public String getRemoteAddr()
157     {
158         if (_remoteAddr==null)
159         {
160             if (_remoteInetAddress==null)
161                 return "127.0.0.1";
162             _remoteAddr=_remoteInetAddress.getHostAddress();
163         }
164         return _remoteAddr;
165     }
166     
167     /* ------------------------------------------------------------ */
168     /** Get the Remote address.
169      * @return the remote host name
170      */
171     public String getRemoteHost()
172     {
173         if (_remoteHost==null)
174         {
175             if (_resolveRemoteHost)
176             {
177                 if (_remoteInetAddress==null)
178                     return "localhost";
179                 _remoteHost=_remoteInetAddress.getHostName();
180             }
181             else
182             {
183                 if (_remoteInetAddress==null)
184                     return "127.0.0.1";
185                 _remoteHost=getRemoteAddr();
186             }
187         }
188         return _remoteHost;
189     }
190     
191     /* ------------------------------------------------------------ */
192     /** Get the connections InputStream.
193      * @return the connections InputStream
194      */
195     public HttpInputStream getInputStream()
196     {
197         return _inputStream;
198     }
199     
200     /* ------------------------------------------------------------ */
201     /** Get the connections OutputStream.
202      * @return the connections OutputStream
203      */
204     public HttpOutputStream getOutputStream()
205     {
206         return _outputStream;
207     }
208 
209     /* ------------------------------------------------------------ */
210     /** Get the underlying connection object.
211      * This opaque object, most likely a socket. This is not used by
212      * HttpConnection other than to make it available via getConnection().
213      * @return Connection abject
214      */
215     public Object getConnection()
216     {
217         return _connection;
218     }
219     
220     /* ------------------------------------------------------------ */
221     /** Get the request.
222      * @return the request
223      */
224     public HttpRequest getRequest()
225     {
226         return _request;
227     }
228     
229     /* ------------------------------------------------------------ */
230     /** Get the response.
231      * @return the response
232      */
233     public HttpResponse getResponse()
234     {
235         return _response;
236     }
237 
238     /* ------------------------------------------------------------ */
239     /** Force the connection to not be persistent.
240      */
241     public void forceClose()
242     {
243         _persistent=false;
244         _close=true;
245     }
246     
247     /* ------------------------------------------------------------ */
248     /** Close the connection.
249      * This method calls close on the input and output streams and
250      * interrupts any thread in the handle method.
251      * may be specialized to close sockets etc.
252      * @exception IOException 
253      */
254     public void close()
255         throws IOException
256     {
257         try{
258             _outputStream.close();
259             _inputStream.close();
260         }
261         finally
262         {
263             if (_handlingThread!=null && Thread.currentThread()!=_handlingThread)
264                 _handlingThread.interrupt();
265         }
266     }
267     
268     /* ------------------------------------------------------------ */
269     /** Get the connections listener. 
270      * @return HttpListener that created this Connection.
271      */
272     public HttpListener getListener()
273     {
274         return _listener;
275     }
276 
277     /* ------------------------------------------------------------ */
278     /** Get the listeners HttpServer .
279      * Conveniance method equivalent to getListener().getHttpServer().
280      * @return HttpServer.
281      */
282     public HttpServer getHttpServer()
283     {
284         return _httpServer;
285     }
286 
287     /* ------------------------------------------------------------ */
288     /** Get the listeners Default scheme. 
289      * Conveniance method equivalent to getListener().getDefaultProtocol().
290      * @return HttpServer.
291      */
292     public String getDefaultScheme()
293     {
294         return _listener.getDefaultScheme();
295     }
296     
297     /* ------------------------------------------------------------ */
298     /** Get the listeners HttpServer.
299      * But if the name is 0.0.0.0, then the real interface address is used.
300      * @return HttpServer.
301      */
302     public String getServerName()
303     {
304         String host=_listener.getHost();
305         if (InetAddrPort.__0_0_0_0.equals(host) &&
306             _connection instanceof Socket)
307             host = ((Socket)_connection).getLocalAddress().getHostName();
308         
309         return host;
310     }
311     
312     /* ------------------------------------------------------------ */
313     /** Get the listeners HttpServer.
314      * @return HttpServer.
315      */
316     public String getServerAddr()
317     {
318         if (_connection instanceof Socket)
319             return ((Socket)_connection).getLocalAddress().getHostAddress();
320         return _listener.getHost();
321     }
322     
323     /* ------------------------------------------------------------ */
324     /** Get the listeners Port .
325      * Conveniance method equivalent to getListener().getPort().
326      * @return local port.
327      */
328     public int getServerPort()
329     {
330         return _listener.getPort();
331     }
332     
333     /* ------------------------------------------------------------ */
334     /** Get the remote Port .
335      * @return remote port.
336      */
337     public int getRemotePort()
338     {
339         if (_connection instanceof Socket)
340             return ((Socket)_connection).getPort();
341         return 0;
342     }
343 
344     /* ------------------------------------------------------------ */
345     /** 
346      * @return True if this connections state has been altered due
347      * to low resources. 
348      */
349     public boolean isThrottled()
350     {
351         return _throttled;
352     }
353     
354      /* ------------------------------------------------------------ */
355     /** 
356      * @param throttled True if this connections state has been altered due
357      * to low resources. 
358      */
359     public void setThrottled(boolean throttled)
360     {
361         _throttled = throttled;
362     }
363     
364     /* ------------------------------------------------------------ */
365     /** Get associated object.
366      * Used by a particular HttpListener implementation to associate
367      * private datastructures with the connection.
368      * @return An object associated with the connecton by setObject.
369      */
370     public Object getObject()
371     {
372         return _object;
373     }
374     
375     /* ------------------------------------------------------------ */
376     /** Set associated object.
377      * Used by a particular HttpListener implementation to associate
378      * private datastructures with the connection.
379      * @param o An object associated with the connecton.
380      */
381     public void setObject(Object o)
382     {
383         _object=o;
384     }
385 
386     /* ------------------------------------------------------------ */
387     /** 
388      * @return The HttpTunnel set for the connection or null.
389      */
390     public HttpTunnel getHttpTunnel()
391     {
392         return _tunnel;
393     }
394     
395     /* ------------------------------------------------------------ */
396     /** Set a HttpTunnel for the connection.
397      * A HTTP tunnel is used if the connection is to be taken over for
398      * non-HTTP communications. An example of this is the CONNECT method
399      * in proxy handling.  If a HttpTunnel is set on a connection, then it's
400      * handle method is called bu the next call to handleNext().
401      * @param tunnel The HttpTunnel set for the connection or null.
402      */
403     public void setHttpTunnel(HttpTunnel tunnel)
404     {
405         _tunnel = tunnel;
406     }
407     
408     /* ------------------------------------------------------------ */
409     /* Verify HTTP/1.0 request
410      * @exception HttpException problem with the request. 
411      * @exception IOException problem with the connection.
412      */
413     private void verifyHTTP_1_0()
414     {
415         // Set content length
416         int content_length=
417             _request.getIntField(HttpFields.__ContentLength);
418         if (content_length>=0)
419             _inputStream.setContentLength(content_length);
420         else if (content_length<0)
421         {
422             // TODO - can't do this check because IE does this after
423             // a redirect.
424             // Can't have content without a content length
425             // String content_type=_request.getField(HttpFields.__ContentType);
426             // if (content_type!=null && content_type.length()>0)
427             //     throw new HttpException(_HttpResponse.__411_Length_Required);
428             _inputStream.setContentLength(0);
429         }
430 
431         // Check netscape proxy connection - this is not strictly correct.
432         if (!_keepAlive && HttpFields.__KeepAlive.equalsIgnoreCase(_request.getField(HttpFields.__ProxyConnection)))
433             _keepAlive=true;
434 
435         // persistent connections in HTTP/1.0 only if requested.
436         _persistent=_keepAlive;
437     }
438     
439     /* ------------------------------------------------------------ */
440     /* Verify HTTP/1.1 request
441      * @exception HttpException problem with the request. 
442      * @exception IOException problem with the connection.
443      */
444     private void verifyHTTP_1_1()
445         throws HttpException, IOException
446     {        
447         // Check Host Field exists
448         String host=_request.getField(HttpFields.__Host);
449         if (host==null)
450             throw new HttpException(HttpResponse.__400_Bad_Request);
451         
452         // check and enable requests transfer encodings.
453         String transfer_coding=
454             _request.getField(HttpFields.__TransferEncoding);
455         
456         if (transfer_coding!=null && transfer_coding.length()>0)
457         {
458             // Handling of codings other than chunking is now
459             // the responsibility of handlers, filters or servlets.
460             // Thanks to the compression filter, we now don't know if
461             // what we can handle here.
462 
463             if (transfer_coding.equalsIgnoreCase(HttpFields.__Chunked) ||
464                 StringUtil.endsWithIgnoreCase(transfer_coding,HttpFields.__Chunked))
465                 _inputStream.setChunking();
466             else if (StringUtil.asciiToLowerCase(transfer_coding)
467                      .indexOf(HttpFields.__Chunked)>=0)
468                 throw new HttpException(HttpResponse.__400_Bad_Request);
469         }
470         
471         // Check input content length can be determined
472         int content_length=_request.getIntField(HttpFields.__ContentLength);
473         String content_type=_request.getField(HttpFields.__ContentType);
474         if (!_inputStream.isChunking())
475         {
476             // If we have a content length, use it
477             if (content_length>=0)
478                 _inputStream.setContentLength(content_length);
479             // else if we have no content
480             else if (content_type==null || content_type.length()==0)
481                 _inputStream.setContentLength(0);
482             // else we need a content length
483             else
484             {
485                 // TODO - can't do this check as IE stuff up on
486                 // a redirect.
487                 // throw new HttpException(HttpResponse.__411_Length_Required);
488                 _inputStream.setContentLength(0);
489             }
490         }
491 
492         // Handle Continue Expectations
493         String expect=_request.getField(HttpFields.__Expect);
494         if (expect!=null && expect.length()>0)
495         {
496             if (StringUtil.asciiToLowerCase(expect)
497                 .equals(HttpFields.__ExpectContinue))
498             {
499                 // Send continue if no body available yet.
500                 if (_inputStream.available()<=0)
501                 {
502                     OutputStream real_out=_outputStream.getOutputStream();
503                     real_out.write(HttpResponse.__Continue);
504                     real_out.flush();
505                 }
506             }
507             else
508                 throw new HttpException(HttpResponse.__417_Expectation_Failed);
509         }
510         else if (__2068_Continues &&
511                  _inputStream.available()<=0 &&
512                  (HttpRequest.__PUT.equals(_request.getMethod()) ||
513                   HttpRequest.__POST.equals(_request.getMethod())))
514         {
515             // Send continue for RFC 2068 exception
516             OutputStream real_out=_outputStream.getOutputStream();
517             real_out.write(HttpResponse.__Continue);
518             real_out.flush();
519         }            
520              
521         // Persistent unless requested otherwise
522         _persistent=!_close;
523     }
524     
525 
526     /* ------------------------------------------------------------ */
527     /** Output Notifications.
528      * Trigger header and/or filters from output stream observations.
529      * Also finalizes method of indicating response content length.
530      * Called as a result of the connection subscribing for notifications
531      * to the HttpOutputStream.
532      * @see HttpOutputStream
533      * @param out The output stream observed.
534      * @param action The action.
535      */
536     public void outputNotify(OutputStream out, int action, Object ignoredData)
537         throws IOException
538     {
539         if (_response==null)
540             return;
541 
542         switch(action)
543         {
544           case OutputObserver.__FIRST_WRITE:
545               if (!_firstWrite)
546               {
547                   firstWrite();
548                   _firstWrite=true;
549               }
550               break;
551               
552           case OutputObserver.__RESET_BUFFER:
553               _firstWrite=false;
554               break;
555               
556           case OutputObserver.__COMMITING:
557               commit();
558               break;
559               
560           case OutputObserver.__CLOSING:
561               if (_response!=null &&
562                   !_response.isCommitted() &&
563                   _request.getState()==HttpMessage.__MSG_RECEIVED)
564                   commit();
565               break;
566               
567           case OutputObserver.__CLOSED:
568               break;
569         }
570     }
571 
572     /* ------------------------------------------------------------ */
573     /** Setup the reponse output stream.
574      * Use the current state of the request and response, to set tranfer
575      * parameters such as chunking and content length.
576      */
577     protected void firstWrite()
578         throws IOException
579     {
580         if (_response.isCommitted())
581             return;
582         
583         // Determine how to limit content length and
584         // enable output transfer encodings
585 
586         String transfer_coding=_response.getField(HttpFields.__TransferEncoding);
587         if (transfer_coding==null ||
588             transfer_coding.length()==0 ||
589             HttpFields.__Identity.equalsIgnoreCase(transfer_coding))
590         {
591             switch(_dotVersion)
592             {
593               case 1:
594                   {
595                       // if (not closed and no length)
596                       if ((!HttpFields.__Close.equals(_response.getField(HttpFields.__Connection)))&&
597                           (_response.getField(HttpFields.__ContentLength)==null))
598                       {
599                           // Chunk it!
600                           _response.setField(HttpFields.__TransferEncoding,HttpFields.__Chunked);
601                           _outputStream.setChunking();
602                       }
603                       break;
604                   }
605               case 0:
606                   {
607                       // If we dont have a content length (except 304 replies), 
608           // or we have been requested to close
609           // then we can't be persistent 
610                       if (!_keepAlive || !_persistent ||
611                           HttpResponse.__304_Not_Modified!=_response.getStatus() &&
612                           _response.getField(HttpFields.__ContentLength)==null ||
613                           HttpFields.__Close.equals(_response.getField(HttpFields.__Connection)))
614                       {
615                           _persistent=false;
616                           if (_keepAlive)
617                               _response.setField(HttpFields.__Connection,
618                                                  HttpFields.__Close);
619                           _keepAlive=false;
620                       }
621                       else if (_keepAlive)
622                           _response.setField(HttpFields.__Connection,
623                                              HttpFields.__KeepAlive);
624                       break;
625                   }
626               default:
627                   _keepAlive=false;
628                   _persistent=false;
629             }
630         }
631         else if (_dotVersion<1)
632         {
633             // Error for transfer encoding to be set in HTTP/1.0
634             _response.removeField(HttpFields.__TransferEncoding);
635             throw new HttpException(HttpResponse.__501_Not_Implemented,
636                                     "Transfer-Encoding not supported in HTTP/1.0");
637         }
638         else
639         {
640             // Use transfer encodings to determine length
641             _response.removeField(HttpFields.__ContentLength);
642             _outputStream.setChunking();
643 
644             if (!HttpFields.__Chunked.equalsIgnoreCase(transfer_coding))
645             {
646                 // Check against any TE field
647                 List te = _request.getAcceptableTransferCodings();
648                 Enumeration enum =
649                     _response.getFieldValues(HttpFields.__TransferEncoding,
650                                              HttpFields.__separators);
651                 while (enum.hasMoreElements())
652                 {
653                     String coding=(String)enum.nextElement();
654                     if (HttpFields.__Identity.equalsIgnoreCase(coding) ||
655                         HttpFields.__Chunked.equalsIgnoreCase(coding))
656                         continue;
657                     if (te==null || !te.contains(coding))
658                         throw new HttpException(HttpResponse.__501_Not_Implemented,coding);
659                 }
660             }
661         }
662 
663         // Nobble the OutputStream for HEAD requests
664         if (HttpRequest.__HEAD.equals(_request.getMethod()))
665             _outputStream.nullOutput();
666     }
667 
668     
669     /* ------------------------------------------------------------ */
670     protected void commit()
671         throws IOException
672     {        
673         if (_response.isCommitted())
674             return;
675 
676         // Mark request as handled.
677         _request.setHandled(true);
678         
679         // Handler forced close, listener stopped or no idle threads left.
680         _close=HttpFields.__Close.equals(_response.getField(HttpFields.__Connection));
681         if (!_close && (!_listener.isStarted()||_listener.isOutOfResources()))
682         {
683             _close=true;
684             _response.setField(HttpFields.__Connection,
685                                HttpFields.__Close);
686         }
687         if (_close)
688             _persistent=false;
689 
690         
691         // if we have no content or encoding, and no content length
692         int status = _response.getStatus();
693         if (!_outputStream.isWritten() &&
694             !_response.containsField(HttpFields.__ContentLength) &&
695             !_response.containsField(HttpFields.__TransferEncoding))
696         {
697             // Special case for responses with no content.
698             if (status==HttpResponse.__304_Not_Modified ||
699                 status==HttpResponse.__204_No_Content)
700             {
701                 if (_persistent && _keepAlive && _dotVersion==0)
702                     _response.setField(HttpFields.__Connection,
703                                        HttpFields.__KeepAlive);
704             }
705             else
706             {
707                 if(_persistent)
708                 {
709                     switch (_dotVersion)
710                     {
711                     case 0:
712                         {
713                             _close=true;
714                             _persistent=false;
715                             _response.setField(HttpFields.__Connection,
716                                                HttpFields.__Close);
717                         }
718                         break;
719                     case 1:
720                         {
721                             // force chunking on.
722                             _response.setField(HttpFields.__TransferEncoding,
723                                                HttpFields.__Chunked);
724                             _outputStream.setChunking();
725                         }
726                         break;
727                         
728                     default:
729                         _close=true;
730                         _response.setField(HttpFields.__Connection,
731                                            HttpFields.__Close);
732                         break;
733                     }
734                 }
735                 else
736                 {
737                     _close=true;
738                     _response.setField(HttpFields.__Connection,
739                                        HttpFields.__Close);
740                 }
741             }
742         }
743 
744         _outputStream.writeHeader(_response);
745     }
746 
747     
748     /* ------------------------------------------------------------ */
749     /* Exception reporting policy method.
750      * @param e the Throwable to report.
751      */
752     private void exception(Throwable e)
753     {
754   try{
755       _persistent=false;
756             int error_code=HttpResponse.__500_Internal_Server_Error;
757             
758             if (e instanceof HttpException)
759             {
760                 error_code=((HttpException)e).getCode();
761                 
762                 if (_request==null)
763                     log.warn(e.toString());
764                 else
765                     log.warn(_request.getRequestLine()+" "+e.toString());
766                 log.debug(LogSupport.EXCEPTION,e);
767             }
768             else if (e instanceof EOFException)
769             {
770                 LogSupport.ignore(log,e);
771                 return;
772             }
773             else
774             {
775                 _request.setAttribute("javax.servlet.error.exception_type",e.getClass());
776                 _request.setAttribute("javax.servlet.error.exception",e);
777 
778                 if (_request==null)
779                     log.warn(LogSupport.EXCEPTION,e);
780                 else
781                     log.warn(_request.getRequestLine(),e);
782             }
783             
784       if (_response != null && !_response.isCommitted())
785       {
786     _response.reset();
787     _response.removeField(HttpFields.__TransferEncoding);
788     _response.setField(HttpFields.__Connection,HttpFields.__Close);
789     _response.sendError(error_code);
790       }
791   }
792         catch(Exception ex)
793         {
794             LogSupport.ignore(log,ex);
795         }
796     }
797 
798     
799     /* ------------------------------------------------------------ */
800     /** Service a Request.
801      * This implementation passes the request and response to the
802      * service method of the HttpServer for this connections listener.
803      * If no HttpServer has been associated, the 503 is returned.
804      * This method may be specialized to implement other ways of
805      * servicing a request.
806      * @param request The request
807      * @param response The response
808      * @return The HttpContext that completed handling of the request or null.
809      * @exception HttpException 
810      * @exception IOException 
811      */
812     protected HttpContext service(HttpRequest request, HttpResponse response)
813         throws HttpException, IOException
814     {
815         if (_httpServer==null)
816                 throw new HttpException(HttpResponse.__503_Service_Unavailable);
817         return _httpServer.service(request,response);
818     }
819     
820     /* ------------------------------------------------------------ */
821     /** Handle the connection.
822      * Once the connection has been created, this method is called
823      * to handle one or more requests that may be received on the
824      * connection.  The method only returns once all requests have been
825      * handled, an error has been returned to the requestor or the
826      * connection has been closed.
827      * The handleNext() is called in a loop until it returns false.
828      */
829     public final void handle()
830     {
831         try
832         {
833             associateThread();
834             while(_listener.isStarted() && handleNext())
835                 recycle();
836         }
837         finally
838         {
839             disassociateThread();
840             destroy();
841         }
842     }
843     
844     /* ------------------------------------------------------------ */
845     protected void associateThread()
846     {
847         __threadConnection.set(this);
848         _handlingThread=Thread.currentThread();
849     }
850     
851     /* ------------------------------------------------------------ */
852     protected void disassociateThread()
853     {
854         _handlingThread=null;
855         __threadConnection.set(null);
856     }
857 
858     
859     /* ------------------------------------------------------------ */
860     protected void readRequest()
861         throws IOException
862     {
863         log.debug("readRequest() ...");
864         _request.readHeader((LineInput)(_inputStream)
865                             .getInputStream());
866     }
867     
868     /* ------------------------------------------------------------ */
869     /** Handle next request off the connection.
870      * The service(request,response) method is called by handle to
871      * service each request received on the connection.
872      * If the thread is a PoolThread, the thread is set as inactive
873      * when waiting for a request. 
874      * <P>
875      * If a HttpTunnel has been set on this connection, it's handle method is
876      * called and when that completes, false is return from this method.
877      * <P>
878      * The Connection is set as a ThreadLocal of the calling thread and is
879      * available via the getHttpConnection() method.
880      * @return true if the connection is still open and may provide
881      * more requests.
882      */
883     public boolean handleNext()
884     {
885         // Handle a HTTP tunnel
886         if (_tunnel!=null)
887         {
888             if(log.isDebugEnabled())log.debug("Tunnel: "+_tunnel);
889             _outputStream.resetObservers();
890             _tunnel.handle(_inputStream.getInputStream(),
891                            _outputStream.getOutputStream());
892             return false;
893         }
894 
895         // Normal handling.
896         HttpContext context=null;
897         try
898         {   
899             // Assume the connection is not persistent,
900             // unless told otherwise.
901             _persistent=false;
902             _close=false;
903             _keepAlive=false;
904             _firstWrite=false;
905             _dotVersion=0;
906 
907             // Read requests
908             readRequest();
909             _listener.customizeRequest(this,_request);
910             if (_request.getState()!=HttpMessage.__MSG_RECEIVED)
911                 throw new HttpException(HttpResponse.__400_Bad_Request);
912             
913             // We have a valid request!
914             statsRequestStart();
915             if (log.isDebugEnabled())
916             {
917                 _response.setField("Jetty-Request",
918                                    _request.getRequestLine());
919                 if(log.isDebugEnabled())log.debug("REQUEST:\n"+_request);
920             }
921             
922             // Pick response version, we assume that _request.getVersion() == 1
923             _dotVersion=_request.getDotVersion();
924             
925             if (_dotVersion>1)
926             {
927                 _dotVersion=1;
928             }
929             
930             // Common fields on the response
931             _response.setVersion(HttpMessage.__HTTP_1_1);
932             _response.setField(HttpFields.__Date,_request.getTimeStampStr());
933             _response.setField(HttpFields.__Server,Version.__VersionDetail);
934             // _response.setField(HttpFields.__ServletEngine,Version.__ServletEngine);
935             
936             // Handle Connection header field
937             Enumeration connectionValues =
938                 _request.getFieldValues(HttpFields.__Connection,
939                                         HttpFields.__separators);
940             if (connectionValues!=null)
941             {
942                 while (connectionValues.hasMoreElements())
943                 {
944                     String token=connectionValues.nextElement().toString();
945                     // handle close token
946                     if (token.equalsIgnoreCase(HttpFields.__Close))
947                     {
948                         _close=true;
949                         _response.setField(HttpFields.__Connection,
950                                            HttpFields.__Close);
951                     }
952                     else if (token.equalsIgnoreCase(HttpFields.__KeepAlive) &&
953                              _dotVersion==0)
954                         _keepAlive=true;
955                     
956                     // Remove headers for HTTP/1.0 requests
957                     if (_dotVersion==0)
958                         _request.forceRemoveField(token);
959                 }
960             }
961             
962             // Handle version specifics
963             if (_dotVersion==1)
964                 verifyHTTP_1_1();
965             else if (_dotVersion==0)
966                 verifyHTTP_1_0();
967             else if (_dotVersion!=-1)
968                 throw new HttpException(HttpResponse.__505_HTTP_Version_Not_Supported);
969             
970             if(LogSupport.isTraceEnabled(log))log.trace("IN is "+(_inputStream.isChunking()?"chunked":"not chunked")+" Content-Length="+_inputStream.getContentLength());
971 
972 
973             // handle HttpListener handlers
974             if (!_request.isHandled() && _listener.getHttpHandler()!=null)
975                 _listener.getHttpHandler().handle("",null, _request, _response);
976             
977             // service the request
978             if (!_request.isHandled())
979                 context=service(_request,_response);
980         }
981         catch(HttpException e) {exception(e);}
982         catch (IOException e)
983         {
984             if (_request.getState()!=HttpMessage.__MSG_RECEIVED)
985             {
986                 if (log.isDebugEnabled())
987                 {
988                     if (LogSupport.isTraceEnabled(log)) log.trace(LogSupport.EXCEPTION,e);
989                     else if(log.isDebugEnabled())log.debug(e.toString());
990                 }
991                 _response.destroy();
992                 _response=null;
993             }
994             else
995                 exception(e);
996         }
997         catch (Exception e)     {exception(e);}
998         catch (Error e)         {exception(e);}
999         finally
1000        {
1001            int bytes_written=0;
1002            int content_length = _response==null
1003                ?-1:_response.getIntField(HttpFields.__ContentLength);
1004                
1005            // Complete the request
1006            if (_persistent)
1007            {                    
1008                try{
1009                    // Read remaining input
1010                    while(_inputStream.skip(4096)>0 || _inputStream.read()>=0);
1011                }
1012                catch(IOException e)
1013                {
1014                    if (_inputStream.getContentLength()>0)
1015                        _inputStream.setContentLength(0);
1016                    _persistent=false;
1017                    LogSupport.ignore(log,e);
1018                    exception(new HttpException(HttpResponse.__400_Bad_Request,
1019                                                "Missing Content"));
1020                }
1021                    
1022                // Check for no more content
1023                if (_inputStream.getContentLength()>0)
1024                {
1025                    _inputStream.setContentLength(0);
1026                    _persistent=false;
1027                    exception (new HttpException(HttpResponse.__400_Bad_Request,
1028                                                 "Missing Content"));
1029                }
1030                
1031                // Commit the response
1032                try{
1033                    _outputStream.close();
1034                    bytes_written=_outputStream.getBytesWritten();
1035                    _outputStream.resetStream();
1036                    _outputStream.addObserver(this);
1037                    _inputStream.resetStream();
1038                }
1039                catch(IOException e) {exception(e);}
1040            }
1041            else if (_response!=null) // There was a request
1042            {
1043                // half hearted attempt to eat any remaining input
1044                try{
1045                    if (_inputStream.getContentLength()>0)
1046                        while(_inputStream.skip(4096)>0 || _inputStream.read()>=0);
1047                    _inputStream.resetStream();
1048                }
1049                catch(IOException e){LogSupport.ignore(log,e);}
1050                
1051                // commit non persistent
1052                try{
1053                    _outputStream.flush();
1054                    _response.commit();
1055                    bytes_written=_outputStream.getBytesWritten();
1056                    _outputStream.close();
1057                    _outputStream.resetStream();
1058                }
1059                catch(IOException e) {exception(e);}
1060            }
1061            
1062            // Check response length
1063            if (_response!=null)
1064            {
1065                if(log.isDebugEnabled())log.debug("RESPONSE:\n"+_response);
1066                if (_persistent &&
1067                    content_length>=0 && bytes_written>0 && content_length!=bytes_written)
1068                {
1069                    log.warn("Invalid length: Content-Length="+content_length+
1070                                 " written="+bytes_written+
1071                                 " for "+_request.getRequestURL());
1072                    _persistent=false;
1073                    try{_outputStream.close();}
1074                    catch(IOException e) {log.warn(LogSupport.EXCEPTION,e);}
1075                }    
1076            }
1077            
1078            // stats & logging
1079            statsRequestEnd();       
1080            if (context!=null)
1081                context.log(_request,_response,bytes_written);
1082        }
1083        
1084        return (_tunnel!=null) || _persistent;
1085    }
1086
1087    /* ------------------------------------------------------------ */
1088    protected void statsRequestStart()
1089    {
1090        if (_statsOn)
1091        {
1092            if (_reqTime>0)
1093                statsRequestEnd();
1094            _requests++;
1095            _tmpTime=_request.getTimeStamp();
1096            _reqTime=_tmpTime;
1097            _httpServer.statsGotRequest();
1098        }
1099    }
1100
1101    /* ------------------------------------------------------------ */
1102    protected void statsRequestEnd()
1103    {
1104        if (_statsOn && _reqTime>0)
1105        {
1106            _httpServer.statsEndRequest(System.currentTimeMillis()-_reqTime,
1107                                        (_response!=null));
1108            _reqTime=0;
1109        }
1110    }
1111    
1112    /* ------------------------------------------------------------ */
1113    /** Recycle the connection.
1114     * called by handle when handleNext returns true.
1115     */
1116    protected void recycle()
1117    {
1118        _listener.persistConnection(this);
1119        if (_request!=null)
1120            _request.recycle(this);
1121        if (_response!=null)
1122            _response.recycle(this);
1123    }
1124    
1125    /* ------------------------------------------------------------ */
1126    /** Destroy the connection.
1127     * called by handle when handleNext returns false.
1128     */
1129    protected void destroy()
1130    {
1131        try{close();}
1132        catch (IOException e){LogSupport.ignore(log,e);}
1133        catch (Exception e){log.warn(LogSupport.EXCEPTION,e);}
1134        
1135        // Destroy request and response
1136        if (_request!=null)
1137            _request.destroy();
1138        if (_response!=null)
1139            _response.destroy();
1140        if (_inputStream!=null)
1141            _inputStream.destroy();
1142        if (_outputStream!=null)
1143            _outputStream.destroy();
1144        _inputStream=null;
1145        _outputStream=null;
1146        _request=null;
1147        _response=null;
1148        _handlingThread=null;
1149        
1150        if (_statsOn)
1151        {
1152            _tmpTime=System.currentTimeMillis();
1153            if (_reqTime>0)
1154                _httpServer.statsEndRequest(_tmpTime-_reqTime,false);
1155            _httpServer.statsCloseConnection(_tmpTime-_openTime,_requests);
1156        }
1157    }
1158}