diff src/org/eclipse/jetty/server/AbstractHttpConnection.java @ 802:3428c60d7cfc

replace jetty jars with source
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 07 Sep 2016 21:15:48 -0600
parents
children 8e9db0bbf4f9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/AbstractHttpConnection.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1273 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.continuation.ContinuationThrowable;
+import org.eclipse.jetty.http.EncodedHttpURI;
+import org.eclipse.jetty.http.Generator;
+import org.eclipse.jetty.http.HttpBuffers;
+import org.eclipse.jetty.http.HttpContent;
+import org.eclipse.jetty.http.HttpException;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpGenerator;
+import org.eclipse.jetty.http.HttpHeaderValues;
+import org.eclipse.jetty.http.HttpHeaders;
+import org.eclipse.jetty.http.HttpMethods;
+import org.eclipse.jetty.http.HttpParser;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersions;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.http.Parser;
+import org.eclipse.jetty.http.PathMap;
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.BufferCache.CachedBuffer;
+import org.eclipse.jetty.io.Buffers;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.io.RuntimeIOException;
+import org.eclipse.jetty.io.UncheckedPrintWriter;
+import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.server.nio.NIOConnector;
+import org.eclipse.jetty.server.ssl.SslConnector;
+import org.eclipse.jetty.util.QuotedStringTokenizer;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+
+/**
+ * <p>A HttpConnection represents the connection of a HTTP client to the server
+ * and is created by an instance of a {@link Connector}. It's prime function is
+ * to associate {@link Request} and {@link Response} instances with a {@link EndPoint}.
+ * </p>
+ * <p>
+ * A connection is also the prime mechanism used by jetty to recycle objects without
+ * pooling.  The {@link Request},  {@link Response}, {@link HttpParser}, {@link HttpGenerator}
+ * and {@link HttpFields} instances are all recycled for the duraction of
+ * a connection. Where appropriate, allocated buffers are also kept associated
+ * with the connection via the parser and/or generator.
+ * </p>
+ * <p>
+ * The connection state is held by 3 separate state machines: The request state, the
+ * response state and the continuation state.  All three state machines must be driven
+ * to completion for every request, and all three can complete in any order.
+ * </p>
+ * <p>
+ * The HttpConnection support protocol upgrade.  If on completion of a request, the
+ * response code is 101 (switch protocols), then the org.eclipse.jetty.io.Connection
+ * request attribute is checked to see if there is a new Connection instance. If so,
+ * the new connection is returned from {@link #handle()} and is used for future
+ * handling of the underlying connection.   Note that for switching protocols that
+ * don't use 101 responses (eg CONNECT), the response should be sent and then the
+ * status code changed to 101 before returning from the handler.  Implementors
+ * of new Connection types should be careful to extract any buffered data from
+ * (HttpParser)http.getParser()).getHeaderBuffer() and
+ * (HttpParser)http.getParser()).getBodyBuffer() to initialise their new connection.
+ * </p>
+ *
+ */
+public abstract class AbstractHttpConnection  extends AbstractConnection
+{
+    private static final Logger LOG = Log.getLogger(AbstractHttpConnection.class);
+
+    private static final int UNKNOWN = -2;
+    private static final ThreadLocal<AbstractHttpConnection> __currentConnection = new ThreadLocal<AbstractHttpConnection>();
+
+    private int _requests;
+
+    protected final Connector _connector;
+    protected final Server _server;
+    protected final HttpURI _uri;
+
+    protected final Parser _parser;
+    protected final HttpFields _requestFields;
+    protected final Request _request;
+    protected volatile ServletInputStream _in;
+
+    protected final Generator _generator;
+    protected final HttpFields _responseFields;
+    protected final Response _response;
+    protected volatile Output _out;
+    protected volatile OutputWriter _writer;
+    protected volatile PrintWriter _printWriter;
+
+    int _include;
+
+    private Object _associatedObject; // associated object
+
+    private int _version = UNKNOWN;
+
+    private String _charset;
+    private boolean _expect = false;
+    private boolean _expect100Continue = false;
+    private boolean _expect102Processing = false;
+    private boolean _head = false;
+    private boolean _host = false;
+    private boolean _delayedHandling=false;
+    private boolean _earlyEOF = false;
+
+    /* ------------------------------------------------------------ */
+    public static AbstractHttpConnection getCurrentConnection()
+    {
+        return __currentConnection.get();
+    }
+
+    /* ------------------------------------------------------------ */
+    protected static void setCurrentConnection(AbstractHttpConnection connection)
+    {
+        __currentConnection.set(connection);
+    }
+
+    /* ------------------------------------------------------------ */
+    public AbstractHttpConnection(Connector connector, EndPoint endpoint, Server server)
+    {
+        super(endpoint);
+        _uri = StringUtil.__UTF8.equals(URIUtil.__CHARSET)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
+        _connector = connector;
+        HttpBuffers ab = (HttpBuffers)_connector;
+        _parser = newHttpParser(ab.getRequestBuffers(), endpoint, new RequestHandler());
+        _requestFields = new HttpFields();
+        _responseFields = new HttpFields();
+        _request = new Request(this);
+        _response = new Response(this);
+        _generator = newHttpGenerator(ab.getResponseBuffers(), endpoint);
+        _generator.setSendServerVersion(server.getSendServerVersion());
+        _server = server;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected AbstractHttpConnection(Connector connector, EndPoint endpoint, Server server,
+            Parser parser, Generator generator, Request request)
+    {
+        super(endpoint);
+
+        _uri = URIUtil.__CHARSET.equals(StringUtil.__UTF8)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
+        _connector = connector;
+        _parser = parser;
+        _requestFields = new HttpFields();
+        _responseFields = new HttpFields();
+        _request = request;
+        _response = new Response(this);
+        _generator = generator;
+        _generator.setSendServerVersion(server.getSendServerVersion());
+        _server = server;
+    }
+
+    protected HttpParser newHttpParser(Buffers requestBuffers, EndPoint endpoint, HttpParser.EventHandler requestHandler)
+    {
+        return new HttpParser(requestBuffers, endpoint, requestHandler);
+    }
+
+    protected HttpGenerator newHttpGenerator(Buffers responseBuffers, EndPoint endPoint)
+    {
+        return new HttpGenerator(responseBuffers, endPoint);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the parser used by this connection
+     */
+    public Parser getParser()
+    {
+        return _parser;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the number of requests handled by this connection
+     */
+    public int getRequests()
+    {
+        return _requests;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Server getServer()
+    {
+        return _server;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the associatedObject.
+     */
+    public Object getAssociatedObject()
+    {
+        return _associatedObject;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param associatedObject The associatedObject to set.
+     */
+    public void setAssociatedObject(Object associatedObject)
+    {
+        _associatedObject = associatedObject;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the connector.
+     */
+    public Connector getConnector()
+    {
+        return _connector;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the requestFields.
+     */
+    public HttpFields getRequestFields()
+    {
+        return _requestFields;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the responseFields.
+     */
+    public HttpFields getResponseFields()
+    {
+        return _responseFields;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Find out if the request supports CONFIDENTIAL security.
+     * @param request the incoming HTTP request
+     * @return the result of calling {@link Connector#isConfidential(Request)}, or false
+     * if there is no connector
+     */
+    public boolean isConfidential(Request request)
+    {
+        return _connector != null && _connector.isConfidential(request);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Find out if the request supports INTEGRAL security.
+     * @param request the incoming HTTP request
+     * @return the result of calling {@link Connector#isIntegral(Request)}, or false
+     * if there is no connector
+     */
+    public boolean isIntegral(Request request)
+    {
+        return _connector != null && _connector.isIntegral(request);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return <code>false</code> (this method is not yet implemented)
+     */
+    public boolean getResolveNames()
+    {
+        return _connector.getResolveNames();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the request.
+     */
+    public Request getRequest()
+    {
+        return _request;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the response.
+     */
+    public Response getResponse()
+    {
+        return _response;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get the inputStream from the connection.
+     * <p>
+     * If the associated response has the Expect header set to 100 Continue,
+     * then accessing the input stream indicates that the handler/servlet
+     * is ready for the request body and thus a 100 Continue response is sent.
+     *
+     * @return The input stream for this connection.
+     * The stream will be created if it does not already exist.
+     * @throws IOException if the input stream cannot be retrieved
+     */
+    public ServletInputStream getInputStream() throws IOException
+    {
+        // If the client is expecting 100 CONTINUE, then send it now.
+        if (_expect100Continue)
+        {
+            // is content missing?
+            if (((HttpParser)_parser).getHeaderBuffer()==null || ((HttpParser)_parser).getHeaderBuffer().length()<2)
+            {
+                if (_generator.isCommitted())
+                    throw new IllegalStateException("Committed before 100 Continues");
+
+                ((HttpGenerator)_generator).send1xx(HttpStatus.CONTINUE_100);
+            }
+            _expect100Continue=false;
+        }
+
+        if (_in == null)
+            _in = new HttpInput(AbstractHttpConnection.this);
+        return _in;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The output stream for this connection. The stream will be created if it does not already exist.
+     */
+    public ServletOutputStream getOutputStream()
+    {
+        if (_out == null)
+            _out = new Output();
+        return _out;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param encoding the PrintWriter encoding
+     * @return A {@link PrintWriter} wrapping the {@link #getOutputStream output stream}. The writer is created if it
+     *    does not already exist.
+     */
+    public PrintWriter getPrintWriter(String encoding)
+    {
+        getOutputStream();
+        if (_writer==null)
+        {
+            _writer=new OutputWriter();
+            if (_server.isUncheckedPrintWriter())
+                _printWriter=new UncheckedPrintWriter(_writer);
+            else
+                _printWriter = new PrintWriter(_writer)
+                {
+                    public void close()
+                    {
+                        synchronized (lock)
+                        {
+                            try
+                            {
+                                out.close();
+                            }
+                            catch (IOException e)
+                            {
+                                setError();
+                            }
+                        }
+                    }
+                };
+        }
+        _writer.setCharacterEncoding(encoding);
+        return _printWriter;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isResponseCommitted()
+    {
+        return _generator.isCommitted();
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isEarlyEOF()
+    {
+        return _earlyEOF;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void reset()
+    {
+        _parser.reset();
+        _parser.returnBuffers(); // TODO maybe only on unhandle
+        _requestFields.clear();
+        _request.recycle();
+        _generator.reset();
+        _generator.returnBuffers();// TODO maybe only on unhandle
+        _responseFields.clear();
+        _response.recycle();
+        _uri.clear();
+        _writer=null;
+        _earlyEOF = false;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void handleRequest() throws IOException
+    {
+        boolean error = false;
+
+        String threadName=null;
+        Throwable async_exception=null;
+        try
+        {
+            if (LOG.isDebugEnabled())
+            {
+                threadName=Thread.currentThread().getName();
+                Thread.currentThread().setName(threadName+" - "+_uri);
+            }
+
+
+            // Loop here to handle async request redispatches.
+            // The loop is controlled by the call to async.unhandle in the
+            // finally block below.  If call is from a non-blocking connector,
+            // then the unhandle will return false only if an async dispatch has
+            // already happened when unhandle is called.   For a blocking connector,
+            // the wait for the asynchronous dispatch or timeout actually happens
+            // within the call to unhandle().
+
+            final Server server=_server;
+            boolean was_continuation=_request._async.isContinuation();
+            boolean handling=_request._async.handling() && server!=null && server.isRunning();
+            while (handling)
+            {
+                _request.setHandled(false);
+
+                String info=null;
+                try
+                {
+                    _uri.getPort();
+                    String path = null;
+
+                    try
+                    {
+                        path = _uri.getDecodedPath();
+                    }
+                    catch (Exception e)
+                    {
+                        LOG.warn("Failed UTF-8 decode for request path, trying ISO-8859-1");
+                        LOG.ignore(e);
+                        path = _uri.getDecodedPath(StringUtil.__ISO_8859_1);
+                    }
+
+                    info=URIUtil.canonicalPath(path);
+                    if (info==null && !_request.getMethod().equals(HttpMethods.CONNECT))
+                    {
+                        if (path==null && _uri.getScheme()!=null && _uri.getHost()!=null)
+                        {
+                            info="/";
+                            _request.setRequestURI("");
+                        }
+                        else
+                            throw new HttpException(400);
+                    }
+                    _request.setPathInfo(info);
+
+                    if (_out!=null)
+                        _out.reopen();
+
+                    if (_request._async.isInitial())
+                    {
+                        _request.setDispatcherType(DispatcherType.REQUEST);
+                        _connector.customize(_endp, _request);
+                        server.handle(this);
+                    }
+                    else
+                    {
+                        if (_request._async.isExpired()&&!was_continuation)
+                        {
+                            async_exception = (Throwable)_request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
+                            _response.setStatus(500,async_exception==null?"Async Timeout":"Async Exception");
+                            _request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(500));
+                            _request.setAttribute(RequestDispatcher.ERROR_MESSAGE, _response.getReason());
+                            _request.setDispatcherType(DispatcherType.ERROR);
+                            
+                            ErrorHandler eh = _request._async.getContextHandler().getErrorHandler();
+                            if (eh instanceof ErrorHandler.ErrorPageMapper)
+                            {
+                                String error_page=((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_request._async.getRequest());
+                                if (error_page!=null)
+                                { 
+                                    AsyncContinuation.AsyncEventState state = _request._async.getAsyncEventState();
+                                    state.setPath(error_page);
+                                }
+                            }
+                        }
+                        else
+                            _request.setDispatcherType(DispatcherType.ASYNC);
+                        server.handleAsync(this);
+                    }
+                }
+                catch (ContinuationThrowable e)
+                {
+                    LOG.ignore(e);
+                }
+                catch (EofException e)
+                {
+                    async_exception=e;
+                    LOG.debug(e);
+                    error=true;
+                    _request.setHandled(true);
+                    if (!_response.isCommitted())
+                        _generator.sendError(500, null, null, true);
+                }
+                catch (RuntimeIOException e)
+                {
+                    async_exception=e;
+                    LOG.debug(e);
+                    error=true;
+                    _request.setHandled(true);
+                }
+                catch (HttpException e)
+                {
+                    LOG.debug(e);
+                    error=true;
+                    _request.setHandled(true);
+                    _response.sendError(e.getStatus(), e.getReason());
+                }
+                catch (Throwable e)
+                {
+                    async_exception=e;
+                    LOG.warn(String.valueOf(_uri),e);
+                    error=true;
+                    _request.setHandled(true);
+                    _generator.sendError(info==null?400:500, null, null, true);
+                    
+                }
+                finally
+                {
+                    // Complete async requests 
+                    if (error && _request.isAsyncStarted())
+                        _request.getAsyncContinuation().errorComplete();
+                        
+                    was_continuation=_request._async.isContinuation();
+                    handling = !_request._async.unhandle() && server.isRunning() && _server!=null;
+                }
+            }
+        }
+        finally
+        {
+            if (threadName!=null)
+                Thread.currentThread().setName(threadName);
+
+            if (_request._async.isUncompleted())
+            {
+                
+                _request._async.doComplete(async_exception);
+
+                if (_expect100Continue)
+                {
+                    LOG.debug("100 continues not sent");
+                    // We didn't send 100 continues, but the latest interpretation
+                    // of the spec (see httpbis) is that the client will either
+                    // send the body anyway, or close.  So we no longer need to
+                    // do anything special here other than make the connection not persistent
+                    _expect100Continue = false;
+                    if (!_response.isCommitted())
+                        _generator.setPersistent(false);
+                }
+
+                if(_endp.isOpen())
+                {
+                    if (error)
+                    {
+                        _endp.shutdownOutput();
+                        _generator.setPersistent(false);
+                        if (!_generator.isComplete())
+                            _response.complete();
+                    }
+                    else
+                    {
+                        if (!_response.isCommitted() && !_request.isHandled())
+                            _response.sendError(HttpServletResponse.SC_NOT_FOUND);
+                        _response.complete();
+                        if (_generator.isPersistent())
+                            _connector.persist(_endp);
+                    }
+                }
+                else
+                {
+                    _response.complete();
+                }
+
+                _request.setHandled(true);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public abstract Connection handle() throws IOException;
+
+    /* ------------------------------------------------------------ */
+    public void commitResponse(boolean last) throws IOException
+    {
+        if (!_generator.isCommitted())
+        {
+            _generator.setResponse(_response.getStatus(), _response.getReason());
+            try
+            {
+                // If the client was expecting 100 continues, but we sent something
+                // else, then we need to close the connection
+                if (_expect100Continue && _response.getStatus()!=100)
+                    _generator.setPersistent(false);
+                _generator.completeHeader(_responseFields, last);
+            }
+            catch(RuntimeException e)
+            {
+                LOG.warn("header full: " + e);
+
+                _response.reset();
+                _generator.reset();
+                _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null);
+                _generator.completeHeader(_responseFields,Generator.LAST);
+                _generator.complete();
+                throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500);
+            }
+
+        }
+        if (last)
+            _generator.complete();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void completeResponse() throws IOException
+    {
+        if (!_generator.isCommitted())
+        {
+            _generator.setResponse(_response.getStatus(), _response.getReason());
+            try
+            {
+                _generator.completeHeader(_responseFields, Generator.LAST);
+            }
+            catch(RuntimeException e)
+            {
+                LOG.warn("header full: "+e);
+                LOG.debug(e);
+
+                _response.reset();
+                _generator.reset();
+                _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null);
+                _generator.completeHeader(_responseFields,Generator.LAST);
+                _generator.complete();
+                throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500);
+            }
+        }
+
+        _generator.complete();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void flushResponse() throws IOException
+    {
+        try
+        {
+            commitResponse(Generator.MORE);
+            _generator.flushBuffer();
+        }
+        catch(IOException e)
+        {
+            throw (e instanceof EofException) ? e:new EofException(e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public Generator getGenerator()
+    {
+        return _generator;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isIncluding()
+    {
+        return _include>0;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void include()
+    {
+        _include++;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void included()
+    {
+        _include--;
+        if (_out!=null)
+            _out.reopen();
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isIdle()
+    {
+        return _generator.isIdle() && (_parser.isIdle() || _delayedHandling);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.io.Connection#isSuspended()
+     */
+    public boolean isSuspended()
+    {
+        return _request.getAsyncContinuation().isSuspended();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void onClose()
+    {
+        LOG.debug("closed {}",this);
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isExpecting100Continues()
+    {
+        return _expect100Continue;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isExpecting102Processing()
+    {
+        return _expect102Processing;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getMaxIdleTime()
+    {
+        if (_connector.isLowResources() && _endp.getMaxIdleTime()==_connector.getMaxIdleTime())
+            return _connector.getLowResourceMaxIdleTime();
+        if (_endp.getMaxIdleTime()>0)
+            return _endp.getMaxIdleTime();
+        return _connector.getMaxIdleTime();
+    }
+
+    /* ------------------------------------------------------------ */
+    public String toString()
+    {
+        return String.format("%s,g=%s,p=%s,r=%d",
+                super.toString(),
+                _generator,
+                _parser,
+                _requests);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException
+    {
+        uri=uri.asImmutableBuffer();
+
+        _host = false;
+        _expect = false;
+        _expect100Continue=false;
+        _expect102Processing=false;
+        _delayedHandling=false;
+        _charset=null;
+
+        if(_request.getTimeStamp()==0)
+            _request.setTimeStamp(System.currentTimeMillis());
+        _request.setMethod(method.toString());
+
+        try
+        {
+            _head=false;
+            switch (HttpMethods.CACHE.getOrdinal(method))
+            {
+              case HttpMethods.CONNECT_ORDINAL:
+                  _uri.parseConnect(uri.array(), uri.getIndex(), uri.length());
+                  break;
+
+              case HttpMethods.HEAD_ORDINAL:
+                  _head=true;
+                  _uri.parse(uri.array(), uri.getIndex(), uri.length());
+                  break;
+
+              default:
+                  _uri.parse(uri.array(), uri.getIndex(), uri.length());
+            }
+
+            _request.setUri(_uri);
+
+            if (version==null)
+            {
+                _request.setProtocol(HttpVersions.HTTP_0_9);
+                _version=HttpVersions.HTTP_0_9_ORDINAL;
+            }
+            else
+            {
+                version= HttpVersions.CACHE.get(version);
+                if (version==null)
+                    throw new HttpException(HttpStatus.BAD_REQUEST_400,null);
+                _version = HttpVersions.CACHE.getOrdinal(version);
+                if (_version <= 0) _version = HttpVersions.HTTP_1_0_ORDINAL;
+                _request.setProtocol(version.toString());
+            }
+        }
+        catch (Exception e)
+        {
+            LOG.debug(e);
+            if (e instanceof HttpException)
+                throw (HttpException)e;
+            throw new HttpException(HttpStatus.BAD_REQUEST_400,null,e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void parsedHeader(Buffer name, Buffer value) throws IOException
+    {
+        int ho = HttpHeaders.CACHE.getOrdinal(name);
+        switch (ho)
+        {
+            case HttpHeaders.HOST_ORDINAL:
+                // TODO check if host matched a host in the URI.
+                _host = true;
+                break;
+
+            case HttpHeaders.EXPECT_ORDINAL:
+                if (_version>=HttpVersions.HTTP_1_1_ORDINAL)
+                {
+                    value = HttpHeaderValues.CACHE.lookup(value);
+                    switch(HttpHeaderValues.CACHE.getOrdinal(value))
+                    {
+                        case HttpHeaderValues.CONTINUE_ORDINAL:
+                            _expect100Continue=_generator instanceof HttpGenerator;
+                            break;
+
+                        case HttpHeaderValues.PROCESSING_ORDINAL:
+                            _expect102Processing=_generator instanceof HttpGenerator;
+                            break;
+
+                        default:
+                            String[] values = value.toString().split(",");
+                            for  (int i=0;values!=null && i<values.length;i++)
+                            {
+                                CachedBuffer cb=HttpHeaderValues.CACHE.get(values[i].trim());
+                                if (cb==null)
+                                    _expect=true;
+                                else
+                                {
+                                    switch(cb.getOrdinal())
+                                    {
+                                        case HttpHeaderValues.CONTINUE_ORDINAL:
+                                            _expect100Continue=_generator instanceof HttpGenerator;
+                                            break;
+                                        case HttpHeaderValues.PROCESSING_ORDINAL:
+                                            _expect102Processing=_generator instanceof HttpGenerator;
+                                            break;
+                                        default:
+                                            _expect=true;
+                                    }
+                                }
+                            }
+                    }
+                }
+                break;
+
+            case HttpHeaders.ACCEPT_ENCODING_ORDINAL:
+            case HttpHeaders.USER_AGENT_ORDINAL:
+                value = HttpHeaderValues.CACHE.lookup(value);
+                break;
+
+            case HttpHeaders.CONTENT_TYPE_ORDINAL:
+                value = MimeTypes.CACHE.lookup(value);
+                _charset=MimeTypes.getCharsetFromContentType(value);
+                break;
+        }
+
+        _requestFields.add(name, value);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void headerComplete() throws IOException
+    {
+        // Handle idle race
+        if (_endp.isOutputShutdown())
+        {
+            _endp.close();
+            return;
+        }
+        
+        _requests++;
+        _generator.setVersion(_version);
+        switch (_version)
+        {
+            case HttpVersions.HTTP_0_9_ORDINAL:
+                break;
+            case HttpVersions.HTTP_1_0_ORDINAL:
+                _generator.setHead(_head);
+                if (_parser.isPersistent())
+                {
+                    _responseFields.add(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.KEEP_ALIVE_BUFFER);
+                    _generator.setPersistent(true);
+                }
+                else if (HttpMethods.CONNECT.equals(_request.getMethod()))
+                {
+                    _generator.setPersistent(true);
+                    _parser.setPersistent(true);
+                }
+
+                if (_server.getSendDateHeader())
+                    _generator.setDate(_request.getTimeStampBuffer());
+                break;
+
+            case HttpVersions.HTTP_1_1_ORDINAL:
+                _generator.setHead(_head);
+
+                if (!_parser.isPersistent())
+                {
+                    _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
+                    _generator.setPersistent(false);
+                }
+                if (_server.getSendDateHeader())
+                    _generator.setDate(_request.getTimeStampBuffer());
+
+                if (!_host)
+                {
+                    LOG.debug("!host {}",this);
+                    _generator.setResponse(HttpStatus.BAD_REQUEST_400, null);
+                    _responseFields.put(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.CLOSE_BUFFER);
+                    _generator.completeHeader(_responseFields, true);
+                    _generator.complete();
+                    return;
+                }
+
+                if (_expect)
+                {
+                    LOG.debug("!expectation {}",this);
+                    _generator.setResponse(HttpStatus.EXPECTATION_FAILED_417, null);
+                    _responseFields.put(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.CLOSE_BUFFER);
+                    _generator.completeHeader(_responseFields, true);
+                    _generator.complete();
+                    return;
+                }
+
+                break;
+            default:
+        }
+
+        if(_charset!=null)
+            _request.setCharacterEncodingUnchecked(_charset);
+
+        // Either handle now or wait for first content
+        if ((((HttpParser)_parser).getContentLength()<=0 && !((HttpParser)_parser).isChunking())||_expect100Continue)
+            handleRequest();
+        else
+            _delayedHandling=true;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void content(Buffer buffer) throws IOException
+    {
+        if (_delayedHandling)
+        {
+            _delayedHandling=false;
+            handleRequest();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void messageComplete(long contentLength) throws IOException
+    {
+        if (_delayedHandling)
+        {
+            _delayedHandling=false;
+            handleRequest();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void earlyEOF()
+    {
+        _earlyEOF = true;
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private class RequestHandler extends HttpParser.EventHandler
+    {
+        /*
+         *
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#startRequest(org.eclipse.io.Buffer,
+         *      org.eclipse.io.Buffer, org.eclipse.io.Buffer)
+         */
+        @Override
+        public void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException
+        {
+            AbstractHttpConnection.this.startRequest(method, uri, version);
+        }
+
+        /*
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#parsedHeaderValue(org.eclipse.io.Buffer)
+         */
+        @Override
+        public void parsedHeader(Buffer name, Buffer value) throws IOException
+        {
+            AbstractHttpConnection.this.parsedHeader(name, value);
+        }
+
+        /*
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#headerComplete()
+         */
+        @Override
+        public void headerComplete() throws IOException
+        {
+            AbstractHttpConnection.this.headerComplete();
+        }
+
+        /* ------------------------------------------------------------ */
+        /*
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#content(int, org.eclipse.io.Buffer)
+         */
+        @Override
+        public void content(Buffer ref) throws IOException
+        {
+            AbstractHttpConnection.this.content(ref);
+        }
+
+        /* ------------------------------------------------------------ */
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#messageComplete(int)
+         */
+        @Override
+        public void messageComplete(long contentLength) throws IOException
+        {
+            AbstractHttpConnection.this.messageComplete(contentLength);
+        }
+
+        /* ------------------------------------------------------------ */
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#startResponse(org.eclipse.io.Buffer, int,
+         *      org.eclipse.io.Buffer)
+         */
+        @Override
+        public void startResponse(Buffer version, int status, Buffer reason)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Bad request!: "+version+" "+status+" "+reason);
+        }
+
+        /* ------------------------------------------------------------ */
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#earlyEOF()
+         */
+        @Override
+        public void earlyEOF()
+        {
+            AbstractHttpConnection.this.earlyEOF();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public class Output extends HttpOutput
+    {
+        Output()
+        {
+            super(AbstractHttpConnection.this);
+        }
+
+        /* ------------------------------------------------------------ */
+        /*
+         * @see java.io.OutputStream#close()
+         */
+        @Override
+        public void close() throws IOException
+        {
+            if (isClosed())
+                return;
+
+            if (!isIncluding() && !super._generator.isCommitted())
+                commitResponse(Generator.LAST);
+            else
+                flushResponse();
+
+            super.close();
+        }
+
+
+        /* ------------------------------------------------------------ */
+        /*
+         * @see java.io.OutputStream#flush()
+         */
+        @Override
+        public void flush() throws IOException
+        {
+            if (!super._generator.isCommitted())
+                commitResponse(Generator.MORE);
+            super.flush();
+        }
+
+        /* ------------------------------------------------------------ */
+        /*
+         * @see javax.servlet.ServletOutputStream#print(java.lang.String)
+         */
+        @Override
+        public void print(String s) throws IOException
+        {
+            if (isClosed())
+                throw new IOException("Closed");
+            PrintWriter writer=getPrintWriter(null);
+            writer.print(s);
+        }
+
+        /* ------------------------------------------------------------ */
+        public void sendResponse(Buffer response) throws IOException
+        {
+            ((HttpGenerator)super._generator).sendResponse(response);
+        }
+
+        /* ------------------------------------------------------------ */
+        public void sendContent(Object content) throws IOException
+        {
+            Resource resource=null;
+
+            if (isClosed())
+                throw new IOException("Closed");
+
+            if (super._generator.isWritten())
+                throw new IllegalStateException("!empty");
+
+            // Convert HTTP content to content
+            if (content instanceof HttpContent)
+            {
+                HttpContent httpContent = (HttpContent) content;
+                Buffer contentType = httpContent.getContentType();
+                if (contentType != null && !_responseFields.containsKey(HttpHeaders.CONTENT_TYPE_BUFFER))
+                {
+                    String enc = _response.getSetCharacterEncoding();
+                    if(enc==null)
+                        _responseFields.add(HttpHeaders.CONTENT_TYPE_BUFFER, contentType);
+                    else
+                    {
+                        if(contentType instanceof CachedBuffer)
+                        {
+                            CachedBuffer content_type = ((CachedBuffer)contentType).getAssociate(enc);
+                            if(content_type!=null)
+                                _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, content_type);
+                            else
+                            {
+                                _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER,
+                                        contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= "));
+                            }
+                        }
+                        else
+                        {
+                            _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER,
+                                    contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= "));
+                        }
+                    }
+                }
+                if (httpContent.getContentLength() > 0)
+                    _responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER, httpContent.getContentLength());
+                Buffer lm = httpContent.getLastModified();
+                long lml=httpContent.getResource().lastModified();
+                if (lm != null)
+                {
+                    _responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER, lm);
+                }
+                else if (httpContent.getResource()!=null)
+                {
+                    if (lml!=-1)
+                        _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, lml);
+                }
+                
+                Buffer etag=httpContent.getETag();
+                if (etag!=null)
+                    _responseFields.put(HttpHeaders.ETAG_BUFFER,etag);
+
+                
+                boolean direct=_connector instanceof NIOConnector && ((NIOConnector)_connector).getUseDirectBuffers() && !(_connector instanceof SslConnector);
+                content = direct?httpContent.getDirectBuffer():httpContent.getIndirectBuffer();
+                if (content==null)
+                    content=httpContent.getInputStream();
+            }
+            else if (content instanceof Resource)
+            {
+                resource=(Resource)content;
+                _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, resource.lastModified());
+                content=resource.getInputStream();
+            }
+
+            // Process content.
+            if (content instanceof Buffer)
+            {
+                super._generator.addContent((Buffer) content, Generator.LAST);
+                commitResponse(Generator.LAST);
+            }
+            else if (content instanceof InputStream)
+            {
+                InputStream in = (InputStream)content;
+
+                try
+                {
+                    int max = super._generator.prepareUncheckedAddContent();
+                    Buffer buffer = super._generator.getUncheckedBuffer();
+
+                    int len=buffer.readFrom(in,max);
+
+                    while (len>=0)
+                    {
+                        super._generator.completeUncheckedAddContent();
+                        _out.flush();
+
+                        max = super._generator.prepareUncheckedAddContent();
+                        buffer = super._generator.getUncheckedBuffer();
+                        len=buffer.readFrom(in,max);
+                    }
+                    super._generator.completeUncheckedAddContent();
+                    _out.flush();
+                }
+                finally
+                {
+                    if (resource!=null)
+                        resource.release();
+                    else
+                        in.close();
+                }
+            }
+            else
+                throw new IllegalArgumentException("unknown content type?");
+
+
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public class OutputWriter extends HttpWriter
+    {
+        OutputWriter()
+        {
+            super(AbstractHttpConnection.this._out);
+        }
+    }
+
+
+}