From cbd88179051a51e66abac4c65d4af865c203c337 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Mon, 8 Jul 2013 19:03:40 +0900 Subject: [PATCH] Remove MessageList from public API and change ChannelInbound/OutboundHandler accordingly I must admit MesageList was pain in the ass. Instead of forcing a handler always loop over the list of messages, this commit splits messageReceived(ctx, list) into two event handlers: - messageReceived(ctx, msg) - mmessageReceivedLast(ctx) When Netty reads one or more messages, messageReceived(ctx, msg) event is triggered for each message. Once the current read operation is finished, messageReceivedLast() is triggered to tell the handler that the last messageReceived() was the last message in the current batch. Similarly, for outbound, write(ctx, list) has been split into two: - write(ctx, msg) - flush(ctx, promise) Instead of writing a list of message with a promise, a user is now supposed to call write(msg) multiple times and then call flush() to actually flush the buffered messages. Please note that write() doesn't have a promise with it. You must call flush() to get notified on completion. (or you can use writeAndFlush()) Other changes: - Because MessageList is completely hidden, codec framework uses List instead of MessageList as an output parameter. --- .../handler/codec/http/HttpClientCodec.java | 6 +- .../codec/http/HttpContentDecoder.java | 5 +- .../codec/http/HttpContentEncoder.java | 16 +- .../codec/http/HttpObjectAggregator.java | 7 +- .../handler/codec/http/HttpObjectDecoder.java | 9 +- .../handler/codec/http/HttpObjectEncoder.java | 10 +- .../multipart/HttpPostRequestEncoder.java | 16 +- .../websocketx/WebSocket00FrameDecoder.java | 5 +- .../websocketx/WebSocket08FrameDecoder.java | 8 +- .../websocketx/WebSocketClientHandshaker.java | 4 +- .../WebSocketClientProtocolHandler.java | 4 +- ...bSocketClientProtocolHandshakeHandler.java | 13 +- .../websocketx/WebSocketFrameAggregator.java | 5 +- .../websocketx/WebSocketProtocolHandler.java | 7 +- .../websocketx/WebSocketServerHandshaker.java | 4 +- .../WebSocketServerHandshaker00.java | 2 +- .../WebSocketServerProtocolHandler.java | 24 ++- ...bSocketServerProtocolHandshakeHandler.java | 58 +++--- .../handler/codec/spdy/SpdyFrameDecoder.java | 30 ++- .../handler/codec/spdy/SpdyHttpDecoder.java | 4 +- .../handler/codec/spdy/SpdyHttpEncoder.java | 3 +- .../spdy/SpdyHttpResponseStreamIdHandler.java | 6 +- .../handler/codec/spdy/SpdyOrHttpChooser.java | 5 +- .../codec/spdy/SpdySessionHandler.java | 164 ++++++++--------- .../WebSocketServerProtocolHandlerTest.java | 20 +- .../codec/spdy/SpdyFrameDecoderTest.java | 6 +- .../codec/spdy/SpdySessionHandlerTest.java | 18 +- .../codec/socks/SocksAuthRequestDecoder.java | 5 +- .../codec/socks/SocksAuthResponseDecoder.java | 5 +- .../codec/socks/SocksCmdRequestDecoder.java | 5 +- .../codec/socks/SocksCmdResponseDecoder.java | 5 +- .../codec/socks/SocksInitRequestDecoder.java | 3 +- .../codec/socks/SocksInitResponseDecoder.java | 5 +- .../handler/codec/ByteToMessageCodec.java | 24 +-- .../handler/codec/ByteToMessageDecoder.java | 122 ++++++------- .../io/netty/handler/codec/CodecOutput.java | 74 ++++++++ .../codec/DelimiterBasedFrameDecoder.java | 5 +- .../codec/FixedLengthFrameDecoder.java | 5 +- .../codec/LengthFieldBasedFrameDecoder.java | 4 +- .../handler/codec/LineBasedFrameDecoder.java | 5 +- .../handler/codec/MessageToByteEncoder.java | 64 ++----- .../handler/codec/MessageToMessageCodec.java | 24 +-- .../codec/MessageToMessageDecoder.java | 49 +++-- .../codec/MessageToMessageEncoder.java | 55 +++--- .../netty/handler/codec/ReplayingDecoder.java | 19 +- .../handler/codec/base64/Base64Decoder.java | 5 +- .../handler/codec/base64/Base64Encoder.java | 5 +- .../handler/codec/bytes/ByteArrayDecoder.java | 5 +- .../handler/codec/bytes/ByteArrayEncoder.java | 5 +- .../codec/compression/JZlibDecoder.java | 10 +- .../codec/compression/JZlibEncoder.java | 13 +- .../codec/compression/JdkZlibEncoder.java | 2 +- .../compression/SnappyFramedDecoder.java | 7 +- .../CompatibleMarshallingDecoder.java | 11 +- .../codec/protobuf/ProtobufDecoder.java | 12 +- .../codec/protobuf/ProtobufEncoder.java | 13 +- .../ProtobufVarint32FrameDecoder.java | 8 +- .../handler/codec/string/StringDecoder.java | 4 +- .../handler/codec/ReplayingDecoderTest.java | 14 +- .../example/applet/AppletDiscardServer.java | 2 +- .../example/discard/DiscardClientHandler.java | 10 +- .../example/discard/DiscardServerHandler.java | 9 +- .../netty/example/echo/EchoClientHandler.java | 12 +- .../netty/example/echo/EchoServerHandler.java | 10 +- .../example/factorial/BigIntegerDecoder.java | 4 +- .../factorial/FactorialClientHandler.java | 10 +- .../factorial/FactorialServerHandler.java | 27 +-- .../example/filetransfer/FileServer.java | 10 +- .../file/HttpStaticFileServerHandler.java | 20 +- .../HttpHelloWorldServerHandler.java | 24 +-- .../http/snoop/HttpSnoopClientHandler.java | 2 +- .../http/snoop/HttpSnoopServerHandler.java | 36 ++-- .../example/http/upload/HttpUploadClient.java | 6 +- .../http/upload/HttpUploadClientHandler.java | 2 +- .../http/upload/HttpUploadServerHandler.java | 4 +- .../autobahn/AutobahnServerHandler.java | 34 ++-- .../client/WebSocketClientHandler.java | 2 +- .../html5/CustomTextFrameHandler.java | 2 +- .../server/WebSocketServerHandler.java | 4 +- .../sslserver/WebSocketSslServerHandler.java | 4 +- .../io/netty/example/localecho/LocalEcho.java | 2 +- .../localecho/LocalEchoClientHandler.java | 2 +- .../localecho/LocalEchoServerHandler.java | 11 +- .../objectecho/ObjectEchoClientHandler.java | 10 +- .../objectecho/ObjectEchoServerHandler.java | 10 +- .../PortUnificationServerHandler.java | 4 +- .../proxy/HexDumpProxyBackendHandler.java | 5 +- .../proxy/HexDumpProxyFrontendHandler.java | 7 +- .../example/qotm/QuoteOfTheMomentClient.java | 2 +- .../qotm/QuoteOfTheMomentClientHandler.java | 2 +- .../qotm/QuoteOfTheMomentServerHandler.java | 2 +- .../netty/example/rxtx/RxtxClientHandler.java | 2 +- .../example/sctp/SctpEchoClientHandler.java | 11 +- .../example/sctp/SctpEchoServerHandler.java | 10 +- .../example/securechat/SecureChatClient.java | 2 +- .../securechat/SecureChatClientHandler.java | 2 +- .../securechat/SecureChatServerHandler.java | 2 +- .../example/socksproxy/RelayHandler.java | 8 +- .../socksproxy/SocksServerConnectHandler.java | 4 +- .../socksproxy/SocksServerHandler.java | 2 +- .../example/socksproxy/SocksServerUtils.java | 2 +- .../io/netty/example/telnet/TelnetClient.java | 2 +- .../example/telnet/TelnetClientHandler.java | 2 +- .../example/telnet/TelnetServerHandler.java | 4 +- .../udt/echo/bytes/ByteEchoClientHandler.java | 22 ++- .../udt/echo/bytes/ByteEchoServerHandler.java | 10 +- .../echo/message/MsgEchoClientHandler.java | 21 ++- .../echo/message/MsgEchoServerHandler.java | 10 +- .../echo/rendezvous/MsgEchoPeerHandler.java | 21 ++- .../rendezvousBytes/ByteEchoPeerHandler.java | 21 ++- .../example/uptime/UptimeClientHandler.java | 8 +- .../worldclock/WorldClockClientHandler.java | 16 +- .../worldclock/WorldClockServerHandler.java | 52 +++--- .../netty/handler/logging/LoggingHandler.java | 51 +++--- .../java/io/netty/handler/ssl/SslHandler.java | 146 ++++++++------- .../handler/stream/ChunkedByteInput.java | 27 --- .../io/netty/handler/stream/ChunkedFile.java | 24 +-- .../io/netty/handler/stream/ChunkedInput.java | 13 +- .../handler/stream/ChunkedMessageInput.java | 28 --- .../netty/handler/stream/ChunkedNioFile.java | 45 +++-- .../handler/stream/ChunkedNioStream.java | 27 ++- .../netty/handler/stream/ChunkedStream.java | 26 ++- .../handler/stream/ChunkedWriteHandler.java | 57 ++---- .../handler/timeout/IdleStateHandler.java | 45 +---- .../handler/timeout/ReadTimeoutHandler.java | 5 +- .../handler/timeout/WriteTimeoutHandler.java | 5 +- .../AbstractTrafficShapingHandler.java | 30 ++- .../stream/ChunkedWriteHandlerTest.java | 22 +-- .../transport/sctp/SctpEchoTest.java | 6 +- .../socket/DatagramMulticastTest.java | 13 +- .../transport/socket/DatagramUnicastTest.java | 11 +- .../socket/SocketBufReleaseTest.java | 10 +- .../transport/socket/SocketEchoTest.java | 19 +- .../socket/SocketFileRegionTest.java | 13 +- .../socket/SocketFixedLengthEchoTest.java | 9 +- .../socket/SocketGatheringWriteTest.java | 11 +- .../socket/SocketObjectEchoTest.java | 29 ++- .../SocketShutdownOutputByPeerTest.java | 2 +- .../SocketShutdownOutputBySelfTest.java | 4 +- .../transport/socket/SocketSpdyEchoTest.java | 14 +- .../transport/socket/SocketSslEchoTest.java | 11 +- .../transport/socket/SocketStartTlsTest.java | 12 +- .../socket/SocketStringEchoTest.java | 9 +- .../socket/WriteBeforeRegisteredTest.java | 2 +- .../udt/UDTClientServerConnectionTest.java | 13 +- .../channel/sctp/nio/NioSctpChannel.java | 10 +- .../sctp/nio/NioSctpServerChannel.java | 6 +- .../channel/sctp/oio/OioSctpChannel.java | 13 +- .../sctp/oio/OioSctpServerChannel.java | 11 +- .../sctp/SctpInboundByteStreamHandler.java | 5 +- .../sctp/SctpMessageCompletionHandler.java | 4 +- .../sctp/SctpOutboundByteStreamHandler.java | 5 +- .../udt/nio/NioUdtAcceptorChannel.java | 3 +- .../udt/nio/NioUdtByteAcceptorChannel.java | 5 +- .../udt/nio/NioUdtMessageAcceptorChannel.java | 5 +- .../nio/NioUdtMessageConnectorChannel.java | 10 +- .../netty/test/udt/util/EchoByteHandler.java | 18 +- .../test/udt/util/EchoMessageHandler.java | 19 +- .../io/netty/bootstrap/ServerBootstrap.java | 8 +- .../io/netty/channel/AbstractChannel.java | 71 ++++---- .../netty/channel/AbstractServerChannel.java | 16 +- .../main/java/io/netty/channel/Channel.java | 15 +- .../netty/channel/ChannelDuplexHandler.java | 9 +- .../netty/channel/ChannelHandlerContext.java | 5 +- .../netty/channel/ChannelInboundHandler.java | 4 +- .../channel/ChannelInboundHandlerAdapter.java | 19 +- .../netty/channel/ChannelInboundInvoker.java | 5 +- .../netty/channel/ChannelOutboundBuffer.java | 69 ++++--- .../netty/channel/ChannelOutboundHandler.java | 7 +- .../ChannelOutboundHandlerAdapter.java | 11 +- .../netty/channel/ChannelOutboundInvoker.java | 33 ++-- .../io/netty/channel/ChannelPipeline.java | 18 +- .../channel/CombinedChannelDuplexHandler.java | 18 +- .../channel/DefaultChannelHandlerContext.java | 134 +++++++++----- .../netty/channel/DefaultChannelPipeline.java | 70 +++---- .../java/io/netty/channel/MessageList.java | 2 +- .../channel/SimpleChannelInboundHandler.java | 71 ++------ .../channel/embedded/EmbeddedChannel.java | 38 ++-- .../io/netty/channel/group/ChannelGroup.java | 14 -- .../channel/group/DefaultChannelGroup.java | 23 +-- .../io/netty/channel/local/LocalChannel.java | 34 ++-- .../channel/local/LocalServerChannel.java | 18 +- .../channel/nio/AbstractNioByteChannel.java | 24 +-- .../nio/AbstractNioMessageChannel.java | 28 +-- .../channel/oio/AbstractOioByteChannel.java | 45 ++--- .../oio/AbstractOioMessageChannel.java | 16 +- .../socket/nio/NioDatagramChannel.java | 9 +- .../socket/nio/NioServerSocketChannel.java | 6 +- .../channel/socket/nio/NioSocketChannel.java | 32 ++-- .../socket/oio/OioDatagramChannel.java | 8 +- .../socket/oio/OioServerSocketChannel.java | 6 +- .../netty/channel/AbstractEventLoopTest.java | 2 +- .../channel/DefaultChannelPipelineTest.java | 46 ++--- .../netty/channel/local/LocalChannelTest.java | 15 +- .../local/LocalTransportThreadModelTest.java | 172 ++++++++---------- .../local/LocalTransportThreadModelTest2.java | 15 +- .../local/LocalTransportThreadModelTest3.java | 23 ++- .../channel/nio/NioDatagramChannelTest.java | 11 +- 198 files changed, 1791 insertions(+), 1808 deletions(-) create mode 100644 codec/src/main/java/io/netty/handler/codec/CodecOutput.java delete mode 100644 handler/src/main/java/io/netty/handler/stream/ChunkedByteInput.java delete mode 100644 handler/src/main/java/io/netty/handler/stream/ChunkedMessageInput.java diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientCodec.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientCodec.java index 8d8aef5d3d..850faa26ad 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientCodec.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientCodec.java @@ -19,10 +19,10 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.CombinedChannelDuplexHandler; -import io.netty.channel.MessageList; import io.netty.handler.codec.PrematureChannelClosureException; import java.util.ArrayDeque; +import java.util.List; import java.util.Queue; import java.util.concurrent.atomic.AtomicLong; @@ -86,7 +86,7 @@ public final class HttpClientCodec @Override protected void encode( - ChannelHandlerContext ctx, HttpObject msg, MessageList out) throws Exception { + ChannelHandlerContext ctx, HttpObject msg, List out) throws Exception { if (msg instanceof HttpRequest && !done) { queue.offer(((HttpRequest) msg).getMethod()); } @@ -110,7 +110,7 @@ public final class HttpClientCodec @Override protected void decode( - ChannelHandlerContext ctx, ByteBuf buffer, MessageList out) throws Exception { + ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception { if (done) { int readable = actualReadableBytes(); if (readable == 0) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java index 45ec52391d..992f281f0f 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java @@ -19,11 +19,12 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufHolder; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.util.ReferenceCountUtil; +import java.util.List; + /** * Decodes the content of the received {@link HttpRequest} and {@link HttpContent}. * The original content is replaced with the new content decoded by the @@ -51,7 +52,7 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, HttpObject msg, List out) throws Exception { if (msg instanceof HttpResponse && ((HttpResponse) msg).getStatus().code() == 100) { if (!(msg instanceof LastHttpContent)) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java index 46ed555bb5..0f9eed2c0f 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java @@ -18,7 +18,6 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufHolder; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.MessageToMessageCodec; import io.netty.handler.codec.http.HttpHeaders.Names; @@ -26,6 +25,7 @@ import io.netty.handler.codec.http.HttpHeaders.Values; import io.netty.util.ReferenceCountUtil; import java.util.ArrayDeque; +import java.util.List; import java.util.Queue; /** @@ -69,7 +69,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec out) + protected void decode(ChannelHandlerContext ctx, HttpRequest msg, List out) throws Exception { String acceptedEncoding = msg.headers().get(HttpHeaders.Names.ACCEPT_ENCODING); if (acceptedEncoding == null) { @@ -80,7 +80,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec out) throws Exception { + protected void encode(ChannelHandlerContext ctx, HttpObject msg, List out) throws Exception { final boolean isFull = msg instanceof HttpResponse && msg instanceof LastHttpContent; switch (state) { case AWAIT_HEADERS: { @@ -163,7 +163,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec out) { + private void encodeContent(HttpContent c, List out) { ByteBuf content = c.content(); encode(content, out); @@ -256,20 +256,20 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec out) { + private void encode(ByteBuf in, List out) { // call retain here as it will call release after its written to the channel encoder.writeOutbound(in.retain()); fetchEncoderOutput(out); } - private void finishEncode(MessageList out) { + private void finishEncode(List out) { if (encoder.finish()) { fetchEncoderOutput(out); } encoder = null; } - private void fetchEncoderOutput(MessageList out) { + private void fetchEncoderOutput(List out) { for (;;) { ByteBuf buf = (ByteBuf) encoder.readOutbound(); if (buf == null) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectAggregator.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectAggregator.java index 5aa88958c0..0cd13b4e02 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectAggregator.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectAggregator.java @@ -15,20 +15,21 @@ */ package io.netty.handler.codec.http; +import static io.netty.handler.codec.http.HttpHeaders.is100ContinueExpected; +import static io.netty.handler.codec.http.HttpHeaders.removeTransferEncodingChunked; import io.netty.buffer.ByteBuf; import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; -import io.netty.channel.MessageList; import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.TooLongFrameException; import io.netty.util.CharsetUtil; import io.netty.util.ReferenceCountUtil; -import static io.netty.handler.codec.http.HttpHeaders.*; +import java.util.List; /** * A {@link ChannelHandler} that aggregates an {@link HttpMessage} @@ -108,7 +109,7 @@ public class HttpObjectAggregator extends MessageToMessageDecoder { } @Override - protected void decode(ChannelHandlerContext ctx, HttpObject msg, MessageList out) throws Exception { + protected void decode(ChannelHandlerContext ctx, HttpObject msg, List out) throws Exception { FullHttpMessage currentMessage = this.currentMessage; if (msg instanceof HttpMessage) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index b97852d258..524412dc8a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -19,7 +19,6 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; -import io.netty.channel.MessageList; import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.TooLongFrameException; @@ -168,7 +167,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception { switch (state()) { case SKIP_CONTROL_CHARS: { try { @@ -421,7 +420,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder out) throws Exception { + protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { decode(ctx, in, out); // Handle the last unfinished message. @@ -488,7 +487,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder out) { + private void reset(List out) { if (out != null) { HttpMessage message = this.message; ByteBuf content = this.content; @@ -539,7 +538,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder out) { + private void readFixedLengthContent(ByteBuf buffer, List out) { //we have a content-length so we just read the correct number of bytes long length = HttpHeaders.getContentLength(message, -1); assert length <= Integer.MAX_VALUE; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java index a95e597559..7f16b712e4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java @@ -15,16 +15,18 @@ */ package io.netty.handler.codec.http; +import static io.netty.handler.codec.http.HttpConstants.COLON; +import static io.netty.handler.codec.http.HttpConstants.CR; +import static io.netty.handler.codec.http.HttpConstants.LF; +import static io.netty.handler.codec.http.HttpConstants.SP; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.util.CharsetUtil; +import java.util.List; import java.util.Map; -import static io.netty.handler.codec.http.HttpConstants.*; - /** * Encodes an {@link HttpMessage} or an {@link HttpContent} into * a {@link ByteBuf}. @@ -52,7 +54,7 @@ public abstract class HttpObjectEncoder extends MessageTo private int state = ST_INIT; @Override - protected void encode(ChannelHandlerContext ctx, HttpObject msg, MessageList out) throws Exception { + protected void encode(ChannelHandlerContext ctx, HttpObject msg, List out) throws Exception { if (msg instanceof HttpMessage) { if (state != ST_INIT) { throw new IllegalStateException("unexpected message type: " + msg.getClass().getSimpleName()); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java index 82d64a4c85..de13cb18c3 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java @@ -15,8 +15,9 @@ */ package io.netty.handler.codec.http.multipart; +import static io.netty.buffer.Unpooled.wrappedBuffer; import io.netty.buffer.ByteBuf; -import io.netty.channel.MessageList; +import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultHttpContent; @@ -28,7 +29,7 @@ import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.stream.ChunkedMessageInput; +import io.netty.handler.stream.ChunkedInput; import io.netty.util.internal.ThreadLocalRandom; import java.io.File; @@ -43,12 +44,10 @@ import java.util.ListIterator; import java.util.Map; import java.util.regex.Pattern; -import static io.netty.buffer.Unpooled.*; - /** * This encoder will help to encode Request for a FORM as POST. */ -public class HttpPostRequestEncoder implements ChunkedMessageInput { +public class HttpPostRequestEncoder implements ChunkedInput { /** * Different modes to use to encode form data. @@ -942,12 +941,11 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput * if the encoding is in error */ @Override - public boolean readChunk(MessageList buffer) throws ErrorDataEncoderException { + public HttpContent readChunk(ChannelHandlerContext ctx) throws Exception { if (isLastChunkSent) { - return false; + return null; } else { - buffer.add(nextChunk()); - return true; + return nextChunk(); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java index 350a0346f6..f88229fcdf 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java @@ -17,10 +17,11 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.TooLongFrameException; +import java.util.List; + /** * Decodes {@link ByteBuf}s into {@link WebSocketFrame}s. *

@@ -50,7 +51,7 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder implements W } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { // Discard all data received if closing handshake was received before. if (receivedClosingHandshake) { in.skipBytes(actualReadableBytes()); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java index be5ea0be38..463afb1d6c 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java @@ -54,16 +54,16 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.CorruptedFrameException; import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.TooLongFrameException; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; +import java.util.List; + /** * Decodes a web socket frame from wire protocol version 8 format. This code was forked from webbit and modified. @@ -121,7 +121,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { // Discard all data received if closing handshake was received before. if (receivedClosingHandshake) { @@ -403,7 +403,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, List out) throws Exception { if (handleCloseFrames && frame instanceof CloseWebSocketFrame) { ctx.close(); return; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandshakeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandshakeHandler.java index 3049aee09e..f90783be66 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandshakeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandshakeHandler.java @@ -18,10 +18,10 @@ package io.netty.handler.codec.http.websocketx; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.http.FullHttpResponse; -class WebSocketClientProtocolHandshakeHandler extends SimpleChannelInboundHandler { +class WebSocketClientProtocolHandshakeHandler extends ChannelInboundHandlerAdapter { private final WebSocketClientHandshaker handshaker; public WebSocketClientProtocolHandshakeHandler(WebSocketClientHandshaker handshaker) { @@ -45,9 +45,14 @@ class WebSocketClientProtocolHandshakeHandler extends SimpleChannelInboundHandle } @Override - protected void messageReceived(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + if (!(msg instanceof FullHttpResponse)) { + ctx.fireMessageReceived(msg); + return; + } + if (!handshaker.isHandshakeComplete()) { - handshaker.finishHandshake(ctx.channel(), msg); + handshaker.finishHandshake(ctx.channel(), (FullHttpResponse) msg); ctx.fireUserEventTriggered( WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE); ctx.pipeline().remove(this); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregator.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregator.java index 10eb9e31f2..080bc7dcb0 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregator.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregator.java @@ -18,10 +18,11 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; import io.netty.buffer.CompositeByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.TooLongFrameException; +import java.util.List; + /** * Handler that aggregate fragmented WebSocketFrame's. * @@ -47,7 +48,7 @@ public class WebSocketFrameAggregator extends MessageToMessageDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg, List out) throws Exception { if (currentFrame == null) { tooLongFrameFound = false; if (msg.isFinalFragment()) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandler.java index 49ca45ec86..4aad5698a1 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandler.java @@ -17,15 +17,16 @@ package io.netty.handler.codec.http.websocketx; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.MessageToMessageDecoder; +import java.util.List; + abstract class WebSocketProtocolHandler extends MessageToMessageDecoder { @Override - protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, MessageList out) throws Exception { + protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, List out) throws Exception { if (frame instanceof PingWebSocketFrame) { frame.content().retain(); - ctx.channel().write(new PongWebSocketFrame(frame.content())); + ctx.channel().write(new PongWebSocketFrame(frame.content())).flush(); return; } if (frame instanceof PongWebSocketFrame) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java index 9e54945017..8b0fe91795 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java @@ -158,7 +158,7 @@ public abstract class WebSocketServerHandshaker { logger.debug(String.format("%s WS Version %s server handshake", channel, version())); } FullHttpResponse response = newHandshakeResponse(req, responseHeaders); - channel.write(response).addListener(new ChannelFutureListener() { + channel.write(response).flush().addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { @@ -225,7 +225,7 @@ public abstract class WebSocketServerHandshaker { if (channel == null) { throw new NullPointerException("channel"); } - return channel.write(frame, promise).addListener(ChannelFutureListener.CLOSE); + return channel.write(frame).flush(promise).addListener(ChannelFutureListener.CLOSE); } /** diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java index 1dd0267dd8..b96f5d5f2e 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java @@ -177,7 +177,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { */ @Override public ChannelFuture close(Channel channel, CloseWebSocketFrame frame, ChannelPromise promise) { - return channel.write(frame, promise); + return channel.write(frame).flush(promise); } @Override diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.java index 017a796a79..3de70e8800 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.java @@ -15,21 +15,21 @@ */ package io.netty.handler.codec.http.websocketx; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelPipeline; -import io.netty.channel.MessageList; -import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.util.AttributeKey; -import static io.netty.handler.codec.http.HttpVersion.*; +import java.util.List; /** * This handler does all the heavy lifting for you to run a websocket server. @@ -91,7 +91,7 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler { } @Override - protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, MessageList out) throws Exception { + protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, List out) throws Exception { if (frame instanceof CloseWebSocketFrame) { WebSocketServerHandshaker handshaker = getHandshaker(ctx); frame.retain(); @@ -106,7 +106,7 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler { if (cause instanceof WebSocketHandshakeException) { FullHttpResponse response = new DefaultFullHttpResponse( HTTP_1_1, HttpResponseStatus.BAD_REQUEST, Unpooled.wrappedBuffer(cause.getMessage().getBytes())); - ctx.channel().write(response).addListener(ChannelFutureListener.CLOSE); + ctx.channel().write(response).flush().addListener(ChannelFutureListener.CLOSE); } else { ctx.close(); } @@ -121,12 +121,16 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler { } static ChannelHandler forbiddenHttpRequestResponder() { - return new SimpleChannelInboundHandler() { + return new ChannelInboundHandlerAdapter() { @Override - protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception { - FullHttpResponse response = - new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN); - ctx.channel().write(response); + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + if (msg instanceof FullHttpRequest) { + FullHttpResponse response = + new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN); + ctx.channel().write(response).flush(); + } else { + ctx.fireMessageReceived(msg); + } } }; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java index 31e4ada4ee..9ca7a81054 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java @@ -20,7 +20,6 @@ import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelPipeline; -import io.netty.channel.MessageList; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpHeaders; @@ -51,42 +50,39 @@ class WebSocketServerProtocolHandshakeHandler } @Override - public void messageReceived(final ChannelHandlerContext ctx, MessageList msgs) throws Exception { - MessageList requests = msgs.cast(); - for (int i = 0; i < requests.size(); i++) { - FullHttpRequest req = requests.get(i); - if (req.getMethod() != GET) { - sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN)); - return; - } + public void messageReceived(final ChannelHandlerContext ctx, Object msg) throws Exception { + FullHttpRequest req = (FullHttpRequest) msg; + if (req.getMethod() != GET) { + sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN)); + return; + } - final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( - getWebSocketLocation(ctx.pipeline(), req, websocketPath), subprotocols, allowExtensions); - final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req); - if (handshaker == null) { - WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel()); - } else { - final ChannelFuture handshakeFuture = handshaker.handshake(ctx.channel(), req); - handshakeFuture.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (!future.isSuccess()) { - ctx.fireExceptionCaught(future.cause()); - } else { - ctx.fireUserEventTriggered( - WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE); - } + final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( + getWebSocketLocation(ctx.pipeline(), req, websocketPath), subprotocols, allowExtensions); + final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req); + if (handshaker == null) { + WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel()); + } else { + final ChannelFuture handshakeFuture = handshaker.handshake(ctx.channel(), req); + handshakeFuture.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if (!future.isSuccess()) { + ctx.fireExceptionCaught(future.cause()); + } else { + ctx.fireUserEventTriggered( + WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE); } - }); - WebSocketServerProtocolHandler.setHandshaker(ctx, handshaker); - ctx.pipeline().replace(this, "WS403Responder", - WebSocketServerProtocolHandler.forbiddenHttpRequestResponder()); - } + } + }); + WebSocketServerProtocolHandler.setHandshaker(ctx, handshaker); + ctx.pipeline().replace(this, "WS403Responder", + WebSocketServerProtocolHandler.forbiddenHttpRequestResponder()); } } private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) { - ChannelFuture f = ctx.channel().write(res); + ChannelFuture f = ctx.channel().write(res).flush(); if (!isKeepAlive(req) || res.getStatus().code() != 200) { f.addListener(ChannelFutureListener.CLOSE); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameDecoder.java index 26711bfb6d..6c4826ef4b 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameDecoder.java @@ -15,12 +15,34 @@ */ package io.netty.handler.codec.spdy; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_DATA_FLAG_FIN; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_DATA_FRAME; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_FLAG_FIN; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_FLAG_UNIDIRECTIONAL; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_GOAWAY_FRAME; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_HEADERS_FRAME; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_HEADER_FLAGS_OFFSET; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_HEADER_LENGTH_OFFSET; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_HEADER_SIZE; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_HEADER_TYPE_OFFSET; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_PING_FRAME; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_RST_STREAM_FRAME; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_SETTINGS_CLEAR; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_SETTINGS_FRAME; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_SETTINGS_PERSISTED; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_SETTINGS_PERSIST_VALUE; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_SYN_REPLY_FRAME; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_SYN_STREAM_FRAME; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_WINDOW_UPDATE_FRAME; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.getSignedInt; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.getUnsignedInt; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.getUnsignedMedium; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.getUnsignedShort; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.ByteToMessageDecoder; -import static io.netty.handler.codec.spdy.SpdyCodecUtil.*; +import java.util.List; /** * Decodes {@link ByteBuf}s into SPDY Frames. @@ -89,7 +111,7 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder { } @Override - public void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { + public void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { try { decode(ctx, in, out); } finally { @@ -98,7 +120,7 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder { } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, MessageList out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception { switch(state) { case READ_COMMON_HEADER: state = readCommonHeader(buffer); diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java index e44ef70eea..f9a991916e 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java @@ -17,7 +17,6 @@ package io.netty.handler.codec.spdy; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.http.DefaultFullHttpRequest; @@ -31,6 +30,7 @@ import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -91,7 +91,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { } @Override - protected void decode(ChannelHandlerContext ctx, SpdyFrame msg, MessageList out) + protected void decode(ChannelHandlerContext ctx, SpdyFrame msg, List out) throws Exception { if (msg instanceof SpdySynStreamFrame) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java index 8f44e91afd..3862e2aedb 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java @@ -16,7 +16,6 @@ package io.netty.handler.codec.spdy; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.handler.codec.UnsupportedMessageTypeException; import io.netty.handler.codec.http.FullHttpRequest; @@ -139,7 +138,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { } @Override - protected void encode(ChannelHandlerContext ctx, HttpObject msg, MessageList out) throws Exception { + protected void encode(ChannelHandlerContext ctx, HttpObject msg, List out) throws Exception { boolean valid = false; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java index 219ccfb3f0..51834a281c 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java @@ -16,12 +16,12 @@ package io.netty.handler.codec.spdy; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.MessageToMessageCodec; import io.netty.handler.codec.http.HttpMessage; import io.netty.util.ReferenceCountUtil; import java.util.LinkedList; +import java.util.List; import java.util.Queue; /** @@ -40,7 +40,7 @@ public class SpdyHttpResponseStreamIdHandler extends } @Override - protected void encode(ChannelHandlerContext ctx, HttpMessage msg, MessageList out) throws Exception { + protected void encode(ChannelHandlerContext ctx, HttpMessage msg, List out) throws Exception { Integer id = ids.poll(); if (id != null && id.intValue() != NO_ID && !msg.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) { SpdyHttpHeaders.setStreamId(msg, id); @@ -50,7 +50,7 @@ public class SpdyHttpResponseStreamIdHandler extends } @Override - protected void decode(ChannelHandlerContext ctx, Object msg, MessageList out) throws Exception { + protected void decode(ChannelHandlerContext ctx, Object msg, List out) throws Exception { if (msg instanceof HttpMessage) { boolean contains = ((HttpMessage) msg).headers().contains(SpdyHttpHeaders.Names.STREAM_ID); if (!contains) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyOrHttpChooser.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyOrHttpChooser.java index b14fec68ff..9cfc57a899 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyOrHttpChooser.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyOrHttpChooser.java @@ -20,7 +20,6 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandler; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelPipeline; -import io.netty.channel.MessageList; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpResponseEncoder; @@ -61,13 +60,13 @@ public abstract class SpdyOrHttpChooser extends ChannelInboundHandlerAdapter { protected abstract SelectedProtocol getProtocol(SSLEngine engine); @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList in) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { if (initPipeline(ctx)) { // When we reached here we can remove this handler as its now clear what protocol we want to use // from this point on. ctx.pipeline().remove(this); - ctx.fireMessageReceived(in); + ctx.fireMessageReceived(msg); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySessionHandler.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySessionHandler.java index 9add62a38a..f9cceeca90 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySessionHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySessionHandler.java @@ -20,9 +20,10 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; -import io.netty.channel.MessageList; import io.netty.util.internal.EmptyArrays; +import java.util.ArrayDeque; +import java.util.Queue; import java.util.concurrent.atomic.AtomicInteger; /** @@ -54,6 +55,8 @@ public class SpdySessionHandler private final AtomicInteger pings = new AtomicInteger(); + private final Queue outboundBuffer = new ArrayDeque(); + private boolean sentGoAwayFrame; private boolean receivedGoAwayFrame; @@ -81,34 +84,7 @@ public class SpdySessionHandler } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList in) throws Exception { - boolean handled = false; - MessageList out = MessageList.newInstance(); - for (int i = 0 ; i < in.size(); i++) { - Object msg = in.get(i); - if (msg == null) { - break; - } - - if (msg instanceof SpdySynStreamFrame) { - // Let the next handlers handle the buffered messages before SYN_STREAM message updates the - // lastGoodStreamId. - if (handled) { - ctx.fireMessageReceived(out); - out = MessageList.newInstance(); - } - } - - handleInboundMessage(ctx, msg, out); - handled = true; - } - - in.recycle(); - ctx.fireMessageReceived(out); - } - - private void handleInboundMessage(ChannelHandlerContext ctx, Object msg, MessageList out) throws Exception { - + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof SpdyDataFrame) { /* @@ -133,7 +109,6 @@ public class SpdySessionHandler * If an endpoint receives a data frame after the stream is closed, it must send * a RST_STREAM frame with the getStatus PROTOCOL_ERROR. */ - SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg; int streamId = spdyDataFrame.getStreamId(); @@ -141,9 +116,9 @@ public class SpdySessionHandler if (!spdySession.isActiveStream(streamId)) { if (streamId <= lastGoodStreamId) { - issueStreamError(ctx, streamId, SpdyStreamStatus.PROTOCOL_ERROR, out); + issueStreamError(ctx, streamId, SpdyStreamStatus.PROTOCOL_ERROR); } else if (!sentGoAwayFrame) { - issueStreamError(ctx, streamId, SpdyStreamStatus.INVALID_STREAM, out); + issueStreamError(ctx, streamId, SpdyStreamStatus.INVALID_STREAM); } return; } @@ -151,13 +126,13 @@ public class SpdySessionHandler // Check if we received a data frame for a stream which is half-closed if (spdySession.isRemoteSideClosed(streamId)) { - issueStreamError(ctx, streamId, SpdyStreamStatus.STREAM_ALREADY_CLOSED, out); + issueStreamError(ctx, streamId, SpdyStreamStatus.STREAM_ALREADY_CLOSED); return; } // Check if we received a data frame before receiving a SYN_REPLY if (!isRemoteInitiatedID(streamId) && !spdySession.hasReceivedReply(streamId)) { - issueStreamError(ctx, streamId, SpdyStreamStatus.PROTOCOL_ERROR, out); + issueStreamError(ctx, streamId, SpdyStreamStatus.PROTOCOL_ERROR); return; } @@ -178,7 +153,7 @@ public class SpdySessionHandler // This difference is stored for the session when writing the SETTINGS frame // and is cleared once we send a WINDOW_UPDATE frame. if (newWindowSize < spdySession.getReceiveWindowSizeLowerBound(streamId)) { - issueStreamError(ctx, streamId, SpdyStreamStatus.FLOW_CONTROL_ERROR, out); + issueStreamError(ctx, streamId, SpdyStreamStatus.FLOW_CONTROL_ERROR); return; } @@ -188,7 +163,7 @@ public class SpdySessionHandler while (spdyDataFrame.content().readableBytes() > initialReceiveWindowSize) { SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamId, spdyDataFrame.content().readSlice(initialReceiveWindowSize).retain()); - ctx.write(partialDataFrame); + ctx.write(partialDataFrame).flush(); } } @@ -198,7 +173,7 @@ public class SpdySessionHandler spdySession.updateReceiveWindowSize(streamId, deltaWindowSize); SpdyWindowUpdateFrame spdyWindowUpdateFrame = new DefaultSpdyWindowUpdateFrame(streamId, deltaWindowSize); - ctx.write(spdyWindowUpdateFrame); + ctx.write(spdyWindowUpdateFrame).flush(); } } @@ -230,7 +205,7 @@ public class SpdySessionHandler if (spdySynStreamFrame.isInvalid() || !isRemoteInitiatedID(streamId) || spdySession.isActiveStream(streamId)) { - issueStreamError(ctx, streamId, SpdyStreamStatus.PROTOCOL_ERROR, out); + issueStreamError(ctx, streamId, SpdyStreamStatus.PROTOCOL_ERROR); return; } @@ -245,7 +220,7 @@ public class SpdySessionHandler boolean remoteSideClosed = spdySynStreamFrame.isLast(); boolean localSideClosed = spdySynStreamFrame.isUnidirectional(); if (!acceptStream(streamId, priority, remoteSideClosed, localSideClosed)) { - issueStreamError(ctx, streamId, SpdyStreamStatus.REFUSED_STREAM, out); + issueStreamError(ctx, streamId, SpdyStreamStatus.REFUSED_STREAM); return; } @@ -265,13 +240,13 @@ public class SpdySessionHandler if (spdySynReplyFrame.isInvalid() || isRemoteInitiatedID(streamId) || spdySession.isRemoteSideClosed(streamId)) { - issueStreamError(ctx, streamId, SpdyStreamStatus.INVALID_STREAM, out); + issueStreamError(ctx, streamId, SpdyStreamStatus.INVALID_STREAM); return; } // Check if we have received multiple frames for the same Stream-ID if (spdySession.hasReceivedReply(streamId)) { - issueStreamError(ctx, streamId, SpdyStreamStatus.STREAM_IN_USE, out); + issueStreamError(ctx, streamId, SpdyStreamStatus.STREAM_IN_USE); return; } @@ -336,7 +311,7 @@ public class SpdySessionHandler SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg; if (isRemoteInitiatedID(spdyPingFrame.getId())) { - ctx.write(spdyPingFrame); + ctx.write(spdyPingFrame).flush(); return; } @@ -357,12 +332,12 @@ public class SpdySessionHandler // Check if we received a valid HEADERS frame if (spdyHeadersFrame.isInvalid()) { - issueStreamError(ctx, streamId, SpdyStreamStatus.PROTOCOL_ERROR, out); + issueStreamError(ctx, streamId, SpdyStreamStatus.PROTOCOL_ERROR); return; } if (spdySession.isRemoteSideClosed(streamId)) { - issueStreamError(ctx, streamId, SpdyStreamStatus.INVALID_STREAM, out); + issueStreamError(ctx, streamId, SpdyStreamStatus.INVALID_STREAM); return; } @@ -395,15 +370,15 @@ public class SpdySessionHandler // Check for numerical overflow if (spdySession.getSendWindowSize(streamId) > Integer.MAX_VALUE - deltaWindowSize) { - issueStreamError(ctx, streamId, SpdyStreamStatus.FLOW_CONTROL_ERROR, out); + issueStreamError(ctx, streamId, SpdyStreamStatus.FLOW_CONTROL_ERROR); return; } - updateSendWindowSize(streamId, deltaWindowSize, out); + updateSendWindowSize(ctx, streamId, deltaWindowSize); } } - out.add(msg); + ctx.fireMessageReceived(msg); } @Override @@ -421,43 +396,49 @@ public class SpdySessionHandler } @Override - public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { - MessageList out = MessageList.newInstance(); - for (int i = 0; i < msgs.size(); i++) { - Object msg = msgs.get(i); - if (msg == null) { - break; - } - if (msg instanceof SpdyDataFrame || - msg instanceof SpdySynStreamFrame || - msg instanceof SpdySynReplyFrame || - msg instanceof SpdyRstStreamFrame || - msg instanceof SpdySettingsFrame || - msg instanceof SpdyPingFrame || - msg instanceof SpdyGoAwayFrame || - msg instanceof SpdyHeadersFrame || - msg instanceof SpdyWindowUpdateFrame) { - try { - handleOutboundMessage(ctx, msg, out); - } catch (SpdyProtocolException e) { - if (e == PROTOCOL_EXCEPTION) { - // on the case of PROTOCOL_EXCEPTION, fail the promise directly - // See #1211 - promise.setFailure(PROTOCOL_EXCEPTION); - return; - } - } - } else { - out.add(msg); - } - } - - msgs.recycle(); - ctx.write(out, promise); + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { + outboundBuffer.add(msg); } - private void handleOutboundMessage(ChannelHandlerContext ctx, Object msg, MessageList out) - throws Exception { + @Override + public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + try { + for (;;) { + Object msg = outboundBuffer.poll(); + if (msg == null) { + break; + } + + if (msg instanceof SpdyDataFrame || + msg instanceof SpdySynStreamFrame || + msg instanceof SpdySynReplyFrame || + msg instanceof SpdyRstStreamFrame || + msg instanceof SpdySettingsFrame || + msg instanceof SpdyPingFrame || + msg instanceof SpdyGoAwayFrame || + msg instanceof SpdyHeadersFrame || + msg instanceof SpdyWindowUpdateFrame) { + try { + handleOutboundMessage(ctx, msg); + } catch (SpdyProtocolException e) { + if (e == PROTOCOL_EXCEPTION) { + // On the case of PROTOCOL_EXCEPTION, fail the promise directly + // See #1211 + promise.setFailure(PROTOCOL_EXCEPTION); + return; + } + } + } else { + ctx.write(msg); + } + } + ctx.flush(promise); + } finally { + outboundBuffer.clear(); + } + } + + private void handleOutboundMessage(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof SpdyDataFrame) { SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg; @@ -647,7 +628,7 @@ public class SpdySessionHandler throw PROTOCOL_EXCEPTION; } - out.add(msg); + ctx.write(msg); } /* @@ -676,17 +657,14 @@ public class SpdySessionHandler * * Note: this is only called by the worker thread */ - private void issueStreamError( - ChannelHandlerContext ctx, int streamId, SpdyStreamStatus status, MessageList in) { + private void issueStreamError(ChannelHandlerContext ctx, int streamId, SpdyStreamStatus status) { boolean fireMessageReceived = !spdySession.isRemoteSideClosed(streamId); removeStream(ctx, streamId); SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamId, status); - ctx.write(spdyRstStreamFrame); + ctx.write(spdyRstStreamFrame).flush(); if (fireMessageReceived) { - in.add(spdyRstStreamFrame); - ctx.fireMessageReceived(in.copy()); - in.clear(); + ctx.fireMessageReceived(spdyRstStreamFrame); } } @@ -783,7 +761,7 @@ public class SpdySessionHandler } } - private void updateSendWindowSize(final int streamId, int deltaWindowSize, MessageList out) { + private void updateSendWindowSize(ChannelHandlerContext ctx, final int streamId, int deltaWindowSize) { synchronized (flowControlLock) { int newWindowSize = spdySession.updateSendWindowSize(streamId, deltaWindowSize); @@ -822,7 +800,7 @@ public class SpdySessionHandler halfCloseStream(streamId, false); } - out.add(spdyDataFrame); + ctx.fireMessageReceived(spdyDataFrame); } else { // We can send a partial frame spdySession.updateSendWindowSize(streamId, -1 * newWindowSize); @@ -848,7 +826,7 @@ public class SpdySessionHandler // } //}); - out.add(partialDataFrame); + ctx.fireMessageReceived(partialDataFrame); newWindowSize = 0; } @@ -878,7 +856,7 @@ public class SpdySessionHandler if (!sentGoAwayFrame) { sentGoAwayFrame = true; SpdyGoAwayFrame spdyGoAwayFrame = new DefaultSpdyGoAwayFrame(lastGoodStreamId, status); - return ctx.write(spdyGoAwayFrame); + return ctx.write(spdyGoAwayFrame).flush(); } else { return ctx.newSucceededFuture(); } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandlerTest.java index ba081e4bca..11bd2ff56a 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandlerTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandlerTest.java @@ -17,10 +17,9 @@ package io.netty.handler.codec.http.websocketx; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; -import io.netty.channel.MessageList; -import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; @@ -147,22 +146,23 @@ public class WebSocketServerProtocolHandlerTest { private class MockOutboundHandler extends ChannelOutboundHandlerAdapter { @Override - public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) - throws Exception { - for (int i = 0; i < msgs.size(); i++) { - responses.add((FullHttpResponse) msgs.get(i)); - } + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { + responses.add((FullHttpResponse) msg); + } + + @Override + public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { promise.setSuccess(); } } - private static class CustomTextFrameHandler extends SimpleChannelInboundHandler { + private static class CustomTextFrameHandler extends ChannelInboundHandlerAdapter { private String content; @Override - public void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { assertNull(content); - content = "processed: " + msg.text(); + content = "processed: " + ((TextWebSocketFrame) msg).text(); } String getContent() { diff --git a/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdyFrameDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdyFrameDecoderTest.java index b51f70b1e0..3d16c57217 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdyFrameDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdyFrameDecoderTest.java @@ -19,9 +19,9 @@ import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; @@ -103,7 +103,7 @@ public class SpdyFrameDecoderTest { } private static void sendAndWaitForFrame(Channel cc, SpdyFrame frame, CaptureHandler handler) { - cc.write(frame); + cc.write(frame).flush(); long theFuture = System.currentTimeMillis() + 3000; while (handler.message == null && System.currentTimeMillis() < theFuture) { try { @@ -127,7 +127,7 @@ public class SpdyFrameDecoderTest { frame.headers().add(headerName.toString(), headerValue.toString()); } - private static class CaptureHandler extends SimpleChannelInboundHandler { + private static class CaptureHandler extends ChannelInboundHandlerAdapter { public volatile Object message; @Override diff --git a/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdySessionHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdySessionHandlerTest.java index 701d62eb53..0b9bc82297 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdySessionHandlerTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdySessionHandlerTest.java @@ -17,8 +17,6 @@ package io.netty.handler.codec.spdy; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.MessageList; -import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -274,7 +272,7 @@ public class SpdySessionHandlerTest { // Echo Handler opens 4 half-closed streams on session connection // and then sets the number of concurrent streams to 3 - private static class EchoHandler extends SimpleChannelInboundHandler { + private static class EchoHandler extends ChannelInboundHandlerAdapter { private final int closeSignal; private final boolean server; @@ -290,18 +288,18 @@ public class SpdySessionHandlerTest { SpdySynStreamFrame spdySynStreamFrame = new DefaultSpdySynStreamFrame(streamId, 0, (byte) 0); spdySynStreamFrame.setLast(true); - ctx.write(spdySynStreamFrame); + ctx.write(spdySynStreamFrame).flush(); spdySynStreamFrame.setStreamId(spdySynStreamFrame.getStreamId() + 2); - ctx.write(spdySynStreamFrame); + ctx.write(spdySynStreamFrame).flush(); spdySynStreamFrame.setStreamId(spdySynStreamFrame.getStreamId() + 2); - ctx.write(spdySynStreamFrame); + ctx.write(spdySynStreamFrame).flush(); spdySynStreamFrame.setStreamId(spdySynStreamFrame.getStreamId() + 2); - ctx.write(spdySynStreamFrame); + ctx.write(spdySynStreamFrame).flush(); // Limit the number of concurrent streams to 3 SpdySettingsFrame spdySettingsFrame = new DefaultSpdySettingsFrame(); spdySettingsFrame.setValue(SpdySettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS, 3); - ctx.write(spdySettingsFrame); + ctx.write(spdySettingsFrame).flush(); } @Override @@ -317,7 +315,7 @@ public class SpdySessionHandlerTest { spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue()); } - ctx.write(spdySynReplyFrame); + ctx.write(spdySynReplyFrame).flush(); } return; } @@ -330,7 +328,7 @@ public class SpdySessionHandlerTest { msg instanceof SpdyPingFrame || msg instanceof SpdyHeadersFrame) { - ctx.write(msg); + ctx.write(msg).flush(); return; } diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequestDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequestDecoder.java index d298fd4917..238efe1cc3 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequestDecoder.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequestDecoder.java @@ -17,10 +17,11 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; import io.netty.util.CharsetUtil; +import java.util.List; + /** * Decodes {@link ByteBuf}s into {@link SocksAuthRequest}. * Before returning SocksRequest decoder removes itself from pipeline. @@ -43,7 +44,7 @@ public class SocksAuthRequestDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List out) throws Exception { switch (state()) { case CHECK_PROTOCOL_VERSION: { version = SocksSubnegotiationVersion.fromByte(byteBuf.readByte()); diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponseDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponseDecoder.java index 01ed2c2979..6695484bd6 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponseDecoder.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponseDecoder.java @@ -17,9 +17,10 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; +import java.util.List; + /** * Decodes {@link ByteBuf}s into {@link SocksAuthResponse}. * Before returning SocksResponse decoder removes itself from pipeline. @@ -40,7 +41,7 @@ public class SocksAuthResponseDecoder extends ReplayingDecoder out) + protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List out) throws Exception { switch (state()) { case CHECK_PROTOCOL_VERSION: { diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequestDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequestDecoder.java index b85df155e9..618f776fd0 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequestDecoder.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequestDecoder.java @@ -17,10 +17,11 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; import io.netty.util.CharsetUtil; +import java.util.List; + /** * Decodes {@link ByteBuf}s into {@link SocksCmdRequest}. * Before returning SocksRequest decoder removes itself from pipeline. @@ -46,7 +47,7 @@ public class SocksCmdRequestDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List out) throws Exception { switch (state()) { case CHECK_PROTOCOL_VERSION: { version = SocksProtocolVersion.fromByte(byteBuf.readByte()); diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponseDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponseDecoder.java index 203005dd2d..e313128be4 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponseDecoder.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponseDecoder.java @@ -17,10 +17,11 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; import io.netty.util.CharsetUtil; +import java.util.List; + /** * Decodes {@link ByteBuf}s into {@link SocksCmdResponse}. * Before returning SocksResponse decoder removes itself from pipeline. @@ -46,7 +47,7 @@ public class SocksCmdResponseDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List out) throws Exception { switch (state()) { case CHECK_PROTOCOL_VERSION: { version = SocksProtocolVersion.fromByte(byteBuf.readByte()); diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequestDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequestDecoder.java index 16c0b7221d..428e6d9b8a 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequestDecoder.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequestDecoder.java @@ -17,7 +17,6 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; import java.util.ArrayList; @@ -44,7 +43,7 @@ public class SocksInitRequestDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List out) throws Exception { switch (state()) { case CHECK_PROTOCOL_VERSION: { version = SocksProtocolVersion.fromByte(byteBuf.readByte()); diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponseDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponseDecoder.java index e5c07a2a1c..3ea3aaa381 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponseDecoder.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponseDecoder.java @@ -17,9 +17,10 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; +import java.util.List; + /** * Decodes {@link ByteBuf}s into {@link SocksInitResponse}. * Before returning SocksResponse decoder removes itself from pipeline. @@ -41,7 +42,7 @@ public class SocksInitResponseDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List out) throws Exception { switch (state()) { case CHECK_PROTOCOL_VERSION: { version = SocksProtocolVersion.fromByte(byteBuf.readByte()); diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToMessageCodec.java b/codec/src/main/java/io/netty/handler/codec/ByteToMessageCodec.java index f2168419d3..9d066118a7 100644 --- a/codec/src/main/java/io/netty/handler/codec/ByteToMessageCodec.java +++ b/codec/src/main/java/io/netty/handler/codec/ByteToMessageCodec.java @@ -18,10 +18,10 @@ package io.netty.handler.codec; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.channel.MessageList; import io.netty.util.internal.TypeParameterMatcher; +import java.util.List; + /** * A Codec for on-the-fly encoding/decoding of bytes to messages and vise-versa. * @@ -47,12 +47,12 @@ public abstract class ByteToMessageCodec extends ChannelDuplexHandler { private final ByteToMessageDecoder decoder = new ByteToMessageDecoder() { @Override - public void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { + public void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { ByteToMessageCodec.this.decode(ctx, in, out); } @Override - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { + protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { ByteToMessageCodec.this.decodeLast(ctx, in, out); } }; @@ -78,13 +78,13 @@ public abstract class ByteToMessageCodec extends ChannelDuplexHandler { } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - decoder.messageReceived(ctx, msgs); + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + decoder.messageReceived(ctx, msg); } @Override - public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { - encoder.write(ctx, msgs, promise); + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { + encoder.write(ctx, msg); } /** @@ -93,14 +93,14 @@ public abstract class ByteToMessageCodec extends ChannelDuplexHandler { protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception; /** - * @see ByteToMessageDecoder#decode(ChannelHandlerContext, ByteBuf, MessageList) + * @see ByteToMessageDecoder#decode(ChannelHandlerContext, ByteBuf, List) */ - protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception; + protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception; /** - * @see ByteToMessageDecoder#decodeLast(ChannelHandlerContext, ByteBuf, MessageList) + * @see ByteToMessageDecoder#decodeLast(ChannelHandlerContext, ByteBuf, List) */ - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { + protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { decode(ctx, in, out); } } diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java index 7f89c14a0b..eaca15e068 100644 --- a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java @@ -19,9 +19,10 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.MessageList; import io.netty.util.internal.StringUtil; +import java.util.List; + /** * {@link ChannelInboundHandlerAdapter} which decodes bytes in a stream-like fashion from one {@link ByteBuf} to an * other Message type. @@ -32,7 +33,7 @@ import io.netty.util.internal.StringUtil; *
  *     public class SquareDecoder extends {@link ByteToMessageDecoder} {
  *         {@code @Override}
- *         public void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} in, {@link MessageList} out)
+ *         public void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} in, List<Object> out)
  *                 throws {@link Exception} {
  *             out.add(in.readBytes(in.readableBytes()));
  *         }
@@ -47,7 +48,6 @@ public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter
     protected ByteBuf cumulation;
     private boolean singleDecode;
     private boolean decodeWasNull;
-    private MessageList out;
 
     protected ByteToMessageDecoder() {
         if (getClass().isAnnotationPresent(Sharable.class)) {
@@ -56,7 +56,7 @@ public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter
     }
 
     /**
-     * If set then only one message is decoded on each {@link #messageReceived(ChannelHandlerContext, MessageList)}
+     * If set then only one message is decoded on each {@link #messageReceived(ChannelHandlerContext, Object)}
      * call. This may be useful if you need to do some protocol upgrade and want to make sure nothing is mixed up.
      *
      * Default is {@code false} as this has performance impacts.
@@ -67,7 +67,7 @@ public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter
 
     /**
      * If {@code true} then only one message is decoded on each
-     * {@link #messageReceived(ChannelHandlerContext, MessageList)} call.
+     * {@link #messageReceived(ChannelHandlerContext, Object)} call.
      *
      * Default is {@code false} as this has performance impacts.
      */
@@ -102,13 +102,9 @@ public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter
     public final void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
         ByteBuf buf = internalBuffer();
         if (buf.isReadable()) {
-            if (out == null) {
-                ctx.fireMessageReceived(buf);
-            } else {
-                out.add(buf.copy());
-            }
-            buf.clear();
+            ctx.fireMessageReceived(buf);
         }
+        ctx.fireMessageReceivedLast();
         handlerRemoved0(ctx);
     }
 
@@ -119,72 +115,58 @@ public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter
     protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        out = MessageList.newInstance();
+    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
+        CodecOutput out = CodecOutput.newInstance();
         try {
-            int size = msgs.size();
-            for (int i = 0; i < size; i ++) {
-                Object m = msgs.get(i);
-                // handler was removed in the loop so now copy over all remaining messages
-                if (ctx.isRemoved()) {
-                    out.add(msgs, i, size - i);
-                    return;
-                }
-                if (m instanceof ByteBuf) {
-                    ByteBuf data = (ByteBuf) m;
-                    if (cumulation == null) {
-                        cumulation = data;
-                        try {
-                            callDecode(ctx, data, out);
-                        } finally {
-                            if (!data.isReadable()) {
-                                cumulation = null;
-                                data.release();
-                            }
-                        }
-                    } else {
-                        try {
-                            if (cumulation.writerIndex() > cumulation.maxCapacity() - data.readableBytes()) {
-                                ByteBuf oldCumulation = cumulation;
-                                cumulation = ctx.alloc().buffer(oldCumulation.readableBytes() + data.readableBytes());
-                                cumulation.writeBytes(oldCumulation);
-                                oldCumulation.release();
-                            }
-                            cumulation.writeBytes(data);
-                            callDecode(ctx, cumulation, out);
-                        } finally {
-                            if (!cumulation.isReadable()) {
-                                cumulation.release();
-                                cumulation = null;
-                            } else {
-                                cumulation.discardSomeReadBytes();
-                            }
+            if (msg instanceof ByteBuf) {
+                ByteBuf data = (ByteBuf) msg;
+                if (cumulation == null) {
+                    cumulation = data;
+                    try {
+                        callDecode(ctx, data, out);
+                    } finally {
+                        if (!data.isReadable()) {
+                            cumulation = null;
                             data.release();
                         }
                     }
                 } else {
-                    out.add(m);
+                    try {
+                        if (cumulation.writerIndex() > cumulation.maxCapacity() - data.readableBytes()) {
+                            ByteBuf oldCumulation = cumulation;
+                            cumulation = ctx.alloc().buffer(oldCumulation.readableBytes() + data.readableBytes());
+                            cumulation.writeBytes(oldCumulation);
+                            oldCumulation.release();
+                        }
+                        cumulation.writeBytes(data);
+                        callDecode(ctx, cumulation, out);
+                    } finally {
+                        if (!cumulation.isReadable()) {
+                            cumulation.release();
+                            cumulation = null;
+                        } else {
+                            cumulation.discardSomeReadBytes();
+                        }
+                        data.release();
+                    }
                 }
+            } else {
+                out.add(msg);
             }
         } catch (DecoderException e) {
             throw e;
         } catch (Throwable t) {
             throw new DecoderException(t);
         } finally {
-            // release the cumulation if the handler was removed while messages are processed
-            if (ctx.isRemoved()) {
-                if (cumulation != null) {
-                    cumulation.release();
-                    cumulation = null;
-                }
-            }
-            MessageList out = this.out;
-            this.out = null;
             if (out.isEmpty()) {
                 decodeWasNull = true;
             }
-            msgs.recycle();
-            ctx.fireMessageReceived(out);
+
+            for (int i = 0; i < out.size(); i ++) {
+                ctx.fireMessageReceived(out.get(i));
+            }
+
+            out.recycle();
         }
     }
 
@@ -201,7 +183,7 @@ public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter
 
     @Override
     public void channelInactive(ChannelHandlerContext ctx) throws Exception {
-        MessageList out = MessageList.newInstance();
+        CodecOutput out = CodecOutput.newInstance();
         try {
             if (cumulation != null) {
                 callDecode(ctx, cumulation, out);
@@ -219,12 +201,14 @@ public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter
                 cumulation = null;
             }
 
-            ctx.fireMessageReceived(out);
+            for (int i = 0; i < out.size(); i ++) {
+                ctx.fireMessageReceived(out.get(i));
+            }
             ctx.fireChannelInactive();
         }
     }
 
-    protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) {
+    protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, CodecOutput out) {
         try {
             while (in.isReadable()) {
                 int outSize = out.size();
@@ -262,20 +246,20 @@ public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter
      *
      * @param ctx           the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to
      * @param in            the {@link ByteBuf} from which to read data
-     * @param out           the {@link MessageList} to which decoded messages should be added
+     * @param out           the {@link CodecOutput} to which decoded messages should be added
 
      * @throws Exception    is thrown if an error accour
      */
-    protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception;
+    protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception;
 
     /**
      * Is called one last time when the {@link ChannelHandlerContext} goes in-active. Which means the
      * {@link #channelInactive(ChannelHandlerContext)} was triggered.
      *
-     * By default this will just call {@link #decode(ChannelHandlerContext, ByteBuf, MessageList)} but sub-classes may
+     * By default this will just call {@link #decode(ChannelHandlerContext, ByteBuf, CodecOutput)} but sub-classes may
      * override this for some special cleanup operation.
      */
-    protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception {
+    protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
         decode(ctx, in, out);
     }
 }
diff --git a/codec/src/main/java/io/netty/handler/codec/CodecOutput.java b/codec/src/main/java/io/netty/handler/codec/CodecOutput.java
new file mode 100644
index 0000000000..7f41ce393a
--- /dev/null
+++ b/codec/src/main/java/io/netty/handler/codec/CodecOutput.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 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;
+
+import io.netty.util.Recycler;
+import io.netty.util.Recycler.Handle;
+
+import java.util.ArrayList;
+
+/**
+ * A simple list that holds the output of a codec.
+ */
+final class CodecOutput extends ArrayList {
+
+    private static final long serialVersionUID = -8605125654176467947L;
+
+    private static final int DEFAULT_INITIAL_CAPACITY = 8;
+
+    private static final Recycler RECYCLER = new Recycler() {
+        @Override
+        protected CodecOutput newObject(Handle handle) {
+            return new CodecOutput(handle);
+        }
+    };
+
+    /**
+     * Create a new empty {@link CodecOutput} instance
+     */
+    public static CodecOutput newInstance() {
+        return newInstance(DEFAULT_INITIAL_CAPACITY);
+    }
+
+    /**
+     * Create a new empty {@link CodecOutput} instance with the given capacity.
+     */
+    public static CodecOutput newInstance(int minCapacity) {
+        CodecOutput ret = (CodecOutput) RECYCLER.get();
+        ret.ensureCapacity(minCapacity);
+        return ret;
+    }
+
+    private final Handle handle;
+
+    CodecOutput(Handle handle) {
+        this(handle, DEFAULT_INITIAL_CAPACITY);
+    }
+
+    CodecOutput(Handle handle, int initialCapacity) {
+        super(initialCapacity);
+        this.handle = handle;
+    }
+
+    /**
+     * Clear and recycle this instance.
+     */
+    boolean recycle() {
+        clear();
+        return RECYCLER.recycle(this, handle);
+    }
+}
diff --git a/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java
index 1a0a02d2fa..3b0273ab27 100644
--- a/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java
@@ -17,7 +17,8 @@ package io.netty.handler.codec;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.MessageList;
+
+import java.util.List;
 
 /**
  * A decoder that splits the received {@link ByteBuf}s by one or more
@@ -211,7 +212,7 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
     }
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
         Object decoded = decode(ctx, in);
         if (decoded != null) {
             out.add(decoded);
diff --git a/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java
index 3cc025e711..adabf58982 100644
--- a/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java
@@ -17,7 +17,8 @@ package io.netty.handler.codec;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.MessageList;
+
+import java.util.List;
 
 /**
  * A decoder that splits the received {@link ByteBuf}s by the fixed number
@@ -53,7 +54,7 @@ public class FixedLengthFrameDecoder extends ByteToMessageDecoder {
     }
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
         Object decoded = decode(ctx, in);
         if (decoded != null) {
             out.add(decoded);
diff --git a/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java
index 808e5e673c..5778aa5f3a 100644
--- a/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java
@@ -18,10 +18,10 @@ package io.netty.handler.codec;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.MessageList;
 import io.netty.handler.codec.serialization.ObjectDecoder;
 
 import java.nio.ByteOrder;
+import java.util.List;
 
 /**
  * A decoder that splits the received {@link ByteBuf}s dynamically by the
@@ -348,7 +348,7 @@ public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder {
     }
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
         Object decoded = decode(ctx, in);
         if (decoded != null) {
             out.add(decoded);
diff --git a/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java
index 414ebb38c0..24b6faedf0 100644
--- a/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java
@@ -17,7 +17,8 @@ package io.netty.handler.codec;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.MessageList;
+
+import java.util.List;
 
 /**
  * A decoder that splits the received {@link ByteBuf}s on line endings.
@@ -69,7 +70,7 @@ public class LineBasedFrameDecoder extends ByteToMessageDecoder {
     }
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
         Object decoded = decode(ctx, in);
         if (decoded != null) {
             out.add(decoded);
diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToByteEncoder.java b/codec/src/main/java/io/netty/handler/codec/MessageToByteEncoder.java
index 3f49d78e63..be3522d906 100644
--- a/codec/src/main/java/io/netty/handler/codec/MessageToByteEncoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/MessageToByteEncoder.java
@@ -18,8 +18,6 @@ package io.netty.handler.codec;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelOutboundHandlerAdapter;
-import io.netty.channel.ChannelPromise;
-import io.netty.channel.MessageList;
 import io.netty.util.ReferenceCountUtil;
 import io.netty.util.internal.TypeParameterMatcher;
 
@@ -69,73 +67,45 @@ public abstract class MessageToByteEncoder extends ChannelOutboundHandlerAdap
     }
 
     @Override
-    public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception {
-        MessageList out = MessageList.newInstance();
-        boolean success = false;
+    public void write(ChannelHandlerContext ctx, Object msg) throws Exception {
         ByteBuf buf = null;
         try {
-            int size = msgs.size();
-            for (int i = 0; i < size; i ++) {
-                // handler was removed in the loop so now copy over all remaining messages
-                if (ctx.isRemoved()) {
-                    if (buf != null && buf.isReadable())  {
-                        out.add(buf);
-                        buf = null;
+            if (acceptOutboundMessage(msg)) {
+                @SuppressWarnings("unchecked")
+                I cast = (I) msg;
+                if (buf == null) {
+                    if (preferDirect) {
+                        buf = ctx.alloc().ioBuffer();
+                    } else {
+                        buf = ctx.alloc().heapBuffer();
                     }
-                    out.add(msgs, i, size - i);
-                    break;
                 }
-                Object m = msgs.get(i);
-                if (acceptOutboundMessage(m)) {
-                    @SuppressWarnings("unchecked")
-                    I cast = (I) m;
-                    if (buf == null) {
-                        if (preferDirect) {
-                            buf = ctx.alloc().ioBuffer();
-                        } else {
-                            buf = ctx.alloc().heapBuffer();
-                        }
-                    }
-                    try {
-                        encode(ctx, cast, buf);
-                    } finally {
-                        ReferenceCountUtil.release(cast);
-                    }
-                } else {
-                    if (buf != null && buf.isReadable()) {
-                        out.add(buf);
-                        buf = null;
-                    }
-
-                    out.add(m);
+                try {
+                    encode(ctx, cast, buf);
+                } finally {
+                    ReferenceCountUtil.release(cast);
                 }
+            } else {
+                ctx.write(msg);
             }
 
             if (buf != null && buf.isReadable()) {
-                out.add(buf);
+                ctx.write(buf);
                 buf = null;
             }
-
-            success = true;
         } catch (EncoderException e) {
             throw e;
         } catch (Throwable e) {
             throw new EncoderException(e);
         } finally {
-            msgs.recycle();
             if (buf != null) {
                 buf.release();
             }
-            if (success) {
-                ctx.write(out, promise);
-            } else {
-                out.releaseAllAndRecycle();
-            }
         }
     }
 
     /**
-     * Encode a message into a {@link ByteBuf}. This method will be called till the {@link MessageList} has
+     * Encode a message into a {@link ByteBuf}. This method will be called till the {@link CodecOutput} has
      * nothing left.
      *
      * @param ctx           the {@link ChannelHandlerContext} which this {@link MessageToByteEncoder} belongs to
diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToMessageCodec.java b/codec/src/main/java/io/netty/handler/codec/MessageToMessageCodec.java
index c7389ef5c7..36e3518b5d 100644
--- a/codec/src/main/java/io/netty/handler/codec/MessageToMessageCodec.java
+++ b/codec/src/main/java/io/netty/handler/codec/MessageToMessageCodec.java
@@ -17,11 +17,11 @@ package io.netty.handler.codec;
 
 import io.netty.channel.ChannelDuplexHandler;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPromise;
-import io.netty.channel.MessageList;
 import io.netty.util.ReferenceCounted;
 import io.netty.util.internal.TypeParameterMatcher;
 
+import java.util.List;
+
 /**
  * A Codec for on-the-fly encoding/decoding of message.
  *
@@ -62,7 +62,7 @@ public abstract class MessageToMessageCodec extends Cha
 
         @Override
         @SuppressWarnings("unchecked")
-        protected void encode(ChannelHandlerContext ctx, Object msg, MessageList out) throws Exception {
+        protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
             MessageToMessageCodec.this.encode(ctx, (OUTBOUND_IN) msg, out);
         }
     };
@@ -76,7 +76,7 @@ public abstract class MessageToMessageCodec extends Cha
 
         @Override
         @SuppressWarnings("unchecked")
-        protected void decode(ChannelHandlerContext ctx, Object msg, MessageList out) throws Exception {
+        protected void decode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
             MessageToMessageCodec.this.decode(ctx, (INBOUND_IN) msg, out);
         }
     };
@@ -96,13 +96,13 @@ public abstract class MessageToMessageCodec extends Cha
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        decoder.messageReceived(ctx, msgs);
+    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
+        decoder.messageReceived(ctx, msg);
     }
 
     @Override
-    public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception {
-        encoder.write(ctx, msgs, promise);
+    public void write(ChannelHandlerContext ctx, Object msg) throws Exception {
+        encoder.write(ctx, msg);
     }
 
     /**
@@ -124,14 +124,14 @@ public abstract class MessageToMessageCodec extends Cha
     }
 
     /**
-     * @see MessageToMessageEncoder#encode(ChannelHandlerContext, Object, MessageList)
+     * @see MessageToMessageEncoder#encode(ChannelHandlerContext, Object, CodecOutput)
      */
-    protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, MessageList out)
+    protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, List out)
             throws Exception;
 
     /**
-     * @see MessageToMessageDecoder#decode(ChannelHandlerContext, Object, MessageList)
+     * @see MessageToMessageDecoder#decode(ChannelHandlerContext, Object, CodecOutput)
      */
-    protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, MessageList out)
+    protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, List out)
             throws Exception;
 }
diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java b/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java
index c6f7bef8fc..2a7af14a9e 100644
--- a/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java
@@ -17,11 +17,12 @@ package io.netty.handler.codec;
 
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
 import io.netty.util.ReferenceCountUtil;
 import io.netty.util.ReferenceCounted;
 import io.netty.util.internal.TypeParameterMatcher;
 
+import java.util.List;
+
 /**
  * {@link ChannelInboundHandlerAdapter} which decodes from one message to an other message.
  *
@@ -35,7 +36,7 @@ import io.netty.util.internal.TypeParameterMatcher;
  *
  *         {@code @Override}
  *         public void decode({@link ChannelHandlerContext} ctx, {@link String} message,
- *                 {@link MessageList} out) throws {@link Exception} {
+ *                            List<Object> out) throws {@link Exception} {
  *             out.add(message.length());
  *         }
  *     }
@@ -63,48 +64,40 @@ public abstract class MessageToMessageDecoder extends ChannelInboundHandlerAd
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        MessageList out = MessageList.newInstance();
+    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
+        CodecOutput out = CodecOutput.newInstance();
         try {
-            int size = msgs.size();
-            for (int i = 0; i < size; i ++) {
-                // handler was removed in the loop so now copy over all remaining messages
-                if (ctx.isRemoved()) {
-                    out.add(msgs, i, size - i);
-                    return;
-                }
-
-                Object m = msgs.get(i);
-                if (acceptInboundMessage(m)) {
-                    @SuppressWarnings("unchecked")
-                    I cast = (I) m;
-                    try {
-                        decode(ctx, cast, out);
-                    } finally {
-                        ReferenceCountUtil.release(cast);
-                    }
-                } else {
-                    out.add(m);
+            if (acceptInboundMessage(msg)) {
+                @SuppressWarnings("unchecked")
+                I cast = (I) msg;
+                try {
+                    decode(ctx, cast, out);
+                } finally {
+                    ReferenceCountUtil.release(cast);
                 }
+            } else {
+                out.add(msg);
             }
         } catch (DecoderException e) {
             throw e;
         } catch (Exception e) {
             throw new DecoderException(e);
         } finally {
-            msgs.recycle();
-            ctx.fireMessageReceived(out);
+            for (int i = 0; i < out.size(); i ++) {
+                ctx.fireMessageReceived(out.get(i));
+            }
+            out.recycle();
         }
     }
 
     /**
-     * Decode from one message to an other. This method will be called till either the {@link MessageList} has
+     * Decode from one message to an other. This method will be called till either the {@link CodecOutput} has
      * nothing left or till this method returns {@code null}.
      *
      * @param ctx           the {@link ChannelHandlerContext} which this {@link MessageToMessageDecoder} belongs to
      * @param msg           the message to decode to an other one
-     * @param out           the {@link MessageList} to which decoded messages should be added
+     * @param out           the {@link CodecOutput} to which decoded messages should be added
      * @throws Exception    is thrown if an error accour
      */
-    protected abstract void decode(ChannelHandlerContext ctx, I msg, MessageList out) throws Exception;
+    protected abstract void decode(ChannelHandlerContext ctx, I msg, List out) throws Exception;
 }
diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java b/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java
index 3d53d72ebf..806fe8844b 100644
--- a/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java
@@ -17,12 +17,12 @@ package io.netty.handler.codec;
 
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelOutboundHandlerAdapter;
-import io.netty.channel.ChannelPromise;
-import io.netty.channel.MessageList;
 import io.netty.util.ReferenceCountUtil;
 import io.netty.util.ReferenceCounted;
 import io.netty.util.internal.TypeParameterMatcher;
 
+import java.util.List;
+
 /**
  * {@link ChannelOutboundHandlerAdapter} which encodes from one message to an other message
  *
@@ -33,7 +33,7 @@ import io.netty.util.internal.TypeParameterMatcher;
  *             {@link MessageToMessageEncoder}<{@link Integer}> {
  *
  *         {@code @Override}
- *         public void encode({@link ChannelHandlerContext} ctx, {@link Integer} message, {@link MessageList} out)
+ *         public void encode({@link ChannelHandlerContext} ctx, {@link Integer} message, List<Object> out)
  *                 throws {@link Exception} {
  *             out.add(message.toString());
  *         }
@@ -61,54 +61,41 @@ public abstract class MessageToMessageEncoder extends ChannelOutboundHandlerA
     }
 
     @Override
-    public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception {
-        MessageList out = MessageList.newInstance();
-        boolean success = false;
+    public void write(ChannelHandlerContext ctx, Object msg) throws Exception {
+        CodecOutput out = CodecOutput.newInstance();
         try {
-            int size = msgs.size();
-            for (int i = 0; i < size; i ++) {
-                // handler was removed in the loop so now copy over all remaining messages
-                if (ctx.isRemoved()) {
-                    out.add(msgs, i, size - i);
-                    break;
-                }
-                Object m = msgs.get(i);
-                if (acceptOutboundMessage(m)) {
-                    @SuppressWarnings("unchecked")
-                    I cast = (I) m;
-                    try {
-                        encode(ctx, cast, out);
-                    } finally {
-                        ReferenceCountUtil.release(cast);
-                    }
-                } else {
-                    out.add(m);
+            if (acceptOutboundMessage(msg)) {
+                @SuppressWarnings("unchecked")
+                I cast = (I) msg;
+                try {
+                    encode(ctx, cast, out);
+                } finally {
+                    ReferenceCountUtil.release(cast);
                 }
+            } else {
+                out.add(msg);
             }
-            success = true;
-        } catch (CodecException e) {
+        } catch (EncoderException e) {
             throw e;
         } catch (Throwable t) {
             throw new EncoderException(t);
         } finally {
-            msgs.recycle();
-            if (success) {
-                ctx.write(out, promise);
-            } else {
-                out.releaseAllAndRecycle();
+            for (int i = 0; i < out.size(); i ++) {
+                ctx.write(out.get(i));
             }
+            out.recycle();
         }
     }
 
     /**
-     * Encode from one message to an other. This method will be called till either the {@link MessageList} has nothing
+     * Encode from one message to an other. This method will be called till either the {@link CodecOutput} has nothing
      * left or till this method returns {@code null}.
      *
      * @param ctx           the {@link ChannelHandlerContext} which this {@link MessageToMessageEncoder} belongs to
      * @param msg           the message to encode to an other one
-     * @param out           the {@link MessageList} into which the encoded msg should be added
+     * @param out           the {@link CodecOutput} into which the encoded msg should be added
      *                      needs to do some kind of aggragation
      * @throws Exception    is thrown if an error accour
      */
-    protected abstract void encode(ChannelHandlerContext ctx, I msg, MessageList out) throws Exception;
+    protected abstract void encode(ChannelHandlerContext ctx, I msg, List out) throws Exception;
 }
diff --git a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoder.java b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoder.java
index 67ad7311b7..fa68e9c3a2 100644
--- a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoder.java
@@ -19,7 +19,6 @@ import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPipeline;
-import io.netty.channel.MessageList;
 import io.netty.util.Signal;
 import io.netty.util.internal.StringUtil;
 
@@ -106,7 +105,7 @@ import io.netty.util.internal.StringUtil;
  *   private final Queue<Integer> values = new LinkedList<Integer>();
  *
  *   {@code @Override}
- *   public void decode(.., {@link ByteBuf} in, {@link MessageList} out) throws Exception {
+ *   public void decode(.., {@link ByteBuf} in, List<Object> out) throws Exception {
  *
  *     // A message contains 2 integers.
  *     values.offer(buffer.readInt());
@@ -126,7 +125,7 @@ import io.netty.util.internal.StringUtil;
  *   private final Queue<Integer> values = new LinkedList<Integer>();
  *
  *   {@code @Override}
- *   public void decode(.., {@link ByteBuf} buffer, {@link MessageList} out) throws Exception {
+ *   public void decode(.., {@link ByteBuf} buffer, List<Object> out) throws Exception {
  *
  *     // Revert the state of the variable that might have been changed
  *     // since the last partial decode.
@@ -179,7 +178,7 @@ import io.netty.util.internal.StringUtil;
  *
  *   {@code @Override}
  *   protected void decode({@link ChannelHandlerContext} ctx,
- *                           {@link ByteBuf} in, {@link MessageList} out) throws Exception {
+ *                           {@link ByteBuf} in, List<Object> out) throws Exception {
  *     switch (state()) {
  *     case READ_LENGTH:
  *       length = buf.readInt();
@@ -207,7 +206,7 @@ import io.netty.util.internal.StringUtil;
  *
  *   {@code @Override}
  *   protected void decode({@link ChannelHandlerContext} ctx,
- *                           {@link ByteBuf} in, {@link MessageList} out) throws Exception {
+ *                           {@link ByteBuf} in, List<Object> out) throws Exception {
  *     if (!readLength) {
  *       length = buf.readInt();
  *       readLength = true;
@@ -238,7 +237,7 @@ import io.netty.util.internal.StringUtil;
  *
  *     {@code @Override}
  *     protected Object decode({@link ChannelHandlerContext} ctx,
- *                             {@link ByteBuf} in, {@link MessageList} out) {
+ *                             {@link ByteBuf} in, List<Object> out) {
  *         ...
  *         // Decode the first message
  *         Object firstMessage = ...;
@@ -320,7 +319,7 @@ public abstract class ReplayingDecoder extends ByteToMessageDecoder {
 
     @Override
     public void channelInactive(ChannelHandlerContext ctx) throws Exception {
-        MessageList out = MessageList.newInstance();
+        CodecOutput out = CodecOutput.newInstance();
         try {
             replayable.terminate();
             callDecode(ctx, internalBuffer(), out);
@@ -338,13 +337,15 @@ public abstract class ReplayingDecoder extends ByteToMessageDecoder {
                 cumulation = null;
             }
 
-            ctx.fireMessageReceived(out);
+            for (int i = 0; i < out.size(); i ++) {
+                ctx.fireMessageReceived(out.get(i));
+            }
             ctx.fireChannelInactive();
         }
     }
 
     @Override
-    protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) {
+    protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, CodecOutput out) {
         replayable.setCumulation(in);
         try {
             while (in.isReadable()) {
diff --git a/codec/src/main/java/io/netty/handler/codec/base64/Base64Decoder.java b/codec/src/main/java/io/netty/handler/codec/base64/Base64Decoder.java
index f8b13edaa9..b8d7279062 100644
--- a/codec/src/main/java/io/netty/handler/codec/base64/Base64Decoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/base64/Base64Decoder.java
@@ -19,12 +19,13 @@ import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPipeline;
-import io.netty.channel.MessageList;
 import io.netty.handler.codec.ByteToMessageDecoder;
 import io.netty.handler.codec.DelimiterBasedFrameDecoder;
 import io.netty.handler.codec.Delimiters;
 import io.netty.handler.codec.MessageToMessageDecoder;
 
+import java.util.List;
+
 /**
  * Decodes a Base64-encoded {@link ByteBuf} or US-ASCII {@link String}
  * into a {@link ByteBuf}.  Please note that this decoder must be used
@@ -59,7 +60,7 @@ public class Base64Decoder extends MessageToMessageDecoder {
     }
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, MessageList out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception {
         out.add(Base64.decode(msg, msg.readerIndex(), msg.readableBytes(), dialect));
     }
 }
diff --git a/codec/src/main/java/io/netty/handler/codec/base64/Base64Encoder.java b/codec/src/main/java/io/netty/handler/codec/base64/Base64Encoder.java
index 5af5b71065..92c23a5bb7 100644
--- a/codec/src/main/java/io/netty/handler/codec/base64/Base64Encoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/base64/Base64Encoder.java
@@ -19,11 +19,12 @@ import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPipeline;
-import io.netty.channel.MessageList;
 import io.netty.handler.codec.DelimiterBasedFrameDecoder;
 import io.netty.handler.codec.Delimiters;
 import io.netty.handler.codec.MessageToMessageEncoder;
 
+import java.util.List;
+
 /**
  * Encodes a {@link ByteBuf} into a Base64-encoded {@link ByteBuf}.
  * A typical setup for TCP/IP would be:
@@ -62,7 +63,7 @@ public class Base64Encoder extends MessageToMessageEncoder {
     }
 
     @Override
-    protected void encode(ChannelHandlerContext ctx, ByteBuf msg, MessageList out) throws Exception {
+    protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception {
         out.add(Base64.encode(msg, msg.readerIndex(), msg.readableBytes(), breakLines, dialect));
     }
 }
diff --git a/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayDecoder.java b/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayDecoder.java
index a1d15b6d2f..4edc5889c9 100644
--- a/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayDecoder.java
@@ -18,11 +18,12 @@ package io.netty.handler.codec.bytes;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPipeline;
-import io.netty.channel.MessageList;
 import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
 import io.netty.handler.codec.LengthFieldPrepender;
 import io.netty.handler.codec.MessageToMessageDecoder;
 
+import java.util.List;
+
 /**
  * Decodes a received {@link ByteBuf} into an array of bytes.
  * A typical setup for TCP/IP would be:
@@ -49,7 +50,7 @@ import io.netty.handler.codec.MessageToMessageDecoder;
  */
 public class ByteArrayDecoder extends MessageToMessageDecoder {
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, MessageList out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception {
         byte[] array;
         if (msg.hasArray()) {
             if (msg.arrayOffset() == 0 && msg.readableBytes() == msg.capacity()) {
diff --git a/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayEncoder.java b/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayEncoder.java
index 1014084e63..04f12ac9d4 100644
--- a/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayEncoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayEncoder.java
@@ -20,11 +20,12 @@ import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPipeline;
-import io.netty.channel.MessageList;
 import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
 import io.netty.handler.codec.LengthFieldPrepender;
 import io.netty.handler.codec.MessageToMessageEncoder;
 
+import java.util.List;
+
 /**
  * Encodes the requested array of bytes into a {@link ByteBuf}.
  * A typical setup for TCP/IP would be:
@@ -52,7 +53,7 @@ import io.netty.handler.codec.MessageToMessageEncoder;
 @Sharable
 public class ByteArrayEncoder extends MessageToMessageEncoder {
     @Override
-    protected void encode(ChannelHandlerContext ctx, byte[] msg, MessageList out) throws Exception {
+    protected void encode(ChannelHandlerContext ctx, byte[] msg, List out) throws Exception {
         out.add(Unpooled.wrappedBuffer(msg));
     }
 }
diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JZlibDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JZlibDecoder.java
index ca9b39dd98..aaf679cc24 100644
--- a/codec/src/main/java/io/netty/handler/codec/compression/JZlibDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/compression/JZlibDecoder.java
@@ -15,11 +15,13 @@
  */
 package io.netty.handler.codec.compression;
 
-import com.jcraft.jzlib.Inflater;
-import com.jcraft.jzlib.JZlib;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.MessageList;
+
+import java.util.List;
+
+import com.jcraft.jzlib.Inflater;
+import com.jcraft.jzlib.JZlib;
 
 public class JZlibDecoder extends ZlibDecoder {
 
@@ -82,7 +84,7 @@ public class JZlibDecoder extends ZlibDecoder {
     }
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
 
         if (!in.isReadable()) {
             return;
diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java
index ddaee4177c..e75db5eeb0 100644
--- a/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java
@@ -343,10 +343,10 @@ public class JZlibEncoder extends ZlibEncoder {
         }
     }
 
-    private ChannelFuture finishEncode(ChannelHandlerContext ctx, ChannelPromise future) {
+    private ChannelFuture finishEncode(ChannelHandlerContext ctx, ChannelPromise promise) {
         if (!finished.compareAndSet(false, true)) {
-            future.setSuccess();
-            return future;
+            promise.setSuccess();
+            return promise;
         }
 
         ByteBuf footer;
@@ -366,8 +366,8 @@ public class JZlibEncoder extends ZlibEncoder {
                 // Write the ADLER32 checksum (stream footer).
                 int resultCode = z.deflate(JZlib.Z_FINISH);
                 if (resultCode != JZlib.Z_OK && resultCode != JZlib.Z_STREAM_END) {
-                    future.setFailure(ZlibUtil.deflaterException(z, "compression failure", resultCode));
-                    return future;
+                    promise.setFailure(ZlibUtil.deflaterException(z, "compression failure", resultCode));
+                    return promise;
                 } else if (z.next_out_index != 0) {
                     footer = Unpooled.wrappedBuffer(out, 0, z.next_out_index);
                 } else {
@@ -385,8 +385,7 @@ public class JZlibEncoder extends ZlibEncoder {
             }
         }
 
-        ctx.write(footer, future);
-        return future;
+        return ctx.write(footer).flush(promise);
     }
 
     @Override
diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java
index e225e731e3..d3957f4e65 100644
--- a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java
@@ -255,7 +255,7 @@ public class JdkZlibEncoder extends ZlibEncoder {
             deflater.end();
         }
 
-        return ctx.write(footer, promise);
+        return ctx.write(footer).flush(promise);
     }
 
     @Override
diff --git a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java
index c0fa5eae80..a2db4488c6 100644
--- a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java
@@ -15,15 +15,14 @@
  */
 package io.netty.handler.codec.compression;
 
+import static io.netty.handler.codec.compression.Snappy.validateChecksum;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufUtil;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.MessageList;
 import io.netty.handler.codec.ByteToMessageDecoder;
 
 import java.util.Arrays;
-
-import static io.netty.handler.codec.compression.Snappy.*;
+import java.util.List;
 
 /**
  * Uncompresses a {@link ByteBuf} encoded with the Snappy framing format.
@@ -76,7 +75,7 @@ public class SnappyFramedDecoder extends ByteToMessageDecoder {
     }
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
         if (corrupted) {
             in.skipBytes(in.readableBytes());
             return;
diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingDecoder.java b/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingDecoder.java
index 1551203d64..998881e615 100644
--- a/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingDecoder.java
@@ -18,13 +18,14 @@ package io.netty.handler.codec.marshalling;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.MessageList;
 import io.netty.handler.codec.ReplayingDecoder;
 import io.netty.handler.codec.TooLongFrameException;
-import org.jboss.marshalling.ByteInput;
-import org.jboss.marshalling.Unmarshaller;
 
 import java.io.ObjectStreamConstants;
+import java.util.List;
+
+import org.jboss.marshalling.ByteInput;
+import org.jboss.marshalling.Unmarshaller;
 
 /**
  * {@link ReplayingDecoder} which use an {@link Unmarshaller} to read the Object out of the {@link ByteBuf}.
@@ -55,7 +56,7 @@ public class CompatibleMarshallingDecoder extends ReplayingDecoder {
     }
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, MessageList out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception {
         if (discardingTooLongFrame) {
             buffer.skipBytes(actualReadableBytes());
             checkpoint();
@@ -83,7 +84,7 @@ public class CompatibleMarshallingDecoder extends ReplayingDecoder {
     }
 
     @Override
-    protected void decodeLast(ChannelHandlerContext ctx, ByteBuf buffer, MessageList out) throws Exception {
+    protected void decodeLast(ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception {
         switch (buffer.readableBytes()) {
         case 0:
             return;
diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java
index 0e5ea7fd05..c13b59bbf6 100644
--- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java
@@ -15,19 +15,21 @@
  */
 package io.netty.handler.codec.protobuf;
 
-import com.google.protobuf.ExtensionRegistry;
-import com.google.protobuf.Message;
-import com.google.protobuf.MessageLite;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPipeline;
-import io.netty.channel.MessageList;
 import io.netty.handler.codec.ByteToMessageDecoder;
 import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
 import io.netty.handler.codec.LengthFieldPrepender;
 import io.netty.handler.codec.MessageToMessageDecoder;
 
+import java.util.List;
+
+import com.google.protobuf.ExtensionRegistry;
+import com.google.protobuf.Message;
+import com.google.protobuf.MessageLite;
+
 /**
  * Decodes a received {@link ByteBuf} into a
  * Google Protocol Buffers
@@ -95,7 +97,7 @@ public class ProtobufDecoder extends MessageToMessageDecoder {
     }
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, MessageList out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception {
         final byte[] array;
         final int offset;
         final int length = msg.readableBytes();
diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java
index 27f1e96e3c..fe1229903a 100644
--- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java
@@ -15,19 +15,20 @@
  */
 package io.netty.handler.codec.protobuf;
 
-import com.google.protobuf.Message;
-import com.google.protobuf.MessageLite;
-import com.google.protobuf.MessageLiteOrBuilder;
+import static io.netty.buffer.Unpooled.wrappedBuffer;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPipeline;
-import io.netty.channel.MessageList;
 import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
 import io.netty.handler.codec.LengthFieldPrepender;
 import io.netty.handler.codec.MessageToMessageEncoder;
 
-import static io.netty.buffer.Unpooled.*;
+import java.util.List;
+
+import com.google.protobuf.Message;
+import com.google.protobuf.MessageLite;
+import com.google.protobuf.MessageLiteOrBuilder;
 
 /**
  * Encodes the requested Google
@@ -60,7 +61,7 @@ import static io.netty.buffer.Unpooled.*;
 public class ProtobufEncoder extends MessageToMessageEncoder {
     @Override
     protected void encode(
-            ChannelHandlerContext ctx, MessageLiteOrBuilder msg, MessageList out) throws Exception {
+            ChannelHandlerContext ctx, MessageLiteOrBuilder msg, List out) throws Exception {
         if (msg instanceof MessageLite) {
             out.add(wrappedBuffer(((MessageLite) msg).toByteArray()));
             return;
diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java
index b599f2ca56..b1abc5b410 100644
--- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java
@@ -15,13 +15,15 @@
  */
 package io.netty.handler.codec.protobuf;
 
-import com.google.protobuf.CodedInputStream;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.MessageList;
 import io.netty.handler.codec.ByteToMessageDecoder;
 import io.netty.handler.codec.CorruptedFrameException;
 
+import java.util.List;
+
+import com.google.protobuf.CodedInputStream;
+
 /**
  * A decoder that splits the received {@link ByteBuf}s dynamically by the
  * value of the Google Protocol Buffers
@@ -43,7 +45,7 @@ public class ProtobufVarint32FrameDecoder extends ByteToMessageDecoder {
     //      (just like LengthFieldBasedFrameDecoder)
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
         in.markReaderIndex();
         final byte[] buf = new byte[5];
         for (int i = 0; i < buf.length; i ++) {
diff --git a/codec/src/main/java/io/netty/handler/codec/string/StringDecoder.java b/codec/src/main/java/io/netty/handler/codec/string/StringDecoder.java
index cfa9324583..f3183113ff 100644
--- a/codec/src/main/java/io/netty/handler/codec/string/StringDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/string/StringDecoder.java
@@ -19,13 +19,13 @@ import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPipeline;
-import io.netty.channel.MessageList;
 import io.netty.handler.codec.ByteToMessageDecoder;
 import io.netty.handler.codec.DelimiterBasedFrameDecoder;
 import io.netty.handler.codec.LineBasedFrameDecoder;
 import io.netty.handler.codec.MessageToMessageDecoder;
 
 import java.nio.charset.Charset;
+import java.util.List;
 
 /**
  * Decodes a received {@link ByteBuf} into a {@link String}.  Please
@@ -75,7 +75,7 @@ public class StringDecoder extends MessageToMessageDecoder {
     }
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, MessageList out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception {
         out.add(msg.toString(charset));
     }
 }
diff --git a/codec/src/test/java/io/netty/handler/codec/ReplayingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/ReplayingDecoderTest.java
index 5a2e4e0fb2..8caf5f3937 100644
--- a/codec/src/test/java/io/netty/handler/codec/ReplayingDecoderTest.java
+++ b/codec/src/test/java/io/netty/handler/codec/ReplayingDecoderTest.java
@@ -15,15 +15,17 @@
  */
 package io.netty.handler.codec;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
 import io.netty.channel.embedded.EmbeddedChannel;
-import org.junit.Test;
 
-import static org.junit.Assert.*;
+import java.util.List;
+
+import org.junit.Test;
 
 public class ReplayingDecoderTest {
 
@@ -55,7 +57,7 @@ public class ReplayingDecoderTest {
         }
 
         @Override
-        protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) {
+        protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) {
             ByteBuf msg = in.readBytes(in.bytesBefore((byte) '\n'));
             out.add(msg);
             in.skipBytes(1);
@@ -80,9 +82,9 @@ public class ReplayingDecoderTest {
 
     private static final class BloatedLineDecoder extends ChannelInboundHandlerAdapter {
         @Override
-        public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
+        public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
             ctx.pipeline().replace(this, "less-bloated", new LineDecoder());
-            ctx.pipeline().fireMessageReceived(msgs);
+            ctx.pipeline().fireMessageReceived(msg);
         }
     }
 
diff --git a/example/src/main/java/io/netty/example/applet/AppletDiscardServer.java b/example/src/main/java/io/netty/example/applet/AppletDiscardServer.java
index 9422d841f6..aaffa34d1d 100644
--- a/example/src/main/java/io/netty/example/applet/AppletDiscardServer.java
+++ b/example/src/main/java/io/netty/example/applet/AppletDiscardServer.java
@@ -75,7 +75,7 @@ public class AppletDiscardServer extends JApplet {
     private static final class DiscardServerHandler extends SimpleChannelInboundHandler {
 
         @Override
-        public void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
+        public void messageReceived0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
             System.out.println("Received: " + msg.toString(CharsetUtil.UTF_8));
         }
 
diff --git a/example/src/main/java/io/netty/example/discard/DiscardClientHandler.java b/example/src/main/java/io/netty/example/discard/DiscardClientHandler.java
index c28c57fb6e..031ae874ed 100644
--- a/example/src/main/java/io/netty/example/discard/DiscardClientHandler.java
+++ b/example/src/main/java/io/netty/example/discard/DiscardClientHandler.java
@@ -19,8 +19,7 @@ import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
+import io.netty.channel.SimpleChannelInboundHandler;
 
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -28,7 +27,7 @@ import java.util.logging.Logger;
 /**
  * Handles a client-side channel.
  */
-public class DiscardClientHandler extends ChannelInboundHandlerAdapter {
+public class DiscardClientHandler extends SimpleChannelInboundHandler {
 
     private static final Logger logger = Logger.getLogger(
             DiscardClientHandler.class.getName());
@@ -63,9 +62,8 @@ public class DiscardClientHandler extends ChannelInboundHandlerAdapter {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
+    public void messageReceived0(ChannelHandlerContext ctx, Object msg) throws Exception {
         // Server is supposed to send nothing, but if it sends something, discard it.
-        msgs.releaseAllAndRecycle();
     }
 
     @Override
@@ -84,7 +82,7 @@ public class DiscardClientHandler extends ChannelInboundHandlerAdapter {
     private void generateTraffic() {
         // Flush the outbound buffer to the socket.
         // Once flushed, generate the same amount of traffic again.
-        ctx.write(content.duplicate().retain()).addListener(trafficGenerator);
+        ctx.write(content.duplicate().retain()).flush().addListener(trafficGenerator);
     }
 
     private final ChannelFutureListener trafficGenerator = new ChannelFutureListener() {
diff --git a/example/src/main/java/io/netty/example/discard/DiscardServerHandler.java b/example/src/main/java/io/netty/example/discard/DiscardServerHandler.java
index 3c3675a5ba..dee7c8e987 100644
--- a/example/src/main/java/io/netty/example/discard/DiscardServerHandler.java
+++ b/example/src/main/java/io/netty/example/discard/DiscardServerHandler.java
@@ -16,8 +16,7 @@
 package io.netty.example.discard;
 
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
+import io.netty.channel.SimpleChannelInboundHandler;
 
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -25,14 +24,14 @@ import java.util.logging.Logger;
 /**
  * Handles a server-side channel.
  */
-public class DiscardServerHandler extends ChannelInboundHandlerAdapter {
+public class DiscardServerHandler extends SimpleChannelInboundHandler {
 
     private static final Logger logger = Logger.getLogger(
             DiscardServerHandler.class.getName());
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        msgs.releaseAllAndRecycle();
+    public void messageReceived0(ChannelHandlerContext ctx, Object msg) throws Exception {
+        // discard
     }
 
     @Override
diff --git a/example/src/main/java/io/netty/example/echo/EchoClientHandler.java b/example/src/main/java/io/netty/example/echo/EchoClientHandler.java
index fd56fce11e..847f178e07 100644
--- a/example/src/main/java/io/netty/example/echo/EchoClientHandler.java
+++ b/example/src/main/java/io/netty/example/echo/EchoClientHandler.java
@@ -19,7 +19,6 @@ import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
 
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -51,12 +50,17 @@ public class EchoClientHandler extends ChannelInboundHandlerAdapter {
 
     @Override
     public void channelActive(ChannelHandlerContext ctx) {
-        ctx.write(firstMessage);
+        ctx.write(firstMessage).flush();
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        ctx.write(msgs);
+    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
+        ctx.write(msg);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+       ctx.flush();
     }
 
     @Override
diff --git a/example/src/main/java/io/netty/example/echo/EchoServerHandler.java b/example/src/main/java/io/netty/example/echo/EchoServerHandler.java
index 110af40f70..1a7ce7294b 100644
--- a/example/src/main/java/io/netty/example/echo/EchoServerHandler.java
+++ b/example/src/main/java/io/netty/example/echo/EchoServerHandler.java
@@ -18,7 +18,6 @@ package io.netty.example.echo;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
 
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -33,8 +32,13 @@ public class EchoServerHandler extends ChannelInboundHandlerAdapter {
             EchoServerHandler.class.getName());
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        ctx.write(msgs);
+    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
+        ctx.write(msg);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 
     @Override
diff --git a/example/src/main/java/io/netty/example/factorial/BigIntegerDecoder.java b/example/src/main/java/io/netty/example/factorial/BigIntegerDecoder.java
index 8ed01fa4f0..a635ba5044 100644
--- a/example/src/main/java/io/netty/example/factorial/BigIntegerDecoder.java
+++ b/example/src/main/java/io/netty/example/factorial/BigIntegerDecoder.java
@@ -17,11 +17,11 @@ package io.netty.example.factorial;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.MessageList;
 import io.netty.handler.codec.ByteToMessageDecoder;
 import io.netty.handler.codec.CorruptedFrameException;
 
 import java.math.BigInteger;
+import java.util.List;
 
 /**
  * Decodes the binary representation of a {@link BigInteger} prepended
@@ -32,7 +32,7 @@ import java.math.BigInteger;
 public class BigIntegerDecoder extends ByteToMessageDecoder {
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) {
         // Wait until the length prefix is available.
         if (in.readableBytes() < 5) {
             return;
diff --git a/example/src/main/java/io/netty/example/factorial/FactorialClientHandler.java b/example/src/main/java/io/netty/example/factorial/FactorialClientHandler.java
index ac58bde9d1..6d1429f7f1 100644
--- a/example/src/main/java/io/netty/example/factorial/FactorialClientHandler.java
+++ b/example/src/main/java/io/netty/example/factorial/FactorialClientHandler.java
@@ -18,7 +18,6 @@ package io.netty.example.factorial;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.MessageList;
 import io.netty.channel.SimpleChannelInboundHandler;
 
 import java.math.BigInteger;
@@ -71,7 +70,7 @@ public class FactorialClientHandler extends SimpleChannelInboundHandler out = MessageList.newInstance(4096);
-        while (out.size() < 4096) {
+        for (int i = 0; i < 4096; i++) {
             if (i <= count) {
-                out.add(Integer.valueOf(i));
+                ctx.write(Integer.valueOf(i));
                 i ++;
             } else {
                 finished = true;
@@ -108,7 +106,7 @@ public class FactorialClientHandler extends SimpleChannelInboundHandler {
 
     private static final Logger logger = Logger.getLogger(
             FactorialServerHandler.class.getName());
@@ -40,29 +39,21 @@ public class FactorialServerHandler extends ChannelInboundHandlerAdapter {
     private BigInteger factorial = new BigInteger("1");
 
     @Override
-    public void messageReceived(
-            ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        MessageList ints = msgs.cast();
-        for (int i = 0; i < ints.size(); i++) {
-            BigInteger msg = ints.get(i);
-            // Calculate the cumulative factorial and send it to the client.
-            lastMultiplier = msg;
-            factorial = factorial.multiply(msg);
-            ctx.write(factorial);
-        }
-        msgs.recycle();
+    public void messageReceived0(ChannelHandlerContext ctx, BigInteger msg) throws Exception {
+        // Calculate the cumulative factorial and send it to the client.
+        lastMultiplier = msg;
+        factorial = factorial.multiply(msg);
+        ctx.write(factorial).flush();
     }
 
     @Override
-    public void channelInactive(
-            ChannelHandlerContext ctx) throws Exception {
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
         logger.info(new Formatter().format(
                 "Factorial of %,d is: %,d", lastMultiplier, factorial).toString());
     }
 
     @Override
-    public void exceptionCaught(
-            ChannelHandlerContext ctx, Throwable cause) throws Exception {
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
         logger.log(
                 Level.WARNING,
                 "Unexpected exception from downstream.", cause);
diff --git a/example/src/main/java/io/netty/example/filetransfer/FileServer.java b/example/src/main/java/io/netty/example/filetransfer/FileServer.java
index 31fed02e02..ab05d9e9f2 100644
--- a/example/src/main/java/io/netty/example/filetransfer/FileServer.java
+++ b/example/src/main/java/io/netty/example/filetransfer/FileServer.java
@@ -23,7 +23,6 @@ import io.netty.channel.ChannelOption;
 import io.netty.channel.DefaultFileRegion;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.FileRegion;
-import io.netty.channel.MessageList;
 import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.channel.socket.SocketChannel;
@@ -94,7 +93,7 @@ public class FileServer {
 
     private static final class FileHandler extends SimpleChannelInboundHandler {
         @Override
-        public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
+        public void messageReceived0(ChannelHandlerContext ctx, String msg) throws Exception {
             File file = new File(msg);
             if (file.exists()) {
                 if (!file.isFile()) {
@@ -102,11 +101,10 @@ public class FileServer {
                     return;
                 }
                 ctx.write(file + " " + file.length() + '\n');
-                MessageList out = MessageList.newInstance();
                 FileRegion region = new DefaultFileRegion(new FileInputStream(file).getChannel(), 0, file.length());
-                out.add(region);
-                out.add("\n");
-                ctx.write(out);
+                ctx.write(region);
+                ctx.write("\n");
+                ctx.flush();
             } else {
                 ctx.write("File not found: " + file + '\n');
             }
diff --git a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java
index 43b51fc50c..c15590ef0a 100644
--- a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java
+++ b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java
@@ -19,7 +19,6 @@ import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.MessageList;
 import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.handler.codec.http.DefaultFullHttpResponse;
 import io.netty.handler.codec.http.DefaultHttpResponse;
@@ -105,7 +104,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler out = MessageList.newInstance();
         // Write the initial line and the header.
-        out.add(response);
+        ctx.write(response);
         // Write the content.
-        out.add(new ChunkedFile(raf, 0, fileLength, 8192));
+        ctx.write(new ChunkedFile(raf, 0, fileLength, 8192));
         // Write the end marker
-        out.add(LastHttpContent.EMPTY_LAST_CONTENT);
+        ctx.write(LastHttpContent.EMPTY_LAST_CONTENT);
 
-        ChannelFuture writeFuture = ctx.write(out);
+        ChannelFuture writeFuture = ctx.flush();
         // Decide whether to close the connection or not.
         if (!isKeepAlive(request)) {
             // Close the connection when the whole content is written out.
@@ -279,7 +277,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler {
+public class HttpHelloWorldServerHandler extends ChannelInboundHandlerAdapter {
     private static final ByteBuf CONTENT =
             Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hello World", CharsetUtil.US_ASCII));
 
-    private MessageList out;
     @Override
-    protected void beginMessageReceived(ChannelHandlerContext ctx) {
-        out = MessageList.newInstance();
-    }
-
-    @Override
-    protected void endMessageReceived(ChannelHandlerContext ctx) {
-        ctx.write(out);
-        out = null;
+    public void messageReceivedLast(ChannelHandlerContext ctx) {
+        ctx.flush();
     }
 
     @Override
@@ -53,7 +45,7 @@ public class HttpHelloWorldServerHandler extends SimpleChannelInboundHandler {
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
+    public void messageReceived0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
         if (msg instanceof HttpResponse) {
             HttpResponse response = (HttpResponse) msg;
 
diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java
index 62c4c8d4a9..3c5d518fae 100644
--- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java
+++ b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java
@@ -18,8 +18,7 @@ package io.netty.example.http.snoop;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
+import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.handler.codec.DecoderResult;
 import io.netty.handler.codec.http.Cookie;
 import io.netty.handler.codec.http.CookieDecoder;
@@ -44,34 +43,24 @@ import static io.netty.handler.codec.http.HttpHeaders.*;
 import static io.netty.handler.codec.http.HttpResponseStatus.*;
 import static io.netty.handler.codec.http.HttpVersion.*;
 
-public class HttpSnoopServerHandler extends ChannelInboundHandlerAdapter {
+public class HttpSnoopServerHandler extends SimpleChannelInboundHandler {
 
     private HttpRequest request;
     /** Buffer that stores the response content */
     private final StringBuilder buf = new StringBuilder();
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        MessageList out = MessageList.newInstance();
-        int size = msgs.size();
-        try {
-            for (int i = 0; i < size; i ++) {
-                if (!messageReceived(ctx, msgs.get(i), out)) {
-                    break;
-                }
-            }
-        } finally {
-            msgs.releaseAllAndRecycle();
-            ctx.write(out);
-        }
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 
-    private boolean messageReceived(ChannelHandlerContext ctx, Object msg, MessageList out) {
+    @Override
+    protected void messageReceived0(ChannelHandlerContext ctx, Object msg) {
         if (msg instanceof HttpRequest) {
             HttpRequest request = this.request = (HttpRequest) msg;
 
             if (is100ContinueExpected(request)) {
-                send100Continue(out);
+                send100Continue(ctx);
             }
 
             buf.setLength(0);
@@ -134,10 +123,9 @@ public class HttpSnoopServerHandler extends ChannelInboundHandlerAdapter {
                     buf.append("\r\n");
                 }
 
-                return writeResponse(trailer, out);
+                writeResponse(trailer, ctx);
             }
         }
-        return true;
     }
 
     private static void appendDecoderResult(StringBuilder buf, HttpObject o) {
@@ -151,7 +139,7 @@ public class HttpSnoopServerHandler extends ChannelInboundHandlerAdapter {
         buf.append("\r\n");
     }
 
-    private boolean writeResponse(HttpObject currentObj, MessageList out) {
+    private boolean writeResponse(HttpObject currentObj, ChannelHandlerContext ctx) {
         // Decide whether to close the connection or not.
         boolean keepAlive = isKeepAlive(request);
         // Build the response object.
@@ -186,14 +174,14 @@ public class HttpSnoopServerHandler extends ChannelInboundHandlerAdapter {
         }
 
         // Write the response.
-        out.add(response);
+        ctx.write(response);
 
         return keepAlive;
     }
 
-    private static void send100Continue(MessageList out) {
+    private static void send100Continue(ChannelHandlerContext ctx) {
         FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, CONTINUE);
-        out.add(response);
+        ctx.write(response);
     }
 
     @Override
diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java
index 51fec007c4..666773e225 100644
--- a/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java
+++ b/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java
@@ -202,7 +202,7 @@ public class HttpUploadClient {
 
         // send request
         List> entries = headers.entries();
-        channel.write(request).sync();
+        channel.write(request).flush().sync();
 
         // Wait for the server to close the connection.
         channel.closeFuture().sync();
@@ -276,7 +276,7 @@ public class HttpUploadClient {
         if (bodyRequestEncoder.isChunked()) {
             // could do either request.isChunked()
             // either do it through ChunkedWriteHandler
-            channel.write(bodyRequestEncoder).awaitUninterruptibly();
+            channel.write(bodyRequestEncoder).flush().awaitUninterruptibly();
         }
 
         // Do not clear here since we will reuse the InterfaceHttpData on the
@@ -351,7 +351,7 @@ public class HttpUploadClient {
 
         // test if request was chunked and if so, finish the write
         if (bodyRequestEncoder.isChunked()) {
-            channel.write(bodyRequestEncoder).awaitUninterruptibly();
+            channel.write(bodyRequestEncoder).flush().awaitUninterruptibly();
         }
 
         // Now no more use of file representation (and list of HttpData)
diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java
index 393715f5b3..463afd686d 100644
--- a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java
+++ b/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java
@@ -36,7 +36,7 @@ public class HttpUploadClientHandler extends SimpleChannelInboundHandler msgs) throws Exception {
-        MessageList out = MessageList.newInstance(msgs.size());
-        for (int i = 0; i < msgs.size(); i++) {
-            Object msg = msgs.get(i);
-            if (msg instanceof FullHttpRequest) {
-                handleHttpRequest(ctx, (FullHttpRequest) msg);
-            } else if (msg instanceof WebSocketFrame) {
-                handleWebSocketFrame(ctx, (WebSocketFrame) msg, out);
-            }
+    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
+        if (msg instanceof FullHttpRequest) {
+            handleHttpRequest(ctx, (FullHttpRequest) msg);
+        } else if (msg instanceof WebSocketFrame) {
+            handleWebSocketFrame(ctx, (WebSocketFrame) msg);
         }
-        msgs.recycle();
-        ctx.write(out);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 
     private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req)
@@ -92,7 +90,7 @@ public class AutobahnServerHandler extends ChannelInboundHandlerAdapter {
         }
     }
 
-    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame, MessageList out) {
+    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
         if (logger.isLoggable(Level.FINE)) {
             logger.fine(String.format(
                     "Channel %s received %s", ctx.channel().hashCode(), frame.getClass().getSimpleName()));
@@ -101,13 +99,13 @@ public class AutobahnServerHandler extends ChannelInboundHandlerAdapter {
         if (frame instanceof CloseWebSocketFrame) {
             handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame);
         } else if (frame instanceof PingWebSocketFrame) {
-            out.add(new PongWebSocketFrame(frame.isFinalFragment(), frame.rsv(), frame.content()));
+            ctx.write(new PongWebSocketFrame(frame.isFinalFragment(), frame.rsv(), frame.content()));
         } else if (frame instanceof TextWebSocketFrame) {
-            out.add(frame);
+            ctx.write(frame);
         } else if (frame instanceof BinaryWebSocketFrame) {
-            out.add(frame);
+            ctx.write(frame);
         } else if (frame instanceof ContinuationWebSocketFrame) {
-            out.add(frame);
+            ctx.write(frame);
         } else if (frame instanceof PongWebSocketFrame) {
             frame.release();
             // Ignore
@@ -126,7 +124,7 @@ public class AutobahnServerHandler extends ChannelInboundHandlerAdapter {
         }
 
         // Send the response and close the connection if necessary.
-        ChannelFuture f = ctx.channel().write(res);
+        ChannelFuture f = ctx.channel().write(res).flush();
         if (!isKeepAlive(req) || res.getStatus().code() != 200) {
             f.addListener(ChannelFutureListener.CLOSE);
         }
diff --git a/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClientHandler.java b/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClientHandler.java
index e95c366a8a..2bd50a830d 100644
--- a/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClientHandler.java
+++ b/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClientHandler.java
@@ -79,7 +79,7 @@ public class WebSocketClientHandler extends SimpleChannelInboundHandler
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
+    public void messageReceived0(ChannelHandlerContext ctx, Object msg) throws Exception {
         Channel ch = ctx.channel();
         if (!handshaker.isHandshakeComplete()) {
             handshaker.finishHandshake(ch, (FullHttpResponse) msg);
diff --git a/example/src/main/java/io/netty/example/http/websocketx/html5/CustomTextFrameHandler.java b/example/src/main/java/io/netty/example/http/websocketx/html5/CustomTextFrameHandler.java
index 22173308a3..0259062b5d 100644
--- a/example/src/main/java/io/netty/example/http/websocketx/html5/CustomTextFrameHandler.java
+++ b/example/src/main/java/io/netty/example/http/websocketx/html5/CustomTextFrameHandler.java
@@ -21,7 +21,7 @@ import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
 
 public class CustomTextFrameHandler extends SimpleChannelInboundHandler {
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception {
+    protected void messageReceived0(ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception {
         String request = frame.text();
         ctx.channel().write(new TextWebSocketFrame(request.toUpperCase()));
     }
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 c625eb01b9..a825cace6c 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
@@ -53,7 +53,7 @@ public class WebSocketServerHandler extends SimpleChannelInboundHandler
     private WebSocketServerHandshaker handshaker;
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
+    public void messageReceived0(ChannelHandlerContext ctx, Object msg) throws Exception {
         if (msg instanceof FullHttpRequest) {
             handleHttpRequest(ctx, (FullHttpRequest) msg);
         } else if (msg instanceof WebSocketFrame) {
@@ -135,7 +135,7 @@ public class WebSocketServerHandler extends SimpleChannelInboundHandler
         }
 
         // Send the response and close the connection if necessary.
-        ChannelFuture f = ctx.channel().write(res);
+        ChannelFuture f = ctx.channel().write(res).flush();
         if (!isKeepAlive(req) || res.getStatus().code() != 200) {
             f.addListener(ChannelFutureListener.CLOSE);
         }
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 79504131b7..b51c0ab99b 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
@@ -54,7 +54,7 @@ public class WebSocketSslServerHandler extends SimpleChannelInboundHandler {
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
+    public void messageReceived0(ChannelHandlerContext ctx, Object msg) throws Exception {
         // Print as received
         System.out.println(msg);
     }
diff --git a/example/src/main/java/io/netty/example/localecho/LocalEchoServerHandler.java b/example/src/main/java/io/netty/example/localecho/LocalEchoServerHandler.java
index bb4c334d1c..1e2c17199e 100644
--- a/example/src/main/java/io/netty/example/localecho/LocalEchoServerHandler.java
+++ b/example/src/main/java/io/netty/example/localecho/LocalEchoServerHandler.java
@@ -17,14 +17,19 @@ package io.netty.example.localecho;
 
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
+
 
 public class LocalEchoServerHandler extends ChannelInboundHandlerAdapter {
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
+    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
         // Write back as received
-        ctx.write(msgs);
+        ctx.write(msg);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 
     @Override
diff --git a/example/src/main/java/io/netty/example/objectecho/ObjectEchoClientHandler.java b/example/src/main/java/io/netty/example/objectecho/ObjectEchoClientHandler.java
index f4e2869237..679609823e 100644
--- a/example/src/main/java/io/netty/example/objectecho/ObjectEchoClientHandler.java
+++ b/example/src/main/java/io/netty/example/objectecho/ObjectEchoClientHandler.java
@@ -17,7 +17,6 @@ package io.netty.example.objectecho;
 
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -57,9 +56,14 @@ public class ObjectEchoClientHandler extends ChannelInboundHandlerAdapter {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
+    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
         // Echo back the received object to the server.
-        ctx.write(msgs);
+        ctx.write(msg);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 
     @Override
diff --git a/example/src/main/java/io/netty/example/objectecho/ObjectEchoServerHandler.java b/example/src/main/java/io/netty/example/objectecho/ObjectEchoServerHandler.java
index f5f4d2ffc5..60676ee4fc 100644
--- a/example/src/main/java/io/netty/example/objectecho/ObjectEchoServerHandler.java
+++ b/example/src/main/java/io/netty/example/objectecho/ObjectEchoServerHandler.java
@@ -17,7 +17,6 @@ package io.netty.example.objectecho;
 
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
 
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -33,9 +32,14 @@ public class ObjectEchoServerHandler extends ChannelInboundHandlerAdapter {
 
     @Override
     public void messageReceived(
-            ChannelHandlerContext ctx, MessageList msgs) throws Exception {
+            ChannelHandlerContext ctx, Object msg) throws Exception {
         // Echo back the received object to the client.
-        ctx.write(msgs);
+        ctx.write(msg);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 
     @Override
diff --git a/example/src/main/java/io/netty/example/portunification/PortUnificationServerHandler.java b/example/src/main/java/io/netty/example/portunification/PortUnificationServerHandler.java
index 4594fea6f0..d3f4b2ede7 100644
--- a/example/src/main/java/io/netty/example/portunification/PortUnificationServerHandler.java
+++ b/example/src/main/java/io/netty/example/portunification/PortUnificationServerHandler.java
@@ -18,7 +18,6 @@ package io.netty.example.portunification;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPipeline;
-import io.netty.channel.MessageList;
 import io.netty.example.factorial.BigIntegerDecoder;
 import io.netty.example.factorial.FactorialServerHandler;
 import io.netty.example.factorial.NumberEncoder;
@@ -33,6 +32,7 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
 import io.netty.handler.ssl.SslHandler;
 
 import javax.net.ssl.SSLEngine;
+import java.util.List;
 
 /**
  * Manipulates the current pipeline dynamically to switch protocols or enable
@@ -53,7 +53,7 @@ public class PortUnificationServerHandler extends ByteToMessageDecoder {
     }
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
         // Will use the first five bytes to detect a protocol.
         if (in.readableBytes() < 5) {
             return;
diff --git a/example/src/main/java/io/netty/example/proxy/HexDumpProxyBackendHandler.java b/example/src/main/java/io/netty/example/proxy/HexDumpProxyBackendHandler.java
index cb917efd81..18743b6a4d 100644
--- a/example/src/main/java/io/netty/example/proxy/HexDumpProxyBackendHandler.java
+++ b/example/src/main/java/io/netty/example/proxy/HexDumpProxyBackendHandler.java
@@ -21,7 +21,6 @@ import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
 
 public class HexDumpProxyBackendHandler extends ChannelInboundHandlerAdapter {
 
@@ -38,8 +37,8 @@ public class HexDumpProxyBackendHandler extends ChannelInboundHandlerAdapter {
     }
 
     @Override
-    public void messageReceived(final ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        inboundChannel.write(msgs).addListener(new ChannelFutureListener() {
+    public void messageReceived(final ChannelHandlerContext ctx, Object msg) throws Exception {
+        inboundChannel.write(msg).flush().addListener(new ChannelFutureListener() {
             @Override
             public void operationComplete(ChannelFuture future) throws Exception {
                 if (future.isSuccess()) {
diff --git a/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java b/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java
index 26b83d63dc..8b4f25434c 100644
--- a/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java
+++ b/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java
@@ -23,7 +23,6 @@ import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
 import io.netty.channel.ChannelOption;
-import io.netty.channel.MessageList;
 
 public class HexDumpProxyFrontendHandler extends ChannelInboundHandlerAdapter {
 
@@ -64,9 +63,9 @@ public class HexDumpProxyFrontendHandler extends ChannelInboundHandlerAdapter {
     }
 
     @Override
-    public void messageReceived(final ChannelHandlerContext ctx, MessageList msgs) throws Exception {
+    public void messageReceived(final ChannelHandlerContext ctx, Object msg) throws Exception {
         if (outboundChannel.isActive()) {
-            outboundChannel.write(msgs).addListener(new ChannelFutureListener() {
+            outboundChannel.write(msg).flush().addListener(new ChannelFutureListener() {
                 @Override
                 public void operationComplete(ChannelFuture future) throws Exception {
                     if (future.isSuccess()) {
@@ -98,7 +97,7 @@ public class HexDumpProxyFrontendHandler extends ChannelInboundHandlerAdapter {
      */
     static void closeOnFlush(Channel ch) {
         if (ch.isActive()) {
-            ch.write(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+            ch.write(Unpooled.EMPTY_BUFFER).flush().addListener(ChannelFutureListener.CLOSE);
         }
     }
 }
diff --git a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClient.java b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClient.java
index 2a94ce7ff5..70988c720c 100644
--- a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClient.java
+++ b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClient.java
@@ -55,7 +55,7 @@ public class QuoteOfTheMomentClient {
             // Broadcast the QOTM request to port 8080.
             ch.write(new DatagramPacket(
                     Unpooled.copiedBuffer("QOTM?", CharsetUtil.UTF_8),
-                    new InetSocketAddress("255.255.255.255", port))).sync();
+                    new InetSocketAddress("255.255.255.255", port))).flush().sync();
 
             // QuoteOfTheMomentClientHandler will close the DatagramChannel when a
             // response is received.  If the channel is not closed within 5 seconds,
diff --git a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClientHandler.java b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClientHandler.java
index de248d93b7..2bfea4c45b 100644
--- a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClientHandler.java
+++ b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClientHandler.java
@@ -23,7 +23,7 @@ import io.netty.util.CharsetUtil;
 public class QuoteOfTheMomentClientHandler extends SimpleChannelInboundHandler {
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
+    public void messageReceived0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
         String response = msg.content().toString(CharsetUtil.UTF_8);
         if (response.startsWith("QOTM: ")) {
             System.out.println("Quote of the Moment: " + response.substring(6));
diff --git a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServerHandler.java b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServerHandler.java
index cdcff680e6..c19aa17781 100644
--- a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServerHandler.java
+++ b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServerHandler.java
@@ -44,7 +44,7 @@ public class QuoteOfTheMomentServerHandler extends SimpleChannelInboundHandler {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
+    public void messageReceived0(ChannelHandlerContext ctx, String msg) throws Exception {
         if ("OK".equals(msg)) {
             System.out.println("Serial port responded to AT");
         } else {
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 79ed5b2f14..47d0ef73e8 100644
--- a/example/src/main/java/io/netty/example/sctp/SctpEchoClientHandler.java
+++ b/example/src/main/java/io/netty/example/sctp/SctpEchoClientHandler.java
@@ -16,11 +16,9 @@
 package io.netty.example.sctp;
 
 import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
 import io.netty.channel.sctp.SctpMessage;
 
 import java.util.logging.Level;
@@ -57,8 +55,13 @@ public class SctpEchoClientHandler extends ChannelInboundHandlerAdapter {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList messages) throws Exception {
-        ctx.write(messages);
+    public void messageReceived(ChannelHandlerContext ctx,  Object msg) throws Exception {
+        ctx.write(msg);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        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 1972c56bb4..04595ddc63 100644
--- a/example/src/main/java/io/netty/example/sctp/SctpEchoServerHandler.java
+++ b/example/src/main/java/io/netty/example/sctp/SctpEchoServerHandler.java
@@ -18,7 +18,6 @@ package io.netty.example.sctp;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
 
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -40,7 +39,12 @@ public class SctpEchoServerHandler extends ChannelInboundHandlerAdapter {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        ctx.write(msgs);
+    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
+        ctx.write(msg);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 }
diff --git a/example/src/main/java/io/netty/example/securechat/SecureChatClient.java b/example/src/main/java/io/netty/example/securechat/SecureChatClient.java
index a4dc9045dd..a3d3e33810 100644
--- a/example/src/main/java/io/netty/example/securechat/SecureChatClient.java
+++ b/example/src/main/java/io/netty/example/securechat/SecureChatClient.java
@@ -60,7 +60,7 @@ public class SecureChatClient {
                 }
 
                 // Sends the received line to the server.
-                lastWriteFuture = ch.write(line + "\r\n");
+                lastWriteFuture = ch.write(line + "\r\n").flush();
 
                 // If user typed the 'bye' command, wait until the server closes
                 // the connection.
diff --git a/example/src/main/java/io/netty/example/securechat/SecureChatClientHandler.java b/example/src/main/java/io/netty/example/securechat/SecureChatClientHandler.java
index 7ee6977fe1..5dc5e6da51 100644
--- a/example/src/main/java/io/netty/example/securechat/SecureChatClientHandler.java
+++ b/example/src/main/java/io/netty/example/securechat/SecureChatClientHandler.java
@@ -30,7 +30,7 @@ public class SecureChatClientHandler extends SimpleChannelInboundHandler
             SecureChatClientHandler.class.getName());
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
+    public void messageReceived0(ChannelHandlerContext ctx, String msg) throws Exception {
         System.err.println(msg);
     }
 
diff --git a/example/src/main/java/io/netty/example/securechat/SecureChatServerHandler.java b/example/src/main/java/io/netty/example/securechat/SecureChatServerHandler.java
index 0a594d8d71..5c9c3b704e 100644
--- a/example/src/main/java/io/netty/example/securechat/SecureChatServerHandler.java
+++ b/example/src/main/java/io/netty/example/securechat/SecureChatServerHandler.java
@@ -61,7 +61,7 @@ public class SecureChatServerHandler extends SimpleChannelInboundHandler
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
+    public void messageReceived0(ChannelHandlerContext ctx, String msg) throws Exception {
         // Send the received message to all channels but the current one.
         for (Channel c: channels) {
             if (c != ctx.channel()) {
diff --git a/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java b/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java
index c05f2d1494..6611964969 100644
--- a/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java
+++ b/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java
@@ -19,7 +19,7 @@ import io.netty.buffer.Unpooled;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
+import io.netty.util.ReferenceCountUtil;
 
 
 public final class RelayHandler extends ChannelInboundHandlerAdapter {
@@ -41,9 +41,11 @@ public final class RelayHandler extends ChannelInboundHandlerAdapter {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
+    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
         if (relayChannel.isActive()) {
-            relayChannel.write(msgs);
+            relayChannel.write(msg).flush();
+        } else {
+            ReferenceCountUtil.release(msg);
         }
     }
 
diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java b/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java
index 89fb5af1d1..d4f751f376 100644
--- a/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java
+++ b/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java
@@ -39,11 +39,11 @@ public final class SocksServerConnectHandler extends SimpleChannelInboundHandler
     private final Bootstrap b = new Bootstrap();
 
     @Override
-    public void messageReceived(final ChannelHandlerContext ctx, final SocksCmdRequest request) throws Exception {
+    public void messageReceived0(final ChannelHandlerContext ctx, final SocksCmdRequest request) throws Exception {
         CallbackNotifier cb = new CallbackNotifier() {
             @Override
             public void onSuccess(final ChannelHandlerContext outboundCtx) {
-                ctx.channel().write(new SocksCmdResponse(SocksCmdStatus.SUCCESS, request.addressType()))
+                ctx.channel().write(new SocksCmdResponse(SocksCmdStatus.SUCCESS, request.addressType())).flush()
                              .addListener(new ChannelFutureListener() {
                     @Override
                     public void operationComplete(ChannelFuture channelFuture) throws Exception {
diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java b/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java
index 19739f84ba..3fb10fd8b3 100644
--- a/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java
+++ b/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java
@@ -37,7 +37,7 @@ public final class SocksServerHandler extends SimpleChannelInboundHandler {
     private static final Logger logger = Logger.getLogger(TelnetClientHandler.class.getName());
 
     @Override
-    protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
+    protected void messageReceived0(ChannelHandlerContext ctx, String msg) throws Exception {
         System.err.println(msg);
     }
 
diff --git a/example/src/main/java/io/netty/example/telnet/TelnetServerHandler.java b/example/src/main/java/io/netty/example/telnet/TelnetServerHandler.java
index 01ee2b7999..889e7ad8ca 100644
--- a/example/src/main/java/io/netty/example/telnet/TelnetServerHandler.java
+++ b/example/src/main/java/io/netty/example/telnet/TelnetServerHandler.java
@@ -43,7 +43,7 @@ public class TelnetServerHandler extends SimpleChannelInboundHandler {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, String request) throws Exception {
+    public void messageReceived0(ChannelHandlerContext ctx, String request) throws Exception {
 
         // Generate and write a response.
         String response;
@@ -59,7 +59,7 @@ public class TelnetServerHandler extends SimpleChannelInboundHandler {
 
         // We do not need to write a ChannelBuffer here.
         // We know the encoder inserted at TelnetPipelineFactory will do the conversion.
-        ChannelFuture future = ctx.write(response);
+        ChannelFuture future = ctx.write(response).flush();
 
         // Close the connection after sending 'Have a good day!'
         // if the client has sent 'bye'.
diff --git a/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoClientHandler.java b/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoClientHandler.java
index 02681f8912..e2e26813c6 100644
--- a/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoClientHandler.java
+++ b/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoClientHandler.java
@@ -20,8 +20,7 @@ import com.yammer.metrics.core.Meter;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
+import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.channel.udt.nio.NioUdtProvider;
 
 import java.util.concurrent.TimeUnit;
@@ -33,7 +32,7 @@ import java.util.logging.Logger;
  * traffic between the echo client and server by sending the first message to
  * the server on activation.
  */
-public class ByteEchoClientHandler extends ChannelInboundHandlerAdapter {
+public class ByteEchoClientHandler extends SimpleChannelInboundHandler {
 
     private static final Logger log = Logger.getLogger(ByteEchoClientHandler.class.getName());
 
@@ -43,6 +42,8 @@ public class ByteEchoClientHandler extends ChannelInboundHandlerAdapter {
             "bytes", TimeUnit.SECONDS);
 
     public ByteEchoClientHandler(final int messageSize) {
+        super(false);
+
         message = Unpooled.buffer(messageSize);
 
         for (int i = 0; i < message.capacity(); i++) {
@@ -57,14 +58,15 @@ public class ByteEchoClientHandler extends ChannelInboundHandlerAdapter {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        MessageList buffers = msgs.cast();
+    public void messageReceived0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
+        meter.mark(msg.readableBytes());
 
-        for (int i = 0; i < buffers.size(); i++) {
-            ByteBuf buf = buffers.get(i);
-            meter.mark(buf.readableBytes());
-        }
-        ctx.write(buffers);
+        ctx.write(msg);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 
     @Override
diff --git a/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoServerHandler.java b/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoServerHandler.java
index af3eebedee..31c387678b 100644
--- a/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoServerHandler.java
+++ b/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoServerHandler.java
@@ -18,7 +18,6 @@ package io.netty.example.udt.echo.bytes;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
 import io.netty.channel.udt.nio.NioUdtProvider;
 
 import java.util.logging.Level;
@@ -33,8 +32,13 @@ public class ByteEchoServerHandler extends ChannelInboundHandlerAdapter {
     private static final Logger log = Logger.getLogger(ByteEchoServerHandler.class.getName());
 
     @Override
-    public void messageReceived(final ChannelHandlerContext ctx, MessageList msgs) {
-        ctx.write(msgs);
+    public void messageReceived(final ChannelHandlerContext ctx, Object msg) {
+        ctx.write(msg);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 
     @Override
diff --git a/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoClientHandler.java b/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoClientHandler.java
index 544a3ff1ea..4207a4bc5b 100644
--- a/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoClientHandler.java
+++ b/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoClientHandler.java
@@ -20,8 +20,7 @@ import com.yammer.metrics.core.Meter;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
+import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.channel.udt.UdtMessage;
 import io.netty.channel.udt.nio.NioUdtProvider;
 
@@ -34,13 +33,14 @@ import java.util.logging.Logger;
  * traffic between the echo client and server by sending the first message to
  * the server on activation.
  */
-public class MsgEchoClientHandler extends ChannelInboundHandlerAdapter {
+public class MsgEchoClientHandler extends SimpleChannelInboundHandler {
 
     private static final Logger log = Logger.getLogger(MsgEchoClientHandler.class.getName());
 
     private final UdtMessage message;
 
     public MsgEchoClientHandler(final int messageSize) {
+        super(false);
         final ByteBuf byteBuf = Unpooled.buffer(messageSize);
         for (int i = 0; i < byteBuf.capacity(); i++) {
             byteBuf.writeByte((byte) i);
@@ -65,13 +65,14 @@ public class MsgEchoClientHandler extends ChannelInboundHandlerAdapter {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        MessageList buffers = msgs.cast();
+    public void messageReceived0(ChannelHandlerContext ctx, UdtMessage msg) throws Exception {
+        meter.mark(msg.content().readableBytes());
 
-        for (int i = 0; i < buffers.size(); i++) {
-            UdtMessage message = buffers.get(i);
-            meter.mark(message.content().readableBytes());
-        }
-        ctx.write(msgs);
+        ctx.write(msg);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 }
diff --git a/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoServerHandler.java b/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoServerHandler.java
index b08e471cf0..40a091a4b1 100644
--- a/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoServerHandler.java
+++ b/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoServerHandler.java
@@ -18,7 +18,6 @@ package io.netty.example.udt.echo.message;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
 import io.netty.channel.udt.nio.NioUdtProvider;
 
 import java.util.logging.Level;
@@ -45,7 +44,12 @@ public class MsgEchoServerHandler extends ChannelInboundHandlerAdapter {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        ctx.write(msgs);
+    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
+        ctx.write(msg);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 }
diff --git a/example/src/main/java/io/netty/example/udt/echo/rendezvous/MsgEchoPeerHandler.java b/example/src/main/java/io/netty/example/udt/echo/rendezvous/MsgEchoPeerHandler.java
index 7a78961ef4..194a9f8cec 100644
--- a/example/src/main/java/io/netty/example/udt/echo/rendezvous/MsgEchoPeerHandler.java
+++ b/example/src/main/java/io/netty/example/udt/echo/rendezvous/MsgEchoPeerHandler.java
@@ -20,8 +20,7 @@ import com.yammer.metrics.core.Meter;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
+import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.channel.udt.UdtMessage;
 import io.netty.channel.udt.nio.NioUdtProvider;
 
@@ -35,13 +34,14 @@ import java.util.logging.Logger;
  * activation.
  */
 public class MsgEchoPeerHandler extends
-        ChannelInboundHandlerAdapter {
+        SimpleChannelInboundHandler {
 
     private static final Logger log = Logger.getLogger(MsgEchoPeerHandler.class.getName());
 
     private final UdtMessage message;
 
     public MsgEchoPeerHandler(final int messageSize) {
+        super(false);
         final ByteBuf byteBuf = Unpooled.buffer(messageSize);
         for (int i = 0; i < byteBuf.capacity(); i++) {
             byteBuf.writeByte((byte) i);
@@ -66,13 +66,14 @@ public class MsgEchoPeerHandler extends
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList messages) throws Exception {
-        MessageList msgs = messages.cast();
+    public void messageReceived0(ChannelHandlerContext ctx, UdtMessage message) throws Exception {
+        meter.mark(message.content().readableBytes());
 
-        for (int i = 0; i < msgs.size(); i++) {
-            UdtMessage message = msgs.get(i);
-            meter.mark(message.content().readableBytes());
-        }
-        ctx.write(msgs);
+        ctx.write(message);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 }
diff --git a/example/src/main/java/io/netty/example/udt/echo/rendezvousBytes/ByteEchoPeerHandler.java b/example/src/main/java/io/netty/example/udt/echo/rendezvousBytes/ByteEchoPeerHandler.java
index 3def6e7e0d..f85d79d3e0 100644
--- a/example/src/main/java/io/netty/example/udt/echo/rendezvousBytes/ByteEchoPeerHandler.java
+++ b/example/src/main/java/io/netty/example/udt/echo/rendezvousBytes/ByteEchoPeerHandler.java
@@ -20,8 +20,7 @@ import com.yammer.metrics.core.Meter;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
+import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.channel.udt.nio.NioUdtProvider;
 
 import java.util.concurrent.TimeUnit;
@@ -33,7 +32,7 @@ import java.util.logging.Logger;
  * traffic between the echo client and server by sending the first message to
  * the server on activation.
  */
-public class ByteEchoPeerHandler extends ChannelInboundHandlerAdapter {
+public class ByteEchoPeerHandler extends SimpleChannelInboundHandler {
     private static final Logger log = Logger.getLogger(ByteEchoPeerHandler.class.getName());
     private final ByteBuf message;
 
@@ -41,6 +40,7 @@ public class ByteEchoPeerHandler extends ChannelInboundHandlerAdapter {
             "bytes", TimeUnit.SECONDS);
 
     public ByteEchoPeerHandler(final int messageSize) {
+        super(false);
         message = Unpooled.buffer(messageSize);
         for (int i = 0; i < message.capacity(); i++) {
             message.writeByte((byte) i);
@@ -60,13 +60,14 @@ public class ByteEchoPeerHandler extends ChannelInboundHandlerAdapter {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        MessageList buffers = msgs.cast();
+    public void messageReceived0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
+        meter.mark(buf.readableBytes());
 
-        for (int i = 0; i < buffers.size(); i++) {
-            ByteBuf buf = buffers.get(i);
-            meter.mark(buf.readableBytes());
-        }
-        ctx.write(buffers);
+        ctx.write(buf);
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 }
diff --git a/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java b/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java
index ca36e1502b..e55246b7be 100644
--- a/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java
+++ b/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java
@@ -18,9 +18,8 @@ package io.netty.example.uptime;
 import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
 import io.netty.channel.EventLoop;
-import io.netty.channel.MessageList;
+import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.handler.timeout.IdleState;
 import io.netty.handler.timeout.IdleStateEvent;
 
@@ -32,7 +31,7 @@ import java.util.concurrent.TimeUnit;
  * connection attempt getStatus.
  */
 @Sharable
-public class UptimeClientHandler extends ChannelInboundHandlerAdapter {
+public class UptimeClientHandler extends SimpleChannelInboundHandler {
 
     private final UptimeClient client;
     private long startTime = -1;
@@ -50,9 +49,8 @@ public class UptimeClientHandler extends ChannelInboundHandlerAdapter {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
+    public void messageReceived0(ChannelHandlerContext ctx, Object msg) throws Exception {
         // Discard received data
-        msgs.releaseAllAndRecycle();
     }
 
     @Override
diff --git a/example/src/main/java/io/netty/example/worldclock/WorldClockClientHandler.java b/example/src/main/java/io/netty/example/worldclock/WorldClockClientHandler.java
index 6bbcb0f253..391e4a85ef 100644
--- a/example/src/main/java/io/netty/example/worldclock/WorldClockClientHandler.java
+++ b/example/src/main/java/io/netty/example/worldclock/WorldClockClientHandler.java
@@ -17,8 +17,7 @@ package io.netty.example.worldclock;
 
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
+import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.example.worldclock.WorldClockProtocol.Continent;
 import io.netty.example.worldclock.WorldClockProtocol.LocalTime;
 import io.netty.example.worldclock.WorldClockProtocol.LocalTimes;
@@ -35,7 +34,7 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.regex.Pattern;
 
-public class WorldClockClientHandler extends ChannelInboundHandlerAdapter {
+public class WorldClockClientHandler extends SimpleChannelInboundHandler {
 
     private static final Logger logger = Logger.getLogger(
             WorldClockClientHandler.class.getName());
@@ -46,6 +45,10 @@ public class WorldClockClientHandler extends ChannelInboundHandlerAdapter {
     private volatile Channel channel;
     private final BlockingQueue answer = new LinkedBlockingQueue();
 
+    public WorldClockClientHandler() {
+        super(false);
+    }
+
     public List getLocalTimes(Collection cities) {
         Locations.Builder builder = Locations.newBuilder();
 
@@ -96,11 +99,8 @@ public class WorldClockClientHandler extends ChannelInboundHandlerAdapter {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        for (int i = 0; i < msgs.size(); i++) {
-            answer.add((LocalTimes) msgs.get(i));
-        }
-        msgs.recycle();
+    public void messageReceived0(ChannelHandlerContext ctx, LocalTimes times) throws Exception {
+        answer.add(times);
     }
 
     @Override
diff --git a/example/src/main/java/io/netty/example/worldclock/WorldClockServerHandler.java b/example/src/main/java/io/netty/example/worldclock/WorldClockServerHandler.java
index d90e28db81..c8c292d482 100644
--- a/example/src/main/java/io/netty/example/worldclock/WorldClockServerHandler.java
+++ b/example/src/main/java/io/netty/example/worldclock/WorldClockServerHandler.java
@@ -16,8 +16,7 @@
 package io.netty.example.worldclock;
 
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.MessageList;
+import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.example.worldclock.WorldClockProtocol.Continent;
 import io.netty.example.worldclock.WorldClockProtocol.DayOfWeek;
 import io.netty.example.worldclock.WorldClockProtocol.LocalTime;
@@ -32,41 +31,38 @@ import java.util.logging.Logger;
 
 import static java.util.Calendar.*;
 
-public class WorldClockServerHandler extends ChannelInboundHandlerAdapter {
+public class WorldClockServerHandler extends SimpleChannelInboundHandler {
 
     private static final Logger logger = Logger.getLogger(
             WorldClockServerHandler.class.getName());
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        int size = msgs.size();
-        MessageList out = MessageList.newInstance(size);
-        for (int i = 0; i < size; i++) {
-            Locations locations = (Locations) msgs.get(i);
-            long currentTime = System.currentTimeMillis();
+    public void messageReceived0(ChannelHandlerContext ctx, Locations locations) throws Exception {
+        long currentTime = System.currentTimeMillis();
 
-            LocalTimes.Builder builder = LocalTimes.newBuilder();
-            for (Location l: locations.getLocationList()) {
-                TimeZone tz = TimeZone.getTimeZone(
-                        toString(l.getContinent()) + '/' + l.getCity());
-                Calendar calendar = getInstance(tz);
-                calendar.setTimeInMillis(currentTime);
+        LocalTimes.Builder builder = LocalTimes.newBuilder();
+        for (Location l: locations.getLocationList()) {
+            TimeZone tz = TimeZone.getTimeZone(
+                    toString(l.getContinent()) + '/' + l.getCity());
+            Calendar calendar = getInstance(tz);
+            calendar.setTimeInMillis(currentTime);
 
-                builder.addLocalTime(LocalTime.newBuilder().
-                        setYear(calendar.get(YEAR)).
-                        setMonth(calendar.get(MONTH) + 1).
-                        setDayOfMonth(calendar.get(DAY_OF_MONTH)).
-                        setDayOfWeek(DayOfWeek.valueOf(calendar.get(DAY_OF_WEEK))).
-                        setHour(calendar.get(HOUR_OF_DAY)).
-                        setMinute(calendar.get(MINUTE)).
-                        setSecond(calendar.get(SECOND)).build());
-            }
-
-            out.add(builder.build());
+            builder.addLocalTime(LocalTime.newBuilder().
+                    setYear(calendar.get(YEAR)).
+                    setMonth(calendar.get(MONTH) + 1).
+                    setDayOfMonth(calendar.get(DAY_OF_MONTH)).
+                    setDayOfWeek(DayOfWeek.valueOf(calendar.get(DAY_OF_WEEK))).
+                    setHour(calendar.get(HOUR_OF_DAY)).
+                    setMinute(calendar.get(MINUTE)).
+                    setSecond(calendar.get(SECOND)).build());
         }
 
-        msgs.recycle();
-        ctx.write(out);
+        ctx.write(builder.build());
+    }
+
+    @Override
+    public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception {
+        ctx.flush();
     }
 
     @Override
diff --git a/handler/src/main/java/io/netty/handler/logging/LoggingHandler.java b/handler/src/main/java/io/netty/handler/logging/LoggingHandler.java
index 37cf8c203c..b5c28a5c96 100644
--- a/handler/src/main/java/io/netty/handler/logging/LoggingHandler.java
+++ b/handler/src/main/java/io/netty/handler/logging/LoggingHandler.java
@@ -21,7 +21,6 @@ import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPromise;
-import io.netty.channel.MessageList;
 import io.netty.util.internal.logging.InternalLogLevel;
 import io.netty.util.internal.logging.InternalLogger;
 import io.netty.util.internal.logging.InternalLoggerFactory;
@@ -292,49 +291,45 @@ public class LoggingHandler extends ChannelDuplexHandler {
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception {
-        logMessages(ctx, "RECEIVED", msgs);
-        ctx.fireMessageReceived(msgs);
+    public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
+        logMessage(ctx, "RECEIVED", msg);
+        ctx.fireMessageReceived(msg);
     }
 
     @Override
-    public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception {
-        logMessages(ctx, "WRITE", msgs);
-        ctx.write(msgs, promise);
+    public void write(ChannelHandlerContext ctx, Object msg) throws Exception {
+        logMessage(ctx, "WRITE", msg);
+        ctx.write(msg);
     }
 
-    private void logMessages(ChannelHandlerContext ctx, String eventName, MessageList msgs) {
+    @Override
+    public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
         if (logger.isEnabled(internalLevel)) {
-            int size = msgs.size();
-            if (size == 0) {
-                logger.log(internalLevel, format(ctx, formatEmptyMessageList(eventName)));
-            } else {
-                for (int i = 0; i < size; i ++) {
-                    logger.log(internalLevel, format(ctx, formatMessage(eventName, i + 1, size, msgs.get(i))));
-                }
-            }
+            logger.log(internalLevel, format(ctx, "FLUSH"));
+        }
+        ctx.flush(promise);
+    }
+
+    private void logMessage(ChannelHandlerContext ctx, String eventName, Object msg) {
+        if (logger.isEnabled(internalLevel)) {
+            logger.log(internalLevel, format(ctx, formatMessage(eventName, msg)));
         }
     }
 
-    protected String formatEmptyMessageList(String eventName) {
-        return eventName + "(empty)";
-    }
-
-    protected String formatMessage(String eventName, int seq, int size, Object msg) {
+    protected String formatMessage(String eventName, Object msg) {
         if (msg instanceof ByteBuf) {
-            return formatByteBuf(eventName, seq, size, (ByteBuf) msg);
+            return formatByteBuf(eventName, (ByteBuf) msg);
         } else {
-            return formatNonByteBuf(eventName, seq, size, msg);
+            return formatNonByteBuf(eventName, msg);
         }
     }
 
-    protected String formatByteBuf(String eventName, int seq, int size, ByteBuf buf) {
+    protected String formatByteBuf(String eventName, ByteBuf buf) {
         int length = buf.readableBytes();
         int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4;
         StringBuilder dump = new StringBuilder(rows * 80 + eventName.length() + 16);
 
-        dump.append(eventName).append('(').append(seq).append('/').append(size).append(", ");
-        dump.append(length).append('B').append(')');
+        dump.append(eventName).append('(').append(length).append('B').append(')');
         dump.append(
                 NEWLINE + "         +-------------------------------------------------+" +
                         NEWLINE + "         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |" +
@@ -380,7 +375,7 @@ public class LoggingHandler extends ChannelDuplexHandler {
         return dump.toString();
     }
 
-    protected String formatNonByteBuf(String eventName, int seq, int size, Object msg) {
-        return eventName + '(' + seq + '/' + size + "): " + msg;
+    protected String formatNonByteBuf(String eventName, Object msg) {
+        return eventName + ": " + msg;
     }
 }
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 ee8ae7554a..6b07160884 100644
--- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java
+++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java
@@ -27,7 +27,7 @@ import io.netty.channel.ChannelInboundHandler;
 import io.netty.channel.ChannelOutboundHandler;
 import io.netty.channel.ChannelPipeline;
 import io.netty.channel.ChannelPromise;
-import io.netty.channel.MessageList;
+import io.netty.channel.ChannelPromiseNotifier;
 import io.netty.handler.codec.ByteToMessageDecoder;
 import io.netty.util.concurrent.DefaultPromise;
 import io.netty.util.concurrent.EventExecutor;
@@ -50,7 +50,9 @@ import java.nio.channels.ClosedChannelException;
 import java.nio.channels.DatagramChannel;
 import java.nio.channels.SocketChannel;
 import java.util.ArrayDeque;
-import java.util.Queue;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
@@ -154,7 +156,7 @@ import java.util.regex.Pattern;
 public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundHandler {
 
     private static final InternalLogger logger =
-        InternalLoggerFactory.getInstance(SslHandler.class);
+            InternalLoggerFactory.getInstance(SslHandler.class);
 
     private static final Pattern IGNORABLE_CLASS_IN_STACK = Pattern.compile(
             "^.*(?:Socket|Datagram|Sctp|Udt)Channel.*$");
@@ -181,7 +183,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
     private final LazyChannelPromise handshakePromise = new LazyChannelPromise();
     private final LazyChannelPromise sslCloseFuture = new LazyChannelPromise();
     private final CloseNotifyListener closeNotifyWriteListener = new CloseNotifyListener();
-    private final Queue pendingUnencryptedWrites = new ArrayDeque();
+    private final Deque pendingUnencryptedWrites = new ArrayDeque();
 
     private int packetLength;
     private ByteBuf decodeOut;
@@ -320,7 +322,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
                 engine.closeOutbound();
                 future.addListener(closeNotifyWriteListener);
                 try {
-                    write(ctx, MessageList.newInstance(Unpooled.EMPTY_BUFFER), future);
+                    flush(ctx, future);
                 } catch (Exception e) {
                     if (!future.tryFailure(e)) {
                         logger.warn("flush() raised a masked exception.", e);
@@ -377,13 +379,13 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
 
     @Override
     public void disconnect(final ChannelHandlerContext ctx,
-            final ChannelPromise promise) throws Exception {
+                           final ChannelPromise promise) throws Exception {
         closeOutboundAndChannel(ctx, promise, true);
     }
 
     @Override
     public void close(final ChannelHandlerContext ctx,
-            final ChannelPromise promise) throws Exception {
+                      final ChannelPromise promise) throws Exception {
         closeOutboundAndChannel(ctx, promise, false);
     }
 
@@ -393,37 +395,39 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
     }
 
     @Override
-    public void write(final ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise)
-            throws Exception {
+    public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
         // Do not encrypt the first write request if this handler is
         // created with startTLS flag turned on.
         if (startTls && !sentFirstMessage) {
             sentFirstMessage = true;
-            ctx.write(msgs, promise);
+            for (;;) {
+                PendingWrite pendingWrite = pendingUnencryptedWrites.poll();
+                if (pendingWrite == null) {
+                    break;
+                }
+                ctx.write(pendingWrite.buf);
+                assert pendingWrite.promise == null;
+            }
+            ctx.flush(promise);
             return;
         }
-        try {
-            if (msgs.isEmpty()) {
-                // if the MessageList is empty we need to add an empty buffer as pending write as otherwise
-                // the promise will never be notified.
-                // See https://github.com/netty/netty/issues/1475
-                pendingUnencryptedWrites.add(new PendingWrite(Unpooled.EMPTY_BUFFER, promise));
+        if (pendingUnencryptedWrites.isEmpty()) {
+            pendingUnencryptedWrites.add(new PendingWrite(Unpooled.EMPTY_BUFFER, promise));
+        } else {
+            PendingWrite write = pendingUnencryptedWrites.peekLast();
+            if (write.promise == null) {
+                write.promise = promise;
             } else {
-                for (int i = 0; i < msgs.size(); i++) {
-                    ByteBuf msg = (ByteBuf) msgs.get(i);
-                    ChannelPromise cp;
-                    if (i + 1 == msgs.size()) {
-                        cp = promise;
-                    } else {
-                        cp = ctx.newPromise();
-                    }
-                    pendingUnencryptedWrites.add(new PendingWrite(msg, cp));
-                }
+                write.promise.addListener(new ChannelPromiseNotifier(promise));
             }
-            flush0(ctx);
-        } finally {
-            msgs.recycle();
         }
+        flush0(ctx);
+    }
+
+    @Override
+    public void write(final ChannelHandlerContext ctx, Object msg)
+            throws Exception {
+        pendingUnencryptedWrites.add(new PendingWrite((ByteBuf) msg));
     }
 
     private void flush0(ChannelHandlerContext ctx) throws SSLException {
@@ -432,13 +436,13 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
         ChannelPromise promise = null;
         try {
             for (;;) {
-                if (out == null) {
-                    out = ctx.alloc().buffer();
-                }
                 PendingWrite pending = pendingUnencryptedWrites.peek();
                 if (pending == null) {
                     break;
                 }
+                if (out == null) {
+                    out = ctx.alloc().buffer();
+                }
                 SSLEngineResult result = wrap(engine, pending.buf, out);
 
                 if (!pending.buf.isReadable()) {
@@ -456,7 +460,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
                     for (;;) {
                         PendingWrite w = pendingUnencryptedWrites.poll();
                         if (w == null) {
-                           break;
+                            break;
                         }
                         failed = true;
                         w.fail(SSLENGINE_CLOSED);
@@ -468,11 +472,13 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
                 } else {
                     switch (result.getHandshakeStatus()) {
                         case NEED_WRAP:
+                            ctx.write(out);
+
                             if (promise != null) {
-                                ctx.write(out, promise);
+                                ctx.flush(promise);
                                 promise = null;
                             } else {
-                                ctx.write(out);
+                                ctx.flush();
                             }
                             out = ctx.alloc().buffer();
                             continue;
@@ -512,10 +518,12 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
             throw e;
         } finally {
             if (out != null && out.isReadable()) {
+                ctx.write(out);
+
                 if (promise != null) {
-                    ctx.write(out, promise);
+                    ctx.flush(promise);
                 } else {
-                    ctx.write(out);
+                    ctx.flush();
                 }
                 out = null;
             } else if (promise != null) {
@@ -528,13 +536,12 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
     }
 
     private void unwrapLater(ChannelHandlerContext ctx) throws SSLException {
-            MessageList messageList = MessageList.newInstance();
-            decode(ctx, internalBuffer(),  messageList);
-            if (messageList.isEmpty()) {
-                messageList.recycle();
-            } else {
-                ctx.fireMessageReceived(messageList);
-            }
+        // TODO: Optimize this for less Garbage
+        List messageList = new ArrayList();
+        decode(ctx, internalBuffer(),  messageList);
+        for (int i = 0; i < messageList.size(); i++) {
+            ctx.fireMessageReceived(messageList.get(i));
+        }
     }
 
     private void flushNonAppData0(ChannelHandlerContext ctx) throws SSLException {
@@ -548,7 +555,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
                 SSLEngineResult result = wrap(engine, Unpooled.EMPTY_BUFFER, out);
 
                 if (result.bytesProduced() > 0) {
-                    ctx.write(out);
+                    ctx.writeAndFlush(out);
                     out = null;
                 }
 
@@ -630,7 +637,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
             if (logger.isDebugEnabled()) {
                 logger.debug(
                         "Swallowing a harmless 'connection reset by peer / broken pipe' error that occurred " +
-                        "while writing close_notify in response to the peer's close_notify", cause);
+                                "while writing close_notify in response to the peer's close_notify", cause);
             }
 
             // Close the connection explicitly just in case the transport
@@ -748,15 +755,15 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
         // SSLv3 or TLS - Check ContentType
         boolean tls;
         switch (buffer.getUnsignedByte(first)) {
-        case 20:  // change_cipher_spec
-        case 21:  // alert
-        case 22:  // handshake
-        case 23:  // application_data
-            tls = true;
-            break;
-        default:
-            // SSLv2 or bad data
-            tls = false;
+            case 20:  // change_cipher_spec
+            case 21:  // alert
+            case 22:  // handshake
+            case 23:  // application_data
+                tls = true;
+                break;
+            default:
+                // SSLv2 or bad data
+                tls = false;
         }
 
         if (tls) {
@@ -802,7 +809,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
     }
 
     @Override
-    protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws SSLException {
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws SSLException {
         int packetLength = this.packetLength;
         if (packetLength == 0) {
             // the previous packet was consumed so try to read the length of the next packet
@@ -842,7 +849,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
         }
     }
 
-    private void unwrap(ChannelHandlerContext ctx, ByteBuffer packet, MessageList out) throws SSLException {
+    private void unwrap(ChannelHandlerContext ctx, ByteBuffer packet, List out) throws SSLException {
         boolean wrapLater = false;
         int bytesProduced = 0;
         try {
@@ -908,11 +915,11 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
             SSLEngineResult result = engine.unwrap(in, out0);
             out.writerIndex(out.writerIndex() + result.bytesProduced());
             switch (result.getStatus()) {
-            case BUFFER_OVERFLOW:
-                out.ensureWritable(engine.getSession().getApplicationBufferSize());
-                break;
-            default:
-                return result;
+                case BUFFER_OVERFLOW:
+                    out.ensureWritable(engine.getSession().getApplicationBufferSize());
+                    break;
+                default:
+                    return result;
             }
         }
     }
@@ -993,7 +1000,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
         engine.closeOutbound();
 
         ChannelPromise closeNotifyFuture = ctx.newPromise().addListener(closeNotifyWriteListener);
-        write(ctx, MessageList.newInstance(Unpooled.EMPTY_BUFFER), closeNotifyFuture);
+        flush(ctx, closeNotifyFuture);
         safeClose(ctx, closeNotifyFuture, promise);
     }
 
@@ -1080,7 +1087,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
                 public void run() {
                     logger.warn(
                             ctx.channel() + " last write attempt timed out." +
-                                            " Force-closing the connection.");
+                                    " Force-closing the connection.");
                     ctx.close(promise);
                 }
             }, closeNotifyTimeoutMillis, TimeUnit.MILLISECONDS);
@@ -1130,15 +1137,22 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
 
     private static final class PendingWrite {
         final ByteBuf buf;
-        final ChannelPromise promise;
+        ChannelPromise promise;
+
         PendingWrite(ByteBuf buf, ChannelPromise promise) {
             this.buf = buf;
             this.promise = promise;
         }
 
+        PendingWrite(ByteBuf buf) {
+            this(buf, null);
+        }
+
         void fail(Throwable cause) {
             buf.release();
-            promise.setFailure(cause);
+            if (promise != null) {
+                promise.setFailure(cause);
+            }
         }
     }
 }
diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedByteInput.java b/handler/src/main/java/io/netty/handler/stream/ChunkedByteInput.java
deleted file mode 100644
index 68f03e5b42..0000000000
--- a/handler/src/main/java/io/netty/handler/stream/ChunkedByteInput.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.stream;
-
-import io.netty.buffer.ByteBuf;
-
-
-/**
- * {@link ChunkedInput} which read its chunks and transfer them to a {@link ByteBuf}
- *
- */
-public interface ChunkedByteInput extends ChunkedInput {
-
-}
diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedFile.java b/handler/src/main/java/io/netty/handler/stream/ChunkedFile.java
index 340c881dfb..8d48c22efe 100644
--- a/handler/src/main/java/io/netty/handler/stream/ChunkedFile.java
+++ b/handler/src/main/java/io/netty/handler/stream/ChunkedFile.java
@@ -16,6 +16,8 @@
 package io.netty.handler.stream;
 
 import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.FileRegion;
 
 import java.io.File;
@@ -23,13 +25,13 @@ import java.io.IOException;
 import java.io.RandomAccessFile;
 
 /**
- * A {@link ChunkedByteInput} that fetches data from a file chunk by chunk.
+ * A {@link ChunkedInput} that fetches data from a file chunk by chunk.
  * 

* If your operating system supports * zero-copy file transfer * such as {@code sendfile()}, you might want to use {@link FileRegion} instead. */ -public class ChunkedFile implements ChunkedByteInput { +public class ChunkedFile implements ChunkedInput { private final RandomAccessFile file; private final long startOffset; @@ -48,7 +50,7 @@ public class ChunkedFile implements ChunkedByteInput { * Creates a new instance that fetches data from the specified file. * * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ByteBuf)} call + * {@link #readChunk(ChannelHandlerContext)} call */ public ChunkedFile(File file, int chunkSize) throws IOException { this(new RandomAccessFile(file, "r"), chunkSize); @@ -65,7 +67,7 @@ public class ChunkedFile implements ChunkedByteInput { * Creates a new instance that fetches data from the specified file. * * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ByteBuf)} call + * {@link #readChunk(ChannelHandlerContext)} call */ public ChunkedFile(RandomAccessFile file, int chunkSize) throws IOException { this(file, 0, file.length(), chunkSize); @@ -77,7 +79,7 @@ public class ChunkedFile implements ChunkedByteInput { * @param offset the offset of the file where the transfer begins * @param length the number of bytes to transfer * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ByteBuf)} call + * {@link #readChunk(ChannelHandlerContext)} call */ public ChunkedFile(RandomAccessFile file, long offset, long length, int chunkSize) throws IOException { if (file == null) { @@ -137,20 +139,18 @@ public class ChunkedFile implements ChunkedByteInput { } @Override - public boolean readChunk(ByteBuf buffer) throws Exception { + public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { long offset = this.offset; if (offset >= endOffset) { - return false; + return null; } int chunkSize = (int) Math.min(this.chunkSize, endOffset - offset); // Check if the buffer is backed by an byte array. If so we can optimize it a bit an safe a copy - byte[] chunk = new byte[chunkSize]; - file.readFully(chunk); - buffer.writeBytes(chunk); + byte[] array = new byte[chunkSize]; + file.readFully(array); this.offset = offset + chunkSize; - - return true; + return Unpooled.wrappedBuffer(array); } } diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedInput.java b/handler/src/main/java/io/netty/handler/stream/ChunkedInput.java index 075bfc5338..582ad1fc3b 100644 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedInput.java +++ b/handler/src/main/java/io/netty/handler/stream/ChunkedInput.java @@ -16,6 +16,8 @@ package io.netty.handler.stream; +import io.netty.channel.ChannelHandlerContext; + /** * A data stream of indefinite length which is consumed by {@link ChunkedWriteHandler}. */ @@ -33,13 +35,16 @@ public interface ChunkedInput { void close() throws Exception; /** - * Fetches a chunked data from the stream. The chunk is then - * transfered to the given buffer. Once this method returns the last chunk + * Fetches a chunked data from the stream. Once this method returns the last chunk * and thus the stream has reached at its end, any subsequent {@link #isEndOfInput()} * call must return {@code false}. * - * @param buffer read the next chunk and transfer it to the given buffer + * @return the fetched chunk. + * {@code null} if there is no data left in the stream. + * Please note that {@code null} does not necessarily mean that the + * stream has reached at its end. In a slow stream, the next chunk + * might be unavailable just momentarily. */ - boolean readChunk(B buffer) throws Exception; + B readChunk(ChannelHandlerContext ctx) throws Exception; } diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedMessageInput.java b/handler/src/main/java/io/netty/handler/stream/ChunkedMessageInput.java deleted file mode 100644 index 52a534a625..0000000000 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedMessageInput.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.stream; - -import io.netty.channel.MessageList; - -import java.util.Queue; - -/** - * {@link ChunkedInput} which reads its chunks and transfer it to a {@link Queue} - * - */ -public interface ChunkedMessageInput extends ChunkedInput> { - -} diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedNioFile.java b/handler/src/main/java/io/netty/handler/stream/ChunkedNioFile.java index dbe17909e9..6032644c09 100644 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedNioFile.java +++ b/handler/src/main/java/io/netty/handler/stream/ChunkedNioFile.java @@ -16,6 +16,7 @@ package io.netty.handler.stream; import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; import io.netty.channel.FileRegion; import java.io.File; @@ -24,14 +25,14 @@ import java.io.IOException; import java.nio.channels.FileChannel; /** - * A {@link ChunkedByteInput} that fetches data from a file chunk by chunk using + * A {@link ChunkedInput} that fetches data from a file chunk by chunk using * NIO {@link FileChannel}. *

* If your operating system supports * zero-copy file transfer * such as {@code sendfile()}, you might want to use {@link FileRegion} instead. */ -public class ChunkedNioFile implements ChunkedByteInput { +public class ChunkedNioFile implements ChunkedInput { private final FileChannel in; private final long startOffset; @@ -50,7 +51,7 @@ public class ChunkedNioFile implements ChunkedByteInput { * Creates a new instance that fetches data from the specified file. * * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ByteBuf)} call + * {@link #readChunk(ChannelHandlerContext)} call */ public ChunkedNioFile(File in, int chunkSize) throws IOException { this(new FileInputStream(in).getChannel(), chunkSize); @@ -67,7 +68,7 @@ public class ChunkedNioFile implements ChunkedByteInput { * Creates a new instance that fetches data from the specified file. * * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ByteBuf)} call + * {@link #readChunk(ChannelHandlerContext)} call */ public ChunkedNioFile(FileChannel in, int chunkSize) throws IOException { this(in, 0, in.size(), chunkSize); @@ -79,7 +80,7 @@ public class ChunkedNioFile implements ChunkedByteInput { * @param offset the offset of the file where the transfer begins * @param length the number of bytes to transfer * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ByteBuf)} call + * {@link #readChunk(ChannelHandlerContext)} call */ public ChunkedNioFile(FileChannel in, long offset, long length, int chunkSize) throws IOException { @@ -141,26 +142,34 @@ public class ChunkedNioFile implements ChunkedByteInput { } @Override - public boolean readChunk(ByteBuf buffer) throws Exception { + public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { long offset = this.offset; if (offset >= endOffset) { - return false; + return null; } int chunkSize = (int) Math.min(this.chunkSize, endOffset - offset); - int readBytes = 0; - for (;;) { - int localReadBytes = buffer.writeBytes(in, chunkSize - readBytes); - if (localReadBytes < 0) { - break; + ByteBuf buffer = ctx.alloc().buffer(chunkSize); + boolean release = true; + try { + int readBytes = 0; + for (;;) { + int localReadBytes = buffer.writeBytes(in, chunkSize - readBytes); + if (localReadBytes < 0) { + break; + } + readBytes += localReadBytes; + if (readBytes == chunkSize) { + break; + } } - readBytes += localReadBytes; - if (readBytes == chunkSize) { - break; + this.offset += readBytes; + release = false; + return buffer; + } finally { + if (release) { + buffer.release(); } } - this.offset += readBytes; - - return true; } } diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedNioStream.java b/handler/src/main/java/io/netty/handler/stream/ChunkedNioStream.java index 176e7882ea..f6dcc754ba 100644 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedNioStream.java +++ b/handler/src/main/java/io/netty/handler/stream/ChunkedNioStream.java @@ -16,16 +16,17 @@ package io.netty.handler.stream; import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; /** - * A {@link ChunkedByteInput} that fetches data from a {@link ReadableByteChannel} + * A {@link ChunkedInput} that fetches data from a {@link ReadableByteChannel} * chunk by chunk. Please note that the {@link ReadableByteChannel} must * operate in blocking mode. Non-blocking mode channels are not supported. */ -public class ChunkedNioStream implements ChunkedByteInput { +public class ChunkedNioStream implements ChunkedInput { private final ReadableByteChannel in; @@ -48,7 +49,7 @@ public class ChunkedNioStream implements ChunkedByteInput { * Creates a new instance that fetches data from the specified channel. * * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ByteBuf)} call + * {@link #readChunk(ChannelHandlerContext)} call */ public ChunkedNioStream(ReadableByteChannel in, int chunkSize) { if (in == null) { @@ -96,9 +97,9 @@ public class ChunkedNioStream implements ChunkedByteInput { } @Override - public boolean readChunk(ByteBuf buffer) throws Exception { + public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { if (isEndOfInput()) { - return false; + return null; } // buffer cannot be not be empty from there int readBytes = byteBuffer.position(); @@ -114,9 +115,17 @@ public class ChunkedNioStream implements ChunkedByteInput { } } byteBuffer.flip(); - buffer.writeBytes(byteBuffer); - byteBuffer.clear(); - - return true; + boolean release = true; + ByteBuf buffer = ctx.alloc().buffer(byteBuffer.remaining()); + try { + buffer.writeBytes(byteBuffer); + byteBuffer.clear(); + release = false; + return buffer; + } finally { + if (release) { + buffer.release(); + } + } } } diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedStream.java b/handler/src/main/java/io/netty/handler/stream/ChunkedStream.java index 00b4d888e2..e50d4fbc57 100644 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedStream.java +++ b/handler/src/main/java/io/netty/handler/stream/ChunkedStream.java @@ -16,12 +16,13 @@ package io.netty.handler.stream; import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; import java.io.InputStream; import java.io.PushbackInputStream; /** - * A {@link ChunkedByteInput} that fetches data from an {@link InputStream} chunk by + * A {@link ChunkedInput} that fetches data from an {@link InputStream} chunk by * chunk. *

* Please note that the {@link InputStream} instance that feeds data into @@ -30,7 +31,7 @@ import java.io.PushbackInputStream; * Otherwise, {@link ChunkedStream} will generate many too small chunks or * block unnecessarily often. */ -public class ChunkedStream implements ChunkedByteInput { +public class ChunkedStream implements ChunkedInput { static final int DEFAULT_CHUNK_SIZE = 8192; @@ -49,7 +50,7 @@ public class ChunkedStream implements ChunkedByteInput { * Creates a new instance that fetches data from the specified stream. * * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ByteBuf)} call + * {@link #readChunk(ChannelHandlerContext)} call */ public ChunkedStream(InputStream in, int chunkSize) { if (in == null) { @@ -93,9 +94,9 @@ public class ChunkedStream implements ChunkedByteInput { } @Override - public boolean readChunk(ByteBuf buffer) throws Exception { + public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { if (isEndOfInput()) { - return false; + return null; } final int availableBytes = in.available(); @@ -106,8 +107,17 @@ public class ChunkedStream implements ChunkedByteInput { chunkSize = Math.min(this.chunkSize, in.available()); } - // transfer to buffer - offset += buffer.writeBytes(in, chunkSize); - return true; + boolean release = true; + ByteBuf buffer = ctx.alloc().buffer(chunkSize); + try { + // transfer to buffer + offset += buffer.writeBytes(in, chunkSize); + release = false; + return buffer; + } finally { + if (release) { + buffer.release(); + } + } } } 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 03d819e6e9..8fbc65725e 100644 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java +++ b/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java @@ -15,8 +15,6 @@ */ package io.netty.handler.stream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelFuture; @@ -25,7 +23,7 @@ import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; -import io.netty.channel.MessageList; +import io.netty.util.ReferenceCountUtil; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -61,7 +59,7 @@ import java.util.concurrent.atomic.AtomicInteger; * * Some {@link ChunkedInput} generates a chunk on a certain event or timing. * Such {@link ChunkedInput} implementation often returns {@code null} on - * {@link ChunkedInput#readChunk(Object)}, resulting in the indefinitely suspended + * {@link ChunkedInput#readChunk(ChannelHandlerContext)}, resulting in the indefinitely suspended * transfer. To resume the transfer when a new chunk is available, you have to * call {@link #resumeTransfer()}. */ @@ -138,11 +136,12 @@ public class ChunkedWriteHandler } @Override - public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { - for (int i = 0; i < msgs.size(); i++) { - queue.add(msgs.get(i)); - } - msgs.recycle(); + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { + queue.add(msg); + } + + @Override + public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { queue.add(promise); if (isWritable() || !ctx.channel().isActive()) { doFlush(ctx); @@ -208,7 +207,6 @@ public class ChunkedWriteHandler discard(ctx, null); return; } - MessageList messages = MessageList.newInstance(); while (isWritable()) { if (currentEvent == null) { currentEvent = queue.poll(); @@ -221,19 +219,17 @@ public class ChunkedWriteHandler final Object currentEvent = this.currentEvent; if (currentEvent instanceof ChannelPromise) { this.currentEvent = null; - ctx.write(messages, (ChannelPromise) currentEvent); - messages = MessageList.newInstance(); + ctx.flush((ChannelPromise) currentEvent); } else if (currentEvent instanceof ChunkedInput) { - MessageList out = MessageList.newInstance(); final ChunkedInput chunks = (ChunkedInput) currentEvent; - boolean read; boolean endOfInput; boolean suspend; + Object message = null; try { - read = readChunk(ctx, chunks, out); + message = chunks.readChunk(ctx); endOfInput = chunks.isEndOfInput(); - if (!read) { + if (message == null) { // No need to suspend when reached at the end. suspend = !endOfInput; } else { @@ -242,6 +238,10 @@ public class ChunkedWriteHandler } catch (final Throwable t) { this.currentEvent = null; + if (message != null) { + ReferenceCountUtil.release(message); + } + if (ctx.executor().inEventLoop()) { ctx.fireExceptionCaught(t); } else { @@ -265,7 +265,7 @@ public class ChunkedWriteHandler } pendingWrites.incrementAndGet(); - ChannelFuture f = ctx.write(out); + ChannelFuture f = ctx.write(message).flush(); if (endOfInput) { this.currentEvent = null; @@ -316,29 +316,6 @@ public class ChunkedWriteHandler } } - /** - * Read the next {@link ChunkedInput} and transfer it the the outbound buffer. - * @param ctx the {@link ChannelHandlerContext} this handler is bound to - * @param chunks the {@link ChunkedInput} to read from - * @return read {@code true} if something could be transfered to the outbound buffer - * @throws Exception if something goes wrong - */ - @SuppressWarnings("unchecked") - protected boolean readChunk( - @SuppressWarnings("UnusedParameters") ChannelHandlerContext ctx, - ChunkedInput chunks, MessageList out) throws Exception { - if (chunks instanceof ChunkedByteInput) { - ByteBuf buf = Unpooled.buffer(); - boolean done = ((ChunkedByteInput) chunks).readChunk(buf); - out.add(buf); - return done; - } else if (chunks instanceof ChunkedMessageInput) { - return ((ChunkedMessageInput) chunks).readChunk(out); - } else { - throw new IllegalArgumentException("ChunkedInput instance " + chunks + " not supported"); - } - } - static void closeInput(ChunkedInput chunks) { try { chunks.close(); diff --git a/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java b/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java index 0afd0c9c05..4bb9a47384 100644 --- a/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java +++ b/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java @@ -21,14 +21,10 @@ import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOutboundHandler; import io.netty.channel.ChannelPromise; -import io.netty.channel.MessageList; import io.netty.util.concurrent.EventExecutor; -import java.net.SocketAddress; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -98,7 +94,7 @@ import java.util.concurrent.TimeUnit; * @see ReadTimeoutHandler * @see WriteTimeoutHandler */ -public class IdleStateHandler extends ChannelInboundHandlerAdapter implements ChannelOutboundHandler { +public class IdleStateHandler extends ChannelDuplexHandler { private final long readerIdleTimeMillis; private final long writerIdleTimeMillis; @@ -251,19 +247,14 @@ public class IdleStateHandler extends ChannelInboundHandlerAdapter implements Ch } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { lastReadTime = System.currentTimeMillis(); firstReaderIdleEvent = firstAllIdleEvent = true; - ctx.fireMessageReceived(msgs); + ctx.fireMessageReceived(msg); } @Override - public void read(ChannelHandlerContext ctx) { - ctx.read(); - } - - @Override - public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { promise.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { @@ -271,33 +262,7 @@ public class IdleStateHandler extends ChannelInboundHandlerAdapter implements Ch firstWriterIdleEvent = firstAllIdleEvent = true; } }); - ctx.write(msgs, promise); - } - - @Override - public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception { - ctx.bind(localAddress, promise); - } - - @Override - public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, - ChannelPromise promise) throws Exception { - ctx.connect(remoteAddress, localAddress, promise); - } - - @Override - public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - ctx.disconnect(promise); - } - - @Override - public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - ctx.close(promise); - } - - @Override - public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - ctx.deregister(promise); + ctx.flush(promise); } private void initialize(ChannelHandlerContext ctx) { diff --git a/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java b/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java index 0327906107..151c6ff47e 100644 --- a/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java +++ b/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java @@ -21,7 +21,6 @@ import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; -import io.netty.channel.MessageList; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -145,9 +144,9 @@ public class ReadTimeoutHandler extends ChannelInboundHandlerAdapter { } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { lastReadTime = System.currentTimeMillis(); - ctx.fireMessageReceived(msgs); + ctx.fireMessageReceived(msg); } private void initialize(ChannelHandlerContext ctx) { diff --git a/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutHandler.java b/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutHandler.java index c5f85f03be..368cd1e6f5 100644 --- a/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutHandler.java +++ b/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutHandler.java @@ -24,7 +24,6 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; -import io.netty.channel.MessageList; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -102,9 +101,9 @@ public class WriteTimeoutHandler extends ChannelOutboundHandlerAdapter { } @Override - public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { scheduleTimeout(ctx, promise); - super.write(ctx, msgs, promise); + ctx.flush(promise); } private void scheduleTimeout(final ChannelHandlerContext ctx, final ChannelPromise future) { diff --git a/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java b/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java index 991f2547b0..2da0645748 100644 --- a/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java +++ b/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java @@ -18,8 +18,6 @@ package io.netty.handler.traffic; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.channel.MessageList; import io.netty.util.Attribute; import io.netty.util.AttributeKey; @@ -210,19 +208,16 @@ public abstract class AbstractTrafficShapingHandler extends ChannelDuplexHandler } @Override - public void messageReceived(final ChannelHandlerContext ctx, final MessageList msgs) throws Exception { - MessageList buffers = msgs.cast(); - long size = 0; - for (int i = 0; i < buffers.size(); i++) { - size += buffers.get(i).readableBytes(); - } + public void messageReceived(final ChannelHandlerContext ctx, final Object msg) throws Exception { + ByteBuf buf = (ByteBuf) msg; + long size = buf.readableBytes(); long curtime = System.currentTimeMillis(); if (trafficCounter != null) { trafficCounter.bytesRecvFlowControl(size); if (readLimit == 0) { // no action - ctx.fireMessageReceived(msgs); + ctx.fireMessageReceived(msg); return; } @@ -253,7 +248,7 @@ public abstract class AbstractTrafficShapingHandler extends ChannelDuplexHandler Runnable bufferUpdateTask = new Runnable() { @Override public void run() { - ctx.fireMessageReceived(msgs); + ctx.fireMessageReceived(msg); } }; ctx.executor().schedule(bufferUpdateTask, wait, TimeUnit.MILLISECONDS); @@ -261,7 +256,7 @@ public abstract class AbstractTrafficShapingHandler extends ChannelDuplexHandler } } } - ctx.fireMessageReceived(msgs); + ctx.fireMessageReceived(msg); } @Override @@ -280,18 +275,15 @@ public abstract class AbstractTrafficShapingHandler extends ChannelDuplexHandler } @Override - public void write(final ChannelHandlerContext ctx, final MessageList msgs, final ChannelPromise promise) + public void write(final ChannelHandlerContext ctx, final Object msg) throws Exception { long curtime = System.currentTimeMillis(); - long size = 0; - for (int i = 0; i < msgs.size(); i++) { - size += ((ByteBuf) msgs.get(i)).readableBytes(); - } + long size = ((ByteBuf) msg).readableBytes(); if (trafficCounter != null) { trafficCounter.bytesWriteFlowControl(size); if (writeLimit == 0) { - ctx.write(msgs, promise); + ctx.write(msg); return; } // compute the number of ms to wait before continue with the @@ -303,13 +295,13 @@ public abstract class AbstractTrafficShapingHandler extends ChannelDuplexHandler ctx.executor().schedule(new Runnable() { @Override public void run() { - ctx.write(msgs, promise); + ctx.write(msg); } }, wait, TimeUnit.MILLISECONDS); return; } } - ctx.write(msgs, promise); + ctx.write(msg); } /** diff --git a/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java b/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java index 6c5fef1c87..b985a6d398 100644 --- a/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; -import io.netty.channel.MessageList; +import io.netty.channel.ChannelHandlerContext; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.CharsetUtil; import org.junit.Test; @@ -100,7 +100,7 @@ public class ChunkedWriteHandlerTest { public void testListenerNotifiedWhenIsEnd() { ByteBuf buffer = Unpooled.copiedBuffer("Test", CharsetUtil.ISO_8859_1); - ChunkedByteInput input = new ChunkedByteInput() { + ChunkedInput input = new ChunkedInput() { private boolean done; private final ByteBuf buffer = Unpooled.copiedBuffer("Test", CharsetUtil.ISO_8859_1); @@ -115,13 +115,12 @@ public class ChunkedWriteHandlerTest { } @Override - public boolean readChunk(ByteBuf buffer) throws Exception { + public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { if (done) { - return false; + return null; } done = true; - buffer.writeBytes(this.buffer.duplicate()); - return true; + return buffer.duplicate().retain(); } }; @@ -135,7 +134,7 @@ public class ChunkedWriteHandlerTest { }; EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); - ch.write(input).addListener(listener).syncUninterruptibly(); + ch.write(input).flush().addListener(listener).syncUninterruptibly(); ch.checkException(); ch.finish(); @@ -149,7 +148,7 @@ public class ChunkedWriteHandlerTest { @Test public void testChunkedMessageInput() { - ChunkedMessageInput input = new ChunkedMessageInput() { + ChunkedInput input = new ChunkedInput() { private boolean done; @Override @@ -163,18 +162,17 @@ public class ChunkedWriteHandlerTest { } @Override - public boolean readChunk(MessageList buffer) throws Exception { + public Object readChunk(ChannelHandlerContext ctx) throws Exception { if (done) { return false; } done = true; - buffer.add(0); - return true; + return 0; } }; EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); - ch.write(input).syncUninterruptibly(); + ch.write(input).flush().syncUninterruptibly(); ch.checkException(); assertTrue(ch.finish()); diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/sctp/SctpEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/sctp/SctpEchoTest.java index 38ea48762b..5667d75a3c 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/sctp/SctpEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/sctp/SctpEchoTest.java @@ -86,7 +86,7 @@ public class SctpEchoTest extends AbstractSctpTest { 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)); + cc.write(Unpooled.wrappedBuffer(data, i, length)).flush(); i += length; } @@ -149,7 +149,7 @@ public class SctpEchoTest extends AbstractSctpTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, ByteBuf in) throws Exception { byte[] actual = new byte[in.readableBytes()]; in.readBytes(actual); @@ -159,7 +159,7 @@ public class SctpEchoTest extends AbstractSctpTest { } if (channel.parent() != null) { - channel.write(Unpooled.wrappedBuffer(actual)); + channel.write(Unpooled.wrappedBuffer(actual)).flush(); } counter += actual.length; diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramMulticastTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramMulticastTest.java index 5f89840d23..05be78ed3b 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramMulticastTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramMulticastTest.java @@ -19,9 +19,7 @@ import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelOption; -import io.netty.channel.MessageList; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramPacket; @@ -45,11 +43,10 @@ public class DatagramMulticastTest extends AbstractDatagramTest { public void testMulticast(Bootstrap sb, Bootstrap cb) throws Throwable { MulticastTestHandler mhandler = new MulticastTestHandler(); - sb.handler(new ChannelInboundHandlerAdapter() { + sb.handler(new SimpleChannelInboundHandler() { @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, Object msg) throws Exception { // Nothing will be sent. - msgs.releaseAllAndRecycle(); } }); @@ -76,7 +73,7 @@ public class DatagramMulticastTest extends AbstractDatagramTest { cc.joinGroup(groupAddress, NetUtil.LOOPBACK_IF).sync(); - sc.write(new DatagramPacket(Unpooled.copyInt(1), groupAddress)).sync(); + sc.write(new DatagramPacket(Unpooled.copyInt(1), groupAddress)).flush().sync(); assertTrue(mhandler.await()); // leave the group @@ -86,7 +83,7 @@ public class DatagramMulticastTest extends AbstractDatagramTest { Thread.sleep(1000); // we should not receive a message anymore as we left the group before - sc.write(new DatagramPacket(Unpooled.copyInt(1), groupAddress)).sync(); + sc.write(new DatagramPacket(Unpooled.copyInt(1), groupAddress)).flush().sync(); mhandler.await(); sc.close().awaitUninterruptibly(); @@ -100,7 +97,7 @@ public class DatagramMulticastTest extends AbstractDatagramTest { private volatile boolean fail; @Override - protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { + protected void messageReceived0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { if (done) { fail = true; } diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java index 6a9fa3a8a4..7626c61c3e 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java @@ -19,8 +19,6 @@ import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.MessageList; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.DatagramPacket; import org.junit.Test; @@ -42,24 +40,23 @@ public class DatagramUnicastTest extends AbstractDatagramTest { sb.handler(new SimpleChannelInboundHandler() { @Override - public void messageReceived(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { assertEquals(1, msg.content().readInt()); latch.countDown(); } }); - cb.handler(new ChannelInboundHandlerAdapter() { + cb.handler(new SimpleChannelInboundHandler() { @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, Object msgs) throws Exception { // Nothing will be sent. - msgs.releaseAllAndRecycle(); } }); Channel sc = sb.bind().sync().channel(); Channel cc = cb.bind().sync().channel(); - cc.write(new DatagramPacket(Unpooled.copyInt(1), addr)).sync(); + cc.write(new DatagramPacket(Unpooled.copyInt(1), addr)).flush().sync(); assertTrue(latch.await(10, TimeUnit.SECONDS)); sc.close().sync(); diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketBufReleaseTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketBufReleaseTest.java index 3032baed69..5e335f759a 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketBufReleaseTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketBufReleaseTest.java @@ -22,8 +22,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.MessageList; +import io.netty.channel.SimpleChannelInboundHandler; import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.DefaultPromise; import io.netty.util.concurrent.DefaultThreadFactory; @@ -67,7 +66,7 @@ public class SocketBufReleaseTest extends AbstractSocketTest { clientHandler.check(); } - private static class BufWriterHandler extends ChannelInboundHandlerAdapter { + private static class BufWriterHandler extends SimpleChannelInboundHandler { private final Random random = new Random(); private final CountDownLatch latch = new CountDownLatch(1); @@ -87,7 +86,7 @@ public class SocketBufReleaseTest extends AbstractSocketTest { buf = ctx.alloc().buffer(); buf.writeBytes(data); - ctx.channel().write(buf).addListener(new ChannelFutureListener() { + ctx.channel().write(buf).flush().addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { latch.countDown(); @@ -96,9 +95,8 @@ public class SocketBufReleaseTest extends AbstractSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, Object msg) throws Exception { // discard - msgs.releaseAllAndRecycle(); } public void check() throws InterruptedException { diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketEchoTest.java index 9a3ab3ddb5..ff2ab39b7c 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketEchoTest.java @@ -21,6 +21,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.SocketChannel; @@ -115,6 +116,12 @@ public class SocketEchoTest extends AbstractSocketTest { }); } else { sb.childHandler(sh); + sb.handler(new ChannelInboundHandlerAdapter() { + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cause.printStackTrace(); + } + }); cb.handler(ch); } @@ -125,9 +132,9 @@ public class SocketEchoTest extends AbstractSocketTest { int length = Math.min(random.nextInt(1024 * 64), data.length - i); ByteBuf buf = Unpooled.wrappedBuffer(data, i, length); if (voidPromise) { - assertEquals(cc.voidPromise(), cc.write(buf, cc.voidPromise())); + assertEquals(cc.voidPromise(), cc.write(buf).flush(cc.voidPromise())); } else { - assertNotEquals(cc.voidPromise(), cc.write(buf)); + assertNotEquals(cc.voidPromise(), cc.write(buf).flush()); } i += length; } @@ -192,7 +199,7 @@ public class SocketEchoTest extends AbstractSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, ByteBuf in) throws Exception { byte[] actual = new byte[in.readableBytes()]; in.readBytes(actual); @@ -208,10 +215,16 @@ public class SocketEchoTest extends AbstractSocketTest { counter += actual.length; } + @Override + public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception { + ctx.flush(); + } + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (exception.compareAndSet(null, cause)) { + cause.printStackTrace(); ctx.close(); } } diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFileRegionTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFileRegionTest.java index eb619b8824..fb28af7f46 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFileRegionTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFileRegionTest.java @@ -21,10 +21,8 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandler; -import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.DefaultFileRegion; import io.netty.channel.FileRegion; -import io.netty.channel.MessageList; import io.netty.channel.SimpleChannelInboundHandler; import org.junit.Test; @@ -72,10 +70,9 @@ public class SocketFileRegionTest extends AbstractSocketTest { out.write(data); out.close(); - ChannelInboundHandler ch = new ChannelInboundHandlerAdapter() { + ChannelInboundHandler ch = new SimpleChannelInboundHandler() { @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - msgs.releaseAllAndRecycle(); + public void messageReceived0(ChannelHandlerContext ctx, Object msg) throws Exception { } @Override @@ -93,9 +90,9 @@ public class SocketFileRegionTest extends AbstractSocketTest { Channel cc = cb.connect().sync().channel(); FileRegion region = new DefaultFileRegion(new FileInputStream(file).getChannel(), 0L, file.length()); if (voidPromise) { - assertEquals(cc.voidPromise(), cc.write(region, cc.voidPromise())); + assertEquals(cc.voidPromise(), cc.write(region).flush(cc.voidPromise())); } else { - assertNotEquals(cc.voidPromise(), cc.write(region)); + assertNotEquals(cc.voidPromise(), cc.write(region).flush()); } while (sh.counter < data.length) { if (sh.exception.get() != null) { @@ -134,7 +131,7 @@ public class SocketFileRegionTest extends AbstractSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, ByteBuf in) throws Exception { byte[] actual = new byte[in.readableBytes()]; in.readBytes(actual); diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFixedLengthEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFixedLengthEchoTest.java index abc0e38f3d..fb304bfe69 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFixedLengthEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFixedLengthEchoTest.java @@ -71,7 +71,7 @@ public class SocketFixedLengthEchoTest extends AbstractSocketTest { Channel cc = cb.connect().sync().channel(); for (int i = 0; i < data.length;) { int length = Math.min(random.nextInt(1024 * 3), data.length - i); - cc.write(Unpooled.wrappedBuffer(data, i, length)); + cc.write(Unpooled.wrappedBuffer(data, i, length)).flush(); i += length; } @@ -134,7 +134,7 @@ public class SocketFixedLengthEchoTest extends AbstractSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { assertEquals(1024, msg.readableBytes()); byte[] actual = new byte[msg.readableBytes()]; @@ -152,6 +152,11 @@ public class SocketFixedLengthEchoTest extends AbstractSocketTest { counter += actual.length; } + @Override + public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception { + ctx.flush(); + } + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (exception.compareAndSet(null, cause)) { diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketGatheringWriteTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketGatheringWriteTest.java index a607c2196b..29d86ef620 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketGatheringWriteTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketGatheringWriteTest.java @@ -22,7 +22,6 @@ import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.channel.SimpleChannelInboundHandler; import org.junit.Test; @@ -69,8 +68,6 @@ public class SocketGatheringWriteTest extends AbstractSocketTest { Channel sc = sb.bind().sync().channel(); Channel cc = cb.connect().sync().channel(); - MessageList messages = MessageList.newInstance(); - for (int i = 0; i < data.length;) { int length = Math.min(random.nextInt(1024 * 64), data.length - i); ByteBuf buf = Unpooled.wrappedBuffer(data, i, length); @@ -82,13 +79,13 @@ public class SocketGatheringWriteTest extends AbstractSocketTest { ByteBuf buf2 = Unpooled.buffer(size).writeBytes(buf, split, oldIndex - split); CompositeByteBuf comp = Unpooled.compositeBuffer(); comp.addComponent(buf).addComponent(buf2).writerIndex(length); - messages.add(comp); + cc.write(comp); } else { - messages.add(buf); + cc.write(buf); } i += length; } - assertNotEquals(cc.voidPromise(), cc.write(messages).sync()); + assertNotEquals(cc.voidPromise(), cc.flush().sync()); while (sh.counter < data.length) { if (sh.exception.get() != null) { @@ -135,7 +132,7 @@ public class SocketGatheringWriteTest extends AbstractSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, ByteBuf in) throws Exception { counter += in.readableBytes(); received.writeBytes(in); } diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketObjectEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketObjectEchoTest.java index 92cb46eee2..2e9388821b 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketObjectEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketObjectEchoTest.java @@ -21,7 +21,6 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; -import io.netty.channel.MessageList; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; @@ -32,7 +31,7 @@ import java.io.IOException; import java.util.Random; import java.util.concurrent.atomic.AtomicReference; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; public class SocketObjectEchoTest extends AbstractSocketTest { @@ -83,7 +82,7 @@ public class SocketObjectEchoTest extends AbstractSocketTest { Channel sc = sb.bind().sync().channel(); Channel cc = cb.connect().sync().channel(); for (String element : data) { - cc.write(element); + cc.write(element).flush(); } while (ch.counter < data.length) { @@ -139,9 +138,6 @@ public class SocketObjectEchoTest extends AbstractSocketTest { final AtomicReference exception = new AtomicReference(); volatile int counter; - EchoHandler() { - } - @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { @@ -149,18 +145,19 @@ public class SocketObjectEchoTest extends AbstractSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - for (int i = 0; i < msgs.size(); i ++) { - String msg = (String) msgs.get(i); - assertEquals(data[counter], msg); + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + assertEquals(data[counter], msg); - if (channel.parent() != null) { - channel.write(msg); - } - - counter ++; + if (channel.parent() != null) { + channel.write(msg); } - msgs.recycle(); + + counter ++; + } + + @Override + public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception { + ctx.flush(); } @Override diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputByPeerTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputByPeerTest.java index f36385bc4c..0a5969e92a 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputByPeerTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputByPeerTest.java @@ -127,7 +127,7 @@ public class SocketShutdownOutputByPeerTest extends AbstractServerSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { queue.offer(msg.readByte()); } diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputBySelfTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputBySelfTest.java index 282c98b97c..254bf4ceed 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputBySelfTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputBySelfTest.java @@ -48,7 +48,7 @@ public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest { assertFalse(ch.isOutputShutdown()); s = ss.accept(); - ch.write(Unpooled.wrappedBuffer(new byte[] { 1 })).sync(); + ch.write(Unpooled.wrappedBuffer(new byte[] { 1 })).flush().sync(); assertEquals(1, s.getInputStream().read()); assertTrue(h.ch.isOpen()); @@ -86,7 +86,7 @@ public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { queue.offer(msg.readByte()); } } diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSpdyEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSpdyEchoTest.java index 0155e4dab5..ddfd7eeae1 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSpdyEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSpdyEchoTest.java @@ -23,7 +23,6 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; -import io.netty.channel.MessageList; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.spdy.SpdyConstants; @@ -199,7 +198,7 @@ public class SocketSpdyEchoTest extends AbstractSocketTest { int port = ((InetSocketAddress) sc.localAddress()).getPort(); Channel cc = cb.remoteAddress(NetUtil.LOCALHOST, port).connect().sync().channel(); - cc.write(frames); + cc.write(frames).flush(); while (ch.counter < frames.writerIndex() - ignoredBytes) { if (sh.exception.get() != null) { @@ -234,8 +233,13 @@ public class SocketSpdyEchoTest extends AbstractSocketTest { final AtomicReference exception = new AtomicReference(); @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - ctx.write(msgs); + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + ctx.write(msg); + } + + @Override + public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception { + ctx.flush(); } @Override @@ -256,7 +260,7 @@ public class SocketSpdyEchoTest extends AbstractSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, ByteBuf in) throws Exception { byte[] actual = new byte[in.readableBytes()]; in.readBytes(actual); diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java index c113c0938a..4066017abe 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java @@ -101,7 +101,7 @@ public class SocketSslEchoTest extends AbstractSocketTest { Channel sc = sb.bind().sync().channel(); Channel cc = cb.connect().sync().channel(); Future hf = cc.pipeline().get(SslHandler.class).handshakeFuture(); - cc.write(Unpooled.wrappedBuffer(data, 0, FIRST_MESSAGE_SIZE)); + cc.write(Unpooled.wrappedBuffer(data, 0, FIRST_MESSAGE_SIZE)).flush(); final AtomicBoolean firstByteWriteFutureDone = new AtomicBoolean(); hf.sync(); @@ -110,7 +110,7 @@ public class SocketSslEchoTest extends AbstractSocketTest { for (int i = FIRST_MESSAGE_SIZE; i < data.length;) { int length = Math.min(random.nextInt(1024 * 64), data.length - i); - ChannelFuture future = cc.write(Unpooled.wrappedBuffer(data, i, length)); + ChannelFuture future = cc.write(Unpooled.wrappedBuffer(data, i, length)).flush(); future.sync(); i += length; } @@ -180,7 +180,7 @@ public class SocketSslEchoTest extends AbstractSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, ByteBuf in) throws Exception { byte[] actual = new byte[in.readableBytes()]; in.readBytes(actual); @@ -196,6 +196,11 @@ public class SocketSslEchoTest extends AbstractSocketTest { counter += actual.length; } + @Override + public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception { + ctx.flush(); + } + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStartTlsTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStartTlsTest.java index 090d52b48b..f683ce37c3 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStartTlsTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStartTlsTest.java @@ -155,15 +155,15 @@ public class SocketStartTlsTest extends AbstractSocketTest { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.write("StartTlsRequest\n"); + ctx.writeAndFlush("StartTlsRequest\n"); } @Override - public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, String msg) throws Exception { if ("StartTlsResponse".equals(msg)) { ctx.pipeline().addAfter("logger", "ssl", sslHandler); handshakeFuture = sslHandler.handshakeFuture(); - ctx.write("EncryptedRequest\n"); + ctx.writeAndFlush("EncryptedRequest\n"); return; } @@ -201,15 +201,15 @@ public class SocketStartTlsTest extends AbstractSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, String msg) throws Exception { if ("StartTlsRequest".equals(msg)) { ctx.pipeline().addAfter("logger", "ssl", sslHandler); - ctx.write("StartTlsResponse\n"); + ctx.writeAndFlush("StartTlsResponse\n"); return; } assertEquals("EncryptedRequest", msg); - ctx.write("EncryptedResponse\n"); + ctx.writeAndFlush("EncryptedResponse\n"); } @Override diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStringEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStringEchoTest.java index cd7d39decc..d02bbc9860 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStringEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStringEchoTest.java @@ -85,7 +85,7 @@ public class SocketStringEchoTest extends AbstractSocketTest { Channel cc = cb.connect().sync().channel(); for (String element : data) { String delimiter = random.nextBoolean() ? "\r\n" : "\n"; - cc.write(element + delimiter); + cc.write(element + delimiter).flush(); } while (ch.counter < data.length) { @@ -146,7 +146,7 @@ public class SocketStringEchoTest extends AbstractSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, String msg) throws Exception { assertEquals(data[counter], msg); if (channel.parent() != null) { @@ -157,6 +157,11 @@ public class SocketStringEchoTest extends AbstractSocketTest { counter ++; } + @Override + public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception { + ctx.flush(); + } + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (exception.compareAndSet(null, cause)) { diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/WriteBeforeRegisteredTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/WriteBeforeRegisteredTest.java index ac553d5242..ced8330268 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/WriteBeforeRegisteredTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/WriteBeforeRegisteredTest.java @@ -34,7 +34,7 @@ public class WriteBeforeRegisteredTest extends AbstractClientSocketTest { SocketChannel ch = null; try { ch = (SocketChannel) cb.handler(h).connect().channel(); - ch.write(Unpooled.wrappedBuffer(new byte[] { 1 })).await(); + ch.write(Unpooled.wrappedBuffer(new byte[] { 1 })).flush(); } finally { if (ch != null) { ch.close(); diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/udt/UDTClientServerConnectionTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/udt/UDTClientServerConnectionTest.java index 19733edc85..dc508d899e 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/udt/UDTClientServerConnectionTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/udt/UDTClientServerConnectionTest.java @@ -19,10 +19,8 @@ import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; -import io.netty.channel.MessageList; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; @@ -169,7 +167,7 @@ public class UDTClientServerConnectionTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + public void messageReceived0(ChannelHandlerContext ctx, Object msg) throws Exception { log.info("Client received: " + msg); } } @@ -284,7 +282,7 @@ public class UDTClientServerConnectionTest { } static class ServerHandler extends - ChannelInboundHandlerAdapter { + SimpleChannelInboundHandler { static final Logger log = LoggerFactory.getLogger(ServerHandler.class); @@ -322,11 +320,8 @@ public class UDTClientServerConnectionTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - for (int i = 0; i < msgs.size(); i ++) { - log.info("Server received: " + msgs.get(i)); - } - msgs.releaseAllAndRecycle(); + public void messageReceived0(ChannelHandlerContext ctx, Object msg) throws Exception { + log.info("Server received: " + msg); } } static final Logger log = LoggerFactory diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpChannel.java index d3b5bcbae3..d29737fc5c 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpChannel.java @@ -25,7 +25,6 @@ import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; -import io.netty.channel.MessageList; import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.nio.AbstractNioMessageChannel; import io.netty.channel.sctp.DefaultSctpChannelConfig; @@ -47,6 +46,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; /** @@ -259,7 +259,7 @@ public class NioSctpChannel extends AbstractNioMessageChannel implements io.nett } @Override - protected int doReadMessages(MessageList buf) throws Exception { + protected int doReadMessages(List buf) throws Exception { SctpChannel ch = javaChannel(); RecvByteBufAllocator.Handle allocHandle = this.allocHandle; @@ -292,8 +292,8 @@ public class NioSctpChannel extends AbstractNioMessageChannel implements io.nett } @Override - protected int doWriteMessages(MessageList msgs, int index, boolean lastSpin) throws Exception { - SctpMessage packet = (SctpMessage) msgs.get(index); + protected int doWriteMessages(Object[] msgs, int msgLength, int startIndex, boolean lastSpin) throws Exception { + SctpMessage packet = (SctpMessage) msgs[startIndex]; ByteBuf data = packet.content(); int dataLen = data.readableBytes(); ByteBuffer nioData; @@ -330,7 +330,7 @@ public class NioSctpChannel extends AbstractNioMessageChannel implements io.nett // packet was written free up buffer packet.release(); - if (index + 1 == msgs.size()) { + if (msgLength == 1) { // Wrote the outbound buffer completely - clear OP_WRITE. if ((interestOps & SelectionKey.OP_WRITE) != 0) { key.interestOps(interestOps & ~SelectionKey.OP_WRITE); diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpServerChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpServerChannel.java index 7d7ae91a12..a14ce71553 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpServerChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpServerChannel.java @@ -21,7 +21,6 @@ import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; -import io.netty.channel.MessageList; import io.netty.channel.nio.AbstractNioMessageChannel; import io.netty.channel.sctp.DefaultSctpServerChannelConfig; import io.netty.channel.sctp.SctpServerChannelConfig; @@ -34,6 +33,7 @@ import java.nio.channels.SelectionKey; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; /** @@ -134,7 +134,7 @@ public class NioSctpServerChannel extends AbstractNioMessageChannel } @Override - protected int doReadMessages(MessageList buf) throws Exception { + protected int doReadMessages(List buf) throws Exception { SctpChannel ch = javaChannel().accept(); if (ch == null) { return 0; @@ -216,7 +216,7 @@ public class NioSctpServerChannel extends AbstractNioMessageChannel } @Override - protected int doWriteMessages(MessageList msgs, int index, boolean lastSpin) throws Exception { + protected int doWriteMessages(Object[] msgs, int msgLength, int startIndex, boolean lastSpin) throws Exception { throw new UnsupportedOperationException(); } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java index de0bfd4e82..f3005357b5 100755 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java @@ -25,7 +25,6 @@ import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; -import io.netty.channel.MessageList; import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.oio.AbstractOioMessageChannel; import io.netty.channel.sctp.DefaultSctpChannelConfig; @@ -47,6 +46,7 @@ import java.nio.channels.Selector; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; /** @@ -167,7 +167,7 @@ public class OioSctpChannel extends AbstractOioMessageChannel } @Override - protected int doReadMessages(MessageList buf) throws Exception { + protected int doReadMessages(List msgs) throws Exception { if (!readSelector.isOpen()) { return 0; } @@ -199,7 +199,7 @@ public class OioSctpChannel extends AbstractOioMessageChannel } data.flip(); - buf.add(new SctpMessage(messageInfo, buffer.writerIndex(buffer.writerIndex() + data.remaining()))); + msgs.add(new SctpMessage(messageInfo, buffer.writerIndex(buffer.writerIndex() + data.remaining()))); free = false; readMessages ++; } catch (Throwable cause) { @@ -215,12 +215,11 @@ public class OioSctpChannel extends AbstractOioMessageChannel } finally { reableKeys.clear(); } - return readMessages; } @Override - protected int doWrite(MessageList msgs, int index) throws Exception { + protected int doWrite(Object[] msgs, int length, int startIndex) throws Exception { if (!writeSelector.isOpen()) { return 0; } @@ -232,8 +231,6 @@ public class OioSctpChannel extends AbstractOioMessageChannel } Iterator writableKeysIt = writableKeys.iterator(); int written = 0; - int length = msgs.size() - index; - for (;;) { if (written == length) { // all written @@ -242,7 +239,7 @@ public class OioSctpChannel extends AbstractOioMessageChannel writableKeysIt.next(); writableKeysIt.remove(); - SctpMessage packet = (SctpMessage) msgs.get(index ++); + SctpMessage packet = (SctpMessage) msgs[startIndex ++]; if (packet == null) { return written; } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpServerChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpServerChannel.java index f8f876fdfa..a2631bdd33 100755 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpServerChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpServerChannel.java @@ -21,11 +21,9 @@ import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; -import io.netty.channel.MessageList; import io.netty.channel.oio.AbstractOioMessageChannel; import io.netty.channel.sctp.DefaultSctpServerChannelConfig; import io.netty.channel.sctp.SctpServerChannelConfig; -import io.netty.util.ReferenceCountUtil; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -38,6 +36,7 @@ import java.nio.channels.Selector; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; /** @@ -179,7 +178,7 @@ public class OioSctpServerChannel extends AbstractOioMessageChannel } @Override - protected int doReadMessages(MessageList buf) throws Exception { + protected int doReadMessages(List buf) throws Exception { if (!isActive()) { return -1; } @@ -286,11 +285,7 @@ public class OioSctpServerChannel extends AbstractOioMessageChannel } @Override - protected int doWrite(MessageList msgs, int index) throws Exception { - int size = msgs.size(); - for (int i = index; i < size; i ++) { - ReferenceCountUtil.release(msgs.get(i)); - } + protected int doWrite(Object[] msgs, int msgsLength, int startIndex) throws Exception { throw new UnsupportedOperationException(); } } diff --git a/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpInboundByteStreamHandler.java b/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpInboundByteStreamHandler.java index fb1e2bb596..7a9a618d91 100644 --- a/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpInboundByteStreamHandler.java +++ b/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpInboundByteStreamHandler.java @@ -18,11 +18,12 @@ package io.netty.handler.codec.sctp; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.channel.sctp.SctpMessage; import io.netty.handler.codec.CodecException; import io.netty.handler.codec.MessageToMessageDecoder; +import java.util.List; + /** * A ChannelHandler which receives {@link SctpMessage}s which belong to a application protocol form a specific * SCTP Stream and decode it as {@link ByteBuf}. @@ -53,7 +54,7 @@ public class SctpInboundByteStreamHandler extends MessageToMessageDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, SctpMessage msg, List out) throws Exception { 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())); diff --git a/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpMessageCompletionHandler.java b/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpMessageCompletionHandler.java index 355d36e076..30559e501f 100644 --- a/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpMessageCompletionHandler.java +++ b/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpMessageCompletionHandler.java @@ -20,11 +20,11 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandler; -import io.netty.channel.MessageList; import io.netty.channel.sctp.SctpMessage; import io.netty.handler.codec.MessageToMessageDecoder; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -36,7 +36,7 @@ public class SctpMessageCompletionHandler extends MessageToMessageDecoder fragments = new HashMap(); @Override - protected void decode(ChannelHandlerContext ctx, SctpMessage msg, MessageList out) throws Exception { + protected void decode(ChannelHandlerContext ctx, SctpMessage msg, List out) throws Exception { final ByteBuf byteBuf = msg.content(); final int protocolIdentifier = msg.protocolIdentifier(); final int streamIdentifier = msg.streamIdentifier(); diff --git a/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpOutboundByteStreamHandler.java b/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpOutboundByteStreamHandler.java index 09e7bbf803..26cecaca46 100644 --- a/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpOutboundByteStreamHandler.java +++ b/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpOutboundByteStreamHandler.java @@ -17,10 +17,11 @@ package io.netty.handler.codec.sctp; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.MessageList; import io.netty.channel.sctp.SctpMessage; import io.netty.handler.codec.MessageToMessageEncoder; +import java.util.List; + /** * A ChannelHandler which transform {@link ByteBuf} to {@link SctpMessage} and send it through a specific stream * with given protocol identifier. @@ -40,7 +41,7 @@ public class SctpOutboundByteStreamHandler extends MessageToMessageEncoder out) throws Exception { + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception { out.add(new SctpMessage(streamIdentifier, protocolIdentifier, msg.retain())); } } diff --git a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtAcceptorChannel.java b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtAcceptorChannel.java index bf83a93db5..b22a285885 100644 --- a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtAcceptorChannel.java +++ b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtAcceptorChannel.java @@ -18,7 +18,6 @@ package io.netty.channel.udt.nio; import com.barchart.udt.TypeUDT; import com.barchart.udt.nio.ServerSocketChannelUDT; import io.netty.channel.ChannelException; -import io.netty.channel.MessageList; import io.netty.channel.nio.AbstractNioMessageChannel; import io.netty.channel.udt.DefaultUdtServerChannelConfig; import io.netty.channel.udt.UdtServerChannel; @@ -94,7 +93,7 @@ public abstract class NioUdtAcceptorChannel extends AbstractNioMessageChannel im } @Override - protected int doWriteMessages(MessageList msg, int index, boolean lastSpin) throws Exception { + protected int doWriteMessages(Object[] msgs, int msgLength, int startIndex, boolean lastSpin) throws Exception { throw new UnsupportedOperationException(); } diff --git a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtByteAcceptorChannel.java b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtByteAcceptorChannel.java index 7ffb3273bb..52d2e30093 100644 --- a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtByteAcceptorChannel.java +++ b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtByteAcceptorChannel.java @@ -18,7 +18,8 @@ package io.netty.channel.udt.nio; import com.barchart.udt.TypeUDT; import com.barchart.udt.nio.SocketChannelUDT; import io.netty.channel.ChannelMetadata; -import io.netty.channel.MessageList; + +import java.util.List; /** * Byte Channel Acceptor for UDT Streams. @@ -32,7 +33,7 @@ public class NioUdtByteAcceptorChannel extends NioUdtAcceptorChannel { } @Override - protected int doReadMessages(MessageList buf) throws Exception { + protected int doReadMessages(List buf) throws Exception { final SocketChannelUDT channelUDT = javaChannel().accept(); if (channelUDT == null) { return 0; diff --git a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageAcceptorChannel.java b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageAcceptorChannel.java index 9977d6ff10..380afc5c5e 100644 --- a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageAcceptorChannel.java +++ b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageAcceptorChannel.java @@ -18,7 +18,8 @@ package io.netty.channel.udt.nio; import com.barchart.udt.TypeUDT; import com.barchart.udt.nio.SocketChannelUDT; import io.netty.channel.ChannelMetadata; -import io.netty.channel.MessageList; + +import java.util.List; /** * Message Channel Acceptor for UDT Datagrams. @@ -32,7 +33,7 @@ public class NioUdtMessageAcceptorChannel extends NioUdtAcceptorChannel { } @Override - protected int doReadMessages(MessageList buf) throws Exception { + protected int doReadMessages(List buf) throws Exception { final SocketChannelUDT channelUDT = javaChannel().accept(); if (channelUDT == null) { return 0; diff --git a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageConnectorChannel.java b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageConnectorChannel.java index 745e029573..b81c121756 100644 --- a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageConnectorChannel.java +++ b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageConnectorChannel.java @@ -21,7 +21,6 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelMetadata; -import io.netty.channel.MessageList; import io.netty.channel.nio.AbstractNioMessageChannel; import io.netty.channel.udt.DefaultUdtChannelConfig; import io.netty.channel.udt.UdtChannel; @@ -33,6 +32,7 @@ import io.netty.util.internal.logging.InternalLoggerFactory; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.channels.SelectionKey; +import java.util.List; import static java.nio.channels.SelectionKey.*; @@ -139,7 +139,7 @@ public class NioUdtMessageConnectorChannel extends AbstractNioMessageChannel imp } @Override - protected int doReadMessages(MessageList buf) throws Exception { + protected int doReadMessages(List buf) throws Exception { final int maximumMessageSize = config.getReceiveBufferSize(); @@ -167,9 +167,9 @@ public class NioUdtMessageConnectorChannel extends AbstractNioMessageChannel imp } @Override - protected int doWriteMessages(MessageList msgs, int index, boolean lastSpin) throws Exception { + protected int doWriteMessages(Object[] msgs, int msgLength, int startIndex, boolean lastSpin) throws Exception { // expects a message - final UdtMessage message = (UdtMessage) msgs.get(index); + final UdtMessage message = (UdtMessage) msgs[startIndex]; final ByteBuf byteBuf = message.content(); @@ -202,7 +202,7 @@ public class NioUdtMessageConnectorChannel extends AbstractNioMessageChannel imp } // wrote the message queue completely - clear OP_WRITE. - if (index + 1 == msgs.size()) { + if (msgLength == 1) { if ((interestOps & OP_WRITE) != 0) { key.interestOps(interestOps & ~OP_WRITE); } diff --git a/transport-udt/src/test/java/io/netty/test/udt/util/EchoByteHandler.java b/transport-udt/src/test/java/io/netty/test/udt/util/EchoByteHandler.java index 3a43bef517..2b7486a9db 100644 --- a/transport-udt/src/test/java/io/netty/test/udt/util/EchoByteHandler.java +++ b/transport-udt/src/test/java/io/netty/test/udt/util/EchoByteHandler.java @@ -21,7 +21,6 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.MessageList; import io.netty.channel.udt.nio.NioUdtProvider; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -57,21 +56,18 @@ public class EchoByteHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(final ChannelHandlerContext ctx) throws Exception { - log.info("ECHO active {}", NioUdtProvider.socketUDT(ctx.channel()) - .toStringOptions()); + log.info("ECHO active {}", NioUdtProvider.socketUDT(ctx.channel()).toStringOptions()); - ctx.write(message); + ctx.write(message).flush(); } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - for (int i = 0; i < msgs.size(); i ++) { - ByteBuf buf = (ByteBuf) msgs.get(i); - if (meter != null) { - meter.mark(buf.readableBytes()); - } + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf buf = (ByteBuf) msg; + if (meter != null) { + meter.mark(buf.readableBytes()); } - ctx.write(msgs); + ctx.write(msg).flush(); } @Override diff --git a/transport-udt/src/test/java/io/netty/test/udt/util/EchoMessageHandler.java b/transport-udt/src/test/java/io/netty/test/udt/util/EchoMessageHandler.java index 5ed41a28f4..b229dd2b88 100644 --- a/transport-udt/src/test/java/io/netty/test/udt/util/EchoMessageHandler.java +++ b/transport-udt/src/test/java/io/netty/test/udt/util/EchoMessageHandler.java @@ -21,7 +21,6 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.MessageList; import io.netty.channel.udt.UdtMessage; import io.netty.channel.udt.nio.NioUdtProvider; import io.netty.util.internal.logging.InternalLogger; @@ -57,10 +56,8 @@ public class EchoMessageHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(final ChannelHandlerContext ctx) throws Exception { - - log.info("ECHO active {}", NioUdtProvider.socketUDT(ctx.channel()) - .toStringOptions()); - ctx.write(message); + log.info("ECHO active {}", NioUdtProvider.socketUDT(ctx.channel()).toStringOptions()); + ctx.write(message).flush(); } @Override @@ -70,13 +67,11 @@ public class EchoMessageHandler extends ChannelInboundHandlerAdapter { } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - for (int i = 0; i < msgs.size(); i ++) { - UdtMessage udtMsg = (UdtMessage) msgs.get(i); - if (meter != null) { - meter.mark(udtMsg.content().readableBytes()); - } + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + UdtMessage udtMsg = (UdtMessage) msg; + if (meter != null) { + meter.mark(udtMsg.content().readableBytes()); } - ctx.write(msgs); + ctx.write(msg).flush(); } } diff --git a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java index 1b9d1f66ed..1ac971f796 100644 --- a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java +++ b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java @@ -19,12 +19,12 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.ServerChannel; -import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.SocketChannel; import io.netty.util.AttributeKey; import io.netty.util.internal.logging.InternalLogger; @@ -207,7 +207,7 @@ public final class ServerBootstrap extends AbstractBootstrap { + private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter { private final EventLoopGroup childGroup; private final ChannelHandler childHandler; @@ -226,7 +226,9 @@ public final class ServerBootstrap extends AbstractBootstrap, Object> e: childOptions) { diff --git a/transport/src/main/java/io/netty/channel/AbstractChannel.java b/transport/src/main/java/io/netty/channel/AbstractChannel.java index db2e6326a6..0ab6900430 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannel.java @@ -178,13 +178,8 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } @Override - public ChannelFuture write(Object msg) { - return pipeline.write(msg); - } - - @Override - public ChannelFuture write(MessageList msgs) { - return pipeline.write(msgs); + public ChannelFuture flush() { + return pipeline.flush(); } @Override @@ -218,18 +213,30 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } @Override - public void read() { + public Channel read() { pipeline.read(); + return this; } @Override - public ChannelFuture write(Object msg, ChannelPromise promise) { - return pipeline.write(msg, promise); + public Channel write(Object msg) { + pipeline.write(msg); + return this; } @Override - public ChannelFuture write(MessageList msgs, ChannelPromise promise) { - return pipeline.write(msgs, promise); + public ChannelFuture flush(ChannelPromise promise) { + return pipeline.flush(promise); + } + + @Override + public ChannelFuture writeAndFlush(Object msg) { + return pipeline.writeAndFlush(msg); + } + + @Override + public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { + return pipeline.writeAndFlush(msg, promise); } @Override @@ -353,14 +360,6 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha */ protected abstract class AbstractUnsafe implements Unsafe { - private final Runnable flushLaterTask = new Runnable() { - @Override - public void run() { - flushNowPending = false; - flush(); - } - }; - @Override public final SocketAddress localAddress() { return localAddress0(); @@ -590,12 +589,14 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } @Override - public void write(MessageList msgs, ChannelPromise promise) { - outboundBuffer.add(msgs, promise); - flush(); + public void write(Object msg) { + outboundBuffer.addMessage(msg); } - private void flush() { + @Override + public void flush(final ChannelPromise promise) { + outboundBuffer.addPromise(promise); + if (!inFlushNow) { // Avoid re-entrance try { // Flush immediately only when there's no pending flush. @@ -611,7 +612,12 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } else { if (!flushNowPending) { flushNowPending = true; - eventLoop().execute(flushLaterTask); + eventLoop().execute(new Runnable() { + @Override + public void run() { + flush(promise); + } + }); } } } @@ -648,12 +654,9 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } MessageList messages = outboundBuffer.currentMessages; - int messageIndex = outboundBuffer.currentMessageIndex; - int messageCount = messages.size(); // Make sure the message list is not empty. - if (messageCount == 0) { - messages.recycle(); + if (messages == null) { promise.trySuccess(); if (!outboundBuffer.next()) { break; @@ -662,11 +665,15 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } } + int messageIndex = outboundBuffer.currentMessageIndex; + int messageCount = messages.size(); + Object[] messageArray = messages.array(); + // Make sure the promise has not been cancelled. if (promise.isCancelled()) { // If cancelled, release all unwritten messages and recycle. for (int i = messageIndex; i < messageCount; i ++) { - ReferenceCountUtil.release(messages.get(i)); + ReferenceCountUtil.release(messageArray[i]); } messages.recycle(); if (!outboundBuffer.next()) { @@ -677,7 +684,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } // Write the messages. - int writtenMessages = doWrite(messages, messageIndex); + int writtenMessages = doWrite(messageArray, messageCount, messageIndex); outboundBuffer.currentMessageIndex = messageIndex += writtenMessages; if (messageIndex >= messageCount) { messages.recycle(); @@ -808,7 +815,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha * * @return the number of written messages */ - protected abstract int doWrite(MessageList msgs, int index) throws Exception; + protected abstract int doWrite(Object[] msgs, int msgsLength, int startIndex) throws Exception; protected static void checkEOF(FileRegion region) throws IOException { if (region.transfered() < region.count()) { diff --git a/transport/src/main/java/io/netty/channel/AbstractServerChannel.java b/transport/src/main/java/io/netty/channel/AbstractServerChannel.java index 946fdc4f27..753e2bff16 100755 --- a/transport/src/main/java/io/netty/channel/AbstractServerChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractServerChannel.java @@ -25,7 +25,8 @@ import java.net.SocketAddress; *
    *
  • {@link #connect(SocketAddress, ChannelPromise)}
  • *
  • {@link #disconnect(ChannelPromise)}
  • - *
  • {@link ChannelOutboundInvoker#write(Object, ChannelPromise)}
  • + *
  • {@link #write(Object)}
  • + *
  • {@link #flush(ChannelPromise)}
  • *
  • and the shortcut methods which calls the methods mentioned above *
*/ @@ -71,18 +72,19 @@ public abstract class AbstractServerChannel extends AbstractChannel implements S } @Override - protected int doWrite(MessageList msgs, int index) throws Exception { + protected int doWrite(Object[] msgs, int msgsLength, int startIndex) throws Exception { throw new UnsupportedOperationException(); } private final class DefaultServerUnsafe extends AbstractUnsafe { @Override - public void write(MessageList msgs, ChannelPromise promise) { + public void write(Object msg) { + ReferenceCountUtil.release(msg); + } + + @Override + public void flush(ChannelPromise promise) { reject(promise); - int size = msgs.size(); - for (int i = 0; i < size; i ++) { - ReferenceCountUtil.release(msgs.get(i)); - } } @Override diff --git a/transport/src/main/java/io/netty/channel/Channel.java b/transport/src/main/java/io/netty/channel/Channel.java index b4621eba29..c6eda10298 100755 --- a/transport/src/main/java/io/netty/channel/Channel.java +++ b/transport/src/main/java/io/netty/channel/Channel.java @@ -147,6 +147,12 @@ public interface Channel extends AttributeMap, ChannelOutboundInvoker, ChannelPr */ boolean isWritable(); + @Override + Channel write(Object msg); + + @Override + Channel read(); + /** * Returns an internal-use-only object that provides unsafe operations. */ @@ -231,10 +237,15 @@ public interface Channel extends AttributeMap, ChannelOutboundInvoker, ChannelPr /** * Schedules a write operation. */ - void write(MessageList msgs, ChannelPromise promise); + void write(Object msg); /** - * Flush out all data now. + * Flush out all scheduled writes. + */ + void flush(ChannelPromise promise); + + /** + * Flush out all schedules writes immediately. */ void flushNow(); diff --git a/transport/src/main/java/io/netty/channel/ChannelDuplexHandler.java b/transport/src/main/java/io/netty/channel/ChannelDuplexHandler.java index 2a8b73bcdc..3246301419 100644 --- a/transport/src/main/java/io/netty/channel/ChannelDuplexHandler.java +++ b/transport/src/main/java/io/netty/channel/ChannelDuplexHandler.java @@ -90,7 +90,12 @@ public class ChannelDuplexHandler extends ChannelInboundHandlerAdapter implement } @Override - public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { - ctx.write(msgs, promise); + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { + ctx.write(msg); + } + + @Override + public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + ctx.flush(promise); } } diff --git a/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java b/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java index 4432c2a50d..4974eccde5 100755 --- a/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java +++ b/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java @@ -179,11 +179,14 @@ public interface ChannelHandlerContext ChannelHandlerContext fireMessageReceived(Object msg); @Override - ChannelHandlerContext fireMessageReceived(MessageList msgs); + ChannelHandlerContext fireMessageReceivedLast(); @Override ChannelHandlerContext fireChannelReadSuspended(); @Override ChannelHandlerContext fireChannelWritabilityChanged(); + + @Override + ChannelHandlerContext write(Object msg); } diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundHandler.java b/transport/src/main/java/io/netty/channel/ChannelInboundHandler.java index a7e1817010..dee5646b3a 100755 --- a/transport/src/main/java/io/netty/channel/ChannelInboundHandler.java +++ b/transport/src/main/java/io/netty/channel/ChannelInboundHandler.java @@ -54,7 +54,9 @@ public interface ChannelInboundHandler extends ChannelHandler { * implementation. It's up to the implementation to consume it or keep it in the buffer * to wait for more data and consume it later. */ - void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception; + void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception; + + void messageReceivedLast(ChannelHandlerContext ctx) throws Exception; /** * Gets called if an user event was triggered. diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelInboundHandlerAdapter.java index 98b2feb335..69e57f7a85 100644 --- a/transport/src/main/java/io/netty/channel/ChannelInboundHandlerAdapter.java +++ b/transport/src/main/java/io/netty/channel/ChannelInboundHandlerAdapter.java @@ -24,11 +24,9 @@ package io.netty.channel; * {@link ChannelPipeline}. Sub-classes may override a method implementation to change this. *

*

- * Be aware that messages are not released after the {@link #messageReceived(ChannelHandlerContext, MessageList)} - * method returns automatically. This is done for make it as flexible as possible and get the most out of - * performance. Because of this you need to explicit call {@link MessageList#releaseAllAndRecycle()} if you - * consumed all the messages. Because this is such a common need {@link SimpleChannelInboundHandler} is provided , - * which will automatically release messages and the {@link MessageList} after processing is done. + * Be aware that messages are not released after the {@link #messageReceived(ChannelHandlerContext, Object)} + * method returns automatically. If you are looking for a {@link ChannelInboundHandler} implementation that + * releases the received messages automatically, please see {@link SimpleChannelInboundHandler}. *

*/ public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler { @@ -88,14 +86,19 @@ public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implemen } /** - * Calls {@link ChannelHandlerContext#fireMessageReceived(MessageList)} to forward + * Calls {@link ChannelHandlerContext#fireMessageReceived(Object)} to forward * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - ctx.fireMessageReceived(msgs); + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + ctx.fireMessageReceived(msg); + } + + @Override + public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception { + ctx.fireMessageReceivedLast(); } /** diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundInvoker.java b/transport/src/main/java/io/netty/channel/ChannelInboundInvoker.java index ac12c17505..d4c48e9968 100644 --- a/transport/src/main/java/io/netty/channel/ChannelInboundInvoker.java +++ b/transport/src/main/java/io/netty/channel/ChannelInboundInvoker.java @@ -78,12 +78,13 @@ interface ChannelInboundInvoker { /** * A {@link Channel} received bytes which are now ready to read from its inbound buffer. * - * This will result in having the {@link ChannelInboundHandler#messageReceived(ChannelHandlerContext, MessageList)} + * This will result in having the {@link ChannelInboundHandler#messageReceived(ChannelHandlerContext, Object)} * method called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelInboundInvoker fireMessageReceived(Object msg); - ChannelInboundInvoker fireMessageReceived(MessageList msgs); + + ChannelInboundInvoker fireMessageReceivedLast(); /** * Triggers an {@link ChannelInboundHandler#channelReadSuspended(ChannelHandlerContext) channelReadSuspended} diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java b/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java index 9185585515..fa92a153a2 100644 --- a/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java +++ b/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java @@ -34,17 +34,18 @@ final class ChannelOutboundBuffer { ChannelPromise currentPromise; MessageList currentMessages; int currentMessageIndex; - private int currentMessageListSize; + private long currentMessageListSize; private ChannelPromise[] promises; private MessageList[] messages; + private long[] messageListSizes; private int head; private int tail; private boolean inFail; private final AbstractChannel channel; - private int pendingOutboundBytes; + private long pendingOutboundBytes; private static final AtomicIntegerFieldUpdater WRITABLE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(ChannelOutboundBuffer.class, "writable"); @@ -80,20 +81,29 @@ final class ChannelOutboundBuffer { promises = new ChannelPromise[initialCapacity]; messages = new MessageList[initialCapacity]; + messageListSizes = new long[initialCapacity]; this.channel = channel; } - @SuppressWarnings("unchecked") - void add(MessageList msgs, ChannelPromise promise) { + void addMessage(Object msg) { + int tail = this.tail; + MessageList msgs = messages[tail]; + if (msgs == null) { + messages[tail] = msgs = MessageList.newInstance(); + } + msgs.add(msg); + + int size = channel.calculateMessageSize(msg); + messageListSizes[tail] += size; + incrementPendingOutboundBytes(size); + } + + void addPromise(ChannelPromise promise) { int tail = this.tail; promises[tail] = promise; - messages[tail] = (MessageList) msgs; - if ((this.tail = tail + 1 & promises.length - 1) == head) { doubleCapacity(); } - - incrementPendingOutboundBytes(messageListSize(msgs)); } private void incrementPendingOutboundBytes(int size) { @@ -101,7 +111,7 @@ final class ChannelOutboundBuffer { return; } - int newWriteBufferSize = pendingOutboundBytes += size; + long newWriteBufferSize = pendingOutboundBytes += size; int highWaterMark = channel.config().getWriteBufferHighWaterMark(); if (newWriteBufferSize > highWaterMark) { @@ -111,12 +121,12 @@ final class ChannelOutboundBuffer { } } - private void decrementPendingOutboundBytes(int size) { + private void decrementPendingOutboundBytes(long size) { if (size == 0) { return; } - int newWriteBufferSize = pendingOutboundBytes -= size; + long newWriteBufferSize = pendingOutboundBytes -= size; int lowWaterMark = channel.config().getWriteBufferLowWaterMark(); if (newWriteBufferSize == 0 || newWriteBufferSize < lowWaterMark) { @@ -149,11 +159,18 @@ final class ChannelOutboundBuffer { System.arraycopy(messages, 0, a2, r, p); messages = a2; + long[] a3 = new long[newCapacity]; + System.arraycopy(messageListSizes, p, a3, 0, r); + System.arraycopy(messageListSizes, 0, a3, r, p); + messageListSizes = a3; + head = 0; tail = n; } boolean next() { + // FIXME: pendingOutboundBytes should be decreased when the messages are flushed. + decrementPendingOutboundBytes(currentMessageListSize); int h = head; @@ -169,23 +186,16 @@ final class ChannelOutboundBuffer { currentPromise = e; currentMessages = messages[h]; currentMessageIndex = 0; - currentMessageListSize = messageListSize(currentMessages); + currentMessageListSize = messageListSizes[h]; promises[h] = null; messages[h] = null; + messageListSizes[h] = 0; head = h + 1 & promises.length - 1; return true; } - private int messageListSize(MessageList messages) { - int size = 0; - for (int i = 0; i < messages.size(); i++) { - size += channel.calculateMessageSize(messages.get(i)); - } - return size; - } - boolean getWritable() { return WRITABLE_UPDATER.get(this) == 1; } @@ -208,6 +218,7 @@ final class ChannelOutboundBuffer { do { promises[i] = null; messages[i] = null; + messageListSizes[i] = 0; i = i + 1 & mask; } while (i != tail); } @@ -233,17 +244,19 @@ final class ChannelOutboundBuffer { do { if (!(currentPromise instanceof VoidChannelPromise) && !currentPromise.tryFailure(cause)) { - logger.warn("Promise done already:", cause); + logger.warn("Promise done already: {} - new exception is:", currentPromise, cause); } - // Release all failed messages. - try { - for (int i = currentMessageIndex; i < currentMessages.size(); i++) { - Object msg = currentMessages.get(i); - ReferenceCountUtil.release(msg); + if (currentMessages != null) { + // Release all failed messages. + try { + for (int i = currentMessageIndex; i < currentMessages.size(); i++) { + Object msg = currentMessages.get(i); + ReferenceCountUtil.release(msg); + } + } finally { + currentMessages.recycle(); } - } finally { - currentMessages.recycle(); } } while(next()); } finally { diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundHandler.java b/transport/src/main/java/io/netty/channel/ChannelOutboundHandler.java index c55f9d5f5d..e49ece48c8 100644 --- a/transport/src/main/java/io/netty/channel/ChannelOutboundHandler.java +++ b/transport/src/main/java/io/netty/channel/ChannelOutboundHandler.java @@ -79,10 +79,9 @@ public interface ChannelOutboundHandler extends ChannelHandler { /** * Called once a flush operation is made and so the outbound data should be written. * - * * @param ctx the {@link ChannelHandlerContext} for which the flush operation is made - * @param promise the {@link ChannelPromise} to notify once the operation completes - * @throws Exception thrown if an error accour */ - void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception; + void write(ChannelHandlerContext ctx, Object msg) throws Exception; + + void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception; } diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelOutboundHandlerAdapter.java index 86818ddb4e..7ffb31dbd0 100644 --- a/transport/src/main/java/io/netty/channel/ChannelOutboundHandlerAdapter.java +++ b/transport/src/main/java/io/netty/channel/ChannelOutboundHandlerAdapter.java @@ -94,13 +94,18 @@ public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter impleme } /** - * Calls {@link ChannelHandlerContext#write(MessageList, ChannelPromise)} to forward + * Calls {@link ChannelHandlerContext#write(Object)} to forward * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Override - public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { - ctx.write(msgs, promise); + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { + ctx.write(msg); + } + + @Override + public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + ctx.flush(promise); } } diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundInvoker.java b/transport/src/main/java/io/netty/channel/ChannelOutboundInvoker.java index 190503e05e..497d27f735 100644 --- a/transport/src/main/java/io/netty/channel/ChannelOutboundInvoker.java +++ b/transport/src/main/java/io/netty/channel/ChannelOutboundInvoker.java @@ -99,12 +99,7 @@ interface ChannelOutboundInvoker { */ ChannelFuture deregister(); - /** - * Request to write a message via this ChannelOutboundInvoker and notify the {@link ChannelFuture} - * once the operation completes, either because the operation was successful or because of an error. - */ - ChannelFuture write(Object msg); - ChannelFuture write(MessageList msgs); + ChannelFuture flush(); /** * Request to bind to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation @@ -195,7 +190,7 @@ interface ChannelOutboundInvoker { /** * Request to Read data from the {@link Channel} into the first inbound buffer, triggers an - * {@link ChannelInboundHandler#messageReceived(ChannelHandlerContext, MessageList)} event if data was + * {@link ChannelInboundHandler#messageReceived(ChannelHandlerContext, Object)} event if data was * read, and triggers an * {@link ChannelInboundHandler#channelReadSuspended(ChannelHandlerContext) channelReadSuspended} event so the * handler can decide to continue reading. If there's a pending read operation already, this method does nothing. @@ -205,12 +200,28 @@ interface ChannelOutboundInvoker { * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ - void read(); + ChannelOutboundInvoker read(); /** - * Request to write a message via this ChannelOutboundInvoker and notify the {@link ChannelFuture} + * Request to write a message via this ChannelOutboundInvoker through the {@link ChannelPipeline}. + * This method will not request to actual flush, so be sure to call {@link #flush()} or + * {@link #flush(ChannelPromise)} once you want to request to flush all pending data to the actual transport. + */ + ChannelOutboundInvoker write(Object msg); + + /** + * Request to flush all pending messages via this ChannelOutboundInvoker and notify the {@link ChannelFuture} * once the operation completes, either because the operation was successful or because of an error. */ - ChannelFuture write(Object msg, ChannelPromise promise); - ChannelFuture write(MessageList msgs, ChannelPromise promise); + ChannelFuture flush(ChannelPromise promise); + + /** + * Shortcut for call {@link #write(Object)} and {@link #flush(ChannelPromise)}. + */ + ChannelFuture writeAndFlush(Object msg, ChannelPromise promise); + + /** + * Shortcut for call {@link #write(Object)} and {@link #flush()}. + */ + ChannelFuture writeAndFlush(Object msg); } diff --git a/transport/src/main/java/io/netty/channel/ChannelPipeline.java b/transport/src/main/java/io/netty/channel/ChannelPipeline.java index 5173459d54..2c791769b7 100644 --- a/transport/src/main/java/io/netty/channel/ChannelPipeline.java +++ b/transport/src/main/java/io/netty/channel/ChannelPipeline.java @@ -44,8 +44,8 @@ import java.util.NoSuchElementException; * The following diagram describes how I/O events are processed by {@link ChannelHandler}s in a {@link ChannelPipeline} * typically. An I/O event is handled by either a {@link ChannelInboundHandler} or a {@link ChannelOutboundHandler} * and be forwarded to its closest handler by calling the event propagation methods defined in - * {@link ChannelHandlerContext}, such as {@link ChannelHandlerContext#fireMessageReceived(MessageList)} and - * {@link ChannelHandlerContext#write(MessageList)}. + * {@link ChannelHandlerContext}, such as {@link ChannelHandlerContext#fireMessageReceived(Object)} and + * {@link ChannelHandlerContext#write(Object)}. * *
  *                                                 I/O Request
@@ -132,7 +132,8 @@ import java.util.NoSuchElementException;
  *     
    *
  • {@link ChannelHandlerContext#fireChannelRegistered()}
  • *
  • {@link ChannelHandlerContext#fireChannelActive()}
  • - *
  • {@link ChannelHandlerContext#fireMessageReceived(MessageList)}
  • + *
  • {@link ChannelHandlerContext#fireMessageReceived(Object)}
  • + *
  • {@link ChannelHandlerContext#fireMessageReceivedLast()}
  • *
  • {@link ChannelHandlerContext#fireExceptionCaught(Throwable)}
  • *
  • {@link ChannelHandlerContext#fireUserEventTriggered(Object)}
  • *
  • {@link ChannelHandlerContext#fireChannelReadSuspended()}
  • @@ -145,7 +146,8 @@ import java.util.NoSuchElementException; *
      *
    • {@link ChannelHandlerContext#bind(SocketAddress, ChannelPromise)}
    • *
    • {@link ChannelHandlerContext#connect(SocketAddress, SocketAddress, ChannelPromise)}
    • - *
    • {@link ChannelHandlerContext#write(MessageList)}
    • + *
    • {@link ChannelHandlerContext#write(Object)}
    • + *
    • {@link ChannelHandlerContext#flush(ChannelPromise)}
    • *
    • {@link ChannelHandlerContext#read()}
    • *
    • {@link ChannelHandlerContext#disconnect(ChannelPromise)}
    • *
    • {@link ChannelHandlerContext#close(ChannelPromise)}
    • @@ -615,11 +617,17 @@ public interface ChannelPipeline ChannelPipeline fireMessageReceived(Object msg); @Override - ChannelPipeline fireMessageReceived(MessageList msgs); + ChannelPipeline fireMessageReceivedLast(); @Override ChannelPipeline fireChannelReadSuspended(); @Override ChannelPipeline fireChannelWritabilityChanged(); + + @Override + ChannelPipeline write(Object msg); + + @Override + ChannelPipeline read(); } diff --git a/transport/src/main/java/io/netty/channel/CombinedChannelDuplexHandler.java b/transport/src/main/java/io/netty/channel/CombinedChannelDuplexHandler.java index 6304335a0f..eed89522d6 100644 --- a/transport/src/main/java/io/netty/channel/CombinedChannelDuplexHandler.java +++ b/transport/src/main/java/io/netty/channel/CombinedChannelDuplexHandler.java @@ -144,8 +144,13 @@ public class CombinedChannelDuplexHandler msgs) throws Exception { - inboundHandler.messageReceived(ctx, msgs); + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + inboundHandler.messageReceived(ctx, msg); + } + + @Override + public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception { + inboundHandler.messageReceivedLast(ctx); } @Override @@ -184,8 +189,13 @@ public class CombinedChannelDuplexHandler msgs, ChannelPromise promise) throws Exception { - outboundHandler.write(ctx, msgs, promise); + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { + outboundHandler.write(ctx, msg); + } + + @Override + public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + outboundHandler.flush(ctx, promise); } @Override diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelHandlerContext.java b/transport/src/main/java/io/netty/channel/DefaultChannelHandlerContext.java index d799744af2..0600e2e701 100755 --- a/transport/src/main/java/io/netty/channel/DefaultChannelHandlerContext.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelHandlerContext.java @@ -41,6 +41,7 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements private ChannelFuture succeededFuture; // Lazily instantiated tasks used to trigger events to a handler with different executor. + private Runnable invokeMessageReceivedLastTask; private Runnable invokeChannelReadSuspendedTask; private Runnable invokeRead0Task; private Runnable invokeChannelWritableStateChangedTask; @@ -340,43 +341,64 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } @Override - public ChannelHandlerContext fireMessageReceived(Object msg) { + public ChannelHandlerContext fireMessageReceived(final Object msg) { if (msg == null) { throw new NullPointerException("msg"); } - return fireMessageReceived(MessageList.newInstance(msg)); - } - @Override - public ChannelHandlerContext fireMessageReceived(final MessageList msgs) { - if (msgs == null) { - throw new NullPointerException("msgs"); - } - - if (msgs.isEmpty()) { - msgs.recycle(); - return this; + // FIXME: Remove once refactoring is done. + if (msg instanceof MessageList) { + throw new IllegalStateException(); } final DefaultChannelHandlerContext next = findContextInbound(); EventExecutor executor = next.executor(); if (executor.inEventLoop()) { - next.invokeMessageReceived(msgs); + next.invokeMessageReceived(msg); } else { executor.execute(new Runnable() { @Override public void run() { - next.invokeMessageReceived(msgs); + next.invokeMessageReceived(msg); } }); } return this; } - private void invokeMessageReceived(MessageList msgs) { + private void invokeMessageReceived(Object msg) { ChannelInboundHandler handler = (ChannelInboundHandler) handler(); try { - handler.messageReceived(this, msgs.cast()); + handler.messageReceived(this, msg); + } catch (Throwable t) { + notifyHandlerException(t); + } + } + + @Override + public ChannelHandlerContext fireMessageReceivedLast() { + final DefaultChannelHandlerContext next = findContextInbound(); + EventExecutor executor = next.executor(); + if (executor.inEventLoop()) { + next.invokeMessageReceivedLast(); + } else { + Runnable task = next.invokeMessageReceivedLastTask; + if (task == null) { + next.invokeMessageReceivedLastTask = task = new Runnable() { + @Override + public void run() { + next.invokeMessageReceivedLast(); + } + }; + } + executor.execute(task); + } + return this; + } + + private void invokeMessageReceivedLast() { + try { + ((ChannelInboundHandler) handler()).messageReceivedLast(this); } catch (Throwable t) { notifyHandlerException(t); } @@ -471,13 +493,8 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } @Override - public ChannelFuture write(Object msg) { - return write(msg, newPromise()); - } - - @Override - public ChannelFuture write(MessageList msgs) { - return write(msgs, newPromise()); + public ChannelFuture flush() { + return flush(newPromise()); } @Override @@ -649,8 +666,9 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } @Override - public void read() { + public ChannelHandlerContext read() { findContextOutbound().invokeRead(); + return this; } private void invokeRead() { @@ -680,32 +698,53 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } @Override - public ChannelFuture write(Object msg, final ChannelPromise promise) { + public ChannelHandlerContext write(Object msg) { if (msg == null) { throw new NullPointerException("msg"); } - return write(MessageList.newInstance(msg), promise); + + findContextOutbound().invokeWrite(msg); + return this; } - @Override - public ChannelFuture write(MessageList msgs, ChannelPromise promise) { - if (msgs == null) { - throw new NullPointerException("msgs"); - } - validatePromise(promise, true); - - return findContextOutbound().invokeWrite(msgs, promise); - } - - private ChannelFuture invokeWrite(final MessageList msgs, final ChannelPromise promise) { + private void invokeWrite(final Object msg) { EventExecutor executor = executor(); if (executor.inEventLoop()) { - invokeWrite0(msgs, promise); + invokeWrite0(msg); } else { executor.execute(new Runnable() { @Override public void run() { - invokeWrite0(msgs, promise); + invokeWrite0(msg); + } + }); + } + } + + private void invokeWrite0(Object msg) { + ChannelOutboundHandler handler = (ChannelOutboundHandler) handler(); + try { + handler.write(this, msg); + } catch (Throwable t) { + notifyHandlerException(t); + } + } + + @Override + public ChannelFuture flush(ChannelPromise promise) { + validatePromise(promise, true); + return findContextOutbound().invokeFlush(promise); + } + + private ChannelFuture invokeFlush(final ChannelPromise promise) { + EventExecutor executor = executor(); + if (executor.inEventLoop()) { + invokeFlush0(promise); + } else { + executor.execute(new Runnable() { + @Override + public void run() { + invokeFlush0(promise); } }); } @@ -713,15 +752,26 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements return promise; } - private void invokeWrite0(MessageList msgs, ChannelPromise promise) { - ChannelOutboundHandler handler = (ChannelOutboundHandler) handler(); + private void invokeFlush0(ChannelPromise promise) { try { - handler.write(this, msgs.cast(), promise); + ((ChannelOutboundHandler) handler()).flush(this, promise); } catch (Throwable t) { notifyOutboundHandlerException(t, promise); } } + @Override + public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { + write(msg); + return flush(promise); + } + + @Override + public ChannelFuture writeAndFlush(Object msg) { + write(msg); + return flush(); + } + private static void notifyOutboundHandlerException(Throwable cause, ChannelPromise promise) { // only try to fail the promise if its not a VoidChannelPromise, as // the VoidChannelPromise would also fire the cause through the pipeline diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java index f6fe786ae8..f46921f3c8 100755 --- a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java @@ -782,8 +782,8 @@ final class DefaultChannelPipeline implements ChannelPipeline { } @Override - public ChannelPipeline fireMessageReceived(MessageList msgs) { - head.fireMessageReceived(msgs); + public ChannelPipeline fireMessageReceivedLast() { + head.fireMessageReceivedLast(); return this; } @@ -833,13 +833,8 @@ final class DefaultChannelPipeline implements ChannelPipeline { } @Override - public ChannelFuture write(Object msg) { - return tail.write(msg); - } - - @Override - public ChannelFuture write(MessageList msgs) { - return tail.write(msgs); + public ChannelFuture flush() { + return tail.flush(); } @Override @@ -873,18 +868,30 @@ final class DefaultChannelPipeline implements ChannelPipeline { } @Override - public void read() { + public ChannelPipeline read() { tail.read(); + return this; } @Override - public ChannelFuture write(Object msg, ChannelPromise promise) { - return tail.write(msg, promise); + public ChannelPipeline write(Object msg) { + tail.write(msg); + return this; } @Override - public ChannelFuture write(MessageList msgs, ChannelPromise promise) { - return tail.write(msgs, promise); + public ChannelFuture flush(ChannelPromise promise) { + return tail.flush(promise); + } + + @Override + public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { + return tail.writeAndFlush(msg, promise); + } + + @Override + public ChannelFuture writeAndFlush(Object msg) { + return tail.writeAndFlush(msg); } private void checkDuplicateName(String name) { @@ -958,27 +965,18 @@ final class DefaultChannelPipeline implements ChannelPipeline { } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - int length = msgs.size(); - if (length == 0) { - return; - } - - for (int i = 0; i < length; i ++) { - Object m = msgs.get(i); + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + try { logger.debug( "Discarded inbound message {} that reached at the tail of the pipeline. " + - "Please check your pipeline configuration.", m); - - ReferenceCountUtil.release(m); - } - - if (length != 1) { - logger.warn( - "Discarded {} inbound message(s) that reached at the tail of the pipeline. " + - "Please check your pipeline configuration.", length); + "Please check your pipeline configuration.", msg); + } finally { + ReferenceCountUtil.release(msg); } } + + @Override + public void messageReceivedLast(ChannelHandlerContext ctx) throws Exception { } } static final class HeadHandler implements ChannelOutboundHandler { @@ -1035,9 +1033,13 @@ final class DefaultChannelPipeline implements ChannelPipeline { } @Override - public void write( - ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { - unsafe.write(msgs, promise); + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { + unsafe.write(msg); + } + + @Override + public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + unsafe.flush(promise); } @Override diff --git a/transport/src/main/java/io/netty/channel/MessageList.java b/transport/src/main/java/io/netty/channel/MessageList.java index 0091a504a1..744bdc3f9c 100644 --- a/transport/src/main/java/io/netty/channel/MessageList.java +++ b/transport/src/main/java/io/netty/channel/MessageList.java @@ -85,7 +85,7 @@ import java.util.NoSuchElementException; * * @param the type of the contained messages */ -public final class MessageList implements Iterable { +final class MessageList implements Iterable { private static final int DEFAULT_INITIAL_CAPACITY = 8; private static final int MIN_INITIAL_CAPACITY = 4; diff --git a/transport/src/main/java/io/netty/channel/SimpleChannelInboundHandler.java b/transport/src/main/java/io/netty/channel/SimpleChannelInboundHandler.java index 4de374aa22..6808243861 100644 --- a/transport/src/main/java/io/netty/channel/SimpleChannelInboundHandler.java +++ b/transport/src/main/java/io/netty/channel/SimpleChannelInboundHandler.java @@ -15,6 +15,7 @@ */ package io.netty.channel; +import io.netty.util.ReferenceCountUtil; import io.netty.util.internal.TypeParameterMatcher; /** @@ -37,8 +38,6 @@ import io.netty.util.internal.TypeParameterMatcher; */ public abstract class SimpleChannelInboundHandler extends ChannelInboundHandlerAdapter { - private static final Object UNRELEASABLE = new Object(); - private final TypeParameterMatcher matcher; private final boolean autoRelease; @@ -65,43 +64,19 @@ public abstract class SimpleChannelInboundHandler extends ChannelInboundHandl } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - MessageList unaccepted = MessageList.newInstance(); - int size = msgs.size(); + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + boolean release = true; try { - beginMessageReceived(ctx); - - for (int i = 0; i < size; i++) { - Object msg = msgs.get(i); - if (!ctx.isRemoved() && acceptInboundMessage(msg)) { - if (!unaccepted.isEmpty()) { - ctx.fireMessageReceived(unaccepted); - unaccepted = MessageList.newInstance(); - } - - @SuppressWarnings("unchecked") - I imsg = (I) msg; - messageReceived(ctx, imsg); - } else { - if (autoRelease) { - msgs.set(i, UNRELEASABLE); // Prevent the message added to 'unaccepted' from being released. - } - unaccepted.add(msg); - } + if (acceptInboundMessage(msg)) { + I imsg = (I) msg; + messageReceived0(ctx, imsg); + } else { + release = false; + ctx.fireMessageReceived(msg); } } finally { - try { - if (autoRelease) { - msgs.releaseAllAndRecycle(); - } else { - msgs.recycle(); - } - } finally { - try { - endMessageReceived(ctx); - } finally { - ctx.fireMessageReceived(unaccepted); - } + if (autoRelease && release) { + ReferenceCountUtil.release(msg); } } } @@ -114,27 +89,5 @@ public abstract class SimpleChannelInboundHandler extends ChannelInboundHandl * @param msg the message to handle * @throws Exception is thrown if an error accour */ - protected abstract void messageReceived(ChannelHandlerContext ctx, I msg) throws Exception; - - /** - * Is called before the first {@link #messageReceived(ChannelHandlerContext, Object)} of the current - * {@link MessageList} is handled. - * - * @param ctx the {@link ChannelHandlerContext} which is bound to this handler - */ - @SuppressWarnings("UnusedParameters") - protected void beginMessageReceived(ChannelHandlerContext ctx) throws Exception { - // NOOP - } - - /** - * Is called after the last {@link #messageReceived(ChannelHandlerContext, Object)} of the current - * {@link MessageList} is handled. - * - * @param ctx the {@link ChannelHandlerContext} which is bound to this handler - */ - @SuppressWarnings("UnusedParameters") - protected void endMessageReceived(ChannelHandlerContext ctx) throws Exception { - // NOOP - } + protected abstract void messageReceived0(ChannelHandlerContext ctx, I msg) throws Exception; } diff --git a/transport/src/main/java/io/netty/channel/embedded/EmbeddedChannel.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedChannel.java index 60f3ab4224..de1c77e5f8 100755 --- a/transport/src/main/java/io/netty/channel/embedded/EmbeddedChannel.java +++ b/transport/src/main/java/io/netty/channel/embedded/EmbeddedChannel.java @@ -27,7 +27,6 @@ import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; import io.netty.channel.DefaultChannelConfig; import io.netty.channel.EventLoop; -import io.netty.channel.MessageList; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -145,9 +144,12 @@ public class EmbeddedChannel extends AbstractChannel { if (msgs.length == 0) { return !lastInboundBuffer.isEmpty(); } - MessageList list = MessageList.newInstance(msgs.length); - list.add(msgs); - pipeline().fireMessageReceived(list); + + ChannelPipeline p = pipeline(); + for (Object m: msgs) { + p.fireMessageReceived(m); + } + p.fireMessageReceivedLast(); runPendingTasks(); checkException(); return !lastInboundBuffer.isEmpty(); @@ -164,9 +166,14 @@ public class EmbeddedChannel extends AbstractChannel { if (msgs.length == 0) { return !lastOutboundBuffer.isEmpty(); } - MessageList list = MessageList.newInstance(msgs.length); - list.add(msgs); - ChannelFuture future = write(list); + + for (Object m: msgs) { + if (m == null) { + break; + } + write(m); + } + ChannelFuture future = flush(); assert future.isDone(); if (future.cause() != null) { recordException(future.cause()); @@ -288,12 +295,11 @@ public class EmbeddedChannel extends AbstractChannel { } @Override - protected int doWrite(MessageList msgs, int index) throws Exception { - int size = msgs.size(); - for (int i = index; i < size; i ++) { - lastOutboundBuffer.add(msgs.get(i)); + protected int doWrite(Object[] msgs, int msgsLength, int startIndex) throws Exception { + for (int i = startIndex; i < msgsLength; i ++) { + lastOutboundBuffer.add(msgs[i]); } - return size - index; + return msgsLength - startIndex; } private class DefaultUnsafe extends AbstractUnsafe { @@ -305,12 +311,8 @@ public class EmbeddedChannel extends AbstractChannel { private final class LastInboundHandler extends ChannelInboundHandlerAdapter { @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - int size = msgs.size(); - for (int i = 0; i < size; i ++) { - lastInboundBuffer.add(msgs.get(i)); - } - msgs.recycle(); + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + lastInboundBuffer.add(msg); } @Override diff --git a/transport/src/main/java/io/netty/channel/group/ChannelGroup.java b/transport/src/main/java/io/netty/channel/group/ChannelGroup.java index 707b284e6e..23e5f498ee 100644 --- a/transport/src/main/java/io/netty/channel/group/ChannelGroup.java +++ b/transport/src/main/java/io/netty/channel/group/ChannelGroup.java @@ -22,7 +22,6 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.EventLoop; -import io.netty.channel.MessageList; import io.netty.channel.ServerChannel; import io.netty.util.CharsetUtil; @@ -109,19 +108,6 @@ public interface ChannelGroup extends Set, Comparable { */ ChannelGroupFuture write(Object message); - /** - * Writes the specified {@code messages} to all {@link Channel}s in this - * group. If the specified {@code messages} are an instance of - * {@link ByteBuf}, it is automatically - * {@linkplain ByteBuf#duplicate() duplicated} to avoid a race - * condition. Please note that this operation is asynchronous as - * {@link Channel#write(Object)} is. - * - * @return the {@link ChannelGroupFuture} instance that notifies when - * the operation is done for all channels - */ - ChannelGroupFuture write(MessageList messages); - /** * Disconnects all {@link Channel}s in this group from their remote peers. * diff --git a/transport/src/main/java/io/netty/channel/group/DefaultChannelGroup.java b/transport/src/main/java/io/netty/channel/group/DefaultChannelGroup.java index 9ba0f4ca8d..d9877fe111 100644 --- a/transport/src/main/java/io/netty/channel/group/DefaultChannelGroup.java +++ b/transport/src/main/java/io/netty/channel/group/DefaultChannelGroup.java @@ -20,7 +20,6 @@ import io.netty.buffer.ByteBufHolder; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; -import io.netty.channel.MessageList; import io.netty.channel.ServerChannel; import io.netty.util.ReferenceCountUtil; import io.netty.util.concurrent.EventExecutor; @@ -200,33 +199,13 @@ public class DefaultChannelGroup extends AbstractSet implements Channel Map futures = new LinkedHashMap(size()); for (Channel c: nonServerChannels) { - futures.put(c, c.write(safeDuplicate(message))); + futures.put(c, c.write(safeDuplicate(message)).flush()); } ReferenceCountUtil.release(message); return new DefaultChannelGroupFuture(this, futures, executor); } - @Override - public ChannelGroupFuture write(MessageList messages) { - if (messages == null) { - throw new NullPointerException("messages"); - } - - Map futures = new LinkedHashMap(size()); - for (Channel c: nonServerChannels) { - int size = messages.size(); - MessageList messageCopy = MessageList.newInstance(size); - for (int i = 0 ; i < size; i++) { - messageCopy.add(safeDuplicate(messages.get(i))); - } - futures.put(c, c.write(messageCopy)); - } - - messages.releaseAllAndRecycle(); - return new DefaultChannelGroupFuture(this, futures, executor); - } - // Create a safe duplicate of the message to write it to a channel but not affect other writes. // See https://github.com/netty/netty/issues/1461 private static Object safeDuplicate(Object message) { diff --git a/transport/src/main/java/io/netty/channel/local/LocalChannel.java b/transport/src/main/java/io/netty/channel/local/LocalChannel.java index d1f78d7e0c..87b44cdee6 100755 --- a/transport/src/main/java/io/netty/channel/local/LocalChannel.java +++ b/transport/src/main/java/io/netty/channel/local/LocalChannel.java @@ -24,7 +24,6 @@ import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; import io.netty.channel.DefaultChannelConfig; import io.netty.channel.EventLoop; -import io.netty.channel.MessageList; import io.netty.channel.SingleThreadEventLoop; import io.netty.util.concurrent.SingleThreadEventExecutor; @@ -52,18 +51,19 @@ public class LocalChannel extends AbstractChannel { }; private final ChannelConfig config = new DefaultChannelConfig(this); - private final Queue> inboundBuffer = new ArrayDeque>(); + private final Queue inboundBuffer = new ArrayDeque(); private final Runnable readTask = new Runnable() { @Override public void run() { ChannelPipeline pipeline = pipeline(); for (;;) { - MessageList m = inboundBuffer.poll(); + Object m = inboundBuffer.poll(); if (m == null) { break; } pipeline.fireMessageReceived(m); } + pipeline.fireMessageReceivedLast(); pipeline.fireChannelReadSuspended(); } }; @@ -239,7 +239,7 @@ public class LocalChannel extends AbstractChannel { } ChannelPipeline pipeline = pipeline(); - Queue> inboundBuffer = this.inboundBuffer; + Queue inboundBuffer = this.inboundBuffer; if (inboundBuffer.isEmpty()) { readInProgress = true; return; @@ -250,12 +250,13 @@ public class LocalChannel extends AbstractChannel { READER_STACK_DEPTH.set(stackDepth + 1); try { for (;;) { - MessageList received = inboundBuffer.poll(); + Object received = inboundBuffer.poll(); if (received == null) { break; } pipeline.fireMessageReceived(received); } + pipeline.fireMessageReceivedLast(); pipeline.fireChannelReadSuspended(); } finally { READER_STACK_DEPTH.set(stackDepth); @@ -266,7 +267,7 @@ public class LocalChannel extends AbstractChannel { } @Override - protected int doWrite(MessageList msgs, int index) throws Exception { + protected int doWrite(Object[] msgs, int msgsLength, int startIndex) throws Exception { if (state < 2) { throw new NotYetConnectedException(); } @@ -277,37 +278,42 @@ public class LocalChannel extends AbstractChannel { final LocalChannel peer = this.peer; final ChannelPipeline peerPipeline = peer.pipeline(); final EventLoop peerLoop = peer.eventLoop(); - final int size = msgs.size(); - - // Use a copy because the original msgs will be recycled by AbstractChannel. - final MessageList msgsCopy = msgs.copy(); if (peerLoop == eventLoop()) { - peer.inboundBuffer.add(msgsCopy); + for (int i = startIndex; i < msgsLength; i ++) { + peer.inboundBuffer.add(msgs[i]); + } finishPeerRead(peer, peerPipeline); } else { + // Use a copy because the original msgs will be recycled by AbstractChannel. + final Object[] msgsCopy = new Object[msgsLength - startIndex]; + System.arraycopy(msgs, startIndex, msgsCopy, 0, msgsCopy.length); + peerLoop.execute(new Runnable() { @Override public void run() { - peer.inboundBuffer.add(msgsCopy); + for (Object o: msgsCopy) { + peer.inboundBuffer.add(o); + } finishPeerRead(peer, peerPipeline); } }); } - return size - index; + return msgsLength - startIndex; } private static void finishPeerRead(LocalChannel peer, ChannelPipeline peerPipeline) { if (peer.readInProgress) { peer.readInProgress = false; for (;;) { - MessageList received = peer.inboundBuffer.poll(); + Object received = peer.inboundBuffer.poll(); if (received == null) { break; } peerPipeline.fireMessageReceived(received); } + peerPipeline.fireMessageReceivedLast(); peerPipeline.fireChannelReadSuspended(); } } diff --git a/transport/src/main/java/io/netty/channel/local/LocalServerChannel.java b/transport/src/main/java/io/netty/channel/local/LocalServerChannel.java index 8c962e1f70..26aa4ce258 100755 --- a/transport/src/main/java/io/netty/channel/local/LocalServerChannel.java +++ b/transport/src/main/java/io/netty/channel/local/LocalServerChannel.java @@ -20,7 +20,6 @@ import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelPipeline; import io.netty.channel.DefaultChannelConfig; import io.netty.channel.EventLoop; -import io.netty.channel.MessageList; import io.netty.channel.ServerChannel; import io.netty.channel.SingleThreadEventLoop; import io.netty.util.concurrent.SingleThreadEventExecutor; @@ -131,9 +130,14 @@ public class LocalServerChannel extends AbstractServerChannel { return; } - Object[] messages = inboundBuffer.toArray(); - inboundBuffer.clear(); - pipeline.fireMessageReceived(messages); + for (;;) { + Object m = inboundBuffer.poll(); + if (m == null) { + break; + } + pipeline.fireMessageReceived(m); + } + pipeline.fireMessageReceivedLast(); pipeline.fireChannelReadSuspended(); } @@ -149,16 +153,14 @@ public class LocalServerChannel extends AbstractServerChannel { inboundBuffer.add(child); if (acceptInProgress) { acceptInProgress = false; - MessageList messages = MessageList.newInstance(); for (;;) { Object m = inboundBuffer.poll(); if (m == null) { break; } - messages.add(m); + pipeline.fireMessageReceived(m); } - inboundBuffer.clear(); - pipeline.fireMessageReceived(messages); + pipeline.fireMessageReceivedLast(); pipeline.fireChannelReadSuspended(); } } else { diff --git a/transport/src/main/java/io/netty/channel/nio/AbstractNioByteChannel.java b/transport/src/main/java/io/netty/channel/nio/AbstractNioByteChannel.java index d1bed3233f..5f9abfc163 100755 --- a/transport/src/main/java/io/netty/channel/nio/AbstractNioByteChannel.java +++ b/transport/src/main/java/io/netty/channel/nio/AbstractNioByteChannel.java @@ -22,7 +22,6 @@ import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.FileRegion; -import io.netty.channel.MessageList; import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.socket.ChannelInputShutdownEvent; import io.netty.util.internal.StringUtil; @@ -76,11 +75,11 @@ public abstract class AbstractNioByteChannel extends AbstractNioChannel { final ByteBufAllocator allocator = config.getAllocator(); final int maxMessagesPerRead = config.getMaxMessagesPerRead(); - final MessageList messages = MessageList.newInstance(); boolean closed = false; Throwable exception = null; ByteBuf byteBuf = null; + int messages = 0; try { for (;;) { byteBuf = allocHandle.allocate(allocator); @@ -97,10 +96,10 @@ public abstract class AbstractNioByteChannel extends AbstractNioChannel { break; } - messages.add(byteBuf); + pipeline.fireMessageReceived(byteBuf); allocHandle.record(localReadAmount); byteBuf = null; - if (messages.size() == maxMessagesPerRead) { + if (++ messages == maxMessagesPerRead) { break; } } @@ -109,13 +108,15 @@ public abstract class AbstractNioByteChannel extends AbstractNioChannel { } finally { if (byteBuf != null) { if (byteBuf.isReadable()) { - messages.add(byteBuf); + pipeline.fireMessageReceived(byteBuf); } else { byteBuf.release(); } } - pipeline.fireMessageReceived(messages); + if (messages != 0) { + pipeline.fireMessageReceivedLast(); + } if (exception != null) { if (exception instanceof IOException) { @@ -143,14 +144,13 @@ public abstract class AbstractNioByteChannel extends AbstractNioChannel { } @Override - protected int doWrite(MessageList msgs, int index) throws Exception { - int size = msgs.size(); - int writeIndex = index; + protected int doWrite(Object[] msgs, int msgsLength, int startIndex) throws Exception { + int writeIndex = startIndex; for (;;) { - if (writeIndex >= size) { + if (writeIndex >= msgsLength) { break; } - Object msg = msgs.get(writeIndex); + Object msg = msgs[writeIndex]; if (msg instanceof ByteBuf) { ByteBuf buf = (ByteBuf) msg; if (!buf.isReadable()) { @@ -201,7 +201,7 @@ public abstract class AbstractNioByteChannel extends AbstractNioChannel { throw new UnsupportedOperationException("unsupported message type: " + StringUtil.simpleClassName(msg)); } } - return writeIndex - index; + return writeIndex - startIndex; } /** diff --git a/transport/src/main/java/io/netty/channel/nio/AbstractNioMessageChannel.java b/transport/src/main/java/io/netty/channel/nio/AbstractNioMessageChannel.java index c65afb0ec1..9ff6837828 100755 --- a/transport/src/main/java/io/netty/channel/nio/AbstractNioMessageChannel.java +++ b/transport/src/main/java/io/netty/channel/nio/AbstractNioMessageChannel.java @@ -18,11 +18,12 @@ package io.netty.channel.nio; import io.netty.channel.Channel; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelPipeline; -import io.netty.channel.MessageList; import java.io.IOException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; +import java.util.ArrayList; +import java.util.List; /** * {@link AbstractNioChannel} base class for {@link Channel}s that operate on messages. @@ -42,6 +43,9 @@ public abstract class AbstractNioMessageChannel extends AbstractNioChannel { } private final class NioMessageUnsafe extends AbstractNioUnsafe { + + private final List readBuf = new ArrayList(); + @Override public void read() { assert eventLoop().inEventLoop(); @@ -59,12 +63,10 @@ public abstract class AbstractNioMessageChannel extends AbstractNioChannel { final boolean autoRead = config.isAutoRead(); final ChannelPipeline pipeline = pipeline(); boolean closed = false; - MessageList msgBuf = MessageList.newInstance(); Throwable exception = null; - int readMessages = 0; try { for (;;) { - int localRead = doReadMessages(msgBuf); + int localRead = doReadMessages(readBuf); if (localRead == 0) { break; } @@ -73,8 +75,7 @@ public abstract class AbstractNioMessageChannel extends AbstractNioChannel { break; } - readMessages += localRead; - if (readMessages >= maxMessagesPerRead | !autoRead) { + if (readBuf.size() >= maxMessagesPerRead | !autoRead) { break; } } @@ -82,7 +83,11 @@ public abstract class AbstractNioMessageChannel extends AbstractNioChannel { exception = t; } - pipeline.fireMessageReceived(msgBuf); + for (int i = 0; i < readBuf.size(); i ++) { + pipeline.fireMessageReceived(readBuf.get(i)); + } + readBuf.clear(); + pipeline.fireMessageReceivedLast(); if (exception != null) { if (exception instanceof IOException) { @@ -103,10 +108,10 @@ public abstract class AbstractNioMessageChannel extends AbstractNioChannel { } @Override - protected int doWrite(MessageList msgs, int index) throws Exception { + protected int doWrite(Object[] msgs, int msgsLength, int startIndex) throws Exception { final int writeSpinCount = config().getWriteSpinCount() - 1; for (int i = writeSpinCount; i >= 0; i --) { - int written = doWriteMessages(msgs, index, i == 0); + int written = doWriteMessages(msgs, msgsLength, startIndex, i == 0); if (written > 0) { return written; } @@ -117,7 +122,7 @@ public abstract class AbstractNioMessageChannel extends AbstractNioChannel { /** * Read messages into the given array and return the amount which was read. */ - protected abstract int doReadMessages(MessageList buf) throws Exception; + protected abstract int doReadMessages(List buf) throws Exception; /** * Write messages to the underlying {@link java.nio.channels.Channel}. @@ -126,5 +131,6 @@ public abstract class AbstractNioMessageChannel extends AbstractNioChannel { * @return written the amount of written messages * @throws Exception thrown if an error accour */ - protected abstract int doWriteMessages(MessageList msg, int index, boolean lastSpin) throws Exception; + protected abstract int doWriteMessages( + Object[] msgs, int msgLength, int startIndex, boolean lastSpin) throws Exception; } diff --git a/transport/src/main/java/io/netty/channel/oio/AbstractOioByteChannel.java b/transport/src/main/java/io/netty/channel/oio/AbstractOioByteChannel.java index 356f8ef7ed..e29e9b40ba 100755 --- a/transport/src/main/java/io/netty/channel/oio/AbstractOioByteChannel.java +++ b/transport/src/main/java/io/netty/channel/oio/AbstractOioByteChannel.java @@ -21,7 +21,6 @@ import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.FileRegion; -import io.netty.channel.MessageList; import io.netty.channel.socket.ChannelInputShutdownEvent; import io.netty.util.internal.StringUtil; @@ -80,11 +79,14 @@ public abstract class AbstractOioByteChannel extends AbstractOioChannel { boolean closed = false; boolean read = false; boolean firedInboundBufferSuspeneded = false; + Throwable exception = null; + boolean readMessage = false; try { for (;;) { int localReadAmount = doReadBytes(byteBuf); if (localReadAmount > 0) { read = true; + readMessage = true; } else if (localReadAmount < 0) { closed = true; } @@ -119,20 +121,7 @@ public abstract class AbstractOioByteChannel extends AbstractOioChannel { } } } catch (Throwable t) { - if (read) { - read = false; - pipeline.fireMessageReceived(byteBuf); - } - - if (t instanceof IOException) { - closed = true; - pipeline.fireExceptionCaught(t); - } else { - firedInboundBufferSuspeneded = true; - pipeline.fireChannelReadSuspended(); - pipeline.fireExceptionCaught(t); - unsafe().close(voidPromise()); - } + exception = t; } finally { if (read) { pipeline.fireMessageReceived(byteBuf); @@ -140,6 +129,21 @@ public abstract class AbstractOioByteChannel extends AbstractOioChannel { // nothing read into the buffer so release it byteBuf.release(); } + if (readMessage) { + pipeline.fireMessageReceivedLast(); + } + if (exception != null) { + if (exception instanceof IOException) { + closed = true; + pipeline().fireExceptionCaught(exception); + } else { + firedInboundBufferSuspeneded = true; + pipeline.fireChannelReadSuspended(); + pipeline.fireExceptionCaught(exception); + unsafe().close(voidPromise()); + } + } + if (closed) { inputShutdown = true; if (isOpen()) { @@ -156,14 +160,13 @@ public abstract class AbstractOioByteChannel extends AbstractOioChannel { } @Override - protected int doWrite(MessageList msgs, int index) throws Exception { - int size = msgs.size(); - int writeIndex = index; + protected int doWrite(Object[] msgs, int msgsLength, int startIndex) throws Exception { + int writeIndex = startIndex; for (;;) { - if (writeIndex >= size) { + if (writeIndex >= msgsLength) { break; } - Object msg = msgs.get(writeIndex); + Object msg = msgs[writeIndex]; if (msg instanceof ByteBuf) { ByteBuf buf = (ByteBuf) msg; while (buf.isReadable()) { @@ -180,7 +183,7 @@ public abstract class AbstractOioByteChannel extends AbstractOioChannel { throw new UnsupportedOperationException("unsupported message type: " + StringUtil.simpleClassName(msg)); } } - return writeIndex - index; + return writeIndex - startIndex; } /** diff --git a/transport/src/main/java/io/netty/channel/oio/AbstractOioMessageChannel.java b/transport/src/main/java/io/netty/channel/oio/AbstractOioMessageChannel.java index ac2fd03d50..9bef3d7a58 100755 --- a/transport/src/main/java/io/netty/channel/oio/AbstractOioMessageChannel.java +++ b/transport/src/main/java/io/netty/channel/oio/AbstractOioMessageChannel.java @@ -17,15 +17,18 @@ package io.netty.channel.oio; import io.netty.channel.Channel; import io.netty.channel.ChannelPipeline; -import io.netty.channel.MessageList; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; /** * Abstract base class for OIO which reads and writes objects from/to a Socket */ public abstract class AbstractOioMessageChannel extends AbstractOioChannel { + private final List readBuf = new ArrayList(); + protected AbstractOioMessageChannel(Channel parent) { super(parent); } @@ -34,10 +37,9 @@ public abstract class AbstractOioMessageChannel extends AbstractOioChannel { protected void doRead() { final ChannelPipeline pipeline = pipeline(); boolean closed = false; - MessageList msgs = MessageList.newInstance(); Throwable exception = null; try { - int localReadAmount = doReadMessages(msgs); + int localReadAmount = doReadMessages(readBuf); if (localReadAmount < 0) { closed = true; } @@ -45,7 +47,11 @@ public abstract class AbstractOioMessageChannel extends AbstractOioChannel { exception = t; } - pipeline.fireMessageReceived(msgs); + for (int i = 0; i < readBuf.size(); i ++) { + pipeline.fireMessageReceived(readBuf.get(i)); + } + readBuf.clear(); + pipeline.fireMessageReceivedLast(); if (exception != null) { if (exception instanceof IOException) { @@ -67,5 +73,5 @@ public abstract class AbstractOioMessageChannel extends AbstractOioChannel { /** * Read messages into the given array and return the amount which was read. */ - protected abstract int doReadMessages(MessageList msgs) throws Exception; + protected abstract int doReadMessages(List msgs) throws Exception; } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java index 295f8573ec..7d1f52fb95 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java @@ -23,7 +23,6 @@ import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; -import io.netty.channel.MessageList; import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.nio.AbstractNioMessageChannel; import io.netty.channel.socket.DatagramChannelConfig; @@ -190,7 +189,7 @@ public final class NioDatagramChannel } @Override - protected int doReadMessages(MessageList buf) throws Exception { + protected int doReadMessages(List buf) throws Exception { DatagramChannel ch = javaChannel(); DatagramChannelConfig config = config(); RecvByteBufAllocator.Handle allocHandle = this.allocHandle; @@ -225,8 +224,8 @@ public final class NioDatagramChannel } @Override - protected int doWriteMessages(MessageList msgs, int index, boolean lastSpin) throws Exception { - final Object o = msgs.get(index); + protected int doWriteMessages(Object[] msgs, int msgsLength, int startIndex, boolean lastSpin) throws Exception { + final Object o = msgs[startIndex]; final Object m; final ByteBuf data; final SocketAddress remoteAddress; @@ -284,7 +283,7 @@ public final class NioDatagramChannel // Wrote a packet - free the message. ReferenceCountUtil.release(o); - if (index + 1 == msgs.size()) { + if (startIndex + 1 == msgsLength) { // Wrote the outbound buffer completely - clear OP_WRITE. if ((interestOps & SelectionKey.OP_WRITE) != 0) { key.interestOps(interestOps & ~SelectionKey.OP_WRITE); 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 615fae8170..fe3f20dc02 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java @@ -17,7 +17,6 @@ package io.netty.channel.socket.nio; import io.netty.channel.ChannelException; import io.netty.channel.ChannelMetadata; -import io.netty.channel.MessageList; import io.netty.channel.nio.AbstractNioMessageChannel; import io.netty.channel.socket.DefaultServerSocketChannelConfig; import io.netty.channel.socket.ServerSocketChannelConfig; @@ -30,6 +29,7 @@ import java.net.SocketAddress; import java.nio.channels.SelectionKey; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; +import java.util.List; /** * A {@link io.netty.channel.socket.ServerSocketChannel} implementation which uses @@ -107,7 +107,7 @@ public class NioServerSocketChannel extends AbstractNioMessageChannel } @Override - protected int doReadMessages(MessageList buf) throws Exception { + protected int doReadMessages(List buf) throws Exception { SocketChannel ch = javaChannel().accept(); try { @@ -151,7 +151,7 @@ public class NioServerSocketChannel extends AbstractNioMessageChannel } @Override - protected int doWriteMessages(MessageList msgs, int index, boolean lastSpin) throws Exception { + protected int doWriteMessages(Object[] msgs, int msgsLength, int startIndex, boolean lastSpin) throws Exception { throw new UnsupportedOperationException(); } } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java index 057bfd6eaf..b2c201d64c 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java @@ -23,7 +23,6 @@ import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoop; import io.netty.channel.FileRegion; -import io.netty.channel.MessageList; import io.netty.channel.nio.AbstractNioByteChannel; import io.netty.channel.socket.DefaultSocketChannelConfig; import io.netty.channel.socket.ServerSocketChannel; @@ -265,21 +264,22 @@ public class NioSocketChannel extends AbstractNioByteChannel implements io.netty } @Override - protected int doWrite(MessageList msgs, final int index) throws Exception { - final int size = msgs.size(); - + protected int doWrite(Object[] msgs, int msgsLength, final int startIndex) throws Exception { // Do non-gathering write for a single buffer case. - if (size <= 1 || !msgs.containsOnly(ByteBuf.class)) { - return super.doWrite(msgs, index); + if (msgsLength <= 1) { + return super.doWrite(msgs, msgsLength, startIndex); } - final Object[] bufs = msgs.array(); - ByteBuffer[] nioBuffers = getNioBufferArray(); int nioBufferCnt = 0; long expectedWrittenBytes = 0; - for (int i = index; i < size; i++) { - ByteBuf buf = (ByteBuf) bufs[i]; + for (int i = startIndex; i < msgsLength; i++) { + Object m = msgs[i]; + if (!(m instanceof ByteBuf)) { + return super.doWrite(msgs, msgsLength, startIndex); + } + + ByteBuf buf = (ByteBuf) m; int readerIndex = buf.readerIndex(); int readableBytes = buf.readableBytes(); @@ -308,7 +308,7 @@ public class NioSocketChannel extends AbstractNioByteChannel implements io.netty ByteBuf directBuf = alloc().directBuffer(readableBytes); directBuf.writeBytes(buf, readerIndex, readableBytes); buf.release(); - bufs[i] = directBuf; + msgs[i] = directBuf; if (nioBufferCnt == nioBuffers.length) { nioBuffers = doubleNioBufferArray(nioBuffers, nioBufferCnt); } @@ -335,16 +335,16 @@ public class NioSocketChannel extends AbstractNioByteChannel implements io.netty if (done) { // release buffers - for (int i = index; i < size; i++) { - ((ReferenceCounted) bufs[i]).release(); + for (int i = startIndex; i < msgsLength; i++) { + ((ReferenceCounted) msgs[i]).release(); } - return size - index; + return msgsLength - startIndex; } else { // Did not write all buffers completely. // Release the fully written buffers and update the indexes of the partially written buffer. int writtenBufs = 0; - for (int i = index; i < size; i++) { - final ByteBuf buf = (ByteBuf) bufs[i]; + for (int i = startIndex; i < msgsLength; i++) { + final ByteBuf buf = (ByteBuf) msgs[i]; final int readerIndex = buf.readerIndex(); final int readableBytes = buf.writerIndex() - readerIndex; diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java index 3d3548c9a8..e37a23f7c1 100755 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java @@ -23,7 +23,6 @@ import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; -import io.netty.channel.MessageList; import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.oio.AbstractOioMessageChannel; import io.netty.channel.socket.DatagramChannel; @@ -45,6 +44,7 @@ import java.net.NetworkInterface; import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketTimeoutException; +import java.util.List; import java.util.Locale; /** @@ -191,7 +191,7 @@ public class OioDatagramChannel extends AbstractOioMessageChannel } @Override - protected int doReadMessages(MessageList buf) throws Exception { + protected int doReadMessages(List buf) throws Exception { DatagramChannelConfig config = config(); RecvByteBufAllocator.Handle allocHandle = this.allocHandle; if (allocHandle == null) { @@ -233,8 +233,8 @@ public class OioDatagramChannel extends AbstractOioMessageChannel } @Override - protected int doWrite(MessageList msgs, int index) throws Exception { - final Object o = msgs.get(index); + protected int doWrite(Object[] msgs, int msgsLength, int startIndex) throws Exception { + final Object o = msgs[startIndex]; final Object m; final ByteBuf data; final SocketAddress remoteAddress; 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 505ac64cd2..0e32dc11b6 100755 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java @@ -17,7 +17,6 @@ package io.netty.channel.socket.oio; import io.netty.channel.ChannelException; import io.netty.channel.ChannelMetadata; -import io.netty.channel.MessageList; import io.netty.channel.oio.AbstractOioMessageChannel; import io.netty.channel.socket.ServerSocketChannel; import io.netty.util.internal.logging.InternalLogger; @@ -29,6 +28,7 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketTimeoutException; +import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -144,7 +144,7 @@ public class OioServerSocketChannel extends AbstractOioMessageChannel } @Override - protected int doReadMessages(MessageList buf) throws Exception { + protected int doReadMessages(List buf) throws Exception { if (socket.isClosed()) { return -1; } @@ -173,7 +173,7 @@ public class OioServerSocketChannel extends AbstractOioMessageChannel } @Override - protected int doWrite(MessageList msgs, int index) throws Exception { + protected int doWrite(Object[] msgs, int msgsLength, int startIndex) throws Exception { throw new UnsupportedOperationException(); } diff --git a/transport/src/test/java/io/netty/channel/AbstractEventLoopTest.java b/transport/src/test/java/io/netty/channel/AbstractEventLoopTest.java index d9c3f45c83..8e29ea176d 100644 --- a/transport/src/test/java/io/netty/channel/AbstractEventLoopTest.java +++ b/transport/src/test/java/io/netty/channel/AbstractEventLoopTest.java @@ -64,7 +64,7 @@ public abstract class AbstractEventLoopTest { private static final class TestChannelHandler2 extends ChannelDuplexHandler { @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { } + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { } } protected abstract EventLoopGroup newEventLoopGroup(); diff --git a/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java b/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java index 2bcb771a5f..db464f7e33 100644 --- a/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java +++ b/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java @@ -23,6 +23,7 @@ import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalChannel; import io.netty.channel.local.LocalEventLoopGroup; import io.netty.channel.local.LocalServerChannel; +import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCounted; import org.junit.After; import org.junit.AfterClass; @@ -60,8 +61,8 @@ public class DefaultChannelPipelineTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - msgs.releaseAllAndRecycle(); + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + ReferenceCountUtil.release(msg); } }); @@ -134,7 +135,7 @@ public class DefaultChannelPipelineTest { StringInboundHandler handler = new StringInboundHandler(); setUp(handler); - peer.write(holder).sync(); + peer.write(holder).flush().sync(); assertTrue(free.await(10, TimeUnit.SECONDS)); assertTrue(handler.called); @@ -144,18 +145,11 @@ public class DefaultChannelPipelineTest { boolean called; @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { called = true; - MessageList out = MessageList.newInstance(); - for (int i = 0; i < msgs.size(); i ++) { - Object m = msgs.get(i); - if (!(m instanceof String)) { - out.add(m); - } + if (!(msg instanceof String)) { + ctx.fireMessageReceived(msg); } - - msgs.recycle(); - ctx.fireMessageReceived(out); } } @@ -492,29 +486,29 @@ public class DefaultChannelPipelineTest { final MessageList outboundBuffer = MessageList.newInstance(); @Override - public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) - throws Exception { - for (int i = 0; i < msgs.size(); i++) { - outboundBuffer.add(msgs.get(i)); - } - msgs.recycle(); + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { + outboundBuffer.add(msg); } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - for (int i = 0; i < msgs.size(); i++) { - inboundBuffer.add(msgs.get(i)); - } - msgs.recycle(); + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + inboundBuffer.add(msg); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { if (!inboundBuffer.isEmpty()) { - ctx.fireMessageReceived(inboundBuffer); + for (Object o: inboundBuffer) { + ctx.fireMessageReceived(o); + } + ctx.fireMessageReceivedLast(); + inboundBuffer.recycle(); } if (!outboundBuffer.isEmpty()) { - ctx.write(outboundBuffer); + for (Object o: outboundBuffer) { + ctx.write(o); + } + ctx.flush(); } } } diff --git a/transport/src/test/java/io/netty/channel/local/LocalChannelTest.java b/transport/src/test/java/io/netty/channel/local/LocalChannelTest.java index 4171893c9c..4f3d1c69a0 100644 --- a/transport/src/test/java/io/netty/channel/local/LocalChannelTest.java +++ b/transport/src/test/java/io/netty/channel/local/LocalChannelTest.java @@ -15,23 +15,26 @@ */ package io.netty.channel.local; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.AbstractChannel; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; -import org.junit.Test; import java.nio.channels.ClosedChannelException; import java.util.concurrent.CountDownLatch; -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; +import org.junit.Test; public class LocalChannelTest { @@ -124,7 +127,7 @@ public class LocalChannelTest { // Close the channel and write something. cc.close().sync(); try { - cc.write(new Object()).sync(); + cc.write(new Object()).flush().sync(); fail("must raise a ClosedChannelException"); } catch (Exception e) { assertThat(e, is(instanceOf(ClosedChannelException.class))); @@ -140,7 +143,7 @@ public class LocalChannelTest { clientGroup.terminationFuture().sync(); } - static class TestHandler extends SimpleChannelInboundHandler { + static class TestHandler extends ChannelInboundHandlerAdapter { @Override public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { logger.info(String.format("Received mesage: %s", msg)); diff --git a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java index 2047268c32..317761356a 100644 --- a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java +++ b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java @@ -22,17 +22,11 @@ import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoopGroup; -import io.netty.channel.MessageList; +import io.netty.util.ReferenceCountUtil; import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.DefaultThreadFactory; import io.netty.util.concurrent.EventExecutorGroup; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; import java.util.HashSet; import java.util.Queue; @@ -40,6 +34,12 @@ import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicReference; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + public class LocalTransportThreadModelTest { private static EventLoopGroup group; @@ -57,9 +57,9 @@ public class LocalTransportThreadModelTest { public void initChannel(LocalChannel ch) throws Exception { ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) { + public void messageReceived(ChannelHandlerContext ctx, Object msg) { // Discard - msgs.releaseAllAndRecycle(); + ReferenceCountUtil.release(msg); } }); } @@ -109,7 +109,7 @@ public class LocalTransportThreadModelTest { ch.pipeline().write("5"); ch.pipeline().context(h3).write("6"); ch.pipeline().context(h2).write("7"); - ch.pipeline().context(h1).write("8").sync(); + ch.pipeline().context(h1).write("8").flush().sync(); ch.close().sync(); @@ -224,6 +224,7 @@ public class LocalTransportThreadModelTest { } @Test(timeout = 30000) + @Ignore public void testConcurrentMessageBufferAccess() throws Throwable { EventLoopGroup l = new LocalEventLoopGroup(4, new DefaultThreadFactory("l")); EventExecutorGroup e1 = new DefaultEventExecutorGroup(4, new DefaultThreadFactory("e1")); @@ -264,11 +265,9 @@ public class LocalTransportThreadModelTest { ch.eventLoop().execute(new Runnable() { @Override public void run() { - MessageList msgs = MessageList.newInstance(end - start); for (int j = start; j < end; j ++) { - msgs.add(Integer.valueOf(j)); + ch.pipeline().fireMessageReceived(Integer.valueOf(j)); } - ch.pipeline().fireMessageReceived(msgs); } }); } @@ -304,12 +303,10 @@ public class LocalTransportThreadModelTest { ch.pipeline().context(h6).executor().execute(new Runnable() { @Override public void run() { - MessageList msgs = MessageList.newInstance(end - start); for (int j = start; j < end; j ++) { - msgs.add(Integer.valueOf(j)); + ch.write(Integer.valueOf(j)); } - - ch.pipeline().write(msgs); + ch.flush(); } }); } @@ -369,15 +366,15 @@ public class LocalTransportThreadModelTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { inboundThreadNames.add(Thread.currentThread().getName()); - ctx.fireMessageReceived(msgs); + ctx.fireMessageReceived(msg); } @Override - public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise future) throws Exception { + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { outboundThreadNames.add(Thread.currentThread().getName()); - ctx.write(msgs, future); + ctx.write(msg); } @Override @@ -392,8 +389,7 @@ public class LocalTransportThreadModelTest { /** * Converts integers into a binary stream. */ - private static class MessageForwarder1 - extends ChannelDuplexHandler { + private static class MessageForwarder1 extends ChannelDuplexHandler { private final AtomicReference exception = new AtomicReference(); private volatile int inCnt; @@ -401,7 +397,7 @@ public class LocalTransportThreadModelTest { private volatile Thread t; @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { Thread t = this.t; if (t == null) { this.t = Thread.currentThread(); @@ -409,45 +405,33 @@ public class LocalTransportThreadModelTest { Assert.assertSame(t, Thread.currentThread()); } - ByteBuf out = ctx.alloc().buffer(msgs.size() * 4); - - for (int i = 0; i < msgs.size(); i ++) { - int m = ((Integer) msgs.get(i)).intValue(); - int expected = inCnt ++; - Assert.assertEquals(expected, m); - out.writeInt(m); - } + ByteBuf out = ctx.alloc().buffer(4); + int m = ((Integer) msg).intValue(); + int expected = inCnt ++; + Assert.assertEquals(expected, m); + out.writeInt(m); ctx.fireMessageReceived(out); } @Override - public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise future) throws Exception { + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { Assert.assertSame(t, Thread.currentThread()); // Don't let the write request go to the server-side channel - just swallow. boolean swallow = this == ctx.pipeline().first(); - for (int i = 0; i < msgs.size(); i ++) { - ByteBuf m = (ByteBuf) msgs.get(i); - int count = m.readableBytes() / 4; - MessageList out = MessageList.newInstance(count); - for (int j = 0; j < count; j ++) { - int actual = m.readInt(); - int expected = outCnt ++; - Assert.assertEquals(expected, actual); - if (!swallow) { - out.add(actual); - } - } - m.release(); - - if (swallow) { - future.setSuccess(); - } else { - ctx.write(out); + ByteBuf m = (ByteBuf) msg; + int count = m.readableBytes() / 4; + for (int j = 0; j < count; j ++) { + int actual = m.readInt(); + int expected = outCnt ++; + Assert.assertEquals(expected, actual); + if (!swallow) { + ctx.write(actual); } } + m.release(); } @Override @@ -470,7 +454,7 @@ public class LocalTransportThreadModelTest { private volatile Thread t; @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { Thread t = this.t; if (t == null) { this.t = Thread.currentThread(); @@ -478,33 +462,26 @@ public class LocalTransportThreadModelTest { Assert.assertSame(t, Thread.currentThread()); } - for (int i = 0; i < msgs.size(); i ++) { - ByteBuf m = (ByteBuf) msgs.get(i); - int count = m.readableBytes() / 4; - MessageList out = MessageList.newInstance(count); - for (int j = 0; j < count; j ++) { - int actual = m.readInt(); - int expected = inCnt ++; - Assert.assertEquals(expected, actual); - out.add(actual); - } - m.release(); - ctx.fireMessageReceived(out); + ByteBuf m = (ByteBuf) msg; + int count = m.readableBytes() / 4; + for (int j = 0; j < count; j ++) { + int actual = m.readInt(); + int expected = inCnt ++; + Assert.assertEquals(expected, actual); + ctx.fireMessageReceived(actual); } + m.release(); } @Override - public void write( - ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { Assert.assertSame(t, Thread.currentThread()); - ByteBuf out = ctx.alloc().buffer(msgs.size() * 4); - for (int i = 0; i < msgs.size(); i ++) { - int m = (Integer) msgs.get(i); - int expected = outCnt ++; - Assert.assertEquals(expected, m); - out.writeInt(m); - } + ByteBuf out = ctx.alloc().buffer(4); + int m = (Integer) msg; + int expected = outCnt ++; + Assert.assertEquals(expected, m); + out.writeInt(m); ctx.write(out); } @@ -529,7 +506,7 @@ public class LocalTransportThreadModelTest { private volatile Thread t; @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { Thread t = this.t; if (t == null) { this.t = Thread.currentThread(); @@ -537,25 +514,22 @@ public class LocalTransportThreadModelTest { Assert.assertSame(t, Thread.currentThread()); } - for (int i = 0; i < msgs.size(); i ++) { - int actual = (Integer) msgs.get(i); - int expected = inCnt ++; - Assert.assertEquals(expected, actual); - } - ctx.fireMessageReceived(msgs); + int actual = (Integer) msg; + int expected = inCnt ++; + Assert.assertEquals(expected, actual); + + ctx.fireMessageReceived(msg); } @Override - public void write( - ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { Assert.assertSame(t, Thread.currentThread()); - for (int i = 0; i < msgs.size(); i ++) { - int actual = (Integer) msgs.get(i); - int expected = outCnt ++; - Assert.assertEquals(expected, actual); - } - ctx.write(msgs, promise); + int actual = (Integer) msg; + int expected = outCnt ++; + Assert.assertEquals(expected, actual); + + ctx.write(msg); } @Override @@ -578,7 +552,7 @@ public class LocalTransportThreadModelTest { private volatile Thread t; @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { Thread t = this.t; if (t == null) { this.t = Thread.currentThread(); @@ -586,24 +560,20 @@ public class LocalTransportThreadModelTest { Assert.assertSame(t, Thread.currentThread()); } - for (int i = 0; i < msgs.size(); i ++) { - int actual = (Integer) msgs.get(i); - int expected = inCnt ++; - Assert.assertEquals(expected, actual); - } + int actual = (Integer) msg; + int expected = inCnt ++; + Assert.assertEquals(expected, actual); } @Override public void write( - ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + ChannelHandlerContext ctx, Object msg) throws Exception { Assert.assertSame(t, Thread.currentThread()); - for (int i = 0; i < msgs.size(); i ++) { - int actual = (Integer) msgs.get(i); - int expected = outCnt ++; - Assert.assertEquals(expected, actual); - } - ctx.write(msgs, promise); + int actual = (Integer) msg; + int expected = outCnt ++; + Assert.assertEquals(expected, actual); + ctx.write(msg); } @Override diff --git a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest2.java b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest2.java index aea62af6fd..dd38925418 100644 --- a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest2.java +++ b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest2.java @@ -15,6 +15,7 @@ */ package io.netty.channel.local; +import static org.junit.Assert.assertEquals; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; @@ -22,12 +23,11 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.MessageList; -import org.junit.Test; +import io.netty.util.ReferenceCountUtil; import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.Assert.*; +import org.junit.Test; public class LocalTransportThreadModelTest2 { @@ -107,14 +107,15 @@ public class LocalTransportThreadModelTest2 { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { for (int i = 0; i < messageCountPerRun; i ++) { - lastWriteFuture = ctx.channel().write(name + ' ' + i); + ctx.channel().write(name + ' ' + i); } + lastWriteFuture = ctx.channel().flush(); } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { - count.addAndGet(msgs.size()); - msgs.releaseAllAndRecycle(); + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + count.incrementAndGet(); + ReferenceCountUtil.release(msg); } } } diff --git a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest3.java b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest3.java index 21206ad6dc..540dbe9801 100644 --- a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest3.java +++ b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest3.java @@ -22,17 +22,11 @@ import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoopGroup; -import io.netty.channel.MessageList; +import io.netty.util.ReferenceCountUtil; import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.DefaultThreadFactory; import io.netty.util.concurrent.EventExecutorGroup; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; import java.util.Deque; import java.util.LinkedList; @@ -41,6 +35,12 @@ import java.util.Random; import java.util.UUID; import java.util.concurrent.ConcurrentLinkedDeque; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + public class LocalTransportThreadModelTest3 { enum EventType { @@ -71,9 +71,9 @@ public class LocalTransportThreadModelTest3 { public void initChannel(LocalChannel ch) throws Exception { ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) { + public void messageReceived(ChannelHandlerContext ctx, Object msg) { // Discard - msgs.releaseAllAndRecycle(); + ReferenceCountUtil.release(msg); } }); } @@ -303,15 +303,14 @@ public class LocalTransportThreadModelTest3 { } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { if (inbound) { events.add(EventType.MESSAGE_RECEIVED); } } @Override - public void write( - ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + public void write(ChannelHandlerContext ctx, Object msg) throws Exception { if (!inbound) { events.add(EventType.WRITE); } diff --git a/transport/src/test/java/io/netty/channel/nio/NioDatagramChannelTest.java b/transport/src/test/java/io/netty/channel/nio/NioDatagramChannelTest.java index f3131e5e64..fd1e4ef226 100644 --- a/transport/src/test/java/io/netty/channel/nio/NioDatagramChannelTest.java +++ b/transport/src/test/java/io/netty/channel/nio/NioDatagramChannelTest.java @@ -19,16 +19,17 @@ import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelOption; -import io.netty.channel.MessageList; import io.netty.channel.group.DefaultChannelGroup; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel; +import io.netty.util.ReferenceCountUtil; import io.netty.util.concurrent.GlobalEventExecutor; -import org.junit.Assert; -import org.junit.Test; import java.net.InetSocketAddress; +import org.junit.Assert; +import org.junit.Test; + public class NioDatagramChannelTest { @@ -46,9 +47,9 @@ public class NioDatagramChannelTest { .option(ChannelOption.SO_BROADCAST, true) .handler(new ChannelInboundHandlerAdapter() { @Override - public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) { + public void messageReceived(ChannelHandlerContext ctx, Object msg) { // Discard - msgs.releaseAllAndRecycle(); + ReferenceCountUtil.release(msg); } }); DatagramChannel datagramChannel = (DatagramChannel) udpBootstrap