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