diff --git a/codec-http/src/test/java/io/netty/handler/codec/spdy/AbstractSocketSpdyEchoTest.java b/codec-http/src/test/java/io/netty/handler/codec/spdy/AbstractSocketSpdyEchoTest.java index d5b2d42434..b2881b7b2b 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/spdy/AbstractSocketSpdyEchoTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/spdy/AbstractSocketSpdyEchoTest.java @@ -164,7 +164,7 @@ public abstract class AbstractSocketSpdyEchoTest { protected abstract ChannelFactory newServerSocketChannelFactory(Executor executor); protected abstract ChannelFactory newClientSocketChannelFactory(Executor executor); - @Test + @Test(timeout = 10000) public void testSpdyEcho() throws Throwable { ServerBootstrap sb = new ServerBootstrap(newServerSocketChannelFactory(executor)); ClientBootstrap cb = new ClientBootstrap(newClientSocketChannelFactory(executor)); diff --git a/common/src/main/java/io/netty/util/HashedWheelTimer.java b/common/src/main/java/io/netty/util/HashedWheelTimer.java index c8f200c2ec..701f01623e 100644 --- a/common/src/main/java/io/netty/util/HashedWheelTimer.java +++ b/common/src/main/java/io/netty/util/HashedWheelTimer.java @@ -518,9 +518,12 @@ public class HashedWheelTimer implements Timer { try { task.run(this); } catch (Throwable t) { - logger.warn( - "An exception was thrown by " + - TimerTask.class.getSimpleName() + ".", t); + if (logger.isWarnEnabled()) { + logger.warn( + "An exception was thrown by " + + TimerTask.class.getSimpleName() + ".", t); + } + } } diff --git a/common/src/main/java/io/netty/util/internal/LegacyLinkedTransferQueue.java b/common/src/main/java/io/netty/util/internal/LegacyLinkedTransferQueue.java index f0168ea637..7e48c15ddf 100644 --- a/common/src/main/java/io/netty/util/internal/LegacyLinkedTransferQueue.java +++ b/common/src/main/java/io/netty/util/internal/LegacyLinkedTransferQueue.java @@ -612,6 +612,7 @@ public class LegacyLinkedTransferQueue extends AbstractQueue @SuppressWarnings("unchecked") static E cast(Object item) { // assert item == null || item.getClass() != Node.class; + // Explicit cast, see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954 return (E) item; } @@ -653,7 +654,8 @@ public class LegacyLinkedTransferQueue extends AbstractQueue } } LockSupport.unpark(p.waiter); - return LegacyLinkedTransferQueue.cast(item); + // Explicit cast, see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954 + return (E) LegacyLinkedTransferQueue.cast(item); } } Node n = p.next; @@ -737,7 +739,8 @@ public class LegacyLinkedTransferQueue extends AbstractQueue if (item != e) { // matched // assert item != s; s.forgetContents(); // avoid garbage - return LegacyLinkedTransferQueue.cast(item); + // Explicit cast, see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954 + return (E) LegacyLinkedTransferQueue.cast(item); } if ((w.isInterrupted() || timed && nanos <= 0) && s.casItem(e, s)) { // cancel @@ -825,7 +828,8 @@ public class LegacyLinkedTransferQueue extends AbstractQueue Object item = p.item; if (p.isData) { if (item != null && item != p) { - return LegacyLinkedTransferQueue.cast(item); + // Explicit cast, see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954 + return (E) LegacyLinkedTransferQueue.cast(item); } } else if (item == null) { @@ -878,7 +882,8 @@ public class LegacyLinkedTransferQueue extends AbstractQueue Object item = p.item; if (p.isData) { if (item != null && item != p) { - nextItem = LegacyLinkedTransferQueue.cast(item); + // Explicit cast, see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954 + nextItem = (E) LegacyLinkedTransferQueue.cast(item); nextNode = p; return; } diff --git a/common/src/main/java/io/netty/util/internal/SharedResourceMisuseDetector.java b/common/src/main/java/io/netty/util/internal/SharedResourceMisuseDetector.java index 7daf3a1e77..f15e38fb0d 100644 --- a/common/src/main/java/io/netty/util/internal/SharedResourceMisuseDetector.java +++ b/common/src/main/java/io/netty/util/internal/SharedResourceMisuseDetector.java @@ -43,12 +43,14 @@ public class SharedResourceMisuseDetector { public void increase() { if (activeInstances.incrementAndGet() > MAX_ACTIVE_INSTANCES) { - if (logged.compareAndSet(false, true)) { - logger.warn( - "You are creating too many " + type.getSimpleName() + - " instances. " + type.getSimpleName() + - " is a shared resource that must be reused across the" + - " application, so that only a few instances are created."); + if (logger.isWarnEnabled()) { + if (logged.compareAndSet(false, true)) { + logger.warn( + "You are creating too many " + type.getSimpleName() + + " instances. " + type.getSimpleName() + + " is a shared resource that must be reused across the" + + " application, so that only a few instances are created."); + } } } } diff --git a/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java b/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java index 83d54025bc..0729d0b5ad 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java +++ b/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java @@ -81,8 +81,11 @@ public class AutobahnServerHandler extends SimpleChannelUpstreamHandler { } private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { - logger.debug(String - .format("Channel %s received %s", ctx.getChannel().getId(), frame.getClass().getSimpleName())); + if (logger.isDebugEnabled()) { + logger.debug(String + .format("Channel %s received %s", ctx.getChannel().getId(), frame.getClass().getSimpleName())); + } + if (frame instanceof CloseWebSocketFrame) { this.handshaker.close(ctx.getChannel(), (CloseWebSocketFrame) frame); diff --git a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerHandler.java b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerHandler.java index 64fdcc6000..f07a1d0d8e 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerHandler.java +++ b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerHandler.java @@ -116,7 +116,9 @@ public class WebSocketServerHandler extends SimpleChannelUpstreamHandler { // Send the uppercase string back. String request = ((TextWebSocketFrame) frame).getText(); - logger.debug(String.format("Channel %s received %s", ctx.getChannel().getId(), request)); + if (logger.isDebugEnabled()) { + logger.debug(String.format("Channel %s received %s", ctx.getChannel().getId(), request)); + } ctx.getChannel().write(new TextWebSocketFrame(request.toUpperCase())); } diff --git a/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java b/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java index c2a6bf6289..5218846ffe 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java +++ b/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java @@ -116,7 +116,9 @@ public class WebSocketSslServerHandler extends SimpleChannelUpstreamHandler { // Send the uppercase string back. String request = ((TextWebSocketFrame) frame).getText(); - logger.debug(String.format("Channel %s received %s", ctx.getChannel().getId(), request)); + if (logger.isDebugEnabled()) { + logger.debug(String.format("Channel %s received %s", ctx.getChannel().getId(), request)); + } ctx.getChannel().write(new TextWebSocketFrame(request.toUpperCase())); } diff --git a/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServerSslContext.java b/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServerSslContext.java index b21cd26502..77df475075 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServerSslContext.java +++ b/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServerSslContext.java @@ -86,7 +86,9 @@ public final class WebSocketSslServerSslContext { } _serverContext = serverContext; } catch (Exception ex) { - logger.error("Error initializing SslContextManager. " + ex.getMessage(), ex); + if (logger.isErrorEnabled()) { + logger.error("Error initializing SslContextManager. " + ex.getMessage(), ex); + } System.exit(1); } diff --git a/example/src/main/java/io/netty/example/sctp/SctpClientHandler.java b/example/src/main/java/io/netty/example/sctp/SctpClientHandler.java index 561fda3d69..4c0d3b62f4 100644 --- a/example/src/main/java/io/netty/example/sctp/SctpClientHandler.java +++ b/example/src/main/java/io/netty/example/sctp/SctpClientHandler.java @@ -25,7 +25,7 @@ import io.netty.channel.ChannelStateEvent; import io.netty.channel.ExceptionEvent; import io.netty.channel.MessageEvent; import io.netty.channel.SimpleChannelUpstreamHandler; -import io.netty.channel.sctp.SctpPayload; +import io.netty.channel.sctp.SctpFrame; /** * Handler implementation for the echo client. It initiates the message @@ -47,7 +47,7 @@ public class SctpClientHandler extends SimpleChannelUpstreamHandler { */ @Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent stateEvent) { - stateEvent.getChannel().write(new SctpPayload(0, 0, ChannelBuffers.wrappedBuffer("SCTP ECHO".getBytes()))); + stateEvent.getChannel().write(new SctpFrame(0, 0, ChannelBuffers.wrappedBuffer("SCTP ECHO".getBytes()))); } @Override diff --git a/handler/src/main/java/io/netty/handler/ipfilter/CIDR6.java b/handler/src/main/java/io/netty/handler/ipfilter/CIDR6.java index 2ee9c7bd29..fe2e81ea3f 100644 --- a/handler/src/main/java/io/netty/handler/ipfilter/CIDR6.java +++ b/handler/src/main/java/io/netty/handler/ipfilter/CIDR6.java @@ -58,7 +58,9 @@ public class CIDR6 extends CIDR { try { return bigIntToIPv6Address(addressEndBigInt); } catch (UnknownHostException e) { - logger.error("invalid ip address calculated as an end address"); + if (logger.isErrorEnabled()) { + logger.error("invalid ip address calculated as an end address"); + } return null; } } diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleList.java b/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleList.java index 020c35aeae..4d0141f489 100644 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleList.java +++ b/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleList.java @@ -69,7 +69,9 @@ public class IpFilterRuleList extends ArrayList { return; } if (!(rule.startsWith("+") || rule.startsWith("-"))) { - logger.error("syntax error in ip filter rule:" + rule); + if (logger.isErrorEnabled()) { + logger.error("syntax error in ip filter rule:" + rule); + } return; } @@ -80,10 +82,14 @@ public class IpFilterRuleList extends ArrayList { try { this.add(new IpSubnetFilterRule(allow, rule.substring(3))); } catch (UnknownHostException e) { - logger.error("error parsing ip filter " + rule, e); + if (logger.isErrorEnabled()) { + logger.error("error parsing ip filter " + rule, e); + } } } else { - logger.error("syntax error in ip filter rule:" + rule); + if (logger.isErrorEnabled()) { + logger.error("syntax error in ip filter rule:" + rule); + } } } } diff --git a/handler/src/main/java/io/netty/handler/ipfilter/PatternRule.java b/handler/src/main/java/io/netty/handler/ipfilter/PatternRule.java index 04d6be559b..9f04e463be 100644 --- a/handler/src/main/java/io/netty/handler/ipfilter/PatternRule.java +++ b/handler/src/main/java/io/netty/handler/ipfilter/PatternRule.java @@ -158,7 +158,9 @@ public class PatternRule implements IpFilterRule, Comparable { return true; } } catch (UnknownHostException e) { - logger.info("error getting ip of localhost", e); + if (logger.isInfoEnabled()) { + logger.info("error getting ip of localhost", e); + } } try { InetAddress[] addrs = InetAddress.getAllByName("127.0.0.1"); @@ -168,7 +170,9 @@ public class PatternRule implements IpFilterRule, Comparable { } } } catch (UnknownHostException e) { - logger.info("error getting ip of localhost", e); + if (logger.isInfoEnabled()) { + logger.info("error getting ip of localhost", e); + } } return false; diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index 059d524c0d..0b37456911 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -497,7 +497,9 @@ public class SslHandler extends FrameDecoder try { engine.closeInbound(); } catch (SSLException ex) { - logger.debug("Failed to clean up SSLEngine.", ex); + if (logger.isDebugEnabled()) { + logger.debug("Failed to clean up SSLEngine.", ex); + } } } } @@ -513,9 +515,12 @@ public class SslHandler extends FrameDecoder synchronized (ignoreClosedChannelExceptionLock) { if (ignoreClosedChannelException > 0) { ignoreClosedChannelException --; - logger.debug( - "Swallowing an exception raised while " + - "writing non-app data", cause); + if (logger.isDebugEnabled()) { + logger.debug( + "Swallowing an exception raised while " + + "writing non-app data", cause); + } + return; } } @@ -524,10 +529,12 @@ public class SslHandler extends FrameDecoder if (IGNORABLE_ERROR_MESSAGE.matcher(message).matches()) { // It is safe to ignore the 'connection reset by peer' or // 'broken pipe' error after sending closure_notify. - logger.debug( - "Swallowing a 'connection reset by peer / " + - "broken pipe' error occurred while writing " + - "'closure_notify'", cause); + if (logger.isDebugEnabled()) { + logger.debug( + "Swallowing a 'connection reset by peer / " + + "broken pipe' error occurred while writing " + + "'closure_notify'", cause); + } // Close the connection explicitly just in case the transport // did not close the connection automatically. @@ -1085,9 +1092,12 @@ public class SslHandler extends FrameDecoder try { engine.closeInbound(); } catch (SSLException e) { - logger.debug( - "SSLEngine.closeInbound() raised an exception after " + - "a handshake failure.", e); + if (logger.isDebugEnabled()) { + logger.debug( + "SSLEngine.closeInbound() raised an exception after " + + "a handshake failure.", e); + } + } } @@ -1106,7 +1116,9 @@ public class SslHandler extends FrameDecoder try { unwrap(context, e.getChannel(), ChannelBuffers.EMPTY_BUFFER, 0, 0); } catch (SSLException ex) { - logger.debug("Failed to unwrap before sending a close_notify message", ex); + if (logger.isDebugEnabled()) { + logger.debug("Failed to unwrap before sending a close_notify message", ex); + } } if (!engine.isInboundDone()) { @@ -1118,7 +1130,9 @@ public class SslHandler extends FrameDecoder new ClosingChannelFutureListener(context, e)); success = true; } catch (SSLException ex) { - logger.debug("Failed to encode a close_notify message", ex); + if (logger.isDebugEnabled()) { + logger.debug("Failed to encode a close_notify message", ex); + } } } } else { diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java b/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java index a071a618db..c5387a33e9 100644 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java +++ b/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java @@ -92,7 +92,9 @@ public class ChunkedWriteHandler implements ChannelUpstreamHandler, ChannelDowns try { flush(ctx); } catch (Exception e) { - logger.warn("Unexpected exception while sending chunks.", e); + if (logger.isWarnEnabled()) { + logger.warn("Unexpected exception while sending chunks.", e); + } } } @@ -270,7 +272,9 @@ public class ChunkedWriteHandler implements ChannelUpstreamHandler, ChannelDowns try { chunks.close(); } catch (Throwable t) { - logger.warn("Failed to close a chunked input.", t); + if (logger.isWarnEnabled()) { + logger.warn("Failed to close a chunked input.", t); + } } } } diff --git a/pom.xml b/pom.xml index 6ac479d717..d704137bb5 100644 --- a/pom.xml +++ b/pom.xml @@ -217,7 +217,7 @@ [1.6.0,) - [3.0.3,) + [3.0.2,) diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSocketSslEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSocketSslEchoTest.java index ef9b6819fe..a9906d3917 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSocketSslEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSocketSslEchoTest.java @@ -129,7 +129,9 @@ public abstract class AbstractSocketSslEchoTest { ChannelFuture ccf = cb.connect(new InetSocketAddress(SocketAddresses.LOCALHOST, port)); ccf.awaitUninterruptibly(); if (!ccf.isSuccess()) { - logger.error("Connection attempt failed", ccf.getCause()); + if(logger.isErrorEnabled()) { + logger.error("Connection attempt failed", ccf.getCause()); + } sc.close().awaitUninterruptibly(); } assertTrue(ccf.isSuccess()); @@ -238,9 +240,11 @@ public abstract class AbstractSocketSslEchoTest { @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { - logger.warn( - "Unexpected exception from the " + - (server? "server" : "client") + " side", e.getCause()); + if (logger.isWarnEnabled()) { + logger.warn( + "Unexpected exception from the " + + (server? "server" : "client") + " side", e.getCause()); + } exception.compareAndSet(null, e.getCause()); e.getChannel().close(); diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index c14941d722..2d6a2d8b6e 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -35,5 +35,36 @@ netty-transport ${project.version} + + ${project.groupId} + netty-codec + ${project.version} + + + ${project.groupId} + netty-handler + ${project.version} + + + + + + maven-surefire-plugin + 2.7.2 + + + default-test + + + true + + + + + + diff --git a/transport-sctp/src/main/java/com/sun/nio/sctp/MessageInfo.java b/transport-sctp/src/main/java/com/sun/nio/sctp/MessageInfo.java index bab8487f3f..1f7eaccbd0 100644 --- a/transport-sctp/src/main/java/com/sun/nio/sctp/MessageInfo.java +++ b/transport-sctp/src/main/java/com/sun/nio/sctp/MessageInfo.java @@ -32,5 +32,7 @@ public abstract class MessageInfo { public abstract int payloadProtocolID(); public abstract MessageInfo payloadProtocolID(int ppid); public abstract boolean isComplete(); + public abstract boolean isUnordered(); + public abstract MessageInfo unordered(boolean b); } diff --git a/transport-sctp/src/main/java/com/sun/nio/sctp/SctpChannel.java b/transport-sctp/src/main/java/com/sun/nio/sctp/SctpChannel.java index 287c40041b..4d0c906d0e 100644 --- a/transport-sctp/src/main/java/com/sun/nio/sctp/SctpChannel.java +++ b/transport-sctp/src/main/java/com/sun/nio/sctp/SctpChannel.java @@ -16,6 +16,7 @@ package com.sun.nio.sctp; import java.io.IOException; +import java.net.InetAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.spi.AbstractSelectableChannel; @@ -45,6 +46,12 @@ public abstract class SctpChannel extends AbstractSelectableChannel { public abstract SctpChannel bind(SocketAddress local) throws IOException; public abstract boolean connect(SocketAddress remote) throws IOException; public abstract boolean finishConnect() throws IOException; + + public abstract SctpChannel bindAddress(InetAddress inetAddress) throws IOException; + public abstract SctpChannel unbindAddress(InetAddress inetAddress) throws IOException; + public abstract MessageInfo receive(ByteBuffer dst, T attachment, NotificationHandler handler) throws IOException; public abstract int send(ByteBuffer src, MessageInfo messageInfo) throws IOException; + + public abstract Set> supportedOptions(); } diff --git a/transport-sctp/src/main/java/com/sun/nio/sctp/SctpServerChannel.java b/transport-sctp/src/main/java/com/sun/nio/sctp/SctpServerChannel.java index 772a768825..eaf617e5f7 100644 --- a/transport-sctp/src/main/java/com/sun/nio/sctp/SctpServerChannel.java +++ b/transport-sctp/src/main/java/com/sun/nio/sctp/SctpServerChannel.java @@ -16,6 +16,7 @@ package com.sun.nio.sctp; import java.io.IOException; +import java.net.InetAddress; import java.net.SocketAddress; import java.nio.channels.spi.AbstractSelectableChannel; import java.nio.channels.spi.SelectorProvider; @@ -41,5 +42,9 @@ public abstract class SctpServerChannel extends AbstractSelectableChannel { public abstract SctpServerChannel bind(SocketAddress local) throws IOException; public abstract SctpServerChannel bind(SocketAddress local, int backlog) throws IOException; + + public abstract SctpServerChannel bindAddress(InetAddress inetAddress) throws IOException; + public abstract SctpServerChannel unbindAddress(InetAddress inetAddress) throws IOException; + public abstract SctpChannel accept() throws IOException; } diff --git a/transport-sctp/src/main/java/com/sun/nio/sctp/SctpStandardSocketOptions.java b/transport-sctp/src/main/java/com/sun/nio/sctp/SctpStandardSocketOptions.java index f5418a9b31..0cbca3061f 100644 --- a/transport-sctp/src/main/java/com/sun/nio/sctp/SctpStandardSocketOptions.java +++ b/transport-sctp/src/main/java/com/sun/nio/sctp/SctpStandardSocketOptions.java @@ -34,5 +34,18 @@ public class SctpStandardSocketOptions { public static final SctpSocketOption SO_SNDBUF = null; public static class InitMaxStreams { + + public static InitMaxStreams create(int i, int i1) { + return null; + } + + public int maxInStreams() { + return 0; + } + + public int maxOutStreams() { + return 0; + } + } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultNioSctpChannelConfig.java b/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultNioSctpChannelConfig.java index 7fc830413f..0598804db8 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultNioSctpChannelConfig.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultNioSctpChannelConfig.java @@ -53,11 +53,13 @@ class DefaultNioSctpChannelConfig extends DefaultSctpChannelConfig implements Ni if (getWriteBufferHighWaterMark() < getWriteBufferLowWaterMark()) { // Recover the integrity of the configuration with a sensible value. setWriteBufferLowWaterMark0(getWriteBufferHighWaterMark() >>> 1); - // Notify the user about misconfiguration. - logger.warn( - "writeBufferLowWaterMark cannot be greater than " + - "writeBufferHighWaterMark; setting to the half of the " + - "writeBufferHighWaterMark."); + if (logger.isWarnEnabled()) { + // Notify the user about misconfiguration. + logger.warn( + "writeBufferLowWaterMark cannot be greater than " + + "writeBufferHighWaterMark; setting to the half of the " + + "writeBufferHighWaterMark."); + } } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpChannelConfig.java b/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpChannelConfig.java index 25d667951d..475ad752ae 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpChannelConfig.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpChannelConfig.java @@ -50,10 +50,9 @@ class DefaultSctpChannelConfig extends DefaultChannelConfig implements SctpChann setSendBufferSize(ConversionUtil.toInt(value)); } else if (key.equals("sctpNoDelay")) { setSctpNoDelay(ConversionUtil.toBoolean(value)); - } else if (key.equals("soLinger")) { - setSoLinger(ConversionUtil.toInt(value)); } else if (key.equals("sctpInitMaxStreams")) { - setInitMaxStreams((InitMaxStreams) value); + final Integer maxInOutStreams = ConversionUtil.toInt(value); + setInitMaxStreams(InitMaxStreams.create(maxInOutStreams, maxInOutStreams)); } else { return false; } @@ -78,24 +77,6 @@ class DefaultSctpChannelConfig extends DefaultChannelConfig implements SctpChann } } - @Override - public int getSoLinger() { - try { - return channel.getOption(SO_LINGER); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setSoLinger(int soLinger) { - try { - channel.setOption(SO_LINGER, soLinger); - } catch (IOException e) { - throw new ChannelException(e); - } - } - @Override public int getSendBufferSize() { try { diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpServerChannelConfig.java b/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpServerChannelConfig.java index b311b31c0b..56cccf2645 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpServerChannelConfig.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpServerChannelConfig.java @@ -49,7 +49,8 @@ public class DefaultSctpServerChannelConfig extends DefaultServerChannelConfig } if (key.equals("sctpInitMaxStreams")) { - setInitMaxStreams((InitMaxStreams) value); + final Integer maxInOutStreams = ConversionUtil.toInt(value); + setInitMaxStreams(InitMaxStreams.create(maxInOutStreams, maxInOutStreams)); } else if (key.equals("backlog")) { setBacklog(ConversionUtil.toInt(value)); } else { diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpBindAddressEvent.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpBindAddressEvent.java new file mode 100644 index 0000000000..40e71b2a4d --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpBindAddressEvent.java @@ -0,0 +1,38 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.channel.sctp; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelState; +import io.netty.channel.DownstreamChannelStateEvent; + +import java.net.InetAddress; + +public class SctpBindAddressEvent extends DownstreamChannelStateEvent { + + /** + * Creates a new instance. + */ + public SctpBindAddressEvent(Channel channel, ChannelFuture future, InetAddress localAddress) { + super(channel, future, ChannelState.INTEREST_OPS, localAddress); + } + + @Override + public InetAddress getValue() { + return (InetAddress) super.getValue(); + } +} diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannel.java index df0b9392ac..0c9231588a 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannel.java @@ -17,6 +17,7 @@ package io.netty.channel.sctp; import com.sun.nio.sctp.Association; import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.SocketChannelConfig; import io.netty.channel.socket.nio.NioSocketChannelConfig; @@ -58,6 +59,17 @@ public interface SctpChannel extends Channel { */ Set getAllRemoteAddresses(); + /** + * Bind a multi-homing address to the already bound channel + */ + ChannelFuture bindAddress(InetAddress localAddress); + + + /** + * Unbind a multi-homing address from a already established channel + */ + ChannelFuture unbindAddress(InetAddress localAddress); + /** * Get the underlying SCTP association */ diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelConfig.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelConfig.java index bf67ec3ac2..798285abcc 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelConfig.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelConfig.java @@ -32,8 +32,6 @@ import io.netty.channel.ChannelConfig; * * {@code "sctpNoDelay"}{@link #setSctpNoDelay(boolean)}} * - * {@code "soLinger"}{@link #setSoLinger(int)} - * * {@code "receiveBufferSize"}{@link #setReceiveBufferSize(int)} * * {@code "sendBufferSize"}{@link #setSendBufferSize(int)} @@ -54,16 +52,6 @@ public interface SctpChannelConfig extends ChannelConfig { */ void setSctpNoDelay(boolean sctpNoDelay); - /** - * Gets the {@code SO_LINGER} option. - */ - int getSoLinger(); - - /** - * Sets the {@code SO_LINGER} option. - */ - void setSoLinger(int soLinger); - /** * Gets the {@code SO_SNDBUF} option. */ diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelImpl.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelImpl.java index a73e42c4a6..e688963c6c 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelImpl.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelImpl.java @@ -17,6 +17,7 @@ package io.netty.channel.sctp; import static io.netty.channel.Channels.*; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Collections; @@ -29,7 +30,6 @@ import java.util.concurrent.atomic.AtomicInteger; import com.sun.nio.sctp.Association; -import io.netty.buffer.ChannelBuffer; import io.netty.channel.AbstractChannel; import io.netty.channel.Channel; import io.netty.channel.ChannelFactory; @@ -153,6 +153,20 @@ class SctpChannelImpl extends AbstractChannel implements SctpChannel { } } + @Override + public ChannelFuture bindAddress(InetAddress localAddress) { + ChannelFuture future = future(this); + getPipeline().sendDownstream(new SctpBindAddressEvent(this, future, localAddress)); + return future; + } + + @Override + public ChannelFuture unbindAddress(InetAddress localAddress) { + ChannelFuture future = future(this); + getPipeline().sendDownstream(new SctpUnbindAddressEvent(this, future, localAddress)); + return future; + } + @Override public Association association() { try { @@ -297,8 +311,8 @@ class SctpChannelImpl extends AbstractChannel implements SctpChannel { private int getMessageSize(MessageEvent e) { Object m = e.getMessage(); - if (m instanceof ChannelBuffer) { - return ((ChannelBuffer) m).readableBytes(); + if (m instanceof SctpFrame) { + return ((SctpFrame) m).getPayloadBuffer().readableBytes(); } return 0; } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpClientChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpClientChannel.java index e875e67bb3..4f12cf512a 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpClientChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpClientChannel.java @@ -41,7 +41,7 @@ final class SctpClientChannel extends SctpChannelImpl { try { underlayingChannel = SctpChannel.open(); } catch (IOException e) { - throw new ChannelException("Failed to open a socket.", e); + throw new ChannelException("Failed to open a sctp channel.", e); } boolean success = false; @@ -55,9 +55,11 @@ final class SctpClientChannel extends SctpChannelImpl { try { underlayingChannel.close(); } catch (IOException e) { - logger.warn( - "Failed to close a partially initialized socket.", - e); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to close a partially initialized socket.", + e); + } } } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpClientPipelineSink.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpClientPipelineSink.java index cdd483beb2..363407f7d5 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpClientPipelineSink.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpClientPipelineSink.java @@ -19,6 +19,7 @@ import static io.netty.channel.Channels.*; import java.io.IOException; import java.net.ConnectException; +import java.net.InetAddress; import java.net.SocketAddress; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; @@ -98,7 +99,15 @@ class SctpClientPipelineSink extends AbstractChannelSink { } break; case INTEREST_OPS: - channel.worker.setInterestOps(channel, future, ((Integer) value).intValue()); + if (event instanceof SctpBindAddressEvent) { + SctpBindAddressEvent bindAddressEvent = (SctpBindAddressEvent) event; + bindAddress(channel, bindAddressEvent.getFuture(), bindAddressEvent.getValue()); + } else if (event instanceof SctpUnbindAddressEvent) { + SctpUnbindAddressEvent unbindAddressEvent = (SctpUnbindAddressEvent) event; + unbindAddress(channel, unbindAddressEvent.getFuture(), unbindAddressEvent.getValue()); + } else { + channel.worker.setInterestOps(channel, future, ((Integer) value).intValue()); + } break; } } else if (e instanceof MessageEvent) { @@ -125,6 +134,32 @@ class SctpClientPipelineSink extends AbstractChannelSink { } } + private void bindAddress( + SctpClientChannel channel, ChannelFuture future, + InetAddress localAddress) { + try { + channel.channel.bindAddress(localAddress); + future.setSuccess(); + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } + + private void unbindAddress( + SctpClientChannel channel, ChannelFuture future, + InetAddress localAddress) { + try { + channel.channel.unbindAddress(localAddress); + future.setSuccess(); + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } + + + private void connect( final SctpClientChannel channel, final ChannelFuture cf, SocketAddress remoteAddress) { @@ -289,8 +324,10 @@ class SctpClientPipelineSink extends AbstractChannelSink { try { selector.close(); } catch (IOException e) { - logger.warn( - "Failed to close a selector.", e); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to close a selector.", e); + } } finally { this.selector = null; } @@ -307,8 +344,10 @@ class SctpClientPipelineSink extends AbstractChannelSink { shutdown = false; } } catch (Throwable t) { - logger.warn( - "Unexpected exception in the selector loop.", t); + if (logger.isWarnEnabled()) { + logger.warn( + "Unexpected exception in the selector loop.", t); + } // Prevent possible consecutive immediate failures. try { diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpPayload.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpFrame.java similarity index 59% rename from transport-sctp/src/main/java/io/netty/channel/sctp/SctpPayload.java rename to transport-sctp/src/main/java/io/netty/channel/sctp/SctpFrame.java index e773a34083..3a12600f4c 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpPayload.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpFrame.java @@ -15,25 +15,36 @@ */ package io.netty.channel.sctp; +import com.sun.nio.sctp.MessageInfo; import io.netty.buffer.ChannelBuffer; import io.netty.buffer.ChannelBuffers; /** */ -public final class SctpPayload { +public final class SctpFrame { private final int streamIdentifier; private final int protocolIdentifier; + private final ChannelBuffer payloadBuffer; + private MessageInfo msgInfo; + /** * Essential data that is being carried within SCTP Data Chunk - * @param streamIdentifier that you want to send the payload * @param protocolIdentifier of payload + * @param streamIdentifier that you want to send the payload * @param payloadBuffer channel buffer */ - public SctpPayload(int streamIdentifier, int protocolIdentifier, ChannelBuffer payloadBuffer) { - this.streamIdentifier = streamIdentifier; + public SctpFrame(int protocolIdentifier, int streamIdentifier, ChannelBuffer payloadBuffer) { this.protocolIdentifier = protocolIdentifier; + this.streamIdentifier = streamIdentifier; + this.payloadBuffer = payloadBuffer; + } + + public SctpFrame(MessageInfo msgInfo, ChannelBuffer payloadBuffer) { + this.msgInfo = msgInfo; + this.streamIdentifier = msgInfo.streamNumber(); + this.protocolIdentifier = msgInfo.payloadProtocolID(); this.payloadBuffer = payloadBuffer; } @@ -53,10 +64,49 @@ public final class SctpPayload { } } + public MessageInfo getMessageInfo() { + return msgInfo; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + SctpFrame sctpFrame = (SctpFrame) o; + + if (protocolIdentifier != sctpFrame.protocolIdentifier) { + return false; + } + + if (streamIdentifier != sctpFrame.streamIdentifier) { + return false; + } + + if (!payloadBuffer.equals(sctpFrame.payloadBuffer)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = streamIdentifier; + result = 31 * result + protocolIdentifier; + result = 31 * result + payloadBuffer.hashCode(); + return result; + } + @Override public String toString() { return new StringBuilder(). - append("SctpPayload{"). + append("SctpFrame{"). append("streamIdentifier="). append(streamIdentifier). append(", protocolIdentifier="). diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpNotificationEvent.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpNotificationEvent.java index 9835dd24c7..0e209f0430 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpNotificationEvent.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpNotificationEvent.java @@ -62,4 +62,13 @@ public class SctpNotificationEvent implements ChannelEvent { public Object getValue() { return value; } + + @Override + public String toString() { + return "SctpNotificationEvent{" + + "channel=" + channel + + ", notification=" + notification + + ", value=" + value + + '}'; + } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpNotificationHandler.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpNotificationHandler.java index 0fe6ab4094..83f5e53910 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpNotificationHandler.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpNotificationHandler.java @@ -23,24 +23,20 @@ import com.sun.nio.sctp.PeerAddressChangeNotification; import com.sun.nio.sctp.SendFailedNotification; import com.sun.nio.sctp.ShutdownNotification; +import io.netty.channel.ChannelPipeline; import io.netty.channel.Channels; -import io.netty.logging.InternalLogger; -import io.netty.logging.InternalLoggerFactory; /** */ class SctpNotificationHandler extends AbstractNotificationHandler { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(SctpNotificationHandler.class); - private final SctpChannelImpl sctpChannel; - private final SctpWorker sctpWorker; + private final ChannelPipeline pipeline; - public SctpNotificationHandler(SctpChannelImpl sctpChannel, SctpWorker sctpWorker) { + SctpNotificationHandler(SctpChannelImpl sctpChannel) { this.sctpChannel = sctpChannel; - this.sctpWorker = sctpWorker; + this.pipeline = sctpChannel.getPipeline(); } @Override @@ -49,12 +45,6 @@ class SctpNotificationHandler extends AbstractNotificationHandler { return HandlerResult.CONTINUE; } - @Override - public HandlerResult handleNotification(Notification notification, Object o) { - fireNotificationReceived(notification, o); - return HandlerResult.CONTINUE; - } - @Override public HandlerResult handleNotification(PeerAddressChangeNotification notification, Object o) { fireNotificationReceived(notification, o); @@ -69,11 +59,11 @@ class SctpNotificationHandler extends AbstractNotificationHandler { @Override public HandlerResult handleNotification(ShutdownNotification notification, Object o) { - sctpWorker.close(sctpChannel, Channels.succeededFuture(sctpChannel)); + Channels.fireChannelDisconnected(sctpChannel); return HandlerResult.RETURN; } private void fireNotificationReceived(Notification notification, Object o) { - sctpChannel.getPipeline().sendUpstream(new SctpNotificationEvent(sctpChannel, notification, o)); + pipeline.sendUpstream(new SctpNotificationEvent(sctpChannel, notification, o)); } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpProviderMetadata.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpProviderMetadata.java index 51902620c8..cafe5c30b4 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpProviderMetadata.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpProviderMetadata.java @@ -61,8 +61,10 @@ final class SctpProviderMetadata { } if (constraintLevel >= 0) { - logger.debug( - "Setting the NIO constraint level to: " + constraintLevel); + if (logger.isDebugEnabled()) { + logger.debug( + "Setting the NIO constraint level to: " + constraintLevel); + } } if (constraintLevel < 0) { @@ -70,18 +72,24 @@ final class SctpProviderMetadata { if (constraintLevel < 0) { constraintLevel = 2; - logger.debug( - "Couldn't determine the NIO constraint level from " + - "the system properties; using the safest level (2)"); + if (logger.isDebugEnabled()) { + logger.debug( + "Couldn't determine the NIO constraint level from " + + "the system properties; using the safest level (2)"); + } } else if (constraintLevel != 0) { - logger.info( - "Using the autodetected NIO constraint level: " + - constraintLevel + - " (Use better NIO provider for better performance)"); + if (logger.isInfoEnabled()) { + logger.info( + "Using the autodetected NIO constraint level: " + + constraintLevel + + " (Use better NIO provider for better performance)"); + } } else { - logger.debug( - "Using the autodetected NIO constraint level: " + - constraintLevel); + if (logger.isDebugEnabled()) { + logger.debug( + "Using the autodetected NIO constraint level: " + + constraintLevel); + } } } @@ -236,7 +244,9 @@ final class SctpProviderMetadata { ch.bind(new InetSocketAddress(0)); ch.configureBlocking(false); } catch (Throwable e) { - logger.warn("Failed to configure a temporary socket.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to configure a temporary socket.", e); + } return -1; } @@ -244,7 +254,9 @@ final class SctpProviderMetadata { try { loop = new SelectorLoop(); } catch (Throwable e) { - logger.warn("Failed to open a temporary selector.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to open a temporary selector.", e); + } return -1; } @@ -252,7 +264,9 @@ final class SctpProviderMetadata { try { ch.register(loop.selector, 0); } catch (Throwable e) { - logger.warn("Failed to register a temporary selector.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to register a temporary selector.", e); + } return -1; } @@ -338,7 +352,9 @@ final class SctpProviderMetadata { try { ch.close(); } catch (Throwable e) { - logger.warn("Failed to close a temporary socket.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to close a temporary socket.", e); + } } } @@ -368,7 +384,9 @@ final class SctpProviderMetadata { try { loop.selector.close(); } catch (Throwable e) { - logger.warn("Failed to close a temporary selector.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to close a temporary selector.", e); + } } } } @@ -406,7 +424,9 @@ final class SctpProviderMetadata { } keys.clear(); } catch (IOException e) { - logger.warn("Failed to wait for a temporary selector.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to wait for a temporary selector.", e); + } } } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpSendBufferPool.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpSendBufferPool.java index 07d5adc8fd..382e5b4fc4 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpSendBufferPool.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpSendBufferPool.java @@ -39,15 +39,15 @@ final class SctpSendBufferPool { } SendBuffer acquire(Object message) { - if (message instanceof SctpPayload) { - return acquire((SctpPayload) message); + if (message instanceof SctpFrame) { + return acquire((SctpFrame) message); } else { throw new IllegalArgumentException( - "unsupported message type: " + message.getClass()); + "unsupported message type: " + message.getClass() + " required: io.netty.channel.sctp.SctpFrame"); } } - private SendBuffer acquire(SctpPayload message) { + private SendBuffer acquire(SctpFrame message) { final ChannelBuffer src = message.getPayloadBuffer(); final int streamNo = message.getStreamIdentifier(); final int protocolId = message.getProtocolIdentifier(); diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerChannel.java index 2fb186cb98..3257f080d6 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerChannel.java @@ -15,8 +15,10 @@ */ package io.netty.channel.sctp; +import io.netty.channel.ChannelFuture; import io.netty.channel.ServerChannel; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.Set; @@ -24,6 +26,17 @@ import java.util.Set; * A SCTP {@link io.netty.channel.ServerChannel} which accepts incoming SCTP connections. */ public interface SctpServerChannel extends ServerChannel { + /** + * Bind a multi-homing address to the already bound channel + */ + ChannelFuture bindAddress(InetAddress localAddress); + + + /** + * Unbind a multi-homing address from a already established channel + */ + ChannelFuture unbindAddress(InetAddress localAddress); + /** * Returns the configuration of this channel. */ diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerChannelImpl.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerChannelImpl.java index 335be780bb..142b1ae83f 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerChannelImpl.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerChannelImpl.java @@ -18,6 +18,7 @@ package io.netty.channel.sctp; import static io.netty.channel.Channels.*; import java.io.IOException; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.channels.Selector; @@ -31,6 +32,7 @@ import java.util.concurrent.locks.ReentrantLock; import io.netty.channel.AbstractServerChannel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFactory; +import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelSink; import io.netty.logging.InternalLogger; @@ -62,7 +64,7 @@ class SctpServerChannelImpl extends AbstractServerChannel serverChannel = com.sun.nio.sctp.SctpServerChannel.open(); } catch (IOException e) { throw new ChannelException( - "Failed to open a server socket.", e); + "Failed to open a server sctp channel.", e); } try { @@ -71,8 +73,10 @@ class SctpServerChannelImpl extends AbstractServerChannel try { serverChannel.close(); } catch (IOException e2) { - logger.warn( - "Failed to close a partially initialized socket.", e2); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to close a partially initialized socket.", e2); + } } throw new ChannelException("Failed to enter non-blocking mode.", e); @@ -83,6 +87,20 @@ class SctpServerChannelImpl extends AbstractServerChannel fireChannelOpen(this); } + @Override + public ChannelFuture bindAddress(InetAddress localAddress) { + ChannelFuture future = future(this); + getPipeline().sendDownstream(new SctpBindAddressEvent(this, future, localAddress)); + return future; + } + + @Override + public ChannelFuture unbindAddress(InetAddress localAddress) { + ChannelFuture future = future(this); + getPipeline().sendDownstream(new SctpUnbindAddressEvent(this, future, localAddress)); + return future; + } + @Override public SctpServerChannelConfig getConfig() { return config; diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerPipelineSink.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerPipelineSink.java index d39ca7b688..3a0f86bb16 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerPipelineSink.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerPipelineSink.java @@ -18,6 +18,7 @@ package io.netty.channel.sctp; import static io.netty.channel.Channels.*; import java.io.IOException; +import java.net.InetAddress; import java.net.SocketAddress; import java.net.SocketTimeoutException; import java.nio.channels.CancelledKeyException; @@ -94,6 +95,16 @@ class SctpServerPipelineSink extends AbstractChannelSink { } else { close(channel, future); } + case INTEREST_OPS: + if (event instanceof SctpBindAddressEvent) { + SctpBindAddressEvent bindAddressEvent = (SctpBindAddressEvent) event; + bindAddress(channel, bindAddressEvent.getFuture(), bindAddressEvent.getValue()); + } + + if (event instanceof SctpUnbindAddressEvent) { + SctpUnbindAddressEvent unbindAddressEvent = (SctpUnbindAddressEvent) event; + unbindAddress(channel, unbindAddressEvent.getFuture(), unbindAddressEvent.getValue()); + } break; } } @@ -158,6 +169,30 @@ class SctpServerPipelineSink extends AbstractChannelSink { } } + private void bindAddress( + SctpServerChannelImpl channel, ChannelFuture future, + InetAddress localAddress) { + try { + channel.serverChannel.bindAddress(localAddress); + future.setSuccess(); + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } + + private void unbindAddress( + SctpServerChannelImpl channel, ChannelFuture future, + InetAddress localAddress) { + try { + channel.serverChannel.unbindAddress(localAddress); + future.setSuccess(); + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } + private void close(SctpServerChannelImpl channel, ChannelFuture future) { boolean bound = channel.isBound(); try { @@ -246,8 +281,10 @@ class SctpServerPipelineSink extends AbstractChannelSink { // Closed as requested. break; } catch (Throwable e) { - logger.warn( - "Failed to accept a connection.", e); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to accept a connection.", e); + } try { Thread.sleep(1000); } catch (InterruptedException e1) { @@ -271,14 +308,18 @@ class SctpServerPipelineSink extends AbstractChannelSink { SctpServerPipelineSink.this, acceptedSocket, worker, currentThread), null); } catch (Exception e) { - logger.warn( - "Failed to initialize an accepted socket.", e); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to initialize an accepted socket.", e); + } try { acceptedSocket.close(); } catch (IOException e2) { - logger.warn( - "Failed to close a partially accepted socket.", - e2); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to close a partially accepted socket.", + e2); + } } } } @@ -288,7 +329,9 @@ class SctpServerPipelineSink extends AbstractChannelSink { try { selector.close(); } catch (Exception e) { - logger.warn("Failed to close a selector.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to close a selector.", e); + } } } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpUnbindAddressEvent.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpUnbindAddressEvent.java new file mode 100644 index 0000000000..f6d7e828bc --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpUnbindAddressEvent.java @@ -0,0 +1,38 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.channel.sctp; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelState; +import io.netty.channel.DownstreamChannelStateEvent; + +import java.net.InetAddress; + +public class SctpUnbindAddressEvent extends DownstreamChannelStateEvent { + + /** + * Creates a new instance. + */ + public SctpUnbindAddressEvent(Channel channel, ChannelFuture future, InetAddress value) { + super(channel, future, ChannelState.INTEREST_OPS, value); + } + + @Override + public InetAddress getValue() { + return (InetAddress) super.getValue(); + } +} diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpWorker.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpWorker.java index e18d6b85df..a7878d77ba 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpWorker.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpWorker.java @@ -86,7 +86,7 @@ class SctpWorker implements Runnable { boolean server = !(channel instanceof SctpClientChannel); Runnable registerTask = new RegisterTask(channel, future, server); - notificationHandler = new SctpNotificationHandler(channel, this); + notificationHandler = new SctpNotificationHandler(channel); Selector selector; synchronized (startStopLock) { @@ -110,7 +110,9 @@ class SctpWorker implements Runnable { try { selector.close(); } catch (Throwable t) { - logger.warn("Failed to close a selector.", t); + if (logger.isWarnEnabled()) { + logger.warn("Failed to close a selector.", t); + } } this.selector = selector = null; // The method will return to the caller at this point. @@ -204,8 +206,10 @@ class SctpWorker implements Runnable { try { selector.close(); } catch (IOException e) { - logger.warn( - "Failed to close a selector.", e); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to close a selector.", e); + } } finally { this.selector = null; } @@ -222,9 +226,10 @@ class SctpWorker implements Runnable { shutdown = false; } } catch (Throwable t) { - logger.warn( - "Unexpected exception in the selector loop.", t); - + if (logger.isWarnEnabled()) { + logger.warn( + "Unexpected exception in the selector loop.", t); + } // Prevent possible consecutive immediate failures that lead to // excessive CPU consumption. try { @@ -310,10 +315,12 @@ class SctpWorker implements Runnable { messageInfo = channel.channel.receive(bb, null, notificationHandler); if (messageInfo != null) { messageReceived = true; - if (messageInfo.isComplete()) { + if (!messageInfo.isUnordered()) { failure = false; } else { - logger.error("Received incomplete sctp packet, can not continue! Expected SCTP_EXPLICIT_COMPLETE message"); + if (logger.isErrorEnabled()) { + logger.error("Received unordered SCTP Packet"); + } failure = true; } } else { @@ -343,9 +350,7 @@ class SctpWorker implements Runnable { // Fire the event. fireMessageReceived(channel, - new SctpPayload(messageInfo.streamNumber(), - messageInfo.payloadProtocolID(), - buffer), + new SctpFrame(messageInfo, buffer), messageInfo.address()); } else { recvBufferPool.release(bb); @@ -764,8 +769,8 @@ class SctpWorker implements Runnable { channel.channel.register( selector, channel.getRawInterestOps(), channel); } + channel.setConnected(); if (future != null) { - channel.setConnected(); future.setSuccess(); } } catch (IOException e) { diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SelectorUtil.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SelectorUtil.java index 500b5473e1..77c4035cb5 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SelectorUtil.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SelectorUtil.java @@ -32,10 +32,13 @@ final class SelectorUtil { try { selector.select(10); // does small timeout give more throughput + less CPU usage? } catch (CancelledKeyException e) { - // Harmless exception - log anyway - logger.debug( - CancelledKeyException.class.getSimpleName() + - " raised by a Selector - JDK bug?", e); + if (logger.isDebugEnabled()) { + // Harmless exception - log anyway + logger.debug( + CancelledKeyException.class.getSimpleName() + + " raised by a Selector - JDK bug?", e); + } + } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/codec/DefaultInboundStreamFilter.java b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/DefaultInboundStreamFilter.java new file mode 100644 index 0000000000..ae8ae6e35a --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/DefaultInboundStreamFilter.java @@ -0,0 +1,26 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.channel.sctp.codec; + +import io.netty.channel.sctp.SctpChannel; +import io.netty.channel.sctp.SctpFrame; + +public class DefaultInboundStreamFilter implements InboundStreamFilter { + @Override + public boolean filter(SctpChannel sctpChannel, SctpFrame sctpFrame) { + return true; + } +} diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/codec/DefaultOutboundStreamSelector.java b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/DefaultOutboundStreamSelector.java new file mode 100644 index 0000000000..c712d2052f --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/DefaultOutboundStreamSelector.java @@ -0,0 +1,25 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.channel.sctp.codec; + +import io.netty.channel.sctp.SctpChannel; + +public class DefaultOutboundStreamSelector implements OutboundStreamSelector { + @Override + public int streamIdentifier(SctpChannel sctpChannel, Object msg) { + return 1; + } +} diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/codec/InboundStreamFilter.java b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/InboundStreamFilter.java new file mode 100644 index 0000000000..a28beb8111 --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/InboundStreamFilter.java @@ -0,0 +1,24 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.channel.sctp.codec; + +import io.netty.channel.sctp.SctpChannel; +import io.netty.channel.sctp.SctpFrame; + +public interface InboundStreamFilter { + + boolean filter(SctpChannel sctpChannel, SctpFrame sctpFrame); +} diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/codec/OutboundStreamSelector.java b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/OutboundStreamSelector.java new file mode 100644 index 0000000000..188e1eb214 --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/OutboundStreamSelector.java @@ -0,0 +1,22 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.channel.sctp.codec; + +import io.netty.channel.sctp.SctpChannel; + +public interface OutboundStreamSelector { + int streamIdentifier(SctpChannel sctpChannel, Object msg); +} diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/codec/SctpFrameDecoder.java b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/SctpFrameDecoder.java new file mode 100644 index 0000000000..b3e6a9ca02 --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/SctpFrameDecoder.java @@ -0,0 +1,76 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.channel.sctp.codec; + +import io.netty.buffer.ChannelBuffer; +import io.netty.buffer.ChannelBuffers; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.sctp.SctpChannel; +import io.netty.channel.sctp.SctpFrame; +import io.netty.handler.codec.oneone.OneToOneDecoder; + +/** + * SCTP Frame Decoder which extract payload channel buffer + * Note: Supported SCTP Frame Interleave Level - 0 + */ + +public class SctpFrameDecoder extends OneToOneDecoder { + + private final InboundStreamFilter inboundStreamFilter; + + private volatile ChannelBuffer cumulation; + + public SctpFrameDecoder() { + this.inboundStreamFilter = new DefaultInboundStreamFilter(); + } + + public SctpFrameDecoder(InboundStreamFilter inboundStreamFilter) { + this.inboundStreamFilter = inboundStreamFilter; + } + + @Override + protected Object decode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { + if (!(msg instanceof SctpFrame)) { + return msg; + } + final SctpChannel sctpChannel = (SctpChannel) channel; + final SctpFrame sctpFrame = (SctpFrame) msg; + + if (inboundStreamFilter.filter(sctpChannel, sctpFrame)) { + + final boolean complete = sctpFrame.getMessageInfo().isComplete(); + if (complete) { + if (cumulation == null) { + return sctpFrame.getPayloadBuffer(); + } else { + final ChannelBuffer extractedFrame = ChannelBuffers.wrappedBuffer(cumulation, sctpFrame.getPayloadBuffer()); + cumulation = null; + return extractedFrame; + } + } else { + if (cumulation == null) { + cumulation = sctpFrame.getPayloadBuffer(); + } else { + cumulation = ChannelBuffers.wrappedBuffer(cumulation, sctpFrame.getPayloadBuffer()); + } + return null; + } + } else { + return msg; + } + } +} diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/codec/SctpFrameEncoder.java b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/SctpFrameEncoder.java new file mode 100644 index 0000000000..96dd2b242a --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/SctpFrameEncoder.java @@ -0,0 +1,59 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.channel.sctp.codec; + +import io.netty.buffer.ChannelBuffer; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.sctp.SctpChannel; +import io.netty.channel.sctp.SctpFrame; +import io.netty.handler.codec.oneone.OneToOneEncoder; + +/** + * SCTP Frame Encoder which encode a channel buffer to SctpFrame object + */ +public class SctpFrameEncoder extends OneToOneEncoder { + + private final int protocolIdentifier; + private final OutboundStreamSelector sctpWriteStreamSelector; + + public SctpFrameEncoder() { + this(0); + } + + public SctpFrameEncoder(int protocolIdentifier) { + this(protocolIdentifier, new DefaultOutboundStreamSelector()); + } + + public SctpFrameEncoder(final int protocolIdentifier, final OutboundStreamSelector sctpWriteStreamSelector) { + if (sctpWriteStreamSelector == null) { + throw new NullPointerException("sctpWriteStreamSelector"); + } + this.protocolIdentifier = Math.max(0, protocolIdentifier); + this.sctpWriteStreamSelector = sctpWriteStreamSelector; + } + + @Override + protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { + if (!(msg instanceof ChannelBuffer)) { + return msg; + } else { + SctpChannel sctpChannel = (SctpChannel) channel; + final int streamIdentifier = sctpWriteStreamSelector.streamIdentifier(sctpChannel, msg); + return new SctpFrame(protocolIdentifier, streamIdentifier, (ChannelBuffer) msg); + } + } +} diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/codec/package-info.java b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/package-info.java new file mode 100644 index 0000000000..d9fd0907d2 --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/codec/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.channel.sctp.codec; diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpChannelHandler.java b/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpChannelHandler.java new file mode 100644 index 0000000000..1a14464999 --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpChannelHandler.java @@ -0,0 +1,50 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.channel.sctp.handler; + +import io.netty.channel.ChannelEvent; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelHandler; +import io.netty.channel.sctp.SctpNotificationEvent; +import io.netty.logging.InternalLogger; +import io.netty.logging.InternalLoggerFactory; + +/** + * SCTP Channel Handler (upstream + downstream) with SCTP notification handling + */ + +public class SimpleSctpChannelHandler extends SimpleChannelHandler { + + private static final InternalLogger logger = + InternalLoggerFactory.getInstance(SimpleSctpUpstreamHandler.class.getName()); + + + @Override + public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent event) throws Exception { + if (!(event instanceof SctpNotificationEvent)) { + super.handleUpstream(ctx, event); + + } + if (event instanceof SctpNotificationEvent) { + sctpNotificationReceived(ctx, (SctpNotificationEvent) event); + } + } + + public void sctpNotificationReceived(ChannelHandlerContext ctx, SctpNotificationEvent event) { + ctx.sendUpstream(event); + } + +} diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpDownstreamHandler.java b/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpDownstreamHandler.java new file mode 100644 index 0000000000..7a70491a37 --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpDownstreamHandler.java @@ -0,0 +1,24 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.channel.sctp.handler; + +import io.netty.channel.SimpleChannelDownstreamHandler; + +/** + * Sctp Downstream handler for sake of completeness + */ +public class SimpleSctpDownstreamHandler extends SimpleChannelDownstreamHandler { +} diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpUpstreamHandler.java b/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpUpstreamHandler.java new file mode 100644 index 0000000000..cf69c86301 --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpUpstreamHandler.java @@ -0,0 +1,47 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.channel.sctp.handler; + +import io.netty.channel.ChannelEvent; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelUpstreamHandler; +import io.netty.channel.sctp.SctpNotificationEvent; +import io.netty.logging.InternalLogger; +import io.netty.logging.InternalLoggerFactory; + +/** + * SCTP Upstream Channel Handler with SCTP notification handling + */ +public class SimpleSctpUpstreamHandler extends SimpleChannelUpstreamHandler { + private static final InternalLogger logger = + InternalLoggerFactory.getInstance(SimpleSctpUpstreamHandler.class.getName()); + + + @Override + public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent event) throws Exception { + if (!(event instanceof SctpNotificationEvent)) { + super.handleUpstream(ctx, event); + + } + if (event instanceof SctpNotificationEvent) { + sctpNotificationReceived(ctx, (SctpNotificationEvent) event); + } + } + + public void sctpNotificationReceived(ChannelHandlerContext ctx, SctpNotificationEvent event) { + ctx.sendUpstream(event); + } +} diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/handler/package-info.java b/transport-sctp/src/main/java/io/netty/channel/sctp/handler/package-info.java new file mode 100644 index 0000000000..790d7ec30b --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/handler/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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. + */ + +/** + * NIO-based socket channel + * API implementation - recommended for a large number of connections (>= 1000). + */ +package io.netty.channel.sctp.handler; diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/package-info.java b/transport-sctp/src/main/java/io/netty/channel/sctp/package-info.java index d99e070321..8da61a048b 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/package-info.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/package-info.java @@ -15,7 +15,6 @@ */ /** - * NIO-based socket channel - * API implementation - recommended for a large number of connections (>= 1000). + * NIO -based SCTP channel API implementation. */ package io.netty.channel.sctp; diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketClientBootstrapTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketClientBootstrapTest.java new file mode 100644 index 0000000000..e8ca6d7fd7 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketClientBootstrapTest.java @@ -0,0 +1,192 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport; + +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpServerChannel; +import io.netty.bootstrap.ClientBootstrap; +import io.netty.channel.ChannelFactory; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelPipelineException; +import io.netty.channel.ChannelPipelineFactory; +import io.netty.channel.sctp.codec.SctpFrameDecoder; +import io.netty.channel.sctp.codec.SctpFrameEncoder; +import io.netty.testsuite.util.DummyHandler; +import io.netty.testsuite.util.SctpSocketAddresses; +import io.netty.util.internal.ExecutorUtil; +import org.easymock.EasyMock; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.Iterator; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + + +/** + * An abstract test class to test socket client bootstraps + */ +public abstract class AbstractSocketClientBootstrapTest { + + private static ExecutorService executor; + + @BeforeClass + public static void init() { + executor = Executors.newCachedThreadPool(); + } + + @AfterClass + public static void destroy() { + ExecutorUtil.terminate(executor); + } + + protected abstract ChannelFactory newClientSocketChannelFactory(Executor executor); + + @Test(timeout = 10000) + public void testFailedConnectionAttempt() throws Exception { + ClientBootstrap bootstrap = new ClientBootstrap(); + bootstrap.setFactory(newClientSocketChannelFactory(executor)); + bootstrap.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + bootstrap.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + bootstrap.getPipeline().addLast("dummy", new DummyHandler()); + bootstrap.setOption("remoteAddress", new InetSocketAddress("255.255.255.255", 1)); + ChannelFuture future = bootstrap.connect(); + future.awaitUninterruptibly(); + assertFalse(future.isSuccess()); + assertTrue(future.getCause() instanceof IOException); + } + + @Test(timeout = 10000) + public void testSuccessfulConnectionAttempt() throws Throwable { + SctpServerChannel serverChannel = SctpServerChannel.open(); + serverChannel.bind(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 0)); + + try { + serverChannel.configureBlocking(false); + + final Iterator serverAddresses = serverChannel.getAllLocalAddresses().iterator(); + InetSocketAddress serverAddress = (InetSocketAddress) serverAddresses.next(); + int serverPort = serverAddress.getPort(); + + ClientBootstrap bootstrap = + new ClientBootstrap(newClientSocketChannelFactory(executor)); + + bootstrap.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + bootstrap.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + bootstrap.getPipeline().addLast("dummy", new DummyHandler()); + bootstrap.setOption( + "remoteAddress", + new InetSocketAddress( + SctpSocketAddresses.LOOP_BACK, + serverPort)); + + ChannelFuture future = bootstrap.connect(); + serverChannel.accept(); + future.awaitUninterruptibly(); + + if (future.getCause() != null) { + throw future.getCause(); + } + assertTrue(future.isSuccess()); + + future.getChannel().close().awaitUninterruptibly(); + } finally { + try { + serverChannel.close(); + } catch (IOException e) { + // Ignore. + } + } + } + + @Test(timeout = 10000) + public void testSuccessfulConnectionAttemptWithLocalAddress() throws Throwable { + SctpServerChannel serverChannel = SctpServerChannel.open(); + + try { + serverChannel.configureBlocking(false); + serverChannel = serverChannel.bind(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 0)); + + final Iterator serverAddresses = serverChannel.getAllLocalAddresses().iterator(); + InetSocketAddress serverAddress = (InetSocketAddress) serverAddresses.next(); + int serverPort = serverAddress.getPort(); + ClientBootstrap bootstrap = + new ClientBootstrap(newClientSocketChannelFactory(executor)); + + bootstrap.getPipeline().addLast("dummy", new DummyHandler()); + bootstrap.setOption( + "remoteAddress", + new InetSocketAddress( + SctpSocketAddresses.LOOP_BACK, + serverPort)); + bootstrap.setOption("localAddress", new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 0)); + + ChannelFuture future = bootstrap.connect(); + serverChannel.accept(); + future.awaitUninterruptibly(); + + if (future.getCause() != null) { + throw future.getCause(); + } + assertTrue(future.isSuccess()); + + future.getChannel().close().awaitUninterruptibly(); + } finally { + try { + serverChannel.close(); + } catch (IOException e) { + // Ignore. + } + } + } + + @Test(expected = ChannelPipelineException.class) + public void testFailedPipelineInitialization() throws Exception { + ClientBootstrap bootstrap = new ClientBootstrap(EasyMock.createMock(ChannelFactory.class)); + ChannelPipelineFactory pipelineFactory = EasyMock.createMock(ChannelPipelineFactory.class); + bootstrap.setPipelineFactory(pipelineFactory); + + EasyMock.expect(pipelineFactory.getPipeline()).andThrow(new ChannelPipelineException()); + EasyMock.replay(pipelineFactory); + + bootstrap.connect(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 1)); + } + + @Test(expected = IllegalStateException.class) + public void shouldHaveRemoteAddressOption() { + new ClientBootstrap(EasyMock.createMock(ChannelFactory.class)).connect(); + } + + + @Test(expected = NullPointerException.class) + public void shouldDisallowNullRemoteAddressParameter1() { + new ClientBootstrap(EasyMock.createMock(ChannelFactory.class)).connect(null); + } + + @Test(expected = NullPointerException.class) + public void shouldDisallowNullRemoteAddressParameter2() { + new ClientBootstrap(EasyMock.createMock(ChannelFactory.class)).connect(null, null); + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketCompatibleObjectStreamEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketCompatibleObjectStreamEchoTest.java new file mode 100644 index 0000000000..eb2f2d424d --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketCompatibleObjectStreamEchoTest.java @@ -0,0 +1,190 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport; + +import io.netty.bootstrap.ClientBootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +import io.netty.channel.sctp.codec.SctpFrameDecoder; +import io.netty.channel.sctp.codec.SctpFrameEncoder; +import io.netty.handler.codec.serialization.CompatibleObjectDecoder; +import io.netty.handler.codec.serialization.CompatibleObjectEncoder; +import io.netty.testsuite.util.SctpSocketAddresses; +import io.netty.util.internal.ExecutorUtil; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Random; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public abstract class AbstractSocketCompatibleObjectStreamEchoTest { + + static final Random random = new Random(); + static final String[] data = new String[512];//could not test with jumbo sctp frame + + private static ExecutorService executor; + + static { + for (int i = 0; i < data.length; i ++) { + int eLen = random.nextInt(512); + char[] e = new char[eLen]; + for (int j = 0; j < eLen; j ++) { + e[j] = (char) ('a' + random.nextInt(26)); + } + + data[i] = new String(e); + } + } + + @BeforeClass + public static void init() { + executor = Executors.newCachedThreadPool(); + } + + @AfterClass + public static void destroy() { + ExecutorUtil.terminate(executor); + } + + protected abstract ChannelFactory newServerSocketChannelFactory(Executor executor); + protected abstract ChannelFactory newClientSocketChannelFactory(Executor executor); + + @Test + @SuppressWarnings("deprecation") + public void testCompatibleObjectEcho() throws Throwable { + ServerBootstrap sb = new ServerBootstrap(newServerSocketChannelFactory(executor)); + ClientBootstrap cb = new ClientBootstrap(newClientSocketChannelFactory(executor)); + + EchoHandler sh = new EchoHandler(); + EchoHandler ch = new EchoHandler(); + + sb.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + sb.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + sb.getPipeline().addLast("decoder", new CompatibleObjectDecoder()); + sb.getPipeline().addLast("encoder", new CompatibleObjectEncoder()); + sb.getPipeline().addLast("handler", sh); + + cb.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + cb.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + cb.getPipeline().addLast("decoder", new CompatibleObjectDecoder()); + cb.getPipeline().addLast("encoder", new CompatibleObjectEncoder()); + cb.getPipeline().addLast("handler", ch); + + Channel sc = sb.bind(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 0)); + int port = ((InetSocketAddress) sc.getLocalAddress()).getPort(); + + ChannelFuture ccf = cb.connect(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, port)); + assertTrue(ccf.awaitUninterruptibly().isSuccess()); + + Channel cc = ccf.getChannel(); + for (String element : data) { + cc.write(element); + } + + while (ch.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // Ignore. + } + } + + while (sh.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // Ignore. + } + } + + sh.channel.close().awaitUninterruptibly(); + ch.channel.close().awaitUninterruptibly(); + sc.close().awaitUninterruptibly(); + + if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) { + throw sh.exception.get(); + } + if (ch.exception.get() != null && !(ch.exception.get() instanceof IOException)) { + throw ch.exception.get(); + } + if (sh.exception.get() != null) { + throw sh.exception.get(); + } + if (ch.exception.get() != null) { + throw ch.exception.get(); + } + } + + private static class EchoHandler extends SimpleChannelUpstreamHandler { + volatile Channel channel; + final AtomicReference exception = new AtomicReference(); + volatile int counter; + + EchoHandler() { + } + + @Override + public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) + throws Exception { + channel = e.getChannel(); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) + throws Exception { + + String m = (String) e.getMessage(); + assertEquals(data[counter], m); + + if (channel.getParent() != null) { + channel.write(m); + } + + counter ++; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) + throws Exception { + if (exception.compareAndSet(null, e.getCause())) { + e.getChannel().close(); + } + } + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketEchoTest.java new file mode 100644 index 0000000000..90821601e1 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketEchoTest.java @@ -0,0 +1,186 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport; + +import io.netty.bootstrap.ClientBootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ChannelBuffer; +import io.netty.buffer.ChannelBuffers; +import io.netty.channel.*; +import io.netty.channel.sctp.codec.SctpFrameDecoder; +import io.netty.channel.sctp.codec.SctpFrameEncoder; +import io.netty.testsuite.util.SctpSocketAddresses; +import io.netty.util.internal.ExecutorUtil; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Random; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public abstract class AbstractSocketEchoTest { + + private static final Random random = new Random(); + static final byte[] data = new byte[4096];//could not test ultra jumbo frames + + private static ExecutorService executor; + + static { + random.nextBytes(data); + } + + @BeforeClass + public static void init() { + executor = Executors.newCachedThreadPool(); + } + + @AfterClass + public static void destroy() { + ExecutorUtil.terminate(executor); + } + + protected abstract ChannelFactory newServerSocketChannelFactory(Executor executor); + protected abstract ChannelFactory newClientSocketChannelFactory(Executor executor); + + @Test + public void testSimpleEcho() throws Throwable { + ServerBootstrap sb = new ServerBootstrap(newServerSocketChannelFactory(executor)); + + ClientBootstrap cb = new ClientBootstrap(newClientSocketChannelFactory(executor)); + + + EchoHandler sh = new EchoHandler(); + EchoHandler ch = new EchoHandler(); + + sb.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + sb.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + sb.getPipeline().addLast("handler", sh); + + cb.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + cb.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + cb.getPipeline().addLast("handler", ch); + + Channel sc = sb.bind(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 0)); + int port = ((InetSocketAddress) sc.getLocalAddress()).getPort(); + + ChannelFuture ccf = cb.connect(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, port)); + assertTrue(ccf.awaitUninterruptibly().isSuccess()); + + Channel cc = ccf.getChannel(); + for (int i = 0; i < data.length;) { + int length = Math.min(random.nextInt(1024 * 64), data.length - i); + cc.write(ChannelBuffers.wrappedBuffer(data, i, length)); + i += length; + } + + while (ch.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(5); + } catch (InterruptedException e) { + // Ignore. + } + } + + while (sh.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(5); + } catch (InterruptedException e) { + // Ignore. + } + } + + sh.channel.close().awaitUninterruptibly(); + ch.channel.close().awaitUninterruptibly(); + sc.close().awaitUninterruptibly(); + + if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) { + throw sh.exception.get(); + } + if (ch.exception.get() != null && !(ch.exception.get() instanceof IOException)) { + throw ch.exception.get(); + } + if (sh.exception.get() != null) { + throw sh.exception.get(); + } + if (ch.exception.get() != null) { + throw ch.exception.get(); + } + } + + private static class EchoHandler extends SimpleChannelUpstreamHandler { + volatile Channel channel; + final AtomicReference exception = new AtomicReference(); + volatile int counter; + + EchoHandler() { + } + + @Override + public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) + throws Exception { + channel = e.getChannel(); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) + throws Exception { + ChannelBuffer m = (ChannelBuffer) e.getMessage(); + byte[] actual = new byte[m.readableBytes()]; + m.getBytes(0, actual); + + int lastIdx = counter; + for (int i = 0; i < actual.length; i ++) { + assertEquals(data[i + lastIdx], actual[i]); + } + + if (channel.getParent() != null) { + channel.write(m); + } + + counter += actual.length; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) + throws Exception { + if (exception.compareAndSet(null, e.getCause())) { + e.getChannel().close(); + } + } + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketFixedLengthEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketFixedLengthEchoTest.java new file mode 100644 index 0000000000..697d694bb1 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketFixedLengthEchoTest.java @@ -0,0 +1,190 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport; + +import io.netty.bootstrap.ClientBootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ChannelBuffer; +import io.netty.buffer.ChannelBuffers; +import io.netty.channel.*; +import io.netty.channel.sctp.codec.SctpFrameDecoder; +import io.netty.channel.sctp.codec.SctpFrameEncoder; +import io.netty.handler.codec.frame.FixedLengthFrameDecoder; +import io.netty.testsuite.util.SctpSocketAddresses; +import io.netty.util.internal.ExecutorUtil; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Random; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public abstract class AbstractSocketFixedLengthEchoTest { + + private static final Random random = new Random(); + static final byte[] data = new byte[1024];//could not test with jumbo frames + + private static ExecutorService executor; + + static { + random.nextBytes(data); + } + + @BeforeClass + public static void init() { + executor = Executors.newCachedThreadPool(); + } + + @AfterClass + public static void destroy() { + ExecutorUtil.terminate(executor); + } + + protected abstract ChannelFactory newServerSocketChannelFactory(Executor executor); + protected abstract ChannelFactory newClientSocketChannelFactory(Executor executor); + + @Test + public void testFixedLengthEcho() throws Throwable { + ServerBootstrap sb = new ServerBootstrap(newServerSocketChannelFactory(executor)); + ClientBootstrap cb = new ClientBootstrap(newClientSocketChannelFactory(executor)); + + EchoHandler sh = new EchoHandler(); + EchoHandler ch = new EchoHandler(); + + sb.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + sb.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + sb.getPipeline().addLast("decoder", new FixedLengthFrameDecoder(1024)); + sb.getPipeline().addAfter("decoder", "handler", sh); + + + cb.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + cb.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + cb.getPipeline().addLast("decoder", new FixedLengthFrameDecoder(1024)); + cb.getPipeline().addAfter("decoder", "handler", ch); + + Channel sc = sb.bind(new InetSocketAddress(0)); + int port = ((InetSocketAddress) sc.getLocalAddress()).getPort(); + + ChannelFuture ccf = cb.connect(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, port)); + assertTrue(ccf.awaitUninterruptibly().isSuccess()); + + Channel cc = ccf.getChannel(); + for (int i = 0; i < data.length;) { + int length = Math.min(random.nextInt(1024 * 3), data.length - i); + cc.write(ChannelBuffers.wrappedBuffer(data, i, length)); + i += length; + } + + while (ch.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // Ignore. + } + } + + while (sh.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // Ignore. + } + } + + sh.channel.close().awaitUninterruptibly(); + ch.channel.close().awaitUninterruptibly(); + sc.close().awaitUninterruptibly(); + + if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) { + throw sh.exception.get(); + } + if (ch.exception.get() != null && !(ch.exception.get() instanceof IOException)) { + throw ch.exception.get(); + } + if (sh.exception.get() != null) { + throw sh.exception.get(); + } + if (ch.exception.get() != null) { + throw ch.exception.get(); + } + } + + private static class EchoHandler extends SimpleChannelUpstreamHandler { + volatile Channel channel; + final AtomicReference exception = new AtomicReference(); + volatile int counter; + + EchoHandler() { + } + + @Override + public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) + throws Exception { + channel = e.getChannel(); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) + throws Exception { + ChannelBuffer m = (ChannelBuffer) e.getMessage(); + assertEquals(1024, m.readableBytes()); + + byte[] actual = new byte[m.readableBytes()]; + m.getBytes(0, actual); + + int lastIdx = counter; + for (int i = 0; i < actual.length; i ++) { + assertEquals(data[i + lastIdx], actual[i]); + } + + if (channel.getParent() != null) { + channel.write(m); + } + + counter += actual.length; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) + throws Exception { + if (exception.compareAndSet(null, e.getCause())) { + e.getChannel().close(); + } + } + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketObjectStreamEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketObjectStreamEchoTest.java new file mode 100644 index 0000000000..59102e3ce8 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketObjectStreamEchoTest.java @@ -0,0 +1,189 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport; + +import io.netty.bootstrap.ClientBootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +import io.netty.channel.sctp.codec.SctpFrameDecoder; +import io.netty.channel.sctp.codec.SctpFrameEncoder; +import io.netty.handler.codec.serialization.ObjectDecoder; +import io.netty.handler.codec.serialization.ObjectEncoder; +import io.netty.testsuite.util.SctpSocketAddresses; +import io.netty.util.internal.ExecutorUtil; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Random; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public abstract class AbstractSocketObjectStreamEchoTest { + + static final Random random = new Random(); + static final String[] data = new String[512];//could not test jumbo frames + + private static ExecutorService executor; + + static { + for (int i = 0; i < data.length; i ++) { + int eLen = random.nextInt(512); + char[] e = new char[eLen]; + for (int j = 0; j < eLen; j ++) { + e[j] = (char) ('a' + random.nextInt(26)); + } + + data[i] = new String(e); + } + } + + @BeforeClass + public static void init() { + executor = Executors.newCachedThreadPool(); + } + + @AfterClass + public static void destroy() { + ExecutorUtil.terminate(executor); + } + + protected abstract ChannelFactory newServerSocketChannelFactory(Executor executor); + protected abstract ChannelFactory newClientSocketChannelFactory(Executor executor); + + @Test + public void testObjectEcho() throws Throwable { + ServerBootstrap sb = new ServerBootstrap(newServerSocketChannelFactory(executor)); + ClientBootstrap cb = new ClientBootstrap(newClientSocketChannelFactory(executor)); + + EchoHandler sh = new EchoHandler(); + EchoHandler ch = new EchoHandler(); + + sb.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + sb.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + sb.getPipeline().addLast("decoder", new ObjectDecoder()); + sb.getPipeline().addLast("encoder", new ObjectEncoder()); + sb.getPipeline().addLast("handler", sh); + + cb.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + cb.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + cb.getPipeline().addLast("decoder", new ObjectDecoder()); + cb.getPipeline().addLast("encoder", new ObjectEncoder()); + cb.getPipeline().addLast("handler", ch); + + Channel sc = sb.bind(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 0)); + int port = ((InetSocketAddress) sc.getLocalAddress()).getPort(); + + ChannelFuture ccf = cb.connect(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, port)); + assertTrue(ccf.awaitUninterruptibly().isSuccess()); + + Channel cc = ccf.getChannel(); + for (String element : data) { + cc.write(element); + } + + while (ch.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(5); + } catch (InterruptedException e) { + // Ignore. + } + } + + while (sh.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(5); + } catch (InterruptedException e) { + // Ignore. + } + } + + sh.channel.close().awaitUninterruptibly(); + ch.channel.close().awaitUninterruptibly(); + sc.close().awaitUninterruptibly(); + + if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) { + throw sh.exception.get(); + } + if (ch.exception.get() != null && !(ch.exception.get() instanceof IOException)) { + throw ch.exception.get(); + } + if (sh.exception.get() != null) { + throw sh.exception.get(); + } + if (ch.exception.get() != null) { + throw ch.exception.get(); + } + } + + private static class EchoHandler extends SimpleChannelUpstreamHandler { + volatile Channel channel; + final AtomicReference exception = new AtomicReference(); + volatile int counter; + + EchoHandler() { + } + + @Override + public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) + throws Exception { + channel = e.getChannel(); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) + throws Exception { + + String m = (String) e.getMessage(); + assertEquals(data[counter], m); + + if (channel.getParent() != null) { + channel.write(m); + } + + counter ++; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) + throws Exception { + if (exception.compareAndSet(null, e.getCause())) { + e.getChannel().close(); + } + } + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketServerBootstrapTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketServerBootstrapTest.java new file mode 100644 index 0000000000..28709c363d --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketServerBootstrapTest.java @@ -0,0 +1,217 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport; + +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpServerChannel; +import com.sun.nio.sctp.SctpStandardSocketOptions; +import io.netty.bootstrap.ClientBootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +import io.netty.channel.sctp.SctpChannelConfig; +import io.netty.testsuite.util.DummyHandler; +import io.netty.testsuite.util.SctpSocketAddresses; +import io.netty.util.internal.ExecutorUtil; +import org.easymock.EasyMock; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.Iterator; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.junit.Assert.assertEquals; + + +/** + * An abstract test class to test server socket bootstraps + */ +public abstract class AbstractSocketServerBootstrapTest { + + private static final boolean BUFSIZE_MODIFIABLE; + + static { + boolean bufSizeModifiable = true; + + SctpChannel s = null; + try { + s = SctpChannel.open(); + bufSizeModifiable = s.supportedOptions().contains(SctpStandardSocketOptions.SO_RCVBUF); + } catch (Exception e) { + bufSizeModifiable = false; + System.err.println( + "SCTP SO_RCVBUF does not work: " + e); + } finally { + BUFSIZE_MODIFIABLE = bufSizeModifiable; + try { + if (s != null) { + s.close(); + } + } catch (IOException e) { + // Ignore. + } + } + } + + private static ExecutorService executor; + + @BeforeClass + public static void init() { + executor = Executors.newCachedThreadPool(); + } + + @AfterClass + public static void destroy() { + ExecutorUtil.terminate(executor); + } + + protected abstract ChannelFactory newServerSocketChannelFactory(Executor executor); + + @Test(timeout = 30000, expected = ChannelException.class) + public void testFailedBindAttempt() throws Exception { + SctpServerChannel serverChannel = SctpServerChannel.open(); + serverChannel.bind(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 0)); + + try { + final Iterator serverAddresses = serverChannel.getAllLocalAddresses().iterator(); + InetSocketAddress serverAddress = (InetSocketAddress) serverAddresses.next(); + final int boundPort = serverAddress.getPort(); + ServerBootstrap bootstrap = new ServerBootstrap(); + bootstrap.setFactory(newServerSocketChannelFactory(executor)); + bootstrap.setOption("localAddress", new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, boundPort)); + bootstrap.bind().close().awaitUninterruptibly(); + } finally { + serverChannel.close(); + } + } + + @Test(timeout = 30000) + public void testSuccessfulBindAttempt() throws Exception { + ServerBootstrap bootstrap = new ServerBootstrap( + newServerSocketChannelFactory(executor)); + + bootstrap.setParentHandler(new ParentChannelHandler()); + bootstrap.setOption("localAddress", new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 0)); + bootstrap.setOption("child.receiveBufferSize", 9753); + bootstrap.setOption("child.sendBufferSize", 8642); + + bootstrap.getPipeline().addLast("dummy", new DummyHandler()); + + Channel channel = bootstrap.bind(); + ParentChannelHandler pch = + channel.getPipeline().get(ParentChannelHandler.class); + + SctpChannel sctpChannel = SctpChannel.open(); + try { + sctpChannel.connect( + new InetSocketAddress( + SctpSocketAddresses.LOOP_BACK, + ((InetSocketAddress) channel.getLocalAddress()).getPort())); + + // Wait until the connection is open in the server side. + while (pch.child == null) { + Thread.yield(); + } + + SctpChannelConfig cfg = (SctpChannelConfig) pch.child.getConfig(); + if (BUFSIZE_MODIFIABLE) { + assertEquals(9753, cfg.getReceiveBufferSize()); + assertEquals(8642, cfg.getSendBufferSize()); + } + } finally { + if (sctpChannel != null) { + try { + sctpChannel.close(); + } catch (IOException e) { + // Ignore. + } + } + channel.close().awaitUninterruptibly(); + } + + // Wait until the child connection is closed in the client side. + // We do not use Channel.close() to make sure it is closed automatically. + while (pch.child.isOpen()) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // Ignore + } + } + + // Wait until all child events are fired. + while (pch.result.length() < 2) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // Ignore + } + } + + // Confirm the received child events. + assertEquals("12", pch.result.toString()); + } + + @Test(expected = ChannelPipelineException.class) + public void testFailedPipelineInitialization() throws Exception { + ClientBootstrap bootstrap = new ClientBootstrap(EasyMock.createMock(ChannelFactory.class)); + ChannelPipelineFactory pipelineFactory = EasyMock.createMock(ChannelPipelineFactory.class); + bootstrap.setPipelineFactory(pipelineFactory); + + EasyMock.expect(pipelineFactory.getPipeline()).andThrow(new ChannelPipelineException()); + EasyMock.replay(pipelineFactory); + + bootstrap.connect(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 1)); + } + + @Test(expected = IllegalStateException.class) + public void shouldHaveLocalAddressOption() { + new ServerBootstrap(EasyMock.createMock(ServerChannelFactory.class)).bind(); + } + + + @Test(expected = NullPointerException.class) + public void shouldDisallowNullLocalAddressParameter() { + new ServerBootstrap(EasyMock.createMock(ServerChannelFactory.class)).bind(null); + } + + private static class ParentChannelHandler extends SimpleChannelUpstreamHandler { + + volatile Channel child; + final StringBuffer result = new StringBuffer(); + + ParentChannelHandler() { + } + + @Override + public void childChannelClosed(ChannelHandlerContext ctx, + ChildChannelStateEvent e) throws Exception { + result.append('2'); + } + + @Override + public void childChannelOpen(ChannelHandlerContext ctx, + ChildChannelStateEvent e) throws Exception { + child = e.getChildChannel(); + result.append('1'); + } + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketSslEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketSslEchoTest.java new file mode 100644 index 0000000000..b169ef6d43 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketSslEchoTest.java @@ -0,0 +1,640 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport; + +import io.netty.bootstrap.ClientBootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ChannelBuffer; +import io.netty.buffer.ChannelBuffers; +import io.netty.channel.*; +import io.netty.channel.sctp.codec.SctpFrameDecoder; +import io.netty.channel.sctp.codec.SctpFrameEncoder; +import io.netty.handler.execution.ExecutionHandler; +import io.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor; +import io.netty.handler.ssl.SslHandler; +import io.netty.logging.InternalLogger; +import io.netty.logging.InternalLoggerFactory; +import io.netty.testsuite.util.SctpSocketAddresses; +import io.netty.util.internal.ExecutorUtil; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.net.ssl.*; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.Security; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Random; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public abstract class AbstractSocketSslEchoTest { + static final InternalLogger logger = + InternalLoggerFactory.getInstance(AbstractSocketSslEchoTest.class); + + private static final Random random = new Random(); + static final byte[] data = new byte[4096];//could not test jumbo frames + + private static ExecutorService executor; + private static ExecutorService eventExecutor; + + static { + random.nextBytes(data); + } + + @BeforeClass + public static void init() { + executor = Executors.newCachedThreadPool(); + eventExecutor = new OrderedMemoryAwareThreadPoolExecutor(16, 0, 0); + } + + @AfterClass + public static void destroy() { + ExecutorUtil.terminate(executor, eventExecutor); + } + + protected abstract ChannelFactory newServerSocketChannelFactory(Executor executor); + protected abstract ChannelFactory newClientSocketChannelFactory(Executor executor); + + protected boolean isExecutorRequired() { + return false; + } + + @Test + public void testSslEcho() throws Throwable { + ServerBootstrap sb = new ServerBootstrap(newServerSocketChannelFactory(executor)); + ClientBootstrap cb = new ClientBootstrap(newClientSocketChannelFactory(executor)); + + EchoHandler sh = new EchoHandler(true); + EchoHandler ch = new EchoHandler(false); + + SSLEngine sse = BogusSslContextFactory.getServerContext().createSSLEngine(); + SSLEngine cse = BogusSslContextFactory.getClientContext().createSSLEngine(); + sse.setUseClientMode(false); + cse.setUseClientMode(true); + + + sb.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + sb.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + sb.getPipeline().addLast("ssl", new SslHandler(sse)); + sb.getPipeline().addLast("handler", sh); + + cb.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + cb.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + cb.getPipeline().addLast("ssl", new SslHandler(cse)); + cb.getPipeline().addLast("handler", ch); + + if (isExecutorRequired()) { + sb.getPipeline().addFirst("executor", new ExecutionHandler(eventExecutor)); + cb.getPipeline().addFirst("executor", new ExecutionHandler(eventExecutor)); + } + + Channel sc = sb.bind(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 0)); + int port = ((InetSocketAddress) sc.getLocalAddress()).getPort(); + + ChannelFuture ccf = cb.connect(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, port)); + ccf.awaitUninterruptibly(); + if (!ccf.isSuccess()) { + logger.error("Connection attempt failed", ccf.getCause()); + sc.close().awaitUninterruptibly(); + } + assertTrue(ccf.isSuccess()); + + Channel cc = ccf.getChannel(); + ChannelFuture hf = cc.getPipeline().get(SslHandler.class).handshake(); + hf.awaitUninterruptibly(); + if (!hf.isSuccess()) { + logger.error("Handshake failed", hf.getCause()); + sh.channel.close().awaitUninterruptibly(); + ch.channel.close().awaitUninterruptibly(); + sc.close().awaitUninterruptibly(); + } + + assertTrue(hf.isSuccess()); + + for (int i = 0; i < data.length;) { + int length = Math.min(random.nextInt(1024 * 64), data.length - i); + cc.write(ChannelBuffers.wrappedBuffer(data, i, length)); + i += length; + } + + while (ch.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // Ignore. + } + } + + while (sh.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // Ignore. + } + } + + sh.channel.close().awaitUninterruptibly(); + ch.channel.close().awaitUninterruptibly(); + sc.close().awaitUninterruptibly(); + + if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) { + throw sh.exception.get(); + } + if (ch.exception.get() != null && !(ch.exception.get() instanceof IOException)) { + throw ch.exception.get(); + } + if (sh.exception.get() != null) { + throw sh.exception.get(); + } + if (ch.exception.get() != null) { + throw ch.exception.get(); + } + } + + private static class EchoHandler extends SimpleChannelUpstreamHandler { + volatile Channel channel; + final AtomicReference exception = new AtomicReference(); + volatile int counter; + private final boolean server; + + EchoHandler(boolean server) { + this.server = server; + } + + @Override + public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) + throws Exception { + channel = e.getChannel(); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) + throws Exception { + ChannelBuffer m = (ChannelBuffer) e.getMessage(); + byte[] actual = new byte[m.readableBytes()]; + m.getBytes(0, actual); + + int lastIdx = counter; + for (int i = 0; i < actual.length; i ++) { + assertEquals(data[i + lastIdx], actual[i]); + } + + if (channel.getParent() != null) { + channel.write(m); + } + + counter += actual.length; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) + throws Exception { + logger.warn( + "Unexpected exception from the " + + (server? "server" : "client") + " side", e.getCause()); + + exception.compareAndSet(null, e.getCause()); + e.getChannel().close(); + } + } + + private static class BogusSslContextFactory { + + private static final String PROTOCOL = "TLS"; + private static final SSLContext SERVER_CONTEXT; + private static final SSLContext CLIENT_CONTEXT; + + static { + String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm"); + if (algorithm == null) { + algorithm = "SunX509"; + } + + SSLContext serverContext = null; + SSLContext clientContext = null; + try { + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(BogusKeyStore.asInputStream(), + BogusKeyStore.getKeyStorePassword()); + + // Set up key manager factory to use our key store + KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); + kmf.init(ks, BogusKeyStore.getCertificatePassword()); + + // Initialize the SSLContext to work with our key managers. + serverContext = SSLContext.getInstance(PROTOCOL); + serverContext.init(kmf.getKeyManagers(), null, null); + } catch (Exception e) { + throw new Error( + "Failed to initialize the server-side SSLContext", e); + } + + try { + clientContext = SSLContext.getInstance(PROTOCOL); + clientContext.init(null, BogusTrustManagerFactory.getTrustManagers(), null); + } catch (Exception e) { + throw new Error( + "Failed to initialize the client-side SSLContext", e); + } + + SERVER_CONTEXT = serverContext; + CLIENT_CONTEXT = clientContext; + } + + public static SSLContext getServerContext() { + return SERVER_CONTEXT; + } + + public static SSLContext getClientContext() { + return CLIENT_CONTEXT; + } + } + + /** + * Bogus {@link javax.net.ssl.TrustManagerFactorySpi} which accepts any certificate + * even if it is invalid. + */ + private static class BogusTrustManagerFactory extends TrustManagerFactorySpi { + + private static final TrustManager DUMMY_TRUST_MANAGER = new X509TrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + @Override + public void checkClientTrusted( + X509Certificate[] chain, String authType) throws CertificateException { + // Always trust - it is an example. + // You should do something in the real world. + // You will reach here only if you enabled client certificate auth, + // as described in SecureChatSslContextFactory. + System.err.println( + "UNKNOWN CLIENT CERTIFICATE: " + chain[0].getSubjectDN()); + } + + @Override + public void checkServerTrusted( + X509Certificate[] chain, String authType) throws CertificateException { + // Always trust - it is an example. + // You should do something in the real world. + System.err.println( + "UNKNOWN SERVER CERTIFICATE: " + chain[0].getSubjectDN()); + } + }; + + public static TrustManager[] getTrustManagers() { + return new TrustManager[] { DUMMY_TRUST_MANAGER }; + } + + @Override + protected TrustManager[] engineGetTrustManagers() { + return getTrustManagers(); + } + + @Override + protected void engineInit(KeyStore keystore) throws KeyStoreException { + // Unused + } + + @Override + protected void engineInit(ManagerFactoryParameters managerFactoryParameters) + throws InvalidAlgorithmParameterException { + // Unused + } + } + + /** + * A bogus key store which provides all the required information to + * create an example SSL connection. + * + * To generate a bogus key store: + *
+     * keytool  -genkey -alias bogus -keysize 2048 -validity 36500
+     *          -keyalg RSA -dname "CN=bogus"
+     *          -keypass secret -storepass secret
+     *          -keystore cert.jks
+     * 
+ */ + private static final class BogusKeyStore { + private static final short[] DATA = { + 0xfe, 0xed, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x00, 0x00, 0x01, 0x1a, 0x9f, 0x57, 0xa5, + 0x27, 0x00, 0x00, 0x01, 0x9a, 0x30, 0x82, 0x01, + 0x96, 0x30, 0x0e, 0x06, 0x0a, 0x2b, 0x06, 0x01, + 0x04, 0x01, 0x2a, 0x02, 0x11, 0x01, 0x01, 0x05, + 0x00, 0x04, 0x82, 0x01, 0x82, 0x48, 0x6d, 0xcf, + 0x16, 0xb5, 0x50, 0x95, 0x36, 0xbf, 0x47, 0x27, + 0x50, 0x58, 0x0d, 0xa2, 0x52, 0x7e, 0x25, 0xab, + 0x14, 0x1a, 0x26, 0x5e, 0x2d, 0x8a, 0x23, 0x90, + 0x60, 0x7f, 0x12, 0x20, 0x56, 0xd1, 0x43, 0xa2, + 0x6b, 0x47, 0x5d, 0xed, 0x9d, 0xd4, 0xe5, 0x83, + 0x28, 0x89, 0xc2, 0x16, 0x4c, 0x76, 0x06, 0xad, + 0x8e, 0x8c, 0x29, 0x1a, 0x9b, 0x0f, 0xdd, 0x60, + 0x4b, 0xb4, 0x62, 0x82, 0x9e, 0x4a, 0x63, 0x83, + 0x2e, 0xd2, 0x43, 0x78, 0xc2, 0x32, 0x1f, 0x60, + 0xa9, 0x8a, 0x7f, 0x0f, 0x7c, 0xa6, 0x1d, 0xe6, + 0x92, 0x9e, 0x52, 0xc7, 0x7d, 0xbb, 0x35, 0x3b, + 0xaa, 0x89, 0x73, 0x4c, 0xfb, 0x99, 0x54, 0x97, + 0x99, 0x28, 0x6e, 0x66, 0x5b, 0xf7, 0x9b, 0x7e, + 0x6d, 0x8a, 0x2f, 0xfa, 0xc3, 0x1e, 0x71, 0xb9, + 0xbd, 0x8f, 0xc5, 0x63, 0x25, 0x31, 0x20, 0x02, + 0xff, 0x02, 0xf0, 0xc9, 0x2c, 0xdd, 0x3a, 0x10, + 0x30, 0xab, 0xe5, 0xad, 0x3d, 0x1a, 0x82, 0x77, + 0x46, 0xed, 0x03, 0x38, 0xa4, 0x73, 0x6d, 0x36, + 0x36, 0x33, 0x70, 0xb2, 0x63, 0x20, 0xca, 0x03, + 0xbf, 0x5a, 0xf4, 0x7c, 0x35, 0xf0, 0x63, 0x1a, + 0x12, 0x33, 0x12, 0x58, 0xd9, 0xa2, 0x63, 0x6b, + 0x63, 0x82, 0x41, 0x65, 0x70, 0x37, 0x4b, 0x99, + 0x04, 0x9f, 0xdd, 0x5e, 0x07, 0x01, 0x95, 0x9f, + 0x36, 0xe8, 0xc3, 0x66, 0x2a, 0x21, 0x69, 0x68, + 0x40, 0xe6, 0xbc, 0xbb, 0x85, 0x81, 0x21, 0x13, + 0xe6, 0xa4, 0xcf, 0xd3, 0x67, 0xe3, 0xfd, 0x75, + 0xf0, 0xdf, 0x83, 0xe0, 0xc5, 0x36, 0x09, 0xac, + 0x1b, 0xd4, 0xf7, 0x2a, 0x23, 0x57, 0x1c, 0x5c, + 0x0f, 0xf4, 0xcf, 0xa2, 0xcf, 0xf5, 0xbd, 0x9c, + 0x69, 0x98, 0x78, 0x3a, 0x25, 0xe4, 0xfd, 0x85, + 0x11, 0xcc, 0x7d, 0xef, 0xeb, 0x74, 0x60, 0xb1, + 0xb7, 0xfb, 0x1f, 0x0e, 0x62, 0xff, 0xfe, 0x09, + 0x0a, 0xc3, 0x80, 0x2f, 0x10, 0x49, 0x89, 0x78, + 0xd2, 0x08, 0xfa, 0x89, 0x22, 0x45, 0x91, 0x21, + 0xbc, 0x90, 0x3e, 0xad, 0xb3, 0x0a, 0xb4, 0x0e, + 0x1c, 0xa1, 0x93, 0x92, 0xd8, 0x72, 0x07, 0x54, + 0x60, 0xe7, 0x91, 0xfc, 0xd9, 0x3c, 0xe1, 0x6f, + 0x08, 0xe4, 0x56, 0xf6, 0x0b, 0xb0, 0x3c, 0x39, + 0x8a, 0x2d, 0x48, 0x44, 0x28, 0x13, 0xca, 0xe9, + 0xf7, 0xa3, 0xb6, 0x8a, 0x5f, 0x31, 0xa9, 0x72, + 0xf2, 0xde, 0x96, 0xf2, 0xb1, 0x53, 0xb1, 0x3e, + 0x24, 0x57, 0xfd, 0x18, 0x45, 0x1f, 0xc5, 0x33, + 0x1b, 0xa4, 0xe8, 0x21, 0xfa, 0x0e, 0xb2, 0xb9, + 0xcb, 0xc7, 0x07, 0x41, 0xdd, 0x2f, 0xb6, 0x6a, + 0x23, 0x18, 0xed, 0xc1, 0xef, 0xe2, 0x4b, 0xec, + 0xc9, 0xba, 0xfb, 0x46, 0x43, 0x90, 0xd7, 0xb5, + 0x68, 0x28, 0x31, 0x2b, 0x8d, 0xa8, 0x51, 0x63, + 0xf7, 0x53, 0x99, 0x19, 0x68, 0x85, 0x66, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x05, 0x58, 0x2e, 0x35, + 0x30, 0x39, 0x00, 0x00, 0x02, 0x3a, 0x30, 0x82, + 0x02, 0x36, 0x30, 0x82, 0x01, 0xe0, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x59, 0xf1, + 0x92, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, + 0x30, 0x81, 0xa0, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4b, 0x52, + 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x13, 0x0a, 0x4b, 0x79, 0x75, 0x6e, 0x67, + 0x67, 0x69, 0x2d, 0x64, 0x6f, 0x31, 0x14, 0x30, + 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0b, + 0x53, 0x65, 0x6f, 0x6e, 0x67, 0x6e, 0x61, 0x6d, + 0x2d, 0x73, 0x69, 0x31, 0x1a, 0x30, 0x18, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x54, 0x68, + 0x65, 0x20, 0x4e, 0x65, 0x74, 0x74, 0x79, 0x20, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x31, + 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x0f, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x73, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x27, 0x73, 0x65, 0x63, 0x75, + 0x72, 0x65, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65, + 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, + 0x65, 0x74, 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65, + 0x61, 0x6d, 0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e, + 0x6e, 0x65, 0x74, 0x30, 0x20, 0x17, 0x0d, 0x30, + 0x38, 0x30, 0x36, 0x31, 0x39, 0x30, 0x35, 0x34, + 0x31, 0x33, 0x38, 0x5a, 0x18, 0x0f, 0x32, 0x31, + 0x38, 0x37, 0x31, 0x31, 0x32, 0x34, 0x30, 0x35, + 0x34, 0x31, 0x33, 0x38, 0x5a, 0x30, 0x81, 0xa0, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x4b, 0x52, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x4b, 0x79, 0x75, 0x6e, 0x67, 0x67, 0x69, 0x2d, + 0x64, 0x6f, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x04, 0x07, 0x13, 0x0b, 0x53, 0x65, 0x6f, + 0x6e, 0x67, 0x6e, 0x61, 0x6d, 0x2d, 0x73, 0x69, + 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x11, 0x54, 0x68, 0x65, 0x20, 0x4e, + 0x65, 0x74, 0x74, 0x79, 0x20, 0x50, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x31, 0x18, 0x30, 0x16, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0f, 0x45, + 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x73, 0x31, 0x30, + 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x27, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63, + 0x68, 0x61, 0x74, 0x2e, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x74, + 0x79, 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d, 0x79, + 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x65, 0x74, + 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, + 0x00, 0xc3, 0xe3, 0x5e, 0x41, 0xa7, 0x87, 0x11, + 0x00, 0x42, 0x2a, 0xb0, 0x4b, 0xed, 0xb2, 0xe0, + 0x23, 0xdb, 0xb1, 0x3d, 0x58, 0x97, 0x35, 0x60, + 0x0b, 0x82, 0x59, 0xd3, 0x00, 0xea, 0xd4, 0x61, + 0xb8, 0x79, 0x3f, 0xb6, 0x3c, 0x12, 0x05, 0x93, + 0x2e, 0x9a, 0x59, 0x68, 0x14, 0x77, 0x3a, 0xc8, + 0x50, 0x25, 0x57, 0xa4, 0x49, 0x18, 0x63, 0x41, + 0xf0, 0x2d, 0x28, 0xec, 0x06, 0xfb, 0xb4, 0x9f, + 0xbf, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, + 0x65, 0x6c, 0x30, 0x01, 0xc2, 0x8e, 0x3e, 0xcb, + 0xb3, 0x77, 0x48, 0xe9, 0x66, 0x61, 0x9a, 0x40, + 0x86, 0xaf, 0xf6, 0x03, 0xeb, 0xba, 0x6a, 0xf2, + 0xfd, 0xe2, 0xaf, 0x36, 0x5e, 0x7b, 0xaa, 0x22, + 0x04, 0xdd, 0x2c, 0x20, 0xc4, 0xfc, 0xdd, 0xd0, + 0x82, 0x20, 0x1c, 0x3d, 0xd7, 0x9e, 0x5e, 0x5c, + 0x92, 0x5a, 0x76, 0x71, 0x28, 0xf5, 0x07, 0x7d, + 0xa2, 0x81, 0xba, 0x77, 0x9f, 0x2a, 0xd9, 0x44, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x6d, 0x79, + 0x6b, 0x65, 0x79, 0x00, 0x00, 0x01, 0x1a, 0x9f, + 0x5b, 0x56, 0xa0, 0x00, 0x00, 0x01, 0x99, 0x30, + 0x82, 0x01, 0x95, 0x30, 0x0e, 0x06, 0x0a, 0x2b, + 0x06, 0x01, 0x04, 0x01, 0x2a, 0x02, 0x11, 0x01, + 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x81, 0x29, + 0xa8, 0xb6, 0x08, 0x0c, 0x85, 0x75, 0x3e, 0xdd, + 0xb5, 0xe5, 0x1a, 0x87, 0x68, 0xd1, 0x90, 0x4b, + 0x29, 0x31, 0xee, 0x90, 0xbc, 0x9d, 0x73, 0xa0, + 0x3f, 0xe9, 0x0b, 0xa4, 0xef, 0x30, 0x9b, 0x36, + 0x9a, 0xb2, 0x54, 0x77, 0x81, 0x07, 0x4b, 0xaa, + 0xa5, 0x77, 0x98, 0xe1, 0xeb, 0xb5, 0x7c, 0x4e, + 0x48, 0xd5, 0x08, 0xfc, 0x2c, 0x36, 0xe2, 0x65, + 0x03, 0xac, 0xe5, 0xf3, 0x96, 0xb7, 0xd0, 0xb5, + 0x3b, 0x92, 0xe4, 0x14, 0x05, 0x7a, 0x6a, 0x92, + 0x56, 0xfe, 0x4e, 0xab, 0xd3, 0x0e, 0x32, 0x04, + 0x22, 0x22, 0x74, 0x47, 0x7d, 0xec, 0x21, 0x99, + 0x30, 0x31, 0x64, 0x46, 0x64, 0x9b, 0xc7, 0x13, + 0xbf, 0xbe, 0xd0, 0x31, 0x49, 0xe7, 0x3c, 0xbf, + 0xba, 0xb1, 0x20, 0xf9, 0x42, 0xf4, 0xa9, 0xa9, + 0xe5, 0x13, 0x65, 0x32, 0xbf, 0x7c, 0xcc, 0x91, + 0xd3, 0xfd, 0x24, 0x47, 0x0b, 0xe5, 0x53, 0xad, + 0x50, 0x30, 0x56, 0xd1, 0xfa, 0x9c, 0x37, 0xa8, + 0xc1, 0xce, 0xf6, 0x0b, 0x18, 0xaa, 0x7c, 0xab, + 0xbd, 0x1f, 0xdf, 0xe4, 0x80, 0xb8, 0xa7, 0xe0, + 0xad, 0x7d, 0x50, 0x74, 0xf1, 0x98, 0x78, 0xbc, + 0x58, 0xb9, 0xc2, 0x52, 0xbe, 0xd2, 0x5b, 0x81, + 0x94, 0x83, 0x8f, 0xb9, 0x4c, 0xee, 0x01, 0x2b, + 0x5e, 0xc9, 0x6e, 0x9b, 0xf5, 0x63, 0x69, 0xe4, + 0xd8, 0x0b, 0x47, 0xd8, 0xfd, 0xd8, 0xe0, 0xed, + 0xa8, 0x27, 0x03, 0x74, 0x1e, 0x5d, 0x32, 0xe6, + 0x5c, 0x63, 0xc2, 0xfb, 0x3f, 0xee, 0xb4, 0x13, + 0xc6, 0x0e, 0x6e, 0x74, 0xe0, 0x22, 0xac, 0xce, + 0x79, 0xf9, 0x43, 0x68, 0xc1, 0x03, 0x74, 0x2b, + 0xe1, 0x18, 0xf8, 0x7f, 0x76, 0x9a, 0xea, 0x82, + 0x3f, 0xc2, 0xa6, 0xa7, 0x4c, 0xfe, 0xae, 0x29, + 0x3b, 0xc1, 0x10, 0x7c, 0xd5, 0x77, 0x17, 0x79, + 0x5f, 0xcb, 0xad, 0x1f, 0xd8, 0xa1, 0xfd, 0x90, + 0xe1, 0x6b, 0xb2, 0xef, 0xb9, 0x41, 0x26, 0xa4, + 0x0b, 0x4f, 0xc6, 0x83, 0x05, 0x6f, 0xf0, 0x64, + 0x40, 0xe1, 0x44, 0xc4, 0xf9, 0x40, 0x2b, 0x3b, + 0x40, 0xdb, 0xaf, 0x35, 0xa4, 0x9b, 0x9f, 0xc4, + 0x74, 0x07, 0xe5, 0x18, 0x60, 0xc5, 0xfe, 0x15, + 0x0e, 0x3a, 0x25, 0x2a, 0x11, 0xee, 0x78, 0x2f, + 0xb8, 0xd1, 0x6e, 0x4e, 0x3c, 0x0a, 0xb5, 0xb9, + 0x40, 0x86, 0x27, 0x6d, 0x8f, 0x53, 0xb7, 0x77, + 0x36, 0xec, 0x5d, 0xed, 0x32, 0x40, 0x43, 0x82, + 0xc3, 0x52, 0x58, 0xc4, 0x26, 0x39, 0xf3, 0xb3, + 0xad, 0x58, 0xab, 0xb7, 0xf7, 0x8e, 0x0e, 0xba, + 0x8e, 0x78, 0x9d, 0xbf, 0x58, 0x34, 0xbd, 0x77, + 0x73, 0xa6, 0x50, 0x55, 0x00, 0x60, 0x26, 0xbf, + 0x6d, 0xb4, 0x98, 0x8a, 0x18, 0x83, 0x89, 0xf8, + 0xcd, 0x0d, 0x49, 0x06, 0xae, 0x51, 0x6e, 0xaf, + 0xbd, 0xe2, 0x07, 0x13, 0xd8, 0x64, 0xcc, 0xbf, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x58, 0x2e, + 0x35, 0x30, 0x39, 0x00, 0x00, 0x02, 0x34, 0x30, + 0x82, 0x02, 0x30, 0x30, 0x82, 0x01, 0xda, 0xa0, + 0x03, 0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x59, + 0xf2, 0x84, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x30, 0x81, 0x9d, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4b, + 0x52, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x4b, 0x79, 0x75, 0x6e, + 0x67, 0x67, 0x69, 0x2d, 0x64, 0x6f, 0x31, 0x14, + 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, + 0x0b, 0x53, 0x65, 0x6f, 0x6e, 0x67, 0x6e, 0x61, + 0x6d, 0x2d, 0x73, 0x69, 0x31, 0x1a, 0x30, 0x18, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x54, + 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x74, 0x79, + 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x31, + 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x27, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, + 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65, 0x78, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, + 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d, + 0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x65, + 0x74, 0x30, 0x20, 0x17, 0x0d, 0x30, 0x38, 0x30, + 0x36, 0x31, 0x39, 0x30, 0x35, 0x34, 0x35, 0x34, + 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x38, 0x37, + 0x31, 0x31, 0x32, 0x33, 0x30, 0x35, 0x34, 0x35, + 0x34, 0x30, 0x5a, 0x30, 0x81, 0x9d, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x4b, 0x52, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x4b, 0x79, + 0x75, 0x6e, 0x67, 0x67, 0x69, 0x2d, 0x64, 0x6f, + 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, + 0x07, 0x13, 0x0b, 0x53, 0x65, 0x6f, 0x6e, 0x67, + 0x6e, 0x61, 0x6d, 0x2d, 0x73, 0x69, 0x31, 0x1a, + 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x11, 0x54, 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, + 0x74, 0x79, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x13, 0x0c, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, + 0x73, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x27, 0x73, 0x65, 0x63, 0x75, + 0x72, 0x65, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65, + 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, + 0x65, 0x74, 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65, + 0x61, 0x6d, 0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e, + 0x6e, 0x65, 0x74, 0x30, 0x5c, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, + 0x48, 0x02, 0x41, 0x00, 0x95, 0xb3, 0x47, 0x17, + 0x95, 0x0f, 0x57, 0xcf, 0x66, 0x72, 0x0a, 0x7e, + 0x5b, 0x54, 0xea, 0x8c, 0x6f, 0x79, 0xde, 0x94, + 0xac, 0x0b, 0x5a, 0xd4, 0xd6, 0x1b, 0x58, 0x12, + 0x1a, 0x16, 0x3d, 0xfe, 0xdf, 0xa5, 0x2b, 0x86, + 0xbc, 0x64, 0xd4, 0x80, 0x1e, 0x3f, 0xf9, 0xe2, + 0x04, 0x03, 0x79, 0x9b, 0xc1, 0x5c, 0xf0, 0xf1, + 0xf3, 0xf1, 0xe3, 0xbf, 0x3f, 0xc0, 0x1f, 0xdd, + 0xdb, 0xc0, 0x5b, 0x21, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, + 0x03, 0x41, 0x00, 0x02, 0xd7, 0xdd, 0xbd, 0x0c, + 0x8e, 0x21, 0x20, 0xef, 0x9e, 0x4f, 0x1f, 0xf5, + 0x49, 0xf1, 0xae, 0x58, 0x9b, 0x94, 0x3a, 0x1f, + 0x70, 0x33, 0xf0, 0x9b, 0xbb, 0xe9, 0xc0, 0xf3, + 0x72, 0xcb, 0xde, 0xb6, 0x56, 0x72, 0xcc, 0x1c, + 0xf0, 0xd6, 0x5a, 0x2a, 0xbc, 0xa1, 0x7e, 0x23, + 0x83, 0xe9, 0xe7, 0xcf, 0x9e, 0xa5, 0xf9, 0xcc, + 0xc2, 0x61, 0xf4, 0xdb, 0x40, 0x93, 0x1d, 0x63, + 0x8a, 0x50, 0x4c, 0x11, 0x39, 0xb1, 0x91, 0xc1, + 0xe6, 0x9d, 0xd9, 0x1a, 0x62, 0x1b, 0xb8, 0xd3, + 0xd6, 0x9a, 0x6d, 0xb9, 0x8e, 0x15, 0x51 }; + + public static InputStream asInputStream() { + byte[] data = new byte[DATA.length]; + for (int i = 0; i < data.length; i ++) { + data[i] = (byte) DATA[i]; + } + return new ByteArrayInputStream(data); + } + + public static char[] getCertificatePassword() { + return "secret".toCharArray(); + } + + public static char[] getKeyStorePassword() { + return "secret".toCharArray(); + } + + private BogusKeyStore() { + // Unused + } + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketStringEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketStringEchoTest.java new file mode 100644 index 0000000000..9163d99e32 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketStringEchoTest.java @@ -0,0 +1,196 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport; + +import io.netty.bootstrap.ClientBootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +import io.netty.channel.sctp.codec.SctpFrameDecoder; +import io.netty.channel.sctp.codec.SctpFrameEncoder; +import io.netty.handler.codec.frame.DelimiterBasedFrameDecoder; +import io.netty.handler.codec.frame.Delimiters; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import io.netty.testsuite.util.SctpSocketAddresses; +import io.netty.util.CharsetUtil; +import io.netty.util.internal.ExecutorUtil; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Random; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public abstract class AbstractSocketStringEchoTest { + + static final Random random = new Random(); + static final String[] data = new String[1024]; + + private static ExecutorService executor; + + static { + for (int i = 0; i < data.length; i ++) { + int eLen = random.nextInt(512); + char[] e = new char[eLen]; + for (int j = 0; j < eLen; j ++) { + e[j] = (char) ('a' + random.nextInt(26)); + } + + data[i] = new String(e); + } + } + + @BeforeClass + public static void init() { + executor = Executors.newCachedThreadPool(); + } + + @AfterClass + public static void destroy() { + ExecutorUtil.terminate(executor); + } + + protected abstract ChannelFactory newServerSocketChannelFactory(Executor executor); + protected abstract ChannelFactory newClientSocketChannelFactory(Executor executor); + + @Test + public void testStringEcho() throws Throwable { + ServerBootstrap sb = new ServerBootstrap(newServerSocketChannelFactory(executor)); + ClientBootstrap cb = new ClientBootstrap(newClientSocketChannelFactory(executor)); + + EchoHandler sh = new EchoHandler(); + EchoHandler ch = new EchoHandler(); + + sb.getPipeline().addFirst("sctp-decoder", new SctpFrameDecoder()); + sb.getPipeline().addFirst("sctp-encoder", new SctpFrameEncoder()); + sb.getPipeline().addLast("framer", new DelimiterBasedFrameDecoder(512, Delimiters.lineDelimiter())); + sb.getPipeline().addLast("decoder", new StringDecoder(CharsetUtil.ISO_8859_1)); + sb.getPipeline().addLast("encoder", new StringEncoder(CharsetUtil.ISO_8859_1)); + sb.getPipeline().addLast("handler", sh); + + cb.getPipeline().addFirst("sctp-decoder", new SctpFrameDecoder()); + cb.getPipeline().addFirst("sctp-encoder", new SctpFrameEncoder()); + cb.getPipeline().addLast("framer", new DelimiterBasedFrameDecoder(512, Delimiters.lineDelimiter())); + cb.getPipeline().addLast("decoder", new StringDecoder(CharsetUtil.ISO_8859_1)); + cb.getPipeline().addLast("encoder", new StringEncoder(CharsetUtil.ISO_8859_1)); + cb.getPipeline().addLast("handler", ch); + + Channel sc = sb.bind(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 0)); + int port = ((InetSocketAddress) sc.getLocalAddress()).getPort(); + + ChannelFuture ccf = cb.connect(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, port)); + assertTrue(ccf.awaitUninterruptibly().isSuccess()); + + Channel cc = ccf.getChannel(); + for (String element : data) { + String delimiter = random.nextBoolean() ? "\r\n" : "\n"; + cc.write(element + delimiter); + } + + while (ch.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // Ignore. + } + } + + while (sh.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // Ignore. + } + } + + sh.channel.close().awaitUninterruptibly(); + ch.channel.close().awaitUninterruptibly(); + sc.close().awaitUninterruptibly(); + + if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) { + throw sh.exception.get(); + } + if (ch.exception.get() != null && !(ch.exception.get() instanceof IOException)) { + throw ch.exception.get(); + } + if (sh.exception.get() != null) { + throw sh.exception.get(); + } + if (ch.exception.get() != null) { + throw ch.exception.get(); + } + } + + private static class EchoHandler extends SimpleChannelUpstreamHandler { + volatile Channel channel; + final AtomicReference exception = new AtomicReference(); + volatile int counter; + + EchoHandler() { + } + + @Override + public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) + throws Exception { + channel = e.getChannel(); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) + throws Exception { + + String m = (String) e.getMessage(); + assertEquals(data[counter], m); + + if (channel.getParent() != null) { + String delimiter = random.nextBoolean() ? "\r\n" : "\n"; + channel.write(m + delimiter); + } + + counter ++; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) + throws Exception { + if (exception.compareAndSet(null, e.getCause())) { + e.getChannel().close(); + } + } + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpClientBootstrapTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpClientBootstrapTest.java new file mode 100644 index 0000000000..33845d13b8 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpClientBootstrapTest.java @@ -0,0 +1,29 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport.sctp; + +import io.netty.channel.ChannelFactory; +import io.netty.channel.sctp.SctpClientSocketChannelFactory; +import io.netty.testsuite.transport.AbstractSocketClientBootstrapTest; + +import java.util.concurrent.Executor; + +public class SctpClientBootstrapTest extends AbstractSocketClientBootstrapTest { + @Override + protected ChannelFactory newClientSocketChannelFactory(Executor executor) { + return new SctpClientSocketChannelFactory(executor, executor); + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpCompatibleObjectStreamEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpCompatibleObjectStreamEchoTest.java new file mode 100644 index 0000000000..dc38655dce --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpCompatibleObjectStreamEchoTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport.sctp; + +import io.netty.channel.ChannelFactory; +import io.netty.channel.sctp.SctpClientSocketChannelFactory; +import io.netty.channel.sctp.SctpServerSocketChannelFactory; +import io.netty.testsuite.transport.AbstractSocketCompatibleObjectStreamEchoTest; + +import java.util.concurrent.Executor; + +public class SctpCompatibleObjectStreamEchoTest extends AbstractSocketCompatibleObjectStreamEchoTest { + @Override + protected ChannelFactory newServerSocketChannelFactory(Executor executor) { + return new SctpServerSocketChannelFactory(executor, executor); + } + + @Override + protected ChannelFactory newClientSocketChannelFactory(Executor executor) { + return new SctpClientSocketChannelFactory(executor, executor); + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpEchoTest.java new file mode 100644 index 0000000000..bbe24bde2a --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpEchoTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport.sctp; + +import io.netty.channel.ChannelFactory; +import io.netty.channel.sctp.SctpClientSocketChannelFactory; +import io.netty.channel.sctp.SctpServerSocketChannelFactory; +import io.netty.testsuite.transport.AbstractSocketEchoTest; + +import java.util.concurrent.Executor; + +public class SctpEchoTest extends AbstractSocketEchoTest { + @Override + protected ChannelFactory newServerSocketChannelFactory(Executor executor) { + return new SctpServerSocketChannelFactory(executor, executor); + } + + @Override + protected ChannelFactory newClientSocketChannelFactory(Executor executor) { + return new SctpClientSocketChannelFactory(executor, executor); + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpFixedLengthEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpFixedLengthEchoTest.java new file mode 100644 index 0000000000..a1e590438d --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpFixedLengthEchoTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport.sctp; + +import io.netty.channel.ChannelFactory; +import io.netty.channel.sctp.SctpClientSocketChannelFactory; +import io.netty.channel.sctp.SctpServerSocketChannelFactory; +import io.netty.testsuite.transport.AbstractSocketFixedLengthEchoTest; + +import java.util.concurrent.Executor; + +public class SctpFixedLengthEchoTest extends AbstractSocketFixedLengthEchoTest { + @Override + protected ChannelFactory newServerSocketChannelFactory(Executor executor) { + return new SctpServerSocketChannelFactory(executor, executor); + } + + @Override + protected ChannelFactory newClientSocketChannelFactory(Executor executor) { + return new SctpClientSocketChannelFactory(executor, executor); + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpMultiHomingEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpMultiHomingEchoTest.java new file mode 100644 index 0000000000..ace6899480 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpMultiHomingEchoTest.java @@ -0,0 +1,222 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport.sctp; + +import io.netty.bootstrap.ClientBootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ChannelBuffer; +import io.netty.buffer.ChannelBuffers; +import io.netty.channel.*; +import io.netty.channel.sctp.*; +import io.netty.channel.sctp.codec.SctpFrameDecoder; +import io.netty.channel.sctp.codec.SctpFrameEncoder; +import io.netty.channel.sctp.handler.SimpleSctpChannelHandler; +import io.netty.testsuite.util.SctpSocketAddresses; +import io.netty.util.internal.ExecutorUtil; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.Random; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class SctpMultiHomingEchoTest { + private static final Random random = new Random(); + static final byte[] data = new byte[4096];//could not test ultra jumbo frames + + private static ExecutorService executor; + + static { + random.nextBytes(data); + } + + @BeforeClass + public static void init() { + executor = Executors.newCachedThreadPool(); + } + + @AfterClass + public static void destroy() { + ExecutorUtil.terminate(executor); + } + + protected ChannelFactory newServerSocketChannelFactory(Executor executor) { + return new SctpServerSocketChannelFactory(executor, executor); + } + + protected ChannelFactory newClientSocketChannelFactory(Executor executor) { + return new SctpClientSocketChannelFactory(executor, executor); + } + + @Test(timeout = 15000) + public void testSimpleEcho() throws Throwable { + ServerBootstrap sb = new ServerBootstrap(newServerSocketChannelFactory(executor)); + + ClientBootstrap cb = new ClientBootstrap(newClientSocketChannelFactory(executor)); + + + EchoHandler sh = new EchoHandler(); + EchoHandler ch = new EchoHandler(); + + sb.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + sb.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + sb.getPipeline().addLast("handler", sh); + + cb.getPipeline().addLast("sctp-decoder", new SctpFrameDecoder()); + cb.getPipeline().addLast("sctp-encoder", new SctpFrameEncoder()); + cb.getPipeline().addLast("handler", ch); + + SctpServerChannel serverChannel = (SctpServerChannel) sb.bind(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 0)); + int port = serverChannel.getLocalAddress().getPort(); + + ChannelFuture multiHomingServerBindFuture = serverChannel.bindAddress(InetAddress.getByName(SctpSocketAddresses.LOOP_BACK2)); + assertTrue(multiHomingServerBindFuture.awaitUninterruptibly().isSuccess()); + + ChannelFuture bindFuture = cb.bind(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 0)); + assertTrue(bindFuture.awaitUninterruptibly().isSuccess()); + + SctpChannel clientChannel = (SctpChannel) bindFuture.getChannel(); + + //adding a muti-homing address to client channel + ChannelFuture multiHomingBindFuture = clientChannel.bindAddress(InetAddress.getByName(SctpSocketAddresses.LOOP_BACK2)); + assertTrue(multiHomingBindFuture.awaitUninterruptibly().isSuccess()); + + ChannelFuture connectFuture = clientChannel.connect(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, port)); + assertTrue(connectFuture.awaitUninterruptibly().isSuccess()); + + assertEquals("Client local addresses count should be 2", 2, clientChannel.getAllLocalAddresses().size()); + assertEquals("Client remote addresses count should be 2", 2, clientChannel.getAllRemoteAddresses().size()); + + assertEquals("Server local addresses count should be 2", 2, serverChannel.getAllLocalAddresses().size()); + + for (int i = 0; i < data.length;) { + int length = Math.min(random.nextInt(1024 * 64), data.length - i); + clientChannel.write(ChannelBuffers.wrappedBuffer(data, i, length)); + i += length; + } + + while (ch.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(5); + } catch (InterruptedException e) { + // Ignore. + } + } + + while (sh.counter < data.length) { + if (sh.exception.get() != null) { + break; + } + if (ch.exception.get() != null) { + break; + } + + try { + Thread.sleep(5); + } catch (InterruptedException e) { + // Ignore. + } + } + + //removing already added muti-homing address from client channel + ChannelFuture multiHomingUnbindFuture = clientChannel.unbindAddress(InetAddress.getByName(SctpSocketAddresses.LOOP_BACK2)); + assertTrue(multiHomingUnbindFuture.awaitUninterruptibly().isSuccess()); + + ChannelFuture multiHomingServerUnbindFuture = serverChannel.unbindAddress(InetAddress.getByName(SctpSocketAddresses.LOOP_BACK2)); + assertTrue(multiHomingUnbindFuture.awaitUninterruptibly().isSuccess()); + + + sh.channel.close().awaitUninterruptibly(); + ch.channel.close().awaitUninterruptibly(); + serverChannel.close().awaitUninterruptibly(); + + if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) { + throw sh.exception.get(); + } + if (ch.exception.get() != null && !(ch.exception.get() instanceof IOException)) { + throw ch.exception.get(); + } + if (sh.exception.get() != null) { + throw sh.exception.get(); + } + if (ch.exception.get() != null) { + throw ch.exception.get(); + } + } + + private static class EchoHandler extends SimpleSctpChannelHandler { + volatile Channel channel; + final AtomicReference exception = new AtomicReference(); + volatile int counter; + + EchoHandler() { + } + + @Override + public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) + throws Exception { + channel = e.getChannel(); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) + throws Exception { + ChannelBuffer m = (ChannelBuffer) e.getMessage(); + byte[] actual = new byte[m.readableBytes()]; + m.getBytes(0, actual); + + int lastIdx = counter; + for (int i = 0; i < actual.length; i ++) { + assertEquals(data[i + lastIdx], actual[i]); + } + + if (channel.getParent() != null) { + channel.write(m); + } + + counter += actual.length; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) + throws Exception { + if (exception.compareAndSet(null, e.getCause())) { + e.getChannel().close(); + } + } + + @Override + public void sctpNotificationReceived(ChannelHandlerContext ctx, SctpNotificationEvent event) { + System.out.println("SCTP notification event received :" + event); + } + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpMultiStreamingEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpMultiStreamingEchoTest.java new file mode 100644 index 0000000000..9df1837864 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpMultiStreamingEchoTest.java @@ -0,0 +1,176 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport.sctp; + +import io.netty.bootstrap.ClientBootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ChannelBuffer; +import io.netty.buffer.ChannelBuffers; +import io.netty.channel.*; +import io.netty.channel.sctp.SctpClientSocketChannelFactory; +import io.netty.channel.sctp.SctpFrame; +import io.netty.channel.sctp.SctpServerSocketChannelFactory; +import io.netty.channel.sctp.codec.SctpFrameDecoder; +import io.netty.channel.sctp.codec.SctpFrameEncoder; +import io.netty.testsuite.util.SctpSocketAddresses; +import io.netty.util.internal.ExecutorUtil; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Random; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class SctpMultiStreamingEchoTest { + private static final Random random = new Random(); + + static final SctpFrame [] sctpFrames = new SctpFrame [4]; + + + private static ExecutorService executor; + + static ChannelBuffer makeRandomFrame() { + byte [] data = new byte[512]; + random.nextBytes(data); + return ChannelBuffers.wrappedBuffer(data); + } + + static { + int protocolId = 0;//unknown + for(int streamNumber = 0; streamNumber <= 3; streamNumber ++) { + sctpFrames [streamNumber] = new SctpFrame(protocolId, streamNumber, makeRandomFrame()); + } + } + + @BeforeClass + public static void init() { + executor = Executors.newCachedThreadPool(); + } + + @AfterClass + public static void destroy() { + ExecutorUtil.terminate(executor); + } + + protected ChannelFactory newServerSocketChannelFactory(Executor executor) { + return new SctpServerSocketChannelFactory(executor, executor); + } + + protected ChannelFactory newClientSocketChannelFactory(Executor executor) { + return new SctpClientSocketChannelFactory(executor, executor); + } + + @Test(timeout = 10000) + public void testMultiStreamingEcho() throws Throwable { + ServerBootstrap sb = new ServerBootstrap(newServerSocketChannelFactory(executor)); + + ClientBootstrap cb = new ClientBootstrap(newClientSocketChannelFactory(executor)); + cb.setOption("sctpInitMaxStreams", 4); + + EchoHandler sh = new EchoHandler(); + EchoHandler ch = new EchoHandler(); + + sb.getPipeline().addLast("handler", sh); + + cb.getPipeline().addLast("handler", ch); + + Channel sc = sb.bind(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, 0)); + int port = ((InetSocketAddress) sc.getLocalAddress()).getPort(); + + ChannelFuture ccf = cb.connect(new InetSocketAddress(SctpSocketAddresses.LOOP_BACK, port)); + assertTrue(ccf.awaitUninterruptibly().isSuccess()); + + Channel cc = ccf.getChannel(); + + for(SctpFrame sctpFrame: sctpFrames) { + cc.write(sctpFrame); + } + + + while (sh.counter < sctpFrames.length) { + Thread.sleep(5); + } + while (ch.counter < sctpFrames.length) { + Thread.sleep(5); + } + + assertEquals(sctpFrames.length, sh.counter); + assertEquals(sctpFrames.length, ch.counter); + + sh.channel.close().awaitUninterruptibly(); + ch.channel.close().awaitUninterruptibly(); + + + + if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) { + throw sh.exception.get(); + } + if (ch.exception.get() != null && !(ch.exception.get() instanceof IOException)) { + throw ch.exception.get(); + } + if (sh.exception.get() != null) { + throw sh.exception.get(); + } + if (ch.exception.get() != null) { + throw ch.exception.get(); + } + } + + private static class EchoHandler extends SimpleChannelUpstreamHandler { + volatile Channel channel; + final AtomicReference exception = new AtomicReference(); + volatile int counter; + + EchoHandler() { + } + + @Override + public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) + throws Exception { + channel = e.getChannel(); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) + throws Exception { + SctpFrame sctpFrame = (SctpFrame) e.getMessage(); + + assertEquals(sctpFrames[counter], sctpFrame); + + if (channel.getParent() != null) { + channel.write(sctpFrame); + } + + counter ++ ; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) + throws Exception { + if (exception.compareAndSet(null, e.getCause())) { + e.getChannel().close(); + } + } + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpObjectStreamEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpObjectStreamEchoTest.java new file mode 100644 index 0000000000..55c84e0684 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpObjectStreamEchoTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport.sctp; + +import io.netty.channel.ChannelFactory; +import io.netty.channel.sctp.SctpClientSocketChannelFactory; +import io.netty.channel.sctp.SctpServerSocketChannelFactory; +import io.netty.testsuite.transport.AbstractSocketObjectStreamEchoTest; + +import java.util.concurrent.Executor; + +public class SctpObjectStreamEchoTest extends AbstractSocketObjectStreamEchoTest { + @Override + protected ChannelFactory newServerSocketChannelFactory(Executor executor) { + return new SctpServerSocketChannelFactory(executor, executor); + } + + @Override + protected ChannelFactory newClientSocketChannelFactory(Executor executor) { + return new SctpClientSocketChannelFactory(executor, executor); + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpServerBootstrapTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpServerBootstrapTest.java new file mode 100644 index 0000000000..bcb680c1f7 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpServerBootstrapTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport.sctp; + +import io.netty.channel.ChannelFactory; +import io.netty.channel.sctp.SctpClientSocketChannelFactory; +import io.netty.channel.sctp.SctpServerSocketChannelFactory; +import io.netty.testsuite.transport.AbstractSocketServerBootstrapTest; + +import java.util.concurrent.Executor; + +public class SctpServerBootstrapTest extends AbstractSocketServerBootstrapTest { + @Override + protected ChannelFactory newServerSocketChannelFactory(Executor executor) { + return new SctpServerSocketChannelFactory(executor, executor); + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpSslEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpSslEchoTest.java new file mode 100644 index 0000000000..e840cce264 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpSslEchoTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport.sctp; + +import io.netty.channel.ChannelFactory; +import io.netty.channel.sctp.SctpClientSocketChannelFactory; +import io.netty.channel.sctp.SctpServerSocketChannelFactory; +import io.netty.testsuite.transport.AbstractSocketSslEchoTest; + +import java.util.concurrent.Executor; + +public class SctpSslEchoTest extends AbstractSocketSslEchoTest { + @Override + protected ChannelFactory newServerSocketChannelFactory(Executor executor) { + return new SctpServerSocketChannelFactory(executor, executor); + } + + @Override + protected ChannelFactory newClientSocketChannelFactory(Executor executor) { + return new SctpClientSocketChannelFactory(executor, executor); + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpStringEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpStringEchoTest.java new file mode 100644 index 0000000000..31440bec6a --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpStringEchoTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.transport.sctp; + +import io.netty.channel.ChannelFactory; +import io.netty.channel.sctp.SctpClientSocketChannelFactory; +import io.netty.channel.sctp.SctpServerSocketChannelFactory; +import io.netty.testsuite.transport.AbstractSocketStringEchoTest; + +import java.util.concurrent.Executor; + +public class SctpStringEchoTest extends AbstractSocketStringEchoTest { + @Override + protected ChannelFactory newServerSocketChannelFactory(Executor executor) { + return new SctpServerSocketChannelFactory(executor, executor); + } + + @Override + protected ChannelFactory newClientSocketChannelFactory(Executor executor) { + return new SctpClientSocketChannelFactory(executor, executor); + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/util/DummyHandler.java b/transport-sctp/src/test/java/io/netty/testsuite/util/DummyHandler.java new file mode 100644 index 0000000000..5ec679a904 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/util/DummyHandler.java @@ -0,0 +1,34 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.util; + +import io.netty.channel.ChannelDownstreamHandler; +import io.netty.channel.ChannelEvent; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelUpstreamHandler; + +public class DummyHandler implements ChannelUpstreamHandler, ChannelDownstreamHandler { + + public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) + throws Exception { + ctx.sendUpstream(e); + } + + public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e) + throws Exception { + ctx.sendDownstream(e); + } +} diff --git a/transport-sctp/src/test/java/io/netty/testsuite/util/SctpSocketAddresses.java b/transport-sctp/src/test/java/io/netty/testsuite/util/SctpSocketAddresses.java new file mode 100644 index 0000000000..1435c414a2 --- /dev/null +++ b/transport-sctp/src/test/java/io/netty/testsuite/util/SctpSocketAddresses.java @@ -0,0 +1,23 @@ +/* + * Copyright 2011 The Netty Project + * + * The Netty Project 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 io.netty.testsuite.util; + +public class SctpSocketAddresses { + //io.netty.util.SocketAddresses.LOCALHOST interface has MTU SIZE issues with SCTP, we have to use local loop back interface for testing + public final static String LOOP_BACK = "127.0.0.1"; + public final static String LOOP_BACK2 = "127.0.0.2"; +} diff --git a/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java b/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java index 91f1b05bd5..ce225ef275 100644 --- a/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java +++ b/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java @@ -227,4 +227,45 @@ public class ClientBootstrap extends Bootstrap { // Connect. return ch.connect(remoteAddress); } + + /** + * Attempts to bind a channel with the specified {@code localAddress}. later the channel can be connected + * to a remoteAddress by calling {@link Channel#connect(SocketAddress)}.This method is useful where bind and connect + * need to be done in separate steps. (For example, SCTP Multihoming) + * + * @return a future object which notifies when this bind attempt + * succeeds or fails + * + * @throws ChannelPipelineException + * if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory} + * failed to create a new {@link ChannelPipeline} + */ + public ChannelFuture bind(final SocketAddress localAddress) { + + if (localAddress == null) { + throw new NullPointerException("localAddress"); + } + + ChannelPipeline pipeline; + try { + pipeline = getPipelineFactory().getPipeline(); + } catch (Exception e) { + throw new ChannelPipelineException("Failed to initialize a pipeline.", e); + } + + // Set the options. + Channel ch = getFactory().newChannel(pipeline); + boolean success = false; + try { + ch.getConfig().setOptions(getOptions()); + success = true; + } finally { + if (!success) { + ch.close(); + } + } + + // Bind. + return ch.bind(localAddress); + } } diff --git a/transport/src/main/java/io/netty/channel/CompleteChannelFuture.java b/transport/src/main/java/io/netty/channel/CompleteChannelFuture.java index 59971fb768..7ca47cdd1b 100644 --- a/transport/src/main/java/io/netty/channel/CompleteChannelFuture.java +++ b/transport/src/main/java/io/netty/channel/CompleteChannelFuture.java @@ -48,9 +48,11 @@ public abstract class CompleteChannelFuture implements ChannelFuture { try { listener.operationComplete(this); } catch (Throwable t) { - logger.warn( - "An exception was thrown by " + - ChannelFutureListener.class.getSimpleName() + ".", t); + if (logger.isWarnEnabled()) { + logger.warn( + "An exception was thrown by " + + ChannelFutureListener.class.getSimpleName() + ".", t); + } } } diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelFuture.java b/transport/src/main/java/io/netty/channel/DefaultChannelFuture.java index c9fbc72445..493ad4d799 100644 --- a/transport/src/main/java/io/netty/channel/DefaultChannelFuture.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelFuture.java @@ -56,10 +56,12 @@ public class DefaultChannelFuture implements ChannelFuture { public static void setUseDeadLockChecker(boolean useDeadLockChecker) { if (!useDeadLockChecker && !disabledDeadLockCheckerOnce) { disabledDeadLockCheckerOnce = true; - logger.debug( - "The dead lock checker in " + - DefaultChannelFuture.class.getSimpleName() + - " has been disabled as requested at your own risk."); + if (logger.isDebugEnabled()) { + logger.debug( + "The dead lock checker in " + + DefaultChannelFuture.class.getSimpleName() + + " has been disabled as requested at your own risk."); + } } DefaultChannelFuture.useDeadLockChecker = useDeadLockChecker; } @@ -413,9 +415,11 @@ public class DefaultChannelFuture implements ChannelFuture { try { l.operationComplete(this); } catch (Throwable t) { - logger.warn( - "An exception was thrown by " + - ChannelFutureListener.class.getSimpleName() + ".", t); + if (logger.isWarnEnabled()) { + logger.warn( + "An exception was thrown by " + + ChannelFutureListener.class.getSimpleName() + ".", t); + } } } @@ -453,9 +457,11 @@ public class DefaultChannelFuture implements ChannelFuture { try { l.operationProgressed(this, amount, current, total); } catch (Throwable t) { - logger.warn( - "An exception was thrown by " + - ChannelFutureProgressListener.class.getSimpleName() + ".", t); + if (logger.isWarnEnabled()) { + logger.warn( + "An exception was thrown by " + + ChannelFutureProgressListener.class.getSimpleName() + ".", t); + } } } } diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java index 312431a796..50487ae36e 100644 --- a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java @@ -357,7 +357,9 @@ public class DefaultChannelPipeline implements ChannelPipeline { remove((DefaultChannelHandlerContext) ctx); removed = true; } catch (Throwable t2) { - logger.warn("Failed to remove a handler: " + ctx.getName(), t2); + if (logger.isWarnEnabled()) { + logger.warn("Failed to remove a handler: " + ctx.getName(), t2); + } } if (removed) { @@ -564,8 +566,9 @@ public class DefaultChannelPipeline implements ChannelPipeline { public void sendUpstream(ChannelEvent e) { DefaultChannelHandlerContext head = getActualUpstreamContext(this.head); if (head == null) { - logger.warn( - "The pipeline contains no upstream handlers; discarding: " + e); + if (logger.isWarnEnabled()) { + logger.warn("The pipeline contains no upstream handlers; discarding: " + e); + } return; } @@ -648,9 +651,11 @@ public class DefaultChannelPipeline implements ChannelPipeline { protected void notifyHandlerException(ChannelEvent e, Throwable t) { if (e instanceof ExceptionEvent) { - logger.warn( - "An exception was thrown by a user handler " + - "while handling an exception event (" + e + ")", t); + if (logger.isWarnEnabled()) { + logger.warn( + "An exception was thrown by a user handler " + + "while handling an exception event (" + e + ")", t); + } return; } @@ -664,7 +669,9 @@ public class DefaultChannelPipeline implements ChannelPipeline { try { sink.exceptionCaught(this, e, pe); } catch (Exception e1) { - logger.warn("An exception was thrown by an exception handler.", e1); + if (logger.isWarnEnabled()) { + logger.warn("An exception was thrown by an exception handler.", e1); + } } } @@ -815,7 +822,9 @@ public class DefaultChannelPipeline implements ChannelPipeline { @Override public void eventSunk(ChannelPipeline pipeline, ChannelEvent e) { - logger.warn("Not attached yet; discarding: " + e); + if (logger.isWarnEnabled()) { + logger.warn("Not attached yet; discarding: " + e); + } } @Override diff --git a/transport/src/main/java/io/netty/channel/DefaultFileRegion.java b/transport/src/main/java/io/netty/channel/DefaultFileRegion.java index 096a944507..4ceb20feb9 100644 --- a/transport/src/main/java/io/netty/channel/DefaultFileRegion.java +++ b/transport/src/main/java/io/netty/channel/DefaultFileRegion.java @@ -81,7 +81,9 @@ public class DefaultFileRegion implements FileRegion { try { file.close(); } catch (IOException e) { - logger.warn("Failed to close a file.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to close a file.", e); + } } } } diff --git a/transport/src/main/java/io/netty/channel/group/DefaultChannelGroupFuture.java b/transport/src/main/java/io/netty/channel/group/DefaultChannelGroupFuture.java index d9e42aae11..b11f3b7520 100644 --- a/transport/src/main/java/io/netty/channel/group/DefaultChannelGroupFuture.java +++ b/transport/src/main/java/io/netty/channel/group/DefaultChannelGroupFuture.java @@ -382,9 +382,11 @@ public class DefaultChannelGroupFuture implements ChannelGroupFuture { try { l.operationComplete(this); } catch (Throwable t) { - logger.warn( - "An exception was thrown by " + - ChannelFutureListener.class.getSimpleName() + ".", t); + if (logger.isWarnEnabled()) { + logger.warn( + "An exception was thrown by " + + ChannelFutureListener.class.getSimpleName() + ".", t); + } } } } diff --git a/transport/src/main/java/io/netty/channel/local/LocalClientChannelSink.java b/transport/src/main/java/io/netty/channel/local/LocalClientChannelSink.java index 0ee8cbd265..9b6bd455de 100644 --- a/transport/src/main/java/io/netty/channel/local/LocalClientChannelSink.java +++ b/transport/src/main/java/io/netty/channel/local/LocalClientChannelSink.java @@ -116,8 +116,10 @@ final class LocalClientChannelSink extends AbstractChannelSink { } catch (Exception e) { future.setFailure(e); fireExceptionCaught(channel, e); - logger.warn( - "Failed to initialize an accepted socket.", e); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to initialize an accepted socket.", e); + } return; } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/DefaultNioDatagramChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/nio/DefaultNioDatagramChannelConfig.java index 42c6db96ca..42fb4e3656 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/DefaultNioDatagramChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/DefaultNioDatagramChannelConfig.java @@ -47,10 +47,13 @@ class DefaultNioDatagramChannelConfig extends DefaultDatagramChannelConfig if (getWriteBufferHighWaterMark() < getWriteBufferLowWaterMark()) { // Recover the integrity of the configuration with a sensible value. setWriteBufferLowWaterMark0(getWriteBufferHighWaterMark() >>> 1); - // Notify the user about misconfiguration. - logger.warn("writeBufferLowWaterMark cannot be greater than " - + "writeBufferHighWaterMark; setting to the half of the " - + "writeBufferHighWaterMark."); + if (logger.isWarnEnabled()) { + // Notify the user about misconfiguration. + logger.warn("writeBufferLowWaterMark cannot be greater than " + + "writeBufferHighWaterMark; setting to the half of the " + + "writeBufferHighWaterMark."); + } + } } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/DefaultNioSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/nio/DefaultNioSocketChannelConfig.java index e0228af325..b637f012ae 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/DefaultNioSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/DefaultNioSocketChannelConfig.java @@ -55,11 +55,14 @@ class DefaultNioSocketChannelConfig extends DefaultSocketChannelConfig if (getWriteBufferHighWaterMark() < getWriteBufferLowWaterMark()) { // Recover the integrity of the configuration with a sensible value. setWriteBufferLowWaterMark0(getWriteBufferHighWaterMark() >>> 1); - // Notify the user about misconfiguration. - logger.warn( - "writeBufferLowWaterMark cannot be greater than " + - "writeBufferHighWaterMark; setting to the half of the " + - "writeBufferHighWaterMark."); + if (logger.isWarnEnabled()) { + // Notify the user about misconfiguration. + logger.warn( + "writeBufferLowWaterMark cannot be greater than " + + "writeBufferHighWaterMark; setting to the half of the " + + "writeBufferHighWaterMark."); + } + } } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioClientSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioClientSocketChannel.java index 571a832e29..756f9d0cc5 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioClientSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioClientSocketChannel.java @@ -52,9 +52,12 @@ final class NioClientSocketChannel extends NioSocketChannel { try { socket.close(); } catch (IOException e) { - logger.warn( - "Failed to close a partially initialized socket.", - e); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to close a partially initialized socket.", + e); + } + } } } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioClientSocketPipelineSink.java b/transport/src/main/java/io/netty/channel/socket/nio/NioClientSocketPipelineSink.java index 14226a5ee6..ca2333659f 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioClientSocketPipelineSink.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioClientSocketPipelineSink.java @@ -208,7 +208,9 @@ class NioClientSocketPipelineSink extends AbstractChannelSink { try { selector.close(); } catch (Throwable t) { - logger.warn("Failed to close a selector.", t); + if (logger.isWarnEnabled()) { + logger.warn("Failed to close a selector.", t); + } } this.selector = selector = null; // The method will return to the caller at this point. @@ -302,8 +304,11 @@ class NioClientSocketPipelineSink extends AbstractChannelSink { try { selector.close(); } catch (IOException e) { - logger.warn( - "Failed to close a selector.", e); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to close a selector.", e); + } + } finally { this.selector = null; } @@ -320,8 +325,11 @@ class NioClientSocketPipelineSink extends AbstractChannelSink { shutdown = false; } } catch (Throwable t) { - logger.warn( - "Unexpected exception in the selector loop.", t); + if (logger.isWarnEnabled()) { + logger.warn( + "Unexpected exception in the selector loop.", t); + } + // Prevent possible consecutive immediate failures. try { diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramWorker.java b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramWorker.java index 54ed300b96..fb7ddde380 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramWorker.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramWorker.java @@ -48,8 +48,6 @@ class NioDatagramWorker extends AbstractNioWorker { super(executor); } - - @Override protected boolean read(final SelectionKey key) { final NioDatagramChannel channel = (NioDatagramChannel) key.attachment(); diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioProviderMetadata.java b/transport/src/main/java/io/netty/channel/socket/nio/NioProviderMetadata.java index b4152e7b89..f2993f52b2 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioProviderMetadata.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioProviderMetadata.java @@ -70,18 +70,25 @@ final class NioProviderMetadata { if (constraintLevel < 0) { constraintLevel = 2; - logger.debug( - "Couldn't determine the NIO constraint level from " + - "the system properties; using the safest level (2)"); + if (logger.isDebugEnabled()) { + logger.debug( + "Couldn't determine the NIO constraint level from " + + "the system properties; using the safest level (2)"); + } } else if (constraintLevel != 0) { - logger.info( - "Using the autodetected NIO constraint level: " + - constraintLevel + - " (Use better NIO provider for better performance)"); + if (logger.isInfoEnabled()) { + logger.info( + "Using the autodetected NIO constraint level: " + + constraintLevel + + " (Use better NIO provider for better performance)"); + } } else { - logger.debug( - "Using the autodetected NIO constraint level: " + - constraintLevel); + if (logger.isDebugEnabled()) { + logger.debug( + "Using the autodetected NIO constraint level: " + + constraintLevel); + } + } } @@ -235,7 +242,9 @@ final class NioProviderMetadata { ch.socket().bind(new InetSocketAddress(0)); ch.configureBlocking(false); } catch (Throwable e) { - logger.warn("Failed to configure a temporary socket.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to configure a temporary socket.", e); + } return -1; } @@ -243,7 +252,9 @@ final class NioProviderMetadata { try { loop = new SelectorLoop(); } catch (Throwable e) { - logger.warn("Failed to open a temporary selector.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to open a temporary selector.", e); + } return -1; } @@ -251,7 +262,9 @@ final class NioProviderMetadata { try { ch.register(loop.selector, 0); } catch (Throwable e) { - logger.warn("Failed to register a temporary selector.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to register a temporary selector.", e); + } return -1; } @@ -337,7 +350,9 @@ final class NioProviderMetadata { try { ch.close(); } catch (Throwable e) { - logger.warn("Failed to close a temporary socket.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to close a temporary socket.", e); + } } } @@ -367,7 +382,9 @@ final class NioProviderMetadata { try { loop.selector.close(); } catch (Throwable e) { - logger.warn("Failed to close a temporary selector.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to close a temporary selector.", e); + } } } } @@ -405,7 +422,9 @@ final class NioProviderMetadata { } keys.clear(); } catch (IOException e) { - logger.warn("Failed to wait for a temporary selector.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to wait for a temporary selector.", e); + } } } } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java index 91b6ca0cb2..4c96469ac9 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java @@ -73,8 +73,11 @@ final class NioServerSocketChannel extends AbstractServerChannel try { socket.close(); } catch (IOException e2) { - logger.warn( - "Failed to close a partially initialized socket.", e2); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to close a partially initialized socket.", e2); + } + } throw new ChannelException("Failed to enter non-blocking mode.", e); diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketPipelineSink.java b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketPipelineSink.java index 9410102d2c..5de57a6f01 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketPipelineSink.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketPipelineSink.java @@ -243,8 +243,10 @@ class NioServerSocketPipelineSink extends AbstractChannelSink { // Closed as requested. break; } catch (Throwable e) { - logger.warn( - "Failed to accept a connection.", e); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to accept a connection.", e); + } try { Thread.sleep(1000); } catch (InterruptedException e1) { @@ -266,14 +268,18 @@ class NioServerSocketPipelineSink extends AbstractChannelSink { worker.register(NioAcceptedSocketChannel.create(channel.getFactory(), pipeline, channel, NioServerSocketPipelineSink.this, acceptedSocket, worker, currentThread), null); } catch (Exception e) { - logger.warn( - "Failed to initialize an accepted socket.", e); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to initialize an accepted socket.", e); + } try { acceptedSocket.close(); } catch (IOException e2) { - logger.warn( - "Failed to close a partially accepted socket.", - e2); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to close a partially accepted socket.", + e2); + } } } } @@ -283,7 +289,9 @@ class NioServerSocketPipelineSink extends AbstractChannelSink { try { selector.close(); } catch (Exception e) { - logger.warn("Failed to close a selector.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to close a selector.", e); + } } } } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioWorker.java b/transport/src/main/java/io/netty/channel/socket/nio/NioWorker.java index e22e614da4..f9fb541cdd 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioWorker.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioWorker.java @@ -43,8 +43,6 @@ class NioWorker extends AbstractNioWorker { super(executor); } - - @Override protected boolean read(SelectionKey k) { final SocketChannel ch = (SocketChannel) k.channel(); diff --git a/transport/src/main/java/io/netty/channel/socket/nio/SelectorUtil.java b/transport/src/main/java/io/netty/channel/socket/nio/SelectorUtil.java index 79ade13920..51c8bbaa8c 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/SelectorUtil.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/SelectorUtil.java @@ -32,10 +32,13 @@ final class SelectorUtil { try { selector.select(500); } catch (CancelledKeyException e) { + if (logger.isDebugEnabled()) { + logger.debug( + CancelledKeyException.class.getSimpleName() + + " raised by a Selector - JDK bug?", e); + } // Harmless exception - log anyway - logger.debug( - CancelledKeyException.class.getSimpleName() + - " raised by a Selector - JDK bug?", e); + } } diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java index 87b256f4a2..ab2f6ede7f 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java @@ -72,8 +72,11 @@ final class OioServerSocketChannel extends AbstractServerChannel try { socket.close(); } catch (IOException e2) { - logger.warn( - "Failed to close a partially initialized socket.", e2); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to close a partially initialized socket.", e2); + } + } throw new ChannelException( "Failed to set the server socket timeout.", e); diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketPipelineSink.java b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketPipelineSink.java index 1e594ace15..e8c4c3278f 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketPipelineSink.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketPipelineSink.java @@ -203,14 +203,18 @@ class OioServerSocketPipelineSink extends AbstractChannelSink { workerExecutor, new OioWorker(acceptedChannel)); } catch (Exception e) { - logger.warn( - "Failed to initialize an accepted socket.", e); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to initialize an accepted socket.", e); + } try { acceptedSocket.close(); } catch (IOException e2) { - logger.warn( - "Failed to close a partially accepted socket.", - e2); + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to close a partially accepted socket.", + e2); + } } } } catch (SocketTimeoutException e) { @@ -221,9 +225,12 @@ class OioServerSocketPipelineSink extends AbstractChannelSink { if (!channel.socket.isBound() || channel.socket.isClosed()) { break; } + + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to accept a connection.", e); + } - logger.warn( - "Failed to accept a connection.", e); try { Thread.sleep(1000); } catch (InterruptedException e1) {