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