KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mortbay > http > HttpConnection


1 // ========================================================================
2
// $Id: HttpConnection.java,v 1.94 2006/02/28 12:45:01 gregwilkins Exp $
3
// Copyright 199-2004 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.http;
17
18 import java.io.IOException JavaDoc;
19 import java.io.InputStream JavaDoc;
20 import java.io.OutputStream JavaDoc;
21 import java.net.InetAddress JavaDoc;
22 import java.net.Socket JavaDoc;
23 import java.util.Enumeration JavaDoc;
24 import java.util.List JavaDoc;
25
26 import javax.net.ssl.SSLSocket;
27
28 import org.apache.commons.logging.Log;
29 import org.mortbay.log.LogFactory;
30 import org.mortbay.util.InetAddrPort;
31 import org.mortbay.util.LineInput;
32 import org.mortbay.util.LogSupport;
33 import org.mortbay.util.OutputObserver;
34 import org.mortbay.util.StringUtil;
35
36
37 /* ------------------------------------------------------------ */
38 /** A HTTP Connection.
39  * This class provides the generic HTTP handling for
40  * a connection to a HTTP server. An instance of HttpConnection
41  * is normally created by a HttpListener and then given control
42  * in order to run the protocol handling before and after passing
43  * a request to the HttpServer of the HttpListener.
44  *
45  * This class is not synchronized as it should only ever be known
46  * to a single thread.
47  *
48  * @see HttpListener
49  * @see HttpServer
50  * @version $Id: HttpConnection.java,v 1.94 2006/02/28 12:45:01 gregwilkins Exp $
51  * @author Greg Wilkins (gregw)
52  */

53 public class HttpConnection
54     implements OutputObserver
55 {
56     private static Log log = LogFactory.getLog(HttpConnection.class);
57
58     /* ------------------------------------------------------------ */
59     private static ThreadLocal JavaDoc __threadConnection=new ThreadLocal JavaDoc();
60
61     /** Support for FRC2068 Continues.
62      * If true, then 100 Continues will be sent when expected or for POST requests. If false, 100 Continues will
63      * only be sent if expected. Can be configured with the org.mortbay.http.HttpConnection.2068Continue system
64      * property.
65      */

66     private static boolean __2068_Continues=Boolean.getBoolean("org.mortbay.http.HttpConnection.2068Continue");
67     
68     /* ------------------------------------------------------------ */
69     protected HttpRequest _request;
70     protected HttpResponse _response;
71     protected boolean _persistent;
72     protected boolean _keepAlive;
73     protected int _dotVersion;
74
75     private HttpListener _listener;
76     private HttpInputStream _inputStream;
77     private HttpOutputStream _outputStream;
78     private boolean _close;
79     private boolean _firstWrite;
80     private boolean _completing;
81     private Thread JavaDoc _handlingThread;
82     
83     private InetAddress JavaDoc _remoteInetAddress;
84     private String JavaDoc _remoteAddr;
85     private String JavaDoc _remoteHost;
86     private HttpServer _httpServer;
87     private Object JavaDoc _connection;
88     private boolean _throttled ;
89     
90     private boolean _statsOn;
91     private long _tmpTime;
92     private long _openTime;
93     private long _reqTime;
94     private int _requests;
95     private Object JavaDoc _object;
96     private HttpTunnel _tunnel ;
97     private boolean _resolveRemoteHost;
98     
99     /* ------------------------------------------------------------ */
100     /** Constructor.
101      * @param listener The listener that created this connection.
102      * @param remoteAddr The address of the remote end or null.
103      * @param in InputStream to read request(s) from.
104      * @param out OutputputStream to write response(s) to.
105      * @param connection The underlying connection object, most likely
106      * a socket. This is not used by HttpConnection other than to make
107      * it available via getConnection().
108      */

109     public HttpConnection(HttpListener listener,
110                           InetAddress JavaDoc remoteAddr,
111                           InputStream JavaDoc in,
112                           OutputStream JavaDoc out,
113                           Object JavaDoc connection)
114     {
115         if(log.isDebugEnabled())log.debug("new HttpConnection: "+connection);
116         _listener=listener;
117         _remoteInetAddress=remoteAddr;
118         int bufferSize=listener==null?4096:listener.getBufferSize();
119         int reserveSize=listener==null?512:listener.getBufferReserve();
120         _inputStream=new HttpInputStream(in,bufferSize);
121         _outputStream=new HttpOutputStream(out,bufferSize,reserveSize);
122         _outputStream.addObserver(this);
123         _firstWrite=false;
124         if (_listener!=null)
125             _httpServer=_listener.getHttpServer();
126         _connection=connection;
127         
128         _statsOn=_httpServer!=null && _httpServer.getStatsOn();
129         if (_statsOn)
130         {
131             _openTime=System.currentTimeMillis();
132             _httpServer.statsOpenConnection();
133         }
134         _reqTime=0;
135         _requests=0;
136         
137         _request = new HttpRequest(this);
138         _response = new HttpResponse(this);
139
140         _resolveRemoteHost=_listener!=null && _listener.getHttpServer()!=null && _listener.getHttpServer().getResolveRemoteHost();
141     }
142
143     /* ------------------------------------------------------------ */
144     /** Get the ThreadLocal HttpConnection.
145      * The ThreadLocal HttpConnection is set by the handle() method.
146      * @return HttpConnection for this thread.
147      */

148     static HttpConnection getHttpConnection()
149     {
150         return (HttpConnection)__threadConnection.get();
151     }
152     
153     /* ------------------------------------------------------------ */
154     /** Get the Remote address.
155      * @return the remote address
156      */

157     public InetAddress JavaDoc getRemoteInetAddress()
158     {
159         return _remoteInetAddress;
160     }
161
162     /* ------------------------------------------------------------ */
163     /** Get the Remote address.
164      * @return the remote host name
165      */

166     public String JavaDoc getRemoteAddr()
167     {
168         if (_remoteAddr==null)
169         {
170             if (_remoteInetAddress==null)
171                 return "127.0.0.1";
172             _remoteAddr=_remoteInetAddress.getHostAddress();
173         }
174         return _remoteAddr;
175     }
176     
177     /* ------------------------------------------------------------ */
178     /** Get the Remote address.
179      * @return the remote host name
180      */

181     public String JavaDoc getRemoteHost()
182     {
183         if (_remoteHost==null)
184         {
185             if (_resolveRemoteHost)
186             {
187                 if (_remoteInetAddress==null)
188                     return "localhost";
189                 _remoteHost=_remoteInetAddress.getHostName();
190             }
191             else
192             {
193                 if (_remoteInetAddress==null)
194                     return "127.0.0.1";
195                 _remoteHost=getRemoteAddr();
196             }
197         }
198         return _remoteHost;
199     }
200     
201     /* ------------------------------------------------------------ */
202     /** Get the connections InputStream.
203      * @return the connections InputStream
204      */

205     public HttpInputStream getInputStream()
206     {
207         return _inputStream;
208     }
209     
210     /* ------------------------------------------------------------ */
211     /** Get the connections OutputStream.
212      * @return the connections OutputStream
213      */

214     public HttpOutputStream getOutputStream()
215     {
216         return _outputStream;
217     }
218
219     /* ------------------------------------------------------------ */
220     /** Get the underlying connection object.
221      * This opaque object, most likely a socket. This is not used by
222      * HttpConnection other than to make it available via getConnection().
223      * @return Connection abject
224      */

225     public Object JavaDoc getConnection()
226     {
227         return _connection;
228     }
229     
230     /* ------------------------------------------------------------ */
231     /** Get the request.
232      * @return the request
233      */

234     public HttpRequest getRequest()
235     {
236         return _request;
237     }
238     
239     /* ------------------------------------------------------------ */
240     /** Get the response.
241      * @return the response
242      */

243     public HttpResponse getResponse()
244     {
245         return _response;
246     }
247
248     /* ------------------------------------------------------------ */
249     /** Force the connection to not be persistent.
250      */

251     public void forceClose()
252     {
253         _persistent=false;
254         _close=true;
255     }
256     
257     /* ------------------------------------------------------------ */
258     /** Close the connection.
259      * This method calls close on the input and output streams and
260      * interrupts any thread in the handle method.
261      * may be specialized to close sockets etc.
262      * @exception IOException
263      */

264     public void close()
265         throws IOException JavaDoc
266     {
267         try{
268             _completing=true;
269             if (_connection instanceof Socket JavaDoc && !(_connection instanceof SSLSocket))
270                 ((Socket JavaDoc)_connection).shutdownOutput();
271             _outputStream.close();
272             _inputStream.close();
273         }
274         finally
275         {
276             if (_handlingThread!=null && Thread.currentThread()!=_handlingThread)
277                 _handlingThread.interrupt();
278         }
279     }
280     
281     /* ------------------------------------------------------------ */
282     /** Get the connections listener.
283      * @return HttpListener that created this Connection.
284      */

285     public HttpListener getListener()
286     {
287         return _listener;
288     }
289
290     /* ------------------------------------------------------------ */
291     /** Get the listeners HttpServer .
292      * Conveniance method equivalent to getListener().getHttpServer().
293      * @return HttpServer.
294      */

295     public HttpServer getHttpServer()
296     {
297         return _httpServer;
298     }
299
300     /* ------------------------------------------------------------ */
301     /** Get the listeners Default scheme.
302      * Conveniance method equivalent to getListener().getDefaultProtocol().
303      * @return HttpServer.
304      */

305     public String JavaDoc getDefaultScheme()
306     {
307         return _listener.getDefaultScheme();
308     }
309     
310     /* ------------------------------------------------------------ */
311     /** Get the listeners HttpServer.
312      * But if the name is 0.0.0.0, then the real interface address is used.
313      * @return HttpServer.
314      */

315     public String JavaDoc getServerName()
316     {
317         String JavaDoc host=_listener.getHost();
318         if (InetAddrPort.__0_0_0_0.equals(host) &&
319             _connection instanceof Socket JavaDoc)
320             host = ((Socket JavaDoc)_connection).getLocalAddress().getHostName();
321         
322         return host;
323     }
324     
325     /* ------------------------------------------------------------ */
326     /** Get the listeners HttpServer.
327      * @return HttpServer.
328      */

329     public String JavaDoc getServerAddr()
330     {
331         if (_connection instanceof Socket JavaDoc)
332             return ((Socket JavaDoc)_connection).getLocalAddress().getHostAddress();
333         return _listener.getHost();
334     }
335     
336     /* ------------------------------------------------------------ */
337     /** Get the listeners Port .
338      * Conveniance method equivalent to getListener().getPort().
339      * @return local port.
340      */

341     public int getServerPort()
342     {
343         return _listener.getPort();
344     }
345     
346     /* ------------------------------------------------------------ */
347     /** Get the remote Port .
348      * @return remote port.
349      */

350     public int getRemotePort()
351     {
352         if (_connection instanceof Socket JavaDoc)
353             return ((Socket JavaDoc)_connection).getPort();
354         return 0;
355     }
356
357     /* ------------------------------------------------------------ */
358     /**
359      * @return True if this connections state has been altered due
360      * to low resources.
361      */

362     public boolean isThrottled()
363     {
364         return _throttled;
365     }
366     
367      /* ------------------------------------------------------------ */
368     /**
369      * @param throttled True if this connections state has been altered due
370      * to low resources.
371      */

372     public void setThrottled(boolean throttled)
373     {
374         _throttled = throttled;
375     }
376     
377     /* ------------------------------------------------------------ */
378     /** Get associated object.
379      * Used by a particular HttpListener implementation to associate
380      * private datastructures with the connection.
381      * @return An object associated with the connecton by setObject.
382      */

383     public Object JavaDoc getObject()
384     {
385         return _object;
386     }
387     
388     /* ------------------------------------------------------------ */
389     /** Set associated object.
390      * Used by a particular HttpListener implementation to associate
391      * private datastructures with the connection.
392      * @param o An object associated with the connecton.
393      */

394     public void setObject(Object JavaDoc o)
395     {
396         _object=o;
397     }
398
399     /* ------------------------------------------------------------ */
400     /**
401      * @return The HttpTunnel set for the connection or null.
402      */

403     public HttpTunnel getHttpTunnel()
404     {
405         return _tunnel;
406     }
407     
408     /* ------------------------------------------------------------ */
409     /** Set a HttpTunnel for the connection.
410      * A HTTP tunnel is used if the connection is to be taken over for
411      * non-HTTP communications. An example of this is the CONNECT method
412      * in proxy handling. If a HttpTunnel is set on a connection, then it's
413      * handle method is called bu the next call to handleNext().
414      * @param tunnel The HttpTunnel set for the connection or null.
415      */

416     public void setHttpTunnel(HttpTunnel tunnel)
417     {
418         _tunnel = tunnel;
419     }
420     
421     /* ------------------------------------------------------------ */
422     /* Verify HTTP/1.0 request
423      * @exception HttpException problem with the request.
424      * @exception IOException problem with the connection.
425      */

426     private void verifyHTTP_1_0()
427     {
428         // Set content length
429
int content_length=
430             _request.getIntField(HttpFields.__ContentLength);
431         if (content_length>=0)
432             _inputStream.setContentLength(content_length);
433         else if (content_length<0)
434         {
435             // TODO - can't do this check because IE does this after
436
// a redirect.
437
// Can't have content without a content length
438
// String content_type=_request.getField(HttpFields.__ContentType);
439
// if (content_type!=null && content_type.length()>0)
440
// throw new HttpException(_HttpResponse.__411_Length_Required);
441
_inputStream.setContentLength(0);
442         }
443
444         // Check netscape proxy connection - this is not strictly correct.
445
if (!_keepAlive && HttpFields.__KeepAlive.equalsIgnoreCase(_request.getField(HttpFields.__ProxyConnection)))
446             _keepAlive=true;
447
448         // persistent connections in HTTP/1.0 only if requested.
449
_persistent=_keepAlive;
450     }
451     
452     /* ------------------------------------------------------------ */
453     /* Verify HTTP/1.1 request
454      * @exception HttpException problem with the request.
455      * @exception IOException problem with the connection.
456      */

457     private void verifyHTTP_1_1()
458         throws HttpException, IOException JavaDoc
459     {
460         // Check Host Field exists
461
String JavaDoc host=_request.getField(HttpFields.__Host);
462         if (host==null)
463             throw new HttpException(HttpResponse.__400_Bad_Request);
464         
465         // check and enable requests transfer encodings.
466
String JavaDoc transfer_coding=
467             _request.getField(HttpFields.__TransferEncoding);
468
469         if (transfer_coding!=null && transfer_coding.length()>0)
470         {
471             // Handling of codings other than chunking is now
472
// the responsibility of handlers, filters or servlets.
473
// Thanks to the compression filter, we now don't know if
474
// what we can handle here.
475
if (transfer_coding.equalsIgnoreCase(HttpFields.__Chunked) ||
476                 StringUtil.endsWithIgnoreCase(transfer_coding,HttpFields.__Chunked))
477                 _inputStream.setChunking();
478             else if (StringUtil.asciiToLowerCase(transfer_coding)
479                      .indexOf(HttpFields.__Chunked)>=0)
480                 throw new HttpException(HttpResponse.__400_Bad_Request);
481         }
482         
483         // Check input content length can be determined
484
int content_length=_request.getIntField(HttpFields.__ContentLength);
485         String JavaDoc content_type=_request.getField(HttpFields.__ContentType);
486         if (!_inputStream.isChunking())
487         {
488             // If we have a content length, use it
489
if (content_length>=0)
490                 _inputStream.setContentLength(content_length);
491             // else if we have no content
492
else if (content_type==null || content_type.length()==0)
493                 _inputStream.setContentLength(0);
494             // else we need a content length
495
else
496             {
497                 // TODO - can't do this check as IE stuff up on
498
// a redirect.
499
// throw new HttpException(HttpResponse.__411_Length_Required);
500
_inputStream.setContentLength(0);
501             }
502         }
503
504         // Handle Continue Expectations
505
String JavaDoc expect=_request.getField(HttpFields.__Expect);
506         if (expect!=null && expect.length()>0)
507         {
508             if (StringUtil.asciiToLowerCase(expect).equals(HttpFields.__ExpectContinue))
509             {
510                 _inputStream.setExpectContinues(_outputStream.getOutputStream());
511             }
512             else
513                 throw new HttpException(HttpResponse.__417_Expectation_Failed);
514         }
515         else if (__2068_Continues &&
516                  _inputStream.available()<=0 &&
517                  (HttpRequest.__PUT.equals(_request.getMethod()) ||
518                   HttpRequest.__POST.equals(_request.getMethod())))
519         {
520             // Send continue for RFC 2068 exception
521
OutputStream JavaDoc real_out=_outputStream.getOutputStream();
522             real_out.write(HttpResponse.__Continue);
523             real_out.flush();
524         }
525              
526         // Persistent unless requested otherwise
527
_persistent=!_close;
528     }
529
530     /* ------------------------------------------------------------ */
531     /** Output Notifications.
532      * Trigger header and/or filters from output stream observations.
533      * Also finalizes method of indicating response content length.
534      * Called as a result of the connection subscribing for notifications
535      * to the HttpOutputStream.
536      * @see HttpOutputStream
537      * @param out The output stream observed.
538      * @param action The action.
539      */

540     public void outputNotify(OutputStream JavaDoc out, int action, Object JavaDoc ignoredData)
541         throws IOException JavaDoc
542     {
543         if (_response==null)
544             return;
545
546         switch(action)
547         {
548           case OutputObserver.__FIRST_WRITE:
549               if (!_firstWrite)
550               {
551                   firstWrite();
552                   _firstWrite=true;
553               }
554               break;
555               
556           case OutputObserver.__RESET_BUFFER:
557               resetBuffer();
558               break;
559               
560           case OutputObserver.__COMMITING:
561               commit();
562               break;
563               
564           case OutputObserver.__CLOSING:
565               
566               if (_response!=null)
567               {
568                   completing();
569                   if (!_response.isCommitted() &&
570                   _request.getState()==HttpMessage.__MSG_RECEIVED)
571                   commit();
572               }
573               break;
574               
575           case OutputObserver.__CLOSED:
576               break;
577         }
578     }
579
580     /* ------------------------------------------------------------ */
581     /** Setup the reponse output stream.
582      * Use the current state of the request and response, to set tranfer
583      * parameters such as chunking and content length.
584      */

585     protected void firstWrite()
586         throws IOException JavaDoc
587     {
588         if (_response.isCommitted())
589             return;
590         
591         // Nobble the OutputStream for HEAD requests
592
if (HttpRequest.__HEAD.equals(_request.getMethod()))
593             _outputStream.nullOutput();
594             
595         int length=_response.getIntField(HttpFields.__ContentLength);
596         if (length>=0)
597             _outputStream.setContentLength(length);
598     }
599
600     /**
601      * Reset headers after response reset
602      */

603     private void resetBuffer()
604     {
605     }
606
607     /* ------------------------------------------------------------ */
608     /* Signal that the next commit/flush is the last */
609     void completing()
610     {
611         _completing=true;
612     }
613     
614     /* ------------------------------------------------------------ */
615     protected void commit()
616         throws IOException JavaDoc
617     {
618         if (_response.isCommitted())
619             return;
620
621         int status=_response.getStatus();
622         int length=-1;
623         
624         // Check if there is missing content expectations
625
if (_inputStream.getExpectContinues()!=null)
626         {
627             // No input read yet - so assume it never will be
628
_inputStream.setExpectContinues(null);
629             _inputStream.unsafeSetContentLength(0);
630         }
631         
632         // Handler forced close, listener stopped or no idle threads left.
633
boolean has_close=HttpFields.__Close.equals(_response.getField(HttpFields.__Connection));
634         if (!_persistent || _close || _listener!=null && (!_listener.isStarted()||_listener.isOutOfResources()))
635         {
636             _close=true;
637             if (!has_close)
638                 _response.setField(HttpFields.__Connection,HttpFields.__Close);
639             has_close=true;
640         }
641         if (_close)
642             _persistent=false;
643         
644         // Determine how to limit content length
645
if (_persistent)
646         {
647             switch(_dotVersion)
648             {
649                 case 1:
650                 {
651                     String JavaDoc transfer_coding=_response.getField(HttpFields.__TransferEncoding);
652                     if (transfer_coding==null || transfer_coding.length()==0 || HttpFields.__Identity.equalsIgnoreCase(transfer_coding))
653                     {
654                         // if (can have content and no content length)
655
if (status!=HttpResponse.__304_Not_Modified &&
656                             status!=HttpResponse.__204_No_Content &&
657                             _response.getField(HttpFields.__ContentLength)==null)
658                         {
659                             if (_completing)
660                             {
661                                 length=_outputStream.getBytesWritten();
662                                 _response.setContentLength(length);
663                             }
664                             else
665                             {
666                                 // Chunk it!
667
_response.setField(HttpFields.__TransferEncoding,HttpFields.__Chunked);
668                                 _outputStream.setChunking();
669                             }
670                         }
671                     }
672                     else
673                     {
674                         // Use transfer encodings to determine length
675
_response.removeField(HttpFields.__ContentLength);
676                         _outputStream.setChunking();
677
678                         if (!HttpFields.__Chunked.equalsIgnoreCase(transfer_coding))
679                         {
680                             // Check against any TE field
681
List JavaDoc te = _request.getAcceptableTransferCodings();
682                             Enumeration JavaDoc enm =
683                                 _response.getFieldValues(HttpFields.__TransferEncoding,
684                                                          HttpFields.__separators);
685                             while (enm.hasMoreElements())
686                             {
687                                 String JavaDoc coding=(String JavaDoc)enm.nextElement();
688                                 if (HttpFields.__Identity.equalsIgnoreCase(coding) ||
689                                     HttpFields.__Chunked.equalsIgnoreCase(coding))
690                                     continue;
691                                 if (te==null || !te.contains(coding))
692                                     throw new HttpException(HttpResponse.__501_Not_Implemented,coding);
693                             }
694                         }
695                     }
696                 }
697                 break;
698                 
699                 case 0:
700                 {
701                     // if (can have content and no content length)
702
_response.removeField(HttpFields.__TransferEncoding);
703                     if (_keepAlive)
704                     {
705                         if (status!=HttpResponse.__304_Not_Modified &&
706                             status!=HttpResponse.__204_No_Content &&
707                             _response.getField(HttpFields.__ContentLength)==null)
708                         {
709                             if (_completing)
710                             {
711                                 length=_outputStream.getBytesWritten();
712                                 _response.setContentLength(length);
713                                 _response.setField(HttpFields.__Connection,HttpFields.__KeepAlive);
714                             }
715                             else
716                             {
717                                 _response.setField(HttpFields.__Connection,HttpFields.__Close);
718                                 has_close=_close=true;
719                                 _persistent=false;
720                             }
721                         }
722                         else
723                             _response.setField(HttpFields.__Connection,HttpFields.__KeepAlive);
724                     }
725                     else if (!has_close)
726                         _response.setField(HttpFields.__Connection,HttpFields.__Close);
727                         
728                         
729                     break;
730                 }
731                 default:
732                 {
733                     _close=true;
734                     _persistent=false;
735                     _keepAlive=false;
736                 }
737             }
738         }
739         
740         
741         // Mark request as handled.
742
_request.setHandled(true);
743         
744         _outputStream.writeHeader(_response);
745         _outputStream.flush();
746         
747     }
748
749     
750     /* ------------------------------------------------------------ */
751     /* Exception reporting policy method.
752      * @param e the Throwable to report.
753      */

754     private void exception(Throwable JavaDoc e)
755     {
756         try
757         {
758             _persistent=false;
759             int error_code=HttpResponse.__500_Internal_Server_Error;
760             
761             if (e instanceof HttpException)
762             {
763                 error_code=((HttpException)e).getCode();
764                 
765                 if (_request==null)
766                     log.warn(e.toString());
767                 else
768                     log.warn(_request.getRequestLine()+" "+e.toString());
769                 log.debug(LogSupport.EXCEPTION,e);
770             }
771             else if (e instanceof EOFException)
772             {
773                 LogSupport.ignore(log,e);
774                 return;
775             }
776             else
777             {
778                 _request.setAttribute("javax.servlet.error.exception_type",e.getClass());
779                 _request.setAttribute("javax.servlet.error.exception",e);
780                 
781                 if (_request==null)
782                     log.warn(LogSupport.EXCEPTION,e);
783                 else
784                     log.warn(_request.getRequestLine(),e);
785             }
786             
787             if (_response != null && !_response.isCommitted())
788             {
789                 _response.reset();
790                 _response.removeField(HttpFields.__TransferEncoding);
791                 _response.setField(HttpFields.__Connection,HttpFields.__Close);
792                 _response.sendError(error_code);
793             }
794         }
795         catch(Exception JavaDoc ex)
796         {
797             LogSupport.ignore(log,ex);
798         }
799     }
800
801     
802     /* ------------------------------------------------------------ */
803     /** Service a Request.
804      * This implementation passes the request and response to the
805      * service method of the HttpServer for this connections listener.
806      * If no HttpServer has been associated, the 503 is returned.
807      * This method may be specialized to implement other ways of
808      * servicing a request.
809      * @param request The request
810      * @param response The response
811      * @return The HttpContext that completed handling of the request or null.
812      * @exception HttpException
813      * @exception IOException
814      */

815     protected HttpContext service(HttpRequest request, HttpResponse response)
816         throws HttpException, IOException JavaDoc
817     {
818         if (_httpServer==null)
819                 throw new HttpException(HttpResponse.__503_Service_Unavailable);
820         return _httpServer.service(request,response);
821     }
822     
823     /* ------------------------------------------------------------ */
824     /** Handle the connection.
825      * Once the connection has been created, this method is called
826      * to handle one or more requests that may be received on the
827      * connection. The method only returns once all requests have been
828      * handled, an error has been returned to the requestor or the
829      * connection has been closed.
830      * The handleNext() is called in a loop until it returns false.
831      */

832     public final void handle()
833     {
834         try
835         {
836             associateThread();
837             while(_listener.isStarted() && handleNext())
838                 recycle();
839         }
840         finally
841         {
842             disassociateThread();
843             destroy();
844         }
845     }
846     
847     /* ------------------------------------------------------------ */
848     protected void associateThread()
849     {
850         __threadConnection.set(this);
851         _handlingThread=Thread.currentThread();
852     }
853     
854     /* ------------------------------------------------------------ */
855     protected void disassociateThread()
856     {
857         _handlingThread=null;
858         __threadConnection.set(null);
859     }
860
861     
862     /* ------------------------------------------------------------ */
863     protected void readRequest()
864         throws IOException JavaDoc
865     {
866         _request.readHeader((LineInput)(_inputStream)
867                             .getInputStream());
868     }
869     
870     /* ------------------------------------------------------------ */
871     /** Handle next request off the connection.
872      * The service(request,response) method is called by handle to
873      * service each request received on the connection.
874      * If the thread is a PoolThread, the thread is set as inactive
875      * when waiting for a request.
876      * <P>
877      * If a HttpTunnel has been set on this connection, it's handle method is
878      * called and when that completes, false is return from this method.
879      * <P>
880      * The Connection is set as a ThreadLocal of the calling thread and is
881      * available via the getHttpConnection() method.
882      * @return true if the connection is still open and may provide
883      * more requests.
884      */

885     public boolean handleNext()
886     {
887         // Handle a HTTP tunnel
888
if (_tunnel!=null)
889         {
890             if(log.isDebugEnabled())log.debug("Tunnel: "+_tunnel);
891             _outputStream.resetObservers();
892             _tunnel.handle(_inputStream.getInputStream(),
893                            _outputStream.getOutputStream());
894             return false;
895         }
896
897         // Normal handling.
898
HttpContext context=null;
899         boolean stats=false;
900         try
901         {
902             // Assume the connection is not persistent,
903
// unless told otherwise.
904
_persistent=false;
905             _close=false;
906             _keepAlive=false;
907             _firstWrite=false;
908             _completing=false;
909             _dotVersion=0;
910
911             // Read requests
912
readRequest();
913             if (_listener==null || !_listener.isStarted())
914             {
915                 // dead connection
916
_response.destroy();
917                 _response=null;
918                 _persistent=false;
919                 return false;
920             }
921             
922             _listener.customizeRequest(this,_request);
923             if (_request.getState()!=HttpMessage.__MSG_RECEIVED)
924                 throw new HttpException(HttpResponse.__400_Bad_Request);
925             
926             // We have a valid request!
927
statsRequestStart();
928             stats=true;
929             
930             // Pick response version, we assume that _request.getVersion() == 1
931
_dotVersion=_request.getDotVersion();
932             
933             if (_dotVersion>1)
934             {
935                 _dotVersion=1;
936             }
937             
938             // Common fields on the response
939
_response.setVersion(HttpMessage.__HTTP_1_1);
940             _response.setField(HttpFields.__Date,_request.getTimeStampStr());
941             if (!Version.isParanoid())
942                 _response.setField(HttpFields.__Server,Version.getDetail());
943             
944             // Handle Connection header field
945
Enumeration JavaDoc connectionValues =
946                 _request.getFieldValues(HttpFields.__Connection,
947                                         HttpFields.__separators);
948             if (connectionValues!=null)
949             {
950                 while (connectionValues.hasMoreElements())
951                 {
952                     String JavaDoc token=connectionValues.nextElement().toString();
953                     // handle close token
954
if (token.equalsIgnoreCase(HttpFields.__Close))
955                     {
956                         _close=true;
957                         _response.setField(HttpFields.__Connection,
958                                            HttpFields.__Close);
959                     }
960                     else if (token.equalsIgnoreCase(HttpFields.__KeepAlive) &&
961                              _dotVersion==0)
962                         _keepAlive=true;
963                     
964                     // Remove headers for HTTP/1.0 requests
965
if (_dotVersion==0)
966                         _request.forceRemoveField(token);
967                 }
968             }
969             
970             // Handle version specifics
971
if (_dotVersion==1)
972                 verifyHTTP_1_1();
973             else if (_dotVersion==0)
974                 verifyHTTP_1_0();
975             else if (_dotVersion!=-1)
976                 throw new HttpException(HttpResponse.__505_HTTP_Version_Not_Supported);
977
978             if(log.isDebugEnabled())log.debug("REQUEST from "+_listener+":\n"+_request);
979
980             // handle HttpListener handlers
981
if (!_request.isHandled() && _listener.getHttpHandler()!=null)
982                 _listener.getHttpHandler().handle("",null, _request, _response);
983             
984             // service the request
985
if (!_request.isHandled())
986                 context=service(_request,_response);
987         }
988         catch(HttpException e) {exception(e);}
989         catch (IOException JavaDoc e)
990         {
991             if (_request.getState()!=HttpMessage.__MSG_RECEIVED)
992             {
993                 if (log.isDebugEnabled())
994                 {
995                     if (log.isTraceEnabled()) log.trace(LogSupport.EXCEPTION,e);
996                     else if(log.isDebugEnabled())log.debug(e.toString());
997                 }
998                 _response.destroy();
999                 _response=null;
1000            }
1001            else
1002                exception(e);
1003        }
1004        catch (Exception JavaDoc e) {exception(e);}
1005        catch (Error JavaDoc e) {exception(e);}
1006        finally
1007        {
1008            int bytes_written=0;
1009            int content_length = _response==null
1010                ?-1:_response.getIntField(HttpFields.__ContentLength);
1011                
1012            // Complete the request
1013
if (_persistent)
1014            {
1015                boolean no_continue_sent=false;
1016                try{
1017                    if (_inputStream.getExpectContinues()!=null)
1018                    {
1019                        _inputStream.setExpectContinues(null);
1020                        no_continue_sent=true;
1021                    }
1022                    else
1023                    {
1024                        int remaining = _inputStream.getContentLength();
1025                        if (remaining!=0)
1026                            // Read remaining input
1027
while(_inputStream.skip(4096)>0 || _inputStream.read()>=0);
1028                    }
1029                }
1030                catch(IOException JavaDoc e)
1031                {
1032                    if (_inputStream.getContentLength()>0)
1033                        _inputStream.setContentLength(0);
1034                    _persistent=false;
1035                    LogSupport.ignore(log,e);
1036                    exception(new HttpException(HttpResponse.__400_Bad_Request,
1037                                                "Missing Content"));
1038                }
1039                    
1040                // Check for no more content
1041
if (!no_continue_sent && _inputStream.getContentLength()>0)
1042                {
1043                    _inputStream.setContentLength(0);
1044                    _persistent=false;
1045                    exception (new HttpException(HttpResponse.__400_Bad_Request,"Missing Content"));
1046                }
1047                
1048                // Commit the response
1049
try{
1050                    _outputStream.close();
1051                    bytes_written=_outputStream.getBytesWritten();
1052                    _outputStream.resetStream();
1053                    _outputStream.addObserver(this);
1054                    _inputStream.resetStream();
1055                }
1056                catch(IOException JavaDoc e) {exception(e);}
1057            }
1058            else if (_response!=null) // There was a request
1059
{
1060                // half hearted attempt to eat any remaining input
1061
try{
1062                    if (_inputStream.getContentLength()>0)
1063                        while(_inputStream.skip(4096)>0 || _inputStream.read()>=0);
1064                    _inputStream.resetStream();
1065                }
1066                catch(IOException JavaDoc e){LogSupport.ignore(log,e);}
1067                
1068                // commit non persistent
1069
try{
1070                    _outputStream.flush();
1071                    _response.commit();
1072                    bytes_written=_outputStream.getBytesWritten();
1073                    _outputStream.close();
1074                    _outputStream.resetStream();
1075                }
1076                catch(IOException JavaDoc e) {exception(e);}
1077            }
1078            
1079            // Check response length
1080
if (_response!=null)
1081            {
1082                if(log.isDebugEnabled())log.debug("RESPONSE:\n"+_response);
1083                if (_persistent && content_length>=0 && bytes_written>0 && content_length!=bytes_written)
1084                {
1085                    log.warn("Invalid length: Content-Length="+content_length+
1086                             " written="+bytes_written+
1087                             " for "+_request.getRequestURL());
1088                    _persistent=false;
1089                    try{_outputStream.close();}
1090                    catch(IOException JavaDoc e) {log.warn(LogSupport.EXCEPTION,e);}
1091                }
1092            }
1093            
1094            // stats & logging
1095
if (stats)
1096                statsRequestEnd();
1097            if (context!=null)
1098                context.log(_request,_response,bytes_written);
1099        }
1100        
1101        return (_tunnel!=null) || _persistent;
1102    }
1103
1104    /* ------------------------------------------------------------ */
1105    protected void statsRequestStart()
1106    {
1107        if (_statsOn)
1108        {
1109            if (_reqTime>0)
1110                statsRequestEnd();
1111            _requests++;
1112            _tmpTime=_request.getTimeStamp();
1113            _reqTime=_tmpTime;
1114            _httpServer.statsGotRequest();
1115        }
1116    }
1117
1118    /* ------------------------------------------------------------ */
1119    protected void statsRequestEnd()
1120    {
1121        if (_statsOn && _reqTime>0)
1122        {
1123            _httpServer.statsEndRequest(System.currentTimeMillis()-_reqTime,
1124                                        (_response!=null));
1125            _reqTime=0;
1126        }
1127    }
1128    
1129    /* ------------------------------------------------------------ */
1130    /** Recycle the connection.
1131     * called by handle when handleNext returns true.
1132     */

1133    protected void recycle()
1134    {
1135        _listener.persistConnection(this);
1136        if (_request!=null)
1137            _request.recycle(this);
1138        if (_response!=null)
1139            _response.recycle(this);
1140    }
1141    
1142    /* ------------------------------------------------------------ */
1143    /** Destroy the connection.
1144     * called by handle when handleNext returns false.
1145     */

1146    protected void destroy()
1147    {
1148        try{close();}
1149        catch (IOException JavaDoc e){LogSupport.ignore(log,e);}
1150        catch (Exception JavaDoc e){log.warn(LogSupport.EXCEPTION,e);}
1151        
1152        // Destroy request and response
1153
if (_request!=null)
1154            _request.destroy();
1155        if (_response!=null)
1156            _response.destroy();
1157        if (_inputStream!=null)
1158            _inputStream.destroy();
1159        if (_outputStream!=null)
1160            _outputStream.destroy();
1161        _inputStream=null;
1162        _outputStream=null;
1163        _request=null;
1164        _response=null;
1165        _handlingThread=null;
1166        
1167        if (_statsOn)
1168        {
1169            _tmpTime=System.currentTimeMillis();
1170            if (_reqTime>0)
1171                _httpServer.statsEndRequest(_tmpTime-_reqTime,false);
1172            _httpServer.statsCloseConnection(_tmpTime-_openTime,_requests);
1173        }
1174    }
1175
1176}
1177
Popular Tags