You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by jo...@apache.org on 2021/07/24 15:20:39 UTC

[mina] branch bugfix/DIRMINA1132 created (now 4c7af56)

This is an automated email from the ASF dual-hosted git repository.

johnnyv pushed a change to branch bugfix/DIRMINA1132
in repository https://gitbox.apache.org/repos/asf/mina.git.


      at 4c7af56  Adds initial ssl2

This branch includes the following new commits:

     new aa500a6  formatted mina.filter.ssl
     new 5b51500  adds IoSession#isServer()
     new bb5b97e  Adds unique identifier to toString() result
     new 4c7af56  Adds initial ssl2

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[mina] 01/04: formatted mina.filter.ssl

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

johnnyv pushed a commit to branch bugfix/DIRMINA1132
in repository https://gitbox.apache.org/repos/asf/mina.git

commit aa500a68ab8f3b94ae5510d6b5da21da83287b23
Author: Jonathan Valliere <jo...@apache.org>
AuthorDate: Wed Sep 23 13:43:42 2020 -0400

    formatted mina.filter.ssl
---
 .../mina/filter/ssl/BogusTrustManagerFactory.java  |  112 +-
 .../apache/mina/filter/ssl/KeyStoreFactory.java    |  292 ++--
 .../apache/mina/filter/ssl/SslContextFactory.java  |  782 +++++-----
 .../java/org/apache/mina/filter/ssl/SslEvent.java  |    6 +-
 .../java/org/apache/mina/filter/ssl/SslFilter.java | 1643 ++++++++++----------
 .../org/apache/mina/filter/ssl/SslHandler.java     | 1615 +++++++++----------
 .../org/apache/mina/filter/ssl/package-info.java   |    3 +-
 7 files changed, 2233 insertions(+), 2220 deletions(-)

diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/BogusTrustManagerFactory.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/BogusTrustManagerFactory.java
index 5984e69..e0402c2 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/BogusTrustManagerFactory.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/BogusTrustManagerFactory.java
@@ -39,68 +39,68 @@ import javax.net.ssl.X509TrustManager;
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */
 public class BogusTrustManagerFactory extends TrustManagerFactory {
-    private static final X509TrustManager X509 = new X509TrustManager() {
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
-            // Do nothing
-        }
+	private static final X509TrustManager X509 = new X509TrustManager() {
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
+			// Do nothing
+		}
 
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
-            // Do nothing
-        }
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
+			// Do nothing
+		}
 
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public X509Certificate[] getAcceptedIssuers() {
-            return new X509Certificate[0];
-        }
-    };
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public X509Certificate[] getAcceptedIssuers() {
+			return new X509Certificate[0];
+		}
+	};
 
-    private static final TrustManager[] X509_MANAGERS = new TrustManager[] { X509 };
+	private static final TrustManager[] X509_MANAGERS = new TrustManager[] { X509 };
 
-    /**
-     * Creates a new BogusTrustManagerFactory instance
-     */
-    public BogusTrustManagerFactory() {
-        super(new BogusTrustManagerFactorySpi(), new Provider("MinaBogus", 1.0, "") {
-            private static final long serialVersionUID = -4024169055312053827L;
-        }, "MinaBogus");
-    }
+	/**
+	 * Creates a new BogusTrustManagerFactory instance
+	 */
+	public BogusTrustManagerFactory() {
+		super(new BogusTrustManagerFactorySpi(), new Provider("MinaBogus", 1.0, "") {
+			private static final long serialVersionUID = -4024169055312053827L;
+		}, "MinaBogus");
+	}
 
-    private static class BogusTrustManagerFactorySpi extends TrustManagerFactorySpi {
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        protected TrustManager[] engineGetTrustManagers() {
-            return X509_MANAGERS;
-        }
+	private static class BogusTrustManagerFactorySpi extends TrustManagerFactorySpi {
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		protected TrustManager[] engineGetTrustManagers() {
+			return X509_MANAGERS;
+		}
 
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        protected void engineInit(KeyStore keystore) throws KeyStoreException {
-            // noop
-        }
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		protected void engineInit(KeyStore keystore) throws KeyStoreException {
+			// noop
+		}
 
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        protected void engineInit(ManagerFactoryParameters managerFactoryParameters)
-                throws InvalidAlgorithmParameterException {
-            // noop
-        }
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		protected void engineInit(ManagerFactoryParameters managerFactoryParameters)
+				throws InvalidAlgorithmParameterException {
+			// noop
+		}
 
-    }
+	}
 }
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/KeyStoreFactory.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/KeyStoreFactory.java
index b0903fa..875f039 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/KeyStoreFactory.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/KeyStoreFactory.java
@@ -40,150 +40,150 @@ import java.security.cert.CertificateException;
  */
 public class KeyStoreFactory {
 
-    private String type = "JKS";
-
-    private String provider = null;
-
-    private char[] password = null;
-
-    private byte[] data = null;
-
-    /**
-     * Creates a new {@link KeyStore}. This method will be called
-     * by the base class when Spring creates a bean using this FactoryBean.
-     *
-     * @return a new {@link KeyStore} instance.
-     * @throws KeyStoreException If we can't create an instance of the KeyStore for the given type
-     * @throws NoSuchProviderException If we don't have the provider registered to create the KeyStore
-     * @throws NoSuchAlgorithmException If the KeyStore algorithm cannot be used
-     * @throws CertificateException If the KeyStore certificate cannot be loaded 
-     * @throws IOException If the KeyStore cannot be loaded
-     */
-    public KeyStore newInstance() throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException,
-            CertificateException, IOException {
-        if (data == null) {
-            throw new IllegalStateException("data property is not set.");
-        }
-
-        KeyStore ks;
-        if (provider == null) {
-            ks = KeyStore.getInstance(type);
-        } else {
-            ks = KeyStore.getInstance(type, provider);
-        }
-
-        InputStream is = new ByteArrayInputStream(data);
-        
-        try {
-            ks.load(is, password);
-        } finally {
-            try {
-                is.close();
-            } catch (IOException ignored) {
-                // Do nothing
-            }
-        }
-
-        return ks;
-    }
-
-    /**
-     * Sets the type of key store to create. The default is to create a
-     * JKS key store.
-     *
-     * @param type the type to use when creating the key store.
-     * @throws IllegalArgumentException if the specified value is
-     *         <code>null</code>.
-     */
-    public void setType(String type) {
-        if (type == null) {
-            throw new IllegalArgumentException("type");
-        }
-        this.type = type;
-    }
-
-    /**
-     * Sets the key store password. If this value is <code>null</code> no
-     * password will be used to check the integrity of the key store.
-     *
-     * @param password the password or <code>null</code> if no password is
-     *        needed.
-     */
-    public void setPassword(String password) {
-        if (password != null) {
-            this.password = password.toCharArray();
-        } else {
-            this.password = null;
-        }
-    }
-
-    /**
-     * Sets the name of the provider to use when creating the key store. The
-     * default is to use the platform default provider.
-     *
-     * @param provider the name of the provider, e.g. <tt>"SUN"</tt>.
-     */
-    public void setProvider(String provider) {
-        this.provider = provider;
-    }
-
-    /**
-     * Sets the data which contains the key store.
-     *
-     * @param data the byte array that contains the key store
-     */
-    public void setData(byte[] data) {
-        byte[] copy = new byte[data.length];
-        System.arraycopy(data, 0, copy, 0, data.length);
-        this.data = copy;
-    }
-
-    /**
-     * Sets the data which contains the key store.
-     *
-     * @param dataStream the {@link InputStream} that contains the key store
-     * @throws IOException If we can't process the stream
-     */
-    private void setData(InputStream dataStream) throws IOException {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        try {
-            for (;;) {
-                int readByte = dataStream.read();
-                
-                if (readByte < 0) {
-                    break;
-                }
-                
-                out.write(readByte);
-            }
-            
-            setData(out.toByteArray());
-        } finally {
-            try {
-                dataStream.close();
-            } catch (IOException e) {
-                // Ignore.
-            }
-        }
-    }
-
-    /**
-     * Sets the data which contains the key store.
-     *
-     * @param dataFile the {@link File} that contains the key store
-     * @throws IOException If we can't process the file
-     */
-    public void setDataFile(File dataFile) throws IOException {
-        setData(new BufferedInputStream(new FileInputStream(dataFile)));
-    }
-
-    /**
-     * Sets the data which contains the key store.
-     *
-     * @param dataUrl the {@link URL} that contains the key store.
-     * @throws IOException If we can't process the URL
-     */
-    public void setDataUrl(URL dataUrl) throws IOException {
-        setData(dataUrl.openStream());
-    }
+	private String type = "JKS";
+
+	private String provider = null;
+
+	private char[] password = null;
+
+	private byte[] data = null;
+
+	/**
+	 * Creates a new {@link KeyStore}. This method will be called by the base class
+	 * when Spring creates a bean using this FactoryBean.
+	 *
+	 * @return a new {@link KeyStore} instance.
+	 * @throws KeyStoreException        If we can't create an instance of the
+	 *                                  KeyStore for the given type
+	 * @throws NoSuchProviderException  If we don't have the provider registered to
+	 *                                  create the KeyStore
+	 * @throws NoSuchAlgorithmException If the KeyStore algorithm cannot be used
+	 * @throws CertificateException     If the KeyStore certificate cannot be loaded
+	 * @throws IOException              If the KeyStore cannot be loaded
+	 */
+	public KeyStore newInstance() throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException,
+			CertificateException, IOException {
+		if (data == null) {
+			throw new IllegalStateException("data property is not set.");
+		}
+
+		KeyStore ks;
+		if (provider == null) {
+			ks = KeyStore.getInstance(type);
+		} else {
+			ks = KeyStore.getInstance(type, provider);
+		}
+
+		InputStream is = new ByteArrayInputStream(data);
+
+		try {
+			ks.load(is, password);
+		} finally {
+			try {
+				is.close();
+			} catch (IOException ignored) {
+				// Do nothing
+			}
+		}
+
+		return ks;
+	}
+
+	/**
+	 * Sets the type of key store to create. The default is to create a JKS key
+	 * store.
+	 *
+	 * @param type the type to use when creating the key store.
+	 * @throws IllegalArgumentException if the specified value is <code>null</code>.
+	 */
+	public void setType(String type) {
+		if (type == null) {
+			throw new IllegalArgumentException("type");
+		}
+		this.type = type;
+	}
+
+	/**
+	 * Sets the key store password. If this value is <code>null</code> no password
+	 * will be used to check the integrity of the key store.
+	 *
+	 * @param password the password or <code>null</code> if no password is needed.
+	 */
+	public void setPassword(String password) {
+		if (password != null) {
+			this.password = password.toCharArray();
+		} else {
+			this.password = null;
+		}
+	}
+
+	/**
+	 * Sets the name of the provider to use when creating the key store. The default
+	 * is to use the platform default provider.
+	 *
+	 * @param provider the name of the provider, e.g. <tt>"SUN"</tt>.
+	 */
+	public void setProvider(String provider) {
+		this.provider = provider;
+	}
+
+	/**
+	 * Sets the data which contains the key store.
+	 *
+	 * @param data the byte array that contains the key store
+	 */
+	public void setData(byte[] data) {
+		byte[] copy = new byte[data.length];
+		System.arraycopy(data, 0, copy, 0, data.length);
+		this.data = copy;
+	}
+
+	/**
+	 * Sets the data which contains the key store.
+	 *
+	 * @param dataStream the {@link InputStream} that contains the key store
+	 * @throws IOException If we can't process the stream
+	 */
+	private void setData(InputStream dataStream) throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		try {
+			for (;;) {
+				int readByte = dataStream.read();
+
+				if (readByte < 0) {
+					break;
+				}
+
+				out.write(readByte);
+			}
+
+			setData(out.toByteArray());
+		} finally {
+			try {
+				dataStream.close();
+			} catch (IOException e) {
+				// Ignore.
+			}
+		}
+	}
+
+	/**
+	 * Sets the data which contains the key store.
+	 *
+	 * @param dataFile the {@link File} that contains the key store
+	 * @throws IOException If we can't process the file
+	 */
+	public void setDataFile(File dataFile) throws IOException {
+		setData(new BufferedInputStream(new FileInputStream(dataFile)));
+	}
+
+	/**
+	 * Sets the data which contains the key store.
+	 *
+	 * @param dataUrl the {@link URL} that contains the key store.
+	 * @throws IOException If we can't process the URL
+	 */
+	public void setDataUrl(URL dataUrl) throws IOException {
+		setData(dataUrl.openStream());
+	}
 }
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslContextFactory.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslContextFactory.java
index e05d3d6..255eeb5 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslContextFactory.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslContextFactory.java
@@ -33,11 +33,12 @@ import javax.net.ssl.TrustManagerFactory;
 /**
  * A factory that creates and configures a new {@link SSLContext}.
  * <p>
- * If no properties are set the returned {@link SSLContext} will
- * be equivalent to what the following creates:
+ * If no properties are set the returned {@link SSLContext} will be equivalent
+ * to what the following creates:
+ * 
  * <pre>
- *      SSLContext c = SSLContext.getInstance( "TLSv1.2" );
- *      c.init(null, null, null);
+ * SSLContext c = SSLContext.getInstance("TLSv1.2");
+ * c.init(null, null, null);
  * </pre>
  * <p>
  * Use the properties prefixed with <code>keyManagerFactory</code> to control
@@ -50,388 +51,393 @@ import javax.net.ssl.TrustManagerFactory;
  */
 public class SslContextFactory {
 
-    private String provider = null;
-
-    private String protocol = "TLSv1.2";
-
-    private SecureRandom secureRandom = null;
-
-    private KeyStore keyManagerFactoryKeyStore = null;
-
-    private char[] keyManagerFactoryKeyStorePassword = null;
-
-    private KeyManagerFactory keyManagerFactory = null;
-
-    private String keyManagerFactoryAlgorithm = null;
-
-    private String keyManagerFactoryProvider = null;
-
-    private boolean keyManagerFactoryAlgorithmUseDefault = true;
-
-    private KeyStore trustManagerFactoryKeyStore = null;
-
-    private TrustManagerFactory trustManagerFactory = null;
-
-    private String trustManagerFactoryAlgorithm = null;
-
-    private String trustManagerFactoryProvider = null;
-
-    private boolean trustManagerFactoryAlgorithmUseDefault = true;
-
-    private ManagerFactoryParameters trustManagerFactoryParameters = null;
-
-    private int clientSessionCacheSize = -1;
-
-    private int clientSessionTimeout = -1;
-
-    private int serverSessionCacheSize = -1;
-
-    private int serverSessionTimeout = -1;
-
-    /**
-     * Create a new SSLContext instance,using the {@link KeyManagerFactory} and the
-     * {@link TrustManagerFactory}.
-     *  
-     * @return The created instance
-     * @throws Exception If we weren't able to create the SSLContext insyance
-     */
-    public SSLContext newInstance() throws Exception {
-        KeyManagerFactory kmf = this.keyManagerFactory;
-        TrustManagerFactory tmf = this.trustManagerFactory;
-
-        if (kmf == null) {
-            String algorithm = keyManagerFactoryAlgorithm;
-            
-            if (algorithm == null && keyManagerFactoryAlgorithmUseDefault) {
-                algorithm = KeyManagerFactory.getDefaultAlgorithm();
-            }
-            
-            if (algorithm != null) {
-                if (keyManagerFactoryProvider == null) {
-                    kmf = KeyManagerFactory.getInstance(algorithm);
-                } else {
-                    kmf = KeyManagerFactory.getInstance(algorithm, keyManagerFactoryProvider);
-                }
-            }
-        }
-
-        if (tmf == null) {
-            String algorithm = trustManagerFactoryAlgorithm;
-            
-            if (algorithm == null && trustManagerFactoryAlgorithmUseDefault) {
-                algorithm = TrustManagerFactory.getDefaultAlgorithm();
-            }
-            
-            if (algorithm != null) {
-                if (trustManagerFactoryProvider == null) {
-                    tmf = TrustManagerFactory.getInstance(algorithm);
-                } else {
-                    tmf = TrustManagerFactory.getInstance(algorithm, trustManagerFactoryProvider);
-                }
-            }
-        }
-
-        KeyManager[] keyManagers = null;
-        
-        if (kmf != null) {
-            kmf.init(keyManagerFactoryKeyStore, keyManagerFactoryKeyStorePassword);
-            keyManagers = kmf.getKeyManagers();
-        }
-        
-        TrustManager[] trustManagers = null;
-        
-        if (tmf != null) {
-            if (trustManagerFactoryParameters != null) {
-                tmf.init(trustManagerFactoryParameters);
-            } else {
-                tmf.init(trustManagerFactoryKeyStore);
-            }
-            
-            trustManagers = tmf.getTrustManagers();
-        }
-
-        SSLContext context;
-        
-        if (provider == null) {
-            context = SSLContext.getInstance(protocol);
-        } else {
-            context = SSLContext.getInstance(protocol, provider);
-        }
-
-        context.init(keyManagers, trustManagers, secureRandom);
-
-        if (clientSessionCacheSize >= 0) {
-            context.getClientSessionContext().setSessionCacheSize(clientSessionCacheSize);
-        }
-
-        if (clientSessionTimeout >= 0) {
-            context.getClientSessionContext().setSessionTimeout(clientSessionTimeout);
-        }
-
-        if (serverSessionCacheSize >= 0) {
-            context.getServerSessionContext().setSessionCacheSize(serverSessionCacheSize);
-        }
-
-        if (serverSessionTimeout >= 0) {
-            context.getServerSessionContext().setSessionTimeout(serverSessionTimeout);
-        }
-
-        return context;
-    }
-
-    /**
-     * Sets the provider of the new {@link SSLContext}. The default value is
-     * <tt>null</tt>, which means the default provider will be used.
-     * 
-     * @param provider the name of the {@link SSLContext} provider
-     */
-    public void setProvider(String provider) {
-        this.provider = provider;
-    }
-
-    /**
-     * Sets the protocol to use when creating the {@link SSLContext}. The
-     * default is <code>TLS</code>.
-     *
-     * @param protocol the name of the protocol.
-     */
-    public void setProtocol(String protocol) {
-        if (protocol == null) {
-            throw new IllegalArgumentException("protocol");
-        }
-        
-        this.protocol = protocol;
-    }
-
-    /**
-     * If this is set to <tt>true</tt> while no {@link KeyManagerFactory}
-     * has been set using {@link #setKeyManagerFactory(KeyManagerFactory)} and
-     * no algorithm has been set using
-     * {@link #setKeyManagerFactoryAlgorithm(String)} the default algorithm
-     * return by {@link KeyManagerFactory#getDefaultAlgorithm()} will be used.
-     * The default value of this property is <tt>true</tt>.
-     *
-     * @param useDefault <tt>true</tt> or <tt>false</tt>.
-     */
-    public void setKeyManagerFactoryAlgorithmUseDefault(boolean useDefault) {
-        this.keyManagerFactoryAlgorithmUseDefault = useDefault;
-    }
-
-    /**
-     * If this is set to <tt>true</tt> while no {@link TrustManagerFactory}
-     * has been set using {@link #setTrustManagerFactory(TrustManagerFactory)} and
-     * no algorithm has been set using
-     * {@link #setTrustManagerFactoryAlgorithm(String)} the default algorithm
-     * return by {@link TrustManagerFactory#getDefaultAlgorithm()} will be used.
-     * The default value of this property is <tt>true</tt>.
-     *
-     * @param useDefault <tt>true</tt> or <tt>false</tt>.
-     */
-    public void setTrustManagerFactoryAlgorithmUseDefault(boolean useDefault) {
-        this.trustManagerFactoryAlgorithmUseDefault = useDefault;
-    }
-
-    /**
-     * Sets the {@link KeyManagerFactory} to use. If this is set the properties
-     * which are used by this factory bean to create a {@link KeyManagerFactory}
-     * will all be ignored.
-     *
-     * @param factory the factory.
-     */
-    public void setKeyManagerFactory(KeyManagerFactory factory) {
-        this.keyManagerFactory = factory;
-    }
-
-    /**
-     * Sets the algorithm to use when creating the {@link KeyManagerFactory}
-     * using {@link KeyManagerFactory#getInstance(java.lang.String)} or
-     * {@link KeyManagerFactory#getInstance(java.lang.String, java.lang.String)}.
-     * <p>
-     * This property will be ignored if a {@link KeyManagerFactory} has been
-     * set directly using {@link #setKeyManagerFactory(KeyManagerFactory)}.
-     * <p>
-     * If this property isn't set while no {@link KeyManagerFactory} has been
-     * set using {@link #setKeyManagerFactory(KeyManagerFactory)} and
-     * {@link #setKeyManagerFactoryAlgorithmUseDefault(boolean)} has been set to
-     * <tt>true</tt> the value returned
-     * by {@link KeyManagerFactory#getDefaultAlgorithm()} will be used instead.
-     *
-     * @param algorithm the algorithm to use.
-     */
-    public void setKeyManagerFactoryAlgorithm(String algorithm) {
-        this.keyManagerFactoryAlgorithm = algorithm;
-    }
-
-    /**
-     * Sets the provider to use when creating the {@link KeyManagerFactory}
-     * using
-     * {@link KeyManagerFactory#getInstance(java.lang.String, java.lang.String)}.
-     * <p>
-     * This property will be ignored if a {@link KeyManagerFactory} has been
-     * set directly using {@link #setKeyManagerFactory(KeyManagerFactory)}.
-     * <p>
-     * If this property isn't set and no {@link KeyManagerFactory} has been set
-     * using {@link #setKeyManagerFactory(KeyManagerFactory)}
-     * {@link KeyManagerFactory#getInstance(java.lang.String)} will be used
-     * to create the {@link KeyManagerFactory}.
-     *
-     * @param provider the name of the provider.
-     */
-    public void setKeyManagerFactoryProvider(String provider) {
-        this.keyManagerFactoryProvider = provider;
-    }
-
-    /**
-     * Sets the {@link KeyStore} which will be used in the call to
-     * {@link KeyManagerFactory#init(java.security.KeyStore, char[])} when
-     * the {@link SSLContext} is created.
-     *
-     * @param keyStore the key store.
-     */
-    public void setKeyManagerFactoryKeyStore(KeyStore keyStore) {
-        this.keyManagerFactoryKeyStore = keyStore;
-    }
-
-    /**
-     * Sets the password which will be used in the call to
-     * {@link KeyManagerFactory#init(java.security.KeyStore, char[])} when
-     * the {@link SSLContext} is created.
-     *
-     * @param password the password. Use <code>null</code> to disable password.
-     */
-    public void setKeyManagerFactoryKeyStorePassword(String password) {
-        if (password != null) {
-            this.keyManagerFactoryKeyStorePassword = password.toCharArray();
-        } else {
-            this.keyManagerFactoryKeyStorePassword = null;
-        }
-    }
-
-    /**
-     * Sets the {@link TrustManagerFactory} to use. If this is set the
-     * properties which are used by this factory bean to create a
-     * {@link TrustManagerFactory} will all be ignored.
-     *
-     * @param factory
-     *            the factory.
-     */
-    public void setTrustManagerFactory(TrustManagerFactory factory) {
-        this.trustManagerFactory = factory;
-    }
-
-    /**
-     * Sets the algorithm to use when creating the {@link TrustManagerFactory}
-     * using {@link TrustManagerFactory#getInstance(java.lang.String)} or
-     * {@link TrustManagerFactory#getInstance(java.lang.String, java.lang.String)}.
-     * <p>
-     * This property will be ignored if a {@link TrustManagerFactory} has been
-     * set directly using {@link #setTrustManagerFactory(TrustManagerFactory)}.
-     * <p>
-     * If this property isn't set while no {@link TrustManagerFactory} has been
-     * set using {@link #setTrustManagerFactory(TrustManagerFactory)} and
-     * {@link #setTrustManagerFactoryAlgorithmUseDefault(boolean)} has been set to
-     * <tt>true</tt> the value returned
-     * by {@link TrustManagerFactory#getDefaultAlgorithm()} will be used instead.
-     *
-     * @param algorithm the algorithm to use.
-     */
-    public void setTrustManagerFactoryAlgorithm(String algorithm) {
-        this.trustManagerFactoryAlgorithm = algorithm;
-    }
-
-    /**
-     * Sets the {@link KeyStore} which will be used in the call to
-     * {@link TrustManagerFactory#init(java.security.KeyStore)} when
-     * the {@link SSLContext} is created.
-     * <p>
-     * This property will be ignored if {@link ManagerFactoryParameters} has been
-     * set directly using {@link #setTrustManagerFactoryParameters(ManagerFactoryParameters)}.
-     *
-     * @param keyStore the key store.
-     */
-    public void setTrustManagerFactoryKeyStore(KeyStore keyStore) {
-        this.trustManagerFactoryKeyStore = keyStore;
-    }
-
-    /**
-     * Sets the {@link ManagerFactoryParameters} which will be used in the call to
-     * {@link TrustManagerFactory#init(javax.net.ssl.ManagerFactoryParameters)} when
-     * the {@link SSLContext} is created.
-     *
-     * @param parameters describing provider-specific trust material.
-     */
-    public void setTrustManagerFactoryParameters(ManagerFactoryParameters parameters) {
-        this.trustManagerFactoryParameters = parameters;
-    }
-
-    /**
-     * Sets the provider to use when creating the {@link TrustManagerFactory}
-     * using
-     * {@link TrustManagerFactory#getInstance(java.lang.String, java.lang.String)}.
-     * <p>
-     * This property will be ignored if a {@link TrustManagerFactory} has been
-     * set directly using {@link #setTrustManagerFactory(TrustManagerFactory)}.
-     * <p>
-     * If this property isn't set and no {@link TrustManagerFactory} has been set
-     * using {@link #setTrustManagerFactory(TrustManagerFactory)}
-     * {@link TrustManagerFactory#getInstance(java.lang.String)} will be used
-     * to create the {@link TrustManagerFactory}.
-     *
-     * @param provider the name of the provider.
-     */
-    public void setTrustManagerFactoryProvider(String provider) {
-        this.trustManagerFactoryProvider = provider;
-    }
-
-    /**
-     * Sets the {@link SecureRandom} to use when initializing the
-     * {@link SSLContext}. The JVM's default will be used if this isn't set.
-     *
-     * @param secureRandom the {@link SecureRandom} or <code>null</code> if the
-     *        JVM's default should be used.
-     * @see SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], java.security.SecureRandom)
-     */
-    public void setSecureRandom(SecureRandom secureRandom) {
-        this.secureRandom = secureRandom;
-    }
-
-    /**
-     * Sets the SSLSession cache size for the {@link SSLSessionContext} for use in client mode.
-     *
-     * @param size the new session cache size limit; zero means there is no limit.
-     * @see SSLSessionContext#setSessionCacheSize(int size)
-     */
-    public void setClientSessionCacheSize(int size) {
-        this.clientSessionCacheSize = size;
-    }
-
-    /**
-     * Set the SSLSession timeout limit for the {@link SSLSessionContext} for use in client mode.
-     *
-     * @param seconds the new session timeout limit in seconds; zero means there is no limit.
-     * @see SSLSessionContext#setSessionTimeout(int seconds)
-     */
-    public void setClientSessionTimeout(int seconds) {
-        this.clientSessionTimeout = seconds;
-    }
-
-    /**
-     * Sets the SSLSession cache size for the {@link SSLSessionContext} for use in server mode.
-     *
-     * @param serverSessionCacheSize the new session cache size limit; zero means there is no limit.
-     * @see SSLSessionContext#setSessionCacheSize(int)
-     */
-    public void setServerSessionCacheSize(int serverSessionCacheSize) {
-        this.serverSessionCacheSize = serverSessionCacheSize;
-    }
-
-    /**
-     * Set the SSLSession timeout limit for the {@link SSLSessionContext} for use in server mode.
-     *
-     * @param serverSessionTimeout the new session timeout limit in seconds; zero means there is no limit.
-     * @see SSLSessionContext#setSessionTimeout(int)
-     */
-    public void setServerSessionTimeout(int serverSessionTimeout) {
-        this.serverSessionTimeout = serverSessionTimeout;
-    }
+	private String provider = null;
+
+	private String protocol = "TLSv1.2";
+
+	private SecureRandom secureRandom = null;
+
+	private KeyStore keyManagerFactoryKeyStore = null;
+
+	private char[] keyManagerFactoryKeyStorePassword = null;
+
+	private KeyManagerFactory keyManagerFactory = null;
+
+	private String keyManagerFactoryAlgorithm = null;
+
+	private String keyManagerFactoryProvider = null;
+
+	private boolean keyManagerFactoryAlgorithmUseDefault = true;
+
+	private KeyStore trustManagerFactoryKeyStore = null;
+
+	private TrustManagerFactory trustManagerFactory = null;
+
+	private String trustManagerFactoryAlgorithm = null;
+
+	private String trustManagerFactoryProvider = null;
+
+	private boolean trustManagerFactoryAlgorithmUseDefault = true;
+
+	private ManagerFactoryParameters trustManagerFactoryParameters = null;
+
+	private int clientSessionCacheSize = -1;
+
+	private int clientSessionTimeout = -1;
+
+	private int serverSessionCacheSize = -1;
+
+	private int serverSessionTimeout = -1;
+
+	/**
+	 * Create a new SSLContext instance,using the {@link KeyManagerFactory} and the
+	 * {@link TrustManagerFactory}.
+	 * 
+	 * @return The created instance
+	 * @throws Exception If we weren't able to create the SSLContext insyance
+	 */
+	public SSLContext newInstance() throws Exception {
+		KeyManagerFactory kmf = this.keyManagerFactory;
+		TrustManagerFactory tmf = this.trustManagerFactory;
+
+		if (kmf == null) {
+			String algorithm = keyManagerFactoryAlgorithm;
+
+			if (algorithm == null && keyManagerFactoryAlgorithmUseDefault) {
+				algorithm = KeyManagerFactory.getDefaultAlgorithm();
+			}
+
+			if (algorithm != null) {
+				if (keyManagerFactoryProvider == null) {
+					kmf = KeyManagerFactory.getInstance(algorithm);
+				} else {
+					kmf = KeyManagerFactory.getInstance(algorithm, keyManagerFactoryProvider);
+				}
+			}
+		}
+
+		if (tmf == null) {
+			String algorithm = trustManagerFactoryAlgorithm;
+
+			if (algorithm == null && trustManagerFactoryAlgorithmUseDefault) {
+				algorithm = TrustManagerFactory.getDefaultAlgorithm();
+			}
+
+			if (algorithm != null) {
+				if (trustManagerFactoryProvider == null) {
+					tmf = TrustManagerFactory.getInstance(algorithm);
+				} else {
+					tmf = TrustManagerFactory.getInstance(algorithm, trustManagerFactoryProvider);
+				}
+			}
+		}
+
+		KeyManager[] keyManagers = null;
+
+		if (kmf != null) {
+			kmf.init(keyManagerFactoryKeyStore, keyManagerFactoryKeyStorePassword);
+			keyManagers = kmf.getKeyManagers();
+		}
+
+		TrustManager[] trustManagers = null;
+
+		if (tmf != null) {
+			if (trustManagerFactoryParameters != null) {
+				tmf.init(trustManagerFactoryParameters);
+			} else {
+				tmf.init(trustManagerFactoryKeyStore);
+			}
+
+			trustManagers = tmf.getTrustManagers();
+		}
+
+		SSLContext context;
+
+		if (provider == null) {
+			context = SSLContext.getInstance(protocol);
+		} else {
+			context = SSLContext.getInstance(protocol, provider);
+		}
+
+		context.init(keyManagers, trustManagers, secureRandom);
+
+		if (clientSessionCacheSize >= 0) {
+			context.getClientSessionContext().setSessionCacheSize(clientSessionCacheSize);
+		}
+
+		if (clientSessionTimeout >= 0) {
+			context.getClientSessionContext().setSessionTimeout(clientSessionTimeout);
+		}
+
+		if (serverSessionCacheSize >= 0) {
+			context.getServerSessionContext().setSessionCacheSize(serverSessionCacheSize);
+		}
+
+		if (serverSessionTimeout >= 0) {
+			context.getServerSessionContext().setSessionTimeout(serverSessionTimeout);
+		}
+
+		return context;
+	}
+
+	/**
+	 * Sets the provider of the new {@link SSLContext}. The default value is
+	 * <tt>null</tt>, which means the default provider will be used.
+	 * 
+	 * @param provider the name of the {@link SSLContext} provider
+	 */
+	public void setProvider(String provider) {
+		this.provider = provider;
+	}
+
+	/**
+	 * Sets the protocol to use when creating the {@link SSLContext}. The default is
+	 * <code>TLS</code>.
+	 *
+	 * @param protocol the name of the protocol.
+	 */
+	public void setProtocol(String protocol) {
+		if (protocol == null) {
+			throw new IllegalArgumentException("protocol");
+		}
+
+		this.protocol = protocol;
+	}
+
+	/**
+	 * If this is set to <tt>true</tt> while no {@link KeyManagerFactory} has been
+	 * set using {@link #setKeyManagerFactory(KeyManagerFactory)} and no algorithm
+	 * has been set using {@link #setKeyManagerFactoryAlgorithm(String)} the default
+	 * algorithm return by {@link KeyManagerFactory#getDefaultAlgorithm()} will be
+	 * used. The default value of this property is <tt>true</tt>.
+	 *
+	 * @param useDefault <tt>true</tt> or <tt>false</tt>.
+	 */
+	public void setKeyManagerFactoryAlgorithmUseDefault(boolean useDefault) {
+		this.keyManagerFactoryAlgorithmUseDefault = useDefault;
+	}
+
+	/**
+	 * If this is set to <tt>true</tt> while no {@link TrustManagerFactory} has been
+	 * set using {@link #setTrustManagerFactory(TrustManagerFactory)} and no
+	 * algorithm has been set using {@link #setTrustManagerFactoryAlgorithm(String)}
+	 * the default algorithm return by
+	 * {@link TrustManagerFactory#getDefaultAlgorithm()} will be used. The default
+	 * value of this property is <tt>true</tt>.
+	 *
+	 * @param useDefault <tt>true</tt> or <tt>false</tt>.
+	 */
+	public void setTrustManagerFactoryAlgorithmUseDefault(boolean useDefault) {
+		this.trustManagerFactoryAlgorithmUseDefault = useDefault;
+	}
+
+	/**
+	 * Sets the {@link KeyManagerFactory} to use. If this is set the properties
+	 * which are used by this factory bean to create a {@link KeyManagerFactory}
+	 * will all be ignored.
+	 *
+	 * @param factory the factory.
+	 */
+	public void setKeyManagerFactory(KeyManagerFactory factory) {
+		this.keyManagerFactory = factory;
+	}
+
+	/**
+	 * Sets the algorithm to use when creating the {@link KeyManagerFactory} using
+	 * {@link KeyManagerFactory#getInstance(java.lang.String)} or
+	 * {@link KeyManagerFactory#getInstance(java.lang.String, java.lang.String)}.
+	 * <p>
+	 * This property will be ignored if a {@link KeyManagerFactory} has been set
+	 * directly using {@link #setKeyManagerFactory(KeyManagerFactory)}.
+	 * <p>
+	 * If this property isn't set while no {@link KeyManagerFactory} has been set
+	 * using {@link #setKeyManagerFactory(KeyManagerFactory)} and
+	 * {@link #setKeyManagerFactoryAlgorithmUseDefault(boolean)} has been set to
+	 * <tt>true</tt> the value returned by
+	 * {@link KeyManagerFactory#getDefaultAlgorithm()} will be used instead.
+	 *
+	 * @param algorithm the algorithm to use.
+	 */
+	public void setKeyManagerFactoryAlgorithm(String algorithm) {
+		this.keyManagerFactoryAlgorithm = algorithm;
+	}
+
+	/**
+	 * Sets the provider to use when creating the {@link KeyManagerFactory} using
+	 * {@link KeyManagerFactory#getInstance(java.lang.String, java.lang.String)}.
+	 * <p>
+	 * This property will be ignored if a {@link KeyManagerFactory} has been set
+	 * directly using {@link #setKeyManagerFactory(KeyManagerFactory)}.
+	 * <p>
+	 * If this property isn't set and no {@link KeyManagerFactory} has been set
+	 * using {@link #setKeyManagerFactory(KeyManagerFactory)}
+	 * {@link KeyManagerFactory#getInstance(java.lang.String)} will be used to
+	 * create the {@link KeyManagerFactory}.
+	 *
+	 * @param provider the name of the provider.
+	 */
+	public void setKeyManagerFactoryProvider(String provider) {
+		this.keyManagerFactoryProvider = provider;
+	}
+
+	/**
+	 * Sets the {@link KeyStore} which will be used in the call to
+	 * {@link KeyManagerFactory#init(java.security.KeyStore, char[])} when the
+	 * {@link SSLContext} is created.
+	 *
+	 * @param keyStore the key store.
+	 */
+	public void setKeyManagerFactoryKeyStore(KeyStore keyStore) {
+		this.keyManagerFactoryKeyStore = keyStore;
+	}
+
+	/**
+	 * Sets the password which will be used in the call to
+	 * {@link KeyManagerFactory#init(java.security.KeyStore, char[])} when the
+	 * {@link SSLContext} is created.
+	 *
+	 * @param password the password. Use <code>null</code> to disable password.
+	 */
+	public void setKeyManagerFactoryKeyStorePassword(String password) {
+		if (password != null) {
+			this.keyManagerFactoryKeyStorePassword = password.toCharArray();
+		} else {
+			this.keyManagerFactoryKeyStorePassword = null;
+		}
+	}
+
+	/**
+	 * Sets the {@link TrustManagerFactory} to use. If this is set the properties
+	 * which are used by this factory bean to create a {@link TrustManagerFactory}
+	 * will all be ignored.
+	 *
+	 * @param factory the factory.
+	 */
+	public void setTrustManagerFactory(TrustManagerFactory factory) {
+		this.trustManagerFactory = factory;
+	}
+
+	/**
+	 * Sets the algorithm to use when creating the {@link TrustManagerFactory} using
+	 * {@link TrustManagerFactory#getInstance(java.lang.String)} or
+	 * {@link TrustManagerFactory#getInstance(java.lang.String, java.lang.String)}.
+	 * <p>
+	 * This property will be ignored if a {@link TrustManagerFactory} has been set
+	 * directly using {@link #setTrustManagerFactory(TrustManagerFactory)}.
+	 * <p>
+	 * If this property isn't set while no {@link TrustManagerFactory} has been set
+	 * using {@link #setTrustManagerFactory(TrustManagerFactory)} and
+	 * {@link #setTrustManagerFactoryAlgorithmUseDefault(boolean)} has been set to
+	 * <tt>true</tt> the value returned by
+	 * {@link TrustManagerFactory#getDefaultAlgorithm()} will be used instead.
+	 *
+	 * @param algorithm the algorithm to use.
+	 */
+	public void setTrustManagerFactoryAlgorithm(String algorithm) {
+		this.trustManagerFactoryAlgorithm = algorithm;
+	}
+
+	/**
+	 * Sets the {@link KeyStore} which will be used in the call to
+	 * {@link TrustManagerFactory#init(java.security.KeyStore)} when the
+	 * {@link SSLContext} is created.
+	 * <p>
+	 * This property will be ignored if {@link ManagerFactoryParameters} has been
+	 * set directly using
+	 * {@link #setTrustManagerFactoryParameters(ManagerFactoryParameters)}.
+	 *
+	 * @param keyStore the key store.
+	 */
+	public void setTrustManagerFactoryKeyStore(KeyStore keyStore) {
+		this.trustManagerFactoryKeyStore = keyStore;
+	}
+
+	/**
+	 * Sets the {@link ManagerFactoryParameters} which will be used in the call to
+	 * {@link TrustManagerFactory#init(javax.net.ssl.ManagerFactoryParameters)} when
+	 * the {@link SSLContext} is created.
+	 *
+	 * @param parameters describing provider-specific trust material.
+	 */
+	public void setTrustManagerFactoryParameters(ManagerFactoryParameters parameters) {
+		this.trustManagerFactoryParameters = parameters;
+	}
+
+	/**
+	 * Sets the provider to use when creating the {@link TrustManagerFactory} using
+	 * {@link TrustManagerFactory#getInstance(java.lang.String, java.lang.String)}.
+	 * <p>
+	 * This property will be ignored if a {@link TrustManagerFactory} has been set
+	 * directly using {@link #setTrustManagerFactory(TrustManagerFactory)}.
+	 * <p>
+	 * If this property isn't set and no {@link TrustManagerFactory} has been set
+	 * using {@link #setTrustManagerFactory(TrustManagerFactory)}
+	 * {@link TrustManagerFactory#getInstance(java.lang.String)} will be used to
+	 * create the {@link TrustManagerFactory}.
+	 *
+	 * @param provider the name of the provider.
+	 */
+	public void setTrustManagerFactoryProvider(String provider) {
+		this.trustManagerFactoryProvider = provider;
+	}
+
+	/**
+	 * Sets the {@link SecureRandom} to use when initializing the
+	 * {@link SSLContext}. The JVM's default will be used if this isn't set.
+	 *
+	 * @param secureRandom the {@link SecureRandom} or <code>null</code> if the
+	 *                     JVM's default should be used.
+	 * @see SSLContext#init(javax.net.ssl.KeyManager[],
+	 *      javax.net.ssl.TrustManager[], java.security.SecureRandom)
+	 */
+	public void setSecureRandom(SecureRandom secureRandom) {
+		this.secureRandom = secureRandom;
+	}
+
+	/**
+	 * Sets the SSLSession cache size for the {@link SSLSessionContext} for use in
+	 * client mode.
+	 *
+	 * @param size the new session cache size limit; zero means there is no limit.
+	 * @see SSLSessionContext#setSessionCacheSize(int size)
+	 */
+	public void setClientSessionCacheSize(int size) {
+		this.clientSessionCacheSize = size;
+	}
+
+	/**
+	 * Set the SSLSession timeout limit for the {@link SSLSessionContext} for use in
+	 * client mode.
+	 *
+	 * @param seconds the new session timeout limit in seconds; zero means there is
+	 *                no limit.
+	 * @see SSLSessionContext#setSessionTimeout(int seconds)
+	 */
+	public void setClientSessionTimeout(int seconds) {
+		this.clientSessionTimeout = seconds;
+	}
+
+	/**
+	 * Sets the SSLSession cache size for the {@link SSLSessionContext} for use in
+	 * server mode.
+	 *
+	 * @param serverSessionCacheSize the new session cache size limit; zero means
+	 *                               there is no limit.
+	 * @see SSLSessionContext#setSessionCacheSize(int)
+	 */
+	public void setServerSessionCacheSize(int serverSessionCacheSize) {
+		this.serverSessionCacheSize = serverSessionCacheSize;
+	}
+
+	/**
+	 * Set the SSLSession timeout limit for the {@link SSLSessionContext} for use in
+	 * server mode.
+	 *
+	 * @param serverSessionTimeout the new session timeout limit in seconds; zero
+	 *                             means there is no limit.
+	 * @see SSLSessionContext#setSessionTimeout(int)
+	 */
+	public void setServerSessionTimeout(int serverSessionTimeout) {
+		this.serverSessionTimeout = serverSessionTimeout;
+	}
 }
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslEvent.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslEvent.java
index 060d313..e1c497d 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslEvent.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslEvent.java
@@ -22,11 +22,11 @@ package org.apache.mina.filter.ssl;
 import org.apache.mina.filter.FilterEvent;
 
 /**
- * A SSL event sent by {@link SslFilter} when the session is secured or not secured.
+ * A SSL event sent by {@link SslFilter} when the session is secured or not
+ * secured.
  * 
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */
 public enum SslEvent implements FilterEvent {
-    SECURED,
-    UNSECURED
+	SECURED, UNSECURED
 }
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslFilter.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslFilter.java
index 45d124e..a55995a 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslFilter.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslFilter.java
@@ -49,37 +49,37 @@ import org.slf4j.LoggerFactory;
 
 /**
  * An SSL filter that encrypts and decrypts the data exchanged in the session.
- * Adding this filter triggers SSL handshake procedure immediately by sending
- * a SSL 'hello' message, so you don't need to call
- * {@link #startSsl(IoSession)} manually unless you are implementing StartTLS
- * (see below).  If you don't want the handshake procedure to start
- * immediately, please specify {@code false} as {@code autoStart} parameter in
- * the constructor.
+ * Adding this filter triggers SSL handshake procedure immediately by sending a
+ * SSL 'hello' message, so you don't need to call {@link #startSsl(IoSession)}
+ * manually unless you are implementing StartTLS (see below). If you don't want
+ * the handshake procedure to start immediately, please specify {@code false} as
+ * {@code autoStart} parameter in the constructor.
  * <p>
- * This filter uses an {@link SSLEngine} which was introduced in Java 5, so
- * Java version 5 or above is mandatory to use this filter. And please note that
- * this filter only works for TCP/IP connections.
+ * This filter uses an {@link SSLEngine} which was introduced in Java 5, so Java
+ * version 5 or above is mandatory to use this filter. And please note that this
+ * filter only works for TCP/IP connections.
  *
  * <h2>Implementing StartTLS</h2>
  * <p>
  * You can use {@link #DISABLE_ENCRYPTION_ONCE} attribute to implement StartTLS:
+ * 
  * <pre>
  * public void messageReceived(IoSession session, Object message) {
- *    if (message instanceof MyStartTLSRequest) {
- *        // Insert SSLFilter to get ready for handshaking
- *        session.getFilterChain().addFirst(sslFilter);
+ * 	if (message instanceof MyStartTLSRequest) {
+ * 		// Insert SSLFilter to get ready for handshaking
+ * 		session.getFilterChain().addFirst(sslFilter);
  *
- *        // Disable encryption temporarily.
- *        // This attribute will be removed by SSLFilter
- *        // inside the Session.write() call below.
- *        session.setAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE);
+ * 		// Disable encryption temporarily.
+ * 		// This attribute will be removed by SSLFilter
+ * 		// inside the Session.write() call below.
+ * 		session.setAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE);
  *
- *        // Write StartTLSResponse which won't be encrypted.
- *        session.write(new MyStartTLSResponse(OK));
+ * 		// Write StartTLSResponse which won't be encrypted.
+ * 		session.write(new MyStartTLSResponse(OK));
  *
- *        // Now DISABLE_ENCRYPTION_ONCE attribute is cleared.
- *        assert session.getAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE) == null;
- *    }
+ * 		// Now DISABLE_ENCRYPTION_ONCE attribute is cleared.
+ * 		assert session.getAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE) == null;
+ * 	}
  * }
  * </pre>
  *
@@ -87,812 +87,817 @@ import org.slf4j.LoggerFactory;
  * @org.apache.xbean.XBean
  */
 public class SslFilter extends IoFilterAdapter {
-    /** The logger */
-    private static final Logger LOGGER = LoggerFactory.getLogger(SslFilter.class);
-
-    /**
-     * A session attribute key that stores underlying {@link SSLSession}
-     * for each session.
-     */
-    public static final AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session");
-
-    /**
-     * A session attribute key that makes next one write request bypass
-     * this filter (not encrypting the data).  This is a marker attribute,
-     * which means that you can put whatever as its value. ({@link Boolean#TRUE}
-     * is preferred.)  The attribute is automatically removed from the session
-     * attribute map as soon as {@link IoSession#write(Object)} is invoked,
-     * and therefore should be put again if you want to make more messages
-     * bypass this filter.  This is especially useful when you implement
-     * StartTLS.
-     */
-    public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce");
-
-    /**
-     * A session attribute key that makes this filter to emit a
-     * {@link IoHandler#messageReceived(IoSession, Object)} event with a
-     * special message ({@link SslEvent#SECURED} or {@link SslEvent#UNSECURED}).
-     * This is a marker attribute, which means that you can put whatever as its
-     * value. ({@link Boolean#TRUE} is preferred.)  By default, this filter
-     * doesn't emit any events related with SSL session flow control.
-     */
-    public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification");
-
-    /**
-     * A session attribute key that should be set to an {@link InetSocketAddress}.
-     * Setting this attribute causes
-     * {@link SSLContext#createSSLEngine(String, int)} to be called passing the
-     * hostname and port of the {@link InetSocketAddress} to get an
-     * {@link SSLEngine} instance. If not set {@link SSLContext#createSSLEngine()}
-     * will be called.
-     * <br>
-     * Using this feature {@link SSLSession} objects may be cached and reused
-     * when in client mode.
-     *
-     * @see SSLContext#createSSLEngine(String, int)
-     */
-    public static final AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress");
-
-    /** An attribute containing the next filter */
-    private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter");
-
-    private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler");
-
-    /** The SslContext used */
-    /* No qualifier */final SSLContext sslContext;
-
-    /** A flag used to tell the filter to start the handshake immediately */
-    private final boolean autoStart;
-
-    /** A flag used to determinate if the handshake should start immediately */
-    public static final boolean START_HANDSHAKE = true;
-
-    /** A flag used to determinate if the handshake should wait for the client to initiate the handshake */
-    public static final boolean CLIENT_HANDSHAKE = false;
-
-    private boolean client;
-
-    private boolean needClientAuth;
-
-    private boolean wantClientAuth;
-
-    private String[] enabledCipherSuites;
-
-    private String[] enabledProtocols;
-
-    /**
-     * Creates a new SSL filter using the specified {@link SSLContext}.
-     * The handshake will start immediately after the filter has been added
-     * to the chain.
-     * 
-     * @param sslContext The SSLContext to use
-     */
-    public SslFilter(SSLContext sslContext) {
-        this(sslContext, START_HANDSHAKE);
-    }
-
-    /**
-     * Creates a new SSL filter using the specified {@link SSLContext}.
-     * If the <tt>autostart</tt> flag is set to <tt>true</tt>, the
-     * handshake will start immediately after the filter has been added
-     * to the chain.
-     * 
-     * @param sslContext The SSLContext to use
-     * @param autoStart The flag used to tell the filter to start the handshake immediately
-     */
-    public SslFilter(SSLContext sslContext, boolean autoStart) {
-        if (sslContext == null) {
-            throw new IllegalArgumentException("sslContext");
-        }
-
-        this.sslContext = sslContext;
-        this.autoStart = autoStart;
-    }
-
-    /**
-     * Returns the underlying {@link SSLSession} for the specified session.
-     *
-     * @param session The current session 
-     * @return <tt>null</tt> if no {@link SSLSession} is initialized yet.
-     */
-    public SSLSession getSslSession(IoSession session) {
-        return (SSLSession) session.getAttribute(SSL_SESSION);
-    }
-
-    /**
-     * (Re)starts SSL session for the specified <tt>session</tt> if not started yet.
-     * Please note that SSL session is automatically started by default, and therefore
-     * you don't need to call this method unless you've used TLS closure.
-     *
-     * @param session The session that will be switched to SSL mode
-     * @return <tt>true</tt> if the SSL session has been started, <tt>false</tt> if already started.
-     * @throws SSLException if failed to start the SSL session
-     */
-    public boolean startSsl(IoSession session) throws SSLException {
-        SslHandler sslHandler = getSslSessionHandler(session);
-        boolean started;
-
-        try {
-            synchronized (sslHandler) {
-                if (sslHandler.isOutboundDone()) {
-                    NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
-                    sslHandler.destroy();
-                    sslHandler.init();
-                    sslHandler.handshake(nextFilter);
-                    started = true;
-                } else {
-                    started = false;
-                }
-                sslHandler.flushFilterWrite();
-            }
-            sslHandler.flushMessageReceived();
-        } catch (SSLException se) {
-            sslHandler.release();
-            throw se;
-        }
-
-        return started;
-    }
-
-    /**
-     * An extended toString() method for sessions. If the SSL handshake
-     * is not yet completed, we will print (ssl) in small caps. Once it's
-     * completed, we will use SSL capitalized.
-     */
-    /* no qualifier */String getSessionInfo(IoSession session) {
-        StringBuilder sb = new StringBuilder();
-
-        if (session.getService() instanceof IoAcceptor) {
-            sb.append("Session Server");
-
-        } else {
-            sb.append("Session Client");
-        }
-
-        sb.append('[').append(session.getId()).append(']');
-
-        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
-        if (sslHandler == null) {
-            sb.append("(no sslEngine)");
-        } else if (isSslStarted(session)) {
-            if (sslHandler.isHandshakeComplete()) {
-                sb.append("(SSL)");
-            } else {
-                sb.append("(ssl...)");
-            }
-        }
-
-        return sb.toString();
-    }
-
-    /**
-     * @return <tt>true</tt> if and only if the specified <tt>session</tt> is
-     * encrypted/decrypted over SSL/TLS currently. This method will start
-     * to return <tt>false</tt> after TLS <tt>close_notify</tt> message
-     * is sent and any messages written after then is not going to get encrypted.
-     * 
-     * @param session the session we want to check
-     */
-    public boolean isSslStarted(IoSession session) {
-        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
-        if (sslHandler == null) {
-            return false;
-        }
-
-        synchronized (sslHandler) {
-            return !sslHandler.isOutboundDone();
-        }
-    }
-
-    /**
-     * @return <tt>true</tt> if and only if the conditions for
-     * {@link #isSslStarted(IoSession)} are met, and the handhake has
-     * completed.
-     *
-     * @param session the session we want to check
-     */
-    public boolean isSecured(IoSession session) {
-        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
-        if (sslHandler == null) {
-            return false;
-        }
-
-        synchronized (sslHandler) {
-            return !sslHandler.isOutboundDone() && sslHandler.isHandshakeComplete();
-        }
-    }
-
-
-    /**
-     * Stops the SSL session by sending TLS <tt>close_notify</tt> message to
-     * initiate TLS closure.
-     *
-     * @param session the {@link IoSession} to initiate TLS closure
-     * @return The Future for the initiated closure
-     * @throws SSLException if failed to initiate TLS closure
-     */
-    public WriteFuture stopSsl(IoSession session) throws SSLException {
-        SslHandler sslHandler = getSslSessionHandler(session);
-        NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
-        WriteFuture future;
-
-        try {
-            synchronized (sslHandler) {
-                future = initiateClosure(nextFilter, session);
-                sslHandler.flushFilterWrite();
-            }
-        } catch (SSLException se) {
-            sslHandler.release();
-            throw se;
-        }
-
-        return future;
-    }
-
-    /**
-     * @return <tt>true</tt> if the engine is set to use client mode
-     * when handshaking.
-     */
-    public boolean isUseClientMode() {
-        return client;
-    }
-
-    /**
-     * Configures the engine to use client (or server) mode when handshaking.
-     * 
-     * @param clientMode <tt>true</tt> when we are in client mode, <tt>false</tt> when in server mode
-     */
-    public void setUseClientMode(boolean clientMode) {
-        this.client = clientMode;
-    }
-
-    /**
-     * @return <tt>true</tt> if the engine will <em>require</em> client authentication.
-     * This option is only useful to engines in the server mode.
-     */
-    public boolean isNeedClientAuth() {
-        return needClientAuth;
-    }
-
-    /**
-     * Configures the engine to <em>require</em> client authentication.
-     * This option is only useful for engines in the server mode.
-     * 
-     * @param needClientAuth A flag set when we need to authenticate the client
-     */
-    public void setNeedClientAuth(boolean needClientAuth) {
-        this.needClientAuth = needClientAuth;
-    }
-
-    /**
-     * @return <tt>true</tt> if the engine will <em>request</em> client authentication.
-     * This option is only useful to engines in the server mode.
-     */
-    public boolean isWantClientAuth() {
-        return wantClientAuth;
-    }
-
-    /**
-     * Configures the engine to <em>request</em> client authentication.
-     * This option is only useful for engines in the server mode.
-     * 
-     * @param wantClientAuth A flag set when we want to check the client authentication
-     */
-    public void setWantClientAuth(boolean wantClientAuth) {
-        this.wantClientAuth = wantClientAuth;
-    }
-
-    /**
-     * @return the list of cipher suites to be enabled when {@link SSLEngine}
-     * is initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.'
-     */
-    public String[] getEnabledCipherSuites() {
-        return enabledCipherSuites;
-    }
-
-    /**
-     * Sets the list of cipher suites to be enabled when {@link SSLEngine}
-     * is initialized.
-     *
-     * @param cipherSuites <tt>null</tt> means 'use {@link SSLEngine}'s default.'
-     */
-    public void setEnabledCipherSuites(String[] cipherSuites) {
-        this.enabledCipherSuites = cipherSuites;
-    }
-
-    /**
-     * @return the list of protocols to be enabled when {@link SSLEngine}
-     * is initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.'
-     */
-    public String[] getEnabledProtocols() {
-        return enabledProtocols;
-    }
-
-    /**
-     * Sets the list of protocols to be enabled when {@link SSLEngine}
-     * is initialized.
-     *
-     * @param protocols <tt>null</tt> means 'use {@link SSLEngine}'s default.'
-     */
-    public void setEnabledProtocols(String[] protocols) {
-        this.enabledProtocols = protocols;
-    }
-
-    /**
-     * Executed just before the filter is added into the chain, we do :
-     * <ul>
-     * <li>check that we don't have a SSL filter already present
-     * <li>we update the next filter
-     * <li>we create the SSL handler helper class
-     * <li>and we store it into the session's Attributes
-     * </ul>
-     */
-    @Override
-    public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
-        // Check that we don't have a SSL filter already present in the chain
-        if (parent.contains(SslFilter.class)) {
-            String msg = "Only one SSL filter is permitted in a chain.";
-            LOGGER.error(msg);
-            throw new IllegalStateException(msg);
-        }
-
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("Adding the SSL Filter {} to the chain", name);
-        }
-
-        IoSession session = parent.getSession();
-        session.setAttribute(NEXT_FILTER, nextFilter);
-
-        // Create a SSL handler and start handshake.
-        SslHandler sslHandler = new SslHandler(this, session);
-        
-        // Adding the supported ciphers in the SSLHandler
-        if ((enabledCipherSuites == null) || (enabledCipherSuites.length == 0)) {
-            enabledCipherSuites = sslContext.getServerSocketFactory().getSupportedCipherSuites();
-        }
-
-        sslHandler.init();
-
-        session.setAttribute(SSL_HANDLER, sslHandler);
-    }
-
-    @Override
-    public void onPostAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
-        if (autoStart == START_HANDSHAKE) {
-            initiateHandshake(nextFilter, parent.getSession());
-        }
-    }
-
-    @Override
-    public void onPreRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
-        IoSession session = parent.getSession();
-        stopSsl(session);
-        session.removeAttribute(NEXT_FILTER);
-        session.removeAttribute(SSL_HANDLER);
-    }
-
-    // IoFilter impl.
-    @Override
-    public void sessionClosed(NextFilter nextFilter, IoSession session) throws SSLException {
-        SslHandler sslHandler = getSslSessionHandler(session);
-
-        try {
-            synchronized (sslHandler) {
-                // release resources
-                sslHandler.destroy();
-            }
-        } finally {
-            // notify closed session
-            nextFilter.sessionClosed(session);
-        }
-    }
-
-    @Override
-    public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws SSLException {
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message);
-        }
-
-        SslHandler sslHandler = getSslSessionHandler(session);
-
-	synchronized (sslHandler) {
-	    if (!isSslStarted(session) && sslHandler.isInboundDone()) {
-		// The SSL session must be established first before we
-		// can push data to the application. Store the incoming
-		// data into a queue for a later processing
-		sslHandler.scheduleMessageReceived(nextFilter, message);
-	    } else {
-		IoBuffer buf = (IoBuffer) message;
+	/** The logger */
+	private static final Logger LOGGER = LoggerFactory.getLogger(SslFilter.class);
+
+	/**
+	 * A session attribute key that stores underlying {@link SSLSession} for each
+	 * session.
+	 */
+	public static final AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session");
+
+	/**
+	 * A session attribute key that makes next one write request bypass this filter
+	 * (not encrypting the data). This is a marker attribute, which means that you
+	 * can put whatever as its value. ({@link Boolean#TRUE} is preferred.) The
+	 * attribute is automatically removed from the session attribute map as soon as
+	 * {@link IoSession#write(Object)} is invoked, and therefore should be put again
+	 * if you want to make more messages bypass this filter. This is especially
+	 * useful when you implement StartTLS.
+	 */
+	public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce");
+
+	/**
+	 * A session attribute key that makes this filter to emit a
+	 * {@link IoHandler#messageReceived(IoSession, Object)} event with a special
+	 * message ({@link SslEvent#SECURED} or {@link SslEvent#UNSECURED}). This is a
+	 * marker attribute, which means that you can put whatever as its value.
+	 * ({@link Boolean#TRUE} is preferred.) By default, this filter doesn't emit any
+	 * events related with SSL session flow control.
+	 */
+	public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification");
+
+	/**
+	 * A session attribute key that should be set to an {@link InetSocketAddress}.
+	 * Setting this attribute causes {@link SSLContext#createSSLEngine(String, int)}
+	 * to be called passing the hostname and port of the {@link InetSocketAddress}
+	 * to get an {@link SSLEngine} instance. If not set
+	 * {@link SSLContext#createSSLEngine()} will be called. <br>
+	 * Using this feature {@link SSLSession} objects may be cached and reused when
+	 * in client mode.
+	 *
+	 * @see SSLContext#createSSLEngine(String, int)
+	 */
+	public static final AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress");
+
+	/** An attribute containing the next filter */
+	private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter");
+
+	private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler");
+
+	/** The SslContext used */
+	/* No qualifier */final SSLContext sslContext;
+
+	/** A flag used to tell the filter to start the handshake immediately */
+	private final boolean autoStart;
+
+	/** A flag used to determinate if the handshake should start immediately */
+	public static final boolean START_HANDSHAKE = true;
+
+	/**
+	 * A flag used to determinate if the handshake should wait for the client to
+	 * initiate the handshake
+	 */
+	public static final boolean CLIENT_HANDSHAKE = false;
+
+	private boolean client;
+
+	private boolean needClientAuth;
+
+	private boolean wantClientAuth;
+
+	private String[] enabledCipherSuites;
+
+	private String[] enabledProtocols;
+
+	/**
+	 * Creates a new SSL filter using the specified {@link SSLContext}. The
+	 * handshake will start immediately after the filter has been added to the
+	 * chain.
+	 * 
+	 * @param sslContext The SSLContext to use
+	 */
+	public SslFilter(SSLContext sslContext) {
+		this(sslContext, START_HANDSHAKE);
+	}
+
+	/**
+	 * Creates a new SSL filter using the specified {@link SSLContext}. If the
+	 * <tt>autostart</tt> flag is set to <tt>true</tt>, the handshake will start
+	 * immediately after the filter has been added to the chain.
+	 * 
+	 * @param sslContext The SSLContext to use
+	 * @param autoStart  The flag used to tell the filter to start the handshake
+	 *                   immediately
+	 */
+	public SslFilter(SSLContext sslContext, boolean autoStart) {
+		if (sslContext == null) {
+			throw new IllegalArgumentException("sslContext");
+		}
+
+		this.sslContext = sslContext;
+		this.autoStart = autoStart;
+	}
+
+	/**
+	 * Returns the underlying {@link SSLSession} for the specified session.
+	 *
+	 * @param session The current session
+	 * @return <tt>null</tt> if no {@link SSLSession} is initialized yet.
+	 */
+	public SSLSession getSslSession(IoSession session) {
+		return (SSLSession) session.getAttribute(SSL_SESSION);
+	}
+
+	/**
+	 * (Re)starts SSL session for the specified <tt>session</tt> if not started yet.
+	 * Please note that SSL session is automatically started by default, and
+	 * therefore you don't need to call this method unless you've used TLS closure.
+	 *
+	 * @param session The session that will be switched to SSL mode
+	 * @return <tt>true</tt> if the SSL session has been started, <tt>false</tt> if
+	 *         already started.
+	 * @throws SSLException if failed to start the SSL session
+	 */
+	public boolean startSsl(IoSession session) throws SSLException {
+		SslHandler sslHandler = getSslSessionHandler(session);
+		boolean started;
+
+		try {
+			synchronized (sslHandler) {
+				if (sslHandler.isOutboundDone()) {
+					NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
+					sslHandler.destroy();
+					sslHandler.init();
+					sslHandler.handshake(nextFilter);
+					started = true;
+				} else {
+					started = false;
+				}
+				sslHandler.flushFilterWrite();
+			}
+			sslHandler.flushMessageReceived();
+		} catch (SSLException se) {
+			sslHandler.release();
+			throw se;
+		}
+
+		return started;
+	}
+
+	/**
+	 * An extended toString() method for sessions. If the SSL handshake is not yet
+	 * completed, we will print (ssl) in small caps. Once it's completed, we will
+	 * use SSL capitalized.
+	 */
+	/* no qualifier */String getSessionInfo(IoSession session) {
+		StringBuilder sb = new StringBuilder();
+
+		if (session.getService() instanceof IoAcceptor) {
+			sb.append("Session Server");
+
+		} else {
+			sb.append("Session Client");
+		}
+
+		sb.append('[').append(session.getId()).append(']');
+
+		SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+		if (sslHandler == null) {
+			sb.append("(no sslEngine)");
+		} else if (isSslStarted(session)) {
+			if (sslHandler.isHandshakeComplete()) {
+				sb.append("(SSL)");
+			} else {
+				sb.append("(ssl...)");
+			}
+		}
+
+		return sb.toString();
+	}
+
+	/**
+	 * @return <tt>true</tt> if and only if the specified <tt>session</tt> is
+	 *         encrypted/decrypted over SSL/TLS currently. This method will start to
+	 *         return <tt>false</tt> after TLS <tt>close_notify</tt> message is sent
+	 *         and any messages written after then is not going to get encrypted.
+	 * 
+	 * @param session the session we want to check
+	 */
+	public boolean isSslStarted(IoSession session) {
+		SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+		if (sslHandler == null) {
+			return false;
+		}
+
+		synchronized (sslHandler) {
+			return !sslHandler.isOutboundDone();
+		}
+	}
+
+	/**
+	 * @return <tt>true</tt> if and only if the conditions for
+	 *         {@link #isSslStarted(IoSession)} are met, and the handhake has
+	 *         completed.
+	 *
+	 * @param session the session we want to check
+	 */
+	public boolean isSecured(IoSession session) {
+		SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+		if (sslHandler == null) {
+			return false;
+		}
+
+		synchronized (sslHandler) {
+			return !sslHandler.isOutboundDone() && sslHandler.isHandshakeComplete();
+		}
+	}
+
+	/**
+	 * Stops the SSL session by sending TLS <tt>close_notify</tt> message to
+	 * initiate TLS closure.
+	 *
+	 * @param session the {@link IoSession} to initiate TLS closure
+	 * @return The Future for the initiated closure
+	 * @throws SSLException if failed to initiate TLS closure
+	 */
+	public WriteFuture stopSsl(IoSession session) throws SSLException {
+		SslHandler sslHandler = getSslSessionHandler(session);
+		NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
+		WriteFuture future;
+
+		try {
+			synchronized (sslHandler) {
+				future = initiateClosure(nextFilter, session);
+				sslHandler.flushFilterWrite();
+			}
+		} catch (SSLException se) {
+			sslHandler.release();
+			throw se;
+		}
+
+		return future;
+	}
+
+	/**
+	 * @return <tt>true</tt> if the engine is set to use client mode when
+	 *         handshaking.
+	 */
+	public boolean isUseClientMode() {
+		return client;
+	}
+
+	/**
+	 * Configures the engine to use client (or server) mode when handshaking.
+	 * 
+	 * @param clientMode <tt>true</tt> when we are in client mode, <tt>false</tt>
+	 *                   when in server mode
+	 */
+	public void setUseClientMode(boolean clientMode) {
+		this.client = clientMode;
+	}
+
+	/**
+	 * @return <tt>true</tt> if the engine will <em>require</em> client
+	 *         authentication. This option is only useful to engines in the server
+	 *         mode.
+	 */
+	public boolean isNeedClientAuth() {
+		return needClientAuth;
+	}
+
+	/**
+	 * Configures the engine to <em>require</em> client authentication. This option
+	 * is only useful for engines in the server mode.
+	 * 
+	 * @param needClientAuth A flag set when we need to authenticate the client
+	 */
+	public void setNeedClientAuth(boolean needClientAuth) {
+		this.needClientAuth = needClientAuth;
+	}
+
+	/**
+	 * @return <tt>true</tt> if the engine will <em>request</em> client
+	 *         authentication. This option is only useful to engines in the server
+	 *         mode.
+	 */
+	public boolean isWantClientAuth() {
+		return wantClientAuth;
+	}
+
+	/**
+	 * Configures the engine to <em>request</em> client authentication. This option
+	 * is only useful for engines in the server mode.
+	 * 
+	 * @param wantClientAuth A flag set when we want to check the client
+	 *                       authentication
+	 */
+	public void setWantClientAuth(boolean wantClientAuth) {
+		this.wantClientAuth = wantClientAuth;
+	}
+
+	/**
+	 * @return the list of cipher suites to be enabled when {@link SSLEngine} is
+	 *         initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.'
+	 */
+	public String[] getEnabledCipherSuites() {
+		return enabledCipherSuites;
+	}
+
+	/**
+	 * Sets the list of cipher suites to be enabled when {@link SSLEngine} is
+	 * initialized.
+	 *
+	 * @param cipherSuites <tt>null</tt> means 'use {@link SSLEngine}'s default.'
+	 */
+	public void setEnabledCipherSuites(String[] cipherSuites) {
+		this.enabledCipherSuites = cipherSuites;
+	}
+
+	/**
+	 * @return the list of protocols to be enabled when {@link SSLEngine} is
+	 *         initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.'
+	 */
+	public String[] getEnabledProtocols() {
+		return enabledProtocols;
+	}
+
+	/**
+	 * Sets the list of protocols to be enabled when {@link SSLEngine} is
+	 * initialized.
+	 *
+	 * @param protocols <tt>null</tt> means 'use {@link SSLEngine}'s default.'
+	 */
+	public void setEnabledProtocols(String[] protocols) {
+		this.enabledProtocols = protocols;
+	}
+
+	/**
+	 * Executed just before the filter is added into the chain, we do :
+	 * <ul>
+	 * <li>check that we don't have a SSL filter already present
+	 * <li>we update the next filter
+	 * <li>we create the SSL handler helper class
+	 * <li>and we store it into the session's Attributes
+	 * </ul>
+	 */
+	@Override
+	public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
+		// Check that we don't have a SSL filter already present in the chain
+		if (parent.contains(SslFilter.class)) {
+			String msg = "Only one SSL filter is permitted in a chain.";
+			LOGGER.error(msg);
+			throw new IllegalStateException(msg);
+		}
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("Adding the SSL Filter {} to the chain", name);
+		}
+
+		IoSession session = parent.getSession();
+		session.setAttribute(NEXT_FILTER, nextFilter);
+
+		// Create a SSL handler and start handshake.
+		SslHandler sslHandler = new SslHandler(this, session);
+
+		// Adding the supported ciphers in the SSLHandler
+		if ((enabledCipherSuites == null) || (enabledCipherSuites.length == 0)) {
+			enabledCipherSuites = sslContext.getServerSocketFactory().getSupportedCipherSuites();
+		}
+
+		sslHandler.init();
+
+		session.setAttribute(SSL_HANDLER, sslHandler);
+	}
+
+	@Override
+	public void onPostAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
+		if (autoStart == START_HANDSHAKE) {
+			initiateHandshake(nextFilter, parent.getSession());
+		}
+	}
+
+	@Override
+	public void onPreRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
+		IoSession session = parent.getSession();
+		stopSsl(session);
+		session.removeAttribute(NEXT_FILTER);
+		session.removeAttribute(SSL_HANDLER);
+	}
+
+	// IoFilter impl.
+	@Override
+	public void sessionClosed(NextFilter nextFilter, IoSession session) throws SSLException {
+		SslHandler sslHandler = getSslSessionHandler(session);
 
 		try {
-		    if (sslHandler.isOutboundDone()) {
-			sslHandler.destroy();
-			throw new SSLException("Outbound done");
-		    }
+			synchronized (sslHandler) {
+				// release resources
+				sslHandler.destroy();
+			}
+		} finally {
+			// notify closed session
+			nextFilter.sessionClosed(session);
+		}
+	}
 
-		    // forward read encrypted data to SSL handler
-		    sslHandler.messageReceived(nextFilter, buf.buf());
+	@Override
+	public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws SSLException {
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message);
+		}
 
-		    // Handle data to be forwarded to application or written to net
-		    handleSslData(nextFilter, sslHandler);
+		SslHandler sslHandler = getSslSessionHandler(session);
 
-		    if (sslHandler.isInboundDone()) {
-			if (sslHandler.isOutboundDone()) {
-			    sslHandler.destroy();
+		synchronized (sslHandler) {
+			if (!isSslStarted(session) && sslHandler.isInboundDone()) {
+				// The SSL session must be established first before we
+				// can push data to the application. Store the incoming
+				// data into a queue for a later processing
+				sslHandler.scheduleMessageReceived(nextFilter, message);
 			} else {
-			    initiateClosure(nextFilter, session);
+				IoBuffer buf = (IoBuffer) message;
+
+				try {
+					if (sslHandler.isOutboundDone()) {
+						sslHandler.destroy();
+						throw new SSLException("Outbound done");
+					}
+
+					// forward read encrypted data to SSL handler
+					sslHandler.messageReceived(nextFilter, buf.buf());
+
+					// Handle data to be forwarded to application or written to net
+					handleSslData(nextFilter, sslHandler);
+
+					if (sslHandler.isInboundDone()) {
+						if (sslHandler.isOutboundDone()) {
+							sslHandler.destroy();
+						} else {
+							initiateClosure(nextFilter, session);
+						}
+
+						if (buf.hasRemaining()) {
+							// Forward the data received after closure.
+							sslHandler.scheduleMessageReceived(nextFilter, buf);
+						}
+					}
+				} catch (SSLException ssle) {
+					if (!sslHandler.isHandshakeComplete()) {
+						SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
+						newSsle.initCause(ssle);
+						ssle = newSsle;
+
+						// Close the session immediately, the handshake has failed
+						session.closeNow();
+					} else {
+						// Free the SSL Handler buffers
+						sslHandler.release();
+					}
+
+					throw ssle;
+				}
+			}
+		}
+
+		sslHandler.flushMessageReceived();
+	}
+
+	@Override
+	public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) {
+		if (writeRequest instanceof EncryptedWriteRequest) {
+			EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
+			nextFilter.messageSent(session, wrappedRequest.getParentRequest());
+		} else {
+			// ignore extra buffers used for handshaking
+		}
+	}
+
+	@Override
+	public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
+
+		if (cause instanceof WriteToClosedSessionException) {
+			// Filter out SSL close notify, which is likely to fail to flush
+			// due to disconnection.
+			WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
+			List<WriteRequest> failedRequests = e.getRequests();
+			boolean containsCloseNotify = false;
+
+			for (WriteRequest r : failedRequests) {
+				if (isCloseNotify(r.getMessage())) {
+					containsCloseNotify = true;
+					break;
+				}
+			}
+
+			if (containsCloseNotify) {
+				if (failedRequests.size() == 1) {
+					// close notify is the only failed request; bail out.
+					return;
+				}
+
+				List<WriteRequest> newFailedRequests = new ArrayList<>(failedRequests.size() - 1);
+
+				for (WriteRequest r : failedRequests) {
+					if (!isCloseNotify(r.getMessage())) {
+						newFailedRequests.add(r);
+					}
+				}
+
+				if (newFailedRequests.isEmpty()) {
+					// the failedRequests were full with close notify; bail out.
+					return;
+				}
+
+				cause = new WriteToClosedSessionException(newFailedRequests, cause.getMessage(), cause.getCause());
+			}
+		}
+
+		nextFilter.exceptionCaught(session, cause);
+	}
+
+	private boolean isCloseNotify(Object message) {
+		if (!(message instanceof IoBuffer)) {
+			return false;
+		}
+
+		IoBuffer buf = (IoBuffer) message;
+		int offset = buf.position();
+
+		return (buf.get(offset + 0) == 0x15) /* Alert */
+				&& (buf.get(offset + 1) == 0x03) /* TLS/SSL */
+				&& ((buf.get(offset + 2) == 0x00) /* SSL 3.0 */
+						|| (buf.get(offset + 2) == 0x01) /* TLS 1.0 */
+						|| (buf.get(offset + 2) == 0x02) /* TLS 1.1 */
+						|| (buf.get(offset + 2) == 0x03)) /* TLS 1.2 */
+				&& (buf.get(offset + 3) == 0x00); /* close_notify */
+	}
+
+	@Override
+	public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws SSLException {
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{}: Writing Message : {}", getSessionInfo(session), writeRequest);
+		}
+
+		boolean needsFlush = true;
+		SslHandler sslHandler = getSslSessionHandler(session);
+
+		try {
+			synchronized (sslHandler) {
+				if (!isSslStarted(session)) {
+					sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
+				}
+				// Don't encrypt the data if encryption is disabled.
+				else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
+					// Remove the marker attribute because it is temporary.
+					session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
+					sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
+				} else {
+					// Otherwise, encrypt the buffer.
+					IoBuffer buf = (IoBuffer) writeRequest.getMessage();
+
+					if (sslHandler.isWritingEncryptedData()) {
+						// data already encrypted; simply return buffer
+						sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
+					} else if (sslHandler.isHandshakeComplete()) {
+						// SSL encrypt
+						sslHandler.encrypt(buf.buf());
+						IoBuffer encryptedBuffer = sslHandler.fetchOutNetBuffer();
+						writeRequest.setMessage(encryptedBuffer);
+						sslHandler.scheduleFilterWrite(nextFilter,
+								new EncryptedWriteRequest(writeRequest, encryptedBuffer));
+					} else {
+						if (session.isConnected()) {
+							// Handshake not complete yet.
+							sslHandler.schedulePreHandshakeWriteRequest(nextFilter, writeRequest);
+						}
+
+						needsFlush = false;
+					}
+				}
+				if (needsFlush) {
+					sslHandler.flushFilterWrite();
+				}
+			}
+		} catch (SSLException se) {
+			sslHandler.release();
+			throw se;
+		}
+	}
+
+	@Override
+	public void filterClose(final NextFilter nextFilter, final IoSession session) throws SSLException {
+		SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+		if (sslHandler == null) {
+			// The connection might already have closed, or
+			// SSL might have not started yet.
+			nextFilter.filterClose(session);
+			return;
+		}
+
+		WriteFuture future = null;
+
+		try {
+			synchronized (sslHandler) {
+				if (isSslStarted(session)) {
+					future = initiateClosure(nextFilter, session);
+					future.addListener(new IoFutureListener<IoFuture>() {
+						@Override
+						public void operationComplete(IoFuture future) {
+							nextFilter.filterClose(session);
+						}
+					});
+				}
+				sslHandler.flushFilterWrite();
+			}
+		} catch (SSLException se) {
+			sslHandler.release();
+			throw se;
+		} finally {
+			if (future == null) {
+				nextFilter.filterClose(session);
+			}
+		}
+	}
+
+	/**
+	 * Initiate the SSL handshake. This can be invoked if you have set the
+	 * 'autoStart' to false when creating the SslFilter instance.
+	 * 
+	 * @param session The session for which the SSL handshake should be done
+	 * @throws SSLException If the handshake failed
+	 */
+	public void initiateHandshake(IoSession session) throws SSLException {
+		IoFilterChain filterChain = session.getFilterChain();
+
+		if (filterChain == null) {
+			throw new SSLException("No filter chain");
+		}
+
+		IoFilter.NextFilter nextFilter = filterChain.getNextFilter(SslFilter.class);
+
+		if (nextFilter == null) {
+			throw new SSLException("No SSL next filter in the chain");
+		}
+
+		initiateHandshake(nextFilter, session);
+	}
+
+	private void initiateHandshake(NextFilter nextFilter, IoSession session) throws SSLException {
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session));
+		}
+
+		SslHandler sslHandler = getSslSessionHandler(session);
+
+		try {
+			synchronized (sslHandler) {
+				sslHandler.handshake(nextFilter);
+				sslHandler.flushFilterWrite();
 			}
+			sslHandler.flushMessageReceived();
+		} catch (SSLException se) {
+			sslHandler.release();
+			throw se;
+		}
+	}
+
+	private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session) throws SSLException {
+		SslHandler sslHandler = getSslSessionHandler(session);
+		WriteFuture future = null;
 
-			if (buf.hasRemaining()) {
-			    // Forward the data received after closure.
-			    sslHandler.scheduleMessageReceived(nextFilter, buf);
+		// if already shut down
+		try {
+			synchronized (sslHandler) {
+				if (!sslHandler.closeOutbound()) {
+					return DefaultWriteFuture.newNotWrittenFuture(session,
+							new IllegalStateException("SSL session is shut down already."));
+				}
+
+				// there might be data to write out here?
+				future = sslHandler.writeNetBuffer(nextFilter);
+
+				if (future == null) {
+					future = DefaultWriteFuture.newWrittenFuture(session);
+				}
+
+				if (sslHandler.isInboundDone()) {
+					sslHandler.destroy();
+				}
 			}
-		    }
-		} catch (SSLException ssle) {
-		    if (!sslHandler.isHandshakeComplete()) {
-			SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
-			newSsle.initCause(ssle);
-			ssle = newSsle;
-
-			// Close the session immediately, the handshake has failed
-			session.closeNow();
-		    } else {
-			// Free the SSL Handler buffers
+
+			// Inform that the session is not any more secured
+			session.getFilterChain().fireEvent(SslEvent.UNSECURED);
+		} catch (SSLException se) {
 			sslHandler.release();
-		    }
-
-		    throw ssle;
-		}
-	    }
-	}
-	
-	sslHandler.flushMessageReceived();
-    }
-
-    @Override
-    public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) {
-        if (writeRequest instanceof EncryptedWriteRequest) {
-            EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
-            nextFilter.messageSent(session, wrappedRequest.getParentRequest());
-        } else {
-            // ignore extra buffers used for handshaking
-        }
-    }
-
-    @Override
-    public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
-
-        if (cause instanceof WriteToClosedSessionException) {
-            // Filter out SSL close notify, which is likely to fail to flush
-            // due to disconnection.
-            WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
-            List<WriteRequest> failedRequests = e.getRequests();
-            boolean containsCloseNotify = false;
-
-            for (WriteRequest r : failedRequests) {
-                if (isCloseNotify(r.getMessage())) {
-                    containsCloseNotify = true;
-                    break;
-                }
-            }
-
-            if (containsCloseNotify) {
-                if (failedRequests.size() == 1) {
-                    // close notify is the only failed request; bail out.
-                    return;
-                }
-
-                List<WriteRequest> newFailedRequests = new ArrayList<>(failedRequests.size() - 1);
-
-                for (WriteRequest r : failedRequests) {
-                    if (!isCloseNotify(r.getMessage())) {
-                        newFailedRequests.add(r);
-                    }
-                }
-
-                if (newFailedRequests.isEmpty()) {
-                    // the failedRequests were full with close notify; bail out.
-                    return;
-                }
-
-                cause = new WriteToClosedSessionException(newFailedRequests, cause.getMessage(), cause.getCause());
-            }
-        }
-
-        nextFilter.exceptionCaught(session, cause);
-    }
-
-    private boolean isCloseNotify(Object message) {
-        if (!(message instanceof IoBuffer)) {
-            return false;
-        }
-
-        IoBuffer buf = (IoBuffer) message;
-        int offset = buf.position();
-
-        return (buf.get(offset + 0) == 0x15) /* Alert */
-                && (buf.get(offset + 1) == 0x03) /* TLS/SSL */
-                && ((buf.get(offset + 2) == 0x00) /* SSL 3.0 */
-                        || (buf.get(offset + 2) == 0x01) /* TLS 1.0 */
-                        || (buf.get(offset + 2) == 0x02) /* TLS 1.1 */
-                        || (buf.get(offset + 2) == 0x03)) /* TLS 1.2 */
-                        && (buf.get(offset + 3) == 0x00); /* close_notify */
-    }
-
-    @Override
-    public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws SSLException {
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{}: Writing Message : {}", getSessionInfo(session), writeRequest);
-        }
-
-        boolean needsFlush = true;
-        SslHandler sslHandler = getSslSessionHandler(session);
-
-        try {
-            synchronized (sslHandler) {
-                if (!isSslStarted(session)) {
-                    sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
-                }
-                // Don't encrypt the data if encryption is disabled.
-                else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
-                    // Remove the marker attribute because it is temporary.
-                    session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
-                    sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
-                } else {
-                    // Otherwise, encrypt the buffer.
-                    IoBuffer buf = (IoBuffer) writeRequest.getMessage();
-
-                    if (sslHandler.isWritingEncryptedData()) {
-                        // data already encrypted; simply return buffer
-                        sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
-                    } else if (sslHandler.isHandshakeComplete()) {
-                        // SSL encrypt
-                        sslHandler.encrypt(buf.buf());
-                        IoBuffer encryptedBuffer = sslHandler.fetchOutNetBuffer();
-                        writeRequest.setMessage( encryptedBuffer );
-                        sslHandler.scheduleFilterWrite(nextFilter, new EncryptedWriteRequest(writeRequest,
-                            encryptedBuffer));
-                    } else {
-                        if (session.isConnected()) {
-                            // Handshake not complete yet.
-                            sslHandler.schedulePreHandshakeWriteRequest(nextFilter, writeRequest);
-                        }
-
-                        needsFlush = false;
-                    }
-                }
-                if (needsFlush) {
-                    sslHandler.flushFilterWrite();
-                }
-            }
-        } catch (SSLException se) {
-            sslHandler.release();
-            throw se;
-        }
-    }
-
-    @Override
-    public void filterClose(final NextFilter nextFilter, final IoSession session) throws SSLException {
-        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
-        if (sslHandler == null) {
-            // The connection might already have closed, or
-            // SSL might have not started yet.
-            nextFilter.filterClose(session);
-            return;
-        }
-
-        WriteFuture future = null;
-
-        try {
-            synchronized (sslHandler) {
-                if (isSslStarted(session)) {
-                    future = initiateClosure(nextFilter, session);
-                    future.addListener(new IoFutureListener<IoFuture>() {
-                        @Override
-                        public void operationComplete(IoFuture future) {
-                            nextFilter.filterClose(session);
-                        }
-                    });
-                }
-                sslHandler.flushFilterWrite();
-            }
-        } catch (SSLException se) {
-            sslHandler.release();
-            throw se;
-        } finally {
-            if (future == null) {
-                nextFilter.filterClose(session);
-            }
-        }
-    }
-
-    /**
-     * Initiate the SSL handshake. This can be invoked if you have set the 'autoStart' to
-     * false when creating the SslFilter instance.
-     * 
-     * @param session The session for which the SSL handshake should be done
-     * @throws SSLException If the handshake failed
-     */
-    public void initiateHandshake(IoSession session) throws SSLException {
-        IoFilterChain filterChain = session.getFilterChain();
-        
-        if (filterChain == null) {
-            throw new SSLException("No filter chain");
-        }
-        
-        IoFilter.NextFilter nextFilter = filterChain.getNextFilter(SslFilter.class);
-        
-        if (nextFilter == null) {
-            throw new SSLException("No SSL next filter in the chain");
-        }
-        
-        initiateHandshake(nextFilter, session);
-    }
-
-    private void initiateHandshake(NextFilter nextFilter, IoSession session) throws SSLException {
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session));
-        }
-        
-        SslHandler sslHandler = getSslSessionHandler(session);
-
-        try {
-            synchronized (sslHandler) {
-                sslHandler.handshake(nextFilter);
-                sslHandler.flushFilterWrite();
-            }
-            sslHandler.flushMessageReceived();
-        } catch (SSLException se) {
-            sslHandler.release();
-            throw se;
-        }
-    }
-
-    private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session) throws SSLException {
-        SslHandler sslHandler = getSslSessionHandler(session);
-        WriteFuture future = null;
-
-        // if already shut down
-        try {
-            synchronized(sslHandler) {
-                if (!sslHandler.closeOutbound()) {
-                    return DefaultWriteFuture.newNotWrittenFuture(session, new IllegalStateException(
-                            "SSL session is shut down already."));
-                }
-    
-                // there might be data to write out here?
-                future = sslHandler.writeNetBuffer(nextFilter);
-    
-                if (future == null) {
-                    future = DefaultWriteFuture.newWrittenFuture(session);
-                }
-    
-                if (sslHandler.isInboundDone()) {
-                    sslHandler.destroy();
-                }
-            }
-
-            // Inform that the session is not any more secured
-            session.getFilterChain().fireEvent(SslEvent.UNSECURED);
-        } catch (SSLException se) {
-            sslHandler.release();
-            throw se;
-        }
-
-        return future;
-    }
-
-    // Utilities
-    private void handleSslData(NextFilter nextFilter, SslHandler sslHandler) throws SSLException {
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(sslHandler.getSession()));
-        }
-
-        // Flush any buffered write requests occurred before handshaking.
-        if (sslHandler.isHandshakeComplete()) {
-            sslHandler.flushPreHandshakeEvents();
-        }
-
-        // Write encrypted data to be written (if any)
-        sslHandler.writeNetBuffer(nextFilter);
-
-        // handle app. data read (if any)
-        handleAppDataRead(nextFilter, sslHandler);
-    }
-
-    private void handleAppDataRead(NextFilter nextFilter, SslHandler sslHandler) {
-        // forward read app data
-        IoBuffer readBuffer = sslHandler.fetchAppBuffer();
-
-        if (readBuffer.hasRemaining()) {
-            sslHandler.scheduleMessageReceived(nextFilter, readBuffer);
-        }
-    }
-
-    private SslHandler getSslSessionHandler(IoSession session) {
-        SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
-        if (sslHandler == null) {
-            throw new IllegalStateException();
-        }
-
-        synchronized(sslHandler) {
-            if (sslHandler.getSslFilter() != this) {
-                throw new IllegalArgumentException("Not managed by this filter.");
-            }
-        }
-
-        return sslHandler;
-    }
-
-    /**
-     * A message that is sent from {@link SslFilter} when the connection became
-     * secure or is not secure anymore.
-     *
-     * @author <a href="http://mina.apache.org">Apache MINA Project</a>
-     */
-    public static class SslFilterMessage {
-        private final String name;
-
-        private SslFilterMessage(String name) {
-            this.name = name;
-        }
-
-        @Override
-        public String toString() {
-            return name;
-        }
-    }
-    
-    /**
-     * A private class used to store encrypted messages. This is necessary
-     * to be able to emit the messageSent event with the proper original
-     * message, but not for handshake messages, which will be swallowed.
-     *
-     */
-    /* package protected */ static class EncryptedWriteRequest extends DefaultWriteRequest {
-        // Thee encrypted messagee
-        private final IoBuffer encryptedMessage;
-        
-        // The original message
-        private WriteRequest parentRequest;
-
-        /**
-         * Create a new instance of an EncryptedWriteRequest
-         * @param writeRequest The parent request
-         * @param encryptedMessage The encrypted message
-         */
-        private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) {
-            super(encryptedMessage);
-            parentRequest = writeRequest;
-            this.encryptedMessage = encryptedMessage;
-        }
-
-        /**
-         * @return teh encrypted message
-         */
-        @Override
-        public Object getMessage() {
-            return encryptedMessage;
-        }
-
-        /**
-         * @return The parent WriteRequest
-         */
-        public WriteRequest getParentRequest() {
-            return parentRequest;
-        }
-        
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public WriteFuture getFuture() {
-            return parentRequest.getFuture();
-        }
-    }
+			throw se;
+		}
+
+		return future;
+	}
+
+	// Utilities
+	private void handleSslData(NextFilter nextFilter, SslHandler sslHandler) throws SSLException {
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(sslHandler.getSession()));
+		}
+
+		// Flush any buffered write requests occurred before handshaking.
+		if (sslHandler.isHandshakeComplete()) {
+			sslHandler.flushPreHandshakeEvents();
+		}
+
+		// Write encrypted data to be written (if any)
+		sslHandler.writeNetBuffer(nextFilter);
+
+		// handle app. data read (if any)
+		handleAppDataRead(nextFilter, sslHandler);
+	}
+
+	private void handleAppDataRead(NextFilter nextFilter, SslHandler sslHandler) {
+		// forward read app data
+		IoBuffer readBuffer = sslHandler.fetchAppBuffer();
+
+		if (readBuffer.hasRemaining()) {
+			sslHandler.scheduleMessageReceived(nextFilter, readBuffer);
+		}
+	}
+
+	private SslHandler getSslSessionHandler(IoSession session) {
+		SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+		if (sslHandler == null) {
+			throw new IllegalStateException();
+		}
+
+		synchronized (sslHandler) {
+			if (sslHandler.getSslFilter() != this) {
+				throw new IllegalArgumentException("Not managed by this filter.");
+			}
+		}
+
+		return sslHandler;
+	}
+
+	/**
+	 * A message that is sent from {@link SslFilter} when the connection became
+	 * secure or is not secure anymore.
+	 *
+	 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+	 */
+	public static class SslFilterMessage {
+		private final String name;
+
+		private SslFilterMessage(String name) {
+			this.name = name;
+		}
+
+		@Override
+		public String toString() {
+			return name;
+		}
+	}
+
+	/**
+	 * A private class used to store encrypted messages. This is necessary to be
+	 * able to emit the messageSent event with the proper original message, but not
+	 * for handshake messages, which will be swallowed.
+	 *
+	 */
+	/* package protected */ static class EncryptedWriteRequest extends DefaultWriteRequest {
+		// Thee encrypted messagee
+		private final IoBuffer encryptedMessage;
+
+		// The original message
+		private WriteRequest parentRequest;
+
+		/**
+		 * Create a new instance of an EncryptedWriteRequest
+		 * 
+		 * @param writeRequest     The parent request
+		 * @param encryptedMessage The encrypted message
+		 */
+		private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) {
+			super(encryptedMessage);
+			parentRequest = writeRequest;
+			this.encryptedMessage = encryptedMessage;
+		}
+
+		/**
+		 * @return teh encrypted message
+		 */
+		@Override
+		public Object getMessage() {
+			return encryptedMessage;
+		}
+
+		/**
+		 * @return The parent WriteRequest
+		 */
+		public WriteRequest getParentRequest() {
+			return parentRequest;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		@Override
+		public WriteFuture getFuture() {
+			return parentRequest.getFuture();
+		}
+	}
 }
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java
index 71ace82..070ee18 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java
@@ -62,812 +62,813 @@ import org.slf4j.LoggerFactory;
  *
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */
-/** No qualifier*/
+/** No qualifier */
 class SslHandler {
-    /** A logger for this class */
-    private static final Logger LOGGER = LoggerFactory.getLogger(SslHandler.class);
-
-    /** The SSL Filter which has created this handler */
-    private final SslFilter sslFilter;
-
-    /** The current session */
-    private final IoSession session;
-
-    private final Queue<IoFilterEvent> preHandshakeEventQueue = new ConcurrentLinkedQueue<>();
-
-    private final Queue<IoFilterEvent> filterWriteEventQueue = new ConcurrentLinkedQueue<>();
-
-    /** A queue used to stack all the incoming data until the SSL session is established */
-    private final Queue<IoFilterEvent> messageReceivedEventQueue = new ConcurrentLinkedQueue<>();
-
-    private SSLEngine sslEngine;
-
-    /**
-     * Encrypted data from the net
-     */
-    private IoBuffer inNetBuffer;
-
-    /**
-     * Encrypted data to be written to the net
-     */
-    private IoBuffer outNetBuffer;
-
-    /**
-     * Application cleartext data to be read by application
-     */
-    private IoBuffer appBuffer;
-
-    /**
-     * Empty buffer used during initial handshake and close operations
-     */
-    private final IoBuffer emptyBuffer = IoBuffer.allocate(0);
-
-    private SSLEngineResult.HandshakeStatus handshakeStatus;
-
-    /**
-     * A flag set to true when the first SSL handshake has been completed
-     * This is used to avoid sending a notification to the application handler
-     * when we switch to a SECURE or UNSECURE session.
-     */
-    private boolean firstSSLNegociation;
-
-    /** A flag set to true when a SSL Handshake has been completed */
-    private boolean handshakeComplete;
-
-    /** A flag used to indicate to the SslFilter that the buffer
-     * it will write is already encrypted (this will be the case
-     * for data being produced during the handshake). */
-    private boolean writingEncryptedData;
-
-    /**
-     * Create a new SSL Handler, and initialize it.
-     *
-     * @param sslContext
-     * @throws SSLException
-     */
-    /* no qualifier */SslHandler(SslFilter sslFilter, IoSession session) {
-        this.sslFilter = sslFilter;
-        this.session = session;
-    }
-
-    /**
-     * Initialize the SSL handshake.
-     *
-     * @throws SSLException If the underlying SSLEngine handshake initialization failed
-     */
-    /* no qualifier */void init() throws SSLException {
-        if (sslEngine != null) {
-            // We already have a SSL engine created, no need to create a new one
-            return;
-        }
-
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{} Initializing the SSL Handler", sslFilter.getSessionInfo(session));
-        }
-
-        InetSocketAddress peer = (InetSocketAddress) session.getAttribute(SslFilter.PEER_ADDRESS);
-
-        // Create the SSL engine here
-        if (peer == null) {
-            sslEngine = sslFilter.sslContext.createSSLEngine();
-        } else {
-            sslEngine = sslFilter.sslContext.createSSLEngine(peer.getHostName(), peer.getPort());
-        }
-
-        // Initialize the engine in client mode if necessary
-        sslEngine.setUseClientMode(sslFilter.isUseClientMode());
-
-        // Initialize the different SslEngine modes
-        if (!sslEngine.getUseClientMode()) {
-            // Those parameters are only valid when in server mode
-            if (sslFilter.isWantClientAuth()) {
-                sslEngine.setWantClientAuth(true);
-            }
-
-            if (sslFilter.isNeedClientAuth()) {
-                sslEngine.setNeedClientAuth(true);
-            }
-        }
-
-        // Set the cipher suite to use by this SslEngine instance
-        if (sslFilter.getEnabledCipherSuites() != null) {
-            sslEngine.setEnabledCipherSuites(sslFilter.getEnabledCipherSuites());
-        }
-
-        // Set the list of enabled protocols
-        if (sslFilter.getEnabledProtocols() != null) {
-            sslEngine.setEnabledProtocols(sslFilter.getEnabledProtocols());
-        }
-
-        // TODO : we may not need to call this method...
-        // However, if we don't call it here, the tests are failing. Why?
-        handshakeStatus = sslEngine.getHandshakeStatus();
-
-        // Default value
-        writingEncryptedData = false;
-
-        // We haven't yet started a SSL negotiation
-        // set the flags accordingly
-        firstSSLNegociation = true;
-        handshakeComplete = false;
-
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{} SSL Handler Initialization done.", sslFilter.getSessionInfo(session));
-        }
-    }
-    
-
-    /**
-     * Release allocated buffers.
-     */
-    /* no qualifier */void destroy() {
-        if (sslEngine == null) {
-            return;
-        }
-
-        // Close inbound and flush all remaining data if available.
-        try {
-            sslEngine.closeInbound();
-        } catch (SSLException e) {
-            if (LOGGER.isDebugEnabled()) {
-                LOGGER.debug("Unexpected exception from SSLEngine.closeInbound().", e);
-            }
-        }
-
-        if (outNetBuffer != null) {
-            outNetBuffer.capacity(sslEngine.getSession().getPacketBufferSize());
-        } else {
-            createOutNetBuffer(0);
-        }
-        try {
-            do {
-                outNetBuffer.clear();
-            } while (sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf()).bytesProduced() > 0);
-        } catch (SSLException e) {
-            // Ignore.
-        } finally {
-            outNetBuffer.free();
-            outNetBuffer = null;
-        }
-
-        sslEngine.closeOutbound();
-        sslEngine = null;
-
-        preHandshakeEventQueue.clear();
-    }
-
-    /**
-     * @return The SSL filter which has created this handler
-     */
-    /* no qualifier */SslFilter getSslFilter() {
-        return sslFilter;
-    }
-
-    /* no qualifier */IoSession getSession() {
-        return session;
-    }
-
-    /**
-     * Check if we are writing encrypted data.
-     */
-    /* no qualifier */boolean isWritingEncryptedData() {
-        return writingEncryptedData;
-    }
-
-    /**
-     * Check if handshake is completed.
-     */
-    /* no qualifier */boolean isHandshakeComplete() {
-        return handshakeComplete;
-    }
-
-    /**
-     * Check if handshake is on going.
-     */
-    /* no qualifier */boolean notHandshaking() {
-        return handshakeStatus == HandshakeStatus.FINISHED || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING;
-    }
-
-    /* no qualifier */boolean isInboundDone() {
-        return sslEngine == null || sslEngine.isInboundDone();
-    }
-
-    /* no qualifier */boolean isOutboundDone() {
-        return sslEngine == null || sslEngine.isOutboundDone();
-    }
-
-    /**
-     * Check if there is any need to complete handshake.
-     */
-    /* no qualifier */boolean needToCompleteHandshake() {
-        return handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone();
-    }
-
-    /* no qualifier */void schedulePreHandshakeWriteRequest(NextFilter nextFilter, WriteRequest writeRequest) {
-        preHandshakeEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
-    }
-
-    /* no qualifier */void flushPreHandshakeEvents() throws SSLException {
-        IoFilterEvent scheduledWrite;
-
-        while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
-            sslFilter
-            .filterWrite(scheduledWrite.getNextFilter(), session, (WriteRequest) scheduledWrite.getParameter());
-        }
-    }
-
-    /* no qualifier */void scheduleFilterWrite(NextFilter nextFilter, WriteRequest writeRequest) {
-        filterWriteEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
-    }
-
-    /* no qualifier */void flushFilterWrite() {
-        // Fire events only when the lock is available for this handler.
-        IoFilterEvent event;
-    
-        // We need synchronization here inevitably because filterWrite can be
-        // called simultaneously and cause 'bad record MAC' integrity error.
-        while ((event = filterWriteEventQueue.poll()) != null) {
-            NextFilter nextFilter = event.getNextFilter();
-            nextFilter.filterWrite(session, (WriteRequest) event.getParameter());
-        }
-    }
-
-    /**
-     * Push the newly received data into a queue, waiting for the SSL session
-     * to be fully established
-     *
-     * @param nextFilter The next filter to call
-     * @param message The incoming data
-     */
-    /* no qualifier */void scheduleMessageReceived(NextFilter nextFilter, Object message) {
-        messageReceivedEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.MESSAGE_RECEIVED, session, message));
-    }
-    
-    /* no qualifier */void flushMessageReceived() {
-	IoFilterEvent event;
-
-	while ((event = messageReceivedEventQueue.poll()) != null) {
-	    NextFilter nextFilter = event.getNextFilter();
-	    nextFilter.messageReceived(session, event.getParameter());
-	}
-    }
-
-    /**
-     * Call when data are read from net. It will perform the initial hanshake or decrypt
-     * the data if SSL has been initialiaed.
-     * 
-     * @param buf buffer to decrypt
-     * @param nextFilter Next filter in chain
-     * @throws SSLException on errors
-     */
-    /* no qualifier */void messageReceived(NextFilter nextFilter, ByteBuffer buf) throws SSLException {
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
-        }
-
-        // append buf to inNetBuffer
-        if (inNetBuffer == null) {
-            inNetBuffer = IoBuffer.allocate(buf.remaining()).setAutoExpand(true);
-        }
-
-        inNetBuffer.put(buf);
-
-        if (!handshakeComplete) {
-            handshake(nextFilter);
-        } else {
-            // Prepare the net data for reading.
-            inNetBuffer.flip();
-
-            if (!inNetBuffer.hasRemaining()) {
-                return;
-            }
-
-            SSLEngineResult res = unwrap();
-
-            // prepare to be written again
-            if (inNetBuffer.hasRemaining()) {
-                inNetBuffer.compact();
-            } else {
-                inNetBuffer.free();
-                inNetBuffer = null;
-            }
-
-            checkStatus(res);
-
-            renegotiateIfNeeded(nextFilter, res);
-        }
-
-        if (isInboundDone()) {
-            // Rewind the MINA buffer if not all data is processed and inbound
-            // is finished.
-            int inNetBufferPosition = inNetBuffer == null ? 0 : inNetBuffer.position();
-            buf.position(buf.position() - inNetBufferPosition);
-
-            if (inNetBuffer != null) {
-                inNetBuffer.free();
-                inNetBuffer = null;
-            }
-        }
-    }
-
-    /**
-     * Get decrypted application data.
-     * 
-     * @return buffer with data
-     */
-    /* no qualifier */IoBuffer fetchAppBuffer() {
-        if (appBuffer == null) {
-            return IoBuffer.allocate(0);
-        } else {
-            IoBuffer newAppBuffer = appBuffer.flip();
-            appBuffer = null;
-
-            return newAppBuffer.shrink();
-        }
-    }
-
-    /**
-     * Get encrypted data to be sent.
-     * 
-     * @return buffer with data
-     */
-    /* no qualifier */IoBuffer fetchOutNetBuffer() {
-        IoBuffer answer = outNetBuffer;
-        
-        if (answer == null) {
-            return emptyBuffer;
-        }
-
-        outNetBuffer = null;
-        
-        return answer.shrink();
-    }
-
-    /**
-     * Encrypt provided buffer. Encrypted data returned by getOutNetBuffer().
-     * 
-     * @param src
-     *            data to encrypt
-     * @throws SSLException
-     *             on errors
-     */
-    /* no qualifier */void encrypt(ByteBuffer src) throws SSLException {
-        if (!handshakeComplete) {
-            throw new IllegalStateException();
-        }
-
-        if (!src.hasRemaining()) {
-            if (outNetBuffer == null) {
-                outNetBuffer = emptyBuffer;
-            }
-            return;
-        }
-
-        createOutNetBuffer(src.remaining());
-
-        // Loop until there is no more data in src
-        while (src.hasRemaining()) {
-
-            SSLEngineResult result = sslEngine.wrap(src, outNetBuffer.buf());
-            
-            if (result.getStatus() == SSLEngineResult.Status.OK) {
-                if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
-                    doTasks();
-                }
-            } else if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
-                outNetBuffer.capacity(outNetBuffer.capacity() << 1);
-                outNetBuffer.limit(outNetBuffer.capacity());
-            } else {
-                throw new SSLException("SSLEngine error during encrypt: " + result.getStatus() + " src: " + src
-                        + "outNetBuffer: " + outNetBuffer);
-            }
-        }
-
-        outNetBuffer.flip();
-    }
-
-    /**
-     * Start SSL shutdown process.
-     * 
-     * @return <tt>true</tt> if shutdown process is started. <tt>false</tt> if
-     *         shutdown process is already finished.
-     * @throws SSLException
-     *             on errors
-     */
-    /* no qualifier */boolean closeOutbound() throws SSLException {
-        if (sslEngine == null || sslEngine.isOutboundDone()) {
-            return false;
-        }
-
-        sslEngine.closeOutbound();
-
-        createOutNetBuffer(0);
-        SSLEngineResult result;
-
-        for (;;) {
-            result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
-            if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
-                outNetBuffer.capacity(outNetBuffer.capacity() << 1);
-                outNetBuffer.limit(outNetBuffer.capacity());
-            } else {
-                break;
-            }
-        }
-
-        if (result.getStatus() != SSLEngineResult.Status.CLOSED) {
-            throw new SSLException("Improper close state: " + result);
-        }
-
-        outNetBuffer.flip();
-
-        return true;
-    }
-
-    /**
-     * @param res
-     * @throws SSLException
-     */
-    private void checkStatus(SSLEngineResult res) throws SSLException {
-
-        SSLEngineResult.Status status = res.getStatus();
-
-        /*
-         * The status may be:
-         * OK - Normal operation
-         * OVERFLOW - Should never happen since the application buffer is sized to hold the maximum
-         * packet size.
-         * UNDERFLOW - Need to read more data from the socket. It's normal.
-         * CLOSED - The other peer closed the socket. Also normal.
-         */
-        switch (status) {
-            case BUFFER_OVERFLOW:
-                throw new SSLException("SSLEngine error during decrypt: " + status + " inNetBuffer: " + inNetBuffer
-                    + "appBuffer: " + appBuffer);
-            case CLOSED:
-                Exception exception =new RuntimeIoException("SSL/TLS close_notify received");
-                
-                // Empty the Ssl queue
-                for (IoFilterEvent event:filterWriteEventQueue) {
-                    EncryptedWriteRequest writeRequest = (EncryptedWriteRequest)event.getParameter();
-                    WriteFuture writeFuture = writeRequest.getParentRequest().getFuture();
-                    writeFuture.setException(exception);
-                    writeFuture.notifyAll();
-                }
-                
-                // Empty the session queue
-                WriteRequestQueue queue = session.getWriteRequestQueue();
-                WriteRequest request = null;
-               
-    	    	while ((request = queue.poll(session)) != null) {
-    	    	    WriteFuture writeFuture = request.getFuture();
-    	    	    writeFuture.setException(exception);
-    	    	    writeFuture.notifyAll();
-        	}
-                    
-                // We *must* shutdown session
-                session.closeNow();
-                break;
-            default: 
-                break;
-        }
-    }
-
-    /**
-     * Perform any handshaking processing.
-     */
-    /* no qualifier */void handshake(NextFilter nextFilter) throws SSLException {
-        for (;;) {
-            switch (handshakeStatus) {
-            case FINISHED:
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug("{} processing the FINISHED state", sslFilter.getSessionInfo(session));
-                }
-
-                session.setAttribute(SslFilter.SSL_SESSION, sslEngine.getSession());
-                handshakeComplete = true;
-
-                // Send the SECURE message only if it's the first SSL handshake
-                if (firstSSLNegociation) {
-                    firstSSLNegociation = false;
-                    
-                    nextFilter.event(session, SslEvent.SECURED);
-                }
-
-                if (LOGGER.isDebugEnabled()) {
-                    if (!isOutboundDone()) {
-                        LOGGER.debug("{} is now secured", sslFilter.getSessionInfo(session));
-                    } else {
-                        LOGGER.debug("{} is not secured yet", sslFilter.getSessionInfo(session));
-                    }
-                }
-
-                return;
-
-            case NEED_TASK:
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug("{} processing the NEED_TASK state", sslFilter.getSessionInfo(session));
-                }
-
-                handshakeStatus = doTasks();
-                break;
-
-            case NEED_UNWRAP:
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug("{} processing the NEED_UNWRAP state", sslFilter.getSessionInfo(session));
-                }
-                // we need more data read
-                SSLEngineResult.Status status = unwrapHandshake(nextFilter);
-
-                if (status == SSLEngineResult.Status.BUFFER_UNDERFLOW
-                        && handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED || isInboundDone()) {
-                    // We need more data or the session is closed
-                    return;
-                }
-
-                break;
-
-            case NEED_WRAP:
-            case NOT_HANDSHAKING:
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug("{} processing the NEED_WRAP state", sslFilter.getSessionInfo(session));
-                }
-
-                // First make sure that the out buffer is completely empty.
-                // Since we cannot call wrap with data left on the buffer
-                if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
-                    return;
-                }
-
-                SSLEngineResult result;
-                createOutNetBuffer(0);
-
-                result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
-
-                while ( result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW ) {
-                    outNetBuffer.capacity(outNetBuffer.capacity() << 1);
-                    outNetBuffer.limit(outNetBuffer.capacity());
-
-                    result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
-                }
-
-                outNetBuffer.flip();
-                handshakeStatus = result.getHandshakeStatus();
-                writeNetBuffer(nextFilter);
-                break;
-
-            default:
-                String msg = "Invalid Handshaking State" + handshakeStatus
-                + " while processing the Handshake for session " + session.getId();
-                LOGGER.error(msg);
-                throw new IllegalStateException(msg);
-            }
-        }
-    }
-
-    private void createOutNetBuffer(int expectedRemaining) {
-        // SSLEngine requires us to allocate unnecessarily big buffer
-        // even for small data. *Shrug*
-        int capacity = Math.max(expectedRemaining, sslEngine.getSession().getPacketBufferSize());
-
-        if (outNetBuffer != null) {
-            outNetBuffer.capacity(capacity);
-        } else {
-            outNetBuffer = IoBuffer.allocate(capacity).minimumCapacity(0);
-        }
-    }
-
-    /* no qualifier */WriteFuture writeNetBuffer(NextFilter nextFilter) throws SSLException {
-        // Check if any net data needed to be writen
-        if (outNetBuffer == null || !outNetBuffer.hasRemaining()) {
-            // no; bail out
-            return null;
-        }
-
-        // set flag that we are writing encrypted data
-        // (used in SSLFilter.filterWrite())
-        writingEncryptedData = true;
-
-        // write net data
-        WriteFuture writeFuture = null;
-
-        try {
-            IoBuffer writeBuffer = fetchOutNetBuffer();
-            writeFuture = new DefaultWriteFuture(session);
-            sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(writeBuffer, writeFuture));
-
-            // loop while more writes required to complete handshake
-            while (needToCompleteHandshake()) {
-                try {
-                    handshake(nextFilter);
-                } catch (SSLException ssle) {
-                    SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
-                    newSsle.initCause(ssle);
-                    throw newSsle;
-                }
-
-                IoBuffer currentOutNetBuffer = fetchOutNetBuffer();
-                
-                if (currentOutNetBuffer != null && currentOutNetBuffer.hasRemaining()) {
-                    writeFuture = new DefaultWriteFuture(session);
-                    sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(currentOutNetBuffer, writeFuture));
-                }
-            }
-        } finally {
-            writingEncryptedData = false;
-        }
-
-        return writeFuture;
-    }
-
-    private SSLEngineResult.Status unwrapHandshake(NextFilter nextFilter) throws SSLException {
-        // Prepare the net data for reading.
-        if (inNetBuffer != null) {
-            inNetBuffer.flip();
-        }
-
-        if ((inNetBuffer == null) || !inNetBuffer.hasRemaining()) {
-            // Need more data.
-            return SSLEngineResult.Status.BUFFER_UNDERFLOW;
-        }
-
-        SSLEngineResult res = unwrap();
-        handshakeStatus = res.getHandshakeStatus();
-
-        checkStatus(res);
-
-        // If handshake finished, no data was produced, and the status is still
-        // ok, try to unwrap more
-        if ((handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED)
-                && (res.getStatus() == SSLEngineResult.Status.OK)
-                && inNetBuffer.hasRemaining()) {
-            res = unwrap();
-
-            // prepare to be written again
-            if (inNetBuffer.hasRemaining()) {
-                inNetBuffer.compact();
-            } else {
-                inNetBuffer.free();
-                inNetBuffer = null;
-            }
-
-            renegotiateIfNeeded(nextFilter, res);
-        } else {
-            // prepare to be written again
-            if (inNetBuffer.hasRemaining()) {
-                inNetBuffer.compact();
-            } else {
-                inNetBuffer.free();
-                inNetBuffer = null;
-            }
-        }
-
-        return res.getStatus();
-    }
-
-    private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res) throws SSLException {
-        if ((res.getStatus() != SSLEngineResult.Status.CLOSED)
-                && (res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW)
-                && (res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
-            // Renegotiation required.
-            handshakeComplete = false;
-            handshakeStatus = res.getHandshakeStatus();
-            handshake(nextFilter);
-        }
-    }
-
-    /**
-     * Decrypt the incoming buffer and move the decrypted data to an
-     * application buffer.
-     */
-    private SSLEngineResult unwrap() throws SSLException {
-        // We first have to create the application buffer if it does not exist
-        if (appBuffer == null) {
-            appBuffer = IoBuffer.allocate(inNetBuffer.remaining());
-        } else {
-            // We already have one, just add the new data into it
-            appBuffer.expand(inNetBuffer.remaining());
-        }
-
-        SSLEngineResult res;
-        Status status;
-        HandshakeStatus localHandshakeStatus;
-
-        do {
-            // Decode the incoming data
-            res = sslEngine.unwrap(inNetBuffer.buf(), appBuffer.buf());
-            status = res.getStatus();
-
-            // We can be processing the Handshake
-            localHandshakeStatus = res.getHandshakeStatus();
-
-            if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
-                // We have to grow the target buffer, it's too small.
-                // Then we can call the unwrap method again
-                int newCapacity = sslEngine.getSession().getApplicationBufferSize();
-                
-                if (appBuffer.remaining() >= newCapacity) {
-                    // The buffer is already larger than the max buffer size suggested by the SSL engine.
-                    // Raising it any more will not make sense and it will end up in an endless loop. Throwing an error is safer
-                    throw new SSLException("SSL buffer overflow");
-                }
-
-                appBuffer.expand(newCapacity);
-                continue;
-            }
-        } while (((status == SSLEngineResult.Status.OK) || (status == SSLEngineResult.Status.BUFFER_OVERFLOW))
-                && ((localHandshakeStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) || 
-                        (localHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)));
-
-        return res;
-    }
-
-    /**
-     * Do all the outstanding handshake tasks in the current Thread.
-     */
-    private SSLEngineResult.HandshakeStatus doTasks() {
-        /*
-         * We could run this in a separate thread, but I don't see the need for
-         * this when used from SSLFilter. Use thread filters in MINA instead?
-         */
-        Runnable runnable;
-        while ((runnable = sslEngine.getDelegatedTask()) != null) {
-            // TODO : we may have to use a thread pool here to improve the
-            // performances
-            runnable.run();
-        }
-        return sslEngine.getHandshakeStatus();
-    }
-
-    /**
-     * Creates a new MINA buffer that is a deep copy of the remaining bytes in
-     * the given buffer (between index buf.position() and buf.limit())
-     * 
-     * @param src
-     *            the buffer to copy
-     * @return the new buffer, ready to read from
-     */
-    /* no qualifier */static IoBuffer copy(ByteBuffer src) {
-        IoBuffer copy = IoBuffer.allocate(src.remaining());
-        copy.put(src);
-        copy.flip();
-        return copy;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-
-        sb.append("SSLStatus <");
-
-        if (handshakeComplete) {
-            sb.append("SSL established");
-        } else {
-            sb.append("Processing Handshake").append("; ");
-            sb.append("Status : ").append(handshakeStatus).append("; ");
-        }
-
-        sb.append(", ");
-        sb.append("HandshakeComplete :").append(handshakeComplete).append(", ");
-        sb.append(">");
-
-        return sb.toString();
-    }
-
-    /**
-     * Free the allocated buffers
-     */
-    /* no qualifier */void release() {
-        if (inNetBuffer != null) {
-            inNetBuffer.free();
-            inNetBuffer = null;
-        }
-
-        if (outNetBuffer != null) {
-            outNetBuffer.free();
-            outNetBuffer = null;
-        }
-    }
+	/** A logger for this class */
+	private static final Logger LOGGER = LoggerFactory.getLogger(SslHandler.class);
+
+	/** The SSL Filter which has created this handler */
+	private final SslFilter sslFilter;
+
+	/** The current session */
+	private final IoSession session;
+
+	private final Queue<IoFilterEvent> preHandshakeEventQueue = new ConcurrentLinkedQueue<>();
+
+	private final Queue<IoFilterEvent> filterWriteEventQueue = new ConcurrentLinkedQueue<>();
+
+	/**
+	 * A queue used to stack all the incoming data until the SSL session is
+	 * established
+	 */
+	private final Queue<IoFilterEvent> messageReceivedEventQueue = new ConcurrentLinkedQueue<>();
+
+	private SSLEngine sslEngine;
+
+	/**
+	 * Encrypted data from the net
+	 */
+	private IoBuffer inNetBuffer;
+
+	/**
+	 * Encrypted data to be written to the net
+	 */
+	private IoBuffer outNetBuffer;
+
+	/**
+	 * Application cleartext data to be read by application
+	 */
+	private IoBuffer appBuffer;
+
+	/**
+	 * Empty buffer used during initial handshake and close operations
+	 */
+	private final IoBuffer emptyBuffer = IoBuffer.allocate(0);
+
+	private SSLEngineResult.HandshakeStatus handshakeStatus;
+
+	/**
+	 * A flag set to true when the first SSL handshake has been completed This is
+	 * used to avoid sending a notification to the application handler when we
+	 * switch to a SECURE or UNSECURE session.
+	 */
+	private boolean firstSSLNegociation;
+
+	/** A flag set to true when a SSL Handshake has been completed */
+	private boolean handshakeComplete;
+
+	/**
+	 * A flag used to indicate to the SslFilter that the buffer it will write is
+	 * already encrypted (this will be the case for data being produced during the
+	 * handshake).
+	 */
+	private boolean writingEncryptedData;
+
+	/**
+	 * Create a new SSL Handler, and initialize it.
+	 *
+	 * @param sslContext
+	 * @throws SSLException
+	 */
+	/* no qualifier */ SslHandler(SslFilter sslFilter, IoSession session) {
+		this.sslFilter = sslFilter;
+		this.session = session;
+	}
+
+	/**
+	 * Initialize the SSL handshake.
+	 *
+	 * @throws SSLException If the underlying SSLEngine handshake initialization
+	 *                      failed
+	 */
+	/* no qualifier */void init() throws SSLException {
+		if (sslEngine != null) {
+			// We already have a SSL engine created, no need to create a new one
+			return;
+		}
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} Initializing the SSL Handler", sslFilter.getSessionInfo(session));
+		}
+
+		InetSocketAddress peer = (InetSocketAddress) session.getAttribute(SslFilter.PEER_ADDRESS);
+
+		// Create the SSL engine here
+		if (peer == null) {
+			sslEngine = sslFilter.sslContext.createSSLEngine();
+		} else {
+			sslEngine = sslFilter.sslContext.createSSLEngine(peer.getHostName(), peer.getPort());
+		}
+
+		// Initialize the engine in client mode if necessary
+		sslEngine.setUseClientMode(sslFilter.isUseClientMode());
+
+		// Initialize the different SslEngine modes
+		if (!sslEngine.getUseClientMode()) {
+			// Those parameters are only valid when in server mode
+			if (sslFilter.isWantClientAuth()) {
+				sslEngine.setWantClientAuth(true);
+			}
+
+			if (sslFilter.isNeedClientAuth()) {
+				sslEngine.setNeedClientAuth(true);
+			}
+		}
+
+		// Set the cipher suite to use by this SslEngine instance
+		if (sslFilter.getEnabledCipherSuites() != null) {
+			sslEngine.setEnabledCipherSuites(sslFilter.getEnabledCipherSuites());
+		}
+
+		// Set the list of enabled protocols
+		if (sslFilter.getEnabledProtocols() != null) {
+			sslEngine.setEnabledProtocols(sslFilter.getEnabledProtocols());
+		}
+
+		// TODO : we may not need to call this method...
+		// However, if we don't call it here, the tests are failing. Why?
+		handshakeStatus = sslEngine.getHandshakeStatus();
+
+		// Default value
+		writingEncryptedData = false;
+
+		// We haven't yet started a SSL negotiation
+		// set the flags accordingly
+		firstSSLNegociation = true;
+		handshakeComplete = false;
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} SSL Handler Initialization done.", sslFilter.getSessionInfo(session));
+		}
+	}
+
+	/**
+	 * Release allocated buffers.
+	 */
+	/* no qualifier */void destroy() {
+		if (sslEngine == null) {
+			return;
+		}
+
+		// Close inbound and flush all remaining data if available.
+		try {
+			sslEngine.closeInbound();
+		} catch (SSLException e) {
+			if (LOGGER.isDebugEnabled()) {
+				LOGGER.debug("Unexpected exception from SSLEngine.closeInbound().", e);
+			}
+		}
+
+		if (outNetBuffer != null) {
+			outNetBuffer.capacity(sslEngine.getSession().getPacketBufferSize());
+		} else {
+			createOutNetBuffer(0);
+		}
+		try {
+			do {
+				outNetBuffer.clear();
+			} while (sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf()).bytesProduced() > 0);
+		} catch (SSLException e) {
+			// Ignore.
+		} finally {
+			outNetBuffer.free();
+			outNetBuffer = null;
+		}
+
+		sslEngine.closeOutbound();
+		sslEngine = null;
+
+		preHandshakeEventQueue.clear();
+	}
+
+	/**
+	 * @return The SSL filter which has created this handler
+	 */
+	/* no qualifier */SslFilter getSslFilter() {
+		return sslFilter;
+	}
+
+	/* no qualifier */IoSession getSession() {
+		return session;
+	}
+
+	/**
+	 * Check if we are writing encrypted data.
+	 */
+	/* no qualifier */boolean isWritingEncryptedData() {
+		return writingEncryptedData;
+	}
+
+	/**
+	 * Check if handshake is completed.
+	 */
+	/* no qualifier */boolean isHandshakeComplete() {
+		return handshakeComplete;
+	}
+
+	/**
+	 * Check if handshake is on going.
+	 */
+	/* no qualifier */boolean notHandshaking() {
+		return handshakeStatus == HandshakeStatus.FINISHED || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING;
+	}
+
+	/* no qualifier */boolean isInboundDone() {
+		return sslEngine == null || sslEngine.isInboundDone();
+	}
+
+	/* no qualifier */boolean isOutboundDone() {
+		return sslEngine == null || sslEngine.isOutboundDone();
+	}
+
+	/**
+	 * Check if there is any need to complete handshake.
+	 */
+	/* no qualifier */boolean needToCompleteHandshake() {
+		return handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone();
+	}
+
+	/* no qualifier */void schedulePreHandshakeWriteRequest(NextFilter nextFilter, WriteRequest writeRequest) {
+		preHandshakeEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
+	}
+
+	/* no qualifier */void flushPreHandshakeEvents() throws SSLException {
+		IoFilterEvent scheduledWrite;
+
+		while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
+			sslFilter.filterWrite(scheduledWrite.getNextFilter(), session,
+					(WriteRequest) scheduledWrite.getParameter());
+		}
+	}
+
+	/* no qualifier */void scheduleFilterWrite(NextFilter nextFilter, WriteRequest writeRequest) {
+		filterWriteEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
+	}
+
+	/* no qualifier */void flushFilterWrite() {
+		// Fire events only when the lock is available for this handler.
+		IoFilterEvent event;
+
+		// We need synchronization here inevitably because filterWrite can be
+		// called simultaneously and cause 'bad record MAC' integrity error.
+		while ((event = filterWriteEventQueue.poll()) != null) {
+			NextFilter nextFilter = event.getNextFilter();
+			nextFilter.filterWrite(session, (WriteRequest) event.getParameter());
+		}
+	}
+
+	/**
+	 * Push the newly received data into a queue, waiting for the SSL session to be
+	 * fully established
+	 *
+	 * @param nextFilter The next filter to call
+	 * @param message    The incoming data
+	 */
+	/* no qualifier */void scheduleMessageReceived(NextFilter nextFilter, Object message) {
+		messageReceivedEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.MESSAGE_RECEIVED, session, message));
+	}
+
+	/* no qualifier */void flushMessageReceived() {
+		IoFilterEvent event;
+
+		while ((event = messageReceivedEventQueue.poll()) != null) {
+			NextFilter nextFilter = event.getNextFilter();
+			nextFilter.messageReceived(session, event.getParameter());
+		}
+	}
+
+	/**
+	 * Call when data are read from net. It will perform the initial hanshake or
+	 * decrypt the data if SSL has been initialiaed.
+	 * 
+	 * @param buf        buffer to decrypt
+	 * @param nextFilter Next filter in chain
+	 * @throws SSLException on errors
+	 */
+	/* no qualifier */void messageReceived(NextFilter nextFilter, ByteBuffer buf) throws SSLException {
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
+		}
+
+		// append buf to inNetBuffer
+		if (inNetBuffer == null) {
+			inNetBuffer = IoBuffer.allocate(buf.remaining()).setAutoExpand(true);
+		}
+
+		inNetBuffer.put(buf);
+
+		if (!handshakeComplete) {
+			handshake(nextFilter);
+		} else {
+			// Prepare the net data for reading.
+			inNetBuffer.flip();
+
+			if (!inNetBuffer.hasRemaining()) {
+				return;
+			}
+
+			SSLEngineResult res = unwrap();
+
+			// prepare to be written again
+			if (inNetBuffer.hasRemaining()) {
+				inNetBuffer.compact();
+			} else {
+				inNetBuffer.free();
+				inNetBuffer = null;
+			}
+
+			checkStatus(res);
+
+			renegotiateIfNeeded(nextFilter, res);
+		}
+
+		if (isInboundDone()) {
+			// Rewind the MINA buffer if not all data is processed and inbound
+			// is finished.
+			int inNetBufferPosition = inNetBuffer == null ? 0 : inNetBuffer.position();
+			buf.position(buf.position() - inNetBufferPosition);
+
+			if (inNetBuffer != null) {
+				inNetBuffer.free();
+				inNetBuffer = null;
+			}
+		}
+	}
+
+	/**
+	 * Get decrypted application data.
+	 * 
+	 * @return buffer with data
+	 */
+	/* no qualifier */IoBuffer fetchAppBuffer() {
+		if (appBuffer == null) {
+			return IoBuffer.allocate(0);
+		} else {
+			IoBuffer newAppBuffer = appBuffer.flip();
+			appBuffer = null;
+
+			return newAppBuffer.shrink();
+		}
+	}
+
+	/**
+	 * Get encrypted data to be sent.
+	 * 
+	 * @return buffer with data
+	 */
+	/* no qualifier */IoBuffer fetchOutNetBuffer() {
+		IoBuffer answer = outNetBuffer;
+
+		if (answer == null) {
+			return emptyBuffer;
+		}
+
+		outNetBuffer = null;
+
+		return answer.shrink();
+	}
+
+	/**
+	 * Encrypt provided buffer. Encrypted data returned by getOutNetBuffer().
+	 * 
+	 * @param src data to encrypt
+	 * @throws SSLException on errors
+	 */
+	/* no qualifier */void encrypt(ByteBuffer src) throws SSLException {
+		if (!handshakeComplete) {
+			throw new IllegalStateException();
+		}
+
+		if (!src.hasRemaining()) {
+			if (outNetBuffer == null) {
+				outNetBuffer = emptyBuffer;
+			}
+			return;
+		}
+
+		createOutNetBuffer(src.remaining());
+
+		// Loop until there is no more data in src
+		while (src.hasRemaining()) {
+
+			SSLEngineResult result = sslEngine.wrap(src, outNetBuffer.buf());
+
+			if (result.getStatus() == SSLEngineResult.Status.OK) {
+				if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+					doTasks();
+				}
+			} else if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+				outNetBuffer.capacity(outNetBuffer.capacity() << 1);
+				outNetBuffer.limit(outNetBuffer.capacity());
+			} else {
+				throw new SSLException("SSLEngine error during encrypt: " + result.getStatus() + " src: " + src
+						+ "outNetBuffer: " + outNetBuffer);
+			}
+		}
+
+		outNetBuffer.flip();
+	}
+
+	/**
+	 * Start SSL shutdown process.
+	 * 
+	 * @return <tt>true</tt> if shutdown process is started. <tt>false</tt> if
+	 *         shutdown process is already finished.
+	 * @throws SSLException on errors
+	 */
+	/* no qualifier */boolean closeOutbound() throws SSLException {
+		if (sslEngine == null || sslEngine.isOutboundDone()) {
+			return false;
+		}
+
+		sslEngine.closeOutbound();
+
+		createOutNetBuffer(0);
+		SSLEngineResult result;
+
+		for (;;) {
+			result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
+			if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+				outNetBuffer.capacity(outNetBuffer.capacity() << 1);
+				outNetBuffer.limit(outNetBuffer.capacity());
+			} else {
+				break;
+			}
+		}
+
+		if (result.getStatus() != SSLEngineResult.Status.CLOSED) {
+			throw new SSLException("Improper close state: " + result);
+		}
+
+		outNetBuffer.flip();
+
+		return true;
+	}
+
+	/**
+	 * @param res
+	 * @throws SSLException
+	 */
+	private void checkStatus(SSLEngineResult res) throws SSLException {
+
+		SSLEngineResult.Status status = res.getStatus();
+
+		/*
+		 * The status may be: OK - Normal operation OVERFLOW - Should never happen since
+		 * the application buffer is sized to hold the maximum packet size. UNDERFLOW -
+		 * Need to read more data from the socket. It's normal. CLOSED - The other peer
+		 * closed the socket. Also normal.
+		 */
+		switch (status) {
+		case BUFFER_OVERFLOW:
+			throw new SSLException("SSLEngine error during decrypt: " + status + " inNetBuffer: " + inNetBuffer
+					+ "appBuffer: " + appBuffer);
+		case CLOSED:
+			Exception exception = new RuntimeIoException("SSL/TLS close_notify received");
+
+			// Empty the Ssl queue
+			for (IoFilterEvent event : filterWriteEventQueue) {
+				EncryptedWriteRequest writeRequest = (EncryptedWriteRequest) event.getParameter();
+				WriteFuture writeFuture = writeRequest.getParentRequest().getFuture();
+				writeFuture.setException(exception);
+				writeFuture.notifyAll();
+			}
+
+			// Empty the session queue
+			WriteRequestQueue queue = session.getWriteRequestQueue();
+			WriteRequest request = null;
+
+			while ((request = queue.poll(session)) != null) {
+				WriteFuture writeFuture = request.getFuture();
+				writeFuture.setException(exception);
+				writeFuture.notifyAll();
+			}
+
+			// We *must* shutdown session
+			session.closeNow();
+			break;
+		default:
+			break;
+		}
+	}
+
+	/**
+	 * Perform any handshaking processing.
+	 */
+	/* no qualifier */void handshake(NextFilter nextFilter) throws SSLException {
+		for (;;) {
+			switch (handshakeStatus) {
+			case FINISHED:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} processing the FINISHED state", sslFilter.getSessionInfo(session));
+				}
+
+				session.setAttribute(SslFilter.SSL_SESSION, sslEngine.getSession());
+				handshakeComplete = true;
+
+				// Send the SECURE message only if it's the first SSL handshake
+				if (firstSSLNegociation) {
+					firstSSLNegociation = false;
+
+					nextFilter.event(session, SslEvent.SECURED);
+				}
+
+				if (LOGGER.isDebugEnabled()) {
+					if (!isOutboundDone()) {
+						LOGGER.debug("{} is now secured", sslFilter.getSessionInfo(session));
+					} else {
+						LOGGER.debug("{} is not secured yet", sslFilter.getSessionInfo(session));
+					}
+				}
+
+				return;
+
+			case NEED_TASK:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} processing the NEED_TASK state", sslFilter.getSessionInfo(session));
+				}
+
+				handshakeStatus = doTasks();
+				break;
+
+			case NEED_UNWRAP:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} processing the NEED_UNWRAP state", sslFilter.getSessionInfo(session));
+				}
+				// we need more data read
+				SSLEngineResult.Status status = unwrapHandshake(nextFilter);
+
+				if (status == SSLEngineResult.Status.BUFFER_UNDERFLOW
+						&& handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED || isInboundDone()) {
+					// We need more data or the session is closed
+					return;
+				}
+
+				break;
+
+			case NEED_WRAP:
+			case NOT_HANDSHAKING:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} processing the NEED_WRAP state", sslFilter.getSessionInfo(session));
+				}
+
+				// First make sure that the out buffer is completely empty.
+				// Since we cannot call wrap with data left on the buffer
+				if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
+					return;
+				}
+
+				SSLEngineResult result;
+				createOutNetBuffer(0);
+
+				result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
+
+				while (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+					outNetBuffer.capacity(outNetBuffer.capacity() << 1);
+					outNetBuffer.limit(outNetBuffer.capacity());
+
+					result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
+				}
+
+				outNetBuffer.flip();
+				handshakeStatus = result.getHandshakeStatus();
+				writeNetBuffer(nextFilter);
+				break;
+
+			default:
+				String msg = "Invalid Handshaking State" + handshakeStatus
+						+ " while processing the Handshake for session " + session.getId();
+				LOGGER.error(msg);
+				throw new IllegalStateException(msg);
+			}
+		}
+	}
+
+	private void createOutNetBuffer(int expectedRemaining) {
+		// SSLEngine requires us to allocate unnecessarily big buffer
+		// even for small data. *Shrug*
+		int capacity = Math.max(expectedRemaining, sslEngine.getSession().getPacketBufferSize());
+
+		if (outNetBuffer != null) {
+			outNetBuffer.capacity(capacity);
+		} else {
+			outNetBuffer = IoBuffer.allocate(capacity).minimumCapacity(0);
+		}
+	}
+
+	/* no qualifier */WriteFuture writeNetBuffer(NextFilter nextFilter) throws SSLException {
+		// Check if any net data needed to be writen
+		if (outNetBuffer == null || !outNetBuffer.hasRemaining()) {
+			// no; bail out
+			return null;
+		}
+
+		// set flag that we are writing encrypted data
+		// (used in SSLFilter.filterWrite())
+		writingEncryptedData = true;
+
+		// write net data
+		WriteFuture writeFuture = null;
+
+		try {
+			IoBuffer writeBuffer = fetchOutNetBuffer();
+			writeFuture = new DefaultWriteFuture(session);
+			sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(writeBuffer, writeFuture));
+
+			// loop while more writes required to complete handshake
+			while (needToCompleteHandshake()) {
+				try {
+					handshake(nextFilter);
+				} catch (SSLException ssle) {
+					SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
+					newSsle.initCause(ssle);
+					throw newSsle;
+				}
+
+				IoBuffer currentOutNetBuffer = fetchOutNetBuffer();
+
+				if (currentOutNetBuffer != null && currentOutNetBuffer.hasRemaining()) {
+					writeFuture = new DefaultWriteFuture(session);
+					sslFilter.filterWrite(nextFilter, session,
+							new DefaultWriteRequest(currentOutNetBuffer, writeFuture));
+				}
+			}
+		} finally {
+			writingEncryptedData = false;
+		}
+
+		return writeFuture;
+	}
+
+	private SSLEngineResult.Status unwrapHandshake(NextFilter nextFilter) throws SSLException {
+		// Prepare the net data for reading.
+		if (inNetBuffer != null) {
+			inNetBuffer.flip();
+		}
+
+		if ((inNetBuffer == null) || !inNetBuffer.hasRemaining()) {
+			// Need more data.
+			return SSLEngineResult.Status.BUFFER_UNDERFLOW;
+		}
+
+		SSLEngineResult res = unwrap();
+		handshakeStatus = res.getHandshakeStatus();
+
+		checkStatus(res);
+
+		// If handshake finished, no data was produced, and the status is still
+		// ok, try to unwrap more
+		if ((handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED)
+				&& (res.getStatus() == SSLEngineResult.Status.OK) && inNetBuffer.hasRemaining()) {
+			res = unwrap();
+
+			// prepare to be written again
+			if (inNetBuffer.hasRemaining()) {
+				inNetBuffer.compact();
+			} else {
+				inNetBuffer.free();
+				inNetBuffer = null;
+			}
+
+			renegotiateIfNeeded(nextFilter, res);
+		} else {
+			// prepare to be written again
+			if (inNetBuffer.hasRemaining()) {
+				inNetBuffer.compact();
+			} else {
+				inNetBuffer.free();
+				inNetBuffer = null;
+			}
+		}
+
+		return res.getStatus();
+	}
+
+	private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res) throws SSLException {
+		if ((res.getStatus() != SSLEngineResult.Status.CLOSED)
+				&& (res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW)
+				&& (res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
+			// Renegotiation required.
+			handshakeComplete = false;
+			handshakeStatus = res.getHandshakeStatus();
+			handshake(nextFilter);
+		}
+	}
+
+	/**
+	 * Decrypt the incoming buffer and move the decrypted data to an application
+	 * buffer.
+	 */
+	private SSLEngineResult unwrap() throws SSLException {
+		// We first have to create the application buffer if it does not exist
+		if (appBuffer == null) {
+			appBuffer = IoBuffer.allocate(inNetBuffer.remaining());
+		} else {
+			// We already have one, just add the new data into it
+			appBuffer.expand(inNetBuffer.remaining());
+		}
+
+		SSLEngineResult res;
+		Status status;
+		HandshakeStatus localHandshakeStatus;
+
+		do {
+			// Decode the incoming data
+			res = sslEngine.unwrap(inNetBuffer.buf(), appBuffer.buf());
+			status = res.getStatus();
+
+			// We can be processing the Handshake
+			localHandshakeStatus = res.getHandshakeStatus();
+
+			if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+				// We have to grow the target buffer, it's too small.
+				// Then we can call the unwrap method again
+				int newCapacity = sslEngine.getSession().getApplicationBufferSize();
+
+				if (appBuffer.remaining() >= newCapacity) {
+					// The buffer is already larger than the max buffer size suggested by the SSL
+					// engine.
+					// Raising it any more will not make sense and it will end up in an endless
+					// loop. Throwing an error is safer
+					throw new SSLException("SSL buffer overflow");
+				}
+
+				appBuffer.expand(newCapacity);
+				continue;
+			}
+		} while (((status == SSLEngineResult.Status.OK) || (status == SSLEngineResult.Status.BUFFER_OVERFLOW))
+				&& ((localHandshakeStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
+						|| (localHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)));
+
+		return res;
+	}
+
+	/**
+	 * Do all the outstanding handshake tasks in the current Thread.
+	 */
+	private SSLEngineResult.HandshakeStatus doTasks() {
+		/*
+		 * We could run this in a separate thread, but I don't see the need for this
+		 * when used from SSLFilter. Use thread filters in MINA instead?
+		 */
+		Runnable runnable;
+		while ((runnable = sslEngine.getDelegatedTask()) != null) {
+			// TODO : we may have to use a thread pool here to improve the
+			// performances
+			runnable.run();
+		}
+		return sslEngine.getHandshakeStatus();
+	}
+
+	/**
+	 * Creates a new MINA buffer that is a deep copy of the remaining bytes in the
+	 * given buffer (between index buf.position() and buf.limit())
+	 * 
+	 * @param src the buffer to copy
+	 * @return the new buffer, ready to read from
+	 */
+	/* no qualifier */static IoBuffer copy(ByteBuffer src) {
+		IoBuffer copy = IoBuffer.allocate(src.remaining());
+		copy.put(src);
+		copy.flip();
+		return copy;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+
+		sb.append("SSLStatus <");
+
+		if (handshakeComplete) {
+			sb.append("SSL established");
+		} else {
+			sb.append("Processing Handshake").append("; ");
+			sb.append("Status : ").append(handshakeStatus).append("; ");
+		}
+
+		sb.append(", ");
+		sb.append("HandshakeComplete :").append(handshakeComplete).append(", ");
+		sb.append(">");
+
+		return sb.toString();
+	}
+
+	/**
+	 * Free the allocated buffers
+	 */
+	/* no qualifier */void release() {
+		if (inNetBuffer != null) {
+			inNetBuffer.free();
+			inNetBuffer = null;
+		}
+
+		if (outNetBuffer != null) {
+			outNetBuffer.free();
+			outNetBuffer = null;
+		}
+	}
 }
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/package-info.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/package-info.java
index 62cce29..73d622f 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/package-info.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/package-info.java
@@ -19,7 +19,8 @@
  */
 
 /**
- * Classes that implement IoFilter and provide Secure Sockets Layer functionality.
+ * Classes that implement IoFilter and provide Secure Sockets Layer
+ * functionality.
  * 
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  */

[mina] 02/04: adds IoSession#isServer()

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

johnnyv pushed a commit to branch bugfix/DIRMINA1132
in repository https://gitbox.apache.org/repos/asf/mina.git

commit 5b51500bb55429599103f8d6c07b0be691f42c7b
Author: Jonathan Valliere <jo...@apache.org>
AuthorDate: Sat Jul 24 11:13:14 2021 -0400

    adds IoSession#isServer()
---
 .../main/java/org/apache/mina/core/session/AbstractIoSession.java    | 5 +++++
 mina-core/src/main/java/org/apache/mina/core/session/IoSession.java  | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/mina-core/src/main/java/org/apache/mina/core/session/AbstractIoSession.java b/mina-core/src/main/java/org/apache/mina/core/session/AbstractIoSession.java
index 524dfa0..1bd9786 100644
--- a/mina-core/src/main/java/org/apache/mina/core/session/AbstractIoSession.java
+++ b/mina-core/src/main/java/org/apache/mina/core/session/AbstractIoSession.java
@@ -249,6 +249,11 @@ public abstract class AbstractIoSession implements IoSession {
         // Always false...
         return false;
     }
+    
+	@Override
+	public boolean isServer() {
+		return (getService() instanceof IoAcceptor);
+	}
 
     /**
      * {@inheritDoc}
diff --git a/mina-core/src/main/java/org/apache/mina/core/session/IoSession.java b/mina-core/src/main/java/org/apache/mina/core/session/IoSession.java
index 9abdf01..d147f3e 100644
--- a/mina-core/src/main/java/org/apache/mina/core/session/IoSession.java
+++ b/mina-core/src/main/java/org/apache/mina/core/session/IoSession.java
@@ -400,6 +400,11 @@ public interface IoSession {
      * or if SSL is not set for this session, or if SSL is not even an option.
      */
     boolean isSecured();
+    
+    /**
+     * @return <tt>true</tt> if the session was created by an acceptor.
+     */
+    boolean isServer();
 
     /**
      * @return the {@link CloseFuture} of this session.  This method returns

[mina] 04/04: Adds initial ssl2

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

johnnyv pushed a commit to branch bugfix/DIRMINA1132
in repository https://gitbox.apache.org/repos/asf/mina.git

commit 4c7af5615549cf557141765cefe84f409413130e
Author: Jonathan Valliere <jo...@apache.org>
AuthorDate: Sat Jul 24 11:19:50 2021 -0400

    Adds initial ssl2
---
 .../mina/filter/ssl2/EncryptedWriteRequest.java    |  32 +++
 .../org/apache/mina/filter/ssl2/SSL2Filter.java    | 248 ++++++++++++++++
 .../org/apache/mina/filter/ssl2/SSL2Handler.java   | 217 ++++++++++++++
 .../org/apache/mina/filter/ssl2/SSL2HandlerG0.java | 318 +++++++++++++++++++++
 .../org/apache/mina/util/BasicThreadFactory.java   |  62 ++++
 .../java/org/apache/mina/util/StackInspector.java  |  78 +++++
 .../apache/mina/filter/ssl2/SSL2SimpleTest.java    | 114 ++++++++
 .../org/apache/mina/filter/ssl2/keystore.sslTest   | Bin 0 -> 1368 bytes
 .../org/apache/mina/filter/ssl2/truststore.sslTest | Bin 0 -> 654 bytes
 9 files changed, 1069 insertions(+)

diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl2/EncryptedWriteRequest.java b/mina-core/src/main/java/org/apache/mina/filter/ssl2/EncryptedWriteRequest.java
new file mode 100644
index 0000000..91fabc7
--- /dev/null
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl2/EncryptedWriteRequest.java
@@ -0,0 +1,32 @@
+package org.apache.mina.filter.ssl2;
+
+import org.apache.mina.core.future.WriteFuture;
+import org.apache.mina.core.write.DefaultWriteRequest;
+import org.apache.mina.core.write.WriteRequest;
+
+public class EncryptedWriteRequest extends DefaultWriteRequest {
+
+	// The original message
+	private WriteRequest parentRequest;
+
+	public EncryptedWriteRequest(Object encodedMessage, WriteRequest parent) {
+		super(encodedMessage, null);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean isEncoded() {
+		return true;
+	}
+
+	public WriteRequest getParentRequest() {
+		return this.parentRequest;
+	}
+
+	@Override
+	public WriteFuture getFuture() {
+		return (this.getParentRequest() != null) ? this.getParentRequest().getFuture() : super.getFuture();
+	}
+}
\ No newline at end of file
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2Filter.java b/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2Filter.java
new file mode 100644
index 0000000..85d43c6
--- /dev/null
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2Filter.java
@@ -0,0 +1,248 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.filter.ssl2;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.filterchain.IoFilterAdapter;
+import org.apache.mina.core.filterchain.IoFilterChain;
+import org.apache.mina.core.session.AttributeKey;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.core.write.WriteRequest;
+import org.apache.mina.util.BasicThreadFactory;
+import org.apache.mina.util.StackInspector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An SSL filter that encrypts and decrypts the data exchanged in the session.
+ * Adding this filter triggers SSL handshake procedure immediately by sending a
+ * SSL 'hello' message, so you don't need to call {@link #startSsl(IoSession)}
+ * manually unless you are implementing StartTLS (see below). If you don't want
+ * the handshake procedure to start immediately, please specify {@code false} as
+ * {@code autoStart} parameter in the constructor.
+ * <p>
+ * This filter uses an {@link SSLEngine} which was introduced in Java 5, so Java
+ * version 5 or above is mandatory to use this filter. And please note that this
+ * filter only works for TCP/IP connections.
+ *
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+public class SSL2Filter extends IoFilterAdapter {
+	/**
+	 * The logger
+	 */
+	protected static final Logger LOGGER = LoggerFactory.getLogger(SSL2Filter.class);
+
+	protected static final Executor EXECUTOR = new ThreadPoolExecutor(2, 4, 100, TimeUnit.MILLISECONDS,
+			new LinkedBlockingDeque<Runnable>(), new BasicThreadFactory("ssl-exec"));
+
+	protected static final AttributeKey SSL_HANDLER = new AttributeKey(SSL2Filter.class, "handler");
+
+	protected final SSLContext mContext;
+
+	protected boolean mNeedClientAuth;
+
+	protected boolean mWantClientAuth;
+
+	protected String[] mEnabledCipherSuites;
+
+	protected String[] mEnabledProtocols;
+
+	/**
+	 * Creates a new SSL filter using the specified {@link SSLContext}.
+	 * 
+	 * @param sslContext The SSLContext to use
+	 */
+	public SSL2Filter(SSLContext sslContext) {
+		if (sslContext == null) {
+			throw new IllegalArgumentException("SSLContext is null");
+		}
+
+		this.mContext = sslContext;
+	}
+
+	/**
+	 * @return <tt>true</tt> if the engine will <em>require</em> client
+	 *         authentication. This option is only useful to engines in the server
+	 *         mode.
+	 */
+	public boolean isNeedClientAuth() {
+		return mNeedClientAuth;
+	}
+
+	/**
+	 * Configures the engine to <em>require</em> client authentication. This option
+	 * is only useful for engines in the server mode.
+	 * 
+	 * @param needClientAuth A flag set when we need to authenticate the client
+	 */
+	public void setNeedClientAuth(boolean needClientAuth) {
+		this.mNeedClientAuth = needClientAuth;
+	}
+
+	/**
+	 * @return <tt>true</tt> if the engine will <em>request</em> client
+	 *         authentication. This option is only useful to engines in the server
+	 *         mode.
+	 */
+	public boolean isWantClientAuth() {
+		return mWantClientAuth;
+	}
+
+	/**
+	 * Configures the engine to <em>request</em> client authentication. This option
+	 * is only useful for engines in the server mode.
+	 * 
+	 * @param wantClientAuth A flag set when we want to check the client
+	 *                       authentication
+	 */
+	public void setWantClientAuth(boolean wantClientAuth) {
+		this.mWantClientAuth = wantClientAuth;
+	}
+
+	/**
+	 * @return the list of cipher suites to be enabled when {@link SSLEngine} is
+	 *         initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.'
+	 */
+	public String[] getEnabledCipherSuites() {
+		return mEnabledCipherSuites;
+	}
+
+	/**
+	 * Sets the list of cipher suites to be enabled when {@link SSLEngine} is
+	 * initialized.
+	 *
+	 * @param cipherSuites <tt>null</tt> means 'use {@link SSLEngine}'s default.'
+	 */
+	public void setEnabledCipherSuites(String[] cipherSuites) {
+		this.mEnabledCipherSuites = cipherSuites;
+	}
+
+	/**
+	 * @return the list of protocols to be enabled when {@link SSLEngine} is
+	 *         initialized. <tt>null</tt> means 'use {@link SSLEngine}'s default.'
+	 */
+	public String[] getEnabledProtocols() {
+		return mEnabledProtocols;
+	}
+
+	/**
+	 * Sets the list of protocols to be enabled when {@link SSLEngine} is
+	 * initialized.
+	 *
+	 * @param protocols <tt>null</tt> means 'use {@link SSLEngine}'s default.'
+	 */
+	public void setEnabledProtocols(String[] protocols) {
+		this.mEnabledProtocols = protocols;
+	}
+
+	/**
+	 * Executed just before the filter is added into the chain, we do :
+	 * <ul>
+	 * <li>check that we don't have a SSL filter already present
+	 * <li>we update the next filter
+	 * <li>we create the SSL handler helper class
+	 * <li>and we store it into the session's Attributes
+	 * </ul>
+	 */
+	@Override
+	public void onPreAdd(IoFilterChain parent, String name, NextFilter next) throws Exception {
+		// Check that we don't have a SSL filter already present in the chain
+		if (parent.contains(SSL2Filter.class)) {
+			String msg = "Only one SSL filter is permitted in a chain.";
+			LOGGER.error(msg);
+			throw new IllegalStateException(msg);
+		}
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("Adding the SSL Filter {} to the chain", name);
+		}
+	}
+
+	@Override
+	public void onPreRemove(IoFilterChain parent, String name, NextFilter next) throws Exception {
+		IoSession session = parent.getSession();
+		session.removeAttribute(SSL_HANDLER);
+	}
+
+	@Override
+	public void sessionOpened(NextFilter next, IoSession session) throws Exception {
+
+		LOGGER.debug("session openend {}", session);
+
+		StackInspector.get().printStackTrace();
+
+		SSL2Handler x = SSL2Handler.class.cast(session.getAttribute(SSL_HANDLER));
+
+		if (x == null) {
+			SSLEngine e = mContext.createSSLEngine();
+
+			e.setNeedClientAuth(mNeedClientAuth);
+			e.setWantClientAuth(mWantClientAuth);
+			e.setEnabledCipherSuites(mEnabledCipherSuites);
+			e.setEnabledProtocols(mEnabledProtocols);
+			e.setUseClientMode(!session.isServer());
+
+			x = new SSL2HandlerG0(e, EXECUTOR, session);
+
+			session.setAttribute(SSL_HANDLER, x);
+		}
+
+		x.open(next);
+	}
+
+	@Override
+	public void messageReceived(NextFilter next, IoSession session, Object message) throws Exception {
+		SSL2Handler x = SSL2Handler.class.cast(session.getAttribute(SSL_HANDLER));
+		x.receive(next, IoBuffer.class.cast(message));
+	}
+
+	@Override
+	public void messageSent(NextFilter next, IoSession session, WriteRequest writeRequest) throws Exception {
+		if (writeRequest instanceof EncryptedWriteRequest) {
+			EncryptedWriteRequest e = EncryptedWriteRequest.class.cast(writeRequest);
+			SSL2Handler x = SSL2Handler.class.cast(session.getAttribute(SSL_HANDLER));
+			x.ack(next, writeRequest);
+			if (e.getParentRequest() != null) {
+				next.messageSent(session, e.getParentRequest());
+			}
+		} else {
+			super.messageSent(next, session, writeRequest);
+		}
+	}
+
+	@Override
+	public void filterWrite(NextFilter next, IoSession session, WriteRequest writeRequest) throws Exception {
+		if (writeRequest instanceof EncryptedWriteRequest) {
+			super.filterWrite(next, session, writeRequest);
+		} else {
+			SSL2Handler x = SSL2Handler.class.cast(session.getAttribute(SSL_HANDLER));
+			x.write(next, writeRequest);
+		}
+	}
+}
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2Handler.java b/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2Handler.java
new file mode 100644
index 0000000..601e73b
--- /dev/null
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2Handler.java
@@ -0,0 +1,217 @@
+package org.apache.mina.filter.ssl2;
+
+import java.util.Deque;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.Executor;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.filterchain.IoFilter.NextFilter;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.core.write.WriteRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class SSL2Handler {
+
+	/**
+	 * Static logger
+	 */
+	static protected final Logger LOGGER = LoggerFactory.getLogger(SSL2Handler.class);
+
+	/**
+	 * Write Requests which are enqueued prior to the completion of the handshaking
+	 */
+	protected final Deque<WriteRequest> mWriteQueue = new ConcurrentLinkedDeque<>();
+
+	/**
+	 * Requests which have been sent to the socket and waiting acknowledgment
+	 */
+	protected final Deque<WriteRequest> mAckQueue = new ConcurrentLinkedDeque<>();
+
+	/**
+	 * SSL Engine
+	 */
+	protected final SSLEngine mEngine;
+
+	/**
+	 * Task executor
+	 */
+	protected final Executor mExecutor;
+
+	/**
+	 * Socket session
+	 */
+	protected final IoSession mSession;
+
+	/**
+	 * Progressive decoder buffer
+	 */
+	protected IoBuffer mReceiveBuffer;
+
+	public SSL2Handler(SSLEngine p, Executor e, IoSession s) {
+		this.mEngine = p;
+		this.mExecutor = e;
+		this.mSession = s;
+	}
+
+	/**
+	 * Opens the encryption session, this may include sending the initial handshake
+	 * message
+	 * 
+	 * @param session
+	 * @param next
+	 * 
+	 * @throws SSLException
+	 */
+	abstract public void open(NextFilter next) throws SSLException;
+
+	/**
+	 * Decodes encrypted messages and passes the results to the {@code next} filter.
+	 * 
+	 * @param message
+	 * @param session
+	 * @param next
+	 * 
+	 * @throws SSLException
+	 */
+	abstract public void receive(NextFilter next, final IoBuffer message) throws SSLException;
+
+	/**
+	 * Acknowledge that a {@link WriteRequest} has been successfully written to the
+	 * {@link IoSession}
+	 * <p>
+	 * This functionality is used to enforce flow control by allowing only a
+	 * specific number of pending write operations at any moment of time. When one
+	 * {@code WriteRequest} is acknowledged, another can be encoded and written.
+	 * 
+	 * @param request
+	 * @param session
+	 * @param next
+	 * 
+	 * @throws SSLException
+	 */
+	abstract public void ack(NextFilter next, final WriteRequest request) throws SSLException;
+
+	/**
+	 * Encrypts and writes the specified {@link WriteRequest} to the
+	 * {@link IoSession} or enqueues it to be processed later.
+	 * <p>
+	 * The encryption session may be currently handshaking preventing application
+	 * messages from being written.
+	 * 
+	 * @param request
+	 * @param session
+	 * @param next
+	 * 
+	 * @throws SSLException
+	 */
+	abstract public void write(NextFilter next, final WriteRequest request) throws SSLException;
+
+	/**
+	 * Closes the encryption session and writes any required messages
+	 * 
+	 * @param session
+	 * @param next
+	 * 
+	 * @throws SSLException
+	 */
+	abstract public void close(NextFilter next) throws SSLException;
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public String toString() {
+		StringBuilder b = new StringBuilder();
+
+		b.append(this.getClass().getSimpleName());
+		b.append("@");
+		b.append(Integer.toHexString(this.hashCode()));
+		b.append("[mode=");
+
+		if (this.mEngine.getUseClientMode()) {
+			b.append("client");
+		} else {
+			b.append("server");
+		}
+
+		b.append("]");
+
+		return b.toString();
+	}
+
+	/**
+	 * Combines the received data with any previously received data
+	 * 
+	 * @param source received data
+	 * @return buffer to decode
+	 */
+	protected IoBuffer resume_decode_buffer(IoBuffer source) {
+		if (mReceiveBuffer == null)
+			if (source == null)
+				return IoBuffer.allocate(0);
+			else
+				return source;
+		else {
+			if (source != null) {
+				mReceiveBuffer.expand(source.remaining());
+				mReceiveBuffer.put(source);
+				source.free();
+			}
+			mReceiveBuffer.flip();
+			return mReceiveBuffer;
+		}
+	}
+
+	/**
+	 * Stores data for later use if any is remaining
+	 * 
+	 * @param source the buffer previously returned by
+	 *               {@link #resume_decode_buffer(IoBuffer)}
+	 */
+	protected void save_decode_buffer(IoBuffer source) {
+		if (source.hasRemaining()) {
+			if (source.isDerived()) {
+				this.mReceiveBuffer = IoBuffer.allocate(source.remaining());
+				this.mReceiveBuffer.put(source);
+			} else {
+				source.compact();
+				this.mReceiveBuffer = source;
+			}
+		} else {
+			source.free();
+			this.mReceiveBuffer = null;
+		}
+	}
+
+	/**
+	 * Allocates the default encoder buffer for the given source size
+	 * 
+	 * @param source
+	 * @return buffer
+	 */
+	protected IoBuffer allocate_encode_buffer(int estimate) {
+		SSLSession session = this.mEngine.getHandshakeSession();
+		if (session == null)
+			session = this.mEngine.getSession();
+		int packets = Math.max(2, Math.min(16, 1 + (estimate / session.getApplicationBufferSize())));
+		return IoBuffer.allocate(packets * session.getPacketBufferSize());
+	}
+
+	/**
+	 * Allocates the default decoder buffer for the given source size
+	 * 
+	 * @param source
+	 * @return buffer
+	 */
+	protected IoBuffer allocate_app_buffer(int estimate) {
+		SSLSession session = this.mEngine.getHandshakeSession();
+		if (session == null)
+			session = this.mEngine.getSession();
+		int packets = 1 + (estimate / session.getPacketBufferSize());
+		return IoBuffer.allocate(packets * session.getApplicationBufferSize());
+	}
+}
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2HandlerG0.java b/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2HandlerG0.java
new file mode 100644
index 0000000..700ebdd
--- /dev/null
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2HandlerG0.java
@@ -0,0 +1,318 @@
+package org.apache.mina.filter.ssl2;
+
+import java.util.concurrent.Executor;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLException;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.filterchain.IoFilter.NextFilter;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.core.write.WriteRequest;
+
+public class SSL2HandlerG0 extends SSL2Handler {
+
+	public SSL2HandlerG0(SSLEngine p, Executor e, IoSession s) {
+		super(p, e, s);
+	}
+
+	synchronized public void open(final NextFilter next) throws SSLException {
+		if (this.mEngine.getUseClientMode()) {
+			this.mEngine.beginHandshake();
+			this.lwrite(next);
+		}
+	}
+
+	synchronized public void receive(final NextFilter next, final IoBuffer message) throws SSLException {
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} receive() - source {}", toString(), message);
+		}
+
+		final IoBuffer input = resume_decode_buffer(message);
+
+		try {
+			while (lreceive(next, input) && message.hasRemaining()) {
+				// spin
+			}
+		} finally {
+			save_decode_buffer(input);
+		}
+	}
+
+	/**
+	 * Process a received message
+	 * 
+	 * @param message received data
+	 * @param session user session
+	 * @param next    filter
+	 * @return {@code true} if some of the message was consumed
+	 * @throws SSLException
+	 */
+	@SuppressWarnings("incomplete-switch")
+	protected boolean lreceive(final NextFilter next, final IoBuffer message) throws SSLException {
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} lreceive() - source {}", toString(), message);
+		}
+
+		final IoBuffer source = message == null ? IoBuffer.allocate(0) : message;
+		final IoBuffer dest = allocate_app_buffer(source.remaining());
+
+		final SSLEngineResult result = mEngine.unwrap(source.buf(), dest.buf());
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} lreceive() - bytes-consumed {}, bytes-produced {}, status {}", toString(),
+					result.bytesConsumed(), result.bytesProduced(), result.getStatus());
+		}
+
+		if (result.bytesProduced() == 0) {
+			dest.free();
+		} else {
+			dest.flip();
+
+			if (LOGGER.isDebugEnabled()) {
+				LOGGER.debug("{} lreceive() - result {}", toString(), dest);
+			}
+
+			next.messageReceived(this.mSession, dest);
+		}
+
+		switch (result.getHandshakeStatus()) {
+			case NEED_TASK:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} lreceive() - handshake needs task, scheduling tasks", toString());
+				}
+				this.schedule_task(next);
+				break;
+			case NEED_WRAP:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} lreceive() - handshake needs to write a new message", toString());
+				}
+				this.lwrite(next);
+				break;
+			case FINISHED:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} lreceive() - handshake finished, flushing pending requests", toString());
+				}
+				this.lflush(next);
+				break;
+		}
+
+		return result.bytesConsumed() > 0;
+	}
+
+	synchronized public void ack(final NextFilter next, final WriteRequest request) throws SSLException {
+
+	}
+
+	synchronized public void write(final NextFilter next, final WriteRequest request) throws SSLException {
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} write() - source {}", toString(), request);
+		}
+
+		this.mWriteQueue.add(request);
+		this.lflush(next);
+	}
+
+	/**
+	 * Attempts to encode the WriteRequest and write the data to the IoSession
+	 * 
+	 * @param request
+	 * @param session
+	 * @param next
+	 * @return {@code true} if the WriteRequest was successfully written
+	 * @throws SSLException
+	 */
+	@SuppressWarnings("incomplete-switch")
+	synchronized protected boolean lwrite(final NextFilter next, final WriteRequest request) throws SSLException {
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} lwrite() - source {}", toString(), request);
+		}
+
+		final IoBuffer source = IoBuffer.class.cast(request.getMessage());
+		final IoBuffer dest = allocate_encode_buffer(source.remaining());
+
+		final SSLEngineResult result = this.mEngine.wrap(source.buf(), dest.buf());
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} lwrite() - bytes-consumed {}, bytes-produced {}, status {}, handshake {}", toString(),
+					result.bytesConsumed(), result.bytesProduced(), result.getStatus(), result.getHandshakeStatus());
+		}
+
+		if (result.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) {
+			// then we probably consumed some data
+			dest.flip();
+			if (source.hasRemaining()) {
+				next.filterWrite(this.mSession, new EncryptedWriteRequest(dest, null));
+				lwrite(next, request); // write additional chunks
+			} else {
+				source.rewind();
+				next.filterWrite(this.mSession, new EncryptedWriteRequest(dest, request));
+			}
+
+			return true;
+		} else {
+			if (dest.position() == 0) {
+				dest.free();
+			} else {
+				next.filterWrite(this.mSession, new EncryptedWriteRequest(dest, null));
+			}
+
+			switch (result.getHandshakeStatus()) {
+				case NEED_TASK:
+					if (LOGGER.isDebugEnabled()) {
+						LOGGER.debug("{} lwrite() - handshake needs task, scheduling tasks", toString());
+					}
+					this.schedule_task(next);
+					break;
+				case NEED_WRAP:
+					if (LOGGER.isDebugEnabled()) {
+						LOGGER.debug("{} lwrite() - handshake needs to encode a message", toString());
+					}
+					return this.lwrite(next, request);
+				case FINISHED:
+					if (LOGGER.isDebugEnabled()) {
+						LOGGER.debug("{} lwrite() - handshake finished, flushing pending requests", toString());
+					}
+					if (this.lwrite(next, request)) {
+						this.lflush(next);
+						return true;
+					}
+					break;
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Attempts to generate a handshake message and write the data to the IoSession
+	 * 
+	 * @param session
+	 * @param next
+	 * @return {@code true} if a message was generated and written
+	 * @throws SSLException
+	 */
+	@SuppressWarnings("incomplete-switch")
+	synchronized protected boolean lwrite(NextFilter next) throws SSLException {
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} lwrite() - internal", toString());
+		}
+
+		final IoBuffer source = IoBuffer.allocate(0);
+		final IoBuffer dest = allocate_encode_buffer(source.remaining());
+
+		final SSLEngineResult result = this.mEngine.wrap(source.buf(), dest.buf());
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} lwrite() - bytes-consumed {}, bytes-produced {}", toString(), result.bytesConsumed(),
+					result.bytesProduced());
+		}
+
+		if (dest.position() == 0) {
+			dest.free();
+		} else {
+			dest.flip();
+
+			if (LOGGER.isDebugEnabled()) {
+				LOGGER.debug("{} lwrite() - result {}", toString(), dest);
+			}
+
+			final EncryptedWriteRequest encrypted = new EncryptedWriteRequest(dest, null);
+			next.filterWrite(this.mSession, encrypted);
+		}
+
+		switch (result.getHandshakeStatus()) {
+			case NEED_TASK:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} lwrite() - handshake needs task, scheduling tasks", toString());
+				}
+				this.schedule_task(next);
+				break;
+			case NEED_WRAP:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} lwrite() - handshake needs to encode a message", toString());
+				}
+				this.lwrite(next);
+				break;
+			case FINISHED:
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} lwrite() - handshake finished, flushing pending requests", toString());
+				}
+				this.lflush(next);
+				break;
+		}
+
+		return result.bytesProduced() > 0;
+	}
+
+	protected void lflush(final NextFilter next) throws SSLException {
+		if (this.mWriteQueue.isEmpty()) {
+			if (LOGGER.isDebugEnabled()) {
+				LOGGER.debug("{} flush() - no saved messages", toString());
+			}
+			return;
+		}
+
+		WriteRequest current = null;
+
+		while ((current = this.mWriteQueue.poll()) != null) {
+			if (lwrite(next, current) == false) {
+				this.mWriteQueue.addFirst(current);
+				break;
+			}
+		}
+	}
+
+	synchronized public void close(final NextFilter next) throws SSLException {
+		if (mEngine.isOutboundDone())
+			return;
+
+		if (LOGGER.isDebugEnabled()) {
+			LOGGER.debug("{} close() - closing session", toString());
+		}
+
+		mEngine.closeOutbound();
+		this.lwrite(next);
+	}
+
+	protected void schedule_task(final NextFilter next) {
+		if (this.mExecutor == null) {
+			this.execute_task(next);
+		} else {
+			this.mExecutor.execute(new Runnable() {
+				@Override
+				public void run() {
+					SSL2HandlerG0.this.execute_task(next);
+				}
+			});
+		}
+	}
+
+	synchronized protected void execute_task(final NextFilter next) {
+		Runnable t = null;
+		while ((t = mEngine.getDelegatedTask()) != null) {
+			try {
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} task() - executing {}", toString(), t);
+				}
+
+				t.run();
+
+				if (LOGGER.isDebugEnabled()) {
+					LOGGER.debug("{} task() - writing handshake messages", toString());
+				}
+
+				lwrite(next);
+			} catch (SSLException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+}
diff --git a/mina-core/src/main/java/org/apache/mina/util/BasicThreadFactory.java b/mina-core/src/main/java/org/apache/mina/util/BasicThreadFactory.java
new file mode 100644
index 0000000..7e73017
--- /dev/null
+++ b/mina-core/src/main/java/org/apache/mina/util/BasicThreadFactory.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.util;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Utility for creating thread factories
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * @author Jonathan Valliere
+ */
+public class BasicThreadFactory implements java.util.concurrent.ThreadFactory {
+	public final AtomicInteger count = new AtomicInteger(0);
+	public final String name;
+
+	public final boolean deamon;
+	public final int priority;
+
+	public BasicThreadFactory(String basename, boolean daemon, int priority) {
+		this.name = basename;
+		this.deamon = daemon;
+		this.priority = priority;
+	}
+
+	public BasicThreadFactory(String basename, boolean daemon) {
+		this(basename, daemon, Thread.NORM_PRIORITY);
+	}
+
+	public BasicThreadFactory(String basename) {
+		this(basename, false, Thread.NORM_PRIORITY);
+	}
+
+	@Override
+	public Thread newThread(Runnable pool) {
+		Thread t = new Thread(pool);
+
+		t.setName(this.name + "-" + this.count.getAndIncrement());
+		t.setPriority(this.priority);
+		t.setDaemon(this.deamon);
+
+		return t;
+	}
+
+}
diff --git a/mina-core/src/main/java/org/apache/mina/util/StackInspector.java b/mina-core/src/main/java/org/apache/mina/util/StackInspector.java
new file mode 100644
index 0000000..45b3414
--- /dev/null
+++ b/mina-core/src/main/java/org/apache/mina/util/StackInspector.java
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.util;
+
+/**
+ * Utility to retrieving the thread stack debug information
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * @author Jonathan Valliere
+ */
+public class StackInspector extends RuntimeException {
+	static public final StackTraceElement callee() {
+		return Thread.currentThread().getStackTrace()[3];
+	}
+
+	static public final StackInspector get(String message) {
+		try {
+			throw new StackInspector(message);
+		} catch (StackInspector e0) {
+			return e0;
+		}
+	}
+
+	static public final StackInspector get(Throwable cause) {
+		try {
+			throw new StackInspector(cause);
+		} catch (StackInspector e0) {
+			return e0;
+		}
+	}
+
+	static public final StackInspector get() {
+		try {
+			throw new StackInspector("Stack from Thread: " + Thread.currentThread().getName());
+		} catch (StackInspector e0) {
+			return e0;
+		}
+	}
+
+	static private final long serialVersionUID = 1L;
+
+	StackInspector() {
+
+	}
+
+	StackInspector(String message) {
+		super(message);
+	}
+
+	StackInspector(Throwable cause) {
+		super(cause);
+	}
+
+	StackInspector(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+	StackInspector(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+		super(message, cause, enableSuppression, writableStackTrace);
+	}
+}
diff --git a/mina-core/src/test/java/org/apache/mina/filter/ssl2/SSL2SimpleTest.java b/mina-core/src/test/java/org/apache/mina/filter/ssl2/SSL2SimpleTest.java
new file mode 100644
index 0000000..d198459
--- /dev/null
+++ b/mina-core/src/test/java/org/apache/mina/filter/ssl2/SSL2SimpleTest.java
@@ -0,0 +1,114 @@
+package org.apache.mina.filter.ssl2;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.future.IoFuture;
+import org.apache.mina.core.service.IoAcceptor;
+import org.apache.mina.core.service.IoConnector;
+import org.apache.mina.core.service.IoHandlerAdapter;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.core.write.DefaultWriteRequest;
+import org.apache.mina.core.write.WriteRequest;
+import org.apache.mina.filter.ssl.SslDIRMINA937Test;
+import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
+import org.apache.mina.transport.socket.nio.NioSocketConnector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SSL2SimpleTest {
+
+	public static void main(String[] args) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException,
+			UnrecoverableKeyException, CertificateException, IOException {
+		// System.setProperty("javax.net.debug", "all");
+
+		KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+		TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+
+		KeyStore ks = KeyStore.getInstance("JKS");
+		KeyStore ts = KeyStore.getInstance("JKS");
+
+		ks.load(SslDIRMINA937Test.class.getResourceAsStream("keystore.sslTest"), "password".toCharArray());
+		ts.load(SslDIRMINA937Test.class.getResourceAsStream("truststore.sslTest"), "password".toCharArray());
+
+		kmf.init(ks, "password".toCharArray());
+		tmf.init(ts);
+
+		final SSLContext context = SSLContext.getInstance("TLS");
+		context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
+
+		final SSL2Filter filter = new SSL2Filter(context);
+		filter.setEnabledCipherSuites(new String[] { "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384" });
+		filter.setEnabledProtocols(new String[] { "TLSv1.3" });
+
+		final IoAcceptor socket_acceptor = new NioSocketAcceptor();
+
+		socket_acceptor.getFilterChain().addFirst("ssl", filter);
+		socket_acceptor.setHandler(new DebugFilter());
+
+		final IoConnector socket_connector = new NioSocketConnector();
+
+		socket_connector.getFilterChain().addFirst("ssl", filter);
+		socket_connector.setHandler(new DebugFilter());
+
+		final InetSocketAddress server_address = new InetSocketAddress("0.0.0.0", 53301);
+		socket_acceptor.bind(server_address);
+
+		final IoFuture connect_future = socket_connector.connect(server_address);
+		connect_future.awaitUninterruptibly();
+
+		final IoSession client_socket = connect_future.getSession();
+		
+		client_socket.write(createWriteRequest()).awaitUninterruptibly();
+
+		try {
+			Thread.sleep(1000);
+		} catch (InterruptedException e) {
+
+		}
+
+		client_socket.closeOnFlush().awaitUninterruptibly();
+
+		socket_connector.dispose();
+
+		socket_acceptor.unbind();
+		socket_acceptor.dispose();
+	}
+
+	public static class DebugFilter extends IoHandlerAdapter {
+		protected static final Logger LOGGER = LoggerFactory.getLogger(DebugFilter.class);
+
+		@Override
+		public void messageReceived(IoSession session, Object message) throws Exception {
+
+			IoBuffer b = IoBuffer.class.cast(message);
+			LOGGER.debug("received clear-text message\n" + b.getHexDump(true));
+		}
+	}
+
+	public static WriteRequest createWriteRequest() {
+		// HTTP request
+		StringBuilder http = new StringBuilder();
+		http.append("GET / HTTP/1.0\r\n");
+		http.append("Connection: close\r\n");
+		http.append("\r\n");
+
+		IoBuffer message = IoBuffer.allocate(1024);
+		message.put(http.toString().getBytes());
+		message.flip();
+
+		return new DefaultWriteRequest(message);
+	}
+}
diff --git a/mina-core/src/test/resources/org/apache/mina/filter/ssl2/keystore.sslTest b/mina-core/src/test/resources/org/apache/mina/filter/ssl2/keystore.sslTest
new file mode 100644
index 0000000..36190ba
Binary files /dev/null and b/mina-core/src/test/resources/org/apache/mina/filter/ssl2/keystore.sslTest differ
diff --git a/mina-core/src/test/resources/org/apache/mina/filter/ssl2/truststore.sslTest b/mina-core/src/test/resources/org/apache/mina/filter/ssl2/truststore.sslTest
new file mode 100644
index 0000000..48c5963
Binary files /dev/null and b/mina-core/src/test/resources/org/apache/mina/filter/ssl2/truststore.sslTest differ

[mina] 03/04: Adds unique identifier to toString() result

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

johnnyv pushed a commit to branch bugfix/DIRMINA1132
in repository https://gitbox.apache.org/repos/asf/mina.git

commit bb5b97e6eace566ec8db0eabdbbb595467204468
Author: Jonathan Valliere <jo...@apache.org>
AuthorDate: Sat Jul 24 11:14:12 2021 -0400

    Adds unique identifier to toString() result
---
 .../src/main/java/org/apache/mina/core/buffer/AbstractIoBuffer.java     | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/mina-core/src/main/java/org/apache/mina/core/buffer/AbstractIoBuffer.java b/mina-core/src/main/java/org/apache/mina/core/buffer/AbstractIoBuffer.java
index cc8c758..15ac0ed 100644
--- a/mina-core/src/main/java/org/apache/mina/core/buffer/AbstractIoBuffer.java
+++ b/mina-core/src/main/java/org/apache/mina/core/buffer/AbstractIoBuffer.java
@@ -1317,6 +1317,8 @@ public abstract class AbstractIoBuffer extends IoBuffer {
 		} else {
 			buf.append("HeapBuffer");
 		}
+		buf.append("@");
+		buf.append(Integer.toHexString(super.hashCode()));
 		buf.append("[pos=");
 		buf.append(position());
 		buf.append(" lim=");