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