From e323b221d7fc88edb1cf824ab92c61be15416fe0 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Wed, 15 Apr 2009 14:45:24 +0000 Subject: [PATCH] Fixed a problem where SslHandler does not notify close futures when partial SSL frame is received. --- .../jboss/netty/handler/ssl/SslHandler.java | 71 ++++++++++--------- 1 file changed, 37 insertions(+), 34 deletions(-) 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 4ec5591513..592fe56ab6 100644 --- a/src/main/java/org/jboss/netty/handler/ssl/SslHandler.java +++ b/src/main/java/org/jboss/netty/handler/ssl/SslHandler.java @@ -24,13 +24,14 @@ package org.jboss.netty.handler.ssl; import static org.jboss.netty.channel.Channels.*; +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.util.LinkedList; import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.regex.Pattern; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; @@ -49,6 +50,7 @@ import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.DownstreamMessageEvent; +import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.handler.codec.frame.FrameDecoder; import org.jboss.netty.logging.InternalLogger; @@ -107,6 +109,9 @@ public class SslHandler extends FrameDecoder { private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); + private static final Pattern CONNECTION_RESET = + Pattern.compile("^.*Connection\\s*reset.*$", Pattern.CASE_INSENSITIVE); + private static SslBufferPool defaultBufferPool; /** @@ -133,7 +138,6 @@ public class SslHandler extends FrameDecoder { private final AtomicBoolean sentFirstMessage = new AtomicBoolean(); private final AtomicBoolean sentCloseNotify = new AtomicBoolean(); - final Queue closeFutures = new ConcurrentLinkedQueue(); private final Queue pendingUnencryptedWrites = new LinkedList(); private final Queue pendingEncryptedWrites = new LinkedList(); @@ -358,17 +362,6 @@ public class SslHandler extends FrameDecoder { } } - // Notify all close futures which were not notified yet. - synchronized (closeFutures) { - for (;;) { - ChannelFuture future = closeFutures.poll(); - if (future == null) { - break; - } - future.setSuccess(); - } - } - try { super.channelDisconnected(ctx, e); } finally { @@ -384,9 +377,33 @@ public class SslHandler extends FrameDecoder { } } + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) + throws Exception { + + Throwable cause = e.getCause(); + if (cause instanceof IOException && engine.isOutboundDone()) { + String message = String.valueOf(cause.getMessage()).toLowerCase(); + if (CONNECTION_RESET.matcher(message).matches()) { + // It is safe to ignore the 'connection reset by peer' error + // after sending closure_notify. + logger.debug( + "Ignoring a 'connection reset by peer' error", + cause); + + // Close the connection explicitly just in case the transport + // did not close the connection automatically. + Channels.close(ctx, succeededFuture(e.getChannel())); + return; + } + } + + ctx.sendUpstream(e); + } + @Override protected Object decode( - ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { + final ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { if (buffer.readableBytes() < 2) { return null; } @@ -414,17 +431,6 @@ public class SslHandler extends FrameDecoder { buffer.skipBytes(packetLength); } - if (frame == null && engine.isInboundDone()) { - synchronized (closeFutures) { - for (;;) { - ChannelFuture future = closeFutures.poll(); - if (future == null) { - break; - } - Channels.close(ctx, future); - } - } - } return frame; } @@ -772,15 +778,12 @@ public class SslHandler extends FrameDecoder { if (!engine.isInboundDone()) { if (sentCloseNotify.compareAndSet(false, true)) { engine.closeOutbound(); - synchronized (closeFutures) { - ChannelFuture closeNotifyFuture = wrapNonAppData(context, e.getChannel()); - closeNotifyFuture.addListener(new ChannelFutureListener() { - public void operationComplete(ChannelFuture closeNotifyFuture) throws Exception { - boolean offered = closeFutures.offer(e.getFuture()); - assert offered; - } - }); - } + ChannelFuture closeNotifyFuture = wrapNonAppData(context, e.getChannel()); + closeNotifyFuture.addListener(new ChannelFutureListener() { + public void operationComplete(ChannelFuture closeNotifyFuture) throws Exception { + Channels.close(context, e.getFuture()); + } + }); return; } }