[jbossweb-commits] JBossWeb SVN: r2087 - in branches/7.2.x: src/main/java/org/apache/catalina/connector and 7 other directories.

jbossweb-commits at lists.jboss.org jbossweb-commits at lists.jboss.org
Wed Sep 26 09:54:12 EDT 2012


Author: remy.maucherat at jboss.com
Date: 2012-09-26 09:54:12 -0400 (Wed, 26 Sep 2012)
New Revision: 2087

Added:
   branches/7.2.x/src/main/java/org/apache/coyote/ajp/AjpProcessor.java
   branches/7.2.x/src/main/java/org/apache/coyote/ajp/AjpProtocol.java
   branches/7.2.x/src/main/java/org/apache/coyote/http11/Http11Processor.java
   branches/7.2.x/src/main/java/org/apache/coyote/http11/Http11Protocol.java
   branches/7.2.x/src/main/java/org/apache/coyote/http11/InternalInputBuffer.java
   branches/7.2.x/src/main/java/org/apache/coyote/http11/InternalOutputBuffer.java
   branches/7.2.x/src/main/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java
   branches/7.2.x/src/main/java/org/apache/tomcat/util/net/JIoEndpoint.java
   branches/7.2.x/src/main/java/org/apache/tomcat/util/net/ServerSocketFactory.java
   branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSEFactory.java
   branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
   branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java
   branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
Modified:
   branches/7.2.x/pom.xml
   branches/7.2.x/src/main/java/org/apache/catalina/connector/Connector.java
   branches/7.2.x/src/main/java/org/apache/catalina/util/ConcurrentMessageDigest.java
   branches/7.2.x/src/main/java/org/apache/coyote/ajp/AjpAprProtocol.java
   branches/7.2.x/src/main/java/org/apache/coyote/ajp/Constants.java
   branches/7.2.x/src/main/java/org/apache/coyote/http11/Http11NioProtocol.java
   branches/7.2.x/src/main/java/org/apache/tomcat/util/net/NioEndpoint.java
   branches/7.2.x/src/main/java/org/apache/tomcat/util/net/SSLImplementation.java
   branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/NioJSSESocketChannelFactory.java
   branches/7.2.x/src/main/java/org/jboss/web/CatalinaLogger.java
   branches/7.2.x/webapps/docs/changelog.xml
Log:
- Add back java.io AJP and HTTP, with i18n.
- Compile for Java 6.
- Try to run NIO 2 HTTP if possible.
- Done ?

Modified: branches/7.2.x/pom.xml
===================================================================
(Binary files differ)

Modified: branches/7.2.x/src/main/java/org/apache/catalina/connector/Connector.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/catalina/connector/Connector.java	2012-09-25 12:49:25 UTC (rev 2086)
+++ branches/7.2.x/src/main/java/org/apache/catalina/connector/Connector.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -549,6 +549,8 @@
         if ("org.apache.coyote.http11.Http11Protocol".equals
             (getProtocolHandlerClassName())
             || "org.apache.coyote.http11.Http11AprProtocol".equals
+            (getProtocolHandlerClassName())
+            || "org.apache.coyote.http11.Http11NioProtocol".equals
             (getProtocolHandlerClassName())) {
             return "HTTP/1.1";
         } else if ("org.apache.coyote.ajp.AjpProtocol".equals
@@ -583,8 +585,19 @@
             }
         } else {
             if ("HTTP/1.1".equals(protocol) || "http".equals(protocol)) {
+                try {
+                    Class.forName("java.nio.channels.CompletionHandler");
+                    setProtocolHandlerClassName
+                        ("org.apache.coyote.http11.Http11NioProtocol");
+                } catch (Exception e) {
+                    // NIO 2 is not available
+                    setProtocolHandlerClassName
+                        ("org.apache.coyote.http11.Http11Protocol");
+                    CatalinaLogger.CONNECTOR_LOGGER.usingJavaIoConnector();
+                }
+            } else if ("AJP/1.3".equals(protocol) || "ajp".equals(protocol)) {
                 setProtocolHandlerClassName
-                    ("org.apache.coyote.http11.Http11NioProtocol");
+                    ("org.apache.coyote.ajp.AjpProtocol");
             } else if (protocol != null) {
                 setProtocolHandlerClassName(protocol);
             }

Modified: branches/7.2.x/src/main/java/org/apache/catalina/util/ConcurrentMessageDigest.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/catalina/util/ConcurrentMessageDigest.java	2012-09-25 12:49:25 UTC (rev 2086)
+++ branches/7.2.x/src/main/java/org/apache/catalina/util/ConcurrentMessageDigest.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -33,7 +33,7 @@
     private static final String MD5 = "MD5";
 
     private static final Map<String,Queue<MessageDigest>> queues =
-            new HashMap<>();
+            new HashMap<String,Queue<MessageDigest>>();
 
 
     private ConcurrentMessageDigest() {
@@ -95,7 +95,7 @@
         synchronized (queues) {
             if (!queues.containsKey(algorithm)) {
                 MessageDigest md = MessageDigest.getInstance(algorithm);
-                Queue<MessageDigest> queue = new ConcurrentLinkedQueue<>();
+                Queue<MessageDigest> queue = new ConcurrentLinkedQueue<MessageDigest>();
                 queue.add(md);
                 queues.put(algorithm, queue);
             }

Modified: branches/7.2.x/src/main/java/org/apache/coyote/ajp/AjpAprProtocol.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/coyote/ajp/AjpAprProtocol.java	2012-09-25 12:49:25 UTC (rev 2086)
+++ branches/7.2.x/src/main/java/org/apache/coyote/ajp/AjpAprProtocol.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -321,7 +321,7 @@
      * Should authentication be done in the native webserver layer, 
      * or in the Servlet container ?
      */
-    protected boolean tomcatAuthentication = true;
+    protected boolean tomcatAuthentication = Constants.DEFAULT_TOMCAT_AUTHENTICATION;
     public boolean getTomcatAuthentication() { return tomcatAuthentication; }
     public void setTomcatAuthentication(boolean tomcatAuthentication) { this.tomcatAuthentication = tomcatAuthentication; }
 

Added: branches/7.2.x/src/main/java/org/apache/coyote/ajp/AjpProcessor.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/coyote/ajp/AjpProcessor.java	                        (rev 0)
+++ branches/7.2.x/src/main/java/org/apache/coyote/ajp/AjpProcessor.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -0,0 +1,1337 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.apache.coyote.ajp;
+
+import static org.jboss.web.CoyoteMessages.MESSAGES;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.ActionHook;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Request;
+import org.apache.coyote.RequestInfo;
+import org.apache.coyote.Response;
+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.HttpMessages;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.net.JIoEndpoint;
+import org.apache.tomcat.util.net.SocketStatus;
+import org.apache.tomcat.util.net.JIoEndpoint.Handler.SocketState;
+import org.jboss.web.CoyoteLogger;
+
+
+/**
+ * Processes HTTP requests.
+ *
+ * @author Remy Maucherat
+ * @author Henri Gomez
+ * @author Dan Milstein
+ * @author Keith Wannamaker
+ * @author Kevin Seguin
+ * @author Costin Manolache
+ * @author Bill Barker
+ */
+public class AjpProcessor implements ActionHook {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public AjpProcessor(int packetSize, JIoEndpoint endpoint) {
+
+        this.endpoint = endpoint;
+
+        request = new Request();
+        request.setInputBuffer(new SocketInputBuffer());
+
+        response = new Response();
+        response.setHook(this);
+        response.setOutputBuffer(new SocketOutputBuffer());
+        request.setResponse(response);
+
+        requestHeaderMessage = new AjpMessage(packetSize);
+        responseHeaderMessage = new AjpMessage(packetSize);
+        bodyMessage = new AjpMessage(packetSize);
+        
+        // Set the get body message buffer
+        AjpMessage getBodyMessage = new AjpMessage(16);
+        getBodyMessage.reset();
+        getBodyMessage.appendByte(Constants.JK_AJP13_GET_BODY_CHUNK);
+        getBodyMessage.appendInt(packetSize - Constants.READ_HEAD_LEN);
+        getBodyMessage.end();
+        getBodyMessageArray = new byte[getBodyMessage.getLen()];
+        System.arraycopy(getBodyMessage.getBuffer(), 0, getBodyMessageArray, 
+                0, getBodyMessage.getLen());
+
+        // Cause loading of HexUtils
+        int foo = HexUtils.DEC[0];
+
+        // Cause loading of HttpMessages
+        HttpMessages.getMessage(200);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated adapter.
+     */
+    protected Adapter adapter = null;
+
+
+    /**
+     * Request object.
+     */
+    protected Request request = null;
+
+
+    /**
+     * Response object.
+     */
+    protected Response response = null;
+
+
+    /**
+     * Header message. Note that this header is merely the one used during the
+     * processing of the first message of a "request", so it might not be a request
+     * header. It will stay unchanged during the processing of the whole request.
+     */
+    protected AjpMessage requestHeaderMessage = null;
+
+
+    /**
+     * Message used for response header composition.
+     */
+    protected AjpMessage responseHeaderMessage = null;
+
+
+    /**
+     * Body message.
+     */
+    protected AjpMessage bodyMessage = null;
+
+
+    /**
+     * Body message.
+     */
+    protected MessageBytes bodyBytes = MessageBytes.newInstance();
+
+
+    /**
+     * Error flag.
+     */
+    protected boolean error = false;
+
+
+    /**
+     * Socket associated with the current connection.
+     */
+    protected Socket socket;
+
+    
+    /**
+     * Input stream.
+     */
+    protected InputStream input;
+    
+    
+    /**
+     * Output stream.
+     */
+    protected OutputStream output;
+    
+
+    /**
+     * Host name (used to avoid useless B2C conversion on the host name).
+     */
+    protected char[] hostNameC = new char[0];
+
+
+    /**
+     * Associated endpoint.
+     */
+    protected JIoEndpoint endpoint;
+
+
+    /**
+     * The socket timeout used when reading the first block of the request
+     * header.
+     */
+    protected long readTimeout;
+
+
+    /**
+     * Temp message bytes used for processing.
+     */
+    protected MessageBytes tmpMB = MessageBytes.newInstance();
+
+
+    /**
+     * Byte chunk for certs.
+     */
+    protected MessageBytes certificates = MessageBytes.newInstance();
+
+
+    /**
+     * End of stream flag.
+     */
+    protected boolean endOfStream = false;
+
+
+    /**
+     * Body empty flag.
+     */
+    protected boolean empty = true;
+
+
+    /**
+     * First read.
+     */
+    protected boolean first = true;
+
+
+    /**
+     * Replay read.
+     */
+    protected boolean replay = false;
+
+
+    /**
+     * Finished response.
+     */
+    protected boolean finished = false;
+
+
+    /**
+     * Direct buffer used for sending right away a get body message.
+     */
+    protected final byte[] getBodyMessageArray;
+
+
+    /**
+     * Direct buffer used for sending right away a pong message.
+     */
+    protected static final byte[] pongMessageArray;
+
+
+    /**
+     * End message array.
+     */
+    protected static final byte[] endMessageArray;
+
+    /**
+     * Flush message array.
+     */
+    protected static final byte[] flushMessageArray;
+
+
+    /**
+     * Event used.
+     */
+    protected boolean event = false;
+    
+
+    /**
+     * Event processing.
+     */
+    protected boolean eventProcessing = true;
+    public void startProcessing() { eventProcessing = true; }
+    public void endProcessing() { eventProcessing = false; }
+    
+
+    // ----------------------------------------------------- Static Initializer
+
+
+    static {
+
+        // Set the read body message buffer
+        AjpMessage pongMessage = new AjpMessage(16);
+        pongMessage.reset();
+        pongMessage.appendByte(Constants.JK_AJP13_CPONG_REPLY);
+        pongMessage.end();
+        pongMessageArray = new byte[pongMessage.getLen()];
+        System.arraycopy(pongMessage.getBuffer(), 0, pongMessageArray, 
+                0, pongMessage.getLen());
+
+        // Allocate the end message array
+        AjpMessage endMessage = new AjpMessage(16);
+        endMessage.reset();
+        endMessage.appendByte(Constants.JK_AJP13_END_RESPONSE);
+        endMessage.appendByte(1);
+        endMessage.end();
+        endMessageArray = new byte[endMessage.getLen()];
+        System.arraycopy(endMessage.getBuffer(), 0, endMessageArray, 0,
+                endMessage.getLen());
+
+        // Allocate the flush message array
+        AjpMessage flushMessage = new AjpMessage(16);
+        flushMessage.reset();
+        flushMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
+        flushMessage.appendInt(0);
+        flushMessage.appendByte(0);
+        flushMessage.end();
+        flushMessageArray = new byte[flushMessage.getLen()];
+        System.arraycopy(flushMessage.getBuffer(), 0, flushMessageArray, 0,
+                flushMessage.getLen());
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Use Tomcat authentication ?
+     */
+    protected boolean tomcatAuthentication = true;
+    public boolean getTomcatAuthentication() { return tomcatAuthentication; }
+    public void setTomcatAuthentication(boolean tomcatAuthentication) { this.tomcatAuthentication = tomcatAuthentication; }
+
+
+    /**
+     * Required secret.
+     */
+    protected String requiredSecret = null;
+    public void setRequiredSecret(String requiredSecret) { this.requiredSecret = requiredSecret; }
+
+
+    /**
+     * The number of milliseconds Tomcat will wait for a subsequent request
+     * before closing the connection. The default is the same as for
+     * Apache HTTP Server (15 000 milliseconds).
+     */
+    protected int keepAliveTimeout = -1;
+    public int getKeepAliveTimeout() { return keepAliveTimeout; }
+    public void setKeepAliveTimeout(int timeout) { keepAliveTimeout = timeout; }
+
+
+    /**
+     * Timeout.
+     */
+    protected int timeout = -1;
+    public void setTimeout(int timeout) { this.timeout = timeout; }
+    public int getTimeout() { return timeout; }
+
+
+    /**
+     * A resume has been requested.
+     */
+    protected boolean resumeNotification = false;
+    public boolean getResumeNotification() { return resumeNotification; }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /** Get the request associated with this processor.
+     *
+     * @return The request
+     */
+    public Request getRequest() {
+        return request;
+    }
+
+
+    public SocketState event(SocketStatus status)
+    throws IOException {
+
+        RequestInfo rp = request.getRequestProcessor();
+        try {
+            if (status == SocketStatus.OPEN_CALLBACK) {
+                // The resume notification is now done
+                resumeNotification = false;
+            } else if (status == SocketStatus.ERROR) {
+                // Set error flag right away
+                error = true;
+            }
+            rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+            error = !adapter.event(request, response, status);
+        } catch (InterruptedIOException e) {
+            error = true;
+        } catch (Throwable t) {
+            CoyoteLogger.AJP_LOGGER.errorProcessingRequest(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 (!event) {
+            finish();
+            recycle();
+            return SocketState.OPEN;
+        } else {
+            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(Socket socket)
+        throws IOException {
+        RequestInfo rp = request.getRequestProcessor();
+        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
+
+        // Setting up the socket
+        this.socket = socket;
+        input = socket.getInputStream();
+        output = socket.getOutputStream();
+        int soTimeout = -1;
+        if (keepAliveTimeout > 0) {
+            soTimeout = socket.getSoTimeout();
+        }
+
+        // Error flag
+        error = false;
+
+        while (!error && !event) {
+
+            // Parsing the request header
+            try {
+                // Set keep alive timeout if enabled
+                if (keepAliveTimeout > 0) {
+                    socket.setSoTimeout(keepAliveTimeout);
+                }
+                // Get first message of the request
+                if (!readMessage(requestHeaderMessage)) {
+                    // This means a connection timeout
+                    rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+                    break;
+                }
+                // Set back timeout if keep alive timeout is enabled
+                if (keepAliveTimeout > 0) {
+                    socket.setSoTimeout(soTimeout);
+                }
+                // Check message type, process right away and break if
+                // not regular request processing
+                int type = requestHeaderMessage.getByte();
+                if (type == Constants.JK_AJP13_CPING_REQUEST) {
+                    try {
+                        output.write(pongMessageArray);
+                    } catch (IOException e) {
+                        error = true;
+                    }
+                    continue;
+                } else if(type != Constants.JK_AJP13_FORWARD_REQUEST) {
+                    // Usually the servlet didn't read the previous request body
+                    CoyoteLogger.AJP_LOGGER.unexpectedAjpMessage(type);
+                    error = true;
+                    break;
+                }
+
+                request.setStartTime(System.currentTimeMillis());
+            } catch (IOException e) {
+                error = true;
+                break;
+            } catch (Throwable t) {
+                CoyoteLogger.AJP_LOGGER.errorParsingAjpHeaderMessage(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) {
+                CoyoteLogger.AJP_LOGGER.errorPreparingAjpRequest(t);
+                // 400 - Internal Server Error
+                response.setStatus(400);
+                error = true;
+            }
+
+            // Process the request in the adapter
+            if (!error) {
+                try {
+                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+                    adapter.service(request, response);
+                } catch (InterruptedIOException e) {
+                    error = true;
+                } catch (Throwable t) {
+                    CoyoteLogger.AJP_LOGGER.errorProcessingRequest(t);
+                    // 500 - Internal Server Error
+                    response.setStatus(500);
+                    error = true;
+                }
+            }
+
+            // Finish the response if not done yet
+            if (!event && !finished) {
+                try {
+                    finish();
+                } catch (Throwable t) {
+                    error = true;
+                }
+            }
+
+            // 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();
+
+            if (!event) {
+                recycle();
+            }
+
+            rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
+
+        }
+
+        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+        
+        if (event) {
+            if (error) {
+                input = null;
+                output = null;
+                recycle();
+                return SocketState.CLOSED;
+            } else {
+                eventProcessing = false;
+                return SocketState.LONG;
+            }
+        } else {
+            input = null;
+            output = null;
+            recycle();
+            return SocketState.CLOSED;
+        }
+    }
+
+
+    // ----------------------------------------------------- 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) {
+
+            if (response.isCommitted())
+                return;
+
+            // Validate and write response headers
+            try {
+                prepareResponse();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+
+        } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
+
+            if (!response.isCommitted()) {
+                // Validate and write response headers
+                try {
+                    prepareResponse();
+                } catch (IOException e) {
+                    // Set error flag
+                    error = true;
+                    return;
+                }
+            }
+
+            try {
+                flush();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+
+        } else if (actionCode == ActionCode.ACTION_CLOSE) {
+            // Close
+
+            // End the processing of the current request, and stop any further
+            // transactions with the client
+
+            try {
+                finish();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+
+        } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
+
+            if (!certificates.isNull()) {
+                ByteChunk certData = certificates.getByteChunk();
+                X509Certificate jsseCerts[] = null;
+                ByteArrayInputStream bais =
+                    new ByteArrayInputStream(certData.getBytes(),
+                            certData.getStart(),
+                            certData.getLength());
+                // Fill the  elements.
+                try {
+                    CertificateFactory cf =
+                        CertificateFactory.getInstance("X.509");
+                    while(bais.available() > 0) {
+                        X509Certificate cert = (X509Certificate)
+                            cf.generateCertificate(bais);
+                        if(jsseCerts == null) {
+                            jsseCerts = new X509Certificate[1];
+                            jsseCerts[0] = cert;
+                        } else {
+                            X509Certificate [] temp = new X509Certificate[jsseCerts.length+1];
+                            System.arraycopy(jsseCerts,0,temp,0,jsseCerts.length);
+                            temp[jsseCerts.length] = cert;
+                            jsseCerts = temp;
+                        }
+                    }
+                } catch (java.security.cert.CertificateException e) {
+                    CoyoteLogger.AJP_LOGGER.errorProcessingCertificates(e);
+                    return;
+                }
+                request.setAttribute(org.apache.tomcat.util.net.Constants.CERTIFICATE_KEY, jsseCerts);
+            }
+
+        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
+
+            // Get remote host name using a DNS resolution
+            if (request.remoteHost().isNull()) {
+                try {
+                    request.remoteHost().setString(InetAddress.getByName
+                            (request.remoteAddr().toString()).getHostName());
+                } catch (IOException iex) {
+                    // Ignore
+                }
+            }
+
+        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
+
+            // Copy from local name for now, which should simply be an address
+            request.localAddr().setString(request.localName().toString());
+
+        } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
+
+            // Set the given bytes as the content
+            ByteChunk bc = (ByteChunk) param;
+            int length = bc.getLength();
+            bodyBytes.setBytes(bc.getBytes(), bc.getStart(), length);
+            request.setContentLength(length);
+            first = false;
+            empty = false;
+            replay = true;
+            endOfStream = false;
+
+        } else if (actionCode == ActionCode.ACTION_EVENT_BEGIN) {
+            event = true;
+        } else if (actionCode == ActionCode.ACTION_EVENT_END) {
+            event = false;
+        } else if (actionCode == ActionCode.ACTION_EVENT_SUSPEND) {
+            // No action needed
+        } else if (actionCode == ActionCode.ACTION_EVENT_RESUME) {
+            // An event is being processed already: adding for resume will be done
+            // when the socket gets back to the poller
+            if (!eventProcessing && !resumeNotification) {
+                endpoint.getEventPoller().add(socket, timeout, true, true);
+            }
+            resumeNotification = true;
+        } else if (actionCode == ActionCode.ACTION_EVENT_TIMEOUT) {
+            timeout = ((Integer) param).intValue();
+        }
+
+
+    }
+
+
+    // ------------------------------------------------------ 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() {
+
+        // Translate the HTTP method code to a String.
+        byte methodCode = requestHeaderMessage.getByte();
+        if (methodCode != Constants.SC_M_JK_STORED) {
+            String methodName = Constants.methodTransArray[(int)methodCode - 1];
+            request.method().setString(methodName);
+        }
+
+        requestHeaderMessage.getBytes(request.protocol());
+        requestHeaderMessage.getBytes(request.requestURI());
+
+        requestHeaderMessage.getBytes(request.remoteAddr());
+        requestHeaderMessage.getBytes(request.remoteHost());
+        requestHeaderMessage.getBytes(request.localName());
+        request.setLocalPort(requestHeaderMessage.getInt());
+
+        boolean isSSL = requestHeaderMessage.getByte() != 0;
+        if (isSSL) {
+            request.scheme().setString("https");
+        }
+
+        // Decode headers
+        MimeHeaders headers = request.getMimeHeaders();
+
+        int hCount = requestHeaderMessage.getInt();
+        for(int i = 0 ; i < hCount ; i++) {
+            String hName = null;
+
+            // Header names are encoded as either an integer code starting
+            // with 0xA0, or as a normal string (in which case the first
+            // two bytes are the length).
+            int isc = requestHeaderMessage.peekInt();
+            int hId = isc & 0xFF;
+
+            MessageBytes vMB = null;
+            isc &= 0xFF00;
+            if(0xA000 == isc) {
+                requestHeaderMessage.getInt(); // To advance the read position
+                hName = Constants.headerTransArray[hId - 1];
+                vMB = headers.addValue(hName);
+            } else {
+                // reset hId -- if the header currently being read
+                // happens to be 7 or 8 bytes long, the code below
+                // will think it's the content-type header or the
+                // content-length header - SC_REQ_CONTENT_TYPE=7,
+                // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
+                // behaviour.  see bug 5861 for more information.
+                hId = -1;
+                requestHeaderMessage.getBytes(tmpMB);
+                ByteChunk bc = tmpMB.getByteChunk();
+                vMB = headers.addValue(bc.getBuffer(),
+                        bc.getStart(), bc.getLength());
+            }
+
+            requestHeaderMessage.getBytes(vMB);
+
+            if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
+                    (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
+                // just read the content-length header, so set it
+                request.setContentLength( vMB.getInt() );
+            } else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
+                    (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
+                // just read the content-type header, so set it
+                ByteChunk bchunk = vMB.getByteChunk();
+                request.contentType().setBytes(bchunk.getBytes(),
+                        bchunk.getOffset(),
+                        bchunk.getLength());
+            }
+        }
+
+        // Decode extra attributes
+        boolean secret = false;
+        byte attributeCode;
+        while ((attributeCode = requestHeaderMessage.getByte())
+                != Constants.SC_A_ARE_DONE) {
+
+            switch (attributeCode) {
+
+            case Constants.SC_A_REQ_ATTRIBUTE :
+                requestHeaderMessage.getBytes(tmpMB);
+                String n = tmpMB.toString();
+                requestHeaderMessage.getBytes(tmpMB);
+                String v = tmpMB.toString();
+                request.setAttribute(n, v);
+                break;
+
+            case Constants.SC_A_CONTEXT :
+                requestHeaderMessage.getBytes(tmpMB);
+                // nothing
+                break;
+
+            case Constants.SC_A_SERVLET_PATH :
+                requestHeaderMessage.getBytes(tmpMB);
+                // nothing
+                break;
+
+            case Constants.SC_A_REMOTE_USER :
+                if (tomcatAuthentication) {
+                    // ignore server
+                    requestHeaderMessage.getBytes(tmpMB);
+                } else {
+                    requestHeaderMessage.getBytes(request.getRemoteUser());
+                }
+                break;
+
+            case Constants.SC_A_AUTH_TYPE :
+                if (tomcatAuthentication) {
+                    // ignore server
+                    requestHeaderMessage.getBytes(tmpMB);
+                } else {
+                    requestHeaderMessage.getBytes(request.getAuthType());
+                }
+                break;
+
+            case Constants.SC_A_QUERY_STRING :
+                requestHeaderMessage.getBytes(request.queryString());
+                break;
+
+            case Constants.SC_A_JVM_ROUTE :
+                requestHeaderMessage.getBytes(request.instanceId());
+                break;
+
+            case Constants.SC_A_SSL_CERT :
+                request.scheme().setString("https");
+                // SSL certificate extraction is lazy, moved to JkCoyoteHandler
+                requestHeaderMessage.getBytes(certificates);
+                break;
+
+            case Constants.SC_A_SSL_CIPHER :
+                request.scheme().setString("https");
+                requestHeaderMessage.getBytes(tmpMB);
+                request.setAttribute(org.apache.tomcat.util.net.Constants.CIPHER_SUITE_KEY,
+                                     tmpMB.toString());
+                break;
+
+            case Constants.SC_A_SSL_SESSION :
+                request.scheme().setString("https");
+                requestHeaderMessage.getBytes(tmpMB);
+                request.setAttribute(org.apache.tomcat.util.net.Constants.SESSION_ID_KEY,
+                                     tmpMB.toString());
+                break;
+
+            case Constants.SC_A_SSL_KEY_SIZE :
+                request.setAttribute(org.apache.tomcat.util.net.Constants.KEY_SIZE_KEY,
+                                     new Integer(requestHeaderMessage.getInt()));
+                break;
+
+            case Constants.SC_A_STORED_METHOD:
+                requestHeaderMessage.getBytes(request.method());
+                break;
+
+            case Constants.SC_A_SECRET:
+                requestHeaderMessage.getBytes(tmpMB);
+                if (requiredSecret != null) {
+                    secret = true;
+                    if (!tmpMB.equals(requiredSecret)) {
+                        response.setStatus(403);
+                        error = true;
+                    }
+                }
+                break;
+
+            default:
+                // Ignore unknown attribute for backward compatibility
+                break;
+
+            }
+
+        }
+
+        // Check if secret was submitted if required
+        if ((requiredSecret != null) && !secret) {
+            response.setStatus(403);
+            error = true;
+        }
+
+        // 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);
+            }
+
+        }
+
+        MessageBytes valueMB = request.getMimeHeaders().getValue("host");
+        parseHost(valueMB);
+
+    }
+
+
+    /**
+     * Parse host.
+     */
+    public void parseHost(MessageBytes valueMB) {
+
+        if (valueMB == null || (valueMB != null && valueMB.isNull()) ) {
+            // HTTP/1.0
+            request.setServerPort(request.getLocalPort());
+            try {
+                request.serverName().duplicate(request.localName());
+            } catch (IOException e) {
+                response.setStatus(400);
+                error = true;
+            }
+            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 (request.scheme().equalsIgnoreCase("https")) {
+                // 443 - Default HTTPS port
+                request.setServerPort(443);
+            } else {
+                // 80 - Default HTTTP port
+                request.setServerPort(80);
+            }
+            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[valueB[i + valueS] & 0xff];
+                if (charValue == -1) {
+                    // Invalid character
+                    error = true;
+                    // 400 - Bad request
+                    response.setStatus(400);
+                    break;
+                }
+                port = port + (charValue * mult);
+                mult = 10 * mult;
+            }
+            request.setServerPort(port);
+
+        }
+
+    }
+
+
+    /**
+     * When committing the response, we have to validate the set of headers, as
+     * well as setup the response filters.
+     */
+    protected void prepareResponse()
+        throws IOException {
+
+        response.setCommitted(true);
+
+        responseHeaderMessage.reset();
+        responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS);
+
+        // HTTP header contents
+        responseHeaderMessage.appendInt(response.getStatus());
+        String message = null;
+        if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER) {
+            message = response.getMessage();
+        }
+        if (message == null){
+            message = HttpMessages.getMessage(response.getStatus());
+        } else {
+            message = message.replace('\n', ' ').replace('\r', ' ');
+        }
+        if (message == null) {
+            // Many httpd 2.x wants a non empty status message
+            message = Integer.toString(response.getStatus());
+        }
+        tmpMB.setString(message);
+        responseHeaderMessage.appendBytes(tmpMB);
+
+        // Special headers
+        MimeHeaders headers = response.getMimeHeaders();
+        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);
+        }
+        int contentLength = response.getContentLength();
+        if (contentLength >= 0) {
+            headers.setValue("Content-Length").setInt(contentLength);
+        }
+
+        // Other headers
+        int numHeaders = headers.size();
+        responseHeaderMessage.appendInt(numHeaders);
+        for (int i = 0; i < numHeaders; i++) {
+            MessageBytes hN = headers.getName(i);
+            MessageBytes hV=headers.getValue(i);
+            if (hN.getLength() > 0 && !hV.isNull()) {
+                int hC = Constants.getResponseAjpIndex(hN.toString());
+                if (hC > 0) {
+                    responseHeaderMessage.appendInt(hC);
+                }
+                else {
+                    responseHeaderMessage.appendBytes(hN);
+                }
+                responseHeaderMessage.appendBytes(hV);
+            }
+        }
+
+        // Write to buffer
+        responseHeaderMessage.end();
+        output.write(responseHeaderMessage.getBuffer(), 0, responseHeaderMessage.getLen());
+
+    }
+
+
+    /**
+     * Finish AJP response.
+     */
+    protected void finish()
+        throws IOException {
+
+        if (!response.isCommitted()) {
+            // Validate and write response headers
+            try {
+                prepareResponse();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+        }
+
+        if (finished)
+            return;
+
+        finished = true;
+
+        // Add the end message
+        output.write(endMessageArray);
+
+
+        // read remaining data from the special first-body-chunk
+        if (first && request.getContentLengthLong() > 0) {
+            try {
+                receive();
+            } catch (IOException e) {
+            } 
+        }
+
+    }
+
+
+    /**
+     * Read at least the specified amount of bytes, and place them
+     * in the input buffer.
+     */
+    protected boolean read(byte[] buf, int pos, int n)
+        throws IOException {
+
+        int read = 0;
+        int res = 0;
+        while (read < n) {
+            res = input.read(buf, read + pos, n - read);
+            if (res > 0) {
+                read += res;
+            } else {
+                throw new IOException(MESSAGES.failedRead());
+            }
+        }
+        
+        return true;
+
+    }
+
+
+    /** Receive a chunk of data. Called to implement the
+     *  'special' packet in ajp13 and to receive the data
+     *  after we send a GET_BODY packet
+     */
+    public boolean receive() throws IOException {
+
+        first = false;
+        bodyMessage.reset();
+        readMessage(bodyMessage);
+
+        // No data received.
+        if (bodyMessage.getLen() == 0) {
+            // just the header
+            // Don't mark 'end of stream' for the first chunk.
+            return false;
+        }
+        int blen = bodyMessage.peekInt();
+        if (blen == 0) {
+            return false;
+        }
+
+        bodyMessage.getBytes(bodyBytes);
+        empty = false;
+        return true;
+    }
+
+    /**
+     * Get more request body data from the web server and store it in the
+     * internal buffer.
+     *
+     * @return true if there is more data, false if not.
+     */
+    private boolean refillReadBuffer() throws IOException {
+        // If the server returns an empty packet, assume that that end of
+        // the stream has been reached (yuck -- fix protocol??).
+        // FORM support
+        if (replay) {
+            endOfStream = true; // we've read everything there is
+        }
+        if (endOfStream) {
+            return false;
+        }
+        if (finished) {
+            return false;
+        }
+
+        // Request more data immediately
+        output.write(getBodyMessageArray);
+
+        boolean moreData = receive();
+        if( !moreData ) {
+            endOfStream = true;
+        }
+        return moreData;
+    }
+
+
+    /**
+     * Read an AJP message.
+     *
+     * @return true if the message has been read, false if the short read
+     *         didn't return anything
+     * @throws IOException any other failure, including incomplete reads
+     */
+    protected boolean readMessage(AjpMessage message)
+        throws IOException {
+
+        byte[] buf = message.getBuffer();
+
+        read(buf, 0, message.getHeaderLength());
+
+        if (message.processHeader() < 0) {
+            throw new IOException(MESSAGES.invalidAjpMessage());
+        }
+        read(buf, message.getHeaderLength(), message.getLen());
+
+        return true;
+
+    }
+
+
+    /**
+     * Recycle the processor.
+     */
+    public void recycle() {
+
+        // Recycle Request object
+        first = true;
+        endOfStream = false;
+        empty = true;
+        replay = false;
+        finished = false;
+        timeout = -1;
+        resumeNotification = false;
+        eventProcessing = true;
+        request.recycle();
+        response.recycle();
+        certificates.recycle();
+
+    }
+
+
+    /**
+     * Callback to write data from the buffer.
+     */
+    protected void flush()
+        throws IOException {
+        // Send the flush message
+        output.write(flushMessageArray);
+    }
+
+
+    // ------------------------------------- InputStreamInputBuffer Inner Class
+
+
+    /**
+     * This class is an input buffer which will read its data from an input
+     * stream.
+     */
+    protected class SocketInputBuffer
+        implements InputBuffer {
+
+
+        /**
+         * Read bytes into the specified chunk.
+         */
+        public int doRead(ByteChunk chunk, Request req )
+            throws IOException {
+
+            if (endOfStream) {
+                return -1;
+            }
+            if (first && req.getContentLength() > 0) {
+                // Handle special first-body-chunk
+                if (!receive()) {
+                    return 0;
+                }
+            } else if (empty) {
+                if (!refillReadBuffer()) {
+                    return -1;
+                }
+            }
+            ByteChunk bc = bodyBytes.getByteChunk();
+            chunk.setBytes(bc.getBuffer(), bc.getStart(), bc.getLength());
+            empty = true;
+            return chunk.getLength();
+
+        }
+
+    }
+
+
+    // ----------------------------------- OutputStreamOutputBuffer Inner Class
+
+
+    /**
+     * This class is an output buffer which will write data to an output
+     * stream.
+     */
+    protected class SocketOutputBuffer
+        implements OutputBuffer {
+
+
+        /**
+         * Write chunk.
+         */
+        public int doWrite(ByteChunk chunk, Response res)
+            throws IOException {
+
+            if (!response.isCommitted()) {
+                // Validate and write response headers
+                try {
+                    prepareResponse();
+                } catch (IOException e) {
+                    // Set error flag
+                    error = true;
+                }
+            }
+
+            int len = chunk.getLength();
+            // 4 - hardcoded, byte[] marshalling overhead
+            int chunkSize = Constants.MAX_SEND_SIZE;
+            int off = 0;
+            while (len > 0) {
+                int thisTime = len;
+                if (thisTime > chunkSize) {
+                    thisTime = chunkSize;
+                }
+                len -= thisTime;
+                responseHeaderMessage.reset();
+                responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
+                responseHeaderMessage.appendBytes(chunk.getBytes(), chunk.getOffset() + off, thisTime);
+                responseHeaderMessage.end();
+                output.write(responseHeaderMessage.getBuffer(), 0, responseHeaderMessage.getLen());
+
+                off += thisTime;
+            }
+
+            return chunk.getLength();
+
+        }
+
+
+    }
+
+
+}

Added: branches/7.2.x/src/main/java/org/apache/coyote/ajp/AjpProtocol.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/coyote/ajp/AjpProtocol.java	                        (rev 0)
+++ branches/7.2.x/src/main/java/org/apache/coyote/ajp/AjpProtocol.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -0,0 +1,538 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.apache.coyote.ajp;
+
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.URLEncoder;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+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.JIoEndpoint;
+import org.apache.tomcat.util.net.SocketStatus;
+import org.apache.tomcat.util.net.JIoEndpoint.Handler;
+import org.jboss.web.CoyoteLogger;
+
+
+/**
+ * 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
+ */
+public class AjpProtocol 
+    implements ProtocolHandler, MBeanRegistration {
+    
+    
+    // ------------------------------------------------------------ Constructor
+
+
+    public AjpProtocol() {
+        cHandler = new AjpConnectionHandler(this);
+        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
+        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
+        //setServerSoTimeout(Constants.DEFAULT_SERVER_SOCKET_TIMEOUT);
+        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
+    }
+
+    
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected ObjectName tpOname;
+    
+    
+    protected ObjectName rgOname;
+
+
+    /**
+     * Associated java.io endpoint.
+     */
+    protected JIoEndpoint endpoint = new JIoEndpoint();
+
+
+    /**
+     * Configuration attributes.
+     */
+    protected Hashtable attributes = new Hashtable();
+
+
+    /**
+     * Adapter which will process the requests recieved by this endpoint.
+     */
+    private Adapter adapter;
+    
+    
+    /**
+     * Connection handler for AJP.
+     */
+    private AjpConnectionHandler cHandler;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /** 
+     * Pass config info
+     */
+    public void setAttribute(String name, Object value) {
+        attributes.put(name, value);
+    }
+
+    public Object getAttribute(String key) {
+        return attributes.get(key);
+    }
+
+
+    public Iterator getAttributeNames() {
+        return attributes.keySet().iterator();
+    }
+
+
+    /**
+     * The adapter, used to call the connector
+     */
+    public void setAdapter(Adapter adapter) {
+        this.adapter = adapter;
+    }
+
+
+    public Adapter getAdapter() {
+        return adapter;
+    }
+
+
+    public boolean hasIoEvents() {
+        return false;
+    }
+
+    public RequestGroupInfo getRequestGroupInfo() {
+        return cHandler.global;
+    }
+
+
+    /** Start the protocol
+     */
+    public void init() throws Exception {
+        endpoint.setName(getName());
+        endpoint.setHandler(cHandler);
+
+        try {
+            endpoint.init();
+        } catch (Exception ex) {
+            CoyoteLogger.AJP_LOGGER.errorInitializingEndpoint(ex);
+            throw ex;
+        }
+    }
+
+
+    public void start() throws Exception {
+        if (org.apache.tomcat.util.Constants.ENABLE_MODELER) {
+            if (this.domain != null ) {
+                try {
+                    tpOname = new ObjectName
+                    (domain + ":" + "type=ThreadPool,name=" + getJmxName());
+                    Registry.getRegistry(null, null)
+                    .registerComponent(endpoint, tpOname, null );
+                } catch (Exception e) {
+                    CoyoteLogger.AJP_LOGGER.errorRegisteringPool(e);
+                }
+                rgOname = new ObjectName
+                (domain + ":type=GlobalRequestProcessor,name=" + getJmxName());
+                Registry.getRegistry(null, null).registerComponent
+                (cHandler.global, rgOname, null);
+            }
+        }
+        try {
+            endpoint.start();
+        } catch (Exception ex) {
+            CoyoteLogger.AJP_LOGGER.errorStartingEndpoint(ex);
+            throw ex;
+        }
+        CoyoteLogger.AJP_LOGGER.startingAjpProtocol(getName());
+    }
+
+    public void pause() throws Exception {
+        try {
+            endpoint.pause();
+        } catch (Exception ex) {
+            CoyoteLogger.AJP_LOGGER.errorPausingEndpoint(ex);
+            throw ex;
+        }
+        // Wait for a while until all the processors are idle
+        RequestInfo[] states = cHandler.global.getRequestProcessors();
+        int retry = 0;
+        boolean done = false;
+        while (!done && retry < org.apache.coyote.Constants.MAX_PAUSE_WAIT) {
+            retry++;
+            done = true;
+            for (int i = 0; i < states.length; i++) {
+                if (states[i].getStage() == org.apache.coyote.Constants.STAGE_SERVICE) {
+                    try {
+                        Thread.sleep(1000);
+                    } catch (InterruptedException e) {
+                        ;
+                    }
+                    done = false;
+                    break;
+                }
+            }
+        }
+        CoyoteLogger.AJP_LOGGER.pausingAjpProtocol(getName());
+    }
+
+    public void resume() throws Exception {
+        try {
+            endpoint.resume();
+        } catch (Exception ex) {
+            CoyoteLogger.AJP_LOGGER.errorResumingEndpoint(ex);
+            throw ex;
+        }
+        CoyoteLogger.AJP_LOGGER.resumingAjpProtocol(getName());
+    }
+
+    public void destroy() throws Exception {
+        CoyoteLogger.AJP_LOGGER.stoppingAjpProtocol(getName());
+        endpoint.destroy();
+        if (org.apache.tomcat.util.Constants.ENABLE_MODELER) {
+            if (tpOname!=null)
+                Registry.getRegistry(null, null).unregisterComponent(tpOname);
+            if (rgOname != null)
+                Registry.getRegistry(null, null).unregisterComponent(rgOname);
+        }
+    }
+
+    public String getJmxName() {
+        String encodedAddr = "";
+        if (getAddress() != null) {
+            encodedAddr = "" + getAddress();
+            encodedAddr = URLEncoder.encode(encodedAddr.replace('/', '-').replace(':', '_').replace('%', '-')) + "-";
+        }
+        return ("ajp-" + encodedAddr + endpoint.getPort());
+    }
+
+    public String getName() {
+        String encodedAddr = "";
+        if (getAddress() != null) {
+            encodedAddr = getAddress() + ":";
+        }
+        return ("ajp-" + encodedAddr + endpoint.getPort());
+    }
+
+    /**
+     * Processor cache.
+     */
+    protected int processorCache = -1;
+    public int getProcessorCache() { return this.processorCache; }
+    public void setProcessorCache(int processorCache) { this.processorCache = processorCache; }
+
+    public Executor getExecutor() { return endpoint.getExecutor(); }
+    public void setExecutor(Executor executor) { endpoint.setExecutor(executor); }
+    
+    public int getMaxThreads() { return endpoint.getMaxThreads(); }
+    public void setMaxThreads(int maxThreads) { endpoint.setMaxThreads(maxThreads); }
+
+    public int getThreadPriority() { return endpoint.getThreadPriority(); }
+    public void setThreadPriority(int threadPriority) { endpoint.setThreadPriority(threadPriority); }
+
+    public int getBacklog() { return endpoint.getBacklog(); }
+    public void setBacklog(int backlog) { endpoint.setBacklog(backlog); }
+
+    public int getPort() { return endpoint.getPort(); }
+    public void setPort(int port) { endpoint.setPort(port); }
+
+    public InetAddress getAddress() { return endpoint.getAddress(); }
+    public void setAddress(InetAddress ia) { endpoint.setAddress(ia); }
+
+    public boolean getTcpNoDelay() { return endpoint.getTcpNoDelay(); }
+    public void setTcpNoDelay(boolean tcpNoDelay) { endpoint.setTcpNoDelay(tcpNoDelay); }
+
+    public int getSoLinger() { return endpoint.getSoLinger(); }
+    public void setSoLinger(int soLinger) { endpoint.setSoLinger(soLinger); }
+
+    public int getSoTimeout() { return endpoint.getSoTimeout(); }
+    public void setSoTimeout(int soTimeout) { endpoint.setSoTimeout(soTimeout); }
+
+    public void setPollerSize(int pollerSize) { endpoint.setPollerSize(pollerSize); }
+    public int getPollerSize() { return endpoint.getPollerSize(); }
+
+    /**
+     * Should authentication be done in the native webserver layer, 
+     * or in the Servlet container ?
+     */
+    protected boolean tomcatAuthentication = Constants.DEFAULT_TOMCAT_AUTHENTICATION;
+    public boolean getTomcatAuthentication() { return tomcatAuthentication; }
+    public void setTomcatAuthentication(boolean tomcatAuthentication) { this.tomcatAuthentication = tomcatAuthentication; }
+
+    /**
+     * Required secret.
+     */
+    protected String requiredSecret = null;
+    public void setRequiredSecret(String requiredSecret) { this.requiredSecret = requiredSecret; }
+    
+    /**
+     * AJP packet size.
+     */
+    protected int packetSize = Constants.MAX_PACKET_SIZE;
+    public int getPacketSize() { return packetSize; }
+    public void setPacketSize(int packetSize) { this.packetSize = packetSize; }
+
+    
+    /**
+     * The number of seconds Tomcat will wait for a subsequent request
+     * before closing the connection.
+     */
+    protected int keepAliveTimeout = -1;
+    public int getKeepAliveTimeout() { return keepAliveTimeout; }
+    public void setKeepAliveTimeout(int timeout) { keepAliveTimeout = timeout; }
+
+
+    // --------------------------------------  AjpConnectionHandler Inner Class
+
+
+    protected static class AjpConnectionHandler implements Handler {
+
+        protected AjpProtocol proto;
+        protected AtomicLong registerCount = new AtomicLong(0);
+        protected RequestGroupInfo global = new RequestGroupInfo();
+
+        protected ConcurrentHashMap<Socket, AjpProcessor> connections =
+            new ConcurrentHashMap<Socket, AjpProcessor>();
+        protected ConcurrentLinkedQueue<AjpProcessor> recycledProcessors = 
+            new ConcurrentLinkedQueue<AjpProcessor>() {
+            protected AtomicInteger size = new AtomicInteger(0);
+            public boolean offer(AjpProcessor processor) {
+                boolean offer = (proto.processorCache == -1) ? true : (size.get() < proto.processorCache);
+                //avoid over growing our cache or add after we have stopped
+                boolean result = false;
+                if ( offer ) {
+                    result = super.offer(processor);
+                    if ( result ) {
+                        size.incrementAndGet();
+                    }
+                }
+                if (!result) unregister(processor);
+                return result;
+            }
+            
+            public AjpProcessor poll() {
+                AjpProcessor result = super.poll();
+                if ( result != null ) {
+                    size.decrementAndGet();
+                }
+                return result;
+            }
+            
+            public void clear() {
+                AjpProcessor next = poll();
+                while ( next != null ) {
+                    unregister(next);
+                    next = poll();
+                }
+                super.clear();
+                size.set(0);
+            }
+        };
+
+        public AjpConnectionHandler(AjpProtocol proto) {
+            this.proto = proto;
+        }
+
+        public SocketState event(Socket socket, SocketStatus status) {
+            AjpProcessor result = connections.get(socket);
+            SocketState state = SocketState.CLOSED; 
+            if (result != null) {
+                result.startProcessing();
+                // Call the appropriate event
+                try {
+                    state = result.event(status);
+                } catch (java.net.SocketException e) {
+                    // SocketExceptions are normal
+                    CoyoteLogger.AJP_LOGGER.socketException(e);
+                } catch (java.io.IOException e) {
+                    // IOExceptions are normal
+                    CoyoteLogger.AJP_LOGGER.socketException(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.
+                    CoyoteLogger.AJP_LOGGER.socketError(e);
+                } finally {
+                    if (state != SocketState.LONG) {
+                        connections.remove(socket);
+                        recycledProcessors.offer(result);
+                    } else {
+                        if (proto.endpoint.isRunning()) {
+                            proto.endpoint.getEventPoller().add(socket, result.getTimeout(), 
+                                    result.getResumeNotification(), false);
+                        }
+                    }
+                    result.endProcessing();
+                }
+            }
+            return state;
+        }
+        
+        public SocketState process(Socket socket) {
+            AjpProcessor processor = recycledProcessors.poll();
+            try {
+
+                if (processor == null) {
+                    processor = createProcessor();
+                }
+
+                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);
+                    proto.endpoint.getEventPoller().add(socket, processor.getTimeout(), 
+                            processor.getResumeNotification(), false);
+                } else {
+                    recycledProcessors.offer(processor);
+                }
+                return state;
+
+            } catch(java.net.SocketException e) {
+                // SocketExceptions are normal
+                CoyoteLogger.AJP_LOGGER.socketException(e);
+            } catch (java.io.IOException e) {
+                // IOExceptions are normal
+                CoyoteLogger.AJP_LOGGER.socketException(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.
+                CoyoteLogger.AJP_LOGGER.socketError(e);
+            }
+            recycledProcessors.offer(processor);
+            return SocketState.CLOSED;
+        }
+
+        protected AjpProcessor createProcessor() {
+            AjpProcessor processor = new AjpProcessor(proto.packetSize, proto.endpoint);
+            processor.setAdapter(proto.adapter);
+            processor.setTomcatAuthentication(proto.tomcatAuthentication);
+            processor.setRequiredSecret(proto.requiredSecret);
+            processor.setKeepAliveTimeout(proto.keepAliveTimeout);
+            register(processor);
+            return processor;
+        }
+        
+        protected void register(AjpProcessor processor) {
+            RequestInfo rp = processor.getRequest().getRequestProcessor();
+            rp.setGlobalProcessor(global);
+            if (org.apache.tomcat.util.Constants.ENABLE_MODELER && proto.getDomain() != null) {
+                synchronized (this) {
+                    try {
+                        long count = registerCount.incrementAndGet();
+                        ObjectName rpName = new ObjectName
+                            (proto.getDomain() + ":type=RequestProcessor,worker="
+                                + proto.getJmxName() + ",name=AjpRequest" + count);
+                        Registry.getRegistry(null, null).registerComponent(rp, rpName, null);
+                        rp.setRpName(rpName);
+                    } catch (Exception e) {
+                        CoyoteLogger.AJP_LOGGER.errorRegisteringRequest(e);
+                    }
+                }
+            }
+        }
+
+        protected void unregister(AjpProcessor processor) {
+            RequestInfo rp = processor.getRequest().getRequestProcessor();
+            rp.setGlobalProcessor(null);
+            if (org.apache.tomcat.util.Constants.ENABLE_MODELER && proto.getDomain() != null) {
+                synchronized (this) {
+                    try {
+                        ObjectName rpName = rp.getRpName();
+                        Registry.getRegistry(null, null).unregisterComponent(rpName);
+                        rp.setRpName(null);
+                    } catch (Exception e) {
+                        CoyoteLogger.AJP_LOGGER.errorUnregisteringRequest(e);
+                    }
+                }
+            }
+        }
+
+    }
+
+
+    // -------------------- 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() {
+    }
+    
+ 
+}

Modified: branches/7.2.x/src/main/java/org/apache/coyote/ajp/Constants.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/coyote/ajp/Constants.java	2012-09-25 12:49:25 UTC (rev 2086)
+++ branches/7.2.x/src/main/java/org/apache/coyote/ajp/Constants.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -44,6 +44,8 @@
     public static final int DEFAULT_CONNECTION_UPLOAD_TIMEOUT = 300000;
     public static final int DEFAULT_SERVER_SOCKET_TIMEOUT = 0;
     public static final boolean DEFAULT_TCP_NO_DELAY = true;
+    public static final boolean DEFAULT_TOMCAT_AUTHENTICATION = 
+            Boolean.valueOf(System.getProperty("org.apache.coyote.ajp.DEFAULT_TOMCAT_AUTHENTICATION", "true")).booleanValue();
 
     // Prefix codes for message types from server to container
     public static final byte JK_AJP13_FORWARD_REQUEST   = 2;

Modified: branches/7.2.x/src/main/java/org/apache/coyote/http11/Http11NioProtocol.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/coyote/http11/Http11NioProtocol.java	2012-09-25 12:49:25 UTC (rev 2086)
+++ branches/7.2.x/src/main/java/org/apache/coyote/http11/Http11NioProtocol.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -41,7 +41,6 @@
 import org.apache.tomcat.util.modeler.Registry;
 import org.apache.tomcat.util.net.NioChannel;
 import org.apache.tomcat.util.net.NioEndpoint;
-import org.apache.tomcat.util.net.SSLImplementation;
 import org.apache.tomcat.util.net.SocketStatus;
 import org.apache.tomcat.util.net.jsse.NioJSSEImplementation;
 import org.apache.tomcat.util.net.jsse.NioJSSESocketChannelFactory;
@@ -92,9 +91,9 @@
 		// Verify the validity of the configured socket factory
 		try {
 			if (isSSLEnabled()) {
-				sslImplementation = SSLImplementation.getInstance(NioJSSEImplementation.class
-						.getName());
-				socketFactory = sslImplementation.getServerSocketChannelFactory();
+				sslImplementation = new NioJSSEImplementation();
+				//Possible pluggability ? SSLImplementation.getInstance(NioJSSEImplementation.class.getName());
+				socketFactory = ((NioJSSEImplementation) sslImplementation).getServerSocketChannelFactory();
 				endpoint.setServerSocketChannelFactory(socketFactory);
 			}
 		} catch (Exception ex) {
@@ -896,7 +895,7 @@
 				}
 
 				if (proto.secure && (proto.sslImplementation != null)) {
-					processor.setSSLSupport(proto.sslImplementation.getSSLSupport(channel));
+					processor.setSSLSupport(((NioJSSEImplementation) proto.sslImplementation).getSSLSupport(channel));
 				} else {
 					processor.setSSLSupport(null);
 				}

Added: branches/7.2.x/src/main/java/org/apache/coyote/http11/Http11Processor.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/coyote/http11/Http11Processor.java	                        (rev 0)
+++ branches/7.2.x/src/main/java/org/apache/coyote/http11/Http11Processor.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -0,0 +1,1721 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.apache.coyote.http11;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.Locale;
+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.JIoEndpoint;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SocketStatus;
+import org.apache.tomcat.util.net.JIoEndpoint.Handler.SocketState;
+import org.jboss.web.CoyoteLogger;
+
+
+/**
+ * Processes HTTP requests.
+ *
+ * @author Remy Maucherat
+ */
+public class Http11Processor implements ActionHook {
+
+
+    protected static final boolean CHUNK_ON_CLOSE = 
+        Boolean.valueOf(System.getProperty("org.apache.coyote.http11.Http11Processor.CHUNK_ON_CLOSE", "false")).booleanValue();
+    
+
+    // ------------------------------------------------------------ Constructor
+
+
+    public Http11Processor(int headerBufferSize, JIoEndpoint endpoint) {
+
+        this.endpoint = endpoint;
+        
+        request = new Request();
+        inputBuffer = new InternalInputBuffer(request, headerBufferSize);
+        request.setInputBuffer(inputBuffer);
+
+        response = new Response();
+        response.setHook(this);
+        outputBuffer = new InternalOutputBuffer(response, headerBufferSize);
+        response.setOutputBuffer(outputBuffer);
+        request.setResponse(response);
+
+        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 InternalInputBuffer inputBuffer = null;
+
+
+    /**
+     * Output.
+     */
+    protected InternalOutputBuffer 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;
+
+
+    /**
+     * 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;
+
+    /**
+     * The number of seconds Tomcat will wait for a subsequent request
+     * before closing the connection.
+     */
+    protected int keepAliveTimeout = -1;
+
+
+    /**
+     * SSL information.
+     */
+    protected SSLSupport sslSupport;
+
+
+    /**
+     * Socket associated with the current connection.
+     */
+    protected Socket socket;
+
+
+    /**
+     * 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;
+
+
+    /**
+     * 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 saved 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 JIoEndpoint endpoint;
+
+
+    /**
+     * Allow a customized the server header for the tin-foil hat folks.
+     */
+    protected String server = null;
+
+    
+    /**
+     * Event used.
+     */
+    protected boolean event = false;
+
+
+    /**
+     * True if a resume has been requested.
+     */
+    protected boolean resumeNotification = false;
+
+
+    /**
+     * Event processing.
+     */
+    protected boolean eventProcessing = true;
+    public void startProcessing() { eventProcessing = true; }
+    public void endProcessing() { eventProcessing = false; }
+
+
+    // ------------------------------------------------------------- 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) {
+            CoyoteLogger.HTTP_LOGGER.errorParsingRegexp(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) {
+            this.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);
+    }
+
+
+    /**
+     * Timeout.
+     */
+    protected int timeout = -1;
+    public void setTimeout(int timeout) { this.timeout = timeout; }
+    public int getTimeout() { return timeout; }
+
+
+    // --------------------------------------------------------- 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 {
+                CoyoteLogger.HTTP_LOGGER.unknownFilter(className);
+            }
+        } catch (Exception e) {
+            CoyoteLogger.HTTP_LOGGER.errorInitializingFilter(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;
+    }
+
+
+    /**
+     * 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) {
+            CoyoteLogger.HTTP_LOGGER.errorParsingRegexp(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 Keep-Alive timeout.
+     */
+    public void setKeepAliveTimeout(int timeout) {
+        keepAliveTimeout = timeout;
+    }
+
+
+    /**
+     * Return the number Keep-Alive timeout.
+     */
+    public int getKeepAliveTimeout() {
+        return keepAliveTimeout;
+    }
+
+
+    /**
+     * 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 SSL information for this HTTP connection.
+     */
+    public void setSSLSupport(SSLSupport sslSupport) {
+        this.sslSupport = sslSupport;
+    }
+
+
+    /**
+     * 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;
+    }
+
+    public boolean getResumeNotification() {
+        return resumeNotification;
+    }
+    
+
+    /**
+     * 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 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;
+    }
+
+    public SocketState event(SocketStatus status)
+    throws IOException {
+
+        RequestInfo rp = request.getRequestProcessor();
+        try {
+            if (status == SocketStatus.OPEN_CALLBACK) {
+                // The resume notification is now done
+                resumeNotification = false;
+            } else if (status == SocketStatus.ERROR) {
+                // Set error flag right away
+                error = true;
+            }
+            rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+            error = !adapter.event(request, response, status);
+        } catch (InterruptedIOException e) {
+            error = true;
+        } catch (Throwable t) {
+            CoyoteLogger.HTTP_LOGGER.errorProcessingRequest(t);
+            // 500 - Internal Server Error
+            response.setStatus(500);
+            error = true;
+        }
+
+        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+
+        if (error) {
+            inputBuffer.nextRequest();
+            outputBuffer.nextRequest();
+            recycle();
+            return SocketState.CLOSED;
+        } else if (!event) {
+            endRequest();
+            boolean pipelined = inputBuffer.nextRequest();
+            outputBuffer.nextRequest();
+            recycle();
+            return (pipelined || !keepAlive) ? SocketState.CLOSED : SocketState.OPEN;
+        } else {
+            return SocketState.LONG;
+        }
+    }
+
+    /**
+     * Process pipelined HTTP requests on the specified socket.
+     *
+     * @param socket Socket from which the HTTP requests will be read
+     *               and the HTTP responses will be written.
+     *  
+     * @throws IOException error during an I/O operation
+     */
+    public SocketState process(Socket 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 I/O
+        this.socket = socket;
+        inputBuffer.setInputStream(socket.getInputStream());
+        outputBuffer.setOutputStream(socket.getOutputStream());
+
+        // Error flag
+        error = false;
+        keepAlive = true;
+
+        int keepAliveLeft = maxKeepAliveRequests;
+        int soTimeout = socket.getSoTimeout();
+
+        int threadRatio = (endpoint.getCurrentThreadsBusy() * 100)
+                / endpoint.getMaxThreads();
+        if (threadRatio > 75) {
+            keepAliveLeft = 1;
+        }
+        
+        boolean keptAlive = false;
+
+        while (!error && keepAlive && !event) {
+
+            // Parsing the request header
+            try {
+                if (!disableUploadTimeout && keptAlive) {
+                    if (keepAliveTimeout > 0) {
+                        socket.setSoTimeout(keepAliveTimeout);
+                    }
+                    else if (soTimeout > 0) {
+                        socket.setSoTimeout(soTimeout);
+                    }
+                }
+                inputBuffer.parseRequestLine();
+                request.setStartTime(System.currentTimeMillis());
+                keptAlive = true;
+                if (!disableUploadTimeout) {
+                    socket.setSoTimeout(timeout);
+                }
+                inputBuffer.parseHeaders();
+            } catch (IOException e) {
+                error = true;
+                break;
+            } catch (Throwable t) {
+                CoyoteLogger.HTTP_LOGGER.errorParsingHeader(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) {
+                CoyoteLogger.HTTP_LOGGER.errorPreparingRequest(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());
+                    }
+
+                } catch (InterruptedIOException e) {
+                    error = true;
+                } catch (Throwable t) {
+                    CoyoteLogger.HTTP_LOGGER.errorProcessingRequest(t);
+                    // 500 - Internal Server Error
+                    response.setStatus(500);
+                    error = true;
+                }
+            }
+
+            // Finish the handling of the request
+            if (error) {
+                // If there is an unspecified error, the connection will be closed
+                inputBuffer.setSwallowInput(false);
+            }
+            if (!event) {
+                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();
+
+            if (!event) {
+                // Next request
+                inputBuffer.nextRequest();
+                outputBuffer.nextRequest();
+            }
+
+            rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
+
+        }
+
+        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+
+        if (event) {
+            if (error) {
+                inputBuffer.nextRequest();
+                outputBuffer.nextRequest();
+                recycle();
+                return SocketState.CLOSED;
+            } else {
+                eventProcessing = false;
+                return SocketState.LONG;
+            }
+        } else {
+            recycle();
+            return SocketState.CLOSED;
+        }
+
+    }
+
+
+    public void endRequest() {
+        
+        // Finish the handling of the request
+        try {
+            inputBuffer.endRequest();
+        } catch (IOException e) {
+            error = true;
+        } catch (Throwable t) {
+            CoyoteLogger.HTTP_LOGGER.errorFinishingRequest(t);
+            // 500 - Internal Server Error
+            response.setStatus(500);
+            error = true;
+        }
+        try {
+            outputBuffer.endRequest();
+        } catch (IOException e) {
+            error = true;
+        } catch (Throwable t) {
+            CoyoteLogger.HTTP_LOGGER.errorFinishingResponse(t);
+            error = true;
+        }
+
+    }
+    
+    
+    public void recycle() {
+        inputBuffer.recycle();
+        outputBuffer.recycle();
+        this.socket = null;
+        sslSupport = null;
+        timeout = -1;
+        resumeNotification = false;
+        eventProcessing = true;
+    }
+
+
+    // ----------------------------------------------------- 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
+
+            try {
+                outputBuffer.endRequest();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+
+        } else if (actionCode == ActionCode.ACTION_CUSTOM) {
+
+            // Do nothing
+
+        } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
+
+            try {
+                if (sslSupport != null) {
+                    Object sslO = sslSupport.getCipherSuite();
+                    if (sslO != null)
+                        request.setAttribute
+                            (org.apache.tomcat.util.net.Constants.CIPHER_SUITE_KEY, sslO);
+                    sslO = sslSupport.getPeerCertificateChain(false);
+                    if (sslO != null)
+                        request.setAttribute
+                            (org.apache.tomcat.util.net.Constants.CERTIFICATE_KEY, sslO);
+                    sslO = sslSupport.getKeySize();
+                    if (sslO != null)
+                        request.setAttribute
+                            (org.apache.tomcat.util.net.Constants.KEY_SIZE_KEY, sslO);
+                    sslO = sslSupport.getSessionId();
+                    if (sslO != null)
+                        request.setAttribute
+                            (org.apache.tomcat.util.net.Constants.SESSION_ID_KEY, sslO);
+                }
+            } catch (Exception e) {
+                CoyoteLogger.HTTP_LOGGER.errorGettingSslAttributes(e);
+            }
+
+        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
+
+            if ((remoteAddr == null) && (socket != null)) {
+                InetAddress inetAddr = socket.getInetAddress();
+                if (inetAddr != null) {
+                    remoteAddr = inetAddr.getHostAddress();
+                }
+            }
+            request.remoteAddr().setString(remoteAddr);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
+
+            if ((localName == null) && (socket != null)) {
+                InetAddress inetAddr = socket.getLocalAddress();
+                if (inetAddr != null) {
+                    localName = inetAddr.getHostName();
+                }
+            }
+            request.localName().setString(localName);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
+
+            if ((remoteHost == null) && (socket != null)) {
+                InetAddress inetAddr = 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.getLocalAddress().getHostAddress();
+
+            request.localAddr().setString(localAddr);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
+
+            if ((remotePort == -1 ) && (socket !=null)) {
+                remotePort = socket.getPort();
+            }
+            request.setRemotePort(remotePort);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
+
+            if ((localPort == -1 ) && (socket !=null)) {
+                localPort = socket.getLocalPort();
+            }
+            request.setLocalPort(localPort);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
+            if( sslSupport != null) {
+                // Consume and buffer the request body, so that it does not
+                // interfere with the client's handshake messages
+                if (maxSavePostSize != 0) {
+                    BufferedInputFilter buffredInputFilter = new BufferedInputFilter();
+                    buffredInputFilter.setLimit(maxSavePostSize);
+                    inputBuffer.addActiveFilter(buffredInputFilter);
+                }
+                try {
+                    Object sslO = sslSupport.getPeerCertificateChain(true);
+                    if( sslO != null) {
+                        request.setAttribute
+                            (org.apache.tomcat.util.net.Constants.CERTIFICATE_KEY, sslO);
+                    }
+                } catch (Exception e) {
+                    CoyoteLogger.HTTP_LOGGER.errorGettingSslAttributes(e);
+                }
+            }
+        } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
+            ByteChunk body = (ByteChunk) param;
+            
+            InputFilter savedBody = new SavedRequestInputFilter(body);
+            savedBody.setRequest(request);
+
+            InternalInputBuffer internalBuffer = (InternalInputBuffer)
+                request.getInputBuffer();
+            internalBuffer.addActiveFilter(savedBody);
+        } else if (actionCode == ActionCode.ACTION_EVENT_BEGIN) {
+            event = true;
+        } else if (actionCode == ActionCode.ACTION_EVENT_END) {
+            event = false;
+        } else if (actionCode == ActionCode.ACTION_EVENT_SUSPEND) {
+            // No action needed
+        } else if (actionCode == ActionCode.ACTION_EVENT_RESUME) {
+            // An event is being processed already: adding for resume will be done
+            // when the socket gets back to the poller
+            if (!eventProcessing && !resumeNotification) {
+                endpoint.getEventPoller().add(socket, timeout, true, true);
+            }
+            resumeNotification = true;
+        } else if (actionCode == ActionCode.ACTION_EVENT_TIMEOUT) {
+            timeout = ((Integer) param).intValue();
+        }
+
+    }
+
+
+    // ------------------------------------------------------ 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;
+        if (sslSupport != null) {
+            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(Locale.ENGLISH).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(Locale.ENGLISH).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;
+        }
+
+    }
+
+
+    /**
+     * Parse host.
+     */
+    protected 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(socket.getLocalPort());
+            InetAddress localAddress = socket.getLocalAddress();
+            // Setting the socket-related fields. The adapter doesn't know
+            // about socket.
+            request.serverName().setString(localAddress.getHostName());
+            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 (sslSupport == null) {
+                // 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[valueB[i + valueS] & 0xff];
+                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;
+        }
+
+        // Check for compression
+        boolean useCompression = false;
+        if (entityBody && (compressionLevel > 0)) {
+            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 || CHUNK_ON_CLOSE)) {
+                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.addValue("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 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: branches/7.2.x/src/main/java/org/apache/coyote/http11/Http11Protocol.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/coyote/http11/Http11Protocol.java	                        (rev 0)
+++ branches/7.2.x/src/main/java/org/apache/coyote/http11/Http11Protocol.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -0,0 +1,778 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.apache.coyote.http11;
+
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.net.ssl.SSLContext;
+
+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.JIoEndpoint;
+import org.apache.tomcat.util.net.SSLImplementation;
+import org.apache.tomcat.util.net.ServerSocketFactory;
+import org.apache.tomcat.util.net.SocketStatus;
+import org.apache.tomcat.util.net.JIoEndpoint.Handler;
+import org.apache.tomcat.util.net.jsse.JSSEImplementation;
+import org.jboss.web.CoyoteLogger;
+
+
+/**
+ * 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
+ */
+public class Http11Protocol 
+    implements ProtocolHandler, MBeanRegistration {
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    public Http11Protocol() {
+        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
+        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
+        //setServerSoTimeout(Constants.DEFAULT_SERVER_SOCKET_TIMEOUT);
+        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
+    }
+
+    
+    // ----------------------------------------------------------------- Fields
+
+
+    protected Http11ConnectionHandler cHandler = new Http11ConnectionHandler(this);
+    protected JIoEndpoint endpoint = new JIoEndpoint();
+
+
+    // *
+    protected ObjectName tpOname = null;
+    // *
+    protected ObjectName rgOname = null;
+
+
+    protected ServerSocketFactory socketFactory = null;
+    protected JSSEImplementation sslImplementation = null;
+
+
+    // ----------------------------------------- ProtocolHandler Implementation
+    // *
+
+
+    protected HashMap<String, Object> attributes = new HashMap<String, Object>();
+
+    
+    /**
+     * Pass config info
+     */
+    public void setAttribute(String name, Object value) {
+        attributes.put(name, value);
+    }
+
+    public Object getAttribute(String 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.
+     */
+    protected Adapter adapter;
+    public void setAdapter(Adapter adapter) { this.adapter = adapter; }
+    public Adapter getAdapter() { return adapter; }
+
+
+    public boolean hasIoEvents() {
+        return false;
+    }
+
+    public RequestGroupInfo getRequestGroupInfo() {
+        return cHandler.global;
+    }
+
+    public void init() throws Exception {
+        endpoint.setName(getName());
+        endpoint.setHandler(cHandler);
+
+        // Verify the validity of the configured socket factory
+        try {
+            if (isSSLEnabled()) {
+                sslImplementation =
+                        (JSSEImplementation) SSLImplementation.getInstance(sslImplementationName);
+                socketFactory = sslImplementation.getServerSocketFactory();
+                endpoint.setServerSocketFactory(socketFactory);
+            } else if (socketFactoryName != null) {
+                socketFactory = (ServerSocketFactory) Class.forName(socketFactoryName).newInstance();
+                endpoint.setServerSocketFactory(socketFactory);
+            }
+        } catch (Exception ex) {
+            CoyoteLogger.HTTP_LOGGER.errorInitializingSocketFactory(ex);
+            throw ex;
+        }
+
+        if (socketFactory!=null) {
+            Iterator<String> attE = attributes.keySet().iterator();
+            while( attE.hasNext() ) {
+                String key = attE.next();
+                Object v=attributes.get(key);
+                socketFactory.setAttribute(key, v);
+            }
+        }
+        
+        try {
+            endpoint.init();
+        } catch (Exception ex) {
+            CoyoteLogger.HTTP_LOGGER.errorInitializingEndpoint(ex);
+            throw ex;
+        }
+        CoyoteLogger.HTTP_LOGGER.initHttpConnector(getName());
+
+    }
+
+    public void start() throws Exception {
+        if (org.apache.tomcat.util.Constants.ENABLE_MODELER) {
+            if (this.domain != null) {
+                try {
+                    tpOname = new ObjectName
+                    (domain + ":" + "type=ThreadPool,name=" + getJmxName());
+                    Registry.getRegistry(null, null)
+                    .registerComponent(endpoint, tpOname, null );
+                } catch (Exception e) {
+                    CoyoteLogger.HTTP_LOGGER.errorRegisteringPool(e);
+                }
+                rgOname=new ObjectName
+                (domain + ":type=GlobalRequestProcessor,name=" + getJmxName());
+                Registry.getRegistry(null, null).registerComponent
+                ( cHandler.global, rgOname, null );
+            }
+        }
+        try {
+            endpoint.start();
+        } catch (Exception ex) {
+            CoyoteLogger.HTTP_LOGGER.errorStartingEndpoint(ex);
+            throw ex;
+        }
+        CoyoteLogger.HTTP_LOGGER.startHttpConnector(getName());
+    }
+
+    public void pause() throws Exception {
+        try {
+            endpoint.pause();
+        } catch (Exception ex) {
+            CoyoteLogger.HTTP_LOGGER.errorPausingEndpoint(ex);
+            throw ex;
+        }
+        // Wait for a while until all the processors are no longer processing requests
+        RequestInfo[] states = cHandler.global.getRequestProcessors();
+        int retry = 0;
+        boolean done = false;
+        while (!done && retry < org.apache.coyote.Constants.MAX_PAUSE_WAIT) {
+            retry++;
+            done = true;
+            for (int i = 0; i < states.length; i++) {
+                if (states[i].getStage() == org.apache.coyote.Constants.STAGE_SERVICE) {
+                    try {
+                        Thread.sleep(1000);
+                    } catch (InterruptedException e) {
+                        ;
+                    }
+                    done = false;
+                    break;
+                }
+            }
+        }
+        CoyoteLogger.HTTP_LOGGER.pauseHttpConnector(getName());
+    }
+
+    public void resume() throws Exception {
+        try {
+            endpoint.resume();
+        } catch (Exception ex) {
+            CoyoteLogger.HTTP_LOGGER.errorResumingEndpoint(ex);
+            throw ex;
+        }
+        CoyoteLogger.HTTP_LOGGER.resumeHttpConnector(getName());
+    }
+
+    public void destroy() throws Exception {
+        CoyoteLogger.HTTP_LOGGER.stopHttpConnector(getName());
+        endpoint.destroy();
+        if (org.apache.tomcat.util.Constants.ENABLE_MODELER) {
+            if (tpOname!=null)
+                Registry.getRegistry(null, null).unregisterComponent(tpOname);
+            if (rgOname != null)
+                Registry.getRegistry(null, null).unregisterComponent(rgOname);
+        }
+    }
+
+    public String getJmxName() {
+        String encodedAddr = "";
+        if (getAddress() != null) {
+            encodedAddr = "" + getAddress();
+            encodedAddr = URLEncoder.encode(encodedAddr.replace('/', '-').replace(':', '_').replace('%', '-')) + "-";
+        }
+        return ("http-" + encodedAddr + endpoint.getPort());
+    }
+
+    public String getName() {
+        String encodedAddr = "";
+        if (getAddress() != null) {
+            encodedAddr = getAddress() + ":";
+        }
+        return ("http-" + encodedAddr + endpoint.getPort());
+    }
+
+    // ------------------------------------------------------------- Properties
+
+    
+    /**
+     * Processor cache.
+     */
+    protected int processorCache = -1;
+    public int getProcessorCache() { return this.processorCache; }
+    public void setProcessorCache(int processorCache) { this.processorCache = processorCache; }
+
+    protected int socketBuffer = 9000;
+    public int getSocketBuffer() { return socketBuffer; }
+    public void setSocketBuffer(int socketBuffer) { this.socketBuffer = socketBuffer; }
+
+    /**
+     * This field indicates if the protocol is secure from the perspective of
+     * the client (= https is used).
+     */
+    protected boolean secure;
+    public boolean getSecure() { return secure; }
+    public void setSecure(boolean b) { secure = b; }
+
+    protected boolean SSLEnabled = false;
+    public boolean isSSLEnabled() { return SSLEnabled;}
+    public void setSSLEnabled(boolean SSLEnabled) {this.SSLEnabled = SSLEnabled;}    
+    
+    /**
+     * Name of the socket factory.
+     */
+    protected String socketFactoryName = null;
+    public String getSocketFactory() { return socketFactoryName; }
+    public void setSocketFactory(String valueS) { socketFactoryName = valueS; }
+    
+    /**
+     * Name of the SSL implementation.
+     */
+    protected String sslImplementationName=null;
+    public String getSSLImplementation() { return sslImplementationName; }
+    public void setSSLImplementation( String valueS) {
+        sslImplementationName = valueS;
+        setSecure(true);
+    }
+    
+    
+    // HTTP
+    /**
+     * Maximum number of requests which can be performed over a keepalive 
+     * connection. The default is the same as for Apache HTTP Server.
+     */
+    protected int maxKeepAliveRequests = (org.apache.tomcat.util.Constants.LOW_MEMORY) ? 1 : 
+        Integer.valueOf(System.getProperty("org.apache.coyote.http11.Http11Protocol.MAX_KEEP_ALIVE_REQUESTS", "-1")).intValue();
+    public int getMaxKeepAliveRequests() { return maxKeepAliveRequests; }
+    public void setMaxKeepAliveRequests(int mkar) { maxKeepAliveRequests = mkar; }
+
+    // HTTP
+    /**
+     * The number of seconds Tomcat will wait for a subsequent request
+     * before closing the connection. The default is the same as for
+     * Apache HTTP Server (15 000 milliseconds).
+     */
+    protected int keepAliveTimeout = -1;
+    public int getKeepAliveTimeout() { return keepAliveTimeout; }
+    public void setKeepAliveTimeout(int timeout) { keepAliveTimeout = timeout; }
+
+    // HTTP
+    /**
+     * This timeout represents the socket timeout which will be used while
+     * the adapter execution is in progress, unless disableUploadTimeout
+     * is set to true. The default is the same as for Apache HTTP Server
+     * (300 000 milliseconds).
+     */
+    protected int timeout = 300000;
+    public int getTimeout() { return timeout; }
+    public void setTimeout(int timeout) { this.timeout = timeout; }
+
+
+    // *
+    /**
+     * Maximum size of the post which will be saved when processing certain
+     * requests, such as a POST.
+     */
+    protected int maxSavePostSize = 4 * 1024;
+    public int getMaxSavePostSize() { return maxSavePostSize; }
+    public void setMaxSavePostSize(int valueI) { maxSavePostSize = valueI; }
+
+
+    // HTTP
+    /**
+     * Maximum size of the HTTP message header.
+     */
+    protected int maxHttpHeaderSize = Integer.valueOf(System.getProperty("org.apache.coyote.http11.Http11Protocol.MAX_HEADER_SIZE", "8192")).intValue();
+    public int getMaxHttpHeaderSize() { return maxHttpHeaderSize; }
+    public void setMaxHttpHeaderSize(int valueI) { maxHttpHeaderSize = valueI; }
+
+
+    // HTTP
+    /**
+     * If true, the regular socket timeout will be used for the full duration
+     * of the connection.
+     */
+    protected boolean disableUploadTimeout = true;
+    public boolean getDisableUploadTimeout() { return disableUploadTimeout; }
+    public void setDisableUploadTimeout(boolean isDisabled) { disableUploadTimeout = isDisabled; }
+
+
+    // HTTP
+    /**
+     * Integrated compression support.
+     */
+    protected String compression = System.getProperty("org.apache.coyote.http11.Http11Protocol.COMPRESSION", "off");
+    public String getCompression() { return compression; }
+    public void setCompression(String valueS) { compression = valueS; }
+    
+    
+    // HTTP
+    protected String noCompressionUserAgents = System.getProperty("org.apache.coyote.http11.Http11Protocol.COMPRESSION_RESTRICTED_UA");
+    public String getNoCompressionUserAgents() { return noCompressionUserAgents; }
+    public void setNoCompressionUserAgents(String valueS) { noCompressionUserAgents = valueS; }
+
+    
+    // HTTP
+    protected String compressableMimeTypes = System.getProperty("org.apache.coyote.http11.Http11Protocol.COMPRESSION_MIME_TYPES", "text/html,text/xml,text/plain");
+    public String getCompressableMimeType() { return compressableMimeTypes; }
+    public void setCompressableMimeType(String valueS) { compressableMimeTypes = valueS; }
+    
+    
+    // HTTP
+    protected int compressionMinSize = Integer.valueOf(System.getProperty("org.apache.coyote.http11.Http11Protocol.COMPRESSION_MIN_SIZE", "2048")).intValue();
+    public int getCompressionMinSize() { return compressionMinSize; }
+    public void setCompressionMinSize(int valueI) { compressionMinSize = valueI; }
+
+
+    // HTTP
+    /**
+     * User agents regular expressions which should be restricted to HTTP/1.0 support.
+     */
+    protected String restrictedUserAgents = null;
+    public String getRestrictedUserAgents() { return restrictedUserAgents; }
+    public void setRestrictedUserAgents(String valueS) { restrictedUserAgents = valueS; }
+    
+    // HTTP
+    /**
+     * Server header.
+     */
+    protected String server = System.getProperty("org.apache.coyote.http11.Http11Protocol.SERVER");
+    public void setServer( String server ) { this.server = server; }
+    public String getServer() { return server; }
+
+    public Executor getExecutor() { return endpoint.getExecutor(); }
+    public void setExecutor(Executor executor) { endpoint.setExecutor(executor); }
+    
+    public int getMaxThreads() { return endpoint.getMaxThreads(); }
+    public void setMaxThreads(int maxThreads) { endpoint.setMaxThreads(maxThreads); }
+
+    public int getThreadPriority() { return endpoint.getThreadPriority(); }
+    public void setThreadPriority(int threadPriority) { endpoint.setThreadPriority(threadPriority); }
+
+    public int getBacklog() { return endpoint.getBacklog(); }
+    public void setBacklog(int backlog) { endpoint.setBacklog(backlog); }
+
+    public int getPort() { return endpoint.getPort(); }
+    public void setPort(int port) { endpoint.setPort(port); }
+
+    public InetAddress getAddress() { return endpoint.getAddress(); }
+    public void setAddress(InetAddress ia) { endpoint.setAddress(ia); }
+
+    public boolean getTcpNoDelay() { return endpoint.getTcpNoDelay(); }
+    public void setTcpNoDelay(boolean tcpNoDelay) { endpoint.setTcpNoDelay(tcpNoDelay); }
+
+    public int getSoLinger() { return endpoint.getSoLinger(); }
+    public void setSoLinger(int soLinger) { endpoint.setSoLinger(soLinger); }
+
+    public int getSoTimeout() { return endpoint.getSoTimeout(); }
+    public void setSoTimeout(int soTimeout) { endpoint.setSoTimeout(soTimeout); }
+
+    public void setPollerSize(int pollerSize) { endpoint.setPollerSize(pollerSize); }
+    public int getPollerSize() { return endpoint.getPollerSize(); }
+
+    // HTTP
+    /**
+     * Return the Keep-Alive policy for the connection.
+     */
+    public boolean getKeepAlive() {
+        return ((maxKeepAliveRequests != 0) && (maxKeepAliveRequests != 1));
+    }
+
+    // HTTP
+    /**
+     * Set the keep-alive policy for this connection.
+     */
+    public void setKeepAlive(boolean keepAlive) {
+        if (!keepAlive) {
+            setMaxKeepAliveRequests(1);
+        }
+    }
+
+    /*
+     * Note: All the following are JSSE/java.io specific attributes.
+     */
+    
+    public String getKeystore() {
+        return (String) getAttribute("keystore");
+    }
+
+    public void setKeystore( String k ) {
+        setAttribute("keystore", k);
+    }
+
+    public String getKeypass() {
+        return (String) getAttribute("keypass");
+    }
+
+    public void setKeypass( String k ) {
+        attributes.put("keypass", k);
+        //setAttribute("keypass", k);
+    }
+
+    public String getKeytype() {
+        return (String) getAttribute("keystoreType");
+    }
+
+    public void setKeytype( String k ) {
+        setAttribute("keystoreType", k);
+    }
+
+    public String getClientauth() {
+        return (String) getAttribute("clientauth");
+    }
+
+    public void setClientauth( String k ) {
+        setAttribute("clientauth", k);
+    }
+
+    public String getProtocols() {
+        return (String) getAttribute("protocols");
+    }
+
+    public void setProtocols(String k) {
+        setAttribute("protocols", k);
+    }
+
+    public String getAlgorithm() {
+        return (String) getAttribute("algorithm");
+    }
+
+    public void setAlgorithm( String k ) {
+        setAttribute("algorithm", k);
+    }
+
+    public String getCiphers() {
+        return (String) getAttribute("ciphers");
+    }
+
+    public void setCiphers(String ciphers) {
+        setAttribute("ciphers", ciphers);
+    }
+
+    public String getKeyAlias() {
+        return (String) getAttribute("keyAlias");
+    }
+
+    public void setKeyAlias(String keyAlias) {
+        setAttribute("keyAlias", keyAlias);
+    }
+
+    public SSLContext getSSLContext() {
+        return (SSLContext) getAttribute("SSLContext");
+    }
+
+    public void setSSLContext(SSLContext sslContext) {
+        setAttribute("SSLContext", sslContext);
+    }
+
+    // -----------------------------------  Http11ConnectionHandler Inner Class
+
+    protected static class Http11ConnectionHandler implements Handler {
+
+        protected Http11Protocol proto;
+        protected AtomicLong registerCount = new AtomicLong(0);
+        protected RequestGroupInfo global = new RequestGroupInfo();
+
+        protected ConcurrentHashMap<Socket, Http11Processor> connections =
+            new ConcurrentHashMap<Socket, Http11Processor>();
+        protected ConcurrentLinkedQueue<Http11Processor> recycledProcessors = 
+            new ConcurrentLinkedQueue<Http11Processor>() {
+            protected AtomicInteger size = new AtomicInteger(0);
+            public boolean offer(Http11Processor processor) {
+                boolean offer = (proto.processorCache == -1) ? true : (size.get() < proto.processorCache);
+                //avoid over growing our cache or add after we have stopped
+                boolean result = false;
+                if ( offer ) {
+                    result = super.offer(processor);
+                    if ( result ) {
+                        size.incrementAndGet();
+                    }
+                }
+                if (!result) unregister(processor);
+                return result;
+            }
+            
+            public Http11Processor poll() {
+                Http11Processor result = super.poll();
+                if ( result != null ) {
+                    size.decrementAndGet();
+                }
+                return result;
+            }
+            
+            public void clear() {
+                Http11Processor next = poll();
+                while ( next != null ) {
+                    unregister(next);
+                    next = poll();
+                }
+                super.clear();
+                size.set(0);
+            }
+        };
+
+        Http11ConnectionHandler(Http11Protocol proto) {
+            this.proto = proto;
+        }
+
+        public SocketState event(Socket socket, SocketStatus status) {
+            Http11Processor result = connections.get(socket);
+            SocketState state = SocketState.CLOSED; 
+            if (result != null) {
+                result.startProcessing();
+                // Call the appropriate event
+                try {
+                    state = result.event(status);
+                } catch (java.net.SocketException e) {
+                    // SocketExceptions are normal
+                    CoyoteLogger.HTTP_LOGGER.socketException(e);
+                } catch (java.io.IOException e) {
+                    // IOExceptions are normal
+                    CoyoteLogger.HTTP_LOGGER.socketException(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.
+                    CoyoteLogger.HTTP_LOGGER.socketError(e);
+                } finally {
+                    if (state != SocketState.LONG) {
+                        connections.remove(socket);
+                        recycledProcessors.offer(result);
+                    } else {
+                        if (proto.endpoint.isRunning()) {
+                            proto.endpoint.getEventPoller().add(socket, result.getTimeout(), 
+                                    result.getResumeNotification(), false);
+                        }
+                    }
+                    result.endProcessing();
+                }
+            }
+            return state;
+        }
+        
+        public SocketState process(Socket socket) {
+            Http11Processor processor = recycledProcessors.poll();
+            try {
+
+                if (processor == null) {
+                    processor = createProcessor();
+                }
+
+                if (proto.secure && (proto.sslImplementation != null)) {
+                    processor.setSSLSupport
+                        (proto.sslImplementation.getSSLSupport(socket));
+                } else {
+                    processor.setSSLSupport(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);
+                    proto.endpoint.getEventPoller().add(socket, processor.getTimeout(), 
+                            processor.getResumeNotification(), false);
+                } else {
+                    recycledProcessors.offer(processor);
+                }
+                return state;
+
+            } catch(java.net.SocketException e) {
+                // SocketExceptions are normal
+                CoyoteLogger.HTTP_LOGGER.socketException(e);
+            } catch (java.io.IOException e) {
+                // IOExceptions are normal
+                CoyoteLogger.HTTP_LOGGER.socketException(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.
+                CoyoteLogger.HTTP_LOGGER.socketError(e);
+            }
+            recycledProcessors.offer(processor);
+            return SocketState.CLOSED;
+        }
+        
+        protected Http11Processor createProcessor() {
+            Http11Processor processor =
+                new Http11Processor(proto.maxHttpHeaderSize, proto.endpoint);
+            processor.setAdapter(proto.adapter);
+            processor.setMaxKeepAliveRequests(proto.maxKeepAliveRequests);
+            processor.setKeepAliveTimeout(proto.keepAliveTimeout);
+            processor.setTimeout(proto.timeout);
+            processor.setDisableUploadTimeout(proto.disableUploadTimeout);
+            processor.setCompressionMinSize(proto.compressionMinSize);
+            processor.setCompression(proto.compression);
+            processor.setNoCompressionUserAgents(proto.noCompressionUserAgents);
+            processor.setCompressableMimeTypes(proto.compressableMimeTypes);
+            processor.setRestrictedUserAgents(proto.restrictedUserAgents);
+            processor.setSocketBuffer(proto.socketBuffer);
+            processor.setMaxSavePostSize(proto.maxSavePostSize);
+            processor.setServer(proto.server);
+            register(processor);
+            return processor;
+        }
+        
+        protected void register(Http11Processor processor) {
+            RequestInfo rp = processor.getRequest().getRequestProcessor();
+            rp.setGlobalProcessor(global);
+            if (org.apache.tomcat.util.Constants.ENABLE_MODELER && proto.getDomain() != null) {
+                synchronized (this) {
+                    try {
+                        long count = registerCount.incrementAndGet();
+                        ObjectName rpName = new ObjectName
+                            (proto.getDomain() + ":type=RequestProcessor,worker="
+                                + proto.getJmxName() + ",name=HttpRequest" + count);
+                        Registry.getRegistry(null, null).registerComponent(rp, rpName, null);
+                        rp.setRpName(rpName);
+                    } catch (Exception e) {
+                        CoyoteLogger.HTTP_LOGGER.errorRegisteringRequest(e);
+                    }
+                }
+            }
+        }
+
+        protected void unregister(Http11Processor processor) {
+            RequestInfo rp = processor.getRequest().getRequestProcessor();
+            rp.setGlobalProcessor(null);
+            if (org.apache.tomcat.util.Constants.ENABLE_MODELER && proto.getDomain() != null) {
+                synchronized (this) {
+                    try {
+                        ObjectName rpName = rp.getRpName();
+                        Registry.getRegistry(null, null).unregisterComponent(rpName);
+                        rp.setRpName(null);
+                    } catch (Exception e) {
+                        CoyoteLogger.HTTP_LOGGER.errorUnregisteringRequest(e);
+                    }
+                }
+            }
+        }
+
+    }
+
+
+    // -------------------- JMX related methods --------------------
+
+    // *
+    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() {
+    }
+}

Added: branches/7.2.x/src/main/java/org/apache/coyote/http11/InternalInputBuffer.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/coyote/http11/InternalInputBuffer.java	                        (rev 0)
+++ branches/7.2.x/src/main/java/org/apache/coyote/http11/InternalInputBuffer.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -0,0 +1,783 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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 static org.jboss.web.CoyoteMessages.MESSAGES;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.EOFException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.Request;
+
+/**
+ * Implementation of InputBuffer which provides HTTP request header parsing as
+ * well as transfer decoding.
+ *
+ * @author <a href="mailto:remm at apache.org">Remy Maucherat</a>
+ */
+public class InternalInputBuffer implements InputBuffer {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor.
+     */
+    public InternalInputBuffer(Request request) {
+        this(request, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
+    }
+
+
+    /**
+     * Alternate constructor.
+     */
+    public InternalInputBuffer(Request request, int headerBufferSize) {
+
+        this.request = request;
+        headers = request.getMimeHeaders();
+
+        buf = new byte[headerBufferSize];
+
+        inputStreamInputBuffer = new InputStreamInputBuffer();
+
+        filterLibrary = new InputFilter[0];
+        activeFilters = new InputFilter[0];
+        lastActiveFilter = -1;
+
+        parsingHeader = true;
+        swallowInput = true;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated Coyote request.
+     */
+    protected Request request;
+
+
+    /**
+     * Headers of the associated request.
+     */
+    protected MimeHeaders headers;
+
+
+    /**
+     * State.
+     */
+    protected boolean parsingHeader;
+
+
+    /**
+     * Swallow input ? (in the case of an expectation)
+     */
+    protected boolean swallowInput;
+
+
+    /**
+     * Pointer to the current read buffer.
+     */
+    protected byte[] buf;
+
+
+    /**
+     * Last valid byte.
+     */
+    protected int lastValid;
+
+
+    /**
+     * Position in the buffer.
+     */
+    protected int pos;
+
+
+    /**
+     * Pos of the end of the header in the buffer, which is also the
+     * start of the body.
+     */
+    protected int end;
+
+
+    /**
+     * Underlying input stream.
+     */
+    protected InputStream inputStream;
+
+
+    /**
+     * Underlying input buffer.
+     */
+    protected InputBuffer inputStreamInputBuffer;
+
+
+    /**
+     * Filter library.
+     * Note: Filter[0] is always the "chunked" filter.
+     */
+    protected InputFilter[] filterLibrary;
+
+
+    /**
+     * Active filters (in order).
+     */
+    protected InputFilter[] activeFilters;
+
+
+    /**
+     * Index of the last active filter.
+     */
+    protected int lastActiveFilter;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the underlying socket input stream.
+     */
+    public void setInputStream(InputStream inputStream) {
+
+        // FIXME: Check for null ?
+
+        this.inputStream = inputStream;
+
+    }
+
+
+    /**
+     * Get the underlying socket input stream.
+     */
+    public InputStream getInputStream() {
+
+        return inputStream;
+
+    }
+
+
+    /**
+     * Add an input filter to the filter library.
+     */
+    public void addFilter(InputFilter filter) {
+
+        // FIXME: Check for null ?
+
+        InputFilter[] newFilterLibrary = 
+            new InputFilter[filterLibrary.length + 1];
+        for (int i = 0; i < filterLibrary.length; i++) {
+            newFilterLibrary[i] = filterLibrary[i];
+        }
+        newFilterLibrary[filterLibrary.length] = filter;
+        filterLibrary = newFilterLibrary;
+
+        activeFilters = new InputFilter[filterLibrary.length];
+
+    }
+
+
+    /**
+     * Get filters.
+     */
+    public InputFilter[] getFilters() {
+
+        return filterLibrary;
+
+    }
+
+
+    /**
+     * Clear filters.
+     */
+    public void clearFilters() {
+
+        filterLibrary = new InputFilter[0];
+        lastActiveFilter = -1;
+
+    }
+
+
+    /**
+     * Add an input filter to the filter library.
+     */
+    public void addActiveFilter(InputFilter filter) {
+
+        if (lastActiveFilter == -1) {
+            filter.setBuffer(inputStreamInputBuffer);
+        } else {
+            for (int i = 0; i <= lastActiveFilter; i++) {
+                if (activeFilters[i] == filter)
+                    return;
+            }
+            filter.setBuffer(activeFilters[lastActiveFilter]);
+        }
+
+        activeFilters[++lastActiveFilter] = filter;
+
+        filter.setRequest(request);
+
+    }
+
+
+    /**
+     * Set the swallow input flag.
+     */
+    public void setSwallowInput(boolean swallowInput) {
+        this.swallowInput = swallowInput;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Recycle the input buffer. This should be called when closing the 
+     * connection.
+     */
+    public void recycle() {
+
+        // Recycle Request object
+        request.recycle();
+
+        inputStream = null;
+        lastValid = 0;
+        pos = 0;
+        lastActiveFilter = -1;
+        parsingHeader = true;
+        swallowInput = true;
+
+    }
+
+
+    /**
+     * End processing of current HTTP request.
+     * Note: All bytes of the current request should have been already 
+     * consumed. This method only resets all the pointers so that we are ready
+     * to parse the next HTTP request.
+     */
+    public boolean nextRequest() {
+
+        // Recycle Request object
+        request.recycle();
+
+        // Copy leftover bytes to the beginning of the buffer
+        if (lastValid - pos > 0) {
+            int npos = 0;
+            int opos = pos;
+            while (lastValid - opos > opos - npos) {
+                System.arraycopy(buf, opos, buf, npos, opos - npos);
+                npos += pos;
+                opos += pos;
+            }
+            System.arraycopy(buf, opos, buf, npos, lastValid - opos);
+        }
+
+        // Recycle filters
+        for (int i = 0; i <= lastActiveFilter; i++) {
+            activeFilters[i].recycle();
+        }
+
+        // Reset pointers
+        lastValid = lastValid - pos;
+        pos = 0;
+        lastActiveFilter = -1;
+        parsingHeader = true;
+        swallowInput = true;
+
+        return (lastValid > 0);
+        
+    }
+
+
+    /**
+     * End request (consumes leftover bytes).
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    public void endRequest()
+        throws IOException {
+
+        if (swallowInput && (lastActiveFilter != -1)) {
+            int extraBytes = (int) activeFilters[lastActiveFilter].end();
+            pos = pos - extraBytes;
+        }
+
+    }
+
+
+    /**
+     * Read the request line. This function is meant to be used during the 
+     * HTTP request header parsing. Do NOT attempt to read the request body 
+     * using it.
+     *
+     * @throws IOException If an exception occurs during the underlying socket
+     * read operations, or if the given buffer is not big enough to accomodate
+     * the whole line.
+     */
+    public void parseRequestLine()
+        throws IOException {
+
+        int start = 0;
+
+        //
+        // Skipping blank lines
+        //
+
+        byte chr = 0;
+        do {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(MESSAGES.eofError());
+            }
+
+            chr = buf[pos++];
+
+        } while ((chr == Constants.CR) || (chr == Constants.LF));
+
+        pos--;
+
+        // Mark the current buffer position
+        start = pos;
+
+        //
+        // Reading the method name
+        // Method name is always US-ASCII
+        //
+
+        boolean space = false;
+
+        while (!space) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(MESSAGES.eofError());
+            }
+
+            // Spec says single SP but it also says be tolerant of HT
+            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+                space = true;
+                request.method().setBytes(buf, start, pos - start);
+            }
+
+            pos++;
+
+        }
+
+        // Spec says single SP but also says be tolerant of multiple and/or HT
+        while (space) {
+        	// Read new bytes if needed
+        	if (pos >= lastValid) {
+        		if (!fill())
+        			throw new EOFException(MESSAGES.eofError());
+        	}
+        	if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+        		pos++;
+        	} else {
+        		space = false;
+        	}
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        int end = 0;
+        int questionPos = -1;
+
+        //
+        // Reading the URI
+        //
+
+        boolean eol = false;
+
+        while (!space) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(MESSAGES.eofError());
+            }
+
+            // Spec says single SP but it also says be tolerant of HT
+            if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+                space = true;
+                end = pos;
+            } else if ((buf[pos] == Constants.CR) 
+                       || (buf[pos] == Constants.LF)) {
+                // HTTP/0.9 style request
+                eol = true;
+                space = true;
+                end = pos;
+            } else if ((buf[pos] == Constants.QUESTION) 
+                       && (questionPos == -1)) {
+                questionPos = pos;
+            }
+
+            pos++;
+
+        }
+
+        request.unparsedURI().setBytes(buf, start, end - start);
+        if (questionPos >= 0) {
+            request.queryString().setBytes(buf, questionPos + 1, 
+                                           end - questionPos - 1);
+            request.requestURI().setBytes(buf, start, questionPos - start);
+        } else {
+            request.requestURI().setBytes(buf, start, end - start);
+        }
+
+        // Spec says single SP but also says be tolerant of multiple and/or HT
+        while (space) {
+        	// Read new bytes if needed
+        	if (pos >= lastValid) {
+        		if (!fill())
+        			throw new EOFException(MESSAGES.eofError());
+        	}
+        	if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+        		pos++;
+        	} else {
+        		space = false;
+        	}
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        end = 0;
+
+        //
+        // Reading the protocol
+        // Protocol is always US-ASCII
+        //
+
+        while (!eol) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(MESSAGES.eofError());
+            }
+
+            if (buf[pos] == Constants.CR) {
+                end = pos;
+            } else if (buf[pos] == Constants.LF) {
+                if (end == 0)
+                    end = pos;
+                eol = true;
+            }
+
+            pos++;
+
+        }
+
+        if ((end - start) > 0) {
+            request.protocol().setBytes(buf, start, end - start);
+        } else {
+            request.protocol().setString("");
+        }
+
+    }
+
+
+    /**
+     * Parse the HTTP headers.
+     */
+    public void parseHeaders()
+        throws IOException {
+
+        while (parseHeader()) {
+        }
+
+        parsingHeader = false;
+        end = pos;
+
+    }
+
+
+    /**
+     * Parse an HTTP header.
+     * 
+     * @return false after reading a blank line (which indicates that the
+     * HTTP header parsing is done
+     */
+    public boolean parseHeader()
+        throws IOException {
+
+        //
+        // Check for blank line
+        //
+
+        byte chr = 0;
+        while (true) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(MESSAGES.eofError());
+            }
+
+            chr = buf[pos];
+
+            if ((chr == Constants.CR) || (chr == Constants.LF)) {
+                if (chr == Constants.LF) {
+                    pos++;
+                    return false;
+                }
+            } else {
+                break;
+            }
+
+            pos++;
+
+        }
+
+        // Mark the current buffer position
+        int start = pos;
+
+        //
+        // Reading the header name
+        // Header name is always US-ASCII
+        //
+
+        boolean colon = false;
+        MessageBytes headerValue = null;
+
+        while (!colon) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(MESSAGES.eofError());
+            }
+
+            if (buf[pos] == Constants.COLON) {
+                colon = true;
+                headerValue = headers.addValue(buf, start, pos - start);
+            }
+            chr = buf[pos];
+            if ((chr >= Constants.A) && (chr <= Constants.Z)) {
+                buf[pos] = (byte) (chr - Constants.LC_OFFSET);
+            }
+
+            pos++;
+
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        int realPos = pos;
+
+        //
+        // Reading the header value (which can be spanned over multiple lines)
+        //
+
+        boolean eol = false;
+        boolean validLine = true;
+
+        while (validLine) {
+
+            boolean space = true;
+
+            // Skipping spaces
+            while (space) {
+
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    if (!fill())
+                        throw new EOFException(MESSAGES.eofError());
+                }
+
+                if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
+                    pos++;
+                } else {
+                    space = false;
+                }
+
+            }
+
+            int lastSignificantChar = realPos;
+
+            // Reading bytes until the end of the line
+            while (!eol) {
+
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    if (!fill())
+                        throw new EOFException(MESSAGES.eofError());
+                }
+
+                if (buf[pos] == Constants.CR) {
+                } else if (buf[pos] == Constants.LF) {
+                    eol = true;
+                } else if (buf[pos] == Constants.SP) {
+                    buf[realPos] = buf[pos];
+                    realPos++;
+                } else {
+                    buf[realPos] = buf[pos];
+                    realPos++;
+                    lastSignificantChar = realPos;
+                }
+
+                pos++;
+
+            }
+
+            realPos = lastSignificantChar;
+
+            // Checking the first character of the new line. If the character
+            // is a LWS, then it's a multiline header
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(MESSAGES.eofError());
+            }
+
+            chr = buf[pos];
+            if ((chr != Constants.SP) && (chr != Constants.HT)) {
+                validLine = false;
+            } else {
+                eol = false;
+                // Copying one extra space in the buffer (since there must
+                // be at least one space inserted between the lines)
+                buf[realPos] = chr;
+                realPos++;
+            }
+
+        }
+
+        // Set the header value
+        headerValue.setBytes(buf, start, realPos - start);
+
+        return true;
+
+    }
+
+
+    // ---------------------------------------------------- InputBuffer Methods
+
+
+    /**
+     * Read some bytes.
+     */
+    public int doRead(ByteChunk chunk, Request req) 
+        throws IOException {
+
+        if (lastActiveFilter == -1)
+            return inputStreamInputBuffer.doRead(chunk, req);
+        else
+            return activeFilters[lastActiveFilter].doRead(chunk,req);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Fill the internal buffer using data from the undelying input stream.
+     * 
+     * @return false if at end of stream
+     */
+    protected boolean fill()
+        throws IOException {
+
+        int nRead = 0;
+
+        if (parsingHeader) {
+
+            if (lastValid == buf.length) {
+                throw MESSAGES.requestHeaderTooLarge();
+            }
+
+            nRead = inputStream.read(buf, pos, buf.length - lastValid);
+            if (nRead > 0) {
+                lastValid = pos + nRead;
+            }
+
+        } else {
+
+            if (buf.length - end < 4500) {
+                // In this case, the request header was really large, so we allocate a 
+                // brand new one; the old one will get GCed when subsequent requests
+                // clear all references
+                buf = new byte[buf.length];
+                end = 0;
+            }
+            pos = end;
+            lastValid = pos;
+            nRead = inputStream.read(buf, pos, buf.length - lastValid);
+            if (nRead > 0) {
+                lastValid = pos + nRead;
+            }
+
+        }
+
+        return (nRead > 0);
+
+    }
+
+
+    // ------------------------------------- InputStreamInputBuffer Inner Class
+
+
+    /**
+     * This class is an input buffer which will read its data from an input
+     * stream.
+     */
+    protected class InputStreamInputBuffer 
+        implements InputBuffer {
+
+
+        /**
+         * Read bytes into the specified chunk.
+         */
+        public int doRead(ByteChunk chunk, Request req ) 
+            throws IOException {
+
+            if (pos >= lastValid) {
+                if (!fill())
+                    return -1;
+            }
+
+            int length = lastValid - pos;
+            chunk.setBytes(buf, pos, length);
+            pos = lastValid;
+
+            return (length);
+
+        }
+
+
+    }
+
+
+}

Added: branches/7.2.x/src/main/java/org/apache/coyote/http11/InternalOutputBuffer.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/coyote/http11/InternalOutputBuffer.java	                        (rev 0)
+++ branches/7.2.x/src/main/java/org/apache/coyote/http11/InternalOutputBuffer.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -0,0 +1,749 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.OutputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.HttpMessages;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+
+/**
+ * Output buffer.
+ * 
+ * @author <a href="mailto:remm at apache.org">Remy Maucherat</a>
+ */
+public class InternalOutputBuffer 
+    implements OutputBuffer, ByteChunk.ByteOutputChannel {
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor.
+     */
+    public InternalOutputBuffer(Response response) {
+        this(response, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
+    }
+
+
+    /**
+     * Alternate constructor.
+     */
+    public InternalOutputBuffer(Response response, int headerBufferSize) {
+
+        this.response = response;
+        
+        headers = response.getMimeHeaders();
+
+        buf = new byte[headerBufferSize];
+
+        outputStreamOutputBuffer = new OutputStreamOutputBuffer();
+
+        filterLibrary = new OutputFilter[0];
+        activeFilters = new OutputFilter[0];
+        lastActiveFilter = -1;
+
+        socketBuffer = new ByteChunk();
+        socketBuffer.setByteOutputChannel(this);
+
+        committed = false;
+        finished = false;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated Coyote response.
+     */
+    protected Response response;
+
+
+    /**
+     * Headers of the associated request.
+     */
+    protected MimeHeaders headers;
+
+
+    /**
+     * Committed flag.
+     */
+    protected boolean committed;
+
+
+    /**
+     * Finished flag.
+     */
+    protected boolean finished;
+
+
+    /**
+     * The buffer used for header composition.
+     */
+    protected byte[] buf;
+
+
+    /**
+     * Position in the buffer.
+     */
+    protected int pos;
+
+
+    /**
+     * Underlying output stream.
+     */
+    protected OutputStream outputStream;
+
+
+    /**
+     * Underlying output buffer.
+     */
+    protected OutputBuffer outputStreamOutputBuffer;
+
+
+    /**
+     * Filter library.
+     * Note: Filter[0] is always the "chunked" filter.
+     */
+    protected OutputFilter[] filterLibrary;
+
+
+    /**
+     * Active filter (which is actually the top of the pipeline).
+     */
+    protected OutputFilter[] activeFilters;
+
+
+    /**
+     * Index of the last active filter.
+     */
+    protected int lastActiveFilter;
+
+
+    /**
+     * Socket buffer.
+     */
+    protected ByteChunk socketBuffer;
+
+
+    /**
+     * Socket buffer (extra buffering to reduce number of packets sent).
+     */
+    protected boolean useSocketBuffer = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the underlying socket output stream.
+     */
+    public void setOutputStream(OutputStream outputStream) {
+
+        // FIXME: Check for null ?
+
+        this.outputStream = outputStream;
+
+    }
+
+
+    /**
+     * Get the underlying socket output stream.
+     */
+    public OutputStream getOutputStream() {
+
+        return outputStream;
+
+    }
+
+
+    /**
+     * Set the socket buffer size.
+     */
+    public void setSocketBuffer(int socketBufferSize) {
+
+        if (socketBufferSize > 500) {
+            useSocketBuffer = true;
+            socketBuffer.allocate(socketBufferSize, socketBufferSize);
+        } else {
+            useSocketBuffer = false;
+        }
+
+    }
+
+
+    /**
+     * Add an output filter to the filter library.
+     */
+    public void addFilter(OutputFilter filter) {
+
+        OutputFilter[] newFilterLibrary = 
+            new OutputFilter[filterLibrary.length + 1];
+        for (int i = 0; i < filterLibrary.length; i++) {
+            newFilterLibrary[i] = filterLibrary[i];
+        }
+        newFilterLibrary[filterLibrary.length] = filter;
+        filterLibrary = newFilterLibrary;
+
+        activeFilters = new OutputFilter[filterLibrary.length];
+
+    }
+
+
+    /**
+     * Get filters.
+     */
+    public OutputFilter[] getFilters() {
+
+        return filterLibrary;
+
+    }
+
+
+    /**
+     * Clear filters.
+     */
+    public void clearFilters() {
+
+        filterLibrary = new OutputFilter[0];
+        lastActiveFilter = -1;
+
+    }
+
+
+    /**
+     * Add an output filter to the filter library.
+     */
+    public void addActiveFilter(OutputFilter filter) {
+
+        if (lastActiveFilter == -1) {
+            filter.setBuffer(outputStreamOutputBuffer);
+        } else {
+            for (int i = 0; i <= lastActiveFilter; i++) {
+                if (activeFilters[i] == filter)
+                    return;
+            }
+            filter.setBuffer(activeFilters[lastActiveFilter]);
+        }
+
+        activeFilters[++lastActiveFilter] = filter;
+
+        filter.setResponse(response);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Flush the response.
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    public void flush()
+        throws IOException {
+
+        if (!committed) {
+
+            // Send the connector a request for commit. The connector should
+            // then validate the headers, send them (using sendHeader) and 
+            // set the filters accordingly.
+            response.action(ActionCode.ACTION_COMMIT, null);
+
+        }
+
+        // Flush the current buffer
+        if (useSocketBuffer) {
+            socketBuffer.flushBuffer();
+        }
+
+    }
+
+
+    /**
+     * Recycle the output buffer. This should be called when closing the 
+     * connection.
+     */
+    public void recycle() {
+
+        // Recycle Request object
+        response.recycle();
+        socketBuffer.recycle();
+
+        outputStream = null;
+        pos = 0;
+        lastActiveFilter = -1;
+        committed = false;
+        finished = false;
+
+    }
+
+
+    /**
+     * End processing of current HTTP request.
+     * Note: All bytes of the current request should have been already 
+     * consumed. This method only resets all the pointers so that we are ready
+     * to parse the next HTTP request.
+     */
+    public void nextRequest() {
+
+        // Recycle Request object
+        response.recycle();
+        socketBuffer.recycle();
+
+        // Recycle filters
+        for (int i = 0; i <= lastActiveFilter; i++) {
+            activeFilters[i].recycle();
+        }
+
+        // Reset pointers
+        pos = 0;
+        lastActiveFilter = -1;
+        committed = false;
+        finished = false;
+
+    }
+
+
+    /**
+     * End request.
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    public void endRequest()
+        throws IOException {
+
+        if (!committed) {
+
+            // Send the connector a request for commit. The connector should
+            // then validate the headers, send them (using sendHeader) and 
+            // set the filters accordingly.
+            response.action(ActionCode.ACTION_COMMIT, null);
+
+        }
+
+        if (finished)
+            return;
+
+        if (lastActiveFilter != -1)
+            activeFilters[lastActiveFilter].end();
+
+        if (useSocketBuffer) {
+            socketBuffer.flushBuffer();
+        }
+
+        finished = true;
+
+    }
+
+
+    // ------------------------------------------------ HTTP/1.1 Output Methods
+
+
+    /**
+     * Send an acknoledgement.
+     */
+    public void sendAck()
+        throws IOException {
+
+        if (!committed)
+            outputStream.write(Constants.ACK_BYTES);
+
+    }
+
+
+    /**
+     * Send the response status line.
+     */
+    public void sendStatus() {
+
+        // Write protocol name
+        write(Constants.HTTP_11_BYTES);
+        buf[pos++] = Constants.SP;
+
+        // Write status code
+        int status = response.getStatus();
+        switch (status) {
+        case 200:
+            write(Constants._200_BYTES);
+            break;
+        case 400:
+            write(Constants._400_BYTES);
+            break;
+        case 404:
+            write(Constants._404_BYTES);
+            break;
+        default:
+            write(status);
+        }
+
+        buf[pos++] = Constants.SP;
+
+        // Write message
+        String message = null;
+        if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER) {
+            message = response.getMessage();
+        }
+        if (message == null) {
+            write(getMessage(status));
+        } else {
+            write(message.replace('\n', ' ').replace('\r', ' '));
+        }
+
+        // End the response status line
+        if (org.apache.coyote.Constants.IS_SECURITY_ENABLED){
+           AccessController.doPrivileged(
+                new PrivilegedAction(){
+                    public Object run(){
+                        buf[pos++] = Constants.CR;
+                        buf[pos++] = Constants.LF;
+                        return null;
+                    }
+                }
+           );
+        } else {
+            buf[pos++] = Constants.CR;
+            buf[pos++] = Constants.LF;
+        }
+
+    }
+
+    private String getMessage(final int message){
+        if (org.apache.coyote.Constants.IS_SECURITY_ENABLED){
+           return (String)AccessController.doPrivileged(
+                new PrivilegedAction(){
+                    public Object run(){
+                        return HttpMessages.getMessage(message); 
+                    }
+                }
+           );
+        } else {
+            return HttpMessages.getMessage(message);
+        }
+    }
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(MessageBytes name, MessageBytes value) {
+
+        if (name.getLength() > 0 && !value.isNull()) {
+            write(name);
+            buf[pos++] = Constants.COLON;
+            buf[pos++] = Constants.SP;
+            write(value);
+            buf[pos++] = Constants.CR;
+            buf[pos++] = Constants.LF;
+        }
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(ByteChunk name, ByteChunk value) {
+
+        write(name);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(String name, String value) {
+
+        write(name);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * End the header block.
+     */
+    public void endHeaders() {
+
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    // --------------------------------------------------- OutputBuffer Methods
+
+
+    /**
+     * Write the contents of a byte chunk.
+     * 
+     * @param chunk byte chunk
+     * @return number of bytes written
+     * @throws IOException an undelying I/O error occured
+     */
+    public int doWrite(ByteChunk chunk, Response res) 
+        throws IOException {
+
+        if (!committed) {
+
+            // Send the connector a request for commit. The connector should
+            // then validate the headers, send them (using sendHeaders) and 
+            // set the filters accordingly.
+            response.action(ActionCode.ACTION_COMMIT, null);
+
+        }
+
+        if (lastActiveFilter == -1)
+            return outputStreamOutputBuffer.doWrite(chunk, res);
+        else
+            return activeFilters[lastActiveFilter].doWrite(chunk, res);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Commit the response.
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    protected void commit()
+        throws IOException {
+
+        // The response is now committed
+        committed = true;
+        response.setCommitted(true);
+
+        if (pos > 0) {
+            // Sending the response header buffer
+            if (useSocketBuffer) {
+                socketBuffer.append(buf, 0, pos);
+            } else {
+                outputStream.write(buf, 0, pos);
+            }
+        }
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied message bytes 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param mb data to be written
+     */
+    protected void write(MessageBytes mb) {
+
+        if (mb.getType() == MessageBytes.T_BYTES) {
+            ByteChunk bc = mb.getByteChunk();
+            write(bc);
+        } else if (mb.getType() == MessageBytes.T_CHARS) {
+            CharChunk cc = mb.getCharChunk();
+            write(cc);
+        } else {
+            write(mb.toString());
+        }
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied message bytes 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param bc data to be written
+     */
+    protected void write(ByteChunk bc) {
+
+        // Writing the byte chunk to the output buffer
+        int length = bc.getLength();
+        System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos, length);
+        pos = pos + length;
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied char 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param cc data to be written
+     */
+    protected void write(CharChunk cc) {
+
+        int start = cc.getStart();
+        int end = cc.getEnd();
+        char[] cbuf = cc.getBuffer();
+        for (int i = start; i < end; i++) {
+            char c = cbuf[i];
+            // Note:  This is clearly incorrect for many strings,
+            // but is the only consistent approach within the current
+            // servlet framework.  It must suffice until servlet output
+            // streams properly encode their output.
+            if (((c <= 31) && (c != 9)) || c == 127 || c > 255) {
+                c = ' ';
+            }
+            buf[pos++] = (byte) c;
+        }
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied byte 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param b data to be written
+     */
+    public void write(byte[] b) {
+
+        // Writing the byte chunk to the output buffer
+        System.arraycopy(b, 0, buf, pos, b.length);
+        pos = pos + b.length;
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied String to the 
+     * output stream, without filtering. This method is meant to be used to 
+     * write the response header.
+     * 
+     * @param s data to be written
+     */
+    protected void write(String s) {
+
+        if (s == null)
+            return;
+
+        // From the Tomcat 3.3 HTTP/1.0 connector
+        int len = s.length();
+        for (int i = 0; i < len; i++) {
+            char c = s.charAt (i);
+            // Note:  This is clearly incorrect for many strings,
+            // but is the only consistent approach within the current
+            // servlet framework.  It must suffice until servlet output
+            // streams properly encode their output.
+            if (((c <= 31) && (c != 9)) || c == 127 || c > 255) {
+                c = ' ';
+            }
+            buf[pos++] = (byte) c;
+        }
+
+    }
+
+
+    /**
+     * This method will print the specified integer to the output stream, 
+     * without filtering. This method is meant to be used to write the 
+     * response header.
+     * 
+     * @param i data to be written
+     */
+    protected void write(int i) {
+
+        write(String.valueOf(i));
+
+    }
+
+
+    /**
+     * Callback to write data from the buffer.
+     */
+    public void realWriteBytes(byte cbuf[], int off, int len)
+        throws IOException {
+        if (len > 0) {
+            outputStream.write(cbuf, off, len);
+        }
+    }
+
+
+    // ----------------------------------- OutputStreamOutputBuffer Inner Class
+
+
+    /**
+     * This class is an output buffer which will write data to an output
+     * stream.
+     */
+    protected class OutputStreamOutputBuffer 
+        implements OutputBuffer {
+
+
+        /**
+         * Write chunk.
+         */
+        public int doWrite(ByteChunk chunk, Response res) 
+            throws IOException {
+
+            int length = chunk.getLength();
+            if (useSocketBuffer) {
+                socketBuffer.append(chunk.getBuffer(), chunk.getStart(), 
+                                    length);
+            } else {
+                outputStream.write(chunk.getBuffer(), chunk.getStart(), 
+                                   length);
+            }
+            return length;
+
+        }
+
+
+    }
+
+
+}

Added: branches/7.2.x/src/main/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java	                        (rev 0)
+++ branches/7.2.x/src/main/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -0,0 +1,70 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.tomcat.util.net;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * Default server socket factory. Doesn't do much except give us
+ * plain ol' server sockets.
+ *
+ * @author db at eng.sun.com
+ * @author Harish Prabandham
+ */
+
+// Default implementation of server sockets.
+
+//
+// WARNING: Some of the APIs in this class are used by J2EE. 
+// Please talk to harishp at eng.sun.com before making any changes.
+//
+class DefaultServerSocketFactory extends ServerSocketFactory {
+
+    DefaultServerSocketFactory () {
+        /* NOTHING */
+    }
+
+    public ServerSocket createSocket (int port)
+    throws IOException {
+        return  new ServerSocket (port);
+    }
+
+    public ServerSocket createSocket (int port, int backlog)
+    throws IOException {
+        return new ServerSocket (port, backlog);
+    }
+
+    public ServerSocket createSocket (int port, int backlog,
+        InetAddress ifAddress)
+    throws IOException {
+        return new ServerSocket (port, backlog, ifAddress);
+    }
+ 
+    public Socket acceptSocket(ServerSocket socket)
+ 	throws IOException {
+ 	return socket.accept();
+    }
+ 
+    public void handshake(Socket sock)
+ 	throws IOException {
+ 	; // NOOP
+    }
+ 	    
+        
+ }

Added: branches/7.2.x/src/main/java/org/apache/tomcat/util/net/JIoEndpoint.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/tomcat/util/net/JIoEndpoint.java	                        (rev 0)
+++ branches/7.2.x/src/main/java/org/apache/tomcat/util/net/JIoEndpoint.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -0,0 +1,1315 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.apache.tomcat.util.net;
+
+import java.io.IOException;
+import java.net.BindException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.concurrent.Executor;
+
+import org.jboss.web.CoyoteLogger;
+
+/**
+ * Handle incoming TCP connections.
+ *
+ * This class implement a simple server model: one listener thread accepts on a socket and
+ * creates a new worker thread for each incoming connection.
+ *
+ * More advanced Endpoints will reuse the threads, use queues, etc.
+ *
+ * @author James Duncan Davidson
+ * @author Jason Hunter
+ * @author James Todd
+ * @author Costin Manolache
+ * @author Gal Shachor
+ * @author Yoav Shapira
+ * @author Remy Maucherat
+ */
+public class JIoEndpoint {
+
+
+    // ----------------------------------------------------------------- Fields
+
+
+    /**
+     * Available workers.
+     */
+    protected WorkerStack workers = null;
+
+
+    /**
+     * Running state of the endpoint.
+     */
+    protected volatile boolean running = false;
+
+
+    /**
+     * Will be set to true whenever the endpoint is paused.
+     */
+    protected volatile boolean paused = false;
+
+
+    /**
+     * Track the initialization state of the endpoint.
+     */
+    protected boolean initialized = false;
+
+
+    /**
+     * Current worker threads busy count.
+     */
+    protected int curThreadsBusy = 0;
+
+
+    /**
+     * Current worker threads count.
+     */
+    protected int curThreads = 0;
+
+
+    /**
+     * Sequence number used to generate thread names.
+     */
+    protected int sequence = 0;
+
+
+    /**
+     * Associated server socket.
+     */
+    protected ServerSocket serverSocket = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Acceptor thread count.
+     */
+    protected int acceptorThreadCount = 0;
+    public void setAcceptorThreadCount(int acceptorThreadCount) { this.acceptorThreadCount = acceptorThreadCount; }
+    public int getAcceptorThreadCount() { return acceptorThreadCount; }
+
+
+    /**
+     * External Executor based thread pool.
+     */
+    protected Executor executor = null;
+    public void setExecutor(Executor executor) { this.executor = executor; }
+    public Executor getExecutor() { return executor; }
+
+
+    /**
+     * Maximum amount of worker threads.
+     */
+    protected int maxThreads = (org.apache.tomcat.util.Constants.LOW_MEMORY) ? 64 : ((Constants.MAX_THREADS == -1) ? 512 * Runtime.getRuntime().availableProcessors() : Constants.MAX_THREADS);
+    public void setMaxThreads(int maxThreads) { this.maxThreads = maxThreads; }
+    public int getMaxThreads() { return maxThreads; }
+
+
+    /**
+     * Priority of the acceptor and poller threads.
+     */
+    protected int threadPriority = Thread.NORM_PRIORITY;
+    public void setThreadPriority(int threadPriority) { this.threadPriority = threadPriority; }
+    public int getThreadPriority() { return threadPriority; }
+
+    
+    /**
+     * Size of the socket poller.
+     */
+    protected int pollerSize = (org.apache.tomcat.util.Constants.LOW_MEMORY) ? 128 : (32 * 1024);
+    public void setPollerSize(int pollerSize) { this.pollerSize = pollerSize; }
+    public int getPollerSize() { return pollerSize; }
+
+
+    /**
+     * Keep-Alive timeout.
+     */
+    protected int keepAliveTimeout = -1;
+    public int getKeepAliveTimeout() { return keepAliveTimeout; }
+    public void setKeepAliveTimeout(int keepAliveTimeout) { this.keepAliveTimeout = keepAliveTimeout; }
+
+
+    /**
+     * Server socket port.
+     */
+    protected int port;
+    public int getPort() { return port; }
+    public void setPort(int port ) { this.port=port; }
+
+
+    /**
+     * Address for the server socket.
+     */
+    protected InetAddress address;
+    public InetAddress getAddress() { return address; }
+    public void setAddress(InetAddress address) { this.address = address; }
+
+
+    /**
+     * Handling of accepted sockets.
+     */
+    protected Handler handler = null;
+    public void setHandler(Handler handler ) { this.handler = handler; }
+    public Handler getHandler() { return handler; }
+
+
+    /**
+     * Allows the server developer to specify the backlog that
+     * should be used for server sockets. By default, this value
+     * is 100.
+     */
+    protected int backlog = 100;
+    public void setBacklog(int backlog) { if (backlog > 0) this.backlog = backlog; }
+    public int getBacklog() { return backlog; }
+
+
+    /**
+     * Socket TCP no delay.
+     */
+    protected boolean tcpNoDelay = false;
+    public boolean getTcpNoDelay() { return tcpNoDelay; }
+    public void setTcpNoDelay(boolean tcpNoDelay) { this.tcpNoDelay = tcpNoDelay; }
+
+
+    /**
+     * Socket linger.
+     */
+    protected int soLinger = 100;
+    public int getSoLinger() { return soLinger; }
+    public void setSoLinger(int soLinger) { this.soLinger = soLinger; }
+
+
+    /**
+     * Socket timeout.
+     */
+    protected int soTimeout = -1;
+    public int getSoTimeout() { return soTimeout; }
+    public void setSoTimeout(int soTimeout) { this.soTimeout = soTimeout; }
+
+
+    /**
+     * The default is true - the created threads will be
+     *  in daemon mode. If set to false, the control thread
+     *  will not be daemon - and will keep the process alive.
+     */
+    protected boolean daemon = true;
+    public void setDaemon(boolean b) { daemon = b; }
+    public boolean getDaemon() { return daemon; }
+
+
+    /**
+     * Name of the thread pool, which will be used for naming child threads.
+     */
+    protected String name = "TP";
+    public void setName(String name) { this.name = name; }
+    public String getName() { return name; }
+
+
+    /**
+     * Server socket factory.
+     */
+    protected ServerSocketFactory serverSocketFactory = null;
+    public void setServerSocketFactory(ServerSocketFactory factory) { this.serverSocketFactory = factory; }
+    public ServerSocketFactory getServerSocketFactory() { return serverSocketFactory; }
+
+
+    /**
+     * The socket poller used for event support.
+     */
+    protected Poller eventPoller = null;
+    public Poller getEventPoller() {
+        return eventPoller;
+    }
+
+
+    public boolean isRunning() {
+        return running;
+    }
+    
+    public boolean isPaused() {
+        return paused;
+    }
+    
+    public int getCurrentThreadCount() {
+        return curThreads;
+    }
+    
+    public int getCurrentThreadsBusy() {
+        return workers!=null?curThreads - workers.size():0;
+    }
+    
+
+    // ------------------------------------------------ Handler Inner Interface
+
+
+    /**
+     * Bare bones interface used for socket processing. Per thread data is to be
+     * stored in the ThreadWithAttributes extra folders, or alternately in
+     * thread local fields.
+     */
+    public interface Handler {
+        public enum SocketState {
+            OPEN, CLOSED, LONG
+        }
+        public SocketState process(Socket socket);
+        public SocketState event(Socket socket, SocketStatus status);
+    }
+
+
+    // --------------------------------------------------- Acceptor Inner Class
+
+
+    /**
+     * Server socket acceptor thread.
+     */
+    protected class Acceptor implements Runnable {
+
+
+        /**
+         * The background thread that listens for incoming TCP/IP connections and
+         * hands them off to an appropriate processor.
+         */
+        public void run() {
+
+            // Loop until we receive a shutdown command
+            while (running) {
+
+                // Loop if endpoint is paused
+                while (paused) {
+                    try {
+                        Thread.sleep(1000);
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    }
+                }
+
+                // Accept the next incoming connection from the server socket
+                try {
+                    Socket socket = serverSocketFactory.acceptSocket(serverSocket);
+                    serverSocketFactory.initSocket(socket);
+                    // Hand this socket off to an appropriate processor
+                    if (!processSocket(socket)) {
+                        // Close socket right away
+                        try { socket.close(); } catch (IOException e) { }
+                    }
+                } catch (IOException x) {
+                    if (running) {
+                        CoyoteLogger.UTIL_LOGGER.errorAcceptingSocket(x);
+                    }
+                } catch (Throwable t) {
+                    CoyoteLogger.UTIL_LOGGER.errorAcceptingSocket(t);
+                }
+
+                // The processor will recycle itself when it finishes
+
+            }
+
+        }
+
+    }
+
+
+    // ------------------------------------------------- SocketInfo Inner Class
+
+
+    /**
+     * Socket list class, used to avoid using a possibly large amount of objects
+     * with very little actual use.
+     */
+    public static class SocketInfo {
+        public static final int RESUME = 4;
+        public static final int WAKEUP = 8;
+        public Socket socket;
+        public int timeout;
+        public int flags;
+        public boolean resume() {
+            return (flags & RESUME) == RESUME;
+        }
+        public boolean wakeup() {
+            return (flags & WAKEUP) == WAKEUP;
+        }
+        public static int merge(int flag1, int flag2) {
+            return ((flag1 & RESUME) | (flag2 & RESUME)) 
+                | ((flag1 & WAKEUP) & (flag2 & WAKEUP));
+        }
+    }
+    
+    
+    // --------------------------------------------- SocketTimeouts Inner Class
+
+
+    /**
+     * Socket list class, used to avoid using a possibly large amount of objects
+     * with very little actual use.
+     */
+    public class SocketTimeouts {
+        protected int size;
+
+        protected Socket[] sockets;
+        protected long[] timeouts;
+        protected int pos = 0;
+
+        public SocketTimeouts(int size) {
+            this.size = 0;
+            sockets = new Socket[size];
+            timeouts = new long[size];
+        }
+
+        public void add(Socket socket, long timeout) {
+            sockets[size] = socket;
+            timeouts[size] = timeout;
+            size++;
+        }
+        
+        public boolean remove(Socket socket) {
+            for (int i = 0; i < size; i++) {
+                if (sockets[i] == socket) {
+                    sockets[i] = sockets[size - 1];
+                    timeouts[i] = timeouts[size - 1];
+                    size--;
+                    return true;
+                }
+            }
+            return false;
+        }
+        
+        public Socket check(long date) {
+            while (pos < size) {
+                if (date >= timeouts[pos]) {
+                    Socket result = sockets[pos];
+                    sockets[pos] = sockets[size - 1];
+                    timeouts[pos] = timeouts[size - 1];
+                    size--;
+                    return result;
+                }
+                pos++;
+            }
+            pos = 0;
+            return null;
+        }
+        
+    }
+    
+    
+    // ------------------------------------------------- SocketList Inner Class
+
+
+    /**
+     * Socket list class, used to avoid using a possibly large amount of objects
+     * with very little actual use.
+     */
+    public class SocketList {
+        protected int size;
+        protected int pos;
+
+        protected Socket[] sockets;
+        protected int[] timeouts;
+        protected int[] flags;
+        
+        protected SocketInfo info = new SocketInfo();
+        
+        public SocketList(int size) {
+            this.size = 0;
+            pos = 0;
+            sockets = new Socket[size];
+            timeouts = new int[size];
+            flags = new int[size];
+        }
+        
+        public int size() {
+            return this.size;
+        }
+        
+        public SocketInfo get() {
+            if (pos == size) {
+                return null;
+            } else {
+                info.socket = sockets[pos];
+                info.timeout = timeouts[pos];
+                info.flags = flags[pos];
+                pos++;
+                return info;
+            }
+        }
+        
+        public void clear() {
+            size = 0;
+            pos = 0;
+        }
+        
+        public boolean add(Socket socket, int timeout, int flag) {
+            if (size == sockets.length) {
+                return false;
+            } else {
+                for (int i = 0; i < size; i++) {
+                    if (sockets[i] == socket) {
+                        flags[i] = SocketInfo.merge(flags[i], flag);
+                        return true;
+                    }
+                }
+                sockets[size] = socket;
+                timeouts[size] = timeout;
+                flags[size] = flag;
+                size++;
+                return true;
+            }
+        }
+        
+        public void duplicate(SocketList copy) {
+            copy.size = size;
+            copy.pos = pos;
+            System.arraycopy(sockets, 0, copy.sockets, 0, size);
+            System.arraycopy(timeouts, 0, copy.timeouts, 0, size);
+            System.arraycopy(flags, 0, copy.flags, 0, size);
+        }
+        
+    }
+    
+    
+    // ------------------------------------------- SocketProcessor Inner Class
+
+
+    /**
+     * This class is the equivalent of the Worker, but will simply use in an
+     * external Executor thread pool.
+     */
+    protected class SocketProcessor implements Runnable {
+        
+        protected Socket socket = null;
+        
+        public SocketProcessor(Socket socket) {
+            this.socket = socket;
+        }
+
+        public void run() {
+
+            // Process the request from this socket
+            if (!setSocketOptions(socket) || (handler.process(socket) == Handler.SocketState.CLOSED)) {
+                // Close socket
+                try { socket.close(); } catch (IOException e) { }
+            }
+
+            // Finish up this request
+            socket = null;
+
+        }
+        
+    }
+    
+    
+    // --------------------------------------- SocketEventProcessor Inner Class
+
+
+    /**
+     * This class is the equivalent of the Worker, but will simply use in an
+     * external Executor thread pool.
+     */
+    protected class SocketEventProcessor implements Runnable {
+        
+        protected Socket socket = null;
+        protected SocketStatus status = null; 
+        
+        public SocketEventProcessor(Socket socket, SocketStatus status) {
+            this.socket = socket;
+            this.status = status;
+        }
+
+        public void run() {
+
+            Handler.SocketState socketState = handler.event(socket, status);
+            if (socketState == Handler.SocketState.CLOSED) {
+                // Close socket
+                try { socket.close(); } catch (IOException e) { }
+            } else if (socketState == Handler.SocketState.OPEN) {
+                // Process the keepalive after the event processing
+                // This is the main behavior difference with endpoint with pollers, which
+                // will add the socket to the poller
+                if (handler.process(socket) == Handler.SocketState.CLOSED) {
+                    // Close socket
+                    try { socket.close(); } catch (IOException e) { }
+                }
+            }
+            socket = null;
+
+        }
+        
+    }
+    
+    
+    // ----------------------------------------------------- Poller Inner Class
+
+
+    /**
+     * Poller class.
+     */
+    public class Poller implements Runnable {
+
+        /**
+         * List of sockets to be added to the poller.
+         */
+        protected SocketList addList = null;
+
+        /**
+         * List of sockets to be added to the poller.
+         */
+        protected SocketList localAddList = null;
+
+        /**
+         * Structure used for storing timeouts.
+         */
+        protected SocketTimeouts timeouts = null;
+        
+        
+        /**
+         * Last run of maintain. Maintain will run usually every 5s.
+         */
+        protected long lastMaintain = System.currentTimeMillis();
+        
+        
+        /**
+         * Amount of connections inside this poller.
+         */
+        protected int connectionCount = 0;
+        public int getConnectionCount() { return connectionCount; }
+
+        public Poller() {
+        }
+        
+        /**
+         * Create the poller. The java.io poller only deals with timeouts.
+         */
+        protected void init() {
+
+            timeouts = new SocketTimeouts(pollerSize);
+            
+            connectionCount = 0;
+            addList = new SocketList(pollerSize);
+            localAddList = new SocketList(pollerSize);
+
+        }
+
+        /**
+         * Destroy the poller.
+         */
+        protected void destroy() {
+            // Wait for pollerTime before doing anything, so that the poller threads
+            // exit, otherwise parallel destruction of sockets which are still
+            // in the poller can cause problems
+            try {
+                synchronized (this) {
+                    this.wait(2);
+                }
+            } catch (InterruptedException e) {
+                // Ignore
+            }
+            // Close all sockets in the add queue
+            SocketInfo info = addList.get();
+            while (info != null) {
+                if (!processSocket(info.socket, SocketStatus.STOP)) {
+                    try { info.socket.close(); } catch (IOException e) { }
+                }
+                info = addList.get();
+            }
+            addList.clear();
+            // Close all sockets still in the poller
+            long future = System.currentTimeMillis() + Integer.MAX_VALUE;
+            Socket socket = timeouts.check(future);
+            while (socket != null) {
+                if (!processSocket(socket, SocketStatus.TIMEOUT)) {
+                    try { socket.close(); } catch (IOException e) { }
+                }
+                socket = timeouts.check(future);
+            }
+            connectionCount = 0;
+        }
+
+        /**
+         * Add specified socket and associated pool to the poller. The socket will
+         * be added to a temporary array, and polled first after a maximum amount
+         * of time equal to pollTime (in most cases, latency will be much lower,
+         * however).
+         *
+         * @param socket to add to the poller
+         */
+        public void add(Socket socket, int timeout, boolean resume, boolean wakeup) {
+            if (timeout < 0) {
+                timeout = keepAliveTimeout;
+            }
+            if (timeout < 0) {
+                timeout = soTimeout;
+            }
+            if (timeout <= 0) {
+                // Always put a timeout in
+                timeout = Integer.MAX_VALUE;
+            }
+            boolean ok = false;
+            synchronized (this) {
+                // Add socket to the list. Newly added sockets will wait
+                // at most for pollTime before being polled
+                if (addList.add(socket, timeout, (resume ? SocketInfo.RESUME : 0)
+                        | (wakeup ? SocketInfo.WAKEUP : 0))) {
+                    ok = true;
+                    this.notify();
+                }
+            }
+            if (!ok) {
+                // Can't do anything: close the socket right away
+                if (!processSocket(socket, SocketStatus.ERROR)) {
+                    try { socket.close(); } catch (IOException e) { }
+                }
+            }
+        }
+
+        /**
+         * Timeout checks.
+         */
+        protected void maintain() {
+
+            long date = System.currentTimeMillis();
+            // Maintain runs at most once every 5s, although it will likely get called more
+            if ((date - lastMaintain) < 5000L) {
+                return;
+            } else {
+                lastMaintain = date;
+            }
+            Socket socket = timeouts.check(date);
+            while (socket != null) {
+                if (!processSocket(socket, SocketStatus.TIMEOUT)) {
+                    try { socket.close(); } catch (IOException e) { }
+                }
+                socket = timeouts.check(date);
+            }
+
+        }
+        
+        /**
+         * The background thread that listens for incoming TCP/IP connections and
+         * hands them off to an appropriate processor.
+         */
+        public void run() {
+
+            int maintain = 0;
+            // Loop until we receive a shutdown command
+            while (running) {
+
+                // Loop if endpoint is paused
+                while (paused) {
+                    try {
+                        Thread.sleep(1000);
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    }
+                }
+                // Check timeouts for suspended connections if the poller is empty
+                while (connectionCount < 1 && addList.size() < 1) {
+                    // Reset maintain time.
+                    try {
+                        if (soTimeout > 0 && running) {
+                            maintain();
+                        }
+                        synchronized (this) {
+                            this.wait(10000);
+                        }
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    } catch (Throwable t) {
+                        CoyoteLogger.UTIL_LOGGER.errorProcessingSocketTimeout(t);
+                    }
+                }
+
+                try {
+                    
+                    // Add sockets which are waiting to the poller
+                    if (addList.size() > 0) {
+                        synchronized (this) {
+                            // Duplicate to another list, so that the syncing is minimal
+                            addList.duplicate(localAddList);
+                            addList.clear();
+                        }
+                        SocketInfo info = localAddList.get();
+                        while (info != null) {
+                            if (info.wakeup()) {
+                                // Resume event if socket is present in the poller
+                                if (timeouts.remove(info.socket)) {
+                                    if (info.resume()) {
+                                        if (!processSocket(info.socket, SocketStatus.OPEN_CALLBACK)) {
+                                            try { info.socket.close(); } catch (IOException e) { }
+                                        }
+                                    } else {
+                                        timeouts.add(info.socket, System.currentTimeMillis() + info.timeout);
+                                    }
+                                }
+                            } else {
+                                if (info.resume()) {
+                                    timeouts.remove(info.socket);
+                                    if (!processSocket(info.socket, SocketStatus.OPEN_CALLBACK)) {
+                                        try { info.socket.close(); } catch (IOException e) { }
+                                    }
+                                } else {
+                                    timeouts.add(info.socket, System.currentTimeMillis() + info.timeout);
+                                }
+                            }
+                            info = localAddList.get();
+                        }
+                    }
+
+                    try {
+                        Thread.sleep(2);
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    }
+
+                    // Process socket timeouts
+                    if (soTimeout > 0 && maintain++ > 1000 && running) {
+                        maintain = 0;
+                        maintain();
+                    }
+
+                } catch (Throwable t) {
+                    if (maintain == 0) {
+                        CoyoteLogger.UTIL_LOGGER.errorProcessingSocketTimeout(t);
+                    } else {
+                        CoyoteLogger.UTIL_LOGGER.errorPollingSocketWithException(t);
+                    }
+                }
+
+            }
+
+            synchronized (this) {
+                this.notifyAll();
+            }
+
+        }
+        
+    }
+
+
+    // ----------------------------------------------------- Worker Inner Class
+
+
+    protected class Worker implements Runnable {
+
+        protected Thread thread = null;
+        protected boolean available = false;
+        protected Socket socket = null;
+        protected SocketStatus status = null;
+
+        
+        /**
+         * Process an incoming TCP/IP connection on the specified socket.  Any
+         * exception that occurs during processing must be logged and swallowed.
+         * <b>NOTE</b>:  This method is called from our Connector's thread.  We
+         * must assign it to our own thread so that multiple simultaneous
+         * requests can be handled.
+         *
+         * @param socket TCP socket to process
+         */
+        protected synchronized void assign(Socket socket) {
+
+            // Wait for the Processor to get the previous Socket
+            while (available) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                }
+            }
+
+            // Store the newly available Socket and notify our thread
+            this.socket = socket;
+            this.status = null;
+            available = true;
+            notifyAll();
+
+        }
+
+        
+        protected synchronized void assign(Socket socket, SocketStatus status) {
+
+            // Wait for the Processor to get the previous Socket
+            while (available) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                }
+            }
+
+            // Store the newly available Socket and notify our thread
+            this.socket = socket;
+            this.status = status;
+            available = true;
+            notifyAll();
+
+        }
+
+
+        /**
+         * Await a newly assigned Socket from our Connector, or <code>null</code>
+         * if we are supposed to shut down.
+         */
+        private synchronized Socket await() {
+
+            // Wait for the Connector to provide a new Socket
+            while (!available) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                }
+            }
+
+            // Notify the Connector that we have received this Socket
+            Socket socket = this.socket;
+            available = false;
+            notifyAll();
+
+            return (socket);
+
+        }
+
+
+
+        /**
+         * The background thread that listens for incoming TCP/IP connections and
+         * hands them off to an appropriate processor.
+         */
+        public void run() {
+
+            // Process requests until we receive a shutdown signal
+            while (running) {
+
+                // Wait for the next socket to be assigned
+                Socket socket = await();
+                if (socket == null)
+                    continue;
+
+                // Process the request from this socket
+                if (status != null){
+                    Handler.SocketState socketState = handler.event(socket, status);
+                    if (socketState == Handler.SocketState.CLOSED) {
+                        // Close socket
+                        try { socket.close(); } catch (IOException e) { }
+                    } else if (socketState == Handler.SocketState.OPEN) {
+                        // Process the keepalive after the event processing
+                        // This is the main behavior difference with endpoint with pollers, which
+                        // will add the socket to the poller
+                        if (handler.process(socket) == Handler.SocketState.CLOSED) {
+                            // Close socket
+                            try { socket.close(); } catch (IOException e) { }
+                        }
+                    }
+                } else if ((status == null) && (!setSocketOptions(socket) || (handler.process(socket) == Handler.SocketState.CLOSED))) {
+                    // Close socket
+                    try { socket.close(); } catch (IOException e) { }
+                }
+
+                // Finish up this request
+                recycleWorkerThread(this);
+
+            }
+
+        }
+
+
+        /**
+         * Start the background processing thread.
+         */
+        public void start() {
+            thread = new Thread(this);
+            thread.setName(getName() + "-" + (++curThreads));
+            thread.setDaemon(true);
+            thread.start();
+        }
+
+
+    }
+
+
+    // -------------------- Public methods --------------------
+
+    public void init()
+        throws Exception {
+
+        if (initialized)
+            return;
+        
+        // Initialize thread count defaults for acceptor
+        if (acceptorThreadCount == 0) {
+            acceptorThreadCount = 1;
+        }
+        if (serverSocketFactory == null) {
+            serverSocketFactory = ServerSocketFactory.getDefault();
+        }
+        if (serverSocket == null) {
+            try {
+                if (address == null) {
+                    serverSocket = serverSocketFactory.createSocket(port, backlog);
+                } else {
+                    serverSocket = serverSocketFactory.createSocket(port, backlog, address);
+                }
+            } catch (BindException be) {
+                if (address == null) {
+                    throw new BindException(be.getMessage() + "<null>:" + port);
+                } else {
+                    throw new BindException(be.getMessage() + " " +
+                            address.toString() + ":" + port);
+                }
+            }
+        }
+        //if( serverTimeout >= 0 )
+        //    serverSocket.setSoTimeout( serverTimeout );
+        
+        initialized = true;
+        
+    }
+    
+    public void start()
+        throws Exception {
+        // Initialize socket if not done before
+        if (!initialized) {
+            init();
+        }
+        if (!running) {
+            running = true;
+            paused = false;
+
+            // Create worker collection
+            if (executor == null) {
+                workers = new WorkerStack(maxThreads);
+            }
+
+            // Start event poller thread
+            eventPoller = new Poller();
+            eventPoller.init();
+            Thread pollerThread = new Thread(eventPoller, getName() + "-Poller");
+            pollerThread.setPriority(threadPriority);
+            pollerThread.setDaemon(true);
+            pollerThread.start();
+
+            // Start acceptor threads
+            for (int i = 0; i < acceptorThreadCount; i++) {
+                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
+                acceptorThread.setPriority(threadPriority);
+                acceptorThread.setDaemon(daemon);
+                acceptorThread.start();
+            }
+        }
+    }
+
+    public void pause() {
+        if (running && !paused) {
+            paused = true;
+            unlockAccept();
+        }
+    }
+
+    public void resume() {
+        if (running) {
+            paused = false;
+        }
+    }
+
+    public void stop() {
+        if (running) {
+            running = false;
+            unlockAccept();
+            eventPoller.destroy();
+            eventPoller = null;
+        }
+    }
+
+    /**
+     * Deallocate APR memory pools, and close server socket.
+     */
+    public void destroy() throws Exception {
+        if (running) {
+            stop();
+        }
+        if (serverSocket != null) {
+            try {
+                if (serverSocket != null)
+                    serverSocket.close();
+            } catch (Exception e) {
+                CoyoteLogger.UTIL_LOGGER.errorClosingSocket(e);
+            }
+            serverSocket = null;
+        }
+        initialized = false ;
+    }
+
+    
+    /**
+     * Unlock the accept by using a local connection.
+     */
+    protected void unlockAccept() {
+        Socket s = null;
+        try {
+            // Need to create a connection to unlock the accept();
+            if (address == null) {
+                s = new Socket("localhost", port);
+            } else {
+                s = new Socket(address, port);
+                // setting soLinger to a small value will help shutdown the
+                // connection quicker
+                s.setSoLinger(true, 0);
+            }
+        } catch (Exception e) {
+            // Ignore
+        } finally {
+            if (s != null) {
+                try {
+                    s.close();
+                } catch (Exception e) {
+                    // Ignore
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Set the options for the current socket.
+     */
+    protected boolean setSocketOptions(Socket socket) {
+        // Process the connection
+        int step = 1;
+        try {
+
+            // 1: Set socket options: timeout, linger, etc
+            if (soLinger >= 0) { 
+                socket.setSoLinger(true, soLinger);
+            }
+            if (tcpNoDelay) {
+                socket.setTcpNoDelay(tcpNoDelay);
+            }
+            if (soTimeout > 0) {
+                socket.setSoTimeout(soTimeout);
+            }
+
+            // 2: SSL handshake
+            step = 2;
+            serverSocketFactory.handshake(socket);
+
+        } catch (Throwable t) {
+            if (step == 2) {
+                CoyoteLogger.UTIL_LOGGER.handshakeFailed(t);
+            } else {
+                CoyoteLogger.UTIL_LOGGER.unexpectedError(t);
+            }
+            // Tell to close the socket
+            return false;
+        }
+        return true;
+    }
+
+    
+    /**
+     * Create (or allocate) and return an available processor for use in
+     * processing a specific HTTP request, if possible.  If the maximum
+     * allowed processors have already been created and are in use, return
+     * <code>null</code> instead.
+     */
+    protected Worker createWorkerThread() {
+
+        synchronized (workers) {
+            if (workers.size() > 0) {
+                curThreadsBusy++;
+                return workers.pop();
+            }
+            if ((maxThreads > 0) && (curThreads < maxThreads)) {
+                curThreadsBusy++;
+                if (curThreadsBusy == maxThreads) {
+                    CoyoteLogger.UTIL_LOGGER.maxThreadsReached(maxThreads, address, port);
+                }
+                return (newWorkerThread());
+            } else {
+                if (maxThreads < 0) {
+                    curThreadsBusy++;
+                    return (newWorkerThread());
+                } else {
+                    return (null);
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Create and return a new processor suitable for processing HTTP
+     * requests and returning the corresponding responses.
+     */
+    protected Worker newWorkerThread() {
+
+        Worker workerThread = new Worker();
+        workerThread.start();
+        return (workerThread);
+
+    }
+
+
+    /**
+     * Return a new worker thread, and block while to worker is available.
+     */
+    protected Worker getWorkerThread() {
+        // Allocate a new worker thread
+        Worker workerThread = createWorkerThread();
+        if (org.apache.tomcat.util.net.Constants.WAIT_FOR_THREAD 
+                || org.apache.tomcat.util.Constants.LOW_MEMORY) {
+            while (workerThread == null) {
+                try {
+                    synchronized (workers) {
+                        workers.wait();
+                    }
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+                workerThread = createWorkerThread();
+            }
+        }
+        return workerThread;
+    }
+
+
+    /**
+     * Recycle the specified Processor so that it can be used again.
+     *
+     * @param workerThread The processor to be recycled
+     */
+    protected void recycleWorkerThread(Worker workerThread) {
+        synchronized (workers) {
+            workers.push(workerThread);
+            curThreadsBusy--;
+            workers.notify();
+        }
+    }
+
+
+    /**
+     * Process given socket.
+     */
+    protected boolean processSocket(Socket socket) {
+        try {
+            if (executor == null) {
+                Worker worker = getWorkerThread();
+                if (worker != null) {
+                    worker.assign(socket);
+                } else {
+                    return false;
+                }
+            } else {
+                executor.execute(new SocketProcessor(socket));
+            }
+        } catch (Throwable t) {
+            // This means we got an OOM or similar creating a thread, or that
+            // the pool and its queue are full
+            CoyoteLogger.UTIL_LOGGER.errorProcessingSocket(t);
+            return false;
+        }
+        return true;
+    }
+    
+
+    /**
+     * Process given socket for an event.
+     */
+    protected boolean processSocket(Socket socket, SocketStatus status) {
+        try {
+            if (executor == null) {
+                Worker worker = getWorkerThread();
+                if (worker != null) {
+                    worker.assign(socket, status);
+                } else {
+                    return false;
+                }
+            } else {
+                executor.execute(new SocketEventProcessor(socket, status));
+            }
+        } catch (Throwable t) {
+            // This means we got an OOM or similar creating a thread, or that
+            // the pool and its queue are full
+            CoyoteLogger.UTIL_LOGGER.errorProcessingSocket(t);
+            return false;
+        }
+        return true;
+    }
+    
+
+    // ------------------------------------------------- WorkerStack Inner Class
+
+
+    public class WorkerStack {
+        
+        protected Worker[] workers = null;
+        protected int end = 0;
+        
+        public WorkerStack(int size) {
+            workers = new Worker[size];
+        }
+        
+        /** 
+         * Put the object into the queue.
+         * 
+         * @param   object      the object to be appended to the queue (first element). 
+         */
+        public void push(Worker worker) {
+            workers[end++] = worker;
+        }
+        
+        /**
+         * Get the first object out of the queue. Return null if the queue
+         * is empty. 
+         */
+        public Worker pop() {
+            if (end > 0) {
+                return workers[--end];
+            }
+            return null;
+        }
+        
+        /**
+         * Get the first object out of the queue, Return null if the queue
+         * is empty.
+         */
+        public Worker peek() {
+            return workers[end];
+        }
+        
+        /**
+         * Is the queue empty?
+         */
+        public boolean isEmpty() {
+            return (end == 0);
+        }
+        
+        /**
+         * How many elements are there in this queue?
+         */
+        public int size() {
+            return (end);
+        }
+    }
+
+}

Modified: branches/7.2.x/src/main/java/org/apache/tomcat/util/net/NioEndpoint.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/tomcat/util/net/NioEndpoint.java	2012-09-25 12:49:25 UTC (rev 2086)
+++ branches/7.2.x/src/main/java/org/apache/tomcat/util/net/NioEndpoint.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -187,15 +187,15 @@
 		}
 
 		if (this.connections == null) {
-			this.connections = new ConcurrentHashMap<>();
+			this.connections = new ConcurrentHashMap<Long, NioChannel>();
 		}
 
 		if (this.recycledChannelProcessors == null) {
-			this.recycledChannelProcessors = new ConcurrentLinkedQueue<>();
+			this.recycledChannelProcessors = new ConcurrentLinkedQueue<ChannelProcessor>();
 		}
 
 		if (this.recycledHandshakeProcessors == null) {
-			this.recycledHandshakeProcessors = new ConcurrentLinkedQueue<>();
+			this.recycledHandshakeProcessors = new ConcurrentLinkedQueue<HandshakeHandler>();
 		}
 
 		// If the executor is not set, create it with a fixed thread pool
@@ -1199,9 +1199,9 @@
 		 */
 		public void init() {
 			this.mutex = new Object();
-			this.channelList = new ConcurrentHashMap<>(this.size);
-			this.recycledChannelList = new ConcurrentLinkedQueue<>();
-			this.recycledCompletionHandlers = new ConcurrentLinkedQueue<>();
+			this.channelList = new ConcurrentHashMap<Long, ChannelInfo>(this.size);
+			this.recycledChannelList = new ConcurrentLinkedQueue<ChannelInfo>();
+			this.recycledCompletionHandlers = new ConcurrentLinkedQueue<CompletionHandler<Integer, NioChannel>>();
 		}
 
 		/**
@@ -1646,8 +1646,8 @@
 			this.size = maxThreads;
 			this.mutex = new Object();
 			this.counter = new AtomicInteger(0);
-			this.fileDatas = new ConcurrentLinkedQueue<>();
-			this.recycledFileDatas = new ConcurrentLinkedQueue<>();
+			this.fileDatas = new ConcurrentLinkedQueue<SendfileData>();
+			this.recycledFileDatas = new ConcurrentLinkedQueue<SendfileData>();
 		}
 
 		/**

Modified: branches/7.2.x/src/main/java/org/apache/tomcat/util/net/SSLImplementation.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/tomcat/util/net/SSLImplementation.java	2012-09-25 12:49:25 UTC (rev 2086)
+++ branches/7.2.x/src/main/java/org/apache/tomcat/util/net/SSLImplementation.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -80,26 +80,6 @@
         }
     }
 
-    /**
-     * @return the implementation name
-     */
     abstract public String getImplementationName();
-
-    /**
-     * 
-     * @return a new instance of {@link NioJSSESocketChannelFactory}
-     */
-    public abstract NioJSSESocketChannelFactory getServerSocketChannelFactory();
-
-    /**
-     * @param channel
-     * @return the {@link SSLSupport} attached to this channel
-     */
-    public abstract SSLSupport getSSLSupport(NioChannel channel);
-    
-    /**
-     * @param session
-     * @return the {@link SSLSupport} attached to this session
-     */
     abstract public SSLSupport getSSLSupport(SSLSession session);
 }
\ No newline at end of file

Added: branches/7.2.x/src/main/java/org/apache/tomcat/util/net/ServerSocketFactory.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/tomcat/util/net/ServerSocketFactory.java	                        (rev 0)
+++ branches/7.2.x/src/main/java/org/apache/tomcat/util/net/ServerSocketFactory.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -0,0 +1,173 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.tomcat.util.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Hashtable;
+
+/**
+ * This class creates server sockets.  It may be subclassed by other
+ * factories, which create particular types of server sockets.  This
+ * provides a general framework for the addition of public socket-level
+ * functionality.  It it is the server side analogue of a socket factory,
+ * and similarly provides a way to capture a variety of policies related
+ * to the sockets being constructed.
+ *
+ * <P> Like socket factories, Server Socket factory instances have two
+ * categories of methods.  First are methods used to create sockets.
+ * Second are methods which set properties used in the production of
+ * sockets, such as networking options.  There is also an environment
+ * specific default server socket factory; frameworks will often use
+ * their own customized factory.
+ * 
+ * <P><hr><em> It may be desirable to move this interface into the
+ * <b>java.net</b> package, so that is not an extension but the preferred
+ * interface.  Should this be serializable, making it a JavaBean which can
+ * be saved along with its networking configuration?
+ * </em>   
+ *
+ * @author db at eng.sun.com
+ * @author Harish Prabandham
+ */
+public abstract class ServerSocketFactory implements Cloneable {
+
+    //
+    // NOTE:  JDK 1.1 bug in class GC, this can get collected
+    // even though it's always accessible via getDefault().
+    //
+
+    private static ServerSocketFactory theFactory;
+    protected Hashtable attributes=new Hashtable();
+
+    /**
+     * Constructor is used only by subclasses.
+     */
+
+    protected ServerSocketFactory () {
+        /* NOTHING */
+    }
+
+    /** General mechanism to pass attributes from the
+     *  ServerConnector to the socket factory.
+     *
+     *  Note that the "prefered" mechanism is to
+     *  use bean setters and explicit methods, but
+     *  this allows easy configuration via server.xml
+     *  or simple Properties
+     */
+    public void setAttribute( String name, Object value ) {
+	if( name!=null && value !=null)
+	    attributes.put( name, value );
+    }
+    
+    /**
+     * Returns a copy of the environment's default socket factory.
+     */
+    public static synchronized ServerSocketFactory getDefault () {
+        //
+        // optimize typical case:  no synch needed
+        //
+
+        if (theFactory == null) {
+            //
+            // Different implementations of this method could
+            // work rather differently.  For example, driving
+            // this from a system property, or using a different
+            // implementation than JavaSoft's.
+            //
+
+            theFactory = new DefaultServerSocketFactory ();
+        }
+
+        try {
+            return (ServerSocketFactory) theFactory.clone ();
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException (e.getMessage ());
+        }
+    }
+
+    /**
+     * Returns a server socket which uses all network interfaces on
+     * the host, and is bound to a the specified port.  The socket is
+     * configured with the socket options (such as accept timeout)
+     * given to this factory.
+     *
+     * @param port the port to listen to
+     * @exception IOException for networking errors
+     * @exception InstantiationException for construction errors
+     */
+    public abstract ServerSocket createSocket (int port)
+    throws IOException, InstantiationException;
+
+    /**
+     * Returns a server socket which uses all network interfaces on
+     * the host, is bound to a the specified port, and uses the 
+     * specified connection backlog.  The socket is configured with
+     * the socket options (such as accept timeout) given to this factory.
+     *
+     * @param port the port to listen to
+     * @param backlog how many connections are queued
+     * @exception IOException for networking errors
+     * @exception InstantiationException for construction errors
+     */
+
+    public abstract ServerSocket createSocket (int port, int backlog)
+    throws IOException, InstantiationException;
+
+    /**
+     * Returns a server socket which uses only the specified network
+     * interface on the local host, is bound to a the specified port,
+     * and uses the specified connection backlog.  The socket is configured
+     * with the socket options (such as accept timeout) given to this factory.
+     *
+     * @param port the port to listen to
+     * @param backlog how many connections are queued
+     * @param ifAddress the network interface address to use
+     * @exception IOException for networking errors
+     * @exception InstantiationException for construction errors
+     */
+
+    public abstract ServerSocket createSocket (int port,
+        int backlog, InetAddress ifAddress)
+    throws IOException, InstantiationException;
+
+    public void initSocket( Socket s ) {
+    }
+ 
+     /**
+       Wrapper function for accept(). This allows us to trap and
+       translate exceptions if necessary
+ 
+       @exception IOException;
+     */ 
+     public abstract Socket acceptSocket(ServerSocket socket)
+ 	throws IOException;
+ 
+     /**
+       Extra function to initiate the handshake. Sometimes necessary
+       for SSL
+ 
+       @exception IOException;
+     */ 
+     public abstract void handshake(Socket sock)
+ 	throws IOException;
+}
+

Added: branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSEFactory.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSEFactory.java	                        (rev 0)
+++ branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSEFactory.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.tomcat.util.net.jsse;
+
+import java.net.Socket;
+
+import javax.net.ssl.SSLSocket;
+
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.ServerSocketFactory;
+import javax.net.ssl.SSLSession;
+
+/** 
+ * Factory interface to construct components based on the JSSE version
+ * in use.
+ *
+ * @author Bill Barker
+ * @author Filip Hanik
+ */
+
+public class JSSEFactory {
+
+    /**
+     * Returns the ServerSocketFactory to use.
+     */
+    public ServerSocketFactory getSocketFactory() {
+        return new JSSESocketFactory();
+    }
+
+    /**
+     * returns the SSLSupport attached to this socket.
+     */
+    public SSLSupport getSSLSupport(Socket socket) {
+        return new JSSESupport((SSLSocket)socket);
+    }
+    
+    public SSLSupport getSSLSupport(SSLSession session) {
+        return new JSSESupport(session);
+    }
+
+};

Added: branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java	                        (rev 0)
+++ branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -0,0 +1,71 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.tomcat.util.net.jsse;
+
+import java.net.Socket;
+
+import org.apache.tomcat.util.net.NioChannel;
+import org.apache.tomcat.util.net.SSLImplementation;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.ServerSocketFactory;
+import javax.net.ssl.SSLSession;
+
+/* JSSEImplementation:
+
+   Concrete implementation class for JSSE
+
+   @author EKR
+*/
+        
+public class JSSEImplementation extends SSLImplementation
+{
+    static final String SSLSocketClass = "javax.net.ssl.SSLSocket";
+
+    static org.jboss.logging.Logger logger = 
+        org.jboss.logging.Logger.getLogger(JSSEImplementation.class);
+
+    private JSSEFactory factory = null;
+
+    public JSSEImplementation() throws ClassNotFoundException {
+        // Check to see if JSSE is floating around somewhere
+        Class.forName(SSLSocketClass);
+        factory = new JSSEFactory();
+    }
+
+
+    public String getImplementationName(){
+      return "JSSE";
+    }
+      
+    public ServerSocketFactory getServerSocketFactory()  {
+        ServerSocketFactory ssf = factory.getSocketFactory();
+        return ssf;
+    } 
+
+    public SSLSupport getSSLSupport(Socket s) {
+        SSLSupport ssls = factory.getSSLSupport(s);
+        return ssls;
+    }
+
+    public SSLSupport getSSLSupport(SSLSession session) {
+        SSLSupport ssls = factory.getSSLSupport(session);
+        return ssls;
+    }
+
+
+}

Added: branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java	                        (rev 0)
+++ branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -0,0 +1,805 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.tomcat.util.net.jsse;
+
+import static org.jboss.web.CoyoteMessages.MESSAGES;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.CRL;
+import java.security.cert.CRLException;
+import java.security.cert.CertPathParameters;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreParameters;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.X509CertSelector;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.net.ssl.CertPathTrustManagerParameters;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509KeyManager;
+
+import org.jboss.web.CoyoteLogger;
+
+/*
+  1. Make the JSSE's jars available, either as an installed
+     extension (copy them into jre/lib/ext) or by adding
+     them to the Tomcat classpath.
+  2. keytool -genkey -alias tomcat -keyalg RSA
+     Use "changeit" as password ( this is the default we use )
+ */
+
+/**
+ * SSL server socket factory. It _requires_ a valid RSA key and
+ * JSSE. 
+ *
+ * @author Harish Prabandham
+ * @author Costin Manolache
+ * @author Stefan Freyr Stefansson
+ * @author EKR -- renamed to JSSESocketFactory
+ * @author Jan Luehe
+ * @author Bill Barker
+ */
+public class JSSESocketFactory
+    extends org.apache.tomcat.util.net.ServerSocketFactory {
+
+    private static final boolean RFC_5746_SUPPORTED;
+    // defaults
+    static String defaultProtocol = "TLS";
+    static boolean defaultClientAuth = false;
+    static String defaultKeystoreType = "JKS";
+    private static final String defaultKeystoreFile
+        = System.getProperty("user.home") + "/.keystore";
+    private static final String defaultKeyPass = "changeit";
+    private static final int defaultSessionCacheSize = 0;
+    private static final int defaultSessionTimeout = 86400;
+
+    static org.jboss.logging.Logger log =
+        org.jboss.logging.Logger.getLogger(JSSESocketFactory.class);
+
+    static {
+        boolean result = false;
+        SSLContext context;
+        try {
+            context = SSLContext.getInstance("TLS");
+            context.init(null, null, new SecureRandom());
+            SSLServerSocketFactory ssf = context.getServerSocketFactory();
+            String ciphers[] = ssf.getSupportedCipherSuites();
+            for (String cipher : ciphers) {
+                if ("TLS_EMPTY_RENEGOTIATION_INFO_SCSV".equals(cipher)) {
+                    result = true;
+                    break;
+                }
+            }
+        } catch (NoSuchAlgorithmException e) {
+            // Assume no RFC 5746 support
+        } catch (KeyManagementException e) {
+            // Assume no RFC 5746 support
+        }
+        RFC_5746_SUPPORTED = result;
+    }
+
+    protected boolean initialized;
+    protected String clientAuth = "false";
+    protected SSLServerSocketFactory sslProxy = null;
+    protected String[] enabledCiphers;
+    protected boolean allowUnsafeLegacyRenegotiation = false;
+
+    /**
+     * Flag to state that we require client authentication.
+     */
+    protected boolean requireClientAuth = false;
+
+    /**
+     * Flag to state that we would like client authentication.
+     */
+    protected boolean wantClientAuth    = false;
+
+
+    public JSSESocketFactory () {
+    }
+
+    public ServerSocket createSocket (int port)
+        throws IOException
+    {
+        if (!initialized) init();
+        ServerSocket socket = sslProxy.createServerSocket(port);
+        initServerSocket(socket);
+        return socket;
+    }
+    
+    public ServerSocket createSocket (int port, int backlog)
+        throws IOException
+    {
+        if (!initialized) init();
+        ServerSocket socket = sslProxy.createServerSocket(port, backlog);
+        initServerSocket(socket);
+        return socket;
+    }
+    
+    public ServerSocket createSocket (int port, int backlog,
+                                      InetAddress ifAddress)
+        throws IOException
+    {   
+        if (!initialized) init();
+        ServerSocket socket = sslProxy.createServerSocket(port, backlog,
+                                                          ifAddress);
+        initServerSocket(socket);
+        return socket;
+    }
+    
+    public Socket acceptSocket(ServerSocket socket)
+        throws IOException
+    {
+        SSLSocket asock = null;
+        try {
+             asock = (SSLSocket)socket.accept();
+        } catch (SSLException e){
+          throw new SocketException("SSL handshake error" + e.toString());
+        }
+        return asock;
+    }
+    
+    public void handshake(Socket sock) throws IOException {
+        // We do getSession instead of startHandshake() so we can call this multiple times
+        SSLSession session = ((SSLSocket)sock).getSession();
+        if (session.getCipherSuite().equals("SSL_NULL_WITH_NULL_NULL"))
+            throw new IOException("SSL handshake failed. Ciper suite in SSL Session is SSL_NULL_WITH_NULL_NULL");
+
+        if (!allowUnsafeLegacyRenegotiation && !RFC_5746_SUPPORTED) {
+            // Prevent further handshakes by removing all cipher suites
+            ((SSLSocket) sock).setEnabledCipherSuites(new String[0]);
+        }
+    }
+
+    /*
+     * Determines the SSL cipher suites to be enabled.
+     *
+     * @param requestedCiphers Comma-separated list of requested ciphers
+     * @param supportedCiphers Array of supported ciphers
+     *
+     * @return Array of SSL cipher suites to be enabled, or null if none of the
+     * requested ciphers are supported
+     */
+    protected String[] getEnabledCiphers(String requestedCiphers,
+                                         String[] supportedCiphers) {
+
+        String[] enabledCiphers = null;
+
+        if (requestedCiphers != null) {
+            Vector vec = null;
+            String cipher = requestedCiphers;
+            int index = requestedCiphers.indexOf(',');
+            if (index != -1) {
+                int fromIndex = 0;
+                while (index != -1) {
+                    cipher = requestedCiphers.substring(fromIndex, index).trim();
+                    if (cipher.length() > 0) {
+                        /*
+                         * Check to see if the requested cipher is among the
+                         * supported ciphers, i.e., may be enabled
+                         */
+                        for (int i=0; supportedCiphers != null
+                                     && i<supportedCiphers.length; i++) {
+                            if (supportedCiphers[i].equals(cipher)) {
+                                if (vec == null) {
+                                    vec = new Vector();
+                                }
+                                vec.addElement(cipher);
+                                break;
+                            }
+                        }
+                    }
+                    fromIndex = index+1;
+                    index = requestedCiphers.indexOf(',', fromIndex);
+                } // while
+                cipher = requestedCiphers.substring(fromIndex);
+            }
+
+            if (cipher != null) {
+                cipher = cipher.trim();
+                if (cipher.length() > 0) {
+                    /*
+                     * Check to see if the requested cipher is among the
+                     * supported ciphers, i.e., may be enabled
+                     */
+                    for (int i=0; supportedCiphers != null
+                                 && i<supportedCiphers.length; i++) {
+                        if (supportedCiphers[i].equals(cipher)) {
+                            if (vec == null) {
+                                vec = new Vector();
+                            }
+                            vec.addElement(cipher);
+                            break;
+                        }
+                    }
+                }
+            }           
+
+            if (vec != null) {
+                enabledCiphers = new String[vec.size()];
+                vec.copyInto(enabledCiphers);
+            }
+        } else {
+            enabledCiphers = sslProxy.getDefaultCipherSuites();
+        }
+
+        return enabledCiphers;
+    }
+     
+    /*
+     * Gets the SSL server's keystore password.
+     */
+    protected String getKeystorePassword() {
+        String keyPass = (String)attributes.get("keypass");
+        if (keyPass == null) {
+            keyPass = defaultKeyPass;
+        }
+        String keystorePass = (String)attributes.get("keystorePass");
+        if (keystorePass == null) {
+            keystorePass = keyPass;
+        }
+        return keystorePass;
+    }
+
+    /*
+     * Gets the SSL server's keystore.
+     */
+    protected KeyStore getKeystore(String type, String provider, String pass)
+            throws IOException {
+
+        String keystoreFile = (String)attributes.get("keystore");
+        if (keystoreFile == null)
+            keystoreFile = defaultKeystoreFile;
+
+        return getStore(type, provider, keystoreFile, pass);
+    }
+
+    /*
+     * Gets the SSL server's truststore.
+     */
+    protected KeyStore getTrustStore(String keystoreType,
+            String keystoreProvider) throws IOException {
+        KeyStore trustStore = null;
+
+        String truststoreFile = (String)attributes.get("truststoreFile");
+        if(truststoreFile == null) {
+            truststoreFile = System.getProperty("javax.net.ssl.trustStore");
+        }
+        if(log.isDebugEnabled()) {
+            log.debug("Truststore = " + truststoreFile);
+        }
+        String truststorePassword = (String)attributes.get("truststorePass");
+        if( truststorePassword == null) {
+            truststorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
+        }
+        if(log.isDebugEnabled()) {
+            log.debug("TrustPass = " + truststorePassword);
+        }
+        String truststoreType = (String)attributes.get("truststoreType");
+        if( truststoreType == null) {
+            truststoreType = System.getProperty("javax.net.ssl.trustStoreType");
+        }
+        if(truststoreType == null) {
+            truststoreType = keystoreType;
+        }
+        if(log.isDebugEnabled()) {
+            log.debug("trustType = " + truststoreType);
+        }
+        String truststoreProvider =
+            (String)attributes.get("truststoreProvider");
+        if( truststoreProvider == null) {
+            truststoreProvider =
+                System.getProperty("javax.net.ssl.trustStoreProvider");
+        }
+        if (truststoreProvider == null) {
+            truststoreProvider = keystoreProvider;
+        }
+        if(log.isDebugEnabled()) {
+            log.debug("trustProvider = " + truststoreProvider);
+        }
+
+        if (truststoreFile != null){
+            trustStore = getStore(truststoreType, truststoreProvider,
+                    truststoreFile, truststorePassword);
+        }
+
+        return trustStore;
+    }
+
+    /*
+     * Gets the key- or truststore with the specified type, path, and password.
+     */
+    private KeyStore getStore(String type, String provider, String path,
+            String pass) throws IOException {
+
+        KeyStore ks = null;
+        InputStream istream = null;
+        try {
+            if (provider == null) {
+                ks = KeyStore.getInstance(type);
+            } else {
+                ks = KeyStore.getInstance(type, provider);
+            }
+            if(!("PKCS11".equalsIgnoreCase(type) || "".equalsIgnoreCase(path))) {
+                File keyStoreFile = new File(path);
+                if (!keyStoreFile.isAbsolute()) {
+                    keyStoreFile = new File(System.getProperty("catalina.base"),
+                                            path);
+                }
+                istream = new FileInputStream(keyStoreFile);
+            }
+
+            char[] storePass = null;
+            if (pass != null) {
+                storePass = pass.toCharArray(); 
+            }
+            ks.load(istream, storePass);
+        } catch (FileNotFoundException fnfe) {
+            CoyoteLogger.UTIL_LOGGER.errorLoadingKeystore(type, path, fnfe.getMessage());
+            throw fnfe;
+        } catch (IOException ioe) {
+            CoyoteLogger.UTIL_LOGGER.errorLoadingKeystoreWithException(type, path, ioe.getMessage(), ioe);
+            throw ioe;      
+        } catch(Exception ex) {
+            CoyoteLogger.UTIL_LOGGER.errorLoadingKeystoreWithException(type, path, ex.getMessage(), ex);
+            throw new IOException(ex);
+        } finally {
+            if (istream != null) {
+                try {
+                    istream.close();
+                } catch (IOException ioe) {
+                    // Do nothing
+                }
+            }
+        }
+
+        return ks;
+    }
+
+    /**
+     * Reads the keystore and initializes the SSL socket factory.
+     */
+    void init() throws IOException {
+        try {
+
+            String clientAuthStr = (String) attributes.get("clientauth");
+            if("true".equalsIgnoreCase(clientAuthStr) ||
+               "yes".equalsIgnoreCase(clientAuthStr)) {
+                requireClientAuth = true;
+            } else if("want".equalsIgnoreCase(clientAuthStr)) {
+                wantClientAuth = true;
+            }
+
+            // SSL protocol variant (e.g., TLS, SSL v3, etc.)
+            String protocol = (String) attributes.get("protocol");
+            if (protocol == null) {
+                protocol = defaultProtocol;
+            }
+
+            // Certificate encoding algorithm (e.g., SunX509)
+            String algorithm = (String) attributes.get("algorithm");
+            if (algorithm == null) {
+                algorithm = KeyManagerFactory.getDefaultAlgorithm();;
+            }
+
+            String keystoreType = (String) attributes.get("keystoreType");
+            if (keystoreType == null) {
+                keystoreType = defaultKeystoreType;
+            }
+
+            String keystoreProvider =
+                (String) attributes.get("keystoreProvider");
+
+            String trustAlgorithm =
+                (String)attributes.get("truststoreAlgorithm");
+            if( trustAlgorithm == null ) {
+                trustAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+            }
+
+            // Create and init SSLContext
+            SSLContext context = (SSLContext) attributes.get("SSLContext");
+            if (context == null) {
+                context = SSLContext.getInstance(protocol); 
+                context.init(
+                        getKeyManagers(keystoreType, keystoreProvider,
+                                algorithm, (String) attributes.get("keyAlias")),
+                        getTrustManagers(keystoreType, keystoreProvider,
+                                trustAlgorithm), new SecureRandom());
+            }
+
+            // Configure SSL session cache
+            int sessionCacheSize;
+            if (attributes.get("sessionCacheSize") != null) {
+                sessionCacheSize = Integer.parseInt(
+                        (String)attributes.get("sessionCacheSize"));
+            } else {
+                sessionCacheSize = defaultSessionCacheSize;
+            }
+            int sessionCacheTimeout;
+            if (attributes.get("sessionCacheTimeout") != null) {
+                sessionCacheTimeout = Integer.parseInt(
+                        (String)attributes.get("sessionCacheTimeout"));
+            } else {
+                sessionCacheTimeout = defaultSessionTimeout;
+            }
+            SSLSessionContext sessionContext =
+                context.getServerSessionContext();
+            if (sessionContext != null) {
+                sessionContext.setSessionCacheSize(sessionCacheSize);
+                sessionContext.setSessionTimeout(sessionCacheTimeout);
+            }
+
+            // create proxy
+            sslProxy = context.getServerSocketFactory();
+
+            // Determine which cipher suites to enable
+            String requestedCiphers = (String)attributes.get("ciphers");
+            enabledCiphers = getEnabledCiphers(requestedCiphers,
+                                               sslProxy.getSupportedCipherSuites());
+
+            allowUnsafeLegacyRenegotiation =
+                "true".equals(attributes.get("allowUnsafeLegacyRenegotiation"));
+            
+            // Check the SSL config is OK
+            checkConfig();
+
+        } catch(Exception e) {
+            if( e instanceof IOException )
+                throw (IOException)e;
+            throw new IOException(e.getMessage());
+        }
+    }
+
+    /**
+     * Gets the initialized key managers.
+     */
+    protected KeyManager[] getKeyManagers(String keystoreType,
+                                          String keystoreProvider,
+                                          String algorithm,
+                                          String keyAlias)
+                throws Exception {
+
+        KeyManager[] kms = null;
+
+        String keystorePass = getKeystorePassword();
+
+        KeyStore ks = getKeystore(keystoreType, keystoreProvider, keystorePass);
+        if (keyAlias != null && !ks.isKeyEntry(keyAlias)) {
+            throw new IOException(MESSAGES.noKeyAlias(keyAlias));
+        }
+
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
+        kmf.init(ks, keystorePass.toCharArray());
+
+        kms = kmf.getKeyManagers();
+        if (keyAlias != null) {
+            if (JSSESocketFactory.defaultKeystoreType.equals(keystoreType)) {
+                keyAlias = keyAlias.toLowerCase(Locale.ENGLISH);
+            }
+            for(int i=0; i<kms.length; i++) {
+                kms[i] = new JSSEKeyManager((X509KeyManager)kms[i], keyAlias);
+            }
+        }
+
+        return kms;
+    }
+
+    /**
+     * Gets the intialized trust managers.
+     */
+    protected TrustManager[] getTrustManagers(String keystoreType,
+            String keystoreProvider, String algorithm)
+        throws Exception {
+        String crlf = (String) attributes.get("crlFile");
+        
+        TrustManager[] tms = null;
+        
+        KeyStore trustStore = getTrustStore(keystoreType, keystoreProvider);
+        if (trustStore != null) {
+            if (crlf == null) {
+                TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+                tmf.init(trustStore);
+                tms = tmf.getTrustManagers();
+            } else {
+                TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+                CertPathParameters params = getParameters(algorithm, crlf, trustStore);
+                ManagerFactoryParameters mfp = new CertPathTrustManagerParameters(params);
+                tmf.init(mfp);
+                tms = tmf.getTrustManagers();
+            }
+        }
+        
+        return tms;
+    }
+    
+    /**
+     * Return the initialization parameters for the TrustManager.
+     * Currently, only the default <code>PKIX</code> is supported.
+     * 
+     * @param algorithm The algorithm to get parameters for.
+     * @param crlf The path to the CRL file.
+     * @param trustStore The configured TrustStore.
+     * @return The parameters including the CRLs and TrustStore.
+     */
+    protected CertPathParameters getParameters(String algorithm, 
+                                                String crlf, 
+                                                KeyStore trustStore)
+        throws Exception {
+        CertPathParameters params = null;
+        if("PKIX".equalsIgnoreCase(algorithm)) {
+            PKIXBuilderParameters xparams = new PKIXBuilderParameters(trustStore, 
+                                                                     new X509CertSelector());
+            Collection crls = getCRLs(crlf);
+            CertStoreParameters csp = new CollectionCertStoreParameters(crls);
+            CertStore store = CertStore.getInstance("Collection", csp);
+            xparams.addCertStore(store);
+            xparams.setRevocationEnabled(true);
+            String trustLength = (String)attributes.get("trustMaxCertLength");
+            if(trustLength != null) {
+                try {
+                    xparams.setMaxPathLength(Integer.parseInt(trustLength));
+                } catch(Exception ex) {
+                    log.warn("Bad maxCertLength: "+trustLength);
+                }
+            }
+
+            params = xparams;
+        } else {
+            throw new CRLException("CRLs not supported for type: "+algorithm);
+        }
+        return params;
+    }
+
+
+    /**
+     * Load the collection of CRLs.
+     * 
+     */
+    protected Collection<? extends CRL> getCRLs(String crlf) 
+        throws IOException, CRLException, CertificateException {
+
+        File crlFile = new File(crlf);
+        if( !crlFile.isAbsolute() ) {
+            crlFile = new File(System.getProperty("catalina.base"), crlf);
+        }
+        Collection<? extends CRL> crls = null;
+        InputStream is = null;
+        try {
+            CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            is = new FileInputStream(crlFile);
+            crls = cf.generateCRLs(is);
+        } catch(IOException iex) {
+            throw iex;
+        } catch(CRLException crle) {
+            throw crle;
+        } catch(CertificateException ce) {
+            throw ce;
+        } finally { 
+            if(is != null) {
+                try{
+                    is.close();
+                } catch(Exception ex) {
+                }
+            }
+        }
+        return crls;
+    }
+
+    /**
+     * Set the SSL protocol variants to be enabled.
+     * @param socket the SSLServerSocket.
+     * @param protocols the protocols to use.
+     */
+    protected void setEnabledProtocols(SSLServerSocket socket, String []protocols){
+        if (protocols != null) {
+            socket.setEnabledProtocols(protocols);
+        }
+    }
+
+    /**
+     * Determines the SSL protocol variants to be enabled.
+     *
+     * @param socket The socket to get supported list from.
+     * @param requestedProtocols Comma-separated list of requested SSL
+     * protocol variants
+     *
+     * @return Array of SSL protocol variants to be enabled, or null if none of
+     * the requested protocol variants are supported
+     */
+    protected String[] getEnabledProtocols(SSLServerSocket socket,
+                                           String requestedProtocols){
+        String[] supportedProtocols = socket.getSupportedProtocols();
+
+        String[] enabledProtocols = null;
+
+        if (requestedProtocols != null) {
+            Vector vec = null;
+            String protocol = requestedProtocols;
+            int index = requestedProtocols.indexOf(',');
+            if (index != -1) {
+                int fromIndex = 0;
+                while (index != -1) {
+                    protocol = requestedProtocols.substring(fromIndex, index).trim();
+                    if (protocol.length() > 0) {
+                        /*
+                         * Check to see if the requested protocol is among the
+                         * supported protocols, i.e., may be enabled
+                         */
+                        for (int i=0; supportedProtocols != null
+                                     && i<supportedProtocols.length; i++) {
+                            if (supportedProtocols[i].equals(protocol)) {
+                                if (vec == null) {
+                                    vec = new Vector();
+                                }
+                                vec.addElement(protocol);
+                                break;
+                            }
+                        }
+                    }
+                    fromIndex = index+1;
+                    index = requestedProtocols.indexOf(',', fromIndex);
+                } // while
+                protocol = requestedProtocols.substring(fromIndex);
+            }
+
+            if (protocol != null) {
+                protocol = protocol.trim();
+                if (protocol.length() > 0) {
+                    /*
+                     * Check to see if the requested protocol is among the
+                     * supported protocols, i.e., may be enabled
+                     */
+                    for (int i=0; supportedProtocols != null
+                                 && i<supportedProtocols.length; i++) {
+                        if (supportedProtocols[i].equals(protocol)) {
+                            if (vec == null) {
+                                vec = new Vector();
+                            }
+                            vec.addElement(protocol);
+                            break;
+                        }
+                    }
+                }
+            }           
+
+            if (vec != null) {
+                enabledProtocols = new String[vec.size()];
+                vec.copyInto(enabledProtocols);
+            }
+        }
+
+        return enabledProtocols;
+    }
+
+    /**
+     * Configure Client authentication for this version of JSSE.  The
+     * JSSE included in Java 1.4 supports the 'want' value.  Prior
+     * versions of JSSE will treat 'want' as 'false'.
+     * @param socket the SSLServerSocket
+     */
+    protected void configureClientAuth(SSLServerSocket socket){
+        if (wantClientAuth){
+            socket.setWantClientAuth(wantClientAuth);
+        } else {
+            socket.setNeedClientAuth(requireClientAuth);
+        }
+    }
+
+    /**
+     * Configure Client authentication for this version of JSSE.  The
+     * JSSE included in Java 1.4 supports the 'want' value.  Prior
+     * versions of JSSE will treat 'want' as 'false'.
+     * @param socket the SSLSocket
+     */
+    protected void configureClientAuth(SSLSocket socket){
+        // Per JavaDocs: SSLSockets returned from 
+        // SSLServerSocket.accept() inherit this setting.
+    }
+    
+    /**
+     * Configures the given SSL server socket with the requested cipher suites,
+     * protocol versions, and need for client authentication
+     */
+    private void initServerSocket(ServerSocket ssocket) {
+
+        SSLServerSocket socket = (SSLServerSocket) ssocket;
+
+        if (enabledCiphers != null) {
+            socket.setEnabledCipherSuites(enabledCiphers);
+        }
+
+        String requestedProtocols = (String) attributes.get("protocols");
+        setEnabledProtocols(socket, getEnabledProtocols(socket, 
+                                                         requestedProtocols));
+
+        // we don't know if client auth is needed -
+        // after parsing the request we may re-handshake
+        configureClientAuth(socket);
+    }
+
+    /**
+     * Checks that the certificate is compatible with the enabled cipher suites.
+     * If we don't check now, the JIoEndpoint can enter a nasty logging loop.
+     * See bug 45528.
+     */
+    private void checkConfig() throws IOException {
+        // Create an unbound server socket
+        ServerSocket socket = sslProxy.createServerSocket();
+        initServerSocket(socket);
+
+        try {
+            // Set the timeout to 1ms as all we care about is if it throws an
+            // SSLException on accept. 
+            socket.setSoTimeout(1);
+
+            socket.accept();
+            // Will never get here - no client can connect to an unbound port
+        } catch (SSLException ssle) {
+            // SSL configuration is invalid. Possibly cert doesn't match ciphers
+            IOException ioe = new IOException(MESSAGES.invalidSSLConfiguration(ssle.getMessage()));
+            ioe.initCause(ssle);
+            throw ioe;
+        } catch (Exception e) {
+            /*
+             * Possible ways of getting here
+             * socket.accept() throws a SecurityException
+             * socket.setSoTimeout() throws a SocketException
+             * socket.accept() throws some other exception (after a JDK change)
+             *      In these cases the test won't work so carry on - essentially
+             *      the behaviour before this patch
+             * socket.accept() throws a SocketTimeoutException
+             *      In this case all is well so carry on
+             */
+        } finally {
+            // Should be open here but just in case
+            if (!socket.isClosed()) {
+                socket.close();
+            }
+        }
+        
+    }
+
+}

Added: branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSESupport.java	                        (rev 0)
+++ branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/JSSESupport.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -0,0 +1,246 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.tomcat.util.net.jsse;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.SocketException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.security.cert.X509Certificate;
+
+import org.apache.tomcat.util.net.Constants;
+import org.apache.tomcat.util.net.SSLSupport;
+
+/** JSSESupport
+
+   Concrete implementation class for JSSE
+   Support classes.
+
+   This will only work with JDK 1.2 and up since it
+   depends on JDK 1.2's certificate support
+
+   @author EKR
+   @author Craig R. McClanahan
+   @author Filip Hanik
+   Parts cribbed from JSSECertCompat       
+   Parts cribbed from CertificatesValve
+*/
+
+class JSSESupport implements SSLSupport {
+    
+    private static org.jboss.logging.Logger log =
+        org.jboss.logging.Logger.getLogger(JSSESupport.class);
+
+    protected SSLSocket ssl;
+    protected SSLSession session;
+
+    Listener listener = new Listener();
+
+    JSSESupport(SSLSocket sock){
+        ssl=sock;
+        session = sock.getSession();
+        sock.addHandshakeCompletedListener(listener);
+    }
+    
+    JSSESupport(SSLSession session) {
+        this.session = session;
+    }
+
+    public String getCipherSuite() throws IOException {
+        // Look up the current SSLSession
+        if (session == null)
+            return null;
+        return session.getCipherSuite();
+    }
+
+    public Object[] getPeerCertificateChain() 
+        throws IOException {
+        return getPeerCertificateChain(false);
+    }
+
+    protected java.security.cert.X509Certificate [] getX509Certificates(SSLSession session) 
+        throws IOException {
+        Certificate [] certs=null;
+        try {
+            certs = session.getPeerCertificates();
+        } catch( Throwable t ) {
+            log.debug("Error getting client certs",t);
+            return null;
+        }
+        if( certs==null ) return null;
+        
+        java.security.cert.X509Certificate [] x509Certs = 
+            new java.security.cert.X509Certificate[certs.length];
+        for(int i=0; i < certs.length; i++) {
+            if (certs[i] instanceof java.security.cert.X509Certificate ) {
+                // always currently true with the JSSE 1.1.x
+                x509Certs[i] = (java.security.cert.X509Certificate) certs[i];
+            } else {
+                try {
+                    byte [] buffer = certs[i].getEncoded();
+                    CertificateFactory cf =
+                        CertificateFactory.getInstance("X.509");
+                    ByteArrayInputStream stream =
+                        new ByteArrayInputStream(buffer);
+                    x509Certs[i] = (java.security.cert.X509Certificate) cf.generateCertificate(stream);
+                } catch(Exception ex) { 
+                    log.info("Error translating cert " + certs[i], ex);
+                    return null;
+                }
+            }
+            if(log.isTraceEnabled())
+                log.trace("Cert #" + i + " = " + x509Certs[i]);
+        }
+        if(x509Certs.length < 1)
+            return null;
+        return x509Certs;
+    }
+
+    public Object[] getPeerCertificateChain(boolean force)
+        throws IOException {
+        // Look up the current SSLSession
+        if (session == null)
+            return null;
+
+        // Convert JSSE's certificate format to the ones we need
+	X509Certificate [] jsseCerts = null;
+	try {
+	    jsseCerts = session.getPeerCertificateChain();
+	} catch(Exception bex) {
+	    // ignore.
+	}
+	if (jsseCerts == null)
+	    jsseCerts = new X509Certificate[0];
+	if(jsseCerts.length <= 0 && force) {
+	    session.invalidate();
+	    handShake();
+	    session = ssl.getSession();
+	}
+        return getX509Certificates(session);
+    }
+
+    protected void handShake() throws IOException {
+        if( ssl.getWantClientAuth() ) {
+            log.debug("No client cert sent for want");
+        } else {
+            ssl.setNeedClientAuth(true);
+        }
+
+        if (ssl.getEnabledCipherSuites().length == 0) {
+            // Handshake is never going to be successful.
+            // Assume this is because handshakes are disabled
+            log.warn("SSL server initiated renegotiation is disabled, closing connection");
+            session.invalidate();
+            ssl.close();
+            return;
+        }
+
+        InputStream in = ssl.getInputStream();
+        int oldTimeout = ssl.getSoTimeout();
+        ssl.setSoTimeout(1000);
+        byte[] b = new byte[0];
+        listener.reset();
+        ssl.startHandshake();
+        int maxTries = 60; // 60 * 1000 = example 1 minute time out
+        for (int i = 0; i < maxTries; i++) {
+        if(log.isTraceEnabled())
+            log.trace("Reading for try #" +i);
+            try {
+                int x = in.read(b);
+            } catch(SSLException sslex) {
+                log.info("SSL Error getting client Certs",sslex);
+                throw sslex;
+            } catch (IOException e) {
+                // ignore - presumably the timeout
+            }
+            if (listener.completed) {
+                break;
+            }
+        }
+        ssl.setSoTimeout(oldTimeout);
+        if (listener.completed == false) {
+            throw new SocketException("SSL Cert handshake timeout");
+        }
+
+    }
+
+    /**
+     * Copied from <code>org.apache.catalina.valves.CertificateValve</code>
+     */
+    public Integer getKeySize() 
+        throws IOException {
+        // Look up the current SSLSession
+        SSLSupport.CipherData c_aux[]=ciphers;
+        if (session == null)
+            return null;
+        Integer keySize = (Integer) session.getValue(Constants.KEY_SIZE_KEY);
+        if (keySize == null) {
+            int size = 0;
+            String cipherSuite = session.getCipherSuite();
+            for (int i = 0; i < c_aux.length; i++) {
+                if (cipherSuite.indexOf(c_aux[i].phrase) >= 0) {
+                    size = c_aux[i].keySize;
+                    break;
+                }
+            }
+            keySize = new Integer(size);
+            session.putValue(Constants.KEY_SIZE_KEY, keySize);
+        }
+        return keySize;
+    }
+
+    public String getSessionId()
+        throws IOException {
+        // Look up the current SSLSession
+        if (session == null)
+            return null;
+        // Expose ssl_session (getId)
+        byte [] ssl_session = session.getId();
+        if ( ssl_session == null) 
+            return null;
+        StringBuilder buf=new StringBuilder("");
+        for(int x=0; x<ssl_session.length; x++) {
+            String digit=Integer.toHexString((int)ssl_session[x]);
+            if (digit.length()<2) buf.append('0');
+            if (digit.length()>2) digit=digit.substring(digit.length()-2);
+            buf.append(digit);
+        }
+        return buf.toString();
+    }
+
+
+    private static class Listener implements HandshakeCompletedListener {
+        volatile boolean completed = false;
+        public void handshakeCompleted(HandshakeCompletedEvent event) {
+            completed = true;
+        }
+        void reset() {
+            completed = false;
+        }
+    }
+
+}
+

Modified: branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/NioJSSESocketChannelFactory.java
===================================================================
--- branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/NioJSSESocketChannelFactory.java	2012-09-25 12:49:25 UTC (rev 2086)
+++ branches/7.2.x/src/main/java/org/apache/tomcat/util/net/jsse/NioJSSESocketChannelFactory.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -112,7 +112,9 @@
 					break;
 				}
 			}
-		} catch (NoSuchAlgorithmException | KeyManagementException e) {
+		} catch (NoSuchAlgorithmException e) {
+            // Assume no RFC 5746 support
+		} catch (KeyManagementException e) {
 			// Assume no RFC 5746 support
 		}
 		RFC_5746_SUPPORTED = result;
@@ -362,7 +364,7 @@
 						for (int i = 0; supportedCiphers != null && i < supportedCiphers.length; i++) {
 							if (supportedCiphers[i].equals(cipher)) {
 								if (vec == null) {
-									vec = new Vector<>();
+									vec = new Vector<Object>();
 								}
 								vec.addElement(cipher);
 								break;
@@ -385,7 +387,7 @@
 					for (int i = 0; supportedCiphers != null && i < supportedCiphers.length; i++) {
 						if (supportedCiphers[i].equals(cipher)) {
 							if (vec == null) {
-								vec = new Vector<>();
+								vec = new Vector<Object>();
 							}
 							vec.addElement(cipher);
 							break;

Modified: branches/7.2.x/src/main/java/org/jboss/web/CatalinaLogger.java
===================================================================
--- branches/7.2.x/src/main/java/org/jboss/web/CatalinaLogger.java	2012-09-25 12:49:25 UTC (rev 2086)
+++ branches/7.2.x/src/main/java/org/jboss/web/CatalinaLogger.java	2012-09-26 13:54:12 UTC (rev 2087)
@@ -653,4 +653,8 @@
     @Message(id = 1140, value = "Failed protocol handler [%s] JMX registration.")
     void failedProtocolJmxRegistration(Object objectName, @Cause Throwable t);
 
+    @LogMessage(level = INFO)
+    @Message(id = 1141, value = "NIO 2 is not available, the java.io connector will be used insteaf")
+    void usingJavaIoConnector();
+
 }

Modified: branches/7.2.x/webapps/docs/changelog.xml
===================================================================
--- branches/7.2.x/webapps/docs/changelog.xml	2012-09-25 12:49:25 UTC (rev 2086)
+++ branches/7.2.x/webapps/docs/changelog.xml	2012-09-26 13:54:12 UTC (rev 2087)
@@ -16,7 +16,14 @@
 
 <body>
 
-<section name="JBoss Web 7.7.0.Alpha1 (remm)">
+<section name="JBoss Web 7.2.0.Alpha1 (remm)">
+  <subsection name="General">
+    <changelog>
+      <add>
+         Switch to JBoss Logging for i18n. (remm)
+      </add>
+    </changelog>
+  </subsection>
   <subsection name="Catalina">
     <changelog>
       <fix>
@@ -65,9 +72,6 @@
       <add>
          NIO 2 HTTP connector. (benothman)
       </add>
-      <add>
-         Drop java.io connectors (AJP and HTTP). AJP now requires native. (remm)
-      </add>
     </changelog>
   </subsection>
   <subsection name="Jasper">



More information about the jbossweb-commits mailing list