View Javadoc

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