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