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                 // TODO try to do this lazily or more efficiently
519                 String info = URIUtil.canonicalPath(_uri.getDecodedPath());
520                 if (info == null)
521                     throw new HttpException(400);
522                 _request.setPathInfo(info);
523 
524                 if (_out != null)
525                     _out.reopen();
526 
527                 if (Log.isDebugEnabled())
528                 {
529                     threadName = Thread.currentThread().getName();
530                     Thread.currentThread().setName(threadName + " - " + _uri);
531                 }
532 
533                 _connector.customize(_endp,_request);
534 
535                 _server.handle(this);
536             }
537             catch (RetryRequest r)
538             {
539                 if (Log.isDebugEnabled())
540                     Log.ignore(r);
541                 retrying = true;
542             }
543             catch (EofException e)
544             {
545                 Log.ignore(e);
546                 error = true;
547             }
548             catch (HttpException e)
549             {
550                 Log.debug(e);
551                 _request.setHandled(true);
552                 _response.sendError(e.getStatus(),e.getReason());
553                 error = true;
554             }
555             catch (Exception e)
556             {
557                 Log.warn(e);
558                 _request.setHandled(true);
559                 _generator.sendError(500,null,null,true);
560                 error = true;
561             }
562             catch (Error e)
563             {
564                 Log.warn(e);
565                 _request.setHandled(true);
566                 _generator.sendError(500,null,null,true);
567                 error = true;
568             }
569             finally
570             {
571                 if (threadName != null)
572                     Thread.currentThread().setName(threadName);
573 
574                 if (!retrying)
575                 {
576                     if (_request.getContinuation() != null)
577                     {
578                         Log.debug("continuation still pending {}");
579                         _request.getContinuation().reset();
580                     }
581 
582                     if (_endp.isOpen())
583                     {
584                         if (_generator.isPersistent())
585                             _connector.persist(_endp);
586 
587                         if (error)
588                             _endp.close();
589                         else
590                         {
591                             if (!_response.isCommitted() && !_request.isHandled())
592                                 _response.sendError(HttpServletResponse.SC_NOT_FOUND);
593                             _response.complete();
594                         }
595                     }
596                     else
597                     {
598                         _response.complete(); // TODO ????????????
599                     }
600                 }
601             }
602         }
603     }
604 
605     /* ------------------------------------------------------------ */
606     public void commitResponse(boolean last) throws IOException
607     {
608         if (!_generator.isCommitted())
609         {
610             _generator.setResponse(_response.getStatus(),_response.getReason());
611             _generator.completeHeader(_responseFields,last);
612         }
613         if (last)
614             _generator.complete();
615     }
616 
617     /* ------------------------------------------------------------ */
618     public void completeResponse() throws IOException
619     {
620         if (!_generator.isCommitted())
621         {
622             _generator.setResponse(_response.getStatus(),_response.getReason());
623             _generator.completeHeader(_responseFields,HttpGenerator.LAST);
624         }
625 
626         _generator.complete();
627     }
628 
629     /* ------------------------------------------------------------ */
630     public void flushResponse() throws IOException
631     {
632         try
633         {
634             commitResponse(HttpGenerator.MORE);
635             _generator.flush();
636         }
637         catch (IOException e)
638         {
639             throw (e instanceof EofException)?e:new EofException(e);
640         }
641     }
642 
643     /* ------------------------------------------------------------ */
644     public Generator getGenerator()
645     {
646         return _generator;
647     }
648 
649     /* ------------------------------------------------------------ */
650     public boolean isIncluding()
651     {
652         return _include > 0;
653     }
654 
655     /* ------------------------------------------------------------ */
656     public void include()
657     {
658         _include++;
659     }
660 
661     /* ------------------------------------------------------------ */
662     public void included()
663     {
664         _include--;
665         if (_out != null)
666             _out.reopen();
667     }
668 
669     /* ------------------------------------------------------------ */
670     public boolean isIdle()
671     {
672         return _generator.isIdle() && (_parser.isIdle() || _delayedHandling);
673     }
674 
675     /* ------------------------------------------------------------ */
676     /* ------------------------------------------------------------ */
677     /* ------------------------------------------------------------ */
678     private class RequestHandler extends HttpParser.EventHandler
679     {
680         private String _charset;
681 
682         /*
683          * 
684          * @see
685          * org.mortbay.jetty.HttpParser.EventHandler#startRequest(org.mortbay
686          * .io.Buffer, org.mortbay.io.Buffer, org.mortbay.io.Buffer)
687          */
688         public void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException
689         {
690             _host = false;
691             _expect = UNKNOWN;
692             _delayedHandling = false;
693             _charset = null;
694 
695             if (_request.getTimeStamp() == 0)
696                 _request.setTimeStamp(System.currentTimeMillis());
697             _request.setMethod(method.toString());
698 
699             try
700             {
701                 _uri.parse(uri.array(),uri.getIndex(),uri.length());
702                 _request.setUri(_uri);
703 
704                 if (version == null)
705                 {
706                     _request.setProtocol(HttpVersions.HTTP_0_9);
707                     _version = HttpVersions.HTTP_0_9_ORDINAL;
708                 }
709                 else
710                 {
711                     version = HttpVersions.CACHE.get(version);
712                     _version = HttpVersions.CACHE.getOrdinal(version);
713                     if (_version <= 0)
714                         _version = HttpVersions.HTTP_1_0_ORDINAL;
715                     _request.setProtocol(version.toString());
716                 }
717 
718                 _head = method == HttpMethods.HEAD_BUFFER; // depends on method
719                                                            // being decached.
720             }
721             catch (Exception e)
722             {
723                 throw new HttpException(HttpStatus.ORDINAL_400_Bad_Request,null,e);
724             }
725         }
726 
727         /*
728          * @see
729          * org.mortbay.jetty.HttpParser.EventHandler#parsedHeaderValue(org.mortbay
730          * .io.Buffer)
731          */
732         public void parsedHeader(Buffer name, Buffer value)
733         {
734             int ho = HttpHeaders.CACHE.getOrdinal(name);
735             switch (ho)
736             {
737                 case HttpHeaders.HOST_ORDINAL:
738                     // TODO check if host matched a host in the URI.
739                     _host = true;
740                     break;
741 
742                 case HttpHeaders.EXPECT_ORDINAL:
743                     value = HttpHeaderValues.CACHE.lookup(value);
744                     _expect = HttpHeaderValues.CACHE.getOrdinal(value);
745                     break;
746 
747                 case HttpHeaders.ACCEPT_ENCODING_ORDINAL:
748                 case HttpHeaders.USER_AGENT_ORDINAL:
749                     value = HttpHeaderValues.CACHE.lookup(value);
750                     break;
751 
752                 case HttpHeaders.CONTENT_TYPE_ORDINAL:
753                     value = MimeTypes.CACHE.lookup(value);
754                     _charset = MimeTypes.getCharsetFromContentType(value);
755                     break;
756 
757                 case HttpHeaders.CONNECTION_ORDINAL:
758                     // looks rather clumsy, but the idea is to optimize for a
759                     // single valued header
760                     int ordinal = HttpHeaderValues.CACHE.getOrdinal(value);
761                     switch (ordinal)
762                     {
763                         case -1:
764                         {
765                             String[] values = value.toString().split(",");
766                             for (int i = 0; values != null && i < values.length; i++)
767                             {
768                                 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim());
769 
770                                 if (cb != null)
771                                 {
772                                     switch (cb.getOrdinal())
773                                     {
774                                         case HttpHeaderValues.CLOSE_ORDINAL:
775                                             _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
776                                             _generator.setPersistent(false);
777                                             break;
778 
779                                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
780                                             if (_version == HttpVersions.HTTP_1_0_ORDINAL)
781                                                 _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
782                                             break;
783                                     }
784                                 }
785                             }
786                             break;
787                         }
788                         case HttpHeaderValues.CLOSE_ORDINAL:
789                             _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
790                             _generator.setPersistent(false);
791                             break;
792 
793                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
794                             if (_version == HttpVersions.HTTP_1_0_ORDINAL)
795                                 _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
796                             break;
797                     }
798             }
799 
800             _requestFields.add(name,value);
801         }
802 
803         /*
804          * @see org.mortbay.jetty.HttpParser.EventHandler#headerComplete()
805          */
806         public void headerComplete() throws IOException
807         {
808             _requests++;
809             _generator.setVersion(_version);
810             switch (_version)
811             {
812                 case HttpVersions.HTTP_0_9_ORDINAL:
813                     break;
814                 case HttpVersions.HTTP_1_0_ORDINAL:
815                     _generator.setHead(_head);
816                     break;
817                 case HttpVersions.HTTP_1_1_ORDINAL:
818                     _generator.setHead(_head);
819 
820                     if (_server.getSendDateHeader())
821                         _responseFields.put(HttpHeaders.DATE_BUFFER,_request.getTimeStampBuffer(),_request.getTimeStamp());
822 
823                     if (!_host)
824                     {
825                         _generator.setResponse(HttpStatus.ORDINAL_400_Bad_Request,null);
826                         _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
827                         _generator.completeHeader(_responseFields,true);
828                         _generator.complete();
829                         return;
830                     }
831 
832                     if (_expect != UNKNOWN)
833                     {
834                         if (_expect == HttpHeaderValues.CONTINUE_ORDINAL)
835                         {
836                             // TODO delay sending 100 response until a read is
837                             // attempted.
838                             if (((HttpParser)_parser).getHeaderBuffer() == null || ((HttpParser)_parser).getHeaderBuffer().length() < 2)
839                             {
840                                 _generator.setResponse(HttpStatus.ORDINAL_100_Continue,null);
841                                 _generator.completeHeader(null,true);
842                                 _generator.complete();
843                                 _generator.reset(false);
844                             }
845                         }
846                         else if (_expect == HttpHeaderValues.PROCESSING_ORDINAL)
847                         {
848                         }
849                         else
850                         {
851                             _generator.sendError(HttpStatus.ORDINAL_417_Expectation_Failed,null,null,true);
852                             return;
853                         }
854                     }
855 
856                     break;
857                 default:
858             }
859 
860             if (_charset != null)
861                 _request.setCharacterEncodingUnchecked(_charset);
862 
863             // Either handle now or wait for first content
864             if (((HttpParser)_parser).getContentLength() <= 0 && !((HttpParser)_parser).isChunking())
865                 handleRequest();
866             else
867                 _delayedHandling = true;
868         }
869 
870         /* ------------------------------------------------------------ */
871         /*
872          * @see org.mortbay.jetty.HttpParser.EventHandler#content(int,
873          * org.mortbay.io.Buffer)
874          */
875         public void content(Buffer ref) throws IOException
876         {
877             if (_delayedHandling)
878             {
879                 _delayedHandling = false;
880                 handleRequest();
881             }
882         }
883 
884         /*
885          * (non-Javadoc)
886          * 
887          * @see org.mortbay.jetty.HttpParser.EventHandler#messageComplete(int)
888          */
889         public void messageComplete(long contentLength) throws IOException
890         {
891             if (_delayedHandling)
892             {
893                 _delayedHandling = false;
894                 handleRequest();
895             }
896         }
897 
898         /*
899          * (non-Javadoc)
900          * 
901          * @see
902          * org.mortbay.jetty.HttpParser.EventHandler#startResponse(org.mortbay
903          * .io.Buffer, int, org.mortbay.io.Buffer)
904          */
905         public void startResponse(Buffer version, int status, Buffer reason)
906         {
907             Log.debug("Bad request!: " + version + " " + status + " " + reason);
908         }
909 
910     }
911 
912     /* ------------------------------------------------------------ */
913     /* ------------------------------------------------------------ */
914     /* ------------------------------------------------------------ */
915     public class Output extends AbstractGenerator.Output
916     {
917         Output()
918         {
919             super((AbstractGenerator)HttpConnection.this._generator,_connector.getMaxIdleTime());
920         }
921 
922         /* ------------------------------------------------------------ */
923         /*
924          * @see java.io.OutputStream#close()
925          */
926         public void close() throws IOException
927         {
928             if (_closed)
929                 return;
930 
931             if (!isIncluding() && !_generator.isCommitted())
932                 commitResponse(HttpGenerator.LAST);
933             else
934                 flushResponse();
935 
936             super.close();
937         }
938 
939         /* ------------------------------------------------------------ */
940         /*
941          * @see java.io.OutputStream#flush()
942          */
943         public void flush() throws IOException
944         {
945             if (!_generator.isCommitted())
946                 commitResponse(HttpGenerator.MORE);
947             super.flush();
948         }
949 
950         /* ------------------------------------------------------------ */
951         /*
952          * @see javax.servlet.ServletOutputStream#print(java.lang.String)
953          */
954         public void print(String s) throws IOException
955         {
956             if (_closed)
957                 throw new IOException("Closed");
958             PrintWriter writer = getPrintWriter(null);
959             writer.print(s);
960         }
961 
962         /* ------------------------------------------------------------ */
963         public void sendResponse(Buffer response) throws IOException
964         {
965             ((HttpGenerator)_generator).sendResponse(response);
966         }
967 
968         /* ------------------------------------------------------------ */
969         public void sendContent(Object content) throws IOException
970         {
971             Resource resource = null;
972 
973             if (_closed)
974                 throw new IOException("Closed");
975 
976             if (_generator.getContentWritten() > 0)
977                 throw new IllegalStateException("!empty");
978 
979             if (content instanceof HttpContent)
980             {
981                 HttpContent c = (HttpContent)content;
982                 Buffer contentType = c.getContentType();
983                 if (contentType != null && !_responseFields.containsKey(HttpHeaders.CONTENT_TYPE_BUFFER))
984                 {
985                     String enc = _response.getSetCharacterEncoding();
986                     if(enc==null)
987                         _responseFields.add(HttpHeaders.CONTENT_TYPE_BUFFER, contentType);
988                     else
989                     {
990                         if(contentType instanceof CachedBuffer)
991                         {
992                             CachedBuffer content_type = ((CachedBuffer)contentType).getAssociate(enc);
993                             if(content_type!=null)
994                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, content_type);
995                             else
996                             {
997                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, 
998                                         contentType+";charset="+QuotedStringTokenizer.quote(enc,";= "));
999                             }
1000                         }
1001                         else
1002                         {
1003                             _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, 
1004                                     contentType+";charset="+QuotedStringTokenizer.quote(enc,";= "));
1005                         }
1006                     }
1007                 }
1008                 if (c.getContentLength() > 0)
1009                     _responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER,c.getContentLength());
1010                 Buffer lm = c.getLastModified();
1011                 long lml = c.getResource().lastModified();
1012                 if (lm != null)
1013                     _responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER,lm,lml);
1014                 else if (c.getResource() != null)
1015                 {
1016                     if (lml != -1)
1017                         _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,lml);
1018                 }
1019 
1020                 content = c.getBuffer();
1021                 if (content == null)
1022                     content = c.getInputStream();
1023             }
1024             else if (content instanceof Resource)
1025             {
1026                 resource = (Resource)content;
1027                 _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,resource.lastModified());
1028                 content = resource.getInputStream();
1029             }
1030 
1031             if (content instanceof Buffer)
1032             {
1033                 _generator.addContent((Buffer)content,HttpGenerator.LAST);
1034                 commitResponse(HttpGenerator.LAST);
1035             }
1036             else if (content instanceof InputStream)
1037             {
1038                 InputStream in = (InputStream)content;
1039 
1040                 try
1041                 {
1042                     int max = _generator.prepareUncheckedAddContent();
1043                     Buffer buffer = _generator.getUncheckedBuffer();
1044 
1045                     int len = buffer.readFrom(in,max);
1046 
1047                     while (len >= 0)
1048                     {
1049                         _generator.completeUncheckedAddContent();
1050                         _out.flush();
1051 
1052                         max = _generator.prepareUncheckedAddContent();
1053                         buffer = _generator.getUncheckedBuffer();
1054                         len = buffer.readFrom(in,max);
1055                     }
1056                     _generator.completeUncheckedAddContent();
1057                     _out.flush();
1058                 }
1059                 finally
1060                 {
1061                     if (resource != null)
1062                         resource.release();
1063                     else
1064                         in.close();
1065 
1066                 }
1067             }
1068             else
1069                 throw new IllegalArgumentException("unknown content type?");
1070 
1071         }
1072     }
1073 
1074     /* ------------------------------------------------------------ */
1075     /* ------------------------------------------------------------ */
1076     /* ------------------------------------------------------------ */
1077     public class OutputWriter extends AbstractGenerator.OutputWriter
1078     {
1079         OutputWriter()
1080         {
1081             super(HttpConnection.this._out);
1082         }
1083     }
1084 
1085 }