View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses.
12  // ========================================================================
13  
14  package org.eclipse.jetty.server;
15  
16  import java.io.IOException;
17  import java.io.InputStream;
18  import java.io.PrintWriter;
19  import javax.servlet.ServletInputStream;
20  import javax.servlet.ServletOutputStream;
21  import javax.servlet.http.HttpServletResponse;
22  
23  import org.eclipse.jetty.continuation.ContinuationThrowable;
24  import org.eclipse.jetty.http.AbstractGenerator;
25  import org.eclipse.jetty.http.EncodedHttpURI;
26  import org.eclipse.jetty.http.Generator;
27  import org.eclipse.jetty.http.HttpBuffers;
28  import org.eclipse.jetty.http.HttpContent;
29  import org.eclipse.jetty.http.HttpException;
30  import org.eclipse.jetty.http.HttpFields;
31  import org.eclipse.jetty.http.HttpGenerator;
32  import org.eclipse.jetty.http.HttpHeaderValues;
33  import org.eclipse.jetty.http.HttpHeaders;
34  import org.eclipse.jetty.http.HttpMethods;
35  import org.eclipse.jetty.http.HttpParser;
36  import org.eclipse.jetty.http.HttpStatus;
37  import org.eclipse.jetty.http.HttpURI;
38  import org.eclipse.jetty.http.HttpVersions;
39  import org.eclipse.jetty.http.MimeTypes;
40  import org.eclipse.jetty.http.Parser;
41  import org.eclipse.jetty.io.AsyncEndPoint;
42  import org.eclipse.jetty.io.Buffer;
43  import org.eclipse.jetty.io.BufferCache.CachedBuffer;
44  import org.eclipse.jetty.io.Connection;
45  import org.eclipse.jetty.io.EndPoint;
46  import org.eclipse.jetty.io.EofException;
47  import org.eclipse.jetty.io.RuntimeIOException;
48  import org.eclipse.jetty.io.UncheckedPrintWriter;
49  import org.eclipse.jetty.util.QuotedStringTokenizer;
50  import org.eclipse.jetty.util.StringUtil;
51  import org.eclipse.jetty.util.URIUtil;
52  import org.eclipse.jetty.util.log.Log;
53  import org.eclipse.jetty.util.resource.Resource;
54  import org.eclipse.jetty.util.thread.Timeout;
55  
56  /**
57   * <p>A HttpConnection represents the connection of a HTTP client to the server
58   * and is created by an instance of a {@link Connector}. It's prime function is
59   * to associate {@link Request} and {@link Response} instances with a {@link EndPoint}.
60   * </p>
61   * <p>
62   * A connection is also the prime mechanism used by jetty to recycle objects without
63   * pooling.  The {@link Request},  {@link Response}, {@link HttpParser}, {@link HttpGenerator}
64   * and {@link HttpFields} instances are all recycled for the duraction of
65   * a connection. Where appropriate, allocated buffers are also kept associated
66   * with the connection via the parser and/or generator.
67   * </p>
68   * <p>
69   * The connection state is held by 3 separate state machines: The request state, the
70   * response state and the continuation state.  All three state machines must be driven
71   * to completion for every request, and all three can complete in any order.
72   * </p>
73   * <p>
74   * The HttpConnection support protocol upgrade.  If on completion of a request, the
75   * response code is 101 (switch protocols), then the org.eclipse.jetty.io.Connection
76   * request attribute is checked to see if there is a new Connection instance. If so,
77   * the new connection is returned from {@link #handle()} and is used for future
78   * handling of the underlying connection.   Note that for switching protocols that
79   * don't use 101 responses (eg CONNECT), the response should be sent and then the
80   * status code changed to 101 before returning from the handler.  Implementors
81   * of new Connection types should be careful to extract any buffered data from
82   * (HttpParser)http.getParser()).getHeaderBuffer() and
83   * (HttpParser)http.getParser()).getBodyBuffer() to initialise their new connection.
84   * </p>
85   *
86   */
87  public class HttpConnection implements Connection
88  {
89      private static final int UNKNOWN = -2;
90      private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<HttpConnection>();
91  
92      private final long _timeStamp=System.currentTimeMillis();
93      private int _requests;
94      private volatile boolean _handling;
95  
96      protected final Connector _connector;
97      protected final EndPoint _endp;
98      protected final Server _server;
99      protected final HttpURI _uri;
100 
101     protected final Parser _parser;
102     protected final HttpFields _requestFields;
103     protected final Request _request;
104     protected ServletInputStream _in;
105 
106     protected final Generator _generator;
107     protected final HttpFields _responseFields;
108     protected final Response _response;
109     protected Output _out;
110     protected OutputWriter _writer;
111     protected PrintWriter _printWriter;
112 
113     int _include;
114 
115     private Object _associatedObject; // associated object
116 
117     private int _version = UNKNOWN;
118 
119     private boolean _expect = false;
120     private boolean _expect100Continue = false;
121     private boolean _expect102Processing = false;
122     private boolean _head = false;
123     private boolean _host = false;
124     private boolean  _delayedHandling=false;
125 
126     /* ------------------------------------------------------------ */
127     public static HttpConnection getCurrentConnection()
128     {
129         return __currentConnection.get();
130     }
131 
132     /* ------------------------------------------------------------ */
133     protected static void setCurrentConnection(HttpConnection connection)
134     {
135         __currentConnection.set(connection);
136     }
137 
138     /* ------------------------------------------------------------ */
139     /** Constructor
140      *
141      */
142     public HttpConnection(Connector connector, EndPoint endpoint, Server server)
143     {
144         _uri = StringUtil.__UTF8.equals(URIUtil.__CHARSET)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
145         _connector = connector;
146         _endp = endpoint;
147         HttpBuffers ab = (HttpBuffers)_connector;
148         _parser = new HttpParser(ab.getRequestBuffers(), endpoint, new RequestHandler());
149         _requestFields = new HttpFields();
150         _responseFields = new HttpFields(server.getMaxCookieVersion());
151         _request = new Request(this);
152         _response = new Response(this);
153         _generator = new HttpGenerator(ab.getResponseBuffers(), _endp);
154         _generator.setSendServerVersion(server.getSendServerVersion());
155         _server = server;
156     }
157 
158     /* ------------------------------------------------------------ */
159     protected HttpConnection(Connector connector, EndPoint endpoint, Server server,
160             Parser parser, Generator generator, Request request)
161     {
162         _uri = URIUtil.__CHARSET.equals(StringUtil.__UTF8)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
163         _connector = connector;
164         _endp = endpoint;
165         _parser = parser;
166         _requestFields = new HttpFields();
167         _responseFields = new HttpFields(server.getMaxCookieVersion());
168         _request = request;
169         _response = new Response(this);
170         _generator = generator;
171         _generator.setSendServerVersion(server.getSendServerVersion());
172         _server = server;
173     }
174 
175     /* ------------------------------------------------------------ */
176     /**
177      * @return the parser used by this connection
178      */
179     public Parser getParser()
180     {
181         return _parser;
182     }
183 
184     /* ------------------------------------------------------------ */
185     /**
186      * @return the number of requests handled by this connection
187      */
188     public int getRequests()
189     {
190         return _requests;
191     }
192     
193     /* ------------------------------------------------------------ */
194     public Server getServer()
195     {
196         return _server;
197     }
198     
199     /* ------------------------------------------------------------ */
200     /**
201      * @return The time this connection was established.
202      */
203     public long getTimeStamp()
204     {
205         return _timeStamp;
206     }
207 
208     /* ------------------------------------------------------------ */
209     /**
210      * @return Returns the associatedObject.
211      */
212     public Object getAssociatedObject()
213     {
214         return _associatedObject;
215     }
216 
217     /* ------------------------------------------------------------ */
218     /**
219      * @param associatedObject The associatedObject to set.
220      */
221     public void setAssociatedObject(Object associatedObject)
222     {
223         _associatedObject = associatedObject;
224     }
225 
226     /* ------------------------------------------------------------ */
227     /**
228      * @return Returns the connector.
229      */
230     public Connector getConnector()
231     {
232         return _connector;
233     }
234 
235     /* ------------------------------------------------------------ */
236     /**
237      * @return Returns the requestFields.
238      */
239     public HttpFields getRequestFields()
240     {
241         return _requestFields;
242     }
243 
244     /* ------------------------------------------------------------ */
245     /**
246      * @return Returns the responseFields.
247      */
248     public HttpFields getResponseFields()
249     {
250         return _responseFields;
251     }
252 
253     /* ------------------------------------------------------------ */
254     /**
255      * @return The result of calling {@link #getConnector}.{@link Connector#isConfidential(Request) isCondidential}(request), or false
256      *  if there is no connector.
257      */
258     public boolean isConfidential(Request request)
259     {
260         if (_connector!=null)
261             return _connector.isConfidential(request);
262         return false;
263     }
264 
265     /* ------------------------------------------------------------ */
266     /**
267      * Find out if the request is INTEGRAL security.
268      * @param request
269      * @return <code>true</code> if there is a {@link #getConnector() connector} and it considers <code>request</code>
270      *         to be {@link Connector#isIntegral(Request) integral}
271      */
272     public boolean isIntegral(Request request)
273     {
274         if (_connector!=null)
275             return _connector.isIntegral(request);
276         return false;
277     }
278 
279     /* ------------------------------------------------------------ */
280     /**
281      * @return The {@link EndPoint} for this connection.
282      */
283     public EndPoint getEndPoint()
284     {
285         return _endp;
286     }
287 
288     /* ------------------------------------------------------------ */
289     /**
290      * @return <code>false</code> (this method is not yet implemented)
291      */
292     public boolean getResolveNames()
293     {
294         return _connector.getResolveNames();
295     }
296 
297     /* ------------------------------------------------------------ */
298     /**
299      * @return Returns the request.
300      */
301     public Request getRequest()
302     {
303         return _request;
304     }
305 
306     /* ------------------------------------------------------------ */
307     /**
308      * @return Returns the response.
309      */
310     public Response getResponse()
311     {
312         return _response;
313     }
314 
315     /* ------------------------------------------------------------ */
316     /**
317      * Get the inputStream from the connection.
318      * <p>
319      * If the associated response has the Expect header set to 100 Continue,
320      * then accessing the input stream indicates that the handler/servlet
321      * is ready for the request body and thus a 100 Continue response is sent.
322      *
323      * @return The input stream for this connection.
324      * The stream will be created if it does not already exist.
325      */
326     public ServletInputStream getInputStream() throws IOException
327     {
328         // If the client is expecting 100 CONTINUE, then send it now.
329         if (_expect100Continue)
330         {
331             // is content missing?
332             if (((HttpParser)_parser).getHeaderBuffer()==null || ((HttpParser)_parser).getHeaderBuffer().length()<2)
333             {
334                 if (_generator.isCommitted())
335                     throw new IllegalStateException("Committed before 100 Continues");
336 
337                 ((HttpGenerator)_generator).send1xx(HttpStatus.CONTINUE_100);
338             }
339             _expect100Continue=false;
340         }
341 
342         if (_in == null)
343             _in = new HttpInput(((HttpParser)_parser),_connector.getMaxIdleTime());
344         return _in;
345     }
346 
347     /* ------------------------------------------------------------ */
348     /**
349      * @return The output stream for this connection. The stream will be created if it does not already exist.
350      */
351     public ServletOutputStream getOutputStream()
352     {
353         if (_out == null)
354             _out = new Output();
355         return _out;
356     }
357 
358     /* ------------------------------------------------------------ */
359     /**
360      * @return A {@link PrintWriter} wrapping the {@link #getOutputStream output stream}. The writer is created if it
361      *    does not already exist.
362      */
363     public PrintWriter getPrintWriter(String encoding)
364     {
365         getOutputStream();
366         if (_writer==null)
367         {
368             _writer=new OutputWriter();
369             _printWriter=new UncheckedPrintWriter(_writer);
370         }
371         _writer.setCharacterEncoding(encoding);
372         return _printWriter;
373     }
374 
375     /* ------------------------------------------------------------ */
376     public boolean isResponseCommitted()
377     {
378         return _generator.isCommitted();
379     }
380 
381     /* ------------------------------------------------------------ */
382     public Connection handle() throws IOException
383     {
384         Connection connection = this;
385 
386         // Loop while more in buffer
387         boolean more_in_buffer =true; // assume true until proven otherwise
388         boolean progress=true;
389 
390         try
391         {
392             assert getCurrentConnection()==null;
393             assert _handling==false;
394             _handling=true;
395             setCurrentConnection(this);
396 
397             while (more_in_buffer && _endp.isOpen())
398             {
399                 try
400                 {
401                     if (_request._async.isAsync())
402                     {
403                         // TODO - handle the case of input being read for a
404                         // suspended request.
405 
406                         Log.debug("async request",_request);
407                         if (!_request._async.isComplete())
408                             handleRequest();
409                         else if (!_parser.isComplete())
410                         {
411                             long parsed=_parser.parseAvailable();
412                             progress|=parsed>0;
413                         }
414 
415                         if (_generator.isCommitted() && !_generator.isComplete())
416                             progress|=_generator.flushBuffer()>0;
417                         if (_endp.isBufferingOutput())
418                             _endp.flush();
419                     }
420                     else
421                     {
422                         // If we are not ended then parse available
423                         if (!_parser.isComplete())
424                             progress|=_parser.parseAvailable()>0;
425 
426                         // Do we have more generating to do?
427                         // Loop here because some writes may take multiple steps and
428                         // we need to flush them all before potentially blocking in the
429                         // next loop.
430                         while (_generator.isCommitted() && !_generator.isComplete())
431                         {
432                             long written=_generator.flushBuffer();
433                             if (written<=0)
434                                 break;
435                             progress=true;
436                             if (_endp.isBufferingOutput())
437                                 _endp.flush();
438                         }
439 
440                         // Flush buffers
441                         if (_endp.isBufferingOutput())
442                         {
443                             _endp.flush();
444                             if (!_endp.isBufferingOutput())
445                                 progress=true;
446                         }
447 
448                         if (!progress)
449                             return this;
450                     }
451                     progress=false;
452                 }
453                 catch (HttpException e)
454                 {
455                     if (Log.isDebugEnabled())
456                     {
457                         Log.debug("uri="+_uri);
458                         Log.debug("fields="+_requestFields);
459                         Log.debug(e);
460                     }
461                     _generator.sendError(e.getStatus(), e.getReason(), null, true);
462 
463                     _parser.reset(true);
464                     _endp.close();
465                 }
466                 finally
467                 {
468                     more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
469 
470                     // Is this request/response round complete?
471                     if (_parser.isComplete() && _generator.isComplete() && !_endp.isBufferingOutput())
472                     {
473                         // look for a switched connection instance?
474                         Connection switched=(_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101)
475                         ?(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection"):null;
476 
477                         // have we switched?
478                         if (switched!=null)
479                         {
480                             _parser.reset(true);
481                             _generator.reset(true);
482                             connection=switched;
483                         }
484                         else
485                         {
486                             // No switch, so cleanup and reset
487                             if (!_generator.isPersistent())
488                             {
489                                 _parser.reset(true);
490                                 more_in_buffer=false;
491                                 _endp.close();
492                             }
493 
494                             if (more_in_buffer)
495                             {
496                                 reset(false);
497                                 more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
498                             }
499                             else
500                                 reset(true);
501                             progress=true;
502                         }
503                     }
504 
505                     if (_request.isAsyncStarted())
506                     {
507                         Log.debug("return with suspended request");
508                         more_in_buffer=false;
509                     }
510                     else if (_generator.isCommitted() && !_generator.isComplete() && _endp instanceof AsyncEndPoint)
511                         ((AsyncEndPoint)_endp).setWritable(false);
512                 }
513             }
514         }
515         finally
516         {
517             setCurrentConnection(null);
518             _handling=false;
519         }
520         return connection;
521     }
522 
523     /* ------------------------------------------------------------ */
524     public void scheduleTimeout(Timeout.Task task, long timeoutMs)
525     {
526         throw new UnsupportedOperationException();
527     }
528 
529     /* ------------------------------------------------------------ */
530     public void cancelTimeout(Timeout.Task task)
531     {
532         throw new UnsupportedOperationException();
533     }
534 
535     /* ------------------------------------------------------------ */
536     public void reset(boolean returnBuffers)
537     {
538         _parser.reset(returnBuffers); // TODO maybe only release when low on resources
539         _requestFields.clear();
540         _request.recycle();
541 
542         _generator.reset(returnBuffers); // TODO maybe only release when low on resources
543         _responseFields.clear();
544         _response.recycle();
545 
546         _uri.clear();
547     }
548 
549     /* ------------------------------------------------------------ */
550     protected void handleRequest() throws IOException
551     {
552         boolean error = false;
553 
554         String threadName=null;
555         try
556         {
557             if (Log.isDebugEnabled())
558             {
559                 threadName=Thread.currentThread().getName();
560                 Thread.currentThread().setName(threadName+" - "+_uri);
561             }
562 
563 
564             // Loop here to handle async request redispatches.
565             // The loop is controlled by the call to async.unhandle in the
566             // finally block below.  If call is from a non-blocking connector,
567             // then the unhandle will return false only if an async dispatch has
568             // already happened when unhandle is called.   For a blocking connector,
569             // the wait for the asynchronous dispatch or timeout actually happens
570             // within the call to unhandle().
571 
572             final Server server=_server;
573             boolean handling=_request._async.handling() && server!=null && server.isRunning();
574             while (handling)
575             {
576                 _request.setHandled(false);
577 
578                 String info=null;
579                 try
580                 {
581                     _uri.getPort();
582                     info=URIUtil.canonicalPath(_uri.getDecodedPath());
583                     if (info==null && !_request.getMethod().equals(HttpMethods.CONNECT))
584                         throw new HttpException(400);
585                     _request.setPathInfo(info);
586 
587                     if (_out!=null)
588                         _out.reopen();
589 
590                     if (_request._async.isInitial())
591                     {
592                         _request.setDispatcherType(DispatcherType.REQUEST);
593                         _connector.customize(_endp, _request);
594                         server.handle(this);
595                     }
596                     else
597                     {
598                         _request.setDispatcherType(DispatcherType.ASYNC);
599                         server.handleAsync(this);
600                     }
601                 }
602                 catch (ContinuationThrowable e)
603                 {
604                     Log.ignore(e);
605                 }
606                 catch (EofException e)
607                 {
608                     Log.debug(e);
609                     _request.setHandled(true);
610                     error=true;
611                 }
612                 catch (RuntimeIOException e)
613                 {
614                     Log.debug(e);
615                     _request.setHandled(true);
616                     error=true;
617                 }
618                 catch (HttpException e)
619                 {
620                     Log.debug(e);
621                     _request.setHandled(true);
622                     _response.sendError(e.getStatus(), e.getReason());
623                     error=true;
624                 }
625                 catch (Throwable e)
626                 {
627                     if (e instanceof ThreadDeath)
628                         throw (ThreadDeath)e;
629 
630                     error=true;
631                     Log.warn(_uri+": "+e);
632                     Log.debug(e);
633                     _request.setHandled(true);
634                     _generator.sendError(info==null?400:500, null, null, true);
635                 }
636                 finally
637                 {
638                     handling = !_request._async.unhandle() && server.isRunning() && _server!=null;
639                 }
640             }
641         }
642         finally
643         {
644             if (threadName!=null)
645                 Thread.currentThread().setName(threadName);
646 
647             if (_request._async.isUncompleted())
648             {
649                 _request._async.doComplete();
650 
651                 if (_expect100Continue)
652                 {
653                     Log.debug("100 continues not sent");
654                     // We didn't send 100 continues, but the latest interpretation
655                     // of the spec (see httpbis) is that the client will either
656                     // send the body anyway, or close.  So we no longer need to
657                     // do anything special here.
658                     _expect100Continue = false;
659                 }
660 
661                 if(_endp.isOpen())
662                 {
663                     if (_generator.isPersistent())
664                         _connector.persist(_endp);
665 
666                     if (error)
667                         _endp.close();
668                     else
669                     {
670                         if (!_response.isCommitted() && !_request.isHandled())
671                             _response.sendError(HttpServletResponse.SC_NOT_FOUND);
672                         _response.complete();
673                     }
674                 }
675                 else
676                 {
677                     _response.complete();
678                 }
679 
680                 _request.setHandled(true);
681             }
682         }
683     }
684 
685     /* ------------------------------------------------------------ */
686     public void commitResponse(boolean last) throws IOException
687     {
688         if (!_generator.isCommitted())
689         {
690             _generator.setResponse(_response.getStatus(), _response.getReason());
691             try
692             {
693                 // If the client was expecting 100 continues, but we sent something
694                 // else, then we need to close the connection
695                 if (_expect100Continue && _response.getStatus()!=100)
696                     _generator.setPersistent(false);
697                 _generator.completeHeader(_responseFields, last);
698             }
699             catch(IOException io)
700             {
701                 throw io;
702             }
703             catch(RuntimeException e)
704             {
705                 Log.warn("header full: "+e);
706 
707                 _response.reset();
708                 _generator.reset(true);
709                 _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null);
710                 _generator.completeHeader(_responseFields,Generator.LAST);
711                 _generator.complete();
712                 throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500);
713             }
714 
715         }
716         if (last)
717             _generator.complete();
718     }
719 
720     /* ------------------------------------------------------------ */
721     public void completeResponse() throws IOException
722     {
723         if (!_generator.isCommitted())
724         {
725             _generator.setResponse(_response.getStatus(), _response.getReason());
726             try
727             {
728                 _generator.completeHeader(_responseFields, Generator.LAST);
729             }
730             catch(IOException io)
731             {
732                 throw io;
733             }
734             catch(RuntimeException e)
735             {
736                 Log.warn("header full: "+e);
737                 Log.debug(e);
738 
739                 _response.reset();
740                 _generator.reset(true);
741                 _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null);
742                 _generator.completeHeader(_responseFields,Generator.LAST);
743                 _generator.complete();
744                 throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500);
745             }
746         }
747 
748         _generator.complete();
749     }
750 
751     /* ------------------------------------------------------------ */
752     public void flushResponse() throws IOException
753     {
754         try
755         {
756             commitResponse(Generator.MORE);
757             _generator.flushBuffer();
758         }
759         catch(IOException e)
760         {
761             throw (e instanceof EofException) ? e:new EofException(e);
762         }
763     }
764 
765     /* ------------------------------------------------------------ */
766     public Generator getGenerator()
767     {
768         return _generator;
769     }
770 
771     /* ------------------------------------------------------------ */
772     public boolean isIncluding()
773     {
774         return _include>0;
775     }
776 
777     /* ------------------------------------------------------------ */
778     public void include()
779     {
780         _include++;
781     }
782 
783     /* ------------------------------------------------------------ */
784     public void included()
785     {
786         _include--;
787         if (_out!=null)
788             _out.reopen();
789     }
790 
791     /* ------------------------------------------------------------ */
792     public boolean isIdle()
793     {
794         return _generator.isIdle() && (_parser.isIdle() || _delayedHandling);
795     }
796 
797     /* ------------------------------------------------------------ */
798     /**
799      * @see org.eclipse.jetty.io.Connection#isSuspended()
800      */
801     public boolean isSuspended()
802     {
803         return _request.getAsyncContinuation().isSuspended();
804     }
805 
806     /* ------------------------------------------------------------ */
807     public boolean isExpecting100Continues()
808     {
809         return _expect100Continue;
810     }
811 
812     /* ------------------------------------------------------------ */
813     public boolean isExpecting102Processing()
814     {
815         return _expect102Processing;
816     }
817 
818     /* ------------------------------------------------------------ */
819     /* ------------------------------------------------------------ */
820     /* ------------------------------------------------------------ */
821     private class RequestHandler extends HttpParser.EventHandler
822     {
823         private String _charset;
824 
825         /*
826          *
827          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#startRequest(org.eclipse.io.Buffer,
828          *      org.eclipse.io.Buffer, org.eclipse.io.Buffer)
829          */
830         @Override
831         public void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException
832         {
833             _host = false;
834             _expect = false;
835             _expect100Continue=false;
836             _expect102Processing=false;
837             _delayedHandling=false;
838             _charset=null;
839 
840             if(_request.getTimeStamp()==0)
841                 _request.setTimeStamp(System.currentTimeMillis());
842             _request.setMethod(method.toString());
843 
844             try
845             {
846                 switch (HttpMethods.CACHE.getOrdinal(method))
847                 {
848                   case HttpMethods.CONNECT_ORDINAL:
849                       _uri.parseConnect(uri.array(), uri.getIndex(), uri.length());
850                       break;
851 
852                   case HttpMethods.HEAD_ORDINAL:
853                       _head=true;
854                       // fall through
855 
856                   default:
857                       _uri.parse(uri.array(), uri.getIndex(), uri.length());
858                 }
859 
860                 _request.setUri(_uri);
861 
862                 if (version==null)
863                 {
864                     _request.setProtocol(HttpVersions.HTTP_0_9);
865                     _version=HttpVersions.HTTP_0_9_ORDINAL;
866                 }
867                 else
868                 {
869                     version= HttpVersions.CACHE.get(version);
870                     _version = HttpVersions.CACHE.getOrdinal(version);
871                     if (_version <= 0) _version = HttpVersions.HTTP_1_0_ORDINAL;
872                     _request.setProtocol(version.toString());
873                 }
874             }
875             catch (Exception e)
876             {
877                 Log.debug(e);
878                 throw new HttpException(HttpStatus.BAD_REQUEST_400,null,e);
879             }
880         }
881 
882         /*
883          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#parsedHeaderValue(org.eclipse.io.Buffer)
884          */
885         @Override
886         public void parsedHeader(Buffer name, Buffer value)
887         {
888             int ho = HttpHeaders.CACHE.getOrdinal(name);
889             switch (ho)
890             {
891                 case HttpHeaders.HOST_ORDINAL:
892                     // TODO check if host matched a host in the URI.
893                     _host = true;
894                     break;
895 
896                 case HttpHeaders.EXPECT_ORDINAL:
897                     value = HttpHeaderValues.CACHE.lookup(value);
898                     switch(HttpHeaderValues.CACHE.getOrdinal(value))
899                     {
900                         case HttpHeaderValues.CONTINUE_ORDINAL:
901                             _expect100Continue=_generator instanceof HttpGenerator;
902                             break;
903 
904                         case HttpHeaderValues.PROCESSING_ORDINAL:
905                             _expect102Processing=_generator instanceof HttpGenerator;
906                             break;
907 
908                         default:
909                             String[] values = value.toString().split(",");
910                             for  (int i=0;values!=null && i<values.length;i++)
911                             {
912                                 CachedBuffer cb=HttpHeaderValues.CACHE.get(values[i].trim());
913                                 if (cb==null)
914                                     _expect=true;
915                                 else
916                                 {
917                                     switch(cb.getOrdinal())
918                                     {
919                                         case HttpHeaderValues.CONTINUE_ORDINAL:
920                                             _expect100Continue=_generator instanceof HttpGenerator;
921                                             break;
922                                         case HttpHeaderValues.PROCESSING_ORDINAL:
923                                             _expect102Processing=_generator instanceof HttpGenerator;
924                                             break;
925                                         default:
926                                             _expect=true;
927                                     }
928                                 }
929                             }
930                     }
931                     break;
932 
933                 case HttpHeaders.ACCEPT_ENCODING_ORDINAL:
934                 case HttpHeaders.USER_AGENT_ORDINAL:
935                     value = HttpHeaderValues.CACHE.lookup(value);
936                     break;
937 
938                 case HttpHeaders.CONTENT_TYPE_ORDINAL:
939                     value = MimeTypes.CACHE.lookup(value);
940                     _charset=MimeTypes.getCharsetFromContentType(value);
941                     break;
942 
943                 case HttpHeaders.CONNECTION_ORDINAL:
944                     //looks rather clumsy, but the idea is to optimize for a single valued header
945                     switch(HttpHeaderValues.CACHE.getOrdinal(value))
946                     {
947                         case -1:
948                         {
949                             String[] values = value.toString().split(",");
950                             for  (int i=0;values!=null && i<values.length;i++)
951                             {
952                                 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim());
953 
954                                 if (cb!=null)
955                                 {
956                                     switch(cb.getOrdinal())
957                                     {
958                                         case HttpHeaderValues.CLOSE_ORDINAL:
959                                             _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
960                                             _generator.setPersistent(false);
961                                             break;
962 
963                                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
964                                             if (_version==HttpVersions.HTTP_1_0_ORDINAL)
965                                                 _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
966                                             break;
967                                     }
968                                 }
969                             }
970                             break;
971                         }
972                         case HttpHeaderValues.CLOSE_ORDINAL:
973                             _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
974                             _generator.setPersistent(false);
975                             break;
976 
977                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
978                             if (_version==HttpVersions.HTTP_1_0_ORDINAL)
979                                 _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
980                             break;
981                     }
982             }
983 
984             _requestFields.add(name, value);
985         }
986 
987         /*
988          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#headerComplete()
989          */
990         @Override
991         public void headerComplete() throws IOException
992         {
993             if (_endp instanceof AsyncEndPoint)
994                 ((AsyncEndPoint)_endp).scheduleIdle();
995             _requests++;
996             _generator.setVersion(_version);
997             switch (_version)
998             {
999                 case HttpVersions.HTTP_0_9_ORDINAL:
1000                     break;
1001                 case HttpVersions.HTTP_1_0_ORDINAL:
1002                     _generator.setHead(_head);
1003 
1004                     if (_server.getSendDateHeader())
1005                         _generator.setDate(_request.getTimeStampBuffer());
1006 
1007                     break;
1008                 case HttpVersions.HTTP_1_1_ORDINAL:
1009                     _generator.setHead(_head);
1010 
1011                     if (_server.getSendDateHeader())
1012                         _generator.setDate(_request.getTimeStampBuffer());
1013 
1014                     if (!_host)
1015                     {
1016                         _generator.setResponse(HttpStatus.BAD_REQUEST_400, null);
1017                         _responseFields.put(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.CLOSE_BUFFER);
1018                         _generator.completeHeader(_responseFields, true);
1019                         _generator.complete();
1020                         return;
1021                     }
1022 
1023                     if (_expect)
1024                     {
1025                         _generator.setResponse(HttpStatus.EXPECTATION_FAILED_417, null);
1026                         _responseFields.put(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.CLOSE_BUFFER);
1027                         _generator.completeHeader(_responseFields, true);
1028                         _generator.complete();
1029                         return;
1030                     }
1031 
1032                     break;
1033                 default:
1034             }
1035 
1036             if(_charset!=null)
1037                 _request.setCharacterEncodingUnchecked(_charset);
1038 
1039             // Either handle now or wait for first content
1040             if ((((HttpParser)_parser).getContentLength()<=0 && !((HttpParser)_parser).isChunking())||_expect100Continue)
1041                 handleRequest();
1042             else
1043                 _delayedHandling=true;
1044         }
1045 
1046         /* ------------------------------------------------------------ */
1047         /*
1048          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#content(int, org.eclipse.io.Buffer)
1049          */
1050         @Override
1051         public void content(Buffer ref) throws IOException
1052         {
1053             if (_endp instanceof AsyncEndPoint)
1054                 ((AsyncEndPoint)_endp).scheduleIdle();
1055             if (_delayedHandling)
1056             {
1057                 _delayedHandling=false;
1058                 handleRequest();
1059             }
1060         }
1061 
1062         /* ------------------------------------------------------------ */
1063         /*
1064          * (non-Javadoc)
1065          *
1066          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#messageComplete(int)
1067          */
1068         @Override
1069         public void messageComplete(long contentLength) throws IOException
1070         {
1071             if (_delayedHandling)
1072             {
1073                 _delayedHandling=false;
1074                 handleRequest();
1075             }
1076         }
1077 
1078         /* ------------------------------------------------------------ */
1079         /*
1080          * (non-Javadoc)
1081          *
1082          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#startResponse(org.eclipse.io.Buffer, int,
1083          *      org.eclipse.io.Buffer)
1084          */
1085         @Override
1086         public void startResponse(Buffer version, int status, Buffer reason)
1087         {
1088             Log.debug("Bad request!: "+version+" "+status+" "+reason);
1089         }
1090     }
1091 
1092 
1093     /* ------------------------------------------------------------ */
1094     /* ------------------------------------------------------------ */
1095     /* ------------------------------------------------------------ */
1096     public class Output extends HttpOutput
1097     {
1098         Output()
1099         {
1100             super((AbstractGenerator)HttpConnection.this._generator,
1101                   _connector.isLowResources()?_connector.getLowResourceMaxIdleTime():_connector.getMaxIdleTime());
1102         }
1103 
1104         /* ------------------------------------------------------------ */
1105         /*
1106          * @see java.io.OutputStream#close()
1107          */
1108         @Override
1109         public void close() throws IOException
1110         {
1111             if (_closed)
1112                 return;
1113 
1114             if (!isIncluding() && !super._generator.isCommitted())
1115                 commitResponse(Generator.LAST);
1116             else
1117                 flushResponse();
1118 
1119             super.close();
1120         }
1121 
1122 
1123         /* ------------------------------------------------------------ */
1124         /*
1125          * @see java.io.OutputStream#flush()
1126          */
1127         @Override
1128         public void flush() throws IOException
1129         {
1130             if (!super._generator.isCommitted())
1131                 commitResponse(Generator.MORE);
1132             super.flush();
1133         }
1134 
1135         /* ------------------------------------------------------------ */
1136         /*
1137          * @see javax.servlet.ServletOutputStream#print(java.lang.String)
1138          */
1139         @Override
1140         public void print(String s) throws IOException
1141         {
1142             if (_closed)
1143                 throw new IOException("Closed");
1144             PrintWriter writer=getPrintWriter(null);
1145             writer.print(s);
1146         }
1147 
1148         /* ------------------------------------------------------------ */
1149         public void sendResponse(Buffer response) throws IOException
1150         {
1151             ((HttpGenerator)super._generator).sendResponse(response);
1152         }
1153 
1154         /* ------------------------------------------------------------ */
1155         public void sendContent(Object content) throws IOException
1156         {
1157             Resource resource=null;
1158 
1159             if (_closed)
1160                 throw new IOException("Closed");
1161 
1162             if (super._generator.getContentWritten() > 0)
1163                 throw new IllegalStateException("!empty");
1164 
1165             if (content instanceof HttpContent)
1166             {
1167                 HttpContent c = (HttpContent) content;
1168                 Buffer contentType = c.getContentType();
1169                 if (contentType != null && !_responseFields.containsKey(HttpHeaders.CONTENT_TYPE_BUFFER))
1170                 {
1171                     String enc = _response.getSetCharacterEncoding();
1172                     if(enc==null)
1173                         _responseFields.add(HttpHeaders.CONTENT_TYPE_BUFFER, contentType);
1174                     else
1175                     {
1176                         if(contentType instanceof CachedBuffer)
1177                         {
1178                             CachedBuffer content_type = ((CachedBuffer)contentType).getAssociate(enc);
1179                             if(content_type!=null)
1180                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, content_type);
1181                             else
1182                             {
1183                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER,
1184                                         contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= "));
1185                             }
1186                         }
1187                         else
1188                         {
1189                             _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER,
1190                                     contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= "));
1191                         }
1192                     }
1193                 }
1194                 if (c.getContentLength() > 0)
1195                     _responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER, c.getContentLength());
1196                 Buffer lm = c.getLastModified();
1197                 long lml=c.getResource().lastModified();
1198                 if (lm != null)
1199                     _responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER, lm,lml);
1200                 else if (c.getResource()!=null)
1201                 {
1202                     if (lml!=-1)
1203                         _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, lml);
1204                 }
1205 
1206                 content = c.getBuffer();
1207                 if (content==null)
1208                     content=c.getInputStream();
1209             }
1210             else if (content instanceof Resource)
1211             {
1212                 resource=(Resource)content;
1213                 _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, resource.lastModified());
1214                 content=resource.getInputStream();
1215             }
1216 
1217 
1218             if (content instanceof Buffer)
1219             {
1220                 super._generator.addContent((Buffer) content, Generator.LAST);
1221                 commitResponse(Generator.LAST);
1222             }
1223             else if (content instanceof InputStream)
1224             {
1225                 InputStream in = (InputStream)content;
1226 
1227                 try
1228                 {
1229                     int max = super._generator.prepareUncheckedAddContent();
1230                     Buffer buffer = super._generator.getUncheckedBuffer();
1231 
1232                     int len=buffer.readFrom(in,max);
1233 
1234                     while (len>=0)
1235                     {
1236                         super._generator.completeUncheckedAddContent();
1237                         _out.flush();
1238 
1239                         max = super._generator.prepareUncheckedAddContent();
1240                         buffer = super._generator.getUncheckedBuffer();
1241                         len=buffer.readFrom(in,max);
1242                     }
1243                     super._generator.completeUncheckedAddContent();
1244                     _out.flush();
1245                 }
1246                 finally
1247                 {
1248                     if (resource!=null)
1249                         resource.release();
1250                     else
1251                         in.close();
1252 
1253                 }
1254             }
1255             else
1256                 throw new IllegalArgumentException("unknown content type?");
1257 
1258 
1259         }
1260     }
1261 
1262     /* ------------------------------------------------------------ */
1263     /* ------------------------------------------------------------ */
1264     /* ------------------------------------------------------------ */
1265     public class OutputWriter extends HttpWriter
1266     {
1267         OutputWriter()
1268         {
1269             super(HttpConnection.this._out);
1270         }
1271     }
1272 
1273 }