diff --git a/src/main/java/org/jboss/netty/handler/ssl/SslBufferPool.java b/src/main/java/org/jboss/netty/handler/ssl/SslBufferPool.java index 376c395388..95a5891fe2 100644 --- a/src/main/java/org/jboss/netty/handler/ssl/SslBufferPool.java +++ b/src/main/java/org/jboss/netty/handler/ssl/SslBufferPool.java @@ -24,12 +24,26 @@ package org.jboss.netty.handler.ssl; import java.nio.ByteBuffer; +import javax.net.ssl.SSLEngine; + /** + * A {@link ByteBuffer} pool dedicated for {@link SslHandler} performance + * improvement. + *

+ * In most cases, you won't need to create a new pool instance because + * {@link SslHandler} has a default pool instance internally. + *

+ * The reason why {@link SslHandler} requires a buffer pool is because the + * current {@link SSLEngine} implementation always requires a 17KiB buffer for + * the 'wrap' and 'unwrap' operation. In most cases, the size of the required + * buffer is much smaller than that, and therefore allocating a 17KiB buffer + * for every 'wrap' and 'unwrap' operation wastes a lot of memory bandwidth, + * resulting in the application performance degradation. + * * @author The Netty Project (netty-dev@lists.jboss.org) * @author Trustin Lee (tlee@redhat.com) * * @version $Rev$, $Date$ - * */ public class SslBufferPool { @@ -41,29 +55,49 @@ public class SslBufferPool { private final int maxBufferCount; private int index; + /** + * Creates a new buffer pool whose size is {@code 18113536}, which can + * hold {@code 1024} buffers. + */ public SslBufferPool() { this(DEFAULT_POOL_SIZE); } - public SslBufferPool(int poolSize) { - if (poolSize <= 0) { - throw new IllegalArgumentException("poolSize: " + poolSize); + /** + * Creates a new buffer pool. + * + * @param maxPoolSize the maximum number of bytes that this pool can hold + */ + public SslBufferPool(int maxPoolSize) { + if (maxPoolSize <= 0) { + throw new IllegalArgumentException("maxPoolSize: " + maxPoolSize); } - int maxBufferCount = poolSize / MAX_PACKET_SIZE; - if (poolSize % MAX_PACKET_SIZE != 0) { + int maxBufferCount = maxPoolSize / MAX_PACKET_SIZE; + if (maxPoolSize % MAX_PACKET_SIZE != 0) { maxBufferCount ++; } - poolSize = maxBufferCount * MAX_PACKET_SIZE; + maxPoolSize = maxBufferCount * MAX_PACKET_SIZE; pool = new ByteBuffer[maxBufferCount]; this.maxBufferCount = maxBufferCount; } + /** + * Returns the maximum size of this pool in byte unit. The returned value + * can be somewhat different from what was specified in the constructor. + */ public int getMaxPoolSize() { return maxBufferCount * MAX_PACKET_SIZE; } + /** + * Returns the number of bytes which were allocated but not acquired yet. + * You can estimate how optimal the specified maximum pool size is from + * this value. If it keeps returning {@code 0}, it means the pool is + * getting exhausted. If it keeps returns a unnecessarily big value, it + * means the pool is wasting the heap space. + */ public synchronized int getUnacquiredPoolSize() { return index * MAX_PACKET_SIZE; } diff --git a/src/main/java/org/jboss/netty/handler/ssl/SslHandler.java b/src/main/java/org/jboss/netty/handler/ssl/SslHandler.java index ec7e8a3e14..4f7f13be20 100644 --- a/src/main/java/org/jboss/netty/handler/ssl/SslHandler.java +++ b/src/main/java/org/jboss/netty/handler/ssl/SslHandler.java @@ -44,6 +44,7 @@ import org.jboss.netty.channel.ChannelEvent; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelFutureListener; import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.MessageEvent; @@ -51,6 +52,42 @@ import org.jboss.netty.handler.codec.frame.FrameDecoder; import org.jboss.netty.util.ImmediateExecutor; /** + * Adds SSL + * · TLS and StartTLS support to a {@link Channel}. Please refer + * to the "SecureChat" example in the distribution or the web + * site for the detailed usage. + * + *

Beginning the handshake

+ *

+ * A user should make sure not to write a message while the + * {@linkplain #handshake(Channel) handshake} is in progress unless it's a + * renegotiation. You will be notified by the {@link ChannelFuture} which is + * returned by the {@link #handshake(Channel)} method when the handshake + * process succeeds or fails. + * + *

Renegotiation

+ *

+ * Once the initial handshake is done successfully. You can always call + * {@link #handshake(Channel)} again to renegotiate the SSL session parameters. + * + *

Closing the session

+ *

+ * To close the SSL session, the {@link #close(Channel)} method should be + * called to send the {@code close_notify} message to the remote peer. One + * exception is when you close the {@link Channel} - {@link SslHandler} + * intercepts the close request and send the {@code close_notify} message + * before the channel closure automatically. Once the SSL session is closed, + * it's not reusable, and consequently you should create a new + * {@link SslHandler} with a new {@link SSLEngine} as explained in the + * following section. + * + *

Restarting the session

+ *

+ * To restart the SSL session, you must remove the existing closed + * {@link SslHandler} from the {@link ChannelPipeline}, insert a new + * {@link SslHandler} with a new {@link SSLEngine} into the pipeline, + * and start the handshake process as described in the first section. + * * @author The Netty Project (netty-dev@lists.jboss.org) * @author Trustin Lee (tlee@redhat.com) * @@ -65,7 +102,11 @@ public class SslHandler extends FrameDecoder implements ChannelDownstreamHandler private static SslBufferPool defaultBufferPool; - private static synchronized SslBufferPool getDefaultBufferPool() { + /** + * Returns the default {@link SslBufferPool} used when no pool is + * specified in the constructor. + */ + public static synchronized SslBufferPool getDefaultBufferPool() { if (defaultBufferPool == null) { defaultBufferPool = new SslBufferPool(); } @@ -88,34 +129,110 @@ public class SslHandler extends FrameDecoder implements ChannelDownstreamHandler private final Queue pendingUnencryptedWrites = new LinkedList(); private final Queue pendingEncryptedWrites = new LinkedList(); + /** + * Creates a new instance. + * + * @param engine the {@link SSLEngine} this handler will use + */ public SslHandler(SSLEngine engine) { this(engine, getDefaultBufferPool(), ImmediateExecutor.INSTANCE); } + /** + * Creates a new instance. + * + * @param engine the {@link SSLEngine} this handler will use + * @param bufferPool the {@link SslBufferPool} where this handler will + * acquire the buffers required by the {@link SSLEngine} + */ public SslHandler(SSLEngine engine, SslBufferPool bufferPool) { this(engine, bufferPool, ImmediateExecutor.INSTANCE); } + /** + * Creates a new instance. + * + * @param engine the {@link SSLEngine} this handler will use + * @param startTls {@code true} if the first write request shouldn't be + * encrypted by the {@link SSLEngine} + */ public SslHandler(SSLEngine engine, boolean startTls) { this(engine, getDefaultBufferPool(), startTls); } + /** + * Creates a new instance. + * + * @param engine the {@link SSLEngine} this handler will use + * @param bufferPool the {@link SslBufferPool} where this handler will + * acquire the buffers required by the {@link SSLEngine} + * @param startTls {@code true} if the first write request shouldn't be + * encrypted by the {@link SSLEngine} + */ public SslHandler(SSLEngine engine, SslBufferPool bufferPool, boolean startTls) { this(engine, bufferPool, startTls, ImmediateExecutor.INSTANCE); } + /** + * Creates a new instance. + * + * @param engine + * the {@link SSLEngine} this handler will use + * @param delegatedTaskExecutor + * the {@link Executor} which will execute the delegated task + * that {@link SSLEngine#getDelegatedTask()} will return + */ public SslHandler(SSLEngine engine, Executor delegatedTaskExecutor) { this(engine, getDefaultBufferPool(), delegatedTaskExecutor); } + /** + * Creates a new instance. + * + * @param engine + * the {@link SSLEngine} this handler will use + * @param bufferPool + * the {@link SslBufferPool} where this handler will acquire + * the buffers required by the {@link SSLEngine} + * @param delegatedTaskExecutor + * the {@link Executor} which will execute the delegated task + * that {@link SSLEngine#getDelegatedTask()} will return + */ public SslHandler(SSLEngine engine, SslBufferPool bufferPool, Executor delegatedTaskExecutor) { this(engine, bufferPool, false, delegatedTaskExecutor); } + /** + * Creates a new instance. + * + * @param engine + * the {@link SSLEngine} this handler will use + * @param startTls + * {@code true} if the first write request shouldn't be encrypted + * by the {@link SSLEngine} + * @param delegatedTaskExecutor + * the {@link Executor} which will execute the delegated task + * that {@link SSLEngine#getDelegatedTask()} will return + */ public SslHandler(SSLEngine engine, boolean startTls, Executor delegatedTaskExecutor) { this(engine, getDefaultBufferPool(), startTls, delegatedTaskExecutor); } + /** + * Creates a new instance. + * + * @param engine + * the {@link SSLEngine} this handler will use + * @param bufferPool + * the {@link SslBufferPool} where this handler will acquire + * the buffers required by the {@link SSLEngine} + * @param startTls + * {@code true} if the first write request shouldn't be encrypted + * by the {@link SSLEngine} + * @param delegatedTaskExecutor + * the {@link Executor} which will execute the delegated task + * that {@link SSLEngine#getDelegatedTask()} will return + */ public SslHandler(SSLEngine engine, SslBufferPool bufferPool, boolean startTls, Executor delegatedTaskExecutor) { if (engine == null) { throw new NullPointerException("engine"); @@ -132,10 +249,19 @@ public class SslHandler extends FrameDecoder implements ChannelDownstreamHandler this.startTls = startTls; } + /** + * Returns the {@link SSLEngine} which is used by this handler. + */ public SSLEngine getEngine() { return engine; } + /** + * Starts SSL / TLS handshake for the specified channel. + * + * @return a {@link ChannelFuture} which is notified when the handshake + * succeeds or fails. + */ public ChannelFuture handshake(Channel channel) throws SSLException { ChannelFuture handshakeFuture; synchronized (handshakeLock) { @@ -153,6 +279,10 @@ public class SslHandler extends FrameDecoder implements ChannelDownstreamHandler return handshakeFuture; } + /** + * Sends a SSL {@code close_notify} message to the specified channel and + * destroys the underlying {@link SSLEngine}. + */ public ChannelFuture close(Channel channel) throws SSLException { ChannelHandlerContext ctx = context(channel); engine.closeOutbound(); diff --git a/src/main/java/org/jboss/netty/handler/ssl/package-info.java b/src/main/java/org/jboss/netty/handler/ssl/package-info.java index 48cfeff11e..e7fb4398de 100644 --- a/src/main/java/org/jboss/netty/handler/ssl/package-info.java +++ b/src/main/java/org/jboss/netty/handler/ssl/package-info.java @@ -23,6 +23,6 @@ /** * SSL · - * TLS implementation. + * TLS implementation based on {@link javax.net.ssl.SSLEngine} */ package org.jboss.netty.handler.ssl; \ No newline at end of file