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.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 duraction 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                     reset(!more_in_buffer);
475                     no_progress = 0;
476                 }
477 
478                 Continuation continuation = _request.getContinuation();
479                 if (continuation != null && continuation.isPending())
480                 {
481                     break;
482                 }
483                 else if (_generator.isCommitted() && !_generator.isComplete() && _endp instanceof SelectChannelEndPoint) // TODO
484                                                                                                                          // remove
485                                                                                                                          // SelectChannel
486                                                                                                                          // dependency
487                     ((SelectChannelEndPoint)_endp).setWritable(false);
488             }
489         }
490     }
491 
492     /* ------------------------------------------------------------ */
493     public void reset(boolean returnBuffers)
494     {
495         _parser.reset(returnBuffers); // TODO maybe only release when low on
496                                       // resources
497         _requestFields.clear();
498         _request.recycle();
499 
500         _generator.reset(returnBuffers); // TODO maybe only release when low on
501                                          // resources
502         _responseFields.clear();
503         _response.recycle();
504 
505         _uri.clear();
506     }
507 
508     /* ------------------------------------------------------------ */
509     protected void handleRequest() throws IOException
510     {
511         if (_server.isRunning())
512         {
513             boolean retrying = false;
514             boolean error = false;
515             String threadName = null;
516             try
517             {
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 contentLength) 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                 Buffer contentType = c.getContentType();
982                 if (contentType != null && !_responseFields.containsKey(HttpHeaders.CONTENT_TYPE_BUFFER))
983                 {
984                     String enc = _response.getSetCharacterEncoding();
985                     if(enc==null)
986                         _responseFields.add(HttpHeaders.CONTENT_TYPE_BUFFER, contentType);
987                     else
988                     {
989                         if(contentType instanceof CachedBuffer)
990                         {
991                             CachedBuffer content_type = ((CachedBuffer)contentType).getAssociate(enc);
992                             if(content_type!=null)
993                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, content_type);
994                             else
995                             {
996                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, 
997                                         contentType+";charset="+QuotedStringTokenizer.quote(enc,";= "));
998                             }
999                         }
1000                         else
1001                         {
1002                             _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, 
1003                                     contentType+";charset="+QuotedStringTokenizer.quote(enc,";= "));
1004                         }
1005                     }
1006                 }
1007                 if (c.getContentLength() > 0)
1008                     _responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER,c.getContentLength());
1009                 Buffer lm = c.getLastModified();
1010                 long lml = c.getResource().lastModified();
1011                 if (lm != null)
1012                     _responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER,lm,lml);
1013                 else if (c.getResource() != null)
1014                 {
1015                     if (lml != -1)
1016                         _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,lml);
1017                 }
1018 
1019                 content = c.getBuffer();
1020                 if (content == null)
1021                     content = c.getInputStream();
1022             }
1023             else if (content instanceof Resource)
1024             {
1025                 resource = (Resource)content;
1026                 _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,resource.lastModified());
1027                 content = resource.getInputStream();
1028             }
1029 
1030             if (content instanceof Buffer)
1031             {
1032                 _generator.addContent((Buffer)content,HttpGenerator.LAST);
1033                 commitResponse(HttpGenerator.LAST);
1034             }
1035             else if (content instanceof InputStream)
1036             {
1037                 InputStream in = (InputStream)content;
1038 
1039                 try
1040                 {
1041                     int max = _generator.prepareUncheckedAddContent();
1042                     Buffer buffer = _generator.getUncheckedBuffer();
1043 
1044                     int len = buffer.readFrom(in,max);
1045 
1046                     while (len >= 0)
1047                     {
1048                         _generator.completeUncheckedAddContent();
1049                         _out.flush();
1050 
1051                         max = _generator.prepareUncheckedAddContent();
1052                         buffer = _generator.getUncheckedBuffer();
1053                         len = buffer.readFrom(in,max);
1054                     }
1055                     _generator.completeUncheckedAddContent();
1056                     _out.flush();
1057                 }
1058                 finally
1059                 {
1060                     if (resource != null)
1061                         resource.release();
1062                     else
1063                         in.close();
1064 
1065                 }
1066             }
1067             else
1068                 throw new IllegalArgumentException("unknown content type?");
1069 
1070         }
1071     }
1072 
1073     /* ------------------------------------------------------------ */
1074     /* ------------------------------------------------------------ */
1075     /* ------------------------------------------------------------ */
1076     public class OutputWriter extends AbstractGenerator.OutputWriter
1077     {
1078         OutputWriter()
1079         {
1080             super(HttpConnection.this._out);
1081         }
1082     }
1083 
1084 }