View Javadoc

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