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