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