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