You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by fh...@apache.org on 2006/06/22 02:48:54 UTC

svn commit: r416187 [1/3] - in /tomcat/tc6.0.x/trunk/java/org/apache: catalina/connector/ coyote/http11/ tomcat/util/net/

Author: fhanik
Date: Wed Jun 21 17:48:53 2006
New Revision: 416187

URL: http://svn.apache.org/viewvc?rev=416187&view=rev
Log:
Non blocking polling information.
This implementation in pure Java NIO is almost a mimic of the APR implementation. It blocks on read and write, but has non blocking polling capabilities. Currently the read/write blocking is "busy" blocking, but I will see if I can simply configure blocking for the socket and if that would still allow the poller to work as expected.
This makes it a suitable connector for comet style protocols and where APR is not desired or available.


Added:
    tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java
    tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java
    tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java
    tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/InternalNioOutputBuffer.java
    tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java
Modified:
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/connector/Connector.java

Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/connector/Connector.java
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/connector/Connector.java?rev=416187&r1=416186&r2=416187&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/connector/Connector.java (original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/connector/Connector.java Wed Jun 21 17:48:53 2006
@@ -623,7 +623,9 @@
         }
 
         if (apr) {
-            if ("HTTP/1.1".equals(protocol)) {
+            if ("HTTP/1.1/NIO".equals(protocol)) {
+                setProtocolHandlerClassName("org.apache.coyote.http11.Http11NioProtocol");
+            } else if ("HTTP/1.1".equals(protocol)) {
                 setProtocolHandlerClassName
                     ("org.apache.coyote.http11.Http11AprProtocol");
             } else if ("AJP/1.3".equals(protocol)) {
@@ -636,7 +638,9 @@
                     ("org.apache.coyote.http11.Http11AprProtocol");
             }
         } else {
-            if ("HTTP/1.1".equals(protocol)) {
+            if ("HTTP/1.1/NIO".equals(protocol)) {
+                setProtocolHandlerClassName("org.apache.coyote.http11.Http11NioProtocol");
+            }else if ("HTTP/1.1".equals(protocol)) {
                 setProtocolHandlerClassName
                     ("org.apache.coyote.http11.Http11Protocol");
             } else if ("AJP/1.3".equals(protocol)) {

Added: tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java?rev=416187&view=auto
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java (added)
+++ tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java Wed Jun 21 17:48:53 2006
@@ -0,0 +1,1797 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetAddress;
+import java.nio.channels.SocketChannel;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.ActionHook;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.RequestInfo;
+import org.apache.coyote.Response;
+import org.apache.coyote.http11.filters.BufferedInputFilter;
+import org.apache.coyote.http11.filters.ChunkedInputFilter;
+import org.apache.coyote.http11.filters.ChunkedOutputFilter;
+import org.apache.coyote.http11.filters.GzipOutputFilter;
+import org.apache.coyote.http11.filters.IdentityInputFilter;
+import org.apache.coyote.http11.filters.IdentityOutputFilter;
+import org.apache.coyote.http11.filters.SavedRequestInputFilter;
+import org.apache.coyote.http11.filters.VoidInputFilter;
+import org.apache.coyote.http11.filters.VoidOutputFilter;
+import org.apache.tomcat.util.buf.Ascii;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.HexUtils;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.FastHttpDateFormat;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.net.NioEndpoint;
+import org.apache.tomcat.util.net.NioEndpoint.Handler;
+import org.apache.tomcat.util.net.NioEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.NioEndpoint.SendfileData;
+import org.apache.tomcat.util.res.StringManager;
+import java.nio.channels.SelectionKey;
+
+
+/**
+ * Processes HTTP requests.
+ *
+ * @author Remy Maucherat
+ * @author Filip Hanik
+ */
+public class Http11NioProcessor implements ActionHook {
+
+
+    /**
+     * Logger.
+     */
+    protected static org.apache.commons.logging.Log log
+        = org.apache.commons.logging.LogFactory.getLog(Http11NioProcessor.class);
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public Http11NioProcessor(int headerBufferSize, NioEndpoint endpoint) {
+
+        this.endpoint = endpoint;
+
+        request = new Request();
+        int readTimeout = endpoint.getFirstReadTimeout();
+        if (readTimeout == 0) {
+            readTimeout = 100;
+        } else if (readTimeout < 0) {
+            readTimeout = timeout;
+            //readTimeout = -1;
+        }
+        inputBuffer = new InternalNioInputBuffer(request, headerBufferSize,
+                readTimeout);
+        request.setInputBuffer(inputBuffer);
+
+        response = new Response();
+        response.setHook(this);
+        outputBuffer = new InternalNioOutputBuffer(response, headerBufferSize);
+        response.setOutputBuffer(outputBuffer);
+        request.setResponse(response);
+
+        ssl = !"off".equalsIgnoreCase(endpoint.getSSLEngine());
+
+        initializeFilters();
+
+        // Cause loading of HexUtils
+        int foo = HexUtils.DEC[0];
+
+        // Cause loading of FastHttpDateFormat
+        FastHttpDateFormat.getCurrentDate();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated adapter.
+     */
+    protected Adapter adapter = null;
+
+
+    /**
+     * Request object.
+     */
+    protected Request request = null;
+
+
+    /**
+     * Response object.
+     */
+    protected Response response = null;
+
+
+    /**
+     * Input.
+     */
+    protected InternalNioInputBuffer inputBuffer = null;
+
+
+    /**
+     * Output.
+     */
+    protected InternalNioOutputBuffer outputBuffer = null;
+
+
+    /**
+     * Error flag.
+     */
+    protected boolean error = false;
+
+
+    /**
+     * Keep-alive.
+     */
+    protected boolean keepAlive = true;
+
+
+    /**
+     * HTTP/1.1 flag.
+     */
+    protected boolean http11 = true;
+
+
+    /**
+     * HTTP/0.9 flag.
+     */
+    protected boolean http09 = false;
+
+
+    /**
+     * Sendfile data.
+     */
+    protected NioEndpoint.SendfileData sendfileData = null;
+
+
+    /**
+     * Comet used.
+     */
+    protected boolean comet = false;
+
+
+    /**
+     * Content delimitator for the request (if false, the connection will
+     * be closed at the end of the request).
+     */
+    protected boolean contentDelimitation = true;
+
+
+    /**
+     * Is there an expectation ?
+     */
+    protected boolean expectation = false;
+
+
+    /**
+     * List of restricted user agents.
+     */
+    protected Pattern[] restrictedUserAgents = null;
+
+
+    /**
+     * Maximum number of Keep-Alive requests to honor.
+     */
+    protected int maxKeepAliveRequests = -1;
+
+
+    /**
+     * SSL enabled ?
+     */
+    protected boolean ssl = false;
+
+
+    /**
+     * Socket associated with the current connection.
+     */
+    protected SocketChannel socket = null;
+
+
+    /**
+     * Remote Address associated with the current connection.
+     */
+    protected String remoteAddr = null;
+
+
+    /**
+     * Remote Host associated with the current connection.
+     */
+    protected String remoteHost = null;
+
+
+    /**
+     * Local Host associated with the current connection.
+     */
+    protected String localName = null;
+
+
+
+    /**
+     * Local port to which the socket is connected
+     */
+    protected int localPort = -1;
+
+
+    /**
+     * Remote port to which the socket is connected
+     */
+    protected int remotePort = -1;
+
+
+    /**
+     * The local Host address.
+     */
+    protected String localAddr = null;
+
+
+    /**
+     * Maximum timeout on uploads. 5 minutes as in Apache HTTPD server.
+     */
+    protected int timeout = 300000;
+
+
+    /**
+     * Flag to disable setting a different time-out on uploads.
+     */
+    protected boolean disableUploadTimeout = false;
+
+
+    /**
+     * Allowed compression level.
+     */
+    protected int compressionLevel = 0;
+
+
+    /**
+     * Minimum contentsize to make compression.
+     */
+    protected int compressionMinSize = 2048;
+
+
+    /**
+     * Socket buffering.
+     */
+    protected int socketBuffer = -1;
+
+
+    /**
+     * Max save post size.
+     */
+    protected int maxSavePostSize = 4 * 1024;
+
+
+    /**
+     * List of user agents to not use gzip with
+     */
+    protected Pattern noCompressionUserAgents[] = null;
+
+    /**
+     * List of MIMES which could be gzipped
+     */
+    protected String[] compressableMimeTypes =
+    { "text/html", "text/xml", "text/plain" };
+
+
+    /**
+     * Host name (used to avoid useless B2C conversion on the host name).
+     */
+    protected char[] hostNameC = new char[0];
+
+
+    /**
+     * Associated endpoint.
+     */
+    protected NioEndpoint endpoint;
+
+
+    /**
+     * Allow a customized the server header for the tin-foil hat folks.
+     */
+    protected String server = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return compression level.
+     */
+    public String getCompression() {
+        switch (compressionLevel) {
+        case 0:
+            return "off";
+        case 1:
+            return "on";
+        case 2:
+            return "force";
+        }
+        return "off";
+    }
+
+
+    /**
+     * Set compression level.
+     */
+    public void setCompression(String compression) {
+        if (compression.equals("on")) {
+            this.compressionLevel = 1;
+        } else if (compression.equals("force")) {
+            this.compressionLevel = 2;
+        } else if (compression.equals("off")) {
+            this.compressionLevel = 0;
+        } else {
+            try {
+                // Try to parse compression as an int, which would give the
+                // minimum compression size
+                compressionMinSize = Integer.parseInt(compression);
+                this.compressionLevel = 1;
+            } catch (Exception e) {
+                this.compressionLevel = 0;
+            }
+        }
+    }
+
+    /**
+     * Set Minimum size to trigger compression.
+     */
+    public void setCompressionMinSize(int compressionMinSize) {
+        this.compressionMinSize = compressionMinSize;
+    }
+
+
+    /**
+     * Add user-agent for which gzip compression didn't works
+     * The user agent String given will be exactly matched
+     * to the user-agent header submitted by the client.
+     *
+     * @param userAgent user-agent string
+     */
+    public void addNoCompressionUserAgent(String userAgent) {
+        try {
+            Pattern nRule = Pattern.compile(userAgent);
+            noCompressionUserAgents =
+                addREArray(noCompressionUserAgents, nRule);
+        } catch (PatternSyntaxException pse) {
+            log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
+        }
+    }
+
+
+    /**
+     * Set no compression user agent list (this method is best when used with
+     * a large number of connectors, where it would be better to have all of
+     * them referenced a single array).
+     */
+    public void setNoCompressionUserAgents(Pattern[] noCompressionUserAgents) {
+        this.noCompressionUserAgents = noCompressionUserAgents;
+    }
+
+
+    /**
+     * Set no compression user agent list.
+     * List contains users agents separated by ',' :
+     *
+     * ie: "gorilla,desesplorer,tigrus"
+     */
+    public void setNoCompressionUserAgents(String noCompressionUserAgents) {
+        if (noCompressionUserAgents != null) {
+            StringTokenizer st = new StringTokenizer(noCompressionUserAgents, ",");
+
+            while (st.hasMoreTokens()) {
+                addNoCompressionUserAgent(st.nextToken().trim());
+            }
+        }
+    }
+
+    /**
+     * Add a mime-type which will be compressable
+     * The mime-type String will be exactly matched
+     * in the response mime-type header .
+     *
+     * @param mimeType mime-type string
+     */
+    public void addCompressableMimeType(String mimeType) {
+        compressableMimeTypes =
+            addStringArray(compressableMimeTypes, mimeType);
+    }
+
+
+    /**
+     * Set compressable mime-type list (this method is best when used with
+     * a large number of connectors, where it would be better to have all of
+     * them referenced a single array).
+     */
+    public void setCompressableMimeTypes(String[] compressableMimeTypes) {
+        this.compressableMimeTypes = compressableMimeTypes;
+    }
+
+
+    /**
+     * Set compressable mime-type list
+     * List contains users agents separated by ',' :
+     *
+     * ie: "text/html,text/xml,text/plain"
+     */
+    public void setCompressableMimeTypes(String compressableMimeTypes) {
+        if (compressableMimeTypes != null) {
+            StringTokenizer st = new StringTokenizer(compressableMimeTypes, ",");
+
+            while (st.hasMoreTokens()) {
+                addCompressableMimeType(st.nextToken().trim());
+            }
+        }
+    }
+
+
+    /**
+     * Return the list of restricted user agents.
+     */
+    public String[] findCompressableMimeTypes() {
+        return (compressableMimeTypes);
+    }
+
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add input or output filter.
+     *
+     * @param className class name of the filter
+     */
+    protected void addFilter(String className) {
+        try {
+            Class clazz = Class.forName(className);
+            Object obj = clazz.newInstance();
+            if (obj instanceof InputFilter) {
+                inputBuffer.addFilter((InputFilter) obj);
+            } else if (obj instanceof OutputFilter) {
+                outputBuffer.addFilter((OutputFilter) obj);
+            } else {
+                log.warn(sm.getString("http11processor.filter.unknown", className));
+            }
+        } catch (Exception e) {
+            log.error(sm.getString("http11processor.filter.error", className), e);
+        }
+    }
+
+
+    /**
+     * General use method
+     *
+     * @param sArray the StringArray
+     * @param value string
+     */
+    private String[] addStringArray(String sArray[], String value) {
+        String[] result = null;
+        if (sArray == null) {
+            result = new String[1];
+            result[0] = value;
+        }
+        else {
+            result = new String[sArray.length + 1];
+            for (int i = 0; i < sArray.length; i++)
+                result[i] = sArray[i];
+            result[sArray.length] = value;
+        }
+        return result;
+    }
+
+
+    /**
+     * General use method
+     *
+     * @param rArray the REArray
+     * @param value Obj
+     */
+    private Pattern[] addREArray(Pattern rArray[], Pattern value) {
+        Pattern[] result = null;
+        if (rArray == null) {
+            result = new Pattern[1];
+            result[0] = value;
+        }
+        else {
+            result = new Pattern[rArray.length + 1];
+            for (int i = 0; i < rArray.length; i++)
+                result[i] = rArray[i];
+            result[rArray.length] = value;
+        }
+        return result;
+    }
+
+
+    /**
+     * General use method
+     *
+     * @param sArray the StringArray
+     * @param value string
+     */
+    private boolean inStringArray(String sArray[], String value) {
+        for (int i = 0; i < sArray.length; i++) {
+            if (sArray[i].equals(value)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Checks if any entry in the string array starts with the specified value
+     *
+     * @param sArray the StringArray
+     * @param value string
+     */
+    private boolean startsWithStringArray(String sArray[], String value) {
+        if (value == null)
+           return false;
+        for (int i = 0; i < sArray.length; i++) {
+            if (value.startsWith(sArray[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Add restricted user-agent (which will downgrade the connector
+     * to HTTP/1.0 mode). The user agent String given will be matched
+     * via regexp to the user-agent header submitted by the client.
+     *
+     * @param userAgent user-agent string
+     */
+    public void addRestrictedUserAgent(String userAgent) {
+        try {
+            Pattern nRule = Pattern.compile(userAgent);
+            restrictedUserAgents = addREArray(restrictedUserAgents, nRule);
+        } catch (PatternSyntaxException pse) {
+            log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
+        }
+    }
+
+
+    /**
+     * Set restricted user agent list (this method is best when used with
+     * a large number of connectors, where it would be better to have all of
+     * them referenced a single array).
+     */
+    public void setRestrictedUserAgents(Pattern[] restrictedUserAgents) {
+        this.restrictedUserAgents = restrictedUserAgents;
+    }
+
+
+    /**
+     * Set restricted user agent list (which will downgrade the connector
+     * to HTTP/1.0 mode). List contains users agents separated by ',' :
+     *
+     * ie: "gorilla,desesplorer,tigrus"
+     */
+    public void setRestrictedUserAgents(String restrictedUserAgents) {
+        if (restrictedUserAgents != null) {
+            StringTokenizer st =
+                new StringTokenizer(restrictedUserAgents, ",");
+            while (st.hasMoreTokens()) {
+                addRestrictedUserAgent(st.nextToken().trim());
+            }
+        }
+    }
+
+
+    /**
+     * Return the list of restricted user agents.
+     */
+    public String[] findRestrictedUserAgents() {
+        String[] sarr = new String [restrictedUserAgents.length];
+
+        for (int i = 0; i < restrictedUserAgents.length; i++)
+            sarr[i] = restrictedUserAgents[i].toString();
+
+        return (sarr);
+    }
+
+
+    /**
+     * Set the maximum number of Keep-Alive requests to honor.
+     * This is to safeguard from DoS attacks.  Setting to a negative
+     * value disables the check.
+     */
+    public void setMaxKeepAliveRequests(int mkar) {
+        maxKeepAliveRequests = mkar;
+    }
+
+
+    /**
+     * Return the number of Keep-Alive requests that we will honor.
+     */
+    public int getMaxKeepAliveRequests() {
+        return maxKeepAliveRequests;
+    }
+
+
+    /**
+     * Set the maximum size of a POST which will be buffered in SSL mode.
+     */
+    public void setMaxSavePostSize(int msps) {
+        maxSavePostSize = msps;
+    }
+
+
+    /**
+     * Return the maximum size of a POST which will be buffered in SSL mode.
+     */
+    public int getMaxSavePostSize() {
+        return maxSavePostSize;
+    }
+
+
+    /**
+     * Set the flag to control upload time-outs.
+     */
+    public void setDisableUploadTimeout(boolean isDisabled) {
+        disableUploadTimeout = isDisabled;
+    }
+
+    /**
+     * Get the flag that controls upload time-outs.
+     */
+    public boolean getDisableUploadTimeout() {
+        return disableUploadTimeout;
+    }
+
+    /**
+     * Set the socket buffer flag.
+     */
+    public void setSocketBuffer(int socketBuffer) {
+        this.socketBuffer = socketBuffer;
+        outputBuffer.setSocketBuffer(socketBuffer);
+    }
+
+    /**
+     * Get the socket buffer flag.
+     */
+    public int getSocketBuffer() {
+        return socketBuffer;
+    }
+
+    /**
+     * Set the upload timeout.
+     */
+    public void setTimeout( int timeouts ) {
+        timeout = timeouts ;
+    }
+
+    /**
+     * Get the upload timeout.
+     */
+    public int getTimeout() {
+        return timeout;
+    }
+
+
+    /**
+     * Set the server header name.
+     */
+    public void setServer( String server ) {
+        if (server==null || server.equals("")) {
+            this.server = null;
+        } else {
+            this.server = server;
+        }
+    }
+
+    /**
+     * Get the server header name.
+     */
+    public String getServer() {
+        return server;
+    }
+
+
+    /** Get the request associated with this processor.
+     *
+     * @return The request
+     */
+    public Request getRequest() {
+        return request;
+    }
+
+    /**
+     * Process pipelined HTTP requests using the specified input and output
+     * streams.
+     *
+     * @throws IOException error during an I/O operation
+     */
+    public SocketState event(boolean error)
+        throws IOException {
+
+        RequestInfo rp = request.getRequestProcessor();
+
+        try {
+            rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+            error = !adapter.event(request, response, error);
+            if (request.getAttribute("org.apache.tomcat.comet") == null) {
+                comet = false;
+            }
+            SelectionKey key = socket.keyFor(endpoint.getPoller().getSelector());
+            if ( key != null ) {
+                NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
+                if ( attach!=null ) attach.setComet(comet);
+            }
+            
+        } catch (InterruptedIOException e) {
+            error = true;
+        } catch (Throwable t) {
+            log.error(sm.getString("http11processor.request.process"), t);
+            // 500 - Internal Server Error
+            response.setStatus(500);
+            error = true;
+        }
+
+        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+
+        if (error) {
+            recycle();
+            return SocketState.CLOSED;
+        } else if (!comet) {
+            recycle();
+            endpoint.getPoller().add(socket);
+            return SocketState.OPEN;
+        } else {
+            endpoint.getCometPoller().add(socket);
+            return SocketState.LONG;
+        }
+    }
+
+    /**
+     * Process pipelined HTTP requests using the specified input and output
+     * streams.
+     *
+     * @throws IOException error during an I/O operation
+     */
+    public SocketState process(SocketChannel socket)
+        throws IOException {
+        RequestInfo rp = request.getRequestProcessor();
+        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
+
+        // Set the remote address
+        remoteAddr = null;
+        remoteHost = null;
+        localAddr = null;
+        localName = null;
+        remotePort = -1;
+        localPort = -1;
+
+        // Setting up the socket
+        this.socket = socket;
+        inputBuffer.setSocket(socket);
+        outputBuffer.setSocket(socket);
+        outputBuffer.setSelector(endpoint.getPoller().getSelector());
+
+        // Error flag
+        error = false;
+        keepAlive = true;
+
+        int keepAliveLeft = maxKeepAliveRequests;
+        long soTimeout = endpoint.getSoTimeout();
+
+        int limit = 0;
+        if (endpoint.getFirstReadTimeout() > 0 || endpoint.getFirstReadTimeout() < -1) {
+            limit = endpoint.getMaxThreads() / 2;
+        }
+
+        boolean keptAlive = false;
+        boolean openSocket = false;
+
+        while (!error && keepAlive && !comet) {
+
+            // Parsing the request header
+            try {
+                if( !disableUploadTimeout && keptAlive && soTimeout > 0 ) {
+                    socket.socket().setSoTimeout((int)soTimeout);
+                    inputBuffer.readTimeout = soTimeout;
+                }
+                if (!inputBuffer.parseRequestLine
+                        (keptAlive && (endpoint.getCurrentThreadsBusy() > limit))) {
+                    // This means that no data is available right now
+                    // (long keepalive), so that the processor should be recycled
+                    // and the method should return true
+                    openSocket = true;
+                    // Add the socket to the poller
+                    endpoint.getPoller().add(socket);
+                    break;
+                }
+                request.setStartTime(System.currentTimeMillis());
+                keptAlive = true;
+                if (!disableUploadTimeout) {
+                    socket.socket().setSoTimeout((int)timeout);
+                    inputBuffer.readTimeout = soTimeout;
+                }
+                inputBuffer.parseHeaders();
+            } catch (IOException e) {
+                error = true;
+                break;
+            } catch (Throwable t) {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("http11processor.header.parse"), t);
+                }
+                // 400 - Bad Request
+                response.setStatus(400);
+                error = true;
+            }
+
+            // Setting up filters, and parse some request headers
+            rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
+            try {
+                prepareRequest();
+            } catch (Throwable t) {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("http11processor.request.prepare"), t);
+                }
+                // 400 - Internal Server Error
+                response.setStatus(400);
+                error = true;
+            }
+
+            if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0)
+                keepAlive = false;
+
+            // Process the request in the adapter
+            if (!error) {
+                try {
+                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+                    adapter.service(request, response);
+                    // Handle when the response was committed before a serious
+                    // error occurred.  Throwing a ServletException should both
+                    // set the status to 500 and set the errorException.
+                    // If we fail here, then the response is likely already
+                    // committed, so we can't try and set headers.
+                    if(keepAlive && !error) { // Avoid checking twice.
+                        error = response.getErrorException() != null ||
+                                statusDropsConnection(response.getStatus());
+                    }
+                    // Comet support
+                    if (request.getAttribute("org.apache.tomcat.comet") != null) {
+                        comet = true;
+                    }
+                    SelectionKey key = socket.keyFor(endpoint.getPoller().getSelector());
+                    if (key != null) {
+                        NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
+                        if (attach != null) attach.setComet(comet);
+                    }
+                } catch (InterruptedIOException e) {
+                    error = true;
+                } catch (Throwable t) {
+                    log.error(sm.getString("http11processor.request.process"), t);
+                    // 500 - Internal Server Error
+                    response.setStatus(500);
+                    error = true;
+                }
+            }
+
+            // Finish the handling of the request
+            if (!comet) {
+                endRequest();
+            }
+
+            // If there was an error, make sure the request is counted as
+            // and error, and update the statistics counter
+            if (error) {
+                response.setStatus(500);
+            }
+            request.updateCounters();
+
+            // Do sendfile as needed: add socket to sendfile and end
+            if (sendfileData != null && !error) {
+                sendfileData.socket = socket;
+                sendfileData.keepAlive = keepAlive;
+                if (!endpoint.getSendfile().add(sendfileData)) {
+                    openSocket = true;
+                    break;
+                }
+            }
+
+            rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
+
+        }
+
+        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+
+        if (comet) {
+            if (error) {
+                recycle();
+                return SocketState.CLOSED;
+            } else {
+                return SocketState.LONG;
+            }
+        } else {
+            recycle();
+            return (openSocket) ? SocketState.OPEN : SocketState.CLOSED;
+        }
+
+    }
+
+
+    public void endRequest() {
+
+        // Finish the handling of the request
+        try {
+            inputBuffer.endRequest();
+        } catch (IOException e) {
+            error = true;
+        } catch (Throwable t) {
+            log.error(sm.getString("http11processor.request.finish"), t);
+            // 500 - Internal Server Error
+            response.setStatus(500);
+            error = true;
+        }
+        try {
+            outputBuffer.endRequest();
+        } catch (IOException e) {
+            error = true;
+        } catch (Throwable t) {
+            log.error(sm.getString("http11processor.response.finish"), t);
+            error = true;
+        }
+
+        // Next request
+        inputBuffer.nextRequest();
+        outputBuffer.nextRequest();
+
+    }
+
+
+    public void recycle() {
+        inputBuffer.recycle();
+        outputBuffer.recycle();
+        this.socket = null;
+    }
+
+
+    // ----------------------------------------------------- ActionHook Methods
+
+
+    /**
+     * Send an action to the connector.
+     *
+     * @param actionCode Type of the action
+     * @param param Action parameter
+     */
+    public void action(ActionCode actionCode, Object param) {
+
+        if (actionCode == ActionCode.ACTION_COMMIT) {
+            // Commit current response
+
+            if (response.isCommitted())
+                return;
+
+            // Validate and write response headers
+            prepareResponse();
+            try {
+                outputBuffer.commit();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+
+        } else if (actionCode == ActionCode.ACTION_ACK) {
+
+            // Acknowlege request
+
+            // Send a 100 status back if it makes sense (response not committed
+            // yet, and client specified an expectation for 100-continue)
+
+            if ((response.isCommitted()) || !expectation)
+                return;
+
+            inputBuffer.setSwallowInput(true);
+            try {
+                outputBuffer.sendAck();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+
+        } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
+
+            try {
+                outputBuffer.flush();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+                response.setErrorException(e);
+            }
+
+        } else if (actionCode == ActionCode.ACTION_CLOSE) {
+            // Close
+
+            // End the processing of the current request, and stop any further
+            // transactions with the client
+
+            comet = false;
+            try {
+                outputBuffer.endRequest();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+
+        } else if (actionCode == ActionCode.ACTION_RESET) {
+
+            // Reset response
+
+            // Note: This must be called before the response is committed
+
+            outputBuffer.reset();
+
+        } else if (actionCode == ActionCode.ACTION_CUSTOM) {
+
+            // Do nothing
+
+        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
+
+            // Get remote host address
+            if ((remoteAddr == null) && (socket != null)) {
+                InetAddress inetAddr = socket.socket().getInetAddress();
+                if (inetAddr != null) {
+                    remoteAddr = inetAddr.getHostAddress();
+                }
+            }
+            request.remoteAddr().setString(remoteAddr);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
+
+            // Get local host name
+            if ((localName == null) && (socket != null)) {
+                InetAddress inetAddr = socket.socket().getLocalAddress();
+                if (inetAddr != null) {
+                    localName = inetAddr.getHostName();
+                }
+            }
+            request.localName().setString(localName);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
+
+            // Get remote host name
+            if ((remoteHost == null) && (socket != null)) {
+                InetAddress inetAddr = socket.socket().getInetAddress();
+                if (inetAddr != null) {
+                    remoteHost = inetAddr.getHostName();
+                }
+                if(remoteHost == null) {
+                    if(remoteAddr != null) {
+                        remoteHost = remoteAddr;
+                    } else { // all we can do is punt
+                        request.remoteHost().recycle();
+                    }
+                }
+            }
+            request.remoteHost().setString(remoteHost);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
+
+            if (localAddr == null)
+               localAddr = socket.socket().getLocalAddress().getHostAddress();
+
+            request.localAddr().setString(localAddr);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
+
+            if ((remotePort == -1 ) && (socket !=null)) {
+                remotePort = socket.socket().getPort();
+            }
+            request.setRemotePort(remotePort);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
+
+            if ((localPort == -1 ) && (socket !=null)) {
+                localPort = socket.socket().getLocalPort();
+            }
+            request.setLocalPort(localPort);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
+
+//            if (ssl && (socket != 0)) {
+//                try {
+//                    // Cipher suite
+//                    Object sslO = SSLSocket.getInfoS(socket, SSL.SSL_INFO_CIPHER);
+//                    if (sslO != null) {
+//                        request.setAttribute
+//                            (NioEndpoint.CIPHER_SUITE_KEY, sslO);
+//                    }
+//                    // Client certificate chain if present
+//                    int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
+//                    X509Certificate[] certs = null;
+//                    if (certLength > 0) {
+//                        certs = new X509Certificate[certLength];
+//                        for (int i = 0; i < certLength; i++) {
+//                            byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
+//                            CertificateFactory cf =
+//                                CertificateFactory.getInstance("X.509");
+//                            ByteArrayInputStream stream = new ByteArrayInputStream(data);
+//                            certs[i] = (X509Certificate) cf.generateCertificate(stream);
+//                        }
+//                    }
+//                    if (certs != null) {
+//                        request.setAttribute
+//                            (NioEndpoint.CERTIFICATE_KEY, certs);
+//                    }
+//                    // User key size
+//                    sslO = new Integer(SSLSocket.getInfoI(socket, SSL.SSL_INFO_CIPHER_USEKEYSIZE));
+//                    if (sslO != null) {
+//                        request.setAttribute
+//                            (NioEndpoint.KEY_SIZE_KEY, sslO);
+//                    }
+//                    // SSL session ID
+//                    sslO = SSLSocket.getInfoS(socket, SSL.SSL_INFO_SESSION_ID);
+//                    if (sslO != null) {
+//                        request.setAttribute
+//                            (NioEndpoint.SESSION_ID_KEY, sslO);
+//                    }
+//                } catch (Exception e) {
+//                    log.warn(sm.getString("http11processor.socket.ssl"), e);
+//                }
+//            }
+
+        } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
+
+//            if (ssl && (socket != 0)) {
+//                 // Consume and buffer the request body, so that it does not
+//                 // interfere with the client's handshake messages
+//                InputFilter[] inputFilters = inputBuffer.getFilters();
+//                ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER])
+//                    .setLimit(maxSavePostSize);
+//                inputBuffer.addActiveFilter
+//                    (inputFilters[Constants.BUFFERED_FILTER]);
+//                try {
+//                    // Renegociate certificates
+//                    SSLSocket.renegotiate(socket);
+//                    // Client certificate chain if present
+//                    int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
+//                    X509Certificate[] certs = null;
+//                    if (certLength > 0) {
+//                        certs = new X509Certificate[certLength];
+//                        for (int i = 0; i < certLength; i++) {
+//                            byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
+//                            CertificateFactory cf =
+//                                CertificateFactory.getInstance("X.509");
+//                            ByteArrayInputStream stream = new ByteArrayInputStream(data);
+//                            certs[i] = (X509Certificate) cf.generateCertificate(stream);
+//                        }
+//                    }
+//                    if (certs != null) {
+//                        request.setAttribute
+//                            (NioEndpoint.CERTIFICATE_KEY, certs);
+//                    }
+//                } catch (Exception e) {
+//                    log.warn(sm.getString("http11processor.socket.ssl"), e);
+//                }
+//            }
+
+        } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
+            ByteChunk body = (ByteChunk) param;
+
+            InputFilter savedBody = new SavedRequestInputFilter(body);
+            savedBody.setRequest(request);
+
+            InternalNioInputBuffer internalBuffer = (InternalNioInputBuffer)
+                request.getInputBuffer();
+            internalBuffer.addActiveFilter(savedBody);
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Connector Methods
+
+
+    /**
+     * Set the associated adapter.
+     *
+     * @param adapter the new adapter
+     */
+    public void setAdapter(Adapter adapter) {
+        this.adapter = adapter;
+    }
+
+
+    /**
+     * Get the associated adapter.
+     *
+     * @return the associated adapter
+     */
+    public Adapter getAdapter() {
+        return adapter;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * After reading the request headers, we have to setup the request filters.
+     */
+    protected void prepareRequest() {
+
+        http11 = true;
+        http09 = false;
+        contentDelimitation = false;
+        expectation = false;
+        sendfileData = null;
+        if (ssl) {
+            request.scheme().setString("https");
+        }
+        MessageBytes protocolMB = request.protocol();
+        if (protocolMB.equals(Constants.HTTP_11)) {
+            http11 = true;
+            protocolMB.setString(Constants.HTTP_11);
+        } else if (protocolMB.equals(Constants.HTTP_10)) {
+            http11 = false;
+            keepAlive = false;
+            protocolMB.setString(Constants.HTTP_10);
+        } else if (protocolMB.equals("")) {
+            // HTTP/0.9
+            http09 = true;
+            http11 = false;
+            keepAlive = false;
+        } else {
+            // Unsupported protocol
+            http11 = false;
+            error = true;
+            // Send 505; Unsupported HTTP version
+            response.setStatus(505);
+        }
+
+        MessageBytes methodMB = request.method();
+        if (methodMB.equals(Constants.GET)) {
+            methodMB.setString(Constants.GET);
+        } else if (methodMB.equals(Constants.POST)) {
+            methodMB.setString(Constants.POST);
+        }
+
+        MimeHeaders headers = request.getMimeHeaders();
+
+        // Check connection header
+        MessageBytes connectionValueMB = headers.getValue("connection");
+        if (connectionValueMB != null) {
+            ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
+            if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
+                keepAlive = false;
+            } else if (findBytes(connectionValueBC,
+                                 Constants.KEEPALIVE_BYTES) != -1) {
+                keepAlive = true;
+            }
+        }
+
+        MessageBytes expectMB = null;
+        if (http11)
+            expectMB = headers.getValue("expect");
+        if ((expectMB != null)
+            && (expectMB.indexOfIgnoreCase("100-continue", 0) != -1)) {
+            inputBuffer.setSwallowInput(false);
+            expectation = true;
+        }
+
+        // Check user-agent header
+        if ((restrictedUserAgents != null) && ((http11) || (keepAlive))) {
+            MessageBytes userAgentValueMB = headers.getValue("user-agent");
+            // Check in the restricted list, and adjust the http11
+            // and keepAlive flags accordingly
+            if(userAgentValueMB != null) {
+                String userAgentValue = userAgentValueMB.toString();
+                for (int i = 0; i < restrictedUserAgents.length; i++) {
+                    if (restrictedUserAgents[i].matcher(userAgentValue).matches()) {
+                        http11 = false;
+                        keepAlive = false;
+                        break;
+                    }
+                }
+            }
+        }
+
+        // Check for a full URI (including protocol://host:port/)
+        ByteChunk uriBC = request.requestURI().getByteChunk();
+        if (uriBC.startsWithIgnoreCase("http", 0)) {
+
+            int pos = uriBC.indexOf("://", 0, 3, 4);
+            int uriBCStart = uriBC.getStart();
+            int slashPos = -1;
+            if (pos != -1) {
+                byte[] uriB = uriBC.getBytes();
+                slashPos = uriBC.indexOf('/', pos + 3);
+                if (slashPos == -1) {
+                    slashPos = uriBC.getLength();
+                    // Set URI as "/"
+                    request.requestURI().setBytes
+                        (uriB, uriBCStart + pos + 1, 1);
+                } else {
+                    request.requestURI().setBytes
+                        (uriB, uriBCStart + slashPos,
+                         uriBC.getLength() - slashPos);
+                }
+                MessageBytes hostMB = headers.setValue("host");
+                hostMB.setBytes(uriB, uriBCStart + pos + 3,
+                                slashPos - pos - 3);
+            }
+
+        }
+
+        // Input filter setup
+        InputFilter[] inputFilters = inputBuffer.getFilters();
+
+        // Parse transfer-encoding header
+        MessageBytes transferEncodingValueMB = null;
+        if (http11)
+            transferEncodingValueMB = headers.getValue("transfer-encoding");
+        if (transferEncodingValueMB != null) {
+            String transferEncodingValue = transferEncodingValueMB.toString();
+            // Parse the comma separated list. "identity" codings are ignored
+            int startPos = 0;
+            int commaPos = transferEncodingValue.indexOf(',');
+            String encodingName = null;
+            while (commaPos != -1) {
+                encodingName = transferEncodingValue.substring
+                    (startPos, commaPos).toLowerCase().trim();
+                if (!addInputFilter(inputFilters, encodingName)) {
+                    // Unsupported transfer encoding
+                    error = true;
+                    // 501 - Unimplemented
+                    response.setStatus(501);
+                }
+                startPos = commaPos + 1;
+                commaPos = transferEncodingValue.indexOf(',', startPos);
+            }
+            encodingName = transferEncodingValue.substring(startPos)
+                .toLowerCase().trim();
+            if (!addInputFilter(inputFilters, encodingName)) {
+                // Unsupported transfer encoding
+                error = true;
+                // 501 - Unimplemented
+                response.setStatus(501);
+            }
+        }
+
+        // Parse content-length header
+        long contentLength = request.getContentLengthLong();
+        if (contentLength >= 0 && !contentDelimitation) {
+            inputBuffer.addActiveFilter
+                (inputFilters[Constants.IDENTITY_FILTER]);
+            contentDelimitation = true;
+        }
+
+        MessageBytes valueMB = headers.getValue("host");
+
+        // Check host header
+        if (http11 && (valueMB == null)) {
+            error = true;
+            // 400 - Bad request
+            response.setStatus(400);
+        }
+
+        parseHost(valueMB);
+
+        if (!contentDelimitation) {
+            // If there's no content length 
+            // (broken HTTP/1.0 or HTTP/1.1), assume
+            // the client is not broken and didn't send a body
+            inputBuffer.addActiveFilter
+                    (inputFilters[Constants.VOID_FILTER]);
+            contentDelimitation = true;
+        }
+
+        // Advertise sendfile support through a request attribute
+        if (endpoint.getUseSendfile()) {
+            request.setAttribute("org.apache.tomcat.sendfile.support", Boolean.FALSE);
+        }
+        // Advertise comet support through a request attribute
+        request.setAttribute("org.apache.tomcat.comet.support", Boolean.TRUE);
+
+    }
+
+
+    /**
+     * Parse host.
+     */
+    public void parseHost(MessageBytes valueMB) {
+
+        if (valueMB == null || valueMB.isNull()) {
+            // HTTP/1.0
+            // Default is what the socket tells us. Overriden if a host is
+            // found/parsed
+            request.setServerPort(endpoint.getPort());
+            return;
+        }
+
+        ByteChunk valueBC = valueMB.getByteChunk();
+        byte[] valueB = valueBC.getBytes();
+        int valueL = valueBC.getLength();
+        int valueS = valueBC.getStart();
+        int colonPos = -1;
+        if (hostNameC.length < valueL) {
+            hostNameC = new char[valueL];
+        }
+
+        boolean ipv6 = (valueB[valueS] == '[');
+        boolean bracketClosed = false;
+        for (int i = 0; i < valueL; i++) {
+            char b = (char) valueB[i + valueS];
+            hostNameC[i] = b;
+            if (b == ']') {
+                bracketClosed = true;
+            } else if (b == ':') {
+                if (!ipv6 || bracketClosed) {
+                    colonPos = i;
+                    break;
+                }
+            }
+        }
+
+        if (colonPos < 0) {
+            if (!ssl) {
+                // 80 - Default HTTP port
+                request.setServerPort(80);
+            } else {
+                // 443 - Default HTTPS port
+                request.setServerPort(443);
+            }
+            request.serverName().setChars(hostNameC, 0, valueL);
+        } else {
+
+            request.serverName().setChars(hostNameC, 0, colonPos);
+
+            int port = 0;
+            int mult = 1;
+            for (int i = valueL - 1; i > colonPos; i--) {
+                int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
+                if (charValue == -1) {
+                    // Invalid character
+                    error = true;
+                    // 400 - Bad request
+                    response.setStatus(400);
+                    break;
+                }
+                port = port + (charValue * mult);
+                mult = 10 * mult;
+            }
+            request.setServerPort(port);
+
+        }
+
+    }
+
+
+    /**
+     * Check for compression
+     */
+    private boolean isCompressable() {
+
+        // Nope Compression could works in HTTP 1.0 also
+        // cf: mod_deflate
+
+        // Compression only since HTTP 1.1
+        // if (! http11)
+        //    return false;
+
+        // Check if browser support gzip encoding
+        MessageBytes acceptEncodingMB =
+            request.getMimeHeaders().getValue("accept-encoding");
+
+        if ((acceptEncodingMB == null)
+            || (acceptEncodingMB.indexOf("gzip") == -1))
+            return false;
+
+        // Check if content is not allready gzipped
+        MessageBytes contentEncodingMB =
+            response.getMimeHeaders().getValue("Content-Encoding");
+
+        if ((contentEncodingMB != null)
+            && (contentEncodingMB.indexOf("gzip") != -1))
+            return false;
+
+        // If force mode, allways compress (test purposes only)
+        if (compressionLevel == 2)
+           return true;
+
+        // Check for incompatible Browser
+        if (noCompressionUserAgents != null) {
+            MessageBytes userAgentValueMB =
+                request.getMimeHeaders().getValue("user-agent");
+            if(userAgentValueMB != null) {
+                String userAgentValue = userAgentValueMB.toString();
+
+                // If one Regexp rule match, disable compression
+                for (int i = 0; i < noCompressionUserAgents.length; i++)
+                    if (noCompressionUserAgents[i].matcher(userAgentValue).matches())
+                        return false;
+            }
+        }
+
+        // Check if suffisant len to trig the compression
+        long contentLength = response.getContentLengthLong();
+        if ((contentLength == -1)
+            || (contentLength > compressionMinSize)) {
+            // Check for compatible MIME-TYPE
+            if (compressableMimeTypes != null) {
+                return (startsWithStringArray(compressableMimeTypes,
+                                              response.getContentType()));
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * When committing the response, we have to validate the set of headers, as
+     * well as setup the response filters.
+     */
+    protected void prepareResponse() {
+
+        boolean entityBody = true;
+        contentDelimitation = false;
+
+        OutputFilter[] outputFilters = outputBuffer.getFilters();
+
+        if (http09 == true) {
+            // HTTP/0.9
+            outputBuffer.addActiveFilter
+                (outputFilters[Constants.IDENTITY_FILTER]);
+            return;
+        }
+
+        int statusCode = response.getStatus();
+        if ((statusCode == 204) || (statusCode == 205)
+            || (statusCode == 304)) {
+            // No entity body
+            outputBuffer.addActiveFilter
+                (outputFilters[Constants.VOID_FILTER]);
+            entityBody = false;
+            contentDelimitation = true;
+        }
+
+        MessageBytes methodMB = request.method();
+        if (methodMB.equals("HEAD")) {
+            // No entity body
+            outputBuffer.addActiveFilter
+                (outputFilters[Constants.VOID_FILTER]);
+            contentDelimitation = true;
+        }
+
+        // Sendfile support
+        if (endpoint.getUseSendfile()) {
+            String fileName = (String) request.getAttribute("org.apache.tomcat.sendfile.filename");
+            if (fileName != null) {
+                // No entity body sent here
+                outputBuffer.addActiveFilter
+                    (outputFilters[Constants.VOID_FILTER]);
+                contentDelimitation = true;
+                sendfileData = new NioEndpoint.SendfileData();
+                sendfileData.fileName = fileName;
+                sendfileData.start = 
+                    ((Long) request.getAttribute("org.apache.tomcat.sendfile.start")).longValue();
+                sendfileData.end = 
+                    ((Long) request.getAttribute("org.apache.tomcat.sendfile.end")).longValue();
+            }
+        }
+
+        // Check for compression
+        boolean useCompression = false;
+        if (entityBody && (compressionLevel > 0) && (sendfileData == null)) {
+            useCompression = isCompressable();
+            // Change content-length to -1 to force chunking
+            if (useCompression) {
+                response.setContentLength(-1);
+            }
+        }
+
+        MimeHeaders headers = response.getMimeHeaders();
+        if (!entityBody) {
+            response.setContentLength(-1);
+        } else {
+            String contentType = response.getContentType();
+            if (contentType != null) {
+                headers.setValue("Content-Type").setString(contentType);
+            }
+            String contentLanguage = response.getContentLanguage();
+            if (contentLanguage != null) {
+                headers.setValue("Content-Language")
+                    .setString(contentLanguage);
+            }
+        }
+
+        long contentLength = response.getContentLengthLong();
+        if (contentLength != -1) {
+            headers.setValue("Content-Length").setLong(contentLength);
+            outputBuffer.addActiveFilter
+                (outputFilters[Constants.IDENTITY_FILTER]);
+            contentDelimitation = true;
+        } else {
+            if (entityBody && http11 && keepAlive) {
+                outputBuffer.addActiveFilter
+                    (outputFilters[Constants.CHUNKED_FILTER]);
+                contentDelimitation = true;
+                headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
+            } else {
+                outputBuffer.addActiveFilter
+                    (outputFilters[Constants.IDENTITY_FILTER]);
+            }
+        }
+
+        if (useCompression) {
+            outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
+            headers.setValue("Content-Encoding").setString("gzip");
+            // Make Proxies happy via Vary (from mod_deflate)
+            headers.setValue("Vary").setString("Accept-Encoding");
+        }
+
+        // Add date header
+        headers.setValue("Date").setString(FastHttpDateFormat.getCurrentDate());
+
+        // FIXME: Add transfer encoding header
+
+        if ((entityBody) && (!contentDelimitation)) {
+            // Mark as close the connection after the request, and add the
+            // connection: close header
+            keepAlive = false;
+        }
+
+        // If we know that the request is bad this early, add the
+        // Connection: close header.
+        keepAlive = keepAlive && !statusDropsConnection(statusCode);
+        if (!keepAlive) {
+            headers.addValue(Constants.CONNECTION).setString(Constants.CLOSE);
+        } else if (!http11 && !error) {
+            headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);
+        }
+
+        // Build the response header
+        outputBuffer.sendStatus();
+
+        // Add server header
+        if (server != null) {
+            headers.setValue("Server").setString(server);
+        } else {
+            outputBuffer.write(Constants.SERVER_BYTES);
+        }
+
+        int size = headers.size();
+        for (int i = 0; i < size; i++) {
+            outputBuffer.sendHeader(headers.getName(i), headers.getValue(i));
+        }
+        outputBuffer.endHeaders();
+
+    }
+
+
+    /**
+     * Initialize standard input and output filters.
+     */
+    protected void initializeFilters() {
+
+        // Create and add the identity filters.
+        inputBuffer.addFilter(new IdentityInputFilter());
+        outputBuffer.addFilter(new IdentityOutputFilter());
+
+        // Create and add the chunked filters.
+        inputBuffer.addFilter(new ChunkedInputFilter());
+        outputBuffer.addFilter(new ChunkedOutputFilter());
+
+        // Create and add the void filters.
+        inputBuffer.addFilter(new VoidInputFilter());
+        outputBuffer.addFilter(new VoidOutputFilter());
+
+        // Create and add buffered input filter
+        inputBuffer.addFilter(new BufferedInputFilter());
+
+        // Create and add the chunked filters.
+        //inputBuffer.addFilter(new GzipInputFilter());
+        outputBuffer.addFilter(new GzipOutputFilter());
+
+    }
+
+
+    /**
+     * Add an input filter to the current request.
+     *
+     * @return false if the encoding was not found (which would mean it is
+     * unsupported)
+     */
+    protected boolean addInputFilter(InputFilter[] inputFilters,
+                                     String encodingName) {
+        if (encodingName.equals("identity")) {
+            // Skip
+        } else if (encodingName.equals("chunked")) {
+            inputBuffer.addActiveFilter
+                (inputFilters[Constants.CHUNKED_FILTER]);
+            contentDelimitation = true;
+        } else {
+            for (int i = 2; i < inputFilters.length; i++) {
+                if (inputFilters[i].getEncodingName()
+                    .toString().equals(encodingName)) {
+                    inputBuffer.addActiveFilter(inputFilters[i]);
+                    return true;
+                }
+            }
+            return false;
+        }
+        return true;
+    }
+
+
+    /**
+     * Specialized utility method: find a sequence of lower case bytes inside
+     * a ByteChunk.
+     */
+    protected int findBytes(ByteChunk bc, byte[] b) {
+
+        byte first = b[0];
+        byte[] buff = bc.getBuffer();
+        int start = bc.getStart();
+        int end = bc.getEnd();
+
+    // Look for first char
+    int srcEnd = b.length;
+
+    for (int i = start; i <= (end - srcEnd); i++) {
+        if (Ascii.toLower(buff[i]) != first) continue;
+        // found first char, now look for a match
+            int myPos = i+1;
+        for (int srcPos = 1; srcPos < srcEnd; ) {
+                if (Ascii.toLower(buff[myPos++]) != b[srcPos++])
+            break;
+                if (srcPos == srcEnd) return i - start; // found it
+        }
+    }
+    return -1;
+
+    }
+
+    /**
+     * Determine if we must drop the connection because of the HTTP status
+     * code.  Use the same list of codes as Apache/httpd.
+     */
+    protected boolean statusDropsConnection(int status) {
+        return status == 400 /* SC_BAD_REQUEST */ ||
+               status == 408 /* SC_REQUEST_TIMEOUT */ ||
+               status == 411 /* SC_LENGTH_REQUIRED */ ||
+               status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||
+               status == 414 /* SC_REQUEST_URI_TOO_LARGE */ ||
+               status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||
+               status == 503 /* SC_SERVICE_UNAVAILABLE */ ||
+               status == 501 /* SC_NOT_IMPLEMENTED */;
+    }
+
+}

Added: tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java
URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java?rev=416187&view=auto
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java (added)
+++ tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java Wed Jun 21 17:48:53 2006
@@ -0,0 +1,775 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11;
+
+import java.net.InetAddress;
+import java.net.URLEncoder;
+import java.nio.channels.SocketChannel;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.ActionHook;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.ProtocolHandler;
+import org.apache.coyote.RequestGroupInfo;
+import org.apache.coyote.RequestInfo;
+import org.apache.tomcat.util.modeler.Registry;
+import org.apache.tomcat.util.net.NioEndpoint;
+import org.apache.tomcat.util.net.NioEndpoint.Handler;
+import org.apache.tomcat.util.res.StringManager;
+
+
+/**
+ * Abstract the protocol implementation, including threading, etc.
+ * Processor is single threaded and specific to stream-based protocols,
+ * will not fit Jk protocols like JNI.
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ * @author Filip Hanik
+ */
+public class Http11NioProtocol implements ProtocolHandler, MBeanRegistration
+{
+    public Http11NioProtocol() {
+        cHandler = new Http11ConnectionHandler( this );
+        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
+        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
+        //setServerSoTimeout(Constants.DEFAULT_SERVER_SOCKET_TIMEOUT);
+        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
+    }
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+    /** Pass config info
+     */
+    public void setAttribute( String name, Object value ) {
+        if( log.isTraceEnabled())
+            log.trace(sm.getString("http11protocol.setattribute", name, value));
+
+        attributes.put(name, value);
+    }
+
+    public Object getAttribute( String key ) {
+        if( log.isTraceEnabled())
+            log.trace(sm.getString("http11protocol.getattribute", key));
+        return attributes.get(key);
+    }
+
+    public Iterator getAttributeNames() {
+        return attributes.keySet().iterator();
+    }
+
+    /**
+     * Set a property.
+     */
+    public void setProperty(String name, String value) {
+        setAttribute(name, value);
+    }
+
+    /**
+     * Get a property
+     */
+    public String getProperty(String name) {
+        return (String)getAttribute(name);
+    }
+
+    /** The adapter, used to call the connector
+     */
+    public void setAdapter(Adapter adapter) {
+        this.adapter=adapter;
+    }
+
+    public Adapter getAdapter() {
+        return adapter;
+    }
+
+
+    /** Start the protocol
+     */
+    public void init() throws Exception {
+        ep.setName(getName());
+        ep.setHandler(cHandler);
+
+        try {
+            ep.init();
+        } catch (Exception ex) {
+            log.error(sm.getString("http11protocol.endpoint.initerror"), ex);
+            throw ex;
+        }
+        if(log.isInfoEnabled())
+            log.info(sm.getString("http11protocol.init", getName()));
+
+    }
+
+    ObjectName tpOname;
+    ObjectName rgOname;
+
+    public void start() throws Exception {
+        if( this.domain != null ) {
+            try {
+                tpOname=new ObjectName
+                    (domain + ":" + "type=ThreadPool,name=" + getName());
+                Registry.getRegistry(null, null)
+                .registerComponent(ep, tpOname, null );
+            } catch (Exception e) {
+                log.error("Can't register threadpool" );
+            }
+            rgOname=new ObjectName
+                (domain + ":type=GlobalRequestProcessor,name=" + getName());
+            Registry.getRegistry(null, null).registerComponent
+                ( cHandler.global, rgOname, null );
+        }
+
+        try {
+            ep.start();
+        } catch (Exception ex) {
+            log.error(sm.getString("http11protocol.endpoint.starterror"), ex);
+            throw ex;
+        }
+        if(log.isInfoEnabled())
+            log.info(sm.getString("http11protocol.start", getName()));
+    }
+
+    public void pause() throws Exception {
+        try {
+            ep.pause();
+        } catch (Exception ex) {
+            log.error(sm.getString("http11protocol.endpoint.pauseerror"), ex);
+            throw ex;
+        }
+        if(log.isInfoEnabled())
+            log.info(sm.getString("http11protocol.pause", getName()));
+    }
+
+    public void resume() throws Exception {
+        try {
+            ep.resume();
+        } catch (Exception ex) {
+            log.error(sm.getString("http11protocol.endpoint.resumeerror"), ex);
+            throw ex;
+        }
+        if(log.isInfoEnabled())
+            log.info(sm.getString("http11protocol.resume", getName()));
+    }
+
+    public void destroy() throws Exception {
+        if(log.isInfoEnabled())
+            log.info(sm.getString("http11protocol.stop", getName()));
+        ep.destroy();
+        if( tpOname!=null )
+            Registry.getRegistry(null, null).unregisterComponent(tpOname);
+        if( rgOname != null )
+            Registry.getRegistry(null, null).unregisterComponent(rgOname);
+    }
+
+    // -------------------- Properties--------------------
+    protected NioEndpoint ep=new NioEndpoint();
+    protected boolean secure;
+
+    protected Hashtable attributes = new Hashtable();
+
+    private int maxKeepAliveRequests=100; // as in Apache HTTPD server
+    private int timeout = 300000;   // 5 minutes as in Apache HTTPD server
+    private int maxSavePostSize = 4 * 1024;
+    private int maxHttpHeaderSize = 8 * 1024;
+    private int socketCloseDelay=-1;
+    private boolean disableUploadTimeout = true;
+    private int socketBuffer = 9000;
+    private Adapter adapter;
+    private Http11ConnectionHandler cHandler;
+
+    /**
+     * Compression value.
+     */
+    private String compression = "off";
+    private String noCompressionUserAgents = null;
+    private String restrictedUserAgents = null;
+    private String compressableMimeTypes = "text/html,text/xml,text/plain";
+    private int compressionMinSize    = 2048;
+
+    private String server;
+
+    // -------------------- Pool setup --------------------
+
+    // *
+    public Executor getExecutor() {
+        return ep.getExecutor();
+    }
+
+    // *
+    public void setExecutor(Executor executor) {
+        ep.setExecutor(executor);
+    }
+
+    public int getMaxThreads() {
+        return ep.getMaxThreads();
+    }
+
+    public void setMaxThreads( int maxThreads ) {
+        ep.setMaxThreads(maxThreads);
+        setAttribute("maxThreads", "" + maxThreads);
+    }
+
+    public void setThreadPriority(int threadPriority) {
+      ep.setThreadPriority(threadPriority);
+      setAttribute("threadPriority", "" + threadPriority);
+    }
+
+    public int getThreadPriority() {
+      return ep.getThreadPriority();
+    }
+
+    // -------------------- Tcp setup --------------------
+
+    public int getBacklog() {
+        return ep.getBacklog();
+    }
+
+    public void setBacklog( int i ) {
+        ep.setBacklog(i);
+        setAttribute("backlog", "" + i);
+    }
+
+    public int getPort() {
+        return ep.getPort();
+    }
+
+    public void setPort( int port ) {
+        ep.setPort(port);
+        setAttribute("port", "" + port);
+    }
+
+    public int getFirstReadTimeout() {
+        return ep.getFirstReadTimeout();
+    }
+
+    public void setFirstReadTimeout( int i ) {
+        ep.setFirstReadTimeout(i);
+        setAttribute("firstReadTimeout", "" + i);
+    }
+
+    public int getPollTime() {
+        return ep.getPollTime();
+    }
+
+    public void setPollTime( int i ) {
+        ep.setPollTime(i);
+        setAttribute("pollTime", "" + i);
+    }
+
+    public void setPollerSize(int i) {
+        ep.setPollerSize(i); 
+        setAttribute("pollerSize", "" + i);
+    }
+
+    public int getPollerSize() {
+        return ep.getPollerSize();
+    }
+
+    public void setSendfileSize(int i) {
+        ep.setSendfileSize(i); 
+        setAttribute("sendfileSize", "" + i);
+    }
+
+    public int getSendfileSize() {
+        return ep.getSendfileSize();
+    }
+
+    public boolean getUseSendfile() {
+        return ep.getUseSendfile();
+    }
+
+    public void setUseSendfile(boolean useSendfile) {
+        ep.setUseSendfile(useSendfile);
+    }
+
+    public InetAddress getAddress() {
+        return ep.getAddress();
+    }
+
+    public void setAddress(InetAddress ia) {
+        ep.setAddress( ia );
+        setAttribute("address", "" + ia);
+    }
+
+    public String getName() {
+        String encodedAddr = "";
+        if (getAddress() != null) {
+            encodedAddr = "" + getAddress();
+            if (encodedAddr.startsWith("/"))
+                encodedAddr = encodedAddr.substring(1);
+            encodedAddr = URLEncoder.encode(encodedAddr) + "-";
+        }
+        return ("http-" + encodedAddr + ep.getPort());
+    }
+
+    public boolean getTcpNoDelay() {
+        return ep.getTcpNoDelay();
+    }
+
+    public void setTcpNoDelay( boolean b ) {
+        ep.setTcpNoDelay( b );
+        setAttribute("tcpNoDelay", "" + b);
+    }
+
+    public boolean getDisableUploadTimeout() {
+        return disableUploadTimeout;
+    }
+
+    public void setDisableUploadTimeout(boolean isDisabled) {
+        disableUploadTimeout = isDisabled;
+    }
+
+    public int getSocketBuffer() {
+        return socketBuffer;
+    }
+
+    public void setSocketBuffer(int valueI) {
+        socketBuffer = valueI;
+    }
+
+    public String getCompression() {
+        return compression;
+    }
+
+    public void setCompression(String valueS) {
+        compression = valueS;
+        setAttribute("compression", valueS);
+    }
+
+    public int getMaxSavePostSize() {
+        return maxSavePostSize;
+    }
+
+    public void setMaxSavePostSize(int valueI) {
+        maxSavePostSize = valueI;
+        setAttribute("maxSavePostSize", "" + valueI);
+    }
+
+    public int getMaxHttpHeaderSize() {
+        return maxHttpHeaderSize;
+    }
+
+    public void setMaxHttpHeaderSize(int valueI) {
+        maxHttpHeaderSize = valueI;
+        setAttribute("maxHttpHeaderSize", "" + valueI);
+    }
+
+    public String getRestrictedUserAgents() {
+        return restrictedUserAgents;
+    }
+
+    public void setRestrictedUserAgents(String valueS) {
+        restrictedUserAgents = valueS;
+        setAttribute("restrictedUserAgents", valueS);
+    }
+
+    public String getNoCompressionUserAgents() {
+        return noCompressionUserAgents;
+    }
+
+    public void setNoCompressionUserAgents(String valueS) {
+        noCompressionUserAgents = valueS;
+        setAttribute("noCompressionUserAgents", valueS);
+    }
+
+    public String getCompressableMimeType() {
+        return compressableMimeTypes;
+    }
+
+    public void setCompressableMimeType(String valueS) {
+        compressableMimeTypes = valueS;
+        setAttribute("compressableMimeTypes", valueS);
+    }
+
+    public int getCompressionMinSize() {
+        return compressionMinSize;
+    }
+
+    public void setCompressionMinSize(int valueI) {
+        compressionMinSize = valueI;
+        setAttribute("compressionMinSize", "" + valueI);
+    }
+
+    public int getSoLinger() {
+        return ep.getSoLinger();
+    }
+
+    public void setSoLinger( int i ) {
+        ep.setSoLinger( i );
+        setAttribute("soLinger", "" + i);
+    }
+
+    public int getSoTimeout() {
+        return ep.getSoTimeout();
+    }
+
+    public void setSoTimeout( int i ) {
+        ep.setSoTimeout(i);
+        setAttribute("soTimeout", "" + i);
+    }
+
+    public String getProtocol() {
+        return getProperty("protocol");
+    }
+
+    public void setProtocol( String k ) {
+        setSecure(true);
+        setAttribute("protocol", k);
+    }
+
+    public boolean getSecure() {
+        return secure;
+    }
+
+    public void setSecure( boolean b ) {
+        secure=b;
+        setAttribute("secure", "" + b);
+    }
+
+    public int getMaxKeepAliveRequests() {
+        return maxKeepAliveRequests;
+    }
+
+    /** Set the maximum number of Keep-Alive requests that we will honor.
+     */
+    public void setMaxKeepAliveRequests(int mkar) {
+        maxKeepAliveRequests = mkar;
+        setAttribute("maxKeepAliveRequests", "" + mkar);
+    }
+
+    /**
+     * Return the Keep-Alive policy for the connection.
+     */
+    public boolean getKeepAlive() {
+        return ((maxKeepAliveRequests != 0) && (maxKeepAliveRequests != 1));
+    }
+
+    /**
+     * Set the keep-alive policy for this connection.
+     */
+    public void setKeepAlive(boolean keepAlive) {
+        if (!keepAlive) {
+            setMaxKeepAliveRequests(1);
+        }
+    }
+
+    public int getSocketCloseDelay() {
+        return socketCloseDelay;
+    }
+
+    public void setSocketCloseDelay( int d ) {
+        socketCloseDelay=d;
+        setAttribute("socketCloseDelay", "" + d);
+    }
+
+    public void setServer( String server ) {
+        this.server = server;
+    }
+
+    public String getServer() {
+        return server;
+    }
+
+    public int getTimeout() {
+        return timeout;
+    }
+
+    public void setTimeout( int timeouts ) {
+        timeout = timeouts;
+        setAttribute("timeout", "" + timeouts);
+    }
+
+    // --------------------  SSL related properties --------------------
+
+    /**
+     * SSL engine.
+     */
+    public String getSSLEngine() { return ep.getSSLEngine(); }
+    public void setSSLEngine(String SSLEngine) { ep.setSSLEngine(SSLEngine); }
+
+
+    /**
+     * SSL protocol.
+     */
+    public String getSSLProtocol() { return ep.getSSLProtocol(); }
+    public void setSSLProtocol(String SSLProtocol) { ep.setSSLProtocol(SSLProtocol); }
+
+
+    /**
+     * SSL password (if a cert is encrypted, and no password has been provided, a callback
+     * will ask for a password).
+     */
+    public String getSSLPassword() { return ep.getSSLPassword(); }
+    public void setSSLPassword(String SSLPassword) { ep.setSSLPassword(SSLPassword); }
+
+
+    /**
+     * SSL cipher suite.
+     */
+    public String getSSLCipherSuite() { return ep.getSSLCipherSuite(); }
+    public void setSSLCipherSuite(String SSLCipherSuite) { ep.setSSLCipherSuite(SSLCipherSuite); }
+
+
+    /**
+     * SSL certificate file.
+     */
+    public String getSSLCertificateFile() { return ep.getSSLCertificateFile(); }
+    public void setSSLCertificateFile(String SSLCertificateFile) { ep.setSSLCertificateFile(SSLCertificateFile); }
+
+
+    /**
+     * SSL certificate key file.
+     */
+    public String getSSLCertificateKeyFile() { return ep.getSSLCertificateKeyFile(); }
+    public void setSSLCertificateKeyFile(String SSLCertificateKeyFile) { ep.setSSLCertificateKeyFile(SSLCertificateKeyFile); }
+
+
+    /**
+     * SSL certificate chain file.
+     */
+    public String getSSLCertificateChainFile() { return ep.getSSLCertificateChainFile(); }
+    public void setSSLCertificateChainFile(String SSLCertificateChainFile) { ep.setSSLCertificateChainFile(SSLCertificateChainFile); }
+
+
+    /**
+     * SSL CA certificate path.
+     */
+    public String getSSLCACertificatePath() { return ep.getSSLCACertificatePath(); }
+    public void setSSLCACertificatePath(String SSLCACertificatePath) { ep.setSSLCACertificatePath(SSLCACertificatePath); }
+
+
+    /**
+     * SSL CA certificate file.
+     */
+    public String getSSLCACertificateFile() { return ep.getSSLCACertificateFile(); }
+    public void setSSLCACertificateFile(String SSLCACertificateFile) { ep.setSSLCACertificateFile(SSLCACertificateFile); }
+
+
+    /**
+     * SSL CA revocation path.
+     */
+    public String getSSLCARevocationPath() { return ep.getSSLCARevocationPath(); }
+    public void setSSLCARevocationPath(String SSLCARevocationPath) { ep.setSSLCARevocationPath(SSLCARevocationPath); }
+
+
+    /**
+     * SSL CA revocation file.
+     */
+    public String getSSLCARevocationFile() { return ep.getSSLCARevocationFile(); }
+    public void setSSLCARevocationFile(String SSLCARevocationFile) { ep.setSSLCARevocationFile(SSLCARevocationFile); }
+
+
+    /**
+     * SSL verify client.
+     */
+    public String getSSLVerifyClient() { return ep.getSSLVerifyClient(); }
+    public void setSSLVerifyClient(String SSLVerifyClient) { ep.setSSLVerifyClient(SSLVerifyClient); }
+
+
+    /**
+     * SSL verify depth.
+     */
+    public int getSSLVerifyDepth() { return ep.getSSLVerifyDepth(); }
+    public void setSSLVerifyDepth(int SSLVerifyDepth) { ep.setSSLVerifyDepth(SSLVerifyDepth); }
+
+    // --------------------  Connection handler --------------------
+
+    static class Http11ConnectionHandler implements Handler {
+
+        protected Http11NioProtocol proto;
+        protected static int count = 0;
+        protected RequestGroupInfo global = new RequestGroupInfo();
+
+        protected ThreadLocal<Http11NioProcessor> localProcessor = 
+            new ThreadLocal<Http11NioProcessor>();
+        protected ConcurrentHashMap<SocketChannel, Http11NioProcessor> connections =
+            new ConcurrentHashMap<SocketChannel, Http11NioProcessor>();
+        protected java.util.Stack<Http11NioProcessor> recycledProcessors = 
+            new java.util.Stack<Http11NioProcessor>();
+
+        Http11ConnectionHandler(Http11NioProtocol proto) {
+            this.proto = proto;
+        }
+
+        public SocketState event(SocketChannel socket, boolean error) {
+            Http11NioProcessor result = connections.get(socket);
+
+            SocketState state = SocketState.CLOSED; 
+            if (result != null) {
+                boolean recycle = error;
+                // Call the appropriate event
+                try {
+                    state = result.event(error);
+                } catch (java.net.SocketException e) {
+                    // SocketExceptions are normal
+                    Http11NioProtocol.log.debug
+                        (sm.getString
+                            ("http11protocol.proto.socketexception.debug"), e);
+                } catch (java.io.IOException e) {
+                    // IOExceptions are normal
+                    Http11NioProtocol.log.debug
+                        (sm.getString
+                            ("http11protocol.proto.ioexception.debug"), e);
+                }
+                // Future developers: if you discover any other
+                // rare-but-nonfatal exceptions, catch them here, and log as
+                // above.
+                catch (Throwable e) {
+                    // any other exception or error is odd. Here we log it
+                    // with "ERROR" level, so it will show up even on
+                    // less-than-verbose logs.
+                    Http11NioProtocol.log.error
+                        (sm.getString("http11protocol.proto.error"), e);
+                } finally {
+                    if (state != SocketState.LONG) {
+                        connections.remove(socket);
+                        recycledProcessors.push(result);
+                    }
+                }
+            }
+            return state;
+        }
+
+        public SocketState process(SocketChannel socket) {
+            Http11NioProcessor processor = null;
+            try {
+                processor = (Http11NioProcessor) localProcessor.get();
+                if (processor == null) {
+                    synchronized (recycledProcessors) {
+                        if (!recycledProcessors.isEmpty()) {
+                            processor = recycledProcessors.pop();
+                            localProcessor.set(processor);
+                        }
+                    }
+                }
+                if (processor == null) {
+                    processor =
+                        new Http11NioProcessor(proto.maxHttpHeaderSize, proto.ep);
+                    processor.setAdapter(proto.adapter);
+                    processor.setMaxKeepAliveRequests(proto.maxKeepAliveRequests);
+                    processor.setTimeout(proto.timeout);
+                    processor.setDisableUploadTimeout(proto.disableUploadTimeout);
+                    processor.setCompression(proto.compression);
+                    processor.setCompressionMinSize(proto.compressionMinSize);
+                    processor.setNoCompressionUserAgents(proto.noCompressionUserAgents);
+                    processor.setCompressableMimeTypes(proto.compressableMimeTypes);
+                    processor.setRestrictedUserAgents(proto.restrictedUserAgents);
+                    processor.setSocketBuffer(proto.socketBuffer);
+                    processor.setMaxSavePostSize(proto.maxSavePostSize);
+                    processor.setServer(proto.server);
+                    localProcessor.set(processor);
+                    if (proto.getDomain() != null) {
+                        synchronized (this) {
+                            try {
+                                RequestInfo rp = processor.getRequest().getRequestProcessor();
+                                rp.setGlobalProcessor(global);
+                                ObjectName rpName = new ObjectName
+                                (proto.getDomain() + ":type=RequestProcessor,worker="
+                                        + proto.getName() + ",name=HttpRequest" + count++);
+                                Registry.getRegistry(null, null).registerComponent(rp, rpName, null);
+                            } catch (Exception e) {
+                                log.warn("Error registering request");
+                            }
+                        }
+                    }
+                }
+
+                if (processor instanceof ActionHook) {
+                    ((ActionHook) processor).action(ActionCode.ACTION_START, null);
+                }
+
+                SocketState state = processor.process(socket);
+                if (state == SocketState.LONG) {
+                    // Associate the connection with the processor. The next request 
+                    // processed by this thread will use either a new or a recycled
+                    // processor.
+                    connections.put(socket, processor);
+                    localProcessor.set(null);
+                    proto.ep.getCometPoller().add(socket);
+                }
+                return state;
+
+            } catch (java.net.SocketException e) {
+                // SocketExceptions are normal
+                Http11NioProtocol.log.debug
+                    (sm.getString
+                     ("http11protocol.proto.socketexception.debug"), e);
+            } catch (java.io.IOException e) {
+                // IOExceptions are normal
+                Http11NioProtocol.log.debug
+                    (sm.getString
+                     ("http11protocol.proto.ioexception.debug"), e);
+            }
+            // Future developers: if you discover any other
+            // rare-but-nonfatal exceptions, catch them here, and log as
+            // above.
+            catch (Throwable e) {
+                // any other exception or error is odd. Here we log it
+                // with "ERROR" level, so it will show up even on
+                // less-than-verbose logs.
+                Http11NioProtocol.log.error
+                    (sm.getString("http11protocol.proto.error"), e);
+            }
+            return SocketState.CLOSED;
+        }
+    }
+
+    protected static org.apache.commons.logging.Log log
+        = org.apache.commons.logging.LogFactory.getLog(Http11NioProtocol.class);
+
+    // -------------------- Various implementation classes --------------------
+
+    protected String domain;
+    protected ObjectName oname;
+    protected MBeanServer mserver;
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name) throws Exception {
+        oname=name;
+        mserver=server;
+        domain=name.getDomain();
+        return name;
+    }
+
+    public void postRegister(Boolean registrationDone) {
+    }
+
+    public void preDeregister() throws Exception {
+    }
+
+    public void postDeregister() {
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: svn commit: r416187 [1/3] - in /tomcat/tc6.0.x/trunk/java/org/apache: catalina/connector/ coyote/http11/ tomcat/util/net/

Posted by Remy Maucherat <re...@apache.org>.
Filip Hanik - Dev Lists wrote:
> I agree, would you mind if I redid the connector, so that the 
> protocolHandlerClassName works?
> currently, it only checks the protocol, as it does so in the constructor 
> of the Connector. so setting protocol or attributes later will have no 
> effect
> 
> public void begin(Attributes attributes) throws Exception {
>     digester.push(new Connector(attributes.getValue("protocol")));
> }

The protocol attribute can accept the classname as its value.

Rémy

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: svn commit: r416187 [1/3] - in /tomcat/tc6.0.x/trunk/java/org/apache: catalina/connector/ coyote/http11/ tomcat/util/net/

Posted by Filip Hanik - Dev Lists <de...@hanik.com>.
I agree, would you mind if I redid the connector, so that the 
protocolHandlerClassName works?
currently, it only checks the protocol, as it does so in the constructor 
of the Connector. so setting protocol or attributes later will have no 
effect

public void begin(Attributes attributes) throws Exception {
     digester.push(new Connector(attributes.getValue("protocol")));
}

Filip




Remy Maucherat wrote:
> fhanik@apache.org wrote:
>> -            if ("HTTP/1.1".equals(protocol)) {
>> +            if ("HTTP/1.1/NIO".equals(protocol)) {
>> +                
>> setProtocolHandlerClassName("org.apache.coyote.http11.Http11NioProtocol"); 
>>
>> +            } else if ("HTTP/1.1".equals(protocol)) {
>
> I think using the actual class name is far better than using arbitrary 
> protocol names.
>
> Rémy
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: dev-help@tomcat.apache.org
>
>


-- 


Filip Hanik

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: svn commit: r416187 [1/3] - in /tomcat/tc6.0.x/trunk/java/org/apache: catalina/connector/ coyote/http11/ tomcat/util/net/

Posted by Remy Maucherat <re...@apache.org>.
fhanik@apache.org wrote:
> -            if ("HTTP/1.1".equals(protocol)) {
> +            if ("HTTP/1.1/NIO".equals(protocol)) {
> +                setProtocolHandlerClassName("org.apache.coyote.http11.Http11NioProtocol");
> +            } else if ("HTTP/1.1".equals(protocol)) {

I think using the actual class name is far better than using arbitrary 
protocol names.

Rémy

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org