View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2011 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.ServletInputStream;
20  import javax.servlet.ServletOutputStream;
21  import javax.servlet.http.HttpServletResponse;
22  
23  import org.eclipse.jetty.continuation.ContinuationThrowable;
24  import org.eclipse.jetty.http.EncodedHttpURI;
25  import org.eclipse.jetty.http.Generator;
26  import org.eclipse.jetty.http.HttpBuffers;
27  import org.eclipse.jetty.http.HttpContent;
28  import org.eclipse.jetty.http.HttpException;
29  import org.eclipse.jetty.http.HttpFields;
30  import org.eclipse.jetty.http.HttpGenerator;
31  import org.eclipse.jetty.http.HttpHeaderValues;
32  import org.eclipse.jetty.http.HttpHeaders;
33  import org.eclipse.jetty.http.HttpMethods;
34  import org.eclipse.jetty.http.HttpParser;
35  import org.eclipse.jetty.http.HttpStatus;
36  import org.eclipse.jetty.http.HttpURI;
37  import org.eclipse.jetty.http.HttpVersions;
38  import org.eclipse.jetty.http.MimeTypes;
39  import org.eclipse.jetty.http.Parser;
40  import org.eclipse.jetty.io.AbstractConnection;
41  import org.eclipse.jetty.io.Buffer;
42  import org.eclipse.jetty.io.BufferCache.CachedBuffer;
43  import org.eclipse.jetty.io.Buffers;
44  import org.eclipse.jetty.io.Connection;
45  import org.eclipse.jetty.io.EndPoint;
46  import org.eclipse.jetty.io.EofException;
47  import org.eclipse.jetty.io.RuntimeIOException;
48  import org.eclipse.jetty.io.UncheckedPrintWriter;
49  import org.eclipse.jetty.server.nio.NIOConnector;
50  import org.eclipse.jetty.server.ssl.SslConnector;
51  import org.eclipse.jetty.util.QuotedStringTokenizer;
52  import org.eclipse.jetty.util.StringUtil;
53  import org.eclipse.jetty.util.URIUtil;
54  import org.eclipse.jetty.util.log.Log;
55  import org.eclipse.jetty.util.log.Logger;
56  import org.eclipse.jetty.util.resource.Resource;
57  
58  /**
59   * <p>A HttpConnection represents the connection of a HTTP client to the server
60   * and is created by an instance of a {@link Connector}. It's prime function is
61   * to associate {@link Request} and {@link Response} instances with a {@link EndPoint}.
62   * </p>
63   * <p>
64   * A connection is also the prime mechanism used by jetty to recycle objects without
65   * pooling.  The {@link Request},  {@link Response}, {@link HttpParser}, {@link HttpGenerator}
66   * and {@link HttpFields} instances are all recycled for the duraction of
67   * a connection. Where appropriate, allocated buffers are also kept associated
68   * with the connection via the parser and/or generator.
69   * </p>
70   * <p>
71   * The connection state is held by 3 separate state machines: The request state, the
72   * response state and the continuation state.  All three state machines must be driven
73   * to completion for every request, and all three can complete in any order.
74   * </p>
75   * <p>
76   * The HttpConnection support protocol upgrade.  If on completion of a request, the
77   * response code is 101 (switch protocols), then the org.eclipse.jetty.io.Connection
78   * request attribute is checked to see if there is a new Connection instance. If so,
79   * the new connection is returned from {@link #handle()} and is used for future
80   * handling of the underlying connection.   Note that for switching protocols that
81   * don't use 101 responses (eg CONNECT), the response should be sent and then the
82   * status code changed to 101 before returning from the handler.  Implementors
83   * of new Connection types should be careful to extract any buffered data from
84   * (HttpParser)http.getParser()).getHeaderBuffer() and
85   * (HttpParser)http.getParser()).getBodyBuffer() to initialise their new connection.
86   * </p>
87   *
88   */
89  public abstract class AbstractHttpConnection  extends AbstractConnection
90  {
91      private static final Logger LOG = Log.getLogger(AbstractHttpConnection.class);
92  
93      private static final int UNKNOWN = -2;
94      private static final ThreadLocal<AbstractHttpConnection> __currentConnection = new ThreadLocal<AbstractHttpConnection>();
95  
96      private int _requests;
97  
98      protected final Connector _connector;
99      protected final Server _server;
100     protected final HttpURI _uri;
101 
102     protected final Parser _parser;
103     protected final HttpFields _requestFields;
104     protected final Request _request;
105     protected ServletInputStream _in;
106 
107     protected final Generator _generator;
108     protected final HttpFields _responseFields;
109     protected final Response _response;
110     protected Output _out;
111     protected OutputWriter _writer;
112     protected PrintWriter _printWriter;
113 
114     int _include;
115 
116     private Object _associatedObject; // associated object
117 
118     private int _version = UNKNOWN;
119 
120     private boolean _expect = false;
121     private boolean _expect100Continue = false;
122     private boolean _expect102Processing = false;
123     private boolean _head = false;
124     private boolean _host = false;
125     private boolean  _delayedHandling=false;
126 
127     /* ------------------------------------------------------------ */
128     public static AbstractHttpConnection getCurrentConnection()
129     {
130         return __currentConnection.get();
131     }
132 
133     /* ------------------------------------------------------------ */
134     protected static void setCurrentConnection(AbstractHttpConnection connection)
135     {
136         __currentConnection.set(connection);
137     }
138 
139     /* ------------------------------------------------------------ */
140     /** Constructor
141      *
142      */
143     public AbstractHttpConnection(Connector connector, EndPoint endpoint, Server server)
144     {
145         super(endpoint);
146         _uri = StringUtil.__UTF8.equals(URIUtil.__CHARSET)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
147         _connector = connector;
148         HttpBuffers ab = (HttpBuffers)_connector;
149         _parser = newHttpParser(ab.getRequestBuffers(), endpoint, new RequestHandler());
150         _requestFields = new HttpFields();
151         _responseFields = new HttpFields(server.getMaxCookieVersion());
152         _request = new Request(this);
153         _response = new Response(this);
154         _generator = new HttpGenerator(ab.getResponseBuffers(), _endp);
155         _generator.setSendServerVersion(server.getSendServerVersion());
156         _server = server;
157     }
158 
159     /* ------------------------------------------------------------ */
160     protected AbstractHttpConnection(Connector connector, EndPoint endpoint, Server server,
161             Parser parser, Generator generator, Request request)
162     {
163         super(endpoint);
164 
165         _uri = URIUtil.__CHARSET.equals(StringUtil.__UTF8)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
166         _connector = connector;
167         _parser = parser;
168         _requestFields = new HttpFields();
169         _responseFields = new HttpFields(server.getMaxCookieVersion());
170         _request = request;
171         _response = new Response(this);
172         _generator = generator;
173         _generator.setSendServerVersion(server.getSendServerVersion());
174         _server = server;
175     }
176 
177     protected HttpParser newHttpParser(Buffers requestBuffers, EndPoint endpoint, HttpParser.EventHandler requestHandler)
178     {
179         return new HttpParser(requestBuffers, endpoint, requestHandler);
180     }
181 
182     /* ------------------------------------------------------------ */
183     /**
184      * @return the parser used by this connection
185      */
186     public Parser getParser()
187     {
188         return _parser;
189     }
190 
191     /* ------------------------------------------------------------ */
192     /**
193      * @return the number of requests handled by this connection
194      */
195     public int getRequests()
196     {
197         return _requests;
198     }
199 
200     /* ------------------------------------------------------------ */
201     public Server getServer()
202     {
203         return _server;
204     }
205 
206     /* ------------------------------------------------------------ */
207     /**
208      * @return Returns the associatedObject.
209      */
210     public Object getAssociatedObject()
211     {
212         return _associatedObject;
213     }
214 
215     /* ------------------------------------------------------------ */
216     /**
217      * @param associatedObject The associatedObject to set.
218      */
219     public void setAssociatedObject(Object associatedObject)
220     {
221         _associatedObject = associatedObject;
222     }
223 
224     /* ------------------------------------------------------------ */
225     /**
226      * @return Returns the connector.
227      */
228     public Connector getConnector()
229     {
230         return _connector;
231     }
232 
233     /* ------------------------------------------------------------ */
234     /**
235      * @return Returns the requestFields.
236      */
237     public HttpFields getRequestFields()
238     {
239         return _requestFields;
240     }
241 
242     /* ------------------------------------------------------------ */
243     /**
244      * @return Returns the responseFields.
245      */
246     public HttpFields getResponseFields()
247     {
248         return _responseFields;
249     }
250 
251     /* ------------------------------------------------------------ */
252     /**
253      * @return The result of calling {@link #getConnector}.{@link Connector#isConfidential(Request) isCondidential}(request), or false
254      *  if there is no connector.
255      */
256     public boolean isConfidential(Request request)
257     {
258         if (_connector!=null)
259             return _connector.isConfidential(request);
260         return false;
261     }
262 
263     /* ------------------------------------------------------------ */
264     /**
265      * Find out if the request is INTEGRAL security.
266      * @param request
267      * @return <code>true</code> if there is a {@link #getConnector() connector} and it considers <code>request</code>
268      *         to be {@link Connector#isIntegral(Request) integral}
269      */
270     public boolean isIntegral(Request request)
271     {
272         if (_connector!=null)
273             return _connector.isIntegral(request);
274         return false;
275     }
276 
277     /* ------------------------------------------------------------ */
278     /**
279      * @return <code>false</code> (this method is not yet implemented)
280      */
281     public boolean getResolveNames()
282     {
283         return _connector.getResolveNames();
284     }
285 
286     /* ------------------------------------------------------------ */
287     /**
288      * @return Returns the request.
289      */
290     public Request getRequest()
291     {
292         return _request;
293     }
294 
295     /* ------------------------------------------------------------ */
296     /**
297      * @return Returns the response.
298      */
299     public Response getResponse()
300     {
301         return _response;
302     }
303 
304     /* ------------------------------------------------------------ */
305     /**
306      * Get the inputStream from the connection.
307      * <p>
308      * If the associated response has the Expect header set to 100 Continue,
309      * then accessing the input stream indicates that the handler/servlet
310      * is ready for the request body and thus a 100 Continue response is sent.
311      *
312      * @return The input stream for this connection.
313      * The stream will be created if it does not already exist.
314      */
315     public ServletInputStream getInputStream() throws IOException
316     {
317         // If the client is expecting 100 CONTINUE, then send it now.
318         if (_expect100Continue)
319         {
320             // is content missing?
321             if (((HttpParser)_parser).getHeaderBuffer()==null || ((HttpParser)_parser).getHeaderBuffer().length()<2)
322             {
323                 if (_generator.isCommitted())
324                     throw new IllegalStateException("Committed before 100 Continues");
325 
326                 ((HttpGenerator)_generator).send1xx(HttpStatus.CONTINUE_100);
327             }
328             _expect100Continue=false;
329         }
330 
331         if (_in == null)
332             _in = new HttpInput(AbstractHttpConnection.this);
333         return _in;
334     }
335 
336     /* ------------------------------------------------------------ */
337     /**
338      * @return The output stream for this connection. The stream will be created if it does not already exist.
339      */
340     public ServletOutputStream getOutputStream()
341     {
342         if (_out == null)
343             _out = new Output();
344         return _out;
345     }
346 
347     /* ------------------------------------------------------------ */
348     /**
349      * @return A {@link PrintWriter} wrapping the {@link #getOutputStream output stream}. The writer is created if it
350      *    does not already exist.
351      */
352     public PrintWriter getPrintWriter(String encoding)
353     {
354         getOutputStream();
355         if (_writer==null)
356         {
357             _writer=new OutputWriter();
358             if (_server.isUncheckedPrintWriter())
359                 _printWriter=new UncheckedPrintWriter(_writer);
360             else
361                 _printWriter = new PrintWriter(_writer)
362                 {
363                     public void close()
364                     {
365                         synchronized (lock)
366                         {
367                             try
368                             {
369                                 out.close();
370                             }
371                             catch (IOException e)
372                             {
373                                 setError();
374                             }
375                         }
376                     }
377                 };
378 
379         }
380         _writer.setCharacterEncoding(encoding);
381         return _printWriter;
382     }
383 
384     /* ------------------------------------------------------------ */
385     public boolean isResponseCommitted()
386     {
387         return _generator.isCommitted();
388     }
389 
390     /* ------------------------------------------------------------ */
391     public void reset()
392     {
393         _parser.reset();
394         _parser.returnBuffers(); // TODO maybe only on unhandle
395         _requestFields.clear();
396         _request.recycle();
397         _generator.reset();
398         _generator.returnBuffers();// TODO maybe only on unhandle
399         _responseFields.clear();
400         _response.recycle();
401         _uri.clear();
402     }
403 
404     /* ------------------------------------------------------------ */
405     protected void handleRequest() throws IOException
406     {
407         boolean error = false;
408 
409         String threadName=null;
410         try
411         {
412             if (LOG.isDebugEnabled())
413             {
414                 threadName=Thread.currentThread().getName();
415                 Thread.currentThread().setName(threadName+" - "+_uri);
416             }
417 
418 
419             // Loop here to handle async request redispatches.
420             // The loop is controlled by the call to async.unhandle in the
421             // finally block below.  If call is from a non-blocking connector,
422             // then the unhandle will return false only if an async dispatch has
423             // already happened when unhandle is called.   For a blocking connector,
424             // the wait for the asynchronous dispatch or timeout actually happens
425             // within the call to unhandle().
426 
427             final Server server=_server;
428             boolean handling=_request._async.handling() && server!=null && server.isRunning();
429             while (handling)
430             {
431                 _request.setHandled(false);
432 
433                 String info=null;
434                 try
435                 {
436                     _uri.getPort();
437                     info=URIUtil.canonicalPath(_uri.getDecodedPath());
438                     if (info==null && !_request.getMethod().equals(HttpMethods.CONNECT))
439                         throw new HttpException(400);
440                     _request.setPathInfo(info);
441 
442                     if (_out!=null)
443                         _out.reopen();
444 
445                     if (_request._async.isInitial())
446                     {
447                         _request.setDispatcherType(DispatcherType.REQUEST);
448                         _connector.customize(_endp, _request);
449                         server.handle(this);
450                     }
451                     else
452                     {
453                         _request.setDispatcherType(DispatcherType.ASYNC);
454                         server.handleAsync(this);
455                     }
456                 }
457                 catch (ContinuationThrowable e)
458                 {
459                     LOG.ignore(e);
460                 }
461                 catch (EofException e)
462                 {
463                     LOG.debug(e);
464                     error=true;
465                     _request.setHandled(true);
466                 }
467                 catch (RuntimeIOException e)
468                 {
469                     LOG.debug(e);
470                     error=true;
471                     _request.setHandled(true);
472                 }
473                 catch (HttpException e)
474                 {
475                     LOG.debug(e);
476                     error=true;
477                     _request.setHandled(true);
478                     _response.sendError(e.getStatus(), e.getReason());
479                 }
480                 catch (Throwable e)
481                 {
482                     LOG.warn(String.valueOf(_uri),e);
483                     error=true;
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                 _request._async.doComplete();
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();
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();
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 onClose()
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     public String toString()
696     {
697         return String.format("%s,g=%s,p=%s,r=%d",
698                 super.toString(),
699                 _generator,
700                 _parser,
701                 _requests);
702     }
703 
704     /* ------------------------------------------------------------ */
705     /* ------------------------------------------------------------ */
706     /* ------------------------------------------------------------ */
707     private class RequestHandler extends HttpParser.EventHandler
708     {
709         private String _charset;
710 
711         /*
712          *
713          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#startRequest(org.eclipse.io.Buffer,
714          *      org.eclipse.io.Buffer, org.eclipse.io.Buffer)
715          */
716         @Override
717         public void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException
718         {
719             uri=uri.asImmutableBuffer();
720 
721             _host = false;
722             _expect = false;
723             _expect100Continue=false;
724             _expect102Processing=false;
725             _delayedHandling=false;
726             _charset=null;
727 
728             if(_request.getTimeStamp()==0)
729                 _request.setTimeStamp(System.currentTimeMillis());
730             _request.setMethod(method.toString());
731 
732             try
733             {
734                 _head=false;
735                 switch (HttpMethods.CACHE.getOrdinal(method))
736                 {
737                   case HttpMethods.CONNECT_ORDINAL:
738                       _uri.parseConnect(uri.array(), uri.getIndex(), uri.length());
739                       break;
740 
741                   case HttpMethods.HEAD_ORDINAL:
742                       _head=true;
743                       _uri.parse(uri.array(), uri.getIndex(), uri.length());
744                       break;
745 
746                   default:
747                       _uri.parse(uri.array(), uri.getIndex(), uri.length());
748                 }
749 
750                 _request.setUri(_uri);
751 
752                 if (version==null)
753                 {
754                     _request.setProtocol(HttpVersions.HTTP_0_9);
755                     _version=HttpVersions.HTTP_0_9_ORDINAL;
756                 }
757                 else
758                 {
759                     version= HttpVersions.CACHE.get(version);
760                     if (version==null)
761                         throw new HttpException(HttpStatus.BAD_REQUEST_400,null);
762                     _version = HttpVersions.CACHE.getOrdinal(version);
763                     if (_version <= 0) _version = HttpVersions.HTTP_1_0_ORDINAL;
764                     _request.setProtocol(version.toString());
765                 }
766             }
767             catch (Exception e)
768             {
769                 LOG.debug(e);
770                 if (e instanceof HttpException)
771                     throw (HttpException)e;
772                 throw new HttpException(HttpStatus.BAD_REQUEST_400,null,e);
773             }
774         }
775 
776         /*
777          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#parsedHeaderValue(org.eclipse.io.Buffer)
778          */
779         @Override
780         public void parsedHeader(Buffer name, Buffer value)
781         {
782             int ho = HttpHeaders.CACHE.getOrdinal(name);
783             switch (ho)
784             {
785                 case HttpHeaders.HOST_ORDINAL:
786                     // TODO check if host matched a host in the URI.
787                     _host = true;
788                     break;
789 
790                 case HttpHeaders.EXPECT_ORDINAL:
791                     value = HttpHeaderValues.CACHE.lookup(value);
792                     switch(HttpHeaderValues.CACHE.getOrdinal(value))
793                     {
794                         case HttpHeaderValues.CONTINUE_ORDINAL:
795                             _expect100Continue=_generator instanceof HttpGenerator;
796                             break;
797 
798                         case HttpHeaderValues.PROCESSING_ORDINAL:
799                             _expect102Processing=_generator instanceof HttpGenerator;
800                             break;
801 
802                         default:
803                             String[] values = value.toString().split(",");
804                             for  (int i=0;values!=null && i<values.length;i++)
805                             {
806                                 CachedBuffer cb=HttpHeaderValues.CACHE.get(values[i].trim());
807                                 if (cb==null)
808                                     _expect=true;
809                                 else
810                                 {
811                                     switch(cb.getOrdinal())
812                                     {
813                                         case HttpHeaderValues.CONTINUE_ORDINAL:
814                                             _expect100Continue=_generator instanceof HttpGenerator;
815                                             break;
816                                         case HttpHeaderValues.PROCESSING_ORDINAL:
817                                             _expect102Processing=_generator instanceof HttpGenerator;
818                                             break;
819                                         default:
820                                             _expect=true;
821                                     }
822                                 }
823                             }
824                     }
825                     break;
826 
827                 case HttpHeaders.ACCEPT_ENCODING_ORDINAL:
828                 case HttpHeaders.USER_AGENT_ORDINAL:
829                     value = HttpHeaderValues.CACHE.lookup(value);
830                     break;
831 
832                 case HttpHeaders.CONTENT_TYPE_ORDINAL:
833                     value = MimeTypes.CACHE.lookup(value);
834                     _charset=MimeTypes.getCharsetFromContentType(value);
835                     break;
836             }
837 
838             _requestFields.add(name, value);
839         }
840 
841         /*
842          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#headerComplete()
843          */
844         @Override
845         public void headerComplete() throws IOException
846         {
847             _requests++;
848             _generator.setVersion(_version);
849             switch (_version)
850             {
851                 case HttpVersions.HTTP_0_9_ORDINAL:
852                     break;
853                 case HttpVersions.HTTP_1_0_ORDINAL:
854                     _generator.setHead(_head);
855                     if (_parser.isPersistent())
856                     {
857                         _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
858                         _generator.setPersistent(true);
859                     }
860                     else if (HttpMethods.CONNECT.equals(_request.getMethod()))
861                     {
862                         _generator.setPersistent(true);
863                         _parser.setPersistent(true);
864                     }
865 
866                     if (_server.getSendDateHeader())
867                         _generator.setDate(_request.getTimeStampBuffer());
868                     break;
869 
870                 case HttpVersions.HTTP_1_1_ORDINAL:
871                     _generator.setHead(_head);
872 
873                     if (!_parser.isPersistent())
874                     {
875                         _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
876                         _generator.setPersistent(false);
877                     }
878                     if (_server.getSendDateHeader())
879                         _generator.setDate(_request.getTimeStampBuffer());
880 
881                     if (!_host)
882                     {
883                         LOG.debug("!host {}",this);
884                         _generator.setResponse(HttpStatus.BAD_REQUEST_400, null);
885                         _responseFields.put(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.CLOSE_BUFFER);
886                         _generator.completeHeader(_responseFields, true);
887                         _generator.complete();
888                         return;
889                     }
890 
891                     if (_expect)
892                     {
893                         LOG.debug("!expectation {}",this);
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 (_delayedHandling)
923             {
924                 _delayedHandling=false;
925                 handleRequest();
926             }
927         }
928 
929         /* ------------------------------------------------------------ */
930         /*
931          * (non-Javadoc)
932          *
933          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#messageComplete(int)
934          */
935         @Override
936         public void messageComplete(long contentLength) throws IOException
937         {
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#startResponse(org.eclipse.io.Buffer, int,
950          *      org.eclipse.io.Buffer)
951          */
952         @Override
953         public void startResponse(Buffer version, int status, Buffer reason)
954         {
955             if (LOG.isDebugEnabled())
956                 LOG.debug("Bad request!: "+version+" "+status+" "+reason);
957         }
958 
959     }
960 
961 
962     /* ------------------------------------------------------------ */
963     /* ------------------------------------------------------------ */
964     /* ------------------------------------------------------------ */
965     public class Output extends HttpOutput
966     {
967         Output()
968         {
969             super(AbstractHttpConnection.this);
970         }
971 
972         /* ------------------------------------------------------------ */
973         /*
974          * @see java.io.OutputStream#close()
975          */
976         @Override
977         public void close() throws IOException
978         {
979             if (isClosed())
980                 return;
981 
982             if (!isIncluding() && !super._generator.isCommitted())
983                 commitResponse(Generator.LAST);
984             else
985                 flushResponse();
986 
987             super.close();
988         }
989 
990 
991         /* ------------------------------------------------------------ */
992         /*
993          * @see java.io.OutputStream#flush()
994          */
995         @Override
996         public void flush() throws IOException
997         {
998             if (!super._generator.isCommitted())
999                 commitResponse(Generator.MORE);
1000             super.flush();
1001         }
1002 
1003         /* ------------------------------------------------------------ */
1004         /*
1005          * @see javax.servlet.ServletOutputStream#print(java.lang.String)
1006          */
1007         @Override
1008         public void print(String s) throws IOException
1009         {
1010             if (isClosed())
1011                 throw new IOException("Closed");
1012             PrintWriter writer=getPrintWriter(null);
1013             writer.print(s);
1014         }
1015 
1016         /* ------------------------------------------------------------ */
1017         public void sendResponse(Buffer response) throws IOException
1018         {
1019             ((HttpGenerator)super._generator).sendResponse(response);
1020         }
1021 
1022         /* ------------------------------------------------------------ */
1023         public void sendContent(Object content) throws IOException
1024         {
1025             Resource resource=null;
1026 
1027             if (isClosed())
1028                 throw new IOException("Closed");
1029 
1030             if (super._generator.isWritten())
1031                 throw new IllegalStateException("!empty");
1032 
1033             // Convert HTTP content to contentl
1034             if (content instanceof HttpContent)
1035             {
1036                 HttpContent httpContent = (HttpContent) content;
1037                 Buffer contentType = httpContent.getContentType();
1038                 if (contentType != null && !_responseFields.containsKey(HttpHeaders.CONTENT_TYPE_BUFFER))
1039                 {
1040                     String enc = _response.getSetCharacterEncoding();
1041                     if(enc==null)
1042                         _responseFields.add(HttpHeaders.CONTENT_TYPE_BUFFER, contentType);
1043                     else
1044                     {
1045                         if(contentType instanceof CachedBuffer)
1046                         {
1047                             CachedBuffer content_type = ((CachedBuffer)contentType).getAssociate(enc);
1048                             if(content_type!=null)
1049                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, content_type);
1050                             else
1051                             {
1052                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER,
1053                                         contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= "));
1054                             }
1055                         }
1056                         else
1057                         {
1058                             _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER,
1059                                     contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= "));
1060                         }
1061                     }
1062                 }
1063                 if (httpContent.getContentLength() > 0)
1064                     _responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER, httpContent.getContentLength());
1065                 Buffer lm = httpContent.getLastModified();
1066                 long lml=httpContent.getResource().lastModified();
1067                 if (lm != null)
1068                     _responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER, lm);
1069                 else if (httpContent.getResource()!=null)
1070                 {
1071                     if (lml!=-1)
1072                         _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, lml);
1073                 }
1074 
1075                 boolean direct=_connector instanceof NIOConnector && ((NIOConnector)_connector).getUseDirectBuffers() && !(_connector instanceof SslConnector);
1076                 content = direct?httpContent.getDirectBuffer():httpContent.getIndirectBuffer();
1077                 if (content==null)
1078                     content=httpContent.getInputStream();
1079             }
1080             else if (content instanceof Resource)
1081             {
1082                 resource=(Resource)content;
1083                 _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, resource.lastModified());
1084                 content=resource.getInputStream();
1085             }
1086 
1087             // Process content.
1088             if (content instanceof Buffer)
1089             {
1090                 super._generator.addContent((Buffer) content, Generator.LAST);
1091                 commitResponse(Generator.LAST);
1092             }
1093             else if (content instanceof InputStream)
1094             {
1095                 InputStream in = (InputStream)content;
1096 
1097                 try
1098                 {
1099                     int max = super._generator.prepareUncheckedAddContent();
1100                     Buffer buffer = super._generator.getUncheckedBuffer();
1101 
1102                     int len=buffer.readFrom(in,max);
1103 
1104                     while (len>=0)
1105                     {
1106                         super._generator.completeUncheckedAddContent();
1107                         _out.flush();
1108 
1109                         max = super._generator.prepareUncheckedAddContent();
1110                         buffer = super._generator.getUncheckedBuffer();
1111                         len=buffer.readFrom(in,max);
1112                     }
1113                     super._generator.completeUncheckedAddContent();
1114                     _out.flush();
1115                 }
1116                 finally
1117                 {
1118                     if (resource!=null)
1119                         resource.release();
1120                     else
1121                         in.close();
1122 
1123                 }
1124             }
1125             else
1126                 throw new IllegalArgumentException("unknown content type?");
1127 
1128 
1129         }
1130     }
1131 
1132     /* ------------------------------------------------------------ */
1133     /* ------------------------------------------------------------ */
1134     /* ------------------------------------------------------------ */
1135     public class OutputWriter extends HttpWriter
1136     {
1137         OutputWriter()
1138         {
1139             super(AbstractHttpConnection.this._out);
1140         }
1141     }
1142 
1143 
1144 }