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