View Javadoc

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