Author: markt Date: Mon Apr 13 13:53:52 2015 New Revision: 1673191 URL: http://svn.apache.org/r1673191 Log: SNI support for NIO2
Modified: tomcat/trunk/java/org/apache/tomcat/util/net/Nio2Endpoint.java tomcat/trunk/java/org/apache/tomcat/util/net/SecureNio2Channel.java Modified: tomcat/trunk/java/org/apache/tomcat/util/net/Nio2Endpoint.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/Nio2Endpoint.java?rev=1673191&r1=1673190&r2=1673191&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/Nio2Endpoint.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/Nio2Endpoint.java Mon Apr 13 13:53:52 2015 @@ -422,27 +422,15 @@ public class Nio2Endpoint extends Abstra Nio2Channel channel = (useCaches) ? nioChannels.pop() : null; if (channel == null) { - // SSL setup + SocketBufferHandler bufhandler = new SocketBufferHandler( + socketProperties.getAppReadBufSize(), + socketProperties.getAppWriteBufSize(), + socketProperties.getDirectBuffer()); if (isSSLEnabled()) { - SSLEngine engine = createSSLEngine(); - int appBufferSize = engine.getSession().getApplicationBufferSize(); - SocketBufferHandler bufhandler = new SocketBufferHandler( - Math.max(appBufferSize, socketProperties.getAppReadBufSize()), - Math.max(appBufferSize, socketProperties.getAppWriteBufSize()), - socketProperties.getDirectBuffer()); - channel = new SecureNio2Channel(engine, bufhandler, this); + channel = new SecureNio2Channel(bufhandler, this); } else { - SocketBufferHandler bufhandler = new SocketBufferHandler( - socketProperties.getAppReadBufSize(), - socketProperties.getAppWriteBufSize(), - socketProperties.getDirectBuffer()); channel = new Nio2Channel(bufhandler); } - } else { - if (isSSLEnabled()) { - SSLEngine engine = createSSLEngine(); - ((SecureNio2Channel) channel).setSSLEngine(engine); - } } Nio2SocketWrapper socketWrapper = new Nio2SocketWrapper(channel, this); channel.reset(socket, socketWrapper); Modified: tomcat/trunk/java/org/apache/tomcat/util/net/SecureNio2Channel.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/SecureNio2Channel.java?rev=1673191&r1=1673190&r2=1673191&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/SecureNio2Channel.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/SecureNio2Channel.java Mon Apr 13 13:53:52 2015 @@ -34,6 +34,10 @@ import javax.net.ssl.SSLEngineResult.Han import javax.net.ssl.SSLEngineResult.Status; import javax.net.ssl.SSLException; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.buf.ByteBufferUtils; +import org.apache.tomcat.util.net.SNIExtractor.SNIResult; import org.apache.tomcat.util.res.StringManager; /** @@ -41,8 +45,12 @@ import org.apache.tomcat.util.res.String */ public class SecureNio2Channel extends Nio2Channel { - protected static final StringManager sm = StringManager.getManager( - SecureNio2Channel.class.getPackage().getName()); + private static final Log log = LogFactory.getLog(SecureNio2Channel.class); + private static final StringManager sm = StringManager.getManager(SecureNio2Channel.class); + + // Value determined by observation of what the SSL Engine requested in + // various scenarios + private static final int DEFAULT_NET_BUFFER_SIZE = 16921; protected ByteBuffer netInBuffer; protected ByteBuffer netOutBuffer; @@ -63,18 +71,15 @@ public class SecureNio2Channel extends N private CompletionHandler<Integer, SocketWrapperBase<Nio2Channel>> handshakeReadCompletionHandler; private CompletionHandler<Integer, SocketWrapperBase<Nio2Channel>> handshakeWriteCompletionHandler; - public SecureNio2Channel(SSLEngine engine, SocketBufferHandler bufHandler, - Nio2Endpoint endpoint0) { + public SecureNio2Channel(SocketBufferHandler bufHandler, Nio2Endpoint endpoint) { super(bufHandler); - sslEngine = engine; - endpoint = endpoint0; - int netBufSize = sslEngine.getSession().getPacketBufferSize(); + this.endpoint = endpoint; if (endpoint.getSocketProperties().getDirectSslBuffer()) { - netInBuffer = ByteBuffer.allocateDirect(netBufSize); - netOutBuffer = ByteBuffer.allocateDirect(netBufSize); + netInBuffer = ByteBuffer.allocateDirect(DEFAULT_NET_BUFFER_SIZE); + netOutBuffer = ByteBuffer.allocateDirect(DEFAULT_NET_BUFFER_SIZE); } else { - netInBuffer = ByteBuffer.allocate(netBufSize); - netOutBuffer = ByteBuffer.allocate(netBufSize); + netInBuffer = ByteBuffer.allocate(DEFAULT_NET_BUFFER_SIZE); + netOutBuffer = ByteBuffer.allocate(DEFAULT_NET_BUFFER_SIZE); } handshakeReadCompletionHandler = new CompletionHandler<Integer, SocketWrapperBase<Nio2Channel>>() { @Override @@ -114,18 +119,12 @@ public class SecureNio2Channel extends N public void reset(AsynchronousSocketChannel channel, SocketWrapperBase<Nio2Channel> socket) throws IOException { super.reset(channel, socket); - netOutBuffer.position(0); - netOutBuffer.limit(0); - netInBuffer.position(0); - netInBuffer.limit(0); + sniComplete = false; handshakeComplete = false; closed = false; closing = false; readPending = false; writePending = false; - //initiate handshake - sslEngine.beginHandshake(); - handshakeStatus = sslEngine.getHandshakeStatus(); } @@ -309,7 +308,70 @@ public class SecureNio2Channel extends N * present and, if it is, what host name has been requested. Based on the * provided host name, configure the SSLEngine for this connection. */ - private int processSNI() { + private int processSNI() throws IOException { + // If there is no data to process, trigger a read immediately. This is + // an optimisation for the typical case so we don't create an + // SNIExtractor only to discover there is no data to process + if (netInBuffer.position() == 0) { + sc.read(netInBuffer, socket, handshakeReadCompletionHandler); + return 1; + } + + SNIExtractor extractor = new SNIExtractor(netInBuffer); + + while (extractor.getResult() == SNIResult.UNDERFLOW) { + // extractor needed more data to process but netInBuffer was full so + // double the size of the buffer and read some more data. + log.info(sm.getString("channel.nio.ssl.expandNetInBuffer", + Integer.toString(netInBuffer.capacity() * 2))); + + netInBuffer = ByteBufferUtils.expand(netInBuffer); + sc.read(netInBuffer); + extractor = new SNIExtractor(netInBuffer); + } + + String hostName = null; + switch (extractor.getResult()) { + case FOUND: + hostName = extractor.getSNIValue(); + break; + case NOT_PRESENT: + // NO-OP + break; + case NEED_READ: + sc.read(netInBuffer, socket, handshakeReadCompletionHandler); + return 1; + case UNDERFLOW: + // Can't happen. Buffer would have been expanded above. + break; + } + + // TODO: Extract the correct configuration for the requested host name + // and set up the SSLEngine accordingly. At that point this can + // become a debug level message. + log.info("SNI hostname was [" + hostName + "]"); + + sslEngine = endpoint.createSSLEngine(); + + // Ensure the application buffers (which have to be created earlier) are + // big enough. + bufHandler.expand(sslEngine.getSession().getApplicationBufferSize()); + if (netOutBuffer.capacity() < sslEngine.getSession().getApplicationBufferSize()) { + // Info for now as we may need to increase DEFAULT_NET_BUFFER_SIZE + log.info(sm.getString("channel.nio.ssl.expandNetOutBuffer", + Integer.toString(sslEngine.getSession().getApplicationBufferSize()))); + } + netInBuffer = ByteBufferUtils.expand(netInBuffer, sslEngine.getSession().getPacketBufferSize()); + netOutBuffer = ByteBufferUtils.expand(netOutBuffer, sslEngine.getSession().getPacketBufferSize()); + + // Set limit and position to expected values + netOutBuffer.position(0); + netOutBuffer.limit(0); + + // Initiate handshake + sslEngine.beginHandshake(); + handshakeStatus = sslEngine.getHandshakeStatus(); + return 0; } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org