View Javadoc

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