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