From b268f0b3334a231b0f929ba4d5292b7b6c576f47 Mon Sep 17 00:00:00 2001 From: Jestan Nirojan Date: Sun, 23 Sep 2012 14:12:32 +0800 Subject: [PATCH 1/4] Added SCTP Codec Handlers + minor refactoring --- .../codec/sctp/SctpMessageDecoder.java | 39 ++++++ .../codec/sctp/SctpMessageEncoder.java | 52 ++++++++ .../handler/codec/sctp/package-info.java | 23 ++++ .../netty/example/sctp/OioSctpEchoClient.java | 6 +- .../example/sctp/SctpEchoClientHandler.java | 11 +- .../example/sctp/SctpEchoServerHandler.java | 9 +- .../io/netty/channel/socket/SctpData.java | 120 ------------------ .../io/netty/channel/socket/SctpMessage.java | 111 +++++++++++++++- ...cation.java => SctpNotificationEvent.java} | 6 +- .../socket/SctpNotificationHandler.java | 3 +- .../channel/socket/nio/NioSctpChannel.java | 6 +- .../channel/socket/oio/OioSctpChannel.java | 6 +- 12 files changed, 243 insertions(+), 149 deletions(-) create mode 100644 codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageDecoder.java create mode 100644 codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageEncoder.java create mode 100644 codec/src/main/java/io/netty/handler/codec/sctp/package-info.java delete mode 100644 transport/src/main/java/io/netty/channel/socket/SctpData.java rename transport/src/main/java/io/netty/channel/socket/{SctpNotification.java => SctpNotificationEvent.java} (90%) diff --git a/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageDecoder.java b/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageDecoder.java new file mode 100644 index 0000000000..08076adb3a --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageDecoder.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012 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.handler.codec.sctp; + +import com.sun.nio.sctp.MessageInfo; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.socket.SctpMessage; +import io.netty.handler.codec.MessageToMessageDecoder; + +public class SctpMessageDecoder extends MessageToMessageDecoder { + private ByteBuf cumulation = Unpooled.EMPTY_BUFFER; + + @Override + public ByteBuf decode(ChannelHandlerContext ctx, SctpMessage msg) throws Exception { + ByteBuf byteBuf = cumulation = Unpooled.wrappedBuffer(cumulation, msg.getPayloadBuffer()); + if (msg.isComplete()) { + cumulation = Unpooled.EMPTY_BUFFER; + return byteBuf; + } else { + return null; + } + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageEncoder.java b/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageEncoder.java new file mode 100644 index 0000000000..103b7b8b5a --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageEncoder.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012 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.handler.codec.sctp; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.MessageBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundMessageHandlerAdapter; +import io.netty.channel.socket.SctpMessage; +import io.netty.handler.codec.EncoderException; + +public class SctpMessageEncoder extends ChannelOutboundMessageHandlerAdapter { + private final int streamIdentifier; + private final int protocolIdentifier; + + public SctpMessageEncoder(int streamIdentifier, int protocolIdentifier) { + this.streamIdentifier = streamIdentifier; + this.protocolIdentifier = protocolIdentifier; + } + + @Override + public void flush(ChannelHandlerContext ctx, ChannelFuture future) throws Exception { + ByteBuf in = ctx.outboundByteBuffer(); + + try { + MessageBuf out = ctx.nextOutboundMessageBuffer(); + ByteBuf payload = Unpooled.buffer(in.readableBytes()); + payload.writeBytes(in); + out.add(new SctpMessage(streamIdentifier, protocolIdentifier, payload)); + in.discardReadBytes(); + } catch (Throwable t) { + ctx.fireExceptionCaught(new EncoderException(t)); + } + + ctx.flush(future); + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/sctp/package-info.java b/codec/src/main/java/io/netty/handler/codec/sctp/package-info.java new file mode 100644 index 0000000000..c277b3b769 --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/sctp/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright 2012 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. + */ + +/** + * Encoder and decoder which transform a {@link io.netty.channel.socket.SctpMessage} into a + * {@link io.netty.buffer.ByteBuf} and vice versa. + * + * @apiviz.exclude \.oneone\. + */ +package io.netty.handler.codec.sctp; diff --git a/example/src/main/java/io/netty/example/sctp/OioSctpEchoClient.java b/example/src/main/java/io/netty/example/sctp/OioSctpEchoClient.java index 6bd9c28ff3..fed754aa35 100644 --- a/example/src/main/java/io/netty/example/sctp/OioSctpEchoClient.java +++ b/example/src/main/java/io/netty/example/sctp/OioSctpEchoClient.java @@ -77,16 +77,16 @@ public class OioSctpEchoClient { public static void main(String[] args) throws Exception { // Print usage if no argument is specified. - if (args.length < 2 || args.length > 3) { +/* if (args.length < 2 || args.length > 3) { System.err.println( "Usage: " + OioSctpEchoClient.class.getSimpleName() + " []"); return; - } + }*/ // Parse options. final String host = "localhost"; - final int port = Integer.parseInt(args[1]); + final int port = 2556; final int firstMessageSize; if (args.length == 3) { firstMessageSize = Integer.parseInt(args[2]); diff --git a/example/src/main/java/io/netty/example/sctp/SctpEchoClientHandler.java b/example/src/main/java/io/netty/example/sctp/SctpEchoClientHandler.java index ef0319ee9a..c5b3f79945 100644 --- a/example/src/main/java/io/netty/example/sctp/SctpEchoClientHandler.java +++ b/example/src/main/java/io/netty/example/sctp/SctpEchoClientHandler.java @@ -20,7 +20,6 @@ import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundMessageHandlerAdapter; -import io.netty.channel.socket.SctpData; import io.netty.channel.socket.SctpMessage; import java.util.logging.Level; @@ -53,16 +52,14 @@ public class SctpEchoClientHandler extends ChannelInboundMessageHandlerAdapter out = ctx.nextOutboundMessageBuffer(); - out.add(msg); - ctx.flush(); - } + MessageBuf out = ctx.nextOutboundMessageBuffer(); + out.add(msg); + ctx.flush(); } @Override diff --git a/example/src/main/java/io/netty/example/sctp/SctpEchoServerHandler.java b/example/src/main/java/io/netty/example/sctp/SctpEchoServerHandler.java index 770452a1a9..1041087cae 100644 --- a/example/src/main/java/io/netty/example/sctp/SctpEchoServerHandler.java +++ b/example/src/main/java/io/netty/example/sctp/SctpEchoServerHandler.java @@ -19,7 +19,6 @@ import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundMessageHandlerAdapter; -import io.netty.channel.socket.SctpData; import io.netty.channel.socket.SctpMessage; import java.util.logging.Level; @@ -43,10 +42,8 @@ public class SctpEchoServerHandler extends ChannelInboundMessageHandlerAdapter out = ctx.nextOutboundMessageBuffer(); - out.add(msg); - ctx.flush(); - } + MessageBuf out = ctx.nextOutboundMessageBuffer(); + out.add(msg); + ctx.flush(); } } diff --git a/transport/src/main/java/io/netty/channel/socket/SctpData.java b/transport/src/main/java/io/netty/channel/socket/SctpData.java deleted file mode 100644 index 25804872c0..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/SctpData.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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.socket; - -import com.sun.nio.sctp.MessageInfo; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; - -/** - * Representation of SCTP Data Chunk - */ -public final class SctpData implements SctpMessage { - private final int streamIdentifier; - private final int protocolIdentifier; - - private final ByteBuf payloadBuffer; - - private MessageInfo msgInfo; - - /** - * Essential data that is being carried within SCTP Data Chunk - * @param protocolIdentifier of payload - * @param streamIdentifier that you want to send the payload - * @param payloadBuffer channel buffer - */ - public SctpData(int protocolIdentifier, int streamIdentifier, ByteBuf payloadBuffer) { - this.protocolIdentifier = protocolIdentifier; - this.streamIdentifier = streamIdentifier; - this.payloadBuffer = payloadBuffer; - } - - public SctpData(MessageInfo msgInfo, ByteBuf payloadBuffer) { - this.msgInfo = msgInfo; - this.streamIdentifier = msgInfo.streamNumber(); - this.protocolIdentifier = msgInfo.payloadProtocolID(); - this.payloadBuffer = payloadBuffer; - } - - public int getStreamIdentifier() { - return streamIdentifier; - } - - public int getProtocolIdentifier() { - return protocolIdentifier; - } - - public ByteBuf getPayloadBuffer() { - if (payloadBuffer.readable()) { - return payloadBuffer.slice(); - } else { - return Unpooled.EMPTY_BUFFER; - } - } - - public MessageInfo getMessageInfo() { - return msgInfo; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (o == null || getClass() != o.getClass()) { - return false; - } - - SctpData sctpFrame = (SctpData) 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("SctpFrame{"). - append("streamIdentifier="). - append(streamIdentifier). - append(", protocolIdentifier="). - append(protocolIdentifier). - append(", payloadBuffer="). - append(ByteBufUtil.hexDump(getPayloadBuffer())). - append('}').toString(); - } -} diff --git a/transport/src/main/java/io/netty/channel/socket/SctpMessage.java b/transport/src/main/java/io/netty/channel/socket/SctpMessage.java index 545dcd5c96..139e34f447 100644 --- a/transport/src/main/java/io/netty/channel/socket/SctpMessage.java +++ b/transport/src/main/java/io/netty/channel/socket/SctpMessage.java @@ -15,8 +15,115 @@ */ package io.netty.channel.socket; +import com.sun.nio.sctp.MessageInfo; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; + /** - * A marker interface for a SCTP/IP message + * Representation of SCTP Data Chunk */ -public interface SctpMessage { +public final class SctpMessage { + private final int streamIdentifier; + private final int protocolIdentifier; + + private final ByteBuf payloadBuffer; + + private MessageInfo msgInfo; + + /** + * Essential data that is being carried within SCTP Data Chunk + * @param protocolIdentifier of payload + * @param streamIdentifier that you want to send the payload + * @param payloadBuffer channel buffer + */ + public SctpMessage(int protocolIdentifier, int streamIdentifier, ByteBuf payloadBuffer) { + this.protocolIdentifier = protocolIdentifier; + this.streamIdentifier = streamIdentifier; + this.payloadBuffer = payloadBuffer; + } + + public SctpMessage(MessageInfo msgInfo, ByteBuf payloadBuffer) { + this.msgInfo = msgInfo; + this.streamIdentifier = msgInfo.streamNumber(); + this.protocolIdentifier = msgInfo.payloadProtocolID(); + this.payloadBuffer = payloadBuffer; + } + + public int getStreamIdentifier() { + return streamIdentifier; + } + + public int getProtocolIdentifier() { + return protocolIdentifier; + } + + public ByteBuf getPayloadBuffer() { + if (payloadBuffer.readable()) { + return payloadBuffer.slice(); + } else { + return Unpooled.EMPTY_BUFFER; + } + } + + public MessageInfo getMessageInfo() { + return msgInfo; + } + + public boolean isComplete() { + if (msgInfo != null) { + return msgInfo.isComplete(); + } else { + //all outbound sctp messages are complete + return true; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + SctpMessage sctpFrame = (SctpMessage) 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("SctpFrame{"). + append("streamIdentifier="). + append(streamIdentifier). + append(", protocolIdentifier="). + append(protocolIdentifier). + append(", payloadBuffer="). + append(ByteBufUtil.hexDump(getPayloadBuffer())). + append('}').toString(); + } } diff --git a/transport/src/main/java/io/netty/channel/socket/SctpNotification.java b/transport/src/main/java/io/netty/channel/socket/SctpNotificationEvent.java similarity index 90% rename from transport/src/main/java/io/netty/channel/socket/SctpNotification.java rename to transport/src/main/java/io/netty/channel/socket/SctpNotificationEvent.java index a02e4bac73..3fefa73162 100644 --- a/transport/src/main/java/io/netty/channel/socket/SctpNotification.java +++ b/transport/src/main/java/io/netty/channel/socket/SctpNotificationEvent.java @@ -17,12 +17,12 @@ package io.netty.channel.socket; import com.sun.nio.sctp.Notification; -public final class SctpNotification implements SctpMessage { +public final class SctpNotificationEvent { private Notification notification; private Object attachment; - public SctpNotification(Notification notification, Object attachment) { + public SctpNotificationEvent(Notification notification, Object attachment) { this.notification = notification; this.attachment = attachment; } @@ -44,7 +44,7 @@ public final class SctpNotification implements SctpMessage { return false; } - SctpNotification that = (SctpNotification) o; + SctpNotificationEvent that = (SctpNotificationEvent) o; if (!attachment.equals(that.attachment)) { return false; diff --git a/transport/src/main/java/io/netty/channel/socket/SctpNotificationHandler.java b/transport/src/main/java/io/netty/channel/socket/SctpNotificationHandler.java index b409a027d5..3ec99d94c1 100644 --- a/transport/src/main/java/io/netty/channel/socket/SctpNotificationHandler.java +++ b/transport/src/main/java/io/netty/channel/socket/SctpNotificationHandler.java @@ -22,7 +22,6 @@ import com.sun.nio.sctp.Notification; import com.sun.nio.sctp.PeerAddressChangeNotification; import com.sun.nio.sctp.SendFailedNotification; import com.sun.nio.sctp.ShutdownNotification; -import io.netty.channel.ChannelPipeline; public class SctpNotificationHandler extends AbstractNotificationHandler { @@ -58,7 +57,7 @@ public class SctpNotificationHandler extends AbstractNotificationHandler } private void updateInboundBuffer(Notification notification, Object o) { - sctpChannel.pipeline().inboundMessageBuffer().add(new SctpNotification(notification, o)); + sctpChannel.pipeline().fireUserEventTriggered(new SctpNotificationEvent(notification, o)); } } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioSctpChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioSctpChannel.java index f3f9a8d7b8..5a4c248582 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioSctpChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioSctpChannel.java @@ -29,7 +29,7 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.socket.DefaultSctpChannelConfig; import io.netty.channel.socket.SctpChannelConfig; -import io.netty.channel.socket.SctpData; +import io.netty.channel.socket.SctpMessage; import io.netty.channel.socket.SctpNotificationHandler; import io.netty.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; @@ -229,13 +229,13 @@ public class NioSctpChannel extends AbstractNioMessageChannel implements io.nett } data.flip(); - buf.add(new SctpData(messageInfo, Unpooled.wrappedBuffer(data))); + buf.add(new SctpMessage(messageInfo, Unpooled.wrappedBuffer(data))); return 1; } @Override protected int doWriteMessages(MessageBuf buf, boolean lastSpin) throws Exception { - SctpData packet = (SctpData) buf.peek(); + SctpMessage packet = (SctpMessage) buf.peek(); ByteBuf data = packet.getPayloadBuffer(); int dataLen = data.readableBytes(); ByteBuffer nioData; diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioSctpChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioSctpChannel.java index 7219ff2f1d..586e885cc2 100755 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioSctpChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioSctpChannel.java @@ -29,7 +29,7 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.socket.DefaultSctpChannelConfig; import io.netty.channel.socket.SctpChannelConfig; -import io.netty.channel.socket.SctpData; +import io.netty.channel.socket.SctpMessage; import io.netty.channel.socket.SctpNotificationHandler; import io.netty.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; @@ -120,7 +120,7 @@ public class OioSctpChannel extends AbstractOioMessageChannel } data.flip(); - buf.add(new SctpData(messageInfo, Unpooled.wrappedBuffer(data))); + buf.add(new SctpMessage(messageInfo, Unpooled.wrappedBuffer(data))); if (readSuspended) { return 0; @@ -132,7 +132,7 @@ public class OioSctpChannel extends AbstractOioMessageChannel @Override protected void doWriteMessages(MessageBuf buf) throws Exception { - SctpData packet = (SctpData) buf.poll(); + SctpMessage packet = (SctpMessage) buf.poll(); ByteBuf data = packet.getPayloadBuffer(); int dataLen = data.readableBytes(); ByteBuffer nioData; From 942f05d33673a9ca0bb5f3f0ca1552fa1b70b336 Mon Sep 17 00:00:00 2001 From: Jestan Nirojan Date: Sun, 23 Sep 2012 14:16:26 +0800 Subject: [PATCH 2/4] Corrected cmd args in SCTP Sample clients --- .../java/io/netty/example/sctp/NioSctpEchoClient.java | 2 +- .../java/io/netty/example/sctp/OioSctpEchoClient.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/example/src/main/java/io/netty/example/sctp/NioSctpEchoClient.java b/example/src/main/java/io/netty/example/sctp/NioSctpEchoClient.java index b76371d325..f7fc649d54 100644 --- a/example/src/main/java/io/netty/example/sctp/NioSctpEchoClient.java +++ b/example/src/main/java/io/netty/example/sctp/NioSctpEchoClient.java @@ -86,7 +86,7 @@ public class NioSctpEchoClient { } // Parse options. - final String host = "localhost"; + final String host = args[0]; final int port = Integer.parseInt(args[1]); final int firstMessageSize; if (args.length == 3) { diff --git a/example/src/main/java/io/netty/example/sctp/OioSctpEchoClient.java b/example/src/main/java/io/netty/example/sctp/OioSctpEchoClient.java index fed754aa35..0036b82091 100644 --- a/example/src/main/java/io/netty/example/sctp/OioSctpEchoClient.java +++ b/example/src/main/java/io/netty/example/sctp/OioSctpEchoClient.java @@ -77,16 +77,16 @@ public class OioSctpEchoClient { public static void main(String[] args) throws Exception { // Print usage if no argument is specified. -/* if (args.length < 2 || args.length > 3) { + if (args.length < 2 || args.length > 3) { System.err.println( "Usage: " + OioSctpEchoClient.class.getSimpleName() + " []"); return; - }*/ + } // Parse options. - final String host = "localhost"; - final int port = 2556; + final String host = args[0]; + final int port = Integer.parseInt(args[1]); final int firstMessageSize; if (args.length == 3) { firstMessageSize = Integer.parseInt(args[2]); From 64ebece730e06be174dbb9e7b4558bab7b5f32e1 Mon Sep 17 00:00:00 2001 From: Jestan Nirojan Date: Sat, 29 Sep 2012 01:42:21 +0800 Subject: [PATCH 3/4] Added SCTP Codec --- .../sctp/SctpInboundByteStreamHandler.java | 63 +++++++++++++++ .../sctp/SctpMessageCompletionHandler.java | 76 +++++++++++++++++++ ....java => SctpMessageToMessageDecoder.java} | 24 +++--- .../sctp/SctpMessageToMessageEncoder.java | 36 +++++++++ ...ava => SctpOutboundByteStreamHandler.java} | 16 +++- .../handler/codec/sctp/package-info.java | 5 +- 6 files changed, 201 insertions(+), 19 deletions(-) create mode 100644 codec/src/main/java/io/netty/handler/codec/sctp/SctpInboundByteStreamHandler.java create mode 100644 codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageCompletionHandler.java rename codec/src/main/java/io/netty/handler/codec/sctp/{SctpMessageDecoder.java => SctpMessageToMessageDecoder.java} (56%) create mode 100644 codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageToMessageEncoder.java rename codec/src/main/java/io/netty/handler/codec/sctp/{SctpMessageEncoder.java => SctpOutboundByteStreamHandler.java} (74%) diff --git a/codec/src/main/java/io/netty/handler/codec/sctp/SctpInboundByteStreamHandler.java b/codec/src/main/java/io/netty/handler/codec/sctp/SctpInboundByteStreamHandler.java new file mode 100644 index 0000000000..4873bccea8 --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/sctp/SctpInboundByteStreamHandler.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012 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.handler.codec.sctp; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.socket.SctpMessage; +import io.netty.handler.codec.CodecException; + +/** + * A ChannelHandler which receives {@link SctpMessage} belongs to a application protocol form a specific SCTP Stream + * and decode it as {@link ByteBuf}. + */ +public class SctpInboundByteStreamHandler extends ChannelInboundMessageHandlerAdapter { + private final int protocolIdentifier; + private final int streamIdentifier; + + + /** + * @param streamIdentifier accepted stream number, this should be >=0 or <= max stream number of the association. + * @param protocolIdentifier supported application protocol. + */ + public SctpInboundByteStreamHandler(int protocolIdentifier, int streamIdentifier) { + this.protocolIdentifier = protocolIdentifier; + this.streamIdentifier = streamIdentifier; + } + + protected boolean isDecodable(SctpMessage msg) { + return msg.getProtocolIdentifier() == protocolIdentifier && msg.getStreamIdentifier() == streamIdentifier; + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, SctpMessage msg) throws Exception { + if (!isDecodable(msg)) { + ctx.nextInboundMessageBuffer().add(msg); + ctx.fireInboundBufferUpdated(); + return; + } + + if (!msg.isComplete()) { + throw new CodecException(String.format("Received SctpMessage is not complete, please add %s in the " + + "pipeline before this handler", SctpMessageCompletionHandler.class.getSimpleName())); + } + + ctx.nextInboundByteBuffer().writeBytes(msg.getPayloadBuffer()); + ctx.fireInboundBufferUpdated(); + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageCompletionHandler.java b/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageCompletionHandler.java new file mode 100644 index 0000000000..afdc890567 --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageCompletionHandler.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012 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.handler.codec.sctp; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.socket.SctpMessage; + +import java.util.HashMap; +import java.util.Map; + +public class SctpMessageCompletionHandler extends ChannelInboundMessageHandlerAdapter { + private Map fragments = new HashMap(); + + /** + */ + public SctpMessageCompletionHandler() { + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, SctpMessage msg) throws Exception { + + final ByteBuf byteBuf = msg.getPayloadBuffer(); + final int protocolIdentifier = msg.getProtocolIdentifier(); + final int streamIdentifier = msg.getStreamIdentifier(); + final boolean isComplete = msg.isComplete(); + + ByteBuf frag; + + if (fragments.containsKey(streamIdentifier)) { + frag = fragments.remove(streamIdentifier); + } else { + frag = Unpooled.EMPTY_BUFFER; + } + + if (isComplete && !frag.readable()) { + //data chunk is not fragmented + fireAssembledMessage(ctx, msg); + } else if (!isComplete && frag.readable()) { + //more message to complete + fragments.put(streamIdentifier, Unpooled.wrappedBuffer(frag, byteBuf)); + } else if (isComplete && frag.readable()) { + //last message to complete + fragments.remove(streamIdentifier); + SctpMessage assembledMsg = new SctpMessage( + protocolIdentifier, + streamIdentifier, + Unpooled.wrappedBuffer(frag, byteBuf)); + fireAssembledMessage(ctx, assembledMsg); + } else { + //first incomplete message + fragments.put(streamIdentifier, byteBuf); + } + } + + protected void fireAssembledMessage(ChannelHandlerContext ctx, SctpMessage assembledMsg) { + ctx.nextInboundMessageBuffer().add(assembledMsg); + ctx.fireInboundBufferUpdated(); + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageDecoder.java b/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageToMessageDecoder.java similarity index 56% rename from codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageDecoder.java rename to codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageToMessageDecoder.java index 08076adb3a..cfeb2c570a 100644 --- a/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageToMessageDecoder.java @@ -16,24 +16,24 @@ package io.netty.handler.codec.sctp; -import com.sun.nio.sctp.MessageInfo; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; import io.netty.channel.socket.SctpMessage; +import io.netty.handler.codec.CodecException; import io.netty.handler.codec.MessageToMessageDecoder; -public class SctpMessageDecoder extends MessageToMessageDecoder { - private ByteBuf cumulation = Unpooled.EMPTY_BUFFER; +public abstract class SctpMessageToMessageDecoder extends MessageToMessageDecoder { @Override - public ByteBuf decode(ChannelHandlerContext ctx, SctpMessage msg) throws Exception { - ByteBuf byteBuf = cumulation = Unpooled.wrappedBuffer(cumulation, msg.getPayloadBuffer()); - if (msg.isComplete()) { - cumulation = Unpooled.EMPTY_BUFFER; - return byteBuf; + public boolean isDecodable(Object msg) throws Exception { + if (msg instanceof SctpMessage) { + SctpMessage sctpMsg = (SctpMessage) msg; + if (sctpMsg.isComplete()) { + return true; + } + + throw new CodecException(String.format("Received SctpMessage is not complete, please add %s in " + + "the pipeline before this handler", SctpMessageCompletionHandler.class.getSimpleName())); } else { - return null; + return false; } } } diff --git a/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageToMessageEncoder.java b/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageToMessageEncoder.java new file mode 100644 index 0000000000..fe94baa6fc --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageToMessageEncoder.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012 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.handler.codec.sctp; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.socket.SctpMessage; +import io.netty.handler.codec.MessageToMessageEncoder; + +public abstract class SctpMessageToMessageEncoder extends MessageToMessageEncoder { + + /** + * Returns {@code true} if and only if the specified message can be encoded by this encoder. + * + * @param msg the message + */ + public boolean isEncodable(Object msg) throws Exception { + return true; + } + + public abstract SctpMessage encode(ChannelHandlerContext ctx, I msg) throws Exception; +} diff --git a/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageEncoder.java b/codec/src/main/java/io/netty/handler/codec/sctp/SctpOutboundByteStreamHandler.java similarity index 74% rename from codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageEncoder.java rename to codec/src/main/java/io/netty/handler/codec/sctp/SctpOutboundByteStreamHandler.java index 103b7b8b5a..127793a08f 100644 --- a/codec/src/main/java/io/netty/handler/codec/sctp/SctpMessageEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/sctp/SctpOutboundByteStreamHandler.java @@ -20,15 +20,25 @@ import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundMessageHandlerAdapter; +import io.netty.channel.ChannelOutboundByteHandlerAdapter; import io.netty.channel.socket.SctpMessage; import io.netty.handler.codec.EncoderException; -public class SctpMessageEncoder extends ChannelOutboundMessageHandlerAdapter { +/** + * A ChannelHandler which transform {@link ByteBuf} to {@link SctpMessage} and send it through a specific stream + * with given protocol identifier. + * + */ +public class SctpOutboundByteStreamHandler extends ChannelOutboundByteHandlerAdapter { private final int streamIdentifier; private final int protocolIdentifier; - public SctpMessageEncoder(int streamIdentifier, int protocolIdentifier) { + + /** + * @param streamIdentifier stream number, this should be >=0 or <= max stream number of the association. + * @param protocolIdentifier supported application protocol id. + */ + public SctpOutboundByteStreamHandler(int streamIdentifier, int protocolIdentifier) { this.streamIdentifier = streamIdentifier; this.protocolIdentifier = protocolIdentifier; } diff --git a/codec/src/main/java/io/netty/handler/codec/sctp/package-info.java b/codec/src/main/java/io/netty/handler/codec/sctp/package-info.java index c277b3b769..e4fac28733 100644 --- a/codec/src/main/java/io/netty/handler/codec/sctp/package-info.java +++ b/codec/src/main/java/io/netty/handler/codec/sctp/package-info.java @@ -15,9 +15,6 @@ */ /** - * Encoder and decoder which transform a {@link io.netty.channel.socket.SctpMessage} into a - * {@link io.netty.buffer.ByteBuf} and vice versa. - * - * @apiviz.exclude \.oneone\. + * Decoder and encoders to manage message completion and multi-streaming codec in SCTP/IP. */ package io.netty.handler.codec.sctp; From 7afa237f3f74c2e508d08a2501efd66601431c27 Mon Sep 17 00:00:00 2001 From: Jestan Nirojan Date: Sun, 30 Sep 2012 14:14:34 +0800 Subject: [PATCH 4/4] Forward ported SCTP Echo Testcases --- .../transport/socket/AbstractSctpTest.java | 74 +++++++ .../transport/socket/SctpEchoTest.java | 201 ++++++++++++++++++ .../socket/SocketTestPermutation.java | 82 +++++++ .../io/netty/testsuite/util/TestUtils.java | 30 ++- 4 files changed, 383 insertions(+), 4 deletions(-) create mode 100644 testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSctpTest.java create mode 100644 testsuite/src/test/java/io/netty/testsuite/transport/socket/SctpEchoTest.java diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSctpTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSctpTest.java new file mode 100644 index 0000000000..45ec98aa41 --- /dev/null +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSctpTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012 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.socket; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.logging.InternalLogger; +import io.netty.logging.InternalLoggerFactory; +import io.netty.testsuite.transport.socket.SocketTestPermutation.Factory; +import io.netty.testsuite.util.TestUtils; +import io.netty.util.NetworkConstants; +import org.junit.Rule; +import org.junit.rules.TestName; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.InetSocketAddress; +import java.util.List; +import java.util.Map.Entry; + +public abstract class AbstractSctpTest { + + private static final List, Factory>> COMBO = + SocketTestPermutation.sctpChannel(); + + @Rule + public final TestName testName = new TestName(); + + protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass()); + + protected volatile ServerBootstrap sb; + protected volatile Bootstrap cb; + protected volatile InetSocketAddress addr; + protected volatile Factory currentBootstrap; + + protected void run() throws Throwable { + int i = 0; + for (Entry, Factory> e: COMBO) { + currentBootstrap = e.getValue(); + sb = e.getKey().newInstance(); + cb = e.getValue().newInstance(); + addr = new InetSocketAddress( + NetworkConstants.LOCALHOST, TestUtils.getFreePort()); + sb.localAddress(addr); + cb.remoteAddress(addr); + + logger.info(String.format( + "Running: %s %d of %d", testName.getMethodName(), ++ i, COMBO.size())); + try { + Method m = getClass().getDeclaredMethod( + testName.getMethodName(), ServerBootstrap.class, Bootstrap.class); + m.invoke(this, sb, cb); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } finally { + sb.shutdown(); + cb.shutdown(); + } + } + } +} diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SctpEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SctpEchoTest.java new file mode 100644 index 0000000000..7899b4cc9f --- /dev/null +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SctpEchoTest.java @@ -0,0 +1,201 @@ +/* + * Copyright 2012 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.socket; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.socket.SctpChannel; +import io.netty.handler.codec.sctp.SctpInboundByteStreamHandler; +import io.netty.handler.codec.sctp.SctpMessageCompletionHandler; +import io.netty.handler.codec.sctp.SctpOutboundByteStreamHandler; +import io.netty.testsuite.util.TestUtils; +import org.junit.Assume; +import org.junit.Ignore; +import org.junit.Test; + +import java.io.IOException; +import java.util.Random; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; + +public class SctpEchoTest extends AbstractSctpTest { + + private static final Random random = new Random(); + static final byte[] data = new byte[4096];//could not test ultra jumbo frames + + static { + random.nextBytes(data); + } + + @Test + public void testSimpleEcho() throws Throwable { + Assume.assumeTrue(TestUtils.isSctpSupported()); + run(); + } + + public void testSimpleEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable { + testSimpleEcho0(sb, cb, Integer.MAX_VALUE); + } + + @Test + @Ignore("TODO: fix this after OioSctp EventLoop done") + public void testSimpleEchoWithBoundedBuffer() throws Throwable { + Assume.assumeTrue(TestUtils.isSctpSupported()); + run(); + } + + public void testSimpleEchoWithBoundedBuffer(ServerBootstrap sb, Bootstrap cb) throws Throwable { + testSimpleEcho0(sb, cb, 32); + } + + private static void testSimpleEcho0(ServerBootstrap sb, Bootstrap cb, int maxInboundBufferSize) throws Throwable { + final EchoHandler sh = new EchoHandler(maxInboundBufferSize); + final EchoHandler ch = new EchoHandler(maxInboundBufferSize); + + sb.childHandler(new ChannelInitializer() { + @Override + public void initChannel(SctpChannel c) throws Exception { + c.pipeline().addLast( + new SctpMessageCompletionHandler(), + new SctpInboundByteStreamHandler(0, 0), + new SctpOutboundByteStreamHandler(0, 0), + sh); + } + }); + cb.handler(new ChannelInitializer() { + @Override + public void initChannel(SctpChannel c) throws Exception { + c.pipeline().addLast( + new SctpMessageCompletionHandler(), + new SctpInboundByteStreamHandler(0, 0), + new SctpOutboundByteStreamHandler(0, 0), + ch); + } + }); + + Channel sc = sb.bind().sync().channel(); + Channel cc = cb.connect().sync().channel(); + + for (int i = 0; i < data.length; ) { + int length = Math.min(random.nextInt(1024 * 64), data.length - i); + cc.write(Unpooled.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().sync(); + ch.channel.close().sync(); + sc.close().sync(); + + 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 ChannelInboundByteHandlerAdapter { + private final int maxInboundBufferSize; + volatile Channel channel; + final AtomicReference exception = new AtomicReference(); + volatile int counter; + + EchoHandler(int maxInboundBufferSize) { + this.maxInboundBufferSize = maxInboundBufferSize; + } + + @Override + public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { + return Unpooled.buffer(0, maxInboundBufferSize); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) + throws Exception { + channel = ctx.channel(); + } + + @Override + public void inboundBufferUpdated( + ChannelHandlerContext ctx, ByteBuf in) + throws Exception { + byte[] actual = new byte[in.readableBytes()]; + in.readBytes(actual); + + int lastIdx = counter; + for (int i = 0; i < actual.length; i++) { + assertEquals(data[i + lastIdx], actual[i]); + } + + if (channel.parent() != null) { + channel.write(Unpooled.wrappedBuffer(actual)); + } + + counter += actual.length; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, + Throwable cause) throws Exception { + if (exception.compareAndSet(null, cause)) { + ctx.close(); + } + } + } +} diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java index ab35cb658c..2eabc7a89c 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java @@ -26,11 +26,15 @@ import io.netty.channel.socket.aio.AioSocketChannel; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSctpServerChannel; import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.channel.socket.nio.NioSctpChannel; import io.netty.channel.socket.oio.OioDatagramChannel; import io.netty.channel.socket.oio.OioEventLoopGroup; import io.netty.channel.socket.oio.OioServerSocketChannel; +import io.netty.channel.socket.oio.OioSctpServerChannel; import io.netty.channel.socket.oio.OioSocketChannel; +import io.netty.channel.socket.oio.OioSctpChannel; import java.util.ArrayList; import java.util.List; @@ -130,6 +134,43 @@ final class SocketTestPermutation { return list; } + static List, Factory>> sctpChannel() { + List, Factory>> list = + new ArrayList, Factory>>(); + + // Make the list of SCTP ServerBootstrap factories. + List> sbfs = sctpServerChannel(); + + // Make the list of SCTP Bootstrap factories. + List> cbfs = sctpClientChannel(); + + // Populate the combinations + for (Factory sbf: sbfs) { + for (Factory cbf: cbfs) { + final Factory sbf0 = sbf; + final Factory cbf0 = cbf; + list.add(new Entry, Factory>() { + @Override + public Factory getKey() { + return sbf0; + } + + @Override + public Factory getValue() { + return cbf0; + } + + @Override + public Factory setValue(Factory value) { + throw new UnsupportedOperationException(); + } + }); + } + } + + return list; + } + static List> serverSocket() { List> list = new ArrayList>(); @@ -197,6 +238,47 @@ final class SocketTestPermutation { return list; } + static List> sctpServerChannel() { + List> list = new ArrayList>(); + + // Make the list of ServerBootstrap factories. + list.add(new Factory() { + @Override + public ServerBootstrap newInstance() { + return new ServerBootstrap(). + group(new NioEventLoopGroup(), new NioEventLoopGroup()). + channel(NioSctpServerChannel.class); + } + }); + list.add(new Factory() { + @Override + public ServerBootstrap newInstance() { + return new ServerBootstrap(). + group(new OioEventLoopGroup(), new OioEventLoopGroup()). + channel(OioSctpServerChannel.class); + } + }); + + return list; + } + + static List> sctpClientChannel() { + List> list = new ArrayList>(); + list.add(new Factory() { + @Override + public Bootstrap newInstance() { + return new Bootstrap().group(new NioEventLoopGroup()).channel(NioSctpChannel.class); + } + }); + list.add(new Factory() { + @Override + public Bootstrap newInstance() { + return new Bootstrap().group(new OioEventLoopGroup()).channel(OioSctpChannel.class); + } + }); + return list; + } + private SocketTestPermutation() {} interface Factory { diff --git a/testsuite/src/test/java/io/netty/testsuite/util/TestUtils.java b/testsuite/src/test/java/io/netty/testsuite/util/TestUtils.java index 38e2f8ac78..0eacf567e0 100644 --- a/testsuite/src/test/java/io/netty/testsuite/util/TestUtils.java +++ b/testsuite/src/test/java/io/netty/testsuite/util/TestUtils.java @@ -15,15 +15,13 @@ */ package io.netty.testsuite.util; +import com.sun.nio.sctp.SctpChannel; import io.netty.util.NetworkConstants; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; +import java.util.*; public class TestUtils { @@ -78,5 +76,29 @@ public class TestUtils { throw new RuntimeException("unable to find a free port"); } + /** + * Return true if SCTP is supported by the running os. + * + */ + public static boolean isSctpSupported() { + String os = System.getProperty("os.name").toLowerCase(Locale.UK); + if (os.equals("unix") || os.equals("linux") || os.equals("sun") || os.equals("solaris")) { + try { + SctpChannel.open(); + } catch (IOException e) { + // ignore + } catch (UnsupportedOperationException e) { + // This exception may get thrown if the OS does not have + // the shared libs installed. + System.out.print("Not supported: " + e.getMessage()); + return false; + + } + + return true; + } + return false; + } + private TestUtils() { } } \ No newline at end of file