You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by rg...@apache.org on 2014/08/02 17:36:15 UTC

svn commit: r1615322 - in /qpid/trunk/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin: HttpManagement.java connector/ connector/TcpAndSslSelectChannelConnector.java

Author: rgodfrey
Date: Sat Aug  2 15:36:14 2014
New Revision: 1615322

URL: http://svn.apache.org/r1615322
Log:
QPID-5955 : [Java Broker] Allow HTTP Management to run TCP and SSL on the same port

Added:
    qpid/trunk/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/connector/
    qpid/trunk/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/connector/TcpAndSslSelectChannelConnector.java
Modified:
    qpid/trunk/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java

Modified: qpid/trunk/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java?rev=1615322&r1=1615321&r2=1615322&view=diff
==============================================================================
--- qpid/trunk/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java (original)
+++ qpid/trunk/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java Sat Aug  2 15:36:14 2014
@@ -41,6 +41,7 @@ import org.eclipse.jetty.server.Connecto
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.SessionManager;
 import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
 import org.eclipse.jetty.server.ssl.SslSocketConnector;
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.ServletContextHandler;
@@ -49,6 +50,7 @@ import org.eclipse.jetty.util.ssl.SslCon
 
 import org.apache.qpid.server.configuration.IllegalConfigurationException;
 import org.apache.qpid.server.logging.messages.ManagementConsoleMessages;
+import org.apache.qpid.server.management.plugin.connector.TcpAndSslSelectChannelConnector;
 import org.apache.qpid.server.management.plugin.filter.ForbiddingAuthorisationFilter;
 import org.apache.qpid.server.management.plugin.filter.RedirectingAuthorisationFilter;
 import org.apache.qpid.server.management.plugin.servlet.DefinedFileServlet;
@@ -380,7 +382,9 @@ public class HttpManagement extends Abst
         {
             throw new ServerScopedRuntimeException("Cannot configure port " + port.getName() + " for transport " + Transport.SSL, e);
         }
-        connector = new SslSocketConnector(factory);
+        connector = port.getTransports().contains(Transport.TCP)
+                ? new TcpAndSslSelectChannelConnector(factory)
+                : new SslSelectChannelConnector(factory);
         return connector;
     }
 

Added: qpid/trunk/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/connector/TcpAndSslSelectChannelConnector.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/connector/TcpAndSslSelectChannelConnector.java?rev=1615322&view=auto
==============================================================================
--- qpid/trunk/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/connector/TcpAndSslSelectChannelConnector.java (added)
+++ qpid/trunk/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/connector/TcpAndSslSelectChannelConnector.java Sat Aug  2 15:36:14 2014
@@ -0,0 +1,356 @@
+package org.apache.qpid.server.management.plugin.connector;
+
+import java.io.IOException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSession;
+
+import org.eclipse.jetty.http.HttpSchemes;
+import org.eclipse.jetty.io.AsyncEndPoint;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.nio.AsyncConnection;
+import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
+import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
+import org.eclipse.jetty.io.nio.SelectorManager;
+import org.eclipse.jetty.io.nio.SslConnection;
+import org.eclipse.jetty.server.AsyncHttpConnection;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.server.ssl.SslCertificates;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class TcpAndSslSelectChannelConnector extends SelectChannelConnector
+{
+
+    private static final Logger LOG = Log.getLogger(TcpAndSslSelectChannelConnector.class);
+
+    private final SslContextFactory _sslContextFactory;
+
+    public TcpAndSslSelectChannelConnector(SslContextFactory factory)
+    {
+        _sslContextFactory = factory;
+        addBean(_sslContextFactory);
+        setUseDirectBuffers(false);
+        setSoLingerTime(30000);
+    }
+
+
+    @Override
+    public void customize(EndPoint endpoint, Request request) throws IOException
+    {
+        if(endpoint instanceof SslConnection.SslEndPoint)
+        {
+            request.setScheme(HttpSchemes.HTTPS);
+        }
+
+        super.customize(endpoint,request);
+
+        if(endpoint instanceof SslConnection.SslEndPoint)
+        {
+            SslConnection.SslEndPoint sslEndpoint = (SslConnection.SslEndPoint) endpoint;
+            SSLEngine sslEngine = sslEndpoint.getSslEngine();
+            SSLSession sslSession = sslEngine.getSession();
+
+            SslCertificates.customize(sslSession, endpoint, request);
+        }
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    @Override
+    protected AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint)
+    {
+        return new ProtocolIdentifyingConnection((ProtocolIdentifyingEndpoint) endpoint);
+    }
+
+    @Override
+    protected SelectChannelEndPoint newEndPoint(final SocketChannel channel,
+                                                final SelectorManager.SelectSet selectSet,
+                                                final SelectionKey key) throws IOException
+    {
+
+        SelectChannelEndPoint endp = new ProtocolIdentifyingEndpoint(channel,selectSet,key, getMaxIdleTime());
+        endp.setConnection(selectSet.getManager().newConnection(channel,endp, key.attachment()));
+        return endp;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param channel A channel which if passed is used as to extract remote
+     * host and port for the purposes of SSL session caching
+     * @return A SSLEngine for a new or cached SSL Session
+     * @throws IOException if the SSLEngine cannot be created
+     */
+    protected SSLEngine createSSLEngine(SocketChannel channel) throws IOException
+    {
+        SSLEngine engine;
+        if (channel != null)
+        {
+            String peerHost = channel.socket().getInetAddress().getHostAddress();
+            int peerPort = channel.socket().getPort();
+            engine = _sslContextFactory.newSslEngine(peerHost, peerPort);
+        }
+        else
+        {
+            engine = _sslContextFactory.newSslEngine();
+        }
+
+        engine.setUseClientMode(false);
+        return engine;
+    }
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        _sslContextFactory.checkKeyStore();
+        _sslContextFactory.start();
+
+        SSLEngine sslEngine = _sslContextFactory.newSslEngine();
+
+        sslEngine.setUseClientMode(false);
+
+        SSLSession sslSession = sslEngine.getSession();
+
+        if (getRequestHeaderSize()<sslSession.getApplicationBufferSize())
+            setRequestHeaderSize(sslSession.getApplicationBufferSize());
+        if (getRequestBufferSize()<sslSession.getApplicationBufferSize())
+            setRequestBufferSize(sslSession.getApplicationBufferSize());
+
+        super.doStart();
+    }
+
+    enum Protocol { UNKNOWN, TCP , SSL }
+
+    private class ProtocolIdentifyingEndpoint extends SelectChannelEndPoint
+    {
+
+        private Protocol _protocol = Protocol.UNKNOWN;
+        private Buffer _preBuffer = new IndirectNIOBuffer(6);
+
+        public ProtocolIdentifyingEndpoint(final SocketChannel channel,
+                                           final SelectorManager.SelectSet selectSet,
+                                           final SelectionKey key, final int maxIdleTime) throws IOException
+        {
+            super(channel, selectSet, key, maxIdleTime);
+        }
+
+        public Protocol getProtocol() throws IOException
+        {
+            if(_protocol == Protocol.UNKNOWN)
+            {
+                if(_preBuffer.space() != 0)
+                {
+                    super.fill(_preBuffer);
+                    _protocol = identifyFromPreBuffer();
+                }
+            }
+            return _protocol;
+        }
+
+        public SocketChannel getSocketChannel()
+        {
+            return (SocketChannel) getChannel();
+        }
+
+        private Protocol identifyFromPreBuffer()
+        {
+            if(_preBuffer.space() == 0)
+            {
+                byte[] helloBytes = _preBuffer.array();
+                if (looksLikeSSLv2ClientHello(helloBytes) || looksLikeSSLv3ClientHello(helloBytes))
+                {
+                    return Protocol.SSL;
+                }
+                else
+                {
+                    return Protocol.TCP;
+                }
+            }
+            return Protocol.UNKNOWN;
+        }
+
+        private boolean looksLikeSSLv3ClientHello(byte[] headerBytes)
+        {
+            return headerBytes[0] == 22 && // SSL Handshake
+                   (headerBytes[1] == 3 && // SSL 3.0 / TLS 1.x
+                    (headerBytes[2] == 0 || // SSL 3.0
+                     headerBytes[2] == 1 || // TLS 1.0
+                     headerBytes[2] == 2 || // TLS 1.1
+                     headerBytes[2] == 3)) && // TLS1.2
+                   (headerBytes[5] == 1); // client_hello
+        }
+
+        private boolean looksLikeSSLv2ClientHello(byte[] headerBytes)
+        {
+            return headerBytes[0] == -128 &&
+                   headerBytes[3] == 3 && // SSL 3.0 / TLS 1.x
+                   (headerBytes[4] == 0 || // SSL 3.0
+                    headerBytes[4] == 1 || // TLS 1.0
+                    headerBytes[4] == 2 || // TLS 1.1
+                    headerBytes[4] == 3);
+        }
+
+        @Override
+        public int fill(final Buffer buffer) throws IOException
+        {
+            int size = 0;
+
+            if(getProtocol() != Protocol.UNKNOWN)
+            {
+                if (_preBuffer.hasContent())
+                {
+                    size = buffer.put(_preBuffer);
+                    _preBuffer.skip(size);
+                }
+                if (buffer.space() != 0)
+                {
+                    size += super.fill(buffer);
+                }
+            }
+            return size;
+        }
+    }
+
+    private class ProtocolIdentifyingConnection implements AsyncConnection
+    {
+        private final ProtocolIdentifyingEndpoint _endpoint;
+        private AsyncConnection _delegate;
+        private final long _timestamp;
+        private IOException _exception;
+
+        private ProtocolIdentifyingConnection(final ProtocolIdentifyingEndpoint endpoint)
+        {
+            _endpoint = endpoint;
+            _timestamp = System.currentTimeMillis();
+        }
+
+        @Override
+        public void onInputShutdown() throws IOException
+        {
+            if (_delegate == null)
+            {
+                createDelegate(true);
+            }
+            _delegate.onInputShutdown();
+        }
+
+        private boolean createDelegate(boolean createPlainWhenUnknown) throws IOException
+        {
+            if(_exception != null)
+            {
+                throw _exception;
+            }
+            Protocol protocol = _endpoint.getProtocol();
+            if(protocol == Protocol.TCP || (createPlainWhenUnknown && protocol == Protocol.UNKNOWN))
+            {
+                // shutdown before enough info arrived to make a decision - just create a non-SSL connection anyway
+                _delegate = new AsyncHttpConnection(TcpAndSslSelectChannelConnector.this, _endpoint, getServer());
+                return true;
+            }
+            else if(protocol == Protocol.SSL)
+            {
+                SocketChannel channel = _endpoint.getSocketChannel();
+                SSLEngine engine = createSSLEngine(channel);
+                SslConnection connection = new SslConnection(engine, _endpoint);
+                AsyncConnection delegate = new AsyncHttpConnection(TcpAndSslSelectChannelConnector.this,
+                                                                   connection.getSslEndPoint(),
+                                                                   getServer());
+                connection.getSslEndPoint().setConnection(delegate);
+                connection.setAllowRenegotiate(_sslContextFactory.isAllowRenegotiate());
+
+                _delegate = connection;
+                return true;
+            }
+            return false;
+        }
+
+        private boolean createDelegateNoException()
+        {
+            try
+            {
+                return createDelegate(false);
+            }
+            catch (IOException e)
+            {
+                _exception = e;
+                return false;
+            }
+        }
+
+        @Override
+        public Connection handle() throws IOException
+        {
+            if(_delegate != null || createDelegate(false))
+            {
+                return _delegate.handle();
+            }
+            return this;
+        }
+
+        @Override
+        public long getTimeStamp()
+        {
+            return _timestamp;
+        }
+
+        @Override
+        public boolean isIdle()
+        {
+            if(_delegate != null || createDelegateNoException())
+            {
+                return _delegate.isIdle();
+            }
+            return false;
+        }
+
+        @Override
+        public boolean isSuspended()
+        {
+            if(_delegate != null || createDelegateNoException())
+            {
+                return _delegate.isSuspended();
+            }
+            return false;
+        }
+
+        @Override
+        public void onClose()
+        {
+            if(_delegate != null)
+            {
+                _delegate.onClose();
+            }
+        }
+
+        @Override
+        public void onIdleExpired(final long idleForMs)
+        {
+            try
+            {
+                if(_delegate != null || createDelegate(true))
+                {
+                    _delegate.onIdleExpired(idleForMs);
+                }
+            }
+            catch (IOException e)
+            {
+                LOG.ignore(e);
+
+                try
+                {
+                    _endpoint.close();
+                }
+                catch(IOException e2)
+                {
+                    LOG.ignore(e2);
+                }
+            }
+        }
+    }
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org