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.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         Throwable async_exception=null;
403         try
404         {
405             if (LOG.isDebugEnabled())
406             {
407                 threadName=Thread.currentThread().getName();
408                 Thread.currentThread().setName(threadName+" - "+_uri);
409             }
410 
411 
412             // Loop here to handle async request redispatches.
413             // The loop is controlled by the call to async.unhandle in the
414             // finally block below.  If call is from a non-blocking connector,
415             // then the unhandle will return false only if an async dispatch has
416             // already happened when unhandle is called.   For a blocking connector,
417             // the wait for the asynchronous dispatch or timeout actually happens
418             // within the call to unhandle().
419 
420             final Server server=_server;
421             boolean handling=_request._async.handling() && server!=null && server.isRunning();
422             while (handling)
423             {
424                 _request.setHandled(false);
425 
426                 String info=null;
427                 try
428                 {
429                     _uri.getPort();
430                     info=URIUtil.canonicalPath(_uri.getDecodedPath());
431                     if (info==null && !_request.getMethod().equals(HttpMethods.CONNECT))
432                         throw new HttpException(400);
433                     _request.setPathInfo(info);
434 
435                     if (_out!=null)
436                         _out.reopen();
437 
438                     if (_request._async.isInitial())
439                     {
440                         _request.setDispatcherType(DispatcherType.REQUEST);
441                         _connector.customize(_endp, _request);
442                         server.handle(this);
443                     }
444                     else
445                     {
446                         _request.setDispatcherType(DispatcherType.ASYNC);
447                         server.handleAsync(this);
448                     }
449                 }
450                 catch (ContinuationThrowable e)
451                 {
452                     LOG.ignore(e);
453                 }
454                 catch (EofException e)
455                 {
456                     async_exception=e;
457                     LOG.debug(e);
458                     error=true;
459                     _request.setHandled(true);
460                 }
461                 catch (UncheckedIOException e)
462                 {
463                     async_exception=e;
464                     LOG.debug(e);
465                     error=true;
466                     _request.setHandled(true);
467                 }
468                 catch (HttpException e)
469                 {
470                     LOG.debug(e);
471                     error=true;
472                     _request.setHandled(true);
473                     _response.sendError(e.getStatus(), e.getReason());
474                 }
475                 catch (Throwable e)
476                 {
477                     if (e instanceof ThreadDeath)
478                         throw (ThreadDeath)e;
479 
480                     async_exception=e;
481                     LOG.warn(String.valueOf(_uri),e);
482                     error=true;
483                     _request.setHandled(true);
484                     _generator.sendError(info==null?400:500, null, null, true);
485                 }
486                 finally
487                 {
488                     handling = !_request._async.unhandle() && server.isRunning() && _server!=null;
489                 }
490             }
491         }
492         finally
493         {
494             if (threadName!=null)
495                 Thread.currentThread().setName(threadName);
496 
497             if (_request._async.isUncompleted())
498             {
499                 
500                 _request._async.doComplete(async_exception);
501 
502                 if (_expect100Continue)
503                 {
504                     LOG.debug("100 continues not sent");
505                     // We didn't send 100 continues, but the latest interpretation
506                     // of the spec (see httpbis) is that the client will either
507                     // send the body anyway, or close.  So we no longer need to
508                     // do anything special here other than make the connection not persistent
509                     _expect100Continue = false;
510                     if (!_response.isCommitted())
511                         _generator.setPersistent(false);
512                 }
513 
514                 if(_endp.isOpen())
515                 {
516                     if (error)
517                     {
518                         _endp.shutdownOutput();
519                         _generator.setPersistent(false);
520                         if (!_generator.isComplete())
521                             _response.complete();
522                     }
523                     else
524                     {
525                         if (!_response.isCommitted() && !_request.isHandled())
526                             _response.sendError(HttpServletResponse.SC_NOT_FOUND);
527                         _response.complete();
528                         if (_generator.isPersistent())
529                             _connector.persist(_endp);
530                     }
531                 }
532                 else
533                 {
534                     _response.complete();
535                 }
536 
537                 _request.setHandled(true);
538             }
539         }
540     }
541 
542     /* ------------------------------------------------------------ */
543     public abstract Connection handle() throws IOException;
544 
545     /* ------------------------------------------------------------ */
546     public void commitResponse(boolean last) throws IOException
547     {
548         if (!_generator.isCommitted())
549         {
550             _generator.setResponse(_response.getStatus(), _response.getReason());
551             try
552             {
553                 // If the client was expecting 100 continues, but we sent something
554                 // else, then we need to close the connection
555                 if (_expect100Continue && _response.getStatus()!=100)
556                     _generator.setPersistent(false);
557                 _generator.completeHeader(_responseFields, last);
558             }
559             catch(IOException io)
560             {
561                 throw io;
562             }
563             catch(RuntimeException e)
564             {
565                 LOG.warn("header full: "+e);
566 
567                 _response.reset();
568                 _generator.reset(true);
569                 _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null);
570                 _generator.completeHeader(_responseFields,Generator.LAST);
571                 _generator.complete();
572                 throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500);
573             }
574 
575         }
576         if (last)
577             _generator.complete();
578     }
579 
580     /* ------------------------------------------------------------ */
581     public void completeResponse() throws IOException
582     {
583         if (!_generator.isCommitted())
584         {
585             _generator.setResponse(_response.getStatus(), _response.getReason());
586             try
587             {
588                 _generator.completeHeader(_responseFields, Generator.LAST);
589             }
590             catch(IOException io)
591             {
592                 throw io;
593             }
594             catch(RuntimeException e)
595             {
596                 LOG.warn("header full: "+e);
597                 LOG.debug(e);
598 
599                 _response.reset();
600                 _generator.reset(true);
601                 _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null);
602                 _generator.completeHeader(_responseFields,Generator.LAST);
603                 _generator.complete();
604                 throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500);
605             }
606         }
607 
608         _generator.complete();
609     }
610 
611     /* ------------------------------------------------------------ */
612     public void flushResponse() throws IOException
613     {
614         try
615         {
616             commitResponse(Generator.MORE);
617             _generator.flushBuffer();
618         }
619         catch(IOException e)
620         {
621             throw (e instanceof EofException) ? e:new EofException(e);
622         }
623     }
624 
625     /* ------------------------------------------------------------ */
626     public Generator getGenerator()
627     {
628         return _generator;
629     }
630 
631     /* ------------------------------------------------------------ */
632     public boolean isIncluding()
633     {
634         return _include>0;
635     }
636 
637     /* ------------------------------------------------------------ */
638     public void include()
639     {
640         _include++;
641     }
642 
643     /* ------------------------------------------------------------ */
644     public void included()
645     {
646         _include--;
647         if (_out!=null)
648             _out.reopen();
649     }
650 
651     /* ------------------------------------------------------------ */
652     public boolean isIdle()
653     {
654         return _generator.isIdle() && (_parser.isIdle() || _delayedHandling);
655     }
656 
657     /* ------------------------------------------------------------ */
658     /**
659      * @see org.eclipse.jetty.io.Connection#isSuspended()
660      */
661     public boolean isSuspended()
662     {
663         return _request.getAsyncContinuation().isSuspended();
664     }
665 
666     /* ------------------------------------------------------------ */
667     public void closed()
668     {
669         LOG.debug("closed {}",this);
670     }
671     
672     /* ------------------------------------------------------------ */
673     public boolean isExpecting100Continues()
674     {
675         return _expect100Continue;
676     }
677 
678     /* ------------------------------------------------------------ */
679     public boolean isExpecting102Processing()
680     {
681         return _expect102Processing;
682     }
683 
684     /* ------------------------------------------------------------ */
685     public int getMaxIdleTime()
686     {
687         if (_connector.isLowResources() && _endp.getMaxIdleTime()==_connector.getMaxIdleTime())
688             return _connector.getLowResourceMaxIdleTime();
689         if (_endp.getMaxIdleTime()>0)
690             return _endp.getMaxIdleTime();
691         return _connector.getMaxIdleTime();
692     }
693 
694     /* ------------------------------------------------------------ */
695     /* ------------------------------------------------------------ */
696     /* ------------------------------------------------------------ */
697     private class RequestHandler extends HttpParser.EventHandler
698     {
699         private String _charset;
700 
701         /*
702          *
703          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#startRequest(org.eclipse.io.Buffer,
704          *      org.eclipse.io.Buffer, org.eclipse.io.Buffer)
705          */
706         @Override
707         public void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException
708         {
709             uri=uri.asImmutableBuffer();
710             
711             _host = false;
712             _expect = false;
713             _expect100Continue=false;
714             _expect102Processing=false;
715             _delayedHandling=false;
716             _charset=null;
717 
718             if(_request.getTimeStamp()==0)
719                 _request.setTimeStamp(System.currentTimeMillis());
720             _request.setMethod(method.toString());
721 
722             try
723             {
724                 _head=false;
725                 switch (HttpMethods.CACHE.getOrdinal(method))
726                 {
727                   case HttpMethods.CONNECT_ORDINAL:
728                       _uri.parseConnect(uri.array(), uri.getIndex(), uri.length());
729                       break;
730 
731                   case HttpMethods.HEAD_ORDINAL:
732                       _head=true;
733                       // fall through
734 
735                   default:
736                       _uri.parse(uri.array(), uri.getIndex(), uri.length());
737                 }
738 
739                 _request.setUri(_uri);
740 
741                 if (version==null)
742                 {
743                     _request.setProtocol(HttpVersions.HTTP_0_9);
744                     _version=HttpVersions.HTTP_0_9_ORDINAL;
745                 }
746                 else
747                 {
748                     version= HttpVersions.CACHE.get(version);
749                     if (version==null)
750                         throw new HttpException(HttpStatus.BAD_REQUEST_400,null);
751                     _version = HttpVersions.CACHE.getOrdinal(version);
752                     if (_version <= 0) _version = HttpVersions.HTTP_1_0_ORDINAL;
753                     _request.setProtocol(version.toString());
754                 }
755             }
756             catch (Exception e)
757             {
758                 LOG.debug(e);
759                 if (e instanceof HttpException)
760                     throw (HttpException)e;
761                 throw new HttpException(HttpStatus.BAD_REQUEST_400,null,e);
762             }
763         }
764 
765         /*
766          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#parsedHeaderValue(org.eclipse.io.Buffer)
767          */
768         @Override
769         public void parsedHeader(Buffer name, Buffer value)
770         {
771             int ho = HttpHeaders.CACHE.getOrdinal(name);
772             switch (ho)
773             {
774                 case HttpHeaders.HOST_ORDINAL:
775                     // TODO check if host matched a host in the URI.
776                     _host = true;
777                     break;
778 
779                 case HttpHeaders.EXPECT_ORDINAL:
780                     value = HttpHeaderValues.CACHE.lookup(value);
781                     switch(HttpHeaderValues.CACHE.getOrdinal(value))
782                     {
783                         case HttpHeaderValues.CONTINUE_ORDINAL:
784                             _expect100Continue=_generator instanceof HttpGenerator;
785                             break;
786 
787                         case HttpHeaderValues.PROCESSING_ORDINAL:
788                             _expect102Processing=_generator instanceof HttpGenerator;
789                             break;
790 
791                         default:
792                             String[] values = value.toString().split(",");
793                             for  (int i=0;values!=null && i<values.length;i++)
794                             {
795                                 CachedBuffer cb=HttpHeaderValues.CACHE.get(values[i].trim());
796                                 if (cb==null)
797                                     _expect=true;
798                                 else
799                                 {
800                                     switch(cb.getOrdinal())
801                                     {
802                                         case HttpHeaderValues.CONTINUE_ORDINAL:
803                                             _expect100Continue=_generator instanceof HttpGenerator;
804                                             break;
805                                         case HttpHeaderValues.PROCESSING_ORDINAL:
806                                             _expect102Processing=_generator instanceof HttpGenerator;
807                                             break;
808                                         default:
809                                             _expect=true;
810                                     }
811                                 }
812                             }
813                     }
814                     break;
815 
816                 case HttpHeaders.ACCEPT_ENCODING_ORDINAL:
817                 case HttpHeaders.USER_AGENT_ORDINAL:
818                     value = HttpHeaderValues.CACHE.lookup(value);
819                     break;
820 
821                 case HttpHeaders.CONTENT_TYPE_ORDINAL:
822                     value = MimeTypes.CACHE.lookup(value);
823                     _charset=MimeTypes.getCharsetFromContentType(value);
824                     break;
825 
826                 case HttpHeaders.CONNECTION_ORDINAL:
827                     //looks rather clumsy, but the idea is to optimize for a single valued header
828                     switch(HttpHeaderValues.CACHE.getOrdinal(value))
829                     {
830                         case -1:
831                         {
832                             String[] values = value.toString().split(",");
833                             for  (int i=0;values!=null && i<values.length;i++)
834                             {
835                                 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim());
836 
837                                 if (cb!=null)
838                                 {
839                                     switch(cb.getOrdinal())
840                                     {
841                                         case HttpHeaderValues.CLOSE_ORDINAL:
842                                             _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
843                                             _generator.setPersistent(false);
844                                             break;
845 
846                                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
847                                             if (_version==HttpVersions.HTTP_1_0_ORDINAL)
848                                                 _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
849                                             break;
850                                     }
851                                 }
852                             }
853                             break;
854                         }
855                         case HttpHeaderValues.CLOSE_ORDINAL:
856                             _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
857                             _generator.setPersistent(false);
858                             break;
859 
860                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
861                             if (_version==HttpVersions.HTTP_1_0_ORDINAL)
862                                 _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
863                             break;
864                     }
865             }
866 
867             _requestFields.add(name, value);
868         }
869 
870         /*
871          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#headerComplete()
872          */
873         @Override
874         public void headerComplete() throws IOException
875         {
876             if (_endp instanceof AsyncEndPoint)
877                 ((AsyncEndPoint)_endp).scheduleIdle();
878             _requests++;
879             _generator.setVersion(_version);
880             switch (_version)
881             {
882                 case HttpVersions.HTTP_0_9_ORDINAL:
883                     break;
884                 case HttpVersions.HTTP_1_0_ORDINAL:
885                     _generator.setHead(_head);
886 
887                     if (_server.getSendDateHeader())
888                         _generator.setDate(_request.getTimeStampBuffer());
889 
890                     break;
891                 case HttpVersions.HTTP_1_1_ORDINAL:
892                     _generator.setHead(_head);
893 
894                     if (_server.getSendDateHeader())
895                         _generator.setDate(_request.getTimeStampBuffer());
896 
897                     if (!_host)
898                     {
899                         _generator.setResponse(HttpStatus.BAD_REQUEST_400, null);
900                         _responseFields.put(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.CLOSE_BUFFER);
901                         _generator.completeHeader(_responseFields, true);
902                         _generator.complete();
903                         return;
904                     }
905 
906                     if (_expect)
907                     {
908                         _generator.setResponse(HttpStatus.EXPECTATION_FAILED_417, null);
909                         _responseFields.put(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.CLOSE_BUFFER);
910                         _generator.completeHeader(_responseFields, true);
911                         _generator.complete();
912                         return;
913                     }
914 
915                     break;
916                 default:
917             }
918 
919             if(_charset!=null)
920                 _request.setCharacterEncodingUnchecked(_charset);
921 
922             // Either handle now or wait for first content
923             if ((((HttpParser)_parser).getContentLength()<=0 && !((HttpParser)_parser).isChunking())||_expect100Continue)
924                 handleRequest();
925             else
926                 _delayedHandling=true;
927         }
928 
929         /* ------------------------------------------------------------ */
930         /*
931          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#content(int, org.eclipse.io.Buffer)
932          */
933         @Override
934         public void content(Buffer ref) throws IOException
935         {
936             if (_endp instanceof AsyncEndPoint)
937                 ((AsyncEndPoint)_endp).scheduleIdle();
938             if (_delayedHandling)
939             {
940                 _delayedHandling=false;
941                 handleRequest();
942             }
943         }
944 
945         /* ------------------------------------------------------------ */
946         /*
947          * (non-Javadoc)
948          *
949          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#messageComplete(int)
950          */
951         @Override
952         public void messageComplete(long contentLength) throws IOException
953         {
954             if (_delayedHandling)
955             {
956                 _delayedHandling=false;
957                 handleRequest();
958             }
959         }
960 
961         /* ------------------------------------------------------------ */
962         /*
963          * (non-Javadoc)
964          *
965          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#startResponse(org.eclipse.io.Buffer, int,
966          *      org.eclipse.io.Buffer)
967          */
968         @Override
969         public void startResponse(Buffer version, int status, Buffer reason)
970         {
971             if (LOG.isDebugEnabled())
972                 LOG.debug("Bad request!: "+version+" "+status+" "+reason);
973         }
974     }
975 
976 
977     /* ------------------------------------------------------------ */
978     /* ------------------------------------------------------------ */
979     /* ------------------------------------------------------------ */
980     public class Output extends HttpOutput
981     {
982         Output()
983         {
984             super(HttpConnection.this);
985         }
986 
987         /* ------------------------------------------------------------ */
988         /*
989          * @see java.io.OutputStream#close()
990          */
991         @Override
992         public void close() throws IOException
993         {
994             if (isClosed())
995                 return;
996 
997             if (!isIncluding() && !super._generator.isCommitted())
998                 commitResponse(Generator.LAST);
999             else
1000                 flushResponse();
1001 
1002             super.close();
1003         }
1004 
1005 
1006         /* ------------------------------------------------------------ */
1007         /*
1008          * @see java.io.OutputStream#flush()
1009          */
1010         @Override
1011         public void flush() throws IOException
1012         {
1013             if (!super._generator.isCommitted())
1014                 commitResponse(Generator.MORE);
1015             super.flush();
1016         }
1017 
1018         /* ------------------------------------------------------------ */
1019         /*
1020          * @see javax.servlet.ServletOutputStream#print(java.lang.String)
1021          */
1022         @Override
1023         public void print(String s) throws IOException
1024         {
1025             if (isClosed())
1026                 throw new IOException("Closed");
1027             PrintWriter writer=getPrintWriter(null);
1028             writer.print(s);
1029         }
1030 
1031         /* ------------------------------------------------------------ */
1032         public void sendResponse(Buffer response) throws IOException
1033         {
1034             ((HttpGenerator)super._generator).sendResponse(response);
1035         }
1036 
1037         /* ------------------------------------------------------------ */
1038         public void sendContent(Object content) throws IOException
1039         {
1040             Resource resource=null;
1041 
1042             if (isClosed())
1043                 throw new IOException("Closed");
1044 
1045             if (super._generator.isWritten())
1046                 throw new IllegalStateException("!empty");
1047 
1048             // Convert HTTP content to contentl
1049             if (content instanceof HttpContent)
1050             {
1051                 HttpContent httpContent = (HttpContent) content;
1052                 Buffer contentType = httpContent.getContentType();
1053                 if (contentType != null && !_responseFields.containsKey(HttpHeaders.CONTENT_TYPE_BUFFER))
1054                 {
1055                     String enc = _response.getSetCharacterEncoding();
1056                     if(enc==null)
1057                         _responseFields.add(HttpHeaders.CONTENT_TYPE_BUFFER, contentType);
1058                     else
1059                     {
1060                         if(contentType instanceof CachedBuffer)
1061                         {
1062                             CachedBuffer content_type = ((CachedBuffer)contentType).getAssociate(enc);
1063                             if(content_type!=null)
1064                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, content_type);
1065                             else
1066                             {
1067                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER,
1068                                         contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= "));
1069                             }
1070                         }
1071                         else
1072                         {
1073                             _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER,
1074                                     contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= "));
1075                         }
1076                     }
1077                 }
1078                 if (httpContent.getContentLength() > 0)
1079                     _responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER, httpContent.getContentLength());
1080                 Buffer lm = httpContent.getLastModified();
1081                 long lml=httpContent.getResource().lastModified();
1082                 if (lm != null)
1083                     _responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER, lm);
1084                 else if (httpContent.getResource()!=null)
1085                 {
1086                     if (lml!=-1)
1087                         _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, lml);
1088                 }
1089 
1090                 boolean direct=_connector instanceof NIOConnector && ((NIOConnector)_connector).getUseDirectBuffers() && !(_connector instanceof SslConnector);
1091                 content = direct?httpContent.getDirectBuffer():httpContent.getIndirectBuffer();
1092                 if (content==null)
1093                     content=httpContent.getInputStream();
1094             }
1095             else if (content instanceof Resource)
1096             {
1097                 resource=(Resource)content;
1098                 _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, resource.lastModified());
1099                 content=resource.getInputStream();
1100             }
1101 
1102             // Process content.
1103             if (content instanceof Buffer)
1104             {
1105                 super._generator.addContent((Buffer) content, Generator.LAST);
1106                 commitResponse(Generator.LAST);
1107             }
1108             else if (content instanceof InputStream)
1109             {
1110                 InputStream in = (InputStream)content;
1111 
1112                 try
1113                 {
1114                     int max = super._generator.prepareUncheckedAddContent();
1115                     Buffer buffer = super._generator.getUncheckedBuffer();
1116 
1117                     int len=buffer.readFrom(in,max);
1118 
1119                     while (len>=0)
1120                     {
1121                         super._generator.completeUncheckedAddContent();
1122                         _out.flush();
1123 
1124                         max = super._generator.prepareUncheckedAddContent();
1125                         buffer = super._generator.getUncheckedBuffer();
1126                         len=buffer.readFrom(in,max);
1127                     }
1128                     super._generator.completeUncheckedAddContent();
1129                     _out.flush();
1130                 }
1131                 finally
1132                 {
1133                     if (resource!=null)
1134                         resource.release();
1135                     else
1136                         in.close();
1137 
1138                 }
1139             }
1140             else
1141                 throw new IllegalArgumentException("unknown content type?");
1142 
1143 
1144         }
1145     }
1146 
1147     /* ------------------------------------------------------------ */
1148     /* ------------------------------------------------------------ */
1149     /* ------------------------------------------------------------ */
1150     public class OutputWriter extends HttpWriter
1151     {
1152         OutputWriter()
1153         {
1154             super(HttpConnection.this._out);
1155         }
1156     }
1157 
1158     
1159 }