View Javadoc

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