Hi Alexey,
see inline. First, gigantic thanks for this great works!! Most of my
comments relate to logging, the most important one is about certs and
protocols.
Thanks!
-- Jeanfrancois
Oleksiy Stashok wrote:
> Hello,
>
> please review client SSL implementation[attached ssl.new] and code
> change I propose additionally [attached ssl.diff].
>
> List of changes:
> + add SSLCallbackHandler, where method onHandshake(IOEvent) added ->
> generic <P extends CallbackHandler> was added to interface ConnectorHandler
> + SSLUtils changed, where possible pass SocketChannel instead of
> SelectionKey. As there are situations, where SelectionKey is unknown.
> May be we can do this change for other classes (where it makes sense).
> + IOEvent.DefaultIOEvent implementation added to reduce the number of
> anonym. inner classes.
> + Removed TCP/UDP ConnectorInstanceHandler classes, as they have the
> same implementation. Created
> ConnectorInstanceHandler.ConcurrentQueueDelegateCIH class, which
> implements the common logic and delegates creating of new
> ConnectorHandlers to the provided Callable factory.
>
> + SSL client implementation added
> + SSL client unit test
>
> WBR,
> Alexey.
>
>
>
> # This patch file was generated by NetBeans IDE
> # Following Index: paths are relative to: C:\Projects\Grizzly\trunk\modules\grizzly\src
> # This patch can be applied using context Tools: Patch action on respective folder.
> # It uses platform neutral UTF-8 encoding and \n newlines.
> # Above lines and this line are ignored by the patching process.
> Index: main/java/com/sun/grizzly/SSLCallbackHandler.java
> *** C:\Projects\Grizzly\trunk\modules\grizzly\src\main\java\com\sun\grizzly\SSLCallbackHandler.java Locally New
> --- C:\Projects\Grizzly\trunk\modules\grizzly\src\main\java\com\sun\grizzly\SSLCallbackHandler.java Locally New
> ***************
> *** 1,0 ****
> --- 1,37 ----
> + /*
> + * The contents of this file are subject to the terms
> + * of the Common Development and Distribution License
> + * (the License). You may not use this file except in
> + * compliance with the License.
> + *
> + * You can obtain a copy of the license at
> + * https://glassfish.dev.java.net/public/CDDLv1.0.html or
> + * glassfish/bootstrap/legal/CDDLv1.0.txt.
> + * See the License for the specific language governing
> + * permissions and limitations under the License.
> + *
> + * When distributing Covered Code, include this CDDL
> + * Header Notice in each file and include the License file
> + * at glassfish/bootstrap/legal/CDDLv1.0.txt.
> + * If applicable, add the following below the CDDL Header,
> + * with the fields enclosed by brackets [] replaced by
> + * you own identifying information:
> + * "Portions Copyrighted [year] [name of copyright owner]"
> + *
> + * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
> + */
> +
> + package com.sun.grizzly;
> +
> + /**
> + * @author Alexey Stashok
> + */
> + public interface SSLCallbackHandler<E> extends CallbackHandler<E> {
> + /**
> + * This method is called when an non blocking OP_CONNECT is ready
> + * to get processed.
> + * @param ioEvent an object containing information about the current
> + * non blocking connection.
> + */
> + public void onHandshake(IOEvent<E> ioEvent);
> + }
> Index: main/java/com/sun/grizzly/SSLConfig.java
> *** C:\Projects\Grizzly\trunk\modules\grizzly\src\main\java\com\sun\grizzly\SSLConfig.java Locally New
> --- C:\Projects\Grizzly\trunk\modules\grizzly\src\main\java\com\sun\grizzly\SSLConfig.java Locally New
> ***************
> *** 1,0 ****
> --- 1,183 ----
> + /*
> + * The contents of this file are subject to the terms
> + * of the Common Development and Distribution License
> + * (the License). You may not use this file except in
> + * compliance with the License.
> + *
> + * You can obtain a copy of the license at
> + * https://glassfish.dev.java.net/public/CDDLv1.0.html or
> + * glassfish/bootstrap/legal/CDDLv1.0.txt.
> + * See the License for the specific language governing
> + * permissions and limitations under the License.
> + *
> + * When distributing Covered Code, include this CDDL
> + * Header Notice in each file and include the License file
> + * at glassfish/bootstrap/legal/CDDLv1.0.txt.
> + * If applicable, add the following below the CDDL Header,
> + * with the fields enclosed by brackets [] replaced by
> + * you own identifying information:
> + * "Portions Copyrighted [year] [name of copyright owner]"
> + *
> + * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
> + */
> +
> + package com.sun.grizzly;
> +
> + import java.io.FileInputStream;
> + import java.security.KeyStore;
> + import javax.net.ssl.KeyManagerFactory;
> + import javax.net.ssl.SSLContext;
> + import javax.net.ssl.TrustManagerFactory;
> +
> + /**
> + * @author Alexey Stashok
> + */
> + public class SSLConfig {
> + public static SSLConfig DEFAULT_CONFIG = new SSLConfig();
> +
> + private String trustStoreType = "JKS";
> + private String keyStoreType = "JKS";
> +
> + private char[] trustStorePass = "changeit".toCharArray();
> + private char[] keyStorePass = "changeit".toCharArray();
> +
> + private String trustStoreFile = System.getProperty("javax.net.ssl.trustStore");
> + private String keyStoreFile = System.getProperty("javax.net.ssl.keyStore");
> +
> + private String trustStoreAlgorithm = "SunX509";
> + private String keyStoreAlgorithm = "SunX509";
> +
> + private String securityProtocol = "TLS";
> +
> + private boolean needClientAuth = false;
> +
> + private boolean wantClientAuth = false;
> +
> + public String getTrustStoreType() {
> + return trustStoreType;
> + }
> +
> + public void setTrustStoreType(String trustStoreType) {
> + this.trustStoreType = trustStoreType;
> + }
> +
> + public String getKeyStoreType() {
> + return keyStoreType;
> + }
> +
> + public void setKeyStoreType(String keyStoreType) {
> + this.keyStoreType = keyStoreType;
> + }
> +
> + public String getTrustStorePass() {
> + return new String(trustStorePass);
> + }
> +
> + public void setTrustStorePass(String trustStorePass) {
> + this.trustStorePass = trustStorePass.toCharArray();
> + }
> +
> + public String getKeyStorePass() {
> + return new String(keyStorePass);
> + }
> +
> + public void setKeyStorePass(String keyStorePass) {
> + this.keyStorePass = keyStorePass.toCharArray();
> + }
> +
> + public String getTrustStoreFile() {
> + return trustStoreFile;
> + }
> +
> + public void setTrustStoreFile(String trustStoreFile) {
> + this.trustStoreFile = trustStoreFile;
> + }
> +
> + public String getKeyStoreFile() {
> + return keyStoreFile;
> + }
> +
> + public void setKeyStoreFile(String keyStoreFile) {
> + this.keyStoreFile = keyStoreFile;
> + }
> +
> + public String getTrustStoreAlgorithm() {
> + return trustStoreAlgorithm;
> + }
> +
> + public void setTrustStoreAlgorithm(String trustStoreAlgorithm) {
> + this.trustStoreAlgorithm = trustStoreAlgorithm;
> + }
> +
> + public String getKeyStoreAlgorithm() {
> + return keyStoreAlgorithm;
> + }
> +
> + public void setKeyStoreAlgorithm(String keyStoreAlgorithm) {
> + this.keyStoreAlgorithm = keyStoreAlgorithm;
> + }
> +
> + public String getSecurityProtocol() {
> + return securityProtocol;
> + }
> +
> + public void setSecurityProtocol(String securityProtocol) {
> + this.securityProtocol = securityProtocol;
> + }
> +
> + public boolean isNeedClientAuth() {
> + return needClientAuth;
> + }
> +
> + public void setNeedClientAuth(boolean needClientAuth) {
> + this.needClientAuth = needClientAuth;
> + }
> +
> + public boolean isWantClientAuth() {
> + return wantClientAuth;
> + }
> +
> + public void setWantClientAuth(boolean wantClientAuth) {
> + this.wantClientAuth = wantClientAuth;
> + }
> +
> + public SSLContext createSSLContext() {
> + SSLContext sslContext = null;
> +
> + try{
> + TrustManagerFactory trustManagerFactory = null;
> + KeyManagerFactory keyManagerFactory = null;
> +
> + try {
> + KeyStore trustStore = KeyStore.getInstance(keyStoreType);
> + trustStore.load(new FileInputStream(trustStoreFile),
> + trustStorePass);
> +
> + trustManagerFactory =
> + TrustManagerFactory.getInstance(trustStoreAlgorithm);
> + trustManagerFactory.init(trustStore);
> + } catch (Exception e) {
> + }
> +
Should we log the exception?
> + try {
> + KeyStore keyStore = KeyStore.getInstance(keyStoreType);
> + keyStore.load(new FileInputStream(keyStoreFile),
> + keyStorePass);
> +
> +
> + keyManagerFactory =
> + KeyManagerFactory.getInstance(keyStoreAlgorithm);
> + keyManagerFactory.init(keyStore, keyStorePass);
> + } catch (Exception e) {
> + }
> +
Should we log the exception?
> + sslContext = SSLContext.getInstance(securityProtocol);
> + sslContext.init(keyManagerFactory != null ? keyManagerFactory.getKeyManagers() : null,
> + trustManagerFactory != null ? trustManagerFactory.getTrustManagers() : null,
> + null);
> + } catch(Exception ex){
> + }
> +
Should we log the exception :-)
> + return sslContext;
> + }
> + }
> Index: test/java/com/sun/grizzly/SSLConnectionTest.java
> *** C:\Projects\Grizzly\trunk\modules\grizzly\src\test\java\com\sun\grizzly\SSLConnectionTest.java Locally New
> --- C:\Projects\Grizzly\trunk\modules\grizzly\src\test\java\com\sun\grizzly\SSLConnectionTest.java Locally New
> ***************
> *** 1,0 ****
> --- 1,315 ----
> + /*
> + * The contents of this file are subject to the terms
> + * of the Common Development and Distribution License
> + * (the License). You may not use this file except in
> + * compliance with the License.
> + *
> + * You can obtain a copy of the license at
> + * https://glassfish.dev.java.net/public/CDDLv1.0.html or
> + * glassfish/bootstrap/legal/CDDLv1.0.txt.
> + * See the License for the specific language governing
> + * permissions and limitations under the License.
> + *
> + * When distributing Covered Code, include this CDDL
> + * Header Notice in each file and include the License file
> + * at glassfish/bootstrap/legal/CDDLv1.0.txt.
> + * If applicable, add the following below the CDDL Header,
> + * with the fields enclosed by brackets [] replaced by
> + * you own identifying information:
> + * "Portions Copyrighted [year] [name of copyright owner]"
> + *
> + * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
> + */
> +
> + package com.sun.grizzly;
> +
> + import com.sun.grizzly.filter.SSLReadFilter;
> + import com.sun.grizzly.utils.ControllerUtils;
> + import com.sun.grizzly.utils.SSLEchoFilter;
> + import java.io.IOException;
> + import java.net.InetSocketAddress;
> + import java.net.URL;
> + import java.nio.ByteBuffer;
> + import java.nio.channels.SelectionKey;
> + import java.util.Arrays;
> + import java.util.Enumeration;
> + import java.util.concurrent.CountDownLatch;
> + import java.util.concurrent.TimeUnit;
> + import javax.net.ssl.SSLContext;
> + import junit.framework.TestCase;
> +
> + /**
> + * @author Alexey Stashok
> + */
> + public class SSLConnectionTest extends TestCase {
> +
> + private static final String TRUST_STORE_PROP = "javax.net.ssl.trustStore";
> + private static final String KEY_STORE_PROP = "javax.net.ssl.keyStore";
> +
> + public static final int PORT = 18888;
> + public static final int PACKETS_COUNT = 10;
> + public static final int CLIENTS_COUNT = 99;
> +
> + /**
> + * A <code>SSLCallbackHandler</code> handler invoked by the TCPSelectorHandler
> + * when a non blocking operation is ready to be processed.
> + */
> + private SSLCallbackHandler callbackHandler;
> +
> + private SSLConfig sslConfig;
> +
> +
> + @Override
> + public void setUp() {
> + sslConfig = new SSLConfig();
> + ClassLoader cl = getClass().getClassLoader();
> + // override system properties
> + URL cacertsUrl = cl.getResource("ssltest-cacerts.jks");
> + if (cacertsUrl != null) {
> + sslConfig.setTrustStoreFile(cacertsUrl.getFile());
> + }
> +
> + // override system properties
> + URL keystoreUrl = cl.getResource("ssltest-keystore.jks");
> + if (keystoreUrl != null) {
> + sslConfig.setKeyStoreFile(keystoreUrl.getFile());
> + }
> +
> + SSLConfig.DEFAULT_CONFIG = sslConfig;
> + }
> +
> + public void testSimplePacket() throws IOException {
> +
> + final Controller controller = createSSLController(SSLConfig.DEFAULT_CONFIG.createSSLContext());
> + ControllerUtils.startController(controller);
> + final SSLConnectorHandler sslConnector = (SSLConnectorHandler) controller.
> + acquireConnectorHandler(Controller.Protocol.TLS);
> +
> + try {
> + final byte[] testData = "Hello".getBytes();
> + final byte[] response = new byte[sslConnector.getApplicationBufferSize()];
> +
> + final ByteBuffer writeBB = ByteBuffer.wrap(testData);
> + final ByteBuffer readBB = ByteBuffer.wrap(response);
> + final CountDownLatch handshakeDoneLatch = new CountDownLatch(1);
> + final CountDownLatch responseArrivedLatch = new CountDownLatch(1);
> +
> + callbackHandler = createCallbackHandler(controller, sslConnector, responseArrivedLatch, handshakeDoneLatch, writeBB, readBB);
> +
> + try {
> + sslConnector.connect(new InetSocketAddress("localhost", PORT), callbackHandler);
> + } catch (Throwable t) {
> + t.printStackTrace();
> + }
> +
> + waitOnLatch(handshakeDoneLatch, 10, TimeUnit.SECONDS);
> + assertTrue(sslConnector.isHandshakeDone());
> +
> + long nWrite = sslConnector.write(writeBB, false);
> +
> + long nRead = -1;
> +
> + // All bytes written
> + if (nWrite == testData.length) {
> + nRead = sslConnector.read(readBB, false);
> + }
> +
> + if (nRead != nWrite) {
> + waitOnLatch(responseArrivedLatch, 5, TimeUnit.SECONDS);
> + }
> +
> + assertTrue(Arrays.equals(testData, toArray(readBB)));
> + } finally {
> + try {
> + sslConnector.close();
> + controller.releaseConnectorHandler(sslConnector);
> + controller.stop();
> + } catch (Throwable t) {
> + t.printStackTrace();
> + }
> + }
> + }
> +
> + public void testSeveralPackets() throws IOException {
> + final Controller controller = createSSLController(SSLConfig.DEFAULT_CONFIG.createSSLContext());
> + ControllerUtils.startController(controller);
> + try {
> +
> + for(int i=0; i<CLIENTS_COUNT; i++) {
> + //System.out.println("Client#" + i);
> + final SSLConnectorHandler sslConnector =
> + (SSLConnectorHandler) controller.acquireConnectorHandler(Controller.Protocol.TLS);
> + sslConnector.setController(controller);
> + final byte[] testData = new String("Hello. Client#" + i + " Packet#000").getBytes();
> + final byte[] response = new byte[sslConnector.getApplicationBufferSize()];
> +
> + final ByteBuffer writeBB = ByteBuffer.wrap(testData);
> + final ByteBuffer readBB = ByteBuffer.wrap(response);
> +
> + final CountDownLatch[] responseArrivedLatchHolder = new CountDownLatch[1];
> + final CountDownLatch[] handshakeDoneLatchHolder = new CountDownLatch[1];
> + CountDownLatch handshakeDoneLatch = new CountDownLatch(1);
> + handshakeDoneLatchHolder[0] = handshakeDoneLatch;
> + callbackHandler = createCallbackHandler(controller, sslConnector,
> + responseArrivedLatchHolder, handshakeDoneLatchHolder,
> + writeBB, readBB);
> +
> + try {
> + sslConnector.connect(new InetSocketAddress("localhost", PORT),
> + callbackHandler);
> +
> + waitOnLatch(handshakeDoneLatch, 10, TimeUnit.SECONDS);
> + assertTrue(sslConnector.isHandshakeDone());
> +
> + for(int j=0; j<PACKETS_COUNT; j++) {
> + //System.out.println("Packet#" + j);
> + CountDownLatch responseArrivedLatch = new CountDownLatch(1);
> + responseArrivedLatchHolder[0] = responseArrivedLatch;
> + readBB.clear();
> + writeBB.position(writeBB.limit() - 3);
> + byte[] packetNum = Integer.toString(j).getBytes();
> + writeBB.put(packetNum);
> + writeBB.position(0);
> + long nWrite = sslConnector.write(writeBB, false);
> + long nRead = -1;
> +
> + // All bytes written
> + if (nWrite == testData.length){
> + nRead = sslConnector.read(readBB, false);
> + }
> +
> + if (nRead != nWrite){
> + waitOnLatch(responseArrivedLatch, 15, TimeUnit.SECONDS);
> + }
> + String val1 = new String(testData);
> + String val2 = new String(toArray(readBB));
> + //System.out.println("Assert. client#" + i + " packet#" + j + " Pattern: " + val1 + " Came: " + val2);
> + assertEquals(val1, val2);
> + }
> + } finally {
> + sslConnector.close();
> + controller.releaseConnectorHandler(sslConnector);
> + }
> + }
> + } finally {
> + try{
> + controller.stop();
> + } catch (Throwable t){
> + t.printStackTrace();
> + }
> + }
> + }
> +
> + private Controller createSSLController(SSLContext sslContext) {
> + final SSLReadFilter readFilter = new SSLReadFilter();
> + readFilter.setSSLContext(sslContext);
> +
> + final ProtocolFilter echoFilter = new SSLEchoFilter();
> +
> + SSLSelectorHandler selectorHandler = new SSLSelectorHandler();
> + selectorHandler.setPort(PORT);
> +
> + final Controller controller = new Controller();
> +
> + controller.setSelectorHandler(selectorHandler);
> +
> + controller.setProtocolChainInstanceHandler(new DefaultProtocolChainInstanceHandler() {
> +
> + public ProtocolChain poll() {
> + ProtocolChain protocolChain = protocolChains.poll();
> + if (protocolChain == null) {
> + protocolChain = new DefaultProtocolChain();
> + protocolChain.addFilter(readFilter);
> + protocolChain.addFilter(echoFilter);
> + }
> + return protocolChain;
> + }
> + });
> +
> + return controller;
> + }
> +
> + private SSLCallbackHandler createCallbackHandler(final Controller controller,
> + final SSLConnectorHandler sslConnector,
> + final CountDownLatch responseArrivedLatch,
> + final CountDownLatch handshakeDoneLatch,
> + final ByteBuffer writeBB, final ByteBuffer readBB) {
> +
> + return createCallbackHandler(controller, sslConnector,
> + new CountDownLatch[] {responseArrivedLatch},
> + new CountDownLatch[] {handshakeDoneLatch},
> + writeBB, readBB);
> + }
> +
> + private SSLCallbackHandler createCallbackHandler(final Controller controller,
> + final SSLConnectorHandler sslConnector,
> + final CountDownLatch[] responseArrivedLatchHolder,
> + final CountDownLatch[] handshakeDoneLatchHolder,
> + final ByteBuffer writeBB, final ByteBuffer readBB) {
> +
> + return new SSLCallbackHandler<Context>() {
> +
> + private int readTry;
> +
> + public void onConnect(IOEvent<Context> ioEvent) {
> + SelectionKey key = ioEvent.attachment().getSelectionKey();
> + sslConnector.finishConnect(key);
> + try {
> + if (sslConnector.handshake(readBB, false)) {
> + onHandshake(ioEvent);
> + }
> + } catch (IOException e) {
> + e.printStackTrace();
> + }
> + }
> +
> + public void onRead(IOEvent<Context> ioEvent) {
> + try {
> + long nRead = sslConnector.read(readBB, false);
> +
> + if (nRead > 0 || readTry++ > 2) {
> + responseArrivedLatchHolder[0].countDown();
> + }
> + } catch (IOException ex) {
> + ex.printStackTrace();
> + controller.cancelKey(ioEvent.attachment().getSelectionKey());
> + }
> + }
> +
> + public void onWrite(IOEvent<Context> ioEvent) {
> + SelectionKey key = ioEvent.attachment().getSelectionKey();
> + try {
> + while (writeBB.hasRemaining()) {
> + long nWrite = sslConnector.write(writeBB, false);
> +
> + if (nWrite == 0) {
> + return;
> + }
> + }
> + } catch (IOException ex) {
> + ex.printStackTrace();
> + controller.cancelKey(key);
> + }
> + }
> +
> + public void onHandshake(IOEvent<Context> ioEvent) {
> + readBB.clear();
> + handshakeDoneLatchHolder[0].countDown();
> + }
> + };
> + }
> +
> + public void waitOnLatch(CountDownLatch latch, int timeout, TimeUnit timeUnit) {
> + try {
> + latch.await(timeout, timeUnit);
> + } catch (InterruptedException ex) {
> + ex.printStackTrace();
> + }
> + }
> +
> + private byte[] toArray(ByteBuffer bb) {
> + byte[] buf = new byte[bb.remaining()];
> + bb.get(buf);
> + return buf;
> + }
> + }
> Index: main/java/com/sun/grizzly/SSLConnectorHandler.java
> *** C:\Projects\Grizzly\trunk\modules\grizzly\src\main\java\com\sun\grizzly\SSLConnectorHandler.java Locally New
> --- C:\Projects\Grizzly\trunk\modules\grizzly\src\main\java\com\sun\grizzly\SSLConnectorHandler.java Locally New
> ***************
> *** 1,0 ****
> --- 1,905 ----
> + /*
> + * The contents of this file are subject to the terms
> + * of the Common Development and Distribution License
> + * (the License). You may not use this file except in
> + * compliance with the License.
> + *
> + * You can obtain a copy of the license at
> + * https://glassfish.dev.java.net/public/CDDLv1.0.html or
> + * glassfish/bootstrap/legal/CDDLv1.0.txt.
> + * See the License for the specific language governing
> + * permissions and limitations under the License.
> + *
> + * When distributing Covered Code, include this CDDL
> + * Header Notice in each file and include the License file
> + * at glassfish/bootstrap/legal/CDDLv1.0.txt.
> + * If applicable, add the following below the CDDL Header,
> + * with the fields enclosed by brackets [] replaced by
> + * you own identifying information:
> + * "Portions Copyrighted [year] [name of copyright owner]"
> + *
> + * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
> + */
> + package com.sun.grizzly;
> +
> + import com.sun.grizzly.Controller.Protocol;
> + import com.sun.grizzly.util.ByteBufferInputStream;
> + import com.sun.grizzly.util.OutputWriter;
> + import com.sun.grizzly.util.SSLOutputWriter;
> + import com.sun.grizzly.util.SSLUtils;
> + import java.io.EOFException;
> + import java.io.IOException;
> + import java.net.SocketAddress;
> + import java.nio.ByteBuffer;
> + import java.nio.channels.AlreadyConnectedException;
> + import java.nio.channels.NotYetConnectedException;
> + import java.nio.channels.SelectableChannel;
> + import java.nio.channels.SelectionKey;
> + import java.nio.channels.SocketChannel;
> + import java.util.concurrent.CountDownLatch;
> + import java.util.concurrent.TimeUnit;
> + import javax.net.ssl.SSLContext;
> + import javax.net.ssl.SSLEngine;
> + import javax.net.ssl.SSLEngineResult;
> + import javax.net.ssl.SSLEngineResult.HandshakeStatus;
> + import javax.net.ssl.SSLException;
> +
> + /**
> + * Non blocking SSL Connector Handler. The recommended way to use this class
> + * is by creating an external Controller and share the same SelectorHandler
> + * instance.
> + *
> + * Recommended
> + * -----------
> + * Controller controller = new Controller();
> + * // new SSLSelectorHandler(true) means the Selector will be used only
> + * // for client operation (OP_READ, OP_WRITE, OP_CONNECT).
> + * SSLSelectorHandler sslSelectorHandler = new SSLSelectorHandler(true);
> + * controller.setSelectorHandler(sslSelectorHandler);
> + * SSLConnectorHandler sslConnectorHandler = new SSLConnectorHandler();
> + * sslConnectorHandler.connect(localhost,port, new SSLCallbackHandler(){...},
> + * sslSelectorHandler);
> + * SSLConnectorHandler sslConnectorHandler2 = new SSLConnectorHandler();
> + * sslConnectorHandler2.connect(localhost,port, new SSLCallbackHandler(){...},
> + * sslSelectorHandler);
> + *
> + * Not recommended (but still works)
> + * ---------------------------------
> + * SSLConnectorHandler sslConnectorHandler = new SSLConnectorHandler();
> + * sslConnectorHandler.connect(localhost,port);
> + *
> + * Internally, an new Controller will be created everytime connect(localhost,port)
> + * is invoked, which has an impact on performance.
> + *
> + * As common comment: developer should be very careful if dealing directly with
> + * <code>SSLConnectorHandler</code>'s underlying socket channel! In most cases
> + * there is no need to do this, but use read, write methods provided
> + * by <code>SSLConnectorHandler</code>
> + *
> + * @author Alexey Stashok
> + * @author Jeanfrancois Arcand
> + */
> + public class SSLConnectorHandler implements ConnectorHandler<SSLSelectorHandler, SSLCallbackHandler>, CallbackHandler {
> +
> + /**
> + * The underlying SSLSelectorHandler used to mange SelectionKeys.
> + */
> + private SSLSelectorHandler selectorHandler;
> +
> +
> + /**
> + * A blocking <code>InputStream</code> that use a pool of Selector
> + * to execute a blocking read operation.
> + */
> + private ByteBufferInputStream inputStream;
> +
> + /**
> + * A <code>SSLCallbackHandler</code> handler invoked by the TCPSelectorHandler
> + * when a non blocking operation is ready to be processed.
> + */
> + private SSLCallbackHandler callbackHandler;
> +
> + /*
> + * An empty ByteBuffer used for handshaking
> + */
> + private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
> +
> + /**
> + * Input buffer for reading encrypted data from channel
> + */
> + private ByteBuffer securedInputBuffer;
> +
> + /**
> + * Output buffer, which contains encrypted data ready for writing to channel
> + */
> + private ByteBuffer securedOutputBuffer;
> +
> + /**
> + * Buffer, where application data could be written during a asynchronous handshaking.
> + * It is set when user application calls: SSLConnectorHandler.handshake(appDataBuffer)
> + * and references appDataBuffer.
> + */
> + private ByteBuffer asyncHandshakeBuffer;
> +
> + /**
> + * The connection's SocketChannel.
> + */
> + private SocketChannel socketChannel;
> +
> + /**
> + * Default <code>SSLContext</code>, created on
> + * top of default <code>SSLConfiguration</code>
> + */
> + private static volatile SSLContext defaultSSLContext;
> +
> + /**
> + * Is the connection established.
> + */
> + private volatile boolean isConnected;
> +
> + /**
> + * Is the handshake phase completed
> + */
> + private volatile boolean isHandshakeDone;
> +
> + /**
> + * The internal Controller used (in case not specified).
> + */
> + private Controller controller;
> +
> + /**
> + * IsConnected Latch related
> + */
> + private CountDownLatch isConnectedLatch;
> +
> + /**
> + * Are we creating a controller every run.
> + */
> + private boolean isStandalone = false;
> +
> + /**
> + * Is async handshake in progress
> + */
> + private boolean isProcessingAsyncHandshake;
> +
> + /**
> + * Result of last <code>SSLEngine</code> operation
> + */
> + private SSLEngineResult sslLastOperationResult;
> +
> + /**
> + * Current handshake status
> + */
> + private SSLEngineResult.HandshakeStatus handshakeStatus;
> +
> + /**
> + * Current <code>SSLEngine</code> status
> + */
> + private SSLEngineResult.Status sslEngineStatus = null;
> +
> +
> + /**
> + * Are we creating a controller every run.
> + */
> + private boolean delegateSSLTasks;
> +
> + /**
> + * Connector's <code>SSLEngine</code>
> + */
> + private SSLEngine sslEngine;
> +
> + /**
> + * Connector's <code>SSLContext</code>
> + */
> + private SSLContext sslContext;
> +
> + public SSLConnectorHandler() {
> + this(defaultSSLContext);
> + }
> +
> + public SSLConnectorHandler(SSLConfig sslConfig) {
> + this(sslConfig.createSSLContext());
> + }
> +
> + public SSLConnectorHandler(SSLContext sslContext) {
> + if (sslContext == null) {
> + if (defaultSSLContext == null) {
> + synchronized (SSLConnectorHandler.class) {
> + if (defaultSSLContext == null) {
> + defaultSSLContext = SSLConfig.DEFAULT_CONFIG.createSSLContext();
> + }
> + }
> + }
> +
> + sslContext = defaultSSLContext;
> + }
> +
> + this.sslContext = sslContext;
> + }
> +
> + public boolean getDelegateSSLTasks() {
> + return delegateSSLTasks;
> + }
> +
> + public void setDelegateSSLTasks(boolean delegateSSLTasks) {
> + this.delegateSSLTasks = delegateSSLTasks;
> + }
> +
> + /**
> + * Connect to hostname:port. When an aysnchronous event happens (e.g
> + * OP_READ or OP_WRITE), the <code>Controller</code> will invoke
> + * the CallBackHandler.
> + * @param remoteAddress remote address to connect
> + * @param callbackHandler the handler invoked by the Controller when
> + * an non blocking operation is ready to be handled.
> + * @throws java.io.IOException
> + */
> + public void connect(SocketAddress remoteAddress, SSLCallbackHandler callbackHandler) throws IOException {
> + connect(remoteAddress, null, callbackHandler);
> + }
> +
> + /**
> + * Connect to hostname:port. When an aysnchronous event happens (e.g
> + * OP_READ or OP_WRITE), the <code>Controller</code> will invoke
> + * the CallBackHandler.
> + * @param remoteAddress remote address to connect
> + * @param localAddress local address to bind
> + * @param callbackHandler the handler invoked by the Controller when
> + * an non blocking operation is ready to be handled.
> + * @throws java.io.IOException
> + */
> + public void connect(SocketAddress remoteAddress, SocketAddress localAddress, SSLCallbackHandler callbackHandler) throws IOException {
> + if (controller == null) {
> + throw new IllegalStateException("Controller cannot be null");
> + }
> +
> + connect(remoteAddress, localAddress, callbackHandler, (SSLSelectorHandler) controller.getSelectorHandler(protocol()));
> + }
> +
> + /**
> + * Connect to hostname:port. When an aysnchronous event happens (e.g
> + * OP_READ or OP_WRITE), the <code>Controller</code> will invoke
> + * the CallBackHandler.
> + * @param remoteAddress remote address to connect
> + * @param callbackHandler the handler invoked by the Controller when
> + * an non blocking operation is ready to be handled.
> + * @param selectorHandler an instance of SelectorHandler.
> + * @throws java.io.IOException
> + */
> + public void connect(SocketAddress remoteAddress, SSLCallbackHandler callbackHandler, SSLSelectorHandler selectorHandler) throws IOException {
> + connect(remoteAddress, null, callbackHandler, selectorHandler);
> + }
> +
> + /**
> + * Connect to hostname:port. When an aysnchronous event happens (e.g
> + * OP_READ or OP_WRITE), the <code>Controller</code> will invoke
> + * the CallBackHandler.
> + * @param remoteAddress remote address to connect
> + * @param localAddress local address to bin
> + * @param callbackHandler the handler invoked by the Controller when
> + * an non blocking operation is ready to be handled.
> + * @param selectorHandler an instance of SelectorHandler.
> + * @throws java.io.IOException
> + */
> + public void connect(SocketAddress remoteAddress, SocketAddress localAddress, SSLCallbackHandler callbackHandler, SSLSelectorHandler selectorHandler) throws IOException {
> + if (isConnected) {
> + throw new AlreadyConnectedException();
> + }
> +
> + if (controller == null) {
> + throw new IllegalStateException("Controller cannot be null");
> + }
> +
> + if (selectorHandler == null) {
> + throw new IllegalStateException("Controller cannot be null");
> + }
> +
> + this.selectorHandler = selectorHandler;
> + this.callbackHandler = callbackHandler;
> +
> + // Wait for the onConnect to be invoked.
> + isConnectedLatch = new CountDownLatch(1);
> +
> + selectorHandler.connect(remoteAddress, localAddress, this);
> + inputStream = new ByteBufferInputStream();
> + inputStream.setSecure(true);
> +
> + try {
> + isConnectedLatch.await(30, TimeUnit.SECONDS);
> + } catch (InterruptedException ex) {
> + throw new IOException(ex.getMessage());
> + }
> + }
> +
> + /**
> + * Connect to hostname:port. Internally an instance of Controller and
> + * its default SelectorHandler will be created everytime this method is
> + * called. This method should be used only and only if no external
> + * Controller has been initialized.
> + * @param remoteAddress remote address to connect
> + * @throws java.io.IOException
> + */
> + public void connect(SocketAddress remoteAddress) throws IOException {
> + connect(remoteAddress, (SocketAddress) null);
> + }
> +
> + /**
> + * Connect to hostname:port. Internally an instance of Controller and
> + * its default SelectorHandler will be created everytime this method is
> + * called. This method should be used only and only if no external
> + * Controller has been initialized.
> + * @param remoteAddress remote address to connect
> + * @throws java.io.IOException
> + * @param localAddress local address to bin
> + */
> + public void connect(SocketAddress remoteAddress, SocketAddress localAddress) throws IOException {
> + if (isConnected) {
> + throw new AlreadyConnectedException();
> + }
> +
> + if (controller == null) {
> + isStandalone = true;
> + controller = new Controller();
> + controller.setSelectorHandler(new TCPSelectorHandler(true));
> + DefaultPipeline pipeline = new DefaultPipeline();
> + pipeline.initPipeline();
> + pipeline.startPipeline();
> + controller.setPipeline(pipeline);
> +
> + callbackHandler = new SSLCallbackHandler<Context>() {
> +
> + public void onConnect(IOEvent<Context> ioEvent) {
> + SelectionKey key = ioEvent.attachment().getSelectionKey();
> + socketChannel = (SocketChannel) key.channel();
> + finishConnect(key);
> + getController().registerKey(key, SelectionKey.OP_WRITE, Protocol.TCP);
> + }
> +
> + public void onRead(IOEvent<Context> ioEvent) {
> + }
> +
> + public void onWrite(IOEvent<Context> ioEvent) {
> + }
> +
> + public void onHandshake(IOEvent<Context> ioEvent) {
> + }
> + };
> +
> + final CountDownLatch latch = new CountDownLatch(1);
> + try {
> + pipeline.execute(new Context() {
> + @Override
> + public Object call() throws Exception {
> + latch.countDown();
> + controller.start();
> + return null;
> + }
> + });
> + } catch (PipelineFullException ex) {
> + throw new IOException(ex.getMessage());
> + }
> +
> + try {
> + latch.await();
> + Thread.sleep(1000);
Just to make sure, can you make that variable configurable in case some
platform gives us surprise?
> + } catch (InterruptedException ex) {
> + }
> + }
> +
> + connect(remoteAddress, localAddress, callbackHandler, (SSLSelectorHandler) controller.getSelectorHandler(protocol()));
> + }
> +
> + /**
> + * Initiate SSL handshake phase.
> + * Handshake is required to be done after connection established.
> + *
> + * @param byteBuffer Application <code>ByteBuffer</code>, where application data
> + * will be stored
> + * @param blocking true, if handshake should be done in blocking mode, for non-blocking false
> + * @return If blocking parameter is true - method should always return true if handshake is done,
> + * or throw IOException otherwise. For non-blocking mode method returns true if handshake is done, or false
> + * if handshake will be completed in non-blocking manner.
> + * @throws java.io.IOException if some error occurs during processing I/O operations/
> + */
> + public boolean handshake(ByteBuffer byteBuffer, boolean blocking) throws IOException {
> + sslEngine.beginHandshake();
> + handshakeStatus = sslEngine.getHandshakeStatus();
> +
> + if (blocking) {
> + SSLUtils.doHandshake(socketChannel, byteBuffer, securedInputBuffer, securedOutputBuffer, sslEngine, handshakeStatus);
> + finishHandshake();
> +
> + // Sync should be always done
> + return true;
> + } else {
> + doAsyncHandshake(byteBuffer);
> +
> + // is async handshake completed
> + return isHandshakeDone();
> + }
> + }
> +
> + /**
> + * Read bytes. If blocking is set to <tt>true</tt>, a pool of temporary
> + * <code>Selector</code> will be used to read bytes.
> + * @param byteBuffer The byteBuffer to store bytes.
> + * @param blocking <tt>true</tt> if a a pool of temporary Selector
> + * is required to handle a blocking read.
> + * @return number of bytes read
> + * @throws java.io.IOException
> + */
> + public long read(ByteBuffer byteBuffer, boolean blocking) throws IOException {
> + if (!isConnected) {
> + throw new NotYetConnectedException();
> + }
> +
> + if (blocking) {
> + inputStream.setSelectionKey(getSelectionKey());
> + return inputStream.read(byteBuffer);
> + } else {
> + int nRead = doReadAsync(byteBuffer);
> +
> + if (!blocking && nRead == 0) {
> + registerSelectionKeyFor(SelectionKey.OP_READ);
> + return 0;
> + }
> + return nRead;
> + }
> + }
> +
> +
> + /**
> + * Writes bytes. If blocking is set to <tt>true</tt>, a pool of temporary
> + * <code>Selector</code> will be used to writes bytes.
> + * @param byteBuffer The byteBuffer to write.
> + * @param blocking <tt>true</tt> if a a pool of temporary Selector
> + * is required to handle a blocking write.
> + * @return number of bytes written. As on socket encrypted data is written -
> + * it's impossible to determine correspondent source data, which was written.
> + * That's why method either returns 0, if not all the source data was written,
> + * or byteBuffer.remaining() - if all the data was written
> + * @throws java.io.IOException
> + */
> + public long write(ByteBuffer byteBuffer, boolean blocking) throws IOException {
> + if (!isConnected) {
> + throw new NotYetConnectedException();
> + }
> +
> + if (blocking) {
> + return SSLOutputWriter.flushChannel(socketChannel, byteBuffer, securedOutputBuffer, sslEngine);
> + } else {
> + if (callbackHandler == null) {
> + throw new IllegalStateException("Non blocking write needs a CallbackHandler");
> + }
> +
> + int nWrite = 1;
> + int totalWrite = 0;
> +
> + while (nWrite > 0 && byteBuffer.hasRemaining()) {
> + nWrite = doWriteAsync(byteBuffer);
> + totalWrite += nWrite;
> + }
> +
> + if (totalWrite == 0 && byteBuffer.hasRemaining()) {
> + registerSelectionKeyFor(SelectionKey.OP_WRITE);
> + }
> + return nWrite;
> + }
> + }
> +
> +
> + /**
> + * Close the underlying connection.
> + */
> + public void close() throws IOException {
> + if (socketChannel != null) {
> + try {
> + if (securedOutputBuffer.hasRemaining()) {
> + // if there is something is securedOutputBuffer - flush it
> + OutputWriter.flushChannel(socketChannel, securedOutputBuffer);
> + }
> +
> + // Close secure outbound channel and flush data
> + sslEngine.closeOutbound();
> + SSLUtils.wrap(EMPTY_BUFFER, securedOutputBuffer, sslEngine);
> + OutputWriter.flushChannel(socketChannel, securedOutputBuffer);
> + } catch (IOException e) {
> + }
> +
Log the exception as FINEST :-)
> + if (selectorHandler != null) {
> + SelectionKey key = socketChannel.keyFor(selectorHandler.getSelector());
> +
> + if (key == null) {
> + return;
> + }
> + key.cancel();
> + key.attach(null);
> + }
> +
> + socketChannel.close();
> + }
> +
> + if (controller != null && isStandalone) {
> + controller.stop();
> + controller = null;
> + }
> +
> + sslEngine = null;
> + asyncHandshakeBuffer = null;
> + isStandalone = false;
> + isConnected = false;
> + isHandshakeDone = false;
> + }
> +
> +
> + /**
> + * Finish handling the OP_CONNECT interest ops.
> + * @param key - a <code>SelectionKey</code>
> + */
> + public void finishConnect(SelectionKey key) {
> + try {
> + socketChannel = (SocketChannel) key.channel();
> + socketChannel.finishConnect();
> + isConnected = socketChannel.isConnected();
> + initSSLEngineIfRequired();
> + } catch (IOException ex) {
> + // XXX LOG ME
> + ex.printStackTrace();
Log.
> + } finally {
> + isConnectedLatch.countDown();
> + }
> + }
> +
> + /**
> + * Changes SSLConnectorHandler state, after handshake operation is done.
> + * Normally should not be called by outside Grizzly, just in case when custom
> + * handshake code was used.
> + */
> + public void finishHandshake() {
> + isProcessingAsyncHandshake = false;
> + isHandshakeDone = true;
> + }
> +
> + /**
> + * A token decribing the protocol supported by an implementation of this
> + * interface
> + * @return this <code>ConnectorHandler</code>'s protocol
> + */
> + public Controller.Protocol protocol() {
> + return Controller.Protocol.TLS;
> + }
> +
> +
> + /**
> + * Is the underlying SocketChannel connected.
> + * @return <tt>true</tt> if connected, otherwise <tt>false</tt>
> + */
> + public boolean isConnected() {
> + return isConnected;
> + }
> +
> + /**
> + * Is the underlying SocketChannel connected.
> + * @return <tt>true</tt> if connected, otherwise <tt>false</tt>
> + */
> + public boolean isHandshakeDone() {
> + return isHandshakeDone && !isProcessingAsyncHandshake;
> + }
> +
> + public Controller getController() {
> + return controller;
> + }
> +
> +
> + public void setController(Controller controller) {
> + this.controller = controller;
> + }
> +
> + public SelectableChannel getUnderlyingChannel() {
> + return socketChannel;
> + }
> +
> + public SSLCallbackHandler getCallbackHandler() {
> + return callbackHandler;
> + }
> +
> + public void setCallbackHandler(SSLCallbackHandler callbackHandler) {
> + this.callbackHandler = callbackHandler;
> + }
> +
> + public SSLSelectorHandler getSelectorHandler() {
> + return selectorHandler;
> + }
> +
> + /**
> + * Gets the size of the largest application buffer that may occur when
> + * using this session.
> + * SSLEngine application data buffers must be large enough to hold the
> + * application data from any inbound network application data packet
> + * received. Typically, outbound application data buffers can be of any size.
> + *
> + * (javadoc is taken from SSLSession.getApplicationBufferSize())
> + * @return largets application buffer size, which may occur
> + */
> + public int getApplicationBufferSize() {
> + initSSLEngineIfRequired();
> + return sslEngine.getSession().getApplicationBufferSize();
> + }
> +
> + public void onConnect(IOEvent ioEvent) {
> + callbackHandler.onConnect(ioEvent);
> + }
> +
> + public void onRead(IOEvent ioEvent) {
> + try {
> + // if processing handshake - pass the data to handshake related code
> + if (isProcessingAsyncHandshake) {
> + doAsyncHandshake(asyncHandshakeBuffer);
> + if (isHandshakeDone()) {
> + callbackHandler.onHandshake(ioEvent);
> + } else {
> + return;
> + }
> + }
> +
> + callbackHandler.onRead(ioEvent);
> + } catch (IOException ex) {
> + //log
> + }
> + }
> +
> + public void onWrite(IOEvent ioEvent) {
> + try {
> + // check if all the secured data was written, if not -
> + // flush as much as possible.
> + if (!securedOutputBuffer.hasRemaining() || flushSecuredOutputBuffer()) {
> + // if no encrypted data left in buffer - continue processing
> + if (isProcessingAsyncHandshake) {
> + doAsyncHandshake(asyncHandshakeBuffer);
> + if (isHandshakeDone()) {
> + callbackHandler.onHandshake(ioEvent);
> + } else {
> + return;
> + }
> + }
> +
> + callbackHandler.onWrite(ioEvent);
> + }
> + } catch (IOException ex) {
> + // log
> + }
> + }
> +
> + /**
> + * Read a data from channel in async mode and decrypt
> + * @param byteBuffer buffer for decrypted data
> + * @return number of bytes read to byteBuffer
> + * @throws java.io.IOException
> + */
> + private int doReadAsync(ByteBuffer byteBuffer) throws IOException {
> + // Clear or compact secured input buffer
> + clearOrCompactBuffer(securedInputBuffer);
> +
> + // Read data to secured buffer
> + int bytesRead = socketChannel.read(securedInputBuffer);
> +
> + if (bytesRead == -1) {
> + try {
> + sslEngine.closeInbound();
> + // check if there is some secured data still available
> + if (securedInputBuffer.position() == 0 ||
> + sslEngineStatus == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
> + return -1;
> + }
> + } catch (SSLException e) {
> + return -1;
> + }
> + }
> +
> + securedInputBuffer.flip();
> +
> + if (bytesRead == 0 && !securedInputBuffer.hasRemaining()) {
> + return 0;
> + }
> +
> + clearOrCompactBuffer(byteBuffer);
> +
> + SSLEngineResult result = null;
> + do {
> + result = sslEngine.unwrap(securedInputBuffer, byteBuffer);
> + // During handshake phase several unwrap actions could be executed on read data
> + } while (result.getStatus() == SSLEngineResult.Status.OK && result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP && result.bytesProduced() == 0);
> +
> + updateSSLEngineStatus(result);
> +
> + if (sslEngineStatus == SSLEngineResult.Status.CLOSED) {
> + return -1;
> + } else if (sslEngineStatus == SSLEngineResult.Status.BUFFER_OVERFLOW) {
> + throw new IllegalStateException("Application buffer is overflowed");
> + }
> +
> + byteBuffer.flip();
> +
> + return result.bytesProduced();
> + }
> +
> + /**
> + * Write secured data to channel in async mode
> + *
> + * @param byteBuffer non-crypted data buffer
> + * @return number of bytes written. As non-crypted data is passed, but crypted
> + * data is written on channel - it's difficult to determine correspondent size
> + * of non-crypted data which was written. That's why method returns 0, if not all
> + * the data was written, or byteBuffer.remaining() if all the source data was written.
> + * @throws java.io.IOException
> + */
> + private int doWriteAsync(ByteBuffer byteBuffer) throws IOException {
> + if (securedOutputBuffer.hasRemaining() && !flushSecuredOutputBuffer()) {
> + return 0;
> + }
> +
> + securedOutputBuffer.clear();
> + SSLEngineResult result = SSLUtils.wrap(byteBuffer, securedOutputBuffer, sslEngine);
> +
> + updateSSLEngineStatus(result);
> +
> + int bytesWritten = socketChannel.write(securedOutputBuffer);
> + if (securedOutputBuffer.hasRemaining()) {
> + return 0;
> + }
> +
> + return result.bytesConsumed();
> + }
> +
> + /**
> + * Perform an SSL handshake in async mode.
> + * @param byteBuffer The application <code>ByteBuffer</code>
> + *
> + * @throws IOException if the handshake fail.
> + */
> + private void doAsyncHandshake(ByteBuffer byteBuffer) throws IOException {
> + SSLEngineResult result;
> + isProcessingAsyncHandshake = true;
> + asyncHandshakeBuffer = byteBuffer;
> + while (handshakeStatus != HandshakeStatus.FINISHED) {
> + switch (handshakeStatus) {
> + case NEED_WRAP:
> + result = SSLUtils.wrap(EMPTY_BUFFER, securedOutputBuffer, sslEngine);
> + updateSSLEngineStatus(result);
> + switch (result.getStatus()) {
> + case OK:
> + if (!flushSecuredOutputBuffer()) {
> + return;
> + }
> + break;
> + default:
> + throw new IOException("Handshaking error: " + result.getStatus());
> + }
> +
> + if (handshakeStatus != HandshakeStatus.NEED_UNWRAP) {
> + break;
> + }
> + case NEED_UNWRAP:
> + int bytesRead = doReadAsync(byteBuffer);
> + if (bytesRead == -1) {
> + try {
> + sslEngine.closeInbound();
> + } catch (IOException ex) {
> + }
> +
> + throw new EOFException("Connection closed");
> + } else if (bytesRead == 0 && sslLastOperationResult.bytesConsumed() == 0) {
> + registerSelectionKeyFor(SelectionKey.OP_READ);
> + return;
> + }
> +
> + if (handshakeStatus != HandshakeStatus.NEED_TASK) {
> + break;
> + }
> + case NEED_TASK:
> + handshakeStatus = executeDelegatedTask();
> + break;
> + default:
> + throw new RuntimeException("Invalid Handshaking State" + handshakeStatus);
> + }
> + }
> +
> + if (isProcessingAsyncHandshake) {
> + finishHandshake();
> + }
> +
> + asyncHandshakeBuffer = null;
> + }
> +
> + /**
> + * Complete hanshakes operations.
> + * @return SSLEngineResult.HandshakeStatus
> + */
> + private SSLEngineResult.HandshakeStatus executeDelegatedTask() {
> + Runnable runnable;
> + while ((runnable = sslEngine.getDelegatedTask()) != null) {
> + runnable.run();
> + }
> +
> + return sslEngine.getHandshakeStatus();
> + }
> +
> + /**
> + * Update <code>SSLConnectorHandler</code> internal status with
> + * last <code>SSLEngine</code> operation result.
> + *
> + * @param result last <code>SSLEngine</code> operation result
> + */
> + private void updateSSLEngineStatus(SSLEngineResult result) {
> + sslLastOperationResult = result;
> + sslEngineStatus = result.getStatus();
> + handshakeStatus = result.getHandshakeStatus();
> + }
> +
> + /**
> + * Clears buffer if there is no info available, or compact buffer otherwise.
> + * @param buffer byte byffer
> + */
> + private static void clearOrCompactBuffer(ByteBuffer buffer) {
> + if (!buffer.hasRemaining()) {
> + buffer.clear();
> + } else if (buffer.position() > 0) {
> + buffer.compact();
> + }
> + }
> +
> + /**
> + * Gets <code>SSLConnectorHandler</code> <code>SelectionKey</code>
> + * @return <code>SelectionKey</code>
> + */
> + private SelectionKey getSelectionKey() {
> + return socketChannel.keyFor(selectorHandler.getSelector());
> + }
> +
> + /**
> + * Registers <code>SSLConnectorHandler<code>'s <code>SelectionKey</code>
> + * to listen channel operations.
> + * @param ops interested channel operations
> + */
> + private void registerSelectionKeyFor(int ops) {
> + SelectionKey key = getSelectionKey();
> + key.attach(this);
> + selectorHandler.register(key, ops);
> + }
> +
> + /**
> + * Flushes as much as possible bytes from the secured output buffer
> + * @return true if secured buffer was completely flushed, false otherwise
> + */
> + private boolean flushSecuredOutputBuffer() throws IOException {
> + int nWrite = 1;
> +
> + while (nWrite > 0 && securedOutputBuffer.hasRemaining()) {
> + nWrite = socketChannel.write(securedOutputBuffer);
> + }
> +
> + if (securedOutputBuffer.hasRemaining()) {
> + SelectionKey key = socketChannel.keyFor(selectorHandler.getSelector());
> + key.attach(callbackHandler);
> + selectorHandler.register(key, SelectionKey.OP_WRITE);
> +
> + return false;
> + }
> +
> + return true;
> + }
> +
> + /**
> + * Initiate <code>SSLEngine</code> and related secure buffers
> + */
> + private void initSSLEngineIfRequired() {
> + if (sslEngine == null) {
> + sslEngine = sslContext.createSSLEngine();
> + sslEngine.setUseClientMode(true);
> +
> + int bbSize = sslEngine.getSession().getPacketBufferSize();
> + securedInputBuffer = ByteBuffer.allocate(bbSize * 2);
> + securedOutputBuffer = ByteBuffer.allocate(bbSize * 2);
> + }
> + }
Might be a stupid question, but here you aren't configuring the
protocols and the certs. Can clients set those as well? I suspect yes,
but just double checking.
> + }
> Index: test/java/com/sun/grizzly/utils/SSLEchoFilter.java
> *** C:\Projects\Grizzly\trunk\modules\grizzly\src\test\java\com\sun\grizzly\utils\SSLEchoFilter.java Locally New
> --- C:\Projects\Grizzly\trunk\modules\grizzly\src\test\java\com\sun\grizzly\utils\SSLEchoFilter.java Locally New
> ***************
> *** 1,0 ****
> --- 1,66 ----
> + /*
> + * The contents of this file are subject to the terms
> + * of the Common Development and Distribution License
> + * (the License). You may not use this file except in
> + * compliance with the License.
> + *
> + * You can obtain a copy of the license at
> + * https://glassfish.dev.java.net/public/CDDLv1.0.html or
> + * glassfish/bootstrap/legal/CDDLv1.0.txt.
> + * See the License for the specific language governing
> + * permissions and limitations under the License.
> + *
> + * When distributing Covered Code, include this CDDL
> + * Header Notice in each file and include the License file
> + * at glassfish/bootstrap/legal/CDDLv1.0.txt.
> + * If applicable, add the following below the CDDL Header,
> + * with the fields enclosed by brackets [] replaced by
> + * you own identifying information:
> + * "Portions Copyrighted [year] [name of copyright owner]"
> + *
> + * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
> + */
> +
> + package com.sun.grizzly.utils;
> +
> + import com.sun.grizzly.*;
> + import com.sun.grizzly.util.SSLOutputWriter;
> + import com.sun.grizzly.util.WorkerThread;
> + import java.io.IOException;
> + import java.nio.ByteBuffer;
> + import java.nio.channels.SocketChannel;
> +
> + /**
> + * Simple echo filter
> + *
> + * @author Alexey Stashok
> + */
> + public class SSLEchoFilter implements ProtocolFilter {
> + public boolean execute(Context ctx) throws IOException {
> + final WorkerThread workerThread = ((WorkerThread)Thread.currentThread());
> + ByteBuffer buffer = workerThread.getByteBuffer();
> + buffer.flip();
> + if (buffer.hasRemaining()) {
> + // Store incoming data in byte[]
> + byte[] data = new byte[buffer.remaining()];
> + int position = buffer.position();
> + buffer.get(data);
> + buffer.position(position);
> +
> + SocketChannel channel = (SocketChannel) ctx.getSelectionKey().channel();
> + try {
> + SSLOutputWriter.flushChannel(channel, buffer);
> + } catch (IOException ex) {
> + System.out.println("Exception. Echo \"" + new String(data) + "\"");
> + throw ex;
> + }
> + }
> +
> + buffer.clear();
> + return false;
> + }
> +
> + public boolean postExecute(Context ctx) throws IOException {
> + return true;
> + }
> + }
>
>
>
> Index: test/java/com/sun/grizzly/TCPConnectorHandlerTest.java
> ===================================================================
> --- test/java/com/sun/grizzly/TCPConnectorHandlerTest.java (revision 329)
> +++ test/java/com/sun/grizzly/TCPConnectorHandlerTest.java (working copy)
> @@ -22,7 +22,6 @@
> */
> package com.sun.grizzly;
>
> -import com.sun.grizzly.connectioncache.client.CacheableConnectorHandlerPool;
> import com.sun.grizzly.filter.ReadFilter;
> import com.sun.grizzly.utils.ControllerUtils;
> import com.sun.grizzly.utils.EchoFilter;
> Index: main/java/com/sun/grizzly/TCPConnectorHandler.java
> ===================================================================
> --- main/java/com/sun/grizzly/TCPConnectorHandler.java (revision 329)
> +++ main/java/com/sun/grizzly/TCPConnectorHandler.java (working copy)
> @@ -65,7 +65,7 @@
> *
> * @author Jeanfrancois Arcand
> */
> -public class TCPConnectorHandler implements ConnectorHandler<TCPSelectorHandler>{
> +public class TCPConnectorHandler implements ConnectorHandler<TCPSelectorHandler, CallbackHandler>{
>
> /**
> * The underlying TCPSelectorHandler used to mange SelectionKeys.
> Index: main/java/com/sun/grizzly/IOEvent.java
> ===================================================================
> --- main/java/com/sun/grizzly/IOEvent.java (revision 329)
> +++ main/java/com/sun/grizzly/IOEvent.java (working copy)
> @@ -48,4 +48,23 @@
> */
> public E attachment();
>
> + /**
> + * Simple IOEvent implementation
> + */
> + public class DefaultIOEvent<E> implements IOEvent<E> {
> + private E attachment;
> +
> + public DefaultIOEvent(E attachment) {
> + this.attachment = attachment;
> + }
> +
> + public E attach(E attachment) {
> + this.attachment = attachment;
> + return attachment;
> + }
> +
> + public E attachment() {
> + return attachment;
> + }
> + }
> }
> Index: main/java/com/sun/grizzly/ConnectorInstanceHandler.java
> ===================================================================
> --- main/java/com/sun/grizzly/ConnectorInstanceHandler.java (revision 329)
> +++ main/java/com/sun/grizzly/ConnectorInstanceHandler.java (working copy)
> @@ -23,6 +23,7 @@
>
> package com.sun.grizzly;
>
> +import java.util.concurrent.Callable;
> import java.util.concurrent.ConcurrentLinkedQueue;
>
> /**
> @@ -85,4 +86,26 @@
> public abstract E newInstance();
> }
>
> + /**
> + * Concurrent Queue ConnectorInstanceHandler implementation
> + * @param E ConnectorHandler implementation this pool will manage
> + */
> + public class ConcurrentQueueDelegateCIH<E extends ConnectorHandler>
> + extends ConcurrentQueueConnectorInstanceHandler<E> {
> +
> + // ConnectorHandler instance creator
> + private Callable<E> delegate;
> +
> + public ConcurrentQueueDelegateCIH(Callable<E> delegate) {
> + this.delegate = delegate;
> + }
> +
> + public E newInstance() {
> + try {
> + return delegate.call();
> + } catch(Exception e) {
> + throw new IllegalStateException("Unexpected exception");
> + }
> + }
> + }
> }
> Index: main/java/com/sun/grizzly/UDPConnectorInstanceHandler.java
> ===================================================================
> --- main/java/com/sun/grizzly/UDPConnectorInstanceHandler.java (revision 329)
> +++ main/java/com/sun/grizzly/UDPConnectorInstanceHandler.java (working copy)
> @@ -1,38 +0,0 @@
> -/*
> - * The contents of this file are subject to the terms
> - * of the Common Development and Distribution License
> - * (the License). You may not use this file except in
> - * compliance with the License.
> - *
> - * You can obtain a copy of the license at
> - * https://glassfish.dev.java.net/public/CDDLv1.0.html or
> - * glassfish/bootstrap/legal/CDDLv1.0.txt.
> - * See the License for the specific language governing
> - * permissions and limitations under the License.
> - *
> - * When distributing Covered Code, include this CDDL
> - * Header Notice in each file and include the License file
> - * at glassfish/bootstrap/legal/CDDLv1.0.txt.
> - * If applicable, add the following below the CDDL Header,
> - * with the fields enclosed by brackets [] replaced by
> - * you own identifying information:
> - * "Portions Copyrighted [year] [name of copyright owner]"
> - *
> - * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
> - */
> -
> -package com.sun.grizzly;
> -
> -/**
> - * Default <code>ConnectorInstanceHandler</code> which use a
> - * <code>List</code> to pool <code>ConnectorHandler</code>
> - *
> - * @author Jeanfrancois
> - */
> -public class UDPConnectorInstanceHandler extends
> - ConnectorInstanceHandler.ConcurrentQueueConnectorInstanceHandler<UDPConnectorHandler> {
> -
> - public UDPConnectorHandler newInstance() {
> - return new UDPConnectorHandler();
> - }
> -}
> Index: main/java/com/sun/grizzly/UDPSelectorHandler.java
> ===================================================================
> --- main/java/com/sun/grizzly/UDPSelectorHandler.java (revision 329)
> +++ main/java/com/sun/grizzly/UDPSelectorHandler.java (working copy)
> @@ -34,6 +34,7 @@
> import java.nio.channels.SelectionKey;
> import java.nio.channels.Selector;
> import java.util.Iterator;
> +import java.util.concurrent.Callable;
> import java.util.concurrent.ConcurrentHashMap;
> import java.util.concurrent.ConcurrentLinkedQueue;
> import java.util.logging.Level;
> @@ -92,7 +93,9 @@
> public void preSelect(Context ctx) throws IOException {
> if (selector == null){
> try{
> - connectorInstanceHandler = new UDPConnectorInstanceHandler();
> + connectorInstanceHandler = new ConnectorInstanceHandler.
> + ConcurrentQueueDelegateCIH(
> + getConnectorInstanceHandlerDelegate());
> datagramChannel = DatagramChannel.open();
> datagramChannel.configureBlocking(false);
> selector = Selector.open();
> @@ -246,4 +249,14 @@
> public void setSocketTimeout(int socketTimeout) {
> throw new IllegalStateException(NOT_SUPPORTED);
> }
> +
> + //--------------- ConnectorInstanceHandler -----------------------------
> + @Override
> + protected Callable<ConnectorHandler> getConnectorInstanceHandlerDelegate() {
> + return new Callable<ConnectorHandler>() {
> + public ConnectorHandler call() throws Exception {
> + return new UDPConnectorHandler();
> + }
> + };
> + }
> }
> Index: main/java/com/sun/grizzly/ConnectorHandler.java
> ===================================================================
> --- main/java/com/sun/grizzly/ConnectorHandler.java (revision 329)
> +++ main/java/com/sun/grizzly/ConnectorHandler.java (working copy)
> @@ -41,7 +41,7 @@
> * @param E a <code>SelectorHandler</code>
> * @author Jeanfrancois Arcand
> */
> -public interface ConnectorHandler<E extends SelectorHandler> extends Handler, Closeable {
> +public interface ConnectorHandler<E extends SelectorHandler, P extends CallbackHandler> extends Handler, Closeable {
>
>
> /**
> @@ -63,7 +63,7 @@
> * @throws java.io.IOException
> */
> public void connect(SocketAddress remoteAddress,
> - CallbackHandler callbackHandler,
> + P callbackHandler,
> E e) throws IOException;
>
>
> @@ -77,7 +77,7 @@
> * @throws java.io.IOException
> */
> public void connect(SocketAddress remoteAddress,
> - CallbackHandler callbackHandler) throws IOException;
> + P callbackHandler) throws IOException;
>
>
> /**
> @@ -104,7 +104,7 @@
> * @throws java.io.IOException
> */
> public void connect(SocketAddress remoteAddress, SocketAddress localAddress,
> - CallbackHandler callbackHandler,
> + P callbackHandler,
> E e) throws IOException;
>
>
> @@ -119,7 +119,7 @@
> * @throws java.io.IOException
> */
> public void connect(SocketAddress remoteAddress, SocketAddress localAddress,
> - CallbackHandler callbackHandler) throws IOException;
> + P callbackHandler) throws IOException;
>
>
> /**
> @@ -205,7 +205,7 @@
> *
> * @return callback handler
> */
> - public CallbackHandler getCallbackHandler();
> + public P getCallbackHandler();
>
> /**
> * Sets <code>ConnectorHandler</code>'s callback handler instance,
> @@ -213,5 +213,5 @@
> *
> * @param callbackHandler handler
> */
> - public void setCallbackHandler(CallbackHandler callbackHandler);
> + public void setCallbackHandler(P callbackHandler);
> }
> Index: main/java/com/sun/grizzly/UDPConnectorHandler.java
> ===================================================================
> --- main/java/com/sun/grizzly/UDPConnectorHandler.java (revision 329)
> +++ main/java/com/sun/grizzly/UDPConnectorHandler.java (working copy)
> @@ -46,7 +46,7 @@
> *
> * @author Jeanfrancois Arcand
> */
> -public class UDPConnectorHandler implements ConnectorHandler<UDPSelectorHandler>{
> +public class UDPConnectorHandler implements ConnectorHandler<UDPSelectorHandler, CallbackHandler>{
>
> /**
> * The underlying UDPSelectorHandler used to mange SelectionKeys.
> Index: main/java/com/sun/grizzly/connectioncache/client/CacheableConnectorHandler.java
> ===================================================================
> --- main/java/com/sun/grizzly/connectioncache/client/CacheableConnectorHandler.java (revision 329)
> +++ main/java/com/sun/grizzly/connectioncache/client/CacheableConnectorHandler.java (working copy)
> @@ -39,7 +39,7 @@
> *
> * @author Alexey Stashok
> */
> -public class CacheableConnectorHandler implements ConnectorHandler<SelectorHandler>,
> +public class CacheableConnectorHandler implements ConnectorHandler<SelectorHandler, CallbackHandler>,
> ContactInfo<ConnectorHandler>, CallbackHandler {
> private SocketAddress targetAddress;
> private Protocol protocol;
> @@ -231,15 +231,7 @@
> Selector protocolSelector = underlyingConnectorHandler.getSelectorHandler().getSelector();
> SelectionKey key = underlyingConnectorHandler.getUnderlyingChannel().keyFor(protocolSelector);
> final Context context = parentPool.getController().pollContext(key);
> - onConnect(new IOEvent<Context>() {
> - public Context attach(Context e) {
> - return context;
> - }
> -
> - public Context attachment() {
> - return context;
> - }
> - });
> + onConnect(new IOEvent.DefaultIOEvent<Context>(context));
> }
>
> //---------------------- CallbackHandler implementation --------------------------------
> Index: main/java/com/sun/grizzly/filter/SSLReadFilter.java
> ===================================================================
> --- main/java/com/sun/grizzly/filter/SSLReadFilter.java (revision 329)
> +++ main/java/com/sun/grizzly/filter/SSLReadFilter.java (working copy)
> @@ -114,7 +114,7 @@
> /**
> * Encrypted ByteBuffer default size.
> */
> - protected int inputBBSize = 5 * 4096;
> + protected int inputBBSize = 5 * 4096;
>
>
> public SSLReadFilter() {
> @@ -177,7 +177,7 @@
> * needs to be invoked.
> */
> public boolean postExecute(Context ctx) throws IOException {
> - ctx.setProtocol(Controller.Protocol.TCP);
> + ctx.setProtocol(Controller.Protocol.TLS);
> if (ctx.getKeyRegistrationState()
> == Context.KeyRegistrationState.CANCEL){
> ctx.getController().cancelKey(ctx.getSelectionKey());
> @@ -266,8 +266,8 @@
> boolean OK = true;
> try{
> byteBuffer = SSLUtils.doHandshake
> - (key,byteBuffer,inputBB,outputBB,sslEngine,
> - handshakeStatus,timeout);
> + ((SocketChannel) key.channel(), byteBuffer, inputBB,
> + outputBB, sslEngine, handshakeStatus, timeout);
> if (doRead(key) == -1){
> throw new EOFException();
> }
> @@ -313,7 +313,7 @@
> } catch (SSLException ex){
> ;
> }
> - }
> + }
> }
> }
>
> Index: main/java/com/sun/grizzly/TCPConnectorInstanceHandler.java
> ===================================================================
> --- main/java/com/sun/grizzly/TCPConnectorInstanceHandler.java (revision 329)
> +++ main/java/com/sun/grizzly/TCPConnectorInstanceHandler.java (working copy)
> @@ -1,38 +0,0 @@
> -/*
> - * The contents of this file are subject to the terms
> - * of the Common Development and Distribution License
> - * (the License). You may not use this file except in
> - * compliance with the License.
> - *
> - * You can obtain a copy of the license at
> - * https://glassfish.dev.java.net/public/CDDLv1.0.html or
> - * glassfish/bootstrap/legal/CDDLv1.0.txt.
> - * See the License for the specific language governing
> - * permissions and limitations under the License.
> - *
> - * When distributing Covered Code, include this CDDL
> - * Header Notice in each file and include the License file
> - * at glassfish/bootstrap/legal/CDDLv1.0.txt.
> - * If applicable, add the following below the CDDL Header,
> - * with the fields enclosed by brackets [] replaced by
> - * you own identifying information:
> - * "Portions Copyrighted [year] [name of copyright owner]"
> - *
> - * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
> - */
> -
> -package com.sun.grizzly;
> -
> -/**
> - * Default <code>ConnectorInstanceHandler</code> which use a
> - * <code>List</code> to pool <code>ConnectorHandler</code>
> - *
> - * @author Jeanfrancois
> - */
> -public class TCPConnectorInstanceHandler extends
> - ConnectorInstanceHandler.ConcurrentQueueConnectorInstanceHandler<TCPConnectorHandler> {
> -
> - public TCPConnectorHandler newInstance() {
> - return new TCPConnectorHandler();
> - }
> -}
> Index: main/java/com/sun/grizzly/TCPSelectorHandler.java
> ===================================================================
> --- main/java/com/sun/grizzly/TCPSelectorHandler.java (revision 329)
> +++ main/java/com/sun/grizzly/TCPSelectorHandler.java (working copy)
> @@ -39,6 +39,7 @@
> import java.nio.channels.SocketChannel;
> import java.util.Iterator;
> import java.util.Set;
> +import java.util.concurrent.Callable;
> import java.util.concurrent.ConcurrentHashMap;
> import java.util.concurrent.ConcurrentLinkedQueue;
> import java.util.logging.Level;
> @@ -232,7 +233,9 @@
> public void preSelect(Context ctx) throws IOException {
> if (selector == null){
> try{
> - connectorInstanceHandler = new TCPConnectorInstanceHandler();
> + connectorInstanceHandler = new ConnectorInstanceHandler.
> + ConcurrentQueueDelegateCIH(
> + getConnectorInstanceHandlerDelegate());
>
> // Create the socket listener
> selector = Selector.open();
> @@ -550,8 +553,7 @@
> protected void invokeCallbackHandler(Context context) throws IOException{
> context.setProtocol(protocol());
>
> - IOEvent<Context>ioEvent = createIOEvent();
> - ioEvent.attach(context);
> + IOEvent<Context>ioEvent = new IOEvent.DefaultIOEvent<Context>(context);
> context.setIOEvent(ioEvent);
> try {
> context.execute();
> @@ -736,27 +738,8 @@
> public void setReuseAddress(boolean reuseAddress) {
> this.reuseAddress = reuseAddress;
> }
> -
> +
> /**
> - * Create IOEvent instance
> - * @return IOEvent
> - */
> - protected static <E> IOEvent<E> createIOEvent() {
> - return new IOEvent<E>() {
> - private E context;
> - public E attach(E context){
> - this.context = context;
> - return context;
> - }
> -
> - public E attachment(){
> - return context;
> - }
> - };
> - }
> -
> -
> - /**
> * Return the Pipeline used to execute this SelectorHandler's
> * SelectionKey ops
> * @return The pipeline to use, or null if the Controller's Pipeline
> @@ -781,4 +764,18 @@
> public void setSelectionKeyHandler(SelectionKeyHandler selectionKeyHandler) {
> this.selectionKeyHandler = selectionKeyHandler;
> }
> +
> + //--------------- ConnectorInstanceHandler -----------------------------
> + /**
> + * Return <Callable>factory<Callable> object, which knows how
> + * to create <code>ConnectorInstanceHandler<code> corresponding to the protocol
> + * @return <Callable>factory</code>
> + */
> + protected Callable<ConnectorHandler> getConnectorInstanceHandlerDelegate() {
> + return new Callable<ConnectorHandler>() {
> + public ConnectorHandler call() throws Exception {
> + return new TCPConnectorHandler();
> + }
> + };
> + }
> }
> Index: main/java/com/sun/grizzly/util/SSLUtils.java
> ===================================================================
> --- main/java/com/sun/grizzly/util/SSLUtils.java (revision 329)
> +++ main/java/com/sun/grizzly/util/SSLUtils.java (working copy)
> @@ -69,24 +69,23 @@
>
> /**
> * Read encrypted bytes using an <code>SSLEngine</code>.
> - * @param key The SelectionKey
> + * @param socketChannel The SocketChannel
> * @param inputBB The byteBuffer to store encrypted bytes
> * @param sslEngine The SSLEngine uses to manage the SSL operations.
> * @param timeout The Selector.select() timeout value. A value of 0 will
> * be exectuted as a Selector.selectNow();
> * @return the bytes read.
> */
> - public static int doRead(SelectionKey key, ByteBuffer inputBB,
> + public static int doRead(SocketChannel socketChannel, ByteBuffer inputBB,
> SSLEngine sslEngine, int timeout){
>
> - if (key == null) return -1;
> + if (socketChannel == null) return -1;
>
> int count = 1;
> int byteRead = 0;
> Selector readSelector = null;
> SelectionKey tmpKey = null;
> try{
> - SocketChannel socketChannel = (SocketChannel)key.channel();
> while (count > 0){
> count = socketChannel.read(inputBB);
>
> @@ -290,7 +289,7 @@
>
> /**
> * Perform an SSL handshake using the SSLEngine.
> - * @param key the <code>SelectionKey</code>
> + * @param socketChannel the <code>SocketChannel</code>
> * @param byteBuffer The application <code>ByteBuffer</code>
> * @param inputBB The encrypted input <code>ByteBuffer</code>
> * @param outputBB The encrypted output <code>ByteBuffer</code>
> @@ -300,18 +299,18 @@
> * @throws java.io.IOException
> * @throw IOException if the handshake fail.
> */
> - public static ByteBuffer doHandshake(SelectionKey key,
> + public static ByteBuffer doHandshake(SocketChannel socketChannel,
> ByteBuffer byteBuffer, ByteBuffer inputBB, ByteBuffer outputBB,
> SSLEngine sslEngine, HandshakeStatus handshakeStatus)
> throws IOException {
> - return doHandshake(key,byteBuffer,inputBB,outputBB,sslEngine,
> - handshakeStatus,readTimeout);
> + return doHandshake(socketChannel, byteBuffer, inputBB, outputBB,
> + sslEngine, handshakeStatus, readTimeout);
> }
>
>
> /**
> * Perform an SSL handshake using the SSLEngine.
> - * @param key the <code>SelectionKey</code>
> + * @param socketChannel the <code>SocketChannel</code>
> * @param byteBuffer The application <code>ByteBuffer</code>
> * @param inputBB The encrypted input <code>ByteBuffer</code>
> * @param outputBB The encrypted output <code>ByteBuffer</code>
> @@ -320,9 +319,9 @@
> * @param timeout
> * @return byteBuffer the new ByteBuffer
> * @throws java.io.IOException
> - * @throw IOException if the handshake fail.
> + * @throws IOException if the handshake fail.
> */
> - public static ByteBuffer doHandshake(SelectionKey key,
> + public static ByteBuffer doHandshake(SocketChannel socketChannel,
> ByteBuffer byteBuffer, ByteBuffer inputBB, ByteBuffer outputBB,
> SSLEngine sslEngine, HandshakeStatus handshakeStatus,int timeout)
> throws IOException {
> @@ -332,7 +331,7 @@
> while (handshakeStatus != HandshakeStatus.FINISHED){
> switch (handshakeStatus) {
> case NEED_UNWRAP:
> - if (doRead(key,inputBB,sslEngine, timeout) <= eof) {
> + if (doRead(socketChannel,inputBB,sslEngine, timeout) <= eof) {
> try{
> sslEngine.closeInbound();
> } catch (IOException ex){
> @@ -390,9 +389,9 @@
> }
>
> // Flush all Server bytes to the client.
> - if (key != null) {
> + if (socketChannel != null) {
> OutputWriter.flushChannel(
> - (SocketChannel)key.channel(), outputBB);
> + socketChannel, outputBB);
> outputBB.clear();
> }
> break;
> Index: main/java/com/sun/grizzly/util/ByteBufferInputStream.java
> ===================================================================
> --- main/java/com/sun/grizzly/util/ByteBufferInputStream.java (revision 329)
> +++ main/java/com/sun/grizzly/util/ByteBufferInputStream.java (working copy)
> @@ -284,7 +284,7 @@
> // bytes, the byteBuffer will be empty event if some encrypted bytes
> // are available.
> while (byteBuffer.position() == initialPosition){
> - byteRead += SSLUtils.doRead(key,workerThread.getInputBB(),
> + byteRead += SSLUtils.doRead((SocketChannel) key.channel(), workerThread.getInputBB(),
> workerThread.getSSLEngine(),readTimeout);
>
> if (byteRead > 0 || workerThread.getInputBB().position() > 0) {
> @@ -371,10 +371,9 @@
> }
> SelectorFactory.returnSelector(readSelector);
> }
> -
> +
> byteBuffer.flip();
> }
> -
> return byteRead;
> }
>
>