View Javadoc

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