From 1eea3cf5032b15347b4868b74afcc3fee46a32c3 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 8 Sep 2012 20:20:02 +0200 Subject: [PATCH 01/24] Support unbindAddress and bindAddress for SCTP. See #560 --- .../io/netty/channel/socket/SctpChannel.java | 15 ++++- .../channel/socket/nio/NioSctpChannel.java | 59 +++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/socket/SctpChannel.java b/transport/src/main/java/io/netty/channel/socket/SctpChannel.java index 0fab200cdc..89015cb443 100644 --- a/transport/src/main/java/io/netty/channel/socket/SctpChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/SctpChannel.java @@ -19,9 +19,7 @@ import com.sun.nio.sctp.Association; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; -import java.io.IOException; import java.net.InetAddress; -import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Set; @@ -72,10 +70,21 @@ public interface SctpChannel extends Channel { @Override SocketAddress remoteAddress(); - /** * Return all remote addresses of the SCTP server channel. * Please note that, it will return more than one address if the remote is using multi-homing. */ Set allRemoteAddresses(); + + /** + * Bind a address to the already bound channel to enable multi-homing. + * The Channel bust be bound and yet to be connected. + */ + ChannelFuture bindAddress(InetAddress localAddress); + + /** + * Unbind the address from channel's multi-homing address list. + * The address should be added already in multi-homing address list. + */ + ChannelFuture unbindAddress(InetAddress localAddress); } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioSctpChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioSctpChannel.java index b4e4e72391..9a62b0c543 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioSctpChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioSctpChannel.java @@ -25,6 +25,7 @@ import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelException; +import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.socket.DefaultSctpChannelConfig; import io.netty.channel.socket.SctpChannelConfig; @@ -34,6 +35,7 @@ import io.netty.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; import java.io.IOException; +import java.net.InetAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; @@ -48,6 +50,7 @@ public class NioSctpChannel extends AbstractNioMessageChannel implements io.nett private final SctpChannelConfig config; + @SuppressWarnings("rawtypes") private final NotificationHandler notificationHandler; private static SctpChannel newSctpChannel() { @@ -216,6 +219,7 @@ public class NioSctpChannel extends AbstractNioMessageChannel implements io.nett javaChannel().close(); } + @SuppressWarnings("unchecked") @Override protected int doReadMessages(MessageBuf buf) throws Exception { SctpChannel ch = javaChannel(); @@ -277,4 +281,59 @@ public class NioSctpChannel extends AbstractNioMessageChannel implements io.nett } return 1; } + + @Override + public ChannelFuture bindAddress(InetAddress localAddress) { + ChannelFuture future = newFuture(); + doBindAddress(localAddress, future); + return future; + } + + void doBindAddress(final InetAddress localAddress, final ChannelFuture future) { + if (eventLoop().inEventLoop()) { + try { + javaChannel().bindAddress(localAddress); + future.setSuccess(); + // TODO: Do we want to fire an event ? + } catch (Throwable t) { + future.setFailure(t); + pipeline().fireExceptionCaught(t); + } + } else { + eventLoop().execute(new Runnable() { + @Override + public void run() { + doBindAddress(localAddress, future); + } + }); + } + } + + @Override + public ChannelFuture unbindAddress(InetAddress localAddress) { + ChannelFuture future = newFuture(); + doUnbindAddress(localAddress, future); + return future; + } + + void doUnbindAddress(final InetAddress localAddress, final ChannelFuture future) { + if (eventLoop().inEventLoop()) { + try { + javaChannel().unbindAddress(localAddress); + future.setSuccess(); + // TODO: Do we want to fire an event ? + } catch (Throwable t) { + future.setFailure(t); + pipeline().fireExceptionCaught(t); + } + } else { + eventLoop().execute(new Runnable() { + @Override + public void run() { + doUnbindAddress(localAddress, future); + } + }); + } + } + } From 09ad4faba98e5f7276a658a04b314ce26aec348d Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 8 Sep 2012 20:20:45 +0200 Subject: [PATCH 02/24] Fix README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 814f6589e0..4e9e5ed0ed 100644 --- a/README.md +++ b/README.md @@ -39,5 +39,5 @@ Netty is an asynchronous event-driven network application framework for rapid de - __master__ branch contains code for Netty 4.x -- __3.2__ branch contains code for Netty 3.x +- __3__ branch contains code for Netty 3.x From 90d15c46272f4b83a35c2640ce1169a5fb8ff024 Mon Sep 17 00:00:00 2001 From: Jeff Smick Date: Sat, 8 Sep 2012 20:43:06 -0700 Subject: [PATCH 03/24] Upgrade connection after completed response Channel handlers above the HttpEncoder may delay the repsonse being written to the socket. We need to wait for the response to complete before upgrading the pipeline. --- .../WebSocketServerHandshaker00.java | 8 +++++++- .../WebSocketServerHandshaker08.java | 18 +++++++++++------- .../WebSocketServerHandshaker13.java | 18 +++++++++++------- 3 files changed, 29 insertions(+), 15 deletions(-) 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 d76fa6a995..0c85265989 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 @@ -22,6 +22,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.HttpChunkAggregator; @@ -180,7 +181,12 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { ChannelFuture future = channel.write(res); - p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket00FrameEncoder()); + future.addListener(new ChannelFutureListener() { + public void operationComplete(ChannelFuture future) { + ChannelPipeline p = future.channel().pipeline(); + p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket00FrameEncoder()); + } + }); return future; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java index f4be1b93a0..c56fa98028 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java @@ -147,14 +147,18 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { ChannelFuture future = channel.write(res); // Upgrade the connection and send the handshake response. - ChannelPipeline p = channel.pipeline(); - if (p.get(HttpChunkAggregator.class) != null) { - p.remove(HttpChunkAggregator.class); - } + future.addListener(new ChannelFutureListener() { + public void operationComplete(ChannelFuture future) { + ChannelPipeline p = future.channel().pipeline(); + if (p.get(HttpChunkAggregator.class) != null) { + p.remove(HttpChunkAggregator.class); + } - p.replace(HttpRequestDecoder.class, "wsdecoder", - new WebSocket08FrameDecoder(true, allowExtensions, getMaxFramePayloadLength())); - p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket08FrameEncoder(false)); + p.replace(HttpRequestDecoder.class, "wsdecoder", + new WebSocket08FrameDecoder(true, allowExtensions, getMaxFramePayloadLength())); + p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket08FrameEncoder(false)); + } + }); return future; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java index 95737bd23e..353bba0e09 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java @@ -147,14 +147,18 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { ChannelFuture future = channel.write(res); // Upgrade the connection and send the handshake response. - ChannelPipeline p = channel.pipeline(); - if (p.get(HttpChunkAggregator.class) != null) { - p.remove(HttpChunkAggregator.class); - } + future.addListener(new ChannelFutureListener() { + public void operationComplete(ChannelFuture future) { + ChannelPipeline p = future.channel().pipeline(); + if (p.get(HttpChunkAggregator.class) != null) { + p.remove(HttpChunkAggregator.class); + } - p.replace(HttpRequestDecoder.class, "wsdecoder", - new WebSocket13FrameDecoder(true, allowExtensions, getMaxFramePayloadLength())); - p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket13FrameEncoder(false)); + p.replace(HttpRequestDecoder.class, "wsdecoder", + new WebSocket13FrameDecoder(true, allowExtensions, getMaxFramePayloadLength())); + p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket13FrameEncoder(false)); + } + }); return future; } From c6436ad470bca66ed729886944d04f5152c1c51e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 9 Sep 2012 08:21:32 +0200 Subject: [PATCH 04/24] Also move the replacement of the decoder to the ChannelFutureListener for the websocket upgrade. See #586 --- .../websocketx/WebSocketServerHandshaker00.java | 14 +++++++------- .../websocketx/WebSocketServerHandshaker08.java | 1 + .../websocketx/WebSocketServerHandshaker13.java | 1 + 3 files changed, 9 insertions(+), 7 deletions(-) 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 0c85265989..d677e4fed3 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 @@ -172,18 +172,18 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { } // Upgrade the connection and send the handshake response. - ChannelPipeline p = channel.pipeline(); - if (p.get(HttpChunkAggregator.class) != null) { - p.remove(HttpChunkAggregator.class); - } - p.replace(HttpRequestDecoder.class, "wsdecoder", - new WebSocket00FrameDecoder(getMaxFramePayloadLength())); - ChannelFuture future = channel.write(res); future.addListener(new ChannelFutureListener() { + @Override public void operationComplete(ChannelFuture future) { ChannelPipeline p = future.channel().pipeline(); + if (p.get(HttpChunkAggregator.class) != null) { + p.remove(HttpChunkAggregator.class); + } + p.replace(HttpRequestDecoder.class, "wsdecoder", + new WebSocket00FrameDecoder(getMaxFramePayloadLength())); + p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket00FrameEncoder()); } }); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java index c56fa98028..3cd32e7f82 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java @@ -148,6 +148,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { // Upgrade the connection and send the handshake response. future.addListener(new ChannelFutureListener() { + @Override public void operationComplete(ChannelFuture future) { ChannelPipeline p = future.channel().pipeline(); if (p.get(HttpChunkAggregator.class) != null) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java index 353bba0e09..cad8aba9ce 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java @@ -148,6 +148,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { // Upgrade the connection and send the handshake response. future.addListener(new ChannelFutureListener() { + @Override public void operationComplete(ChannelFuture future) { ChannelPipeline p = future.channel().pipeline(); if (p.get(HttpChunkAggregator.class) != null) { From 150e8b4105888affcd9b9899d85d437dfd7ed40d Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Tue, 24 Jul 2012 14:38:39 +0200 Subject: [PATCH 05/24] WebSocket enhancements - Refactoring and adding suggestions from Norman and Vibul. --- .../WebSocketServerHandshakeHandler.java | 99 +++++++++++ .../WebSocketServerProtocolHandler.java | 112 ++++++++++++ .../websocketx/WebSocketRequestBuilder.java | 131 ++++++++++++++ .../WebSocketServerProtocolHandlerTest.java | 164 ++++++++++++++++++ 4 files changed, 506 insertions(+) create mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java create mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.java create mode 100644 codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketRequestBuilder.java create mode 100644 codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandlerTest.java diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java new file mode 100644 index 0000000000..972615e6df --- /dev/null +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java @@ -0,0 +1,99 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.http.websocketx; + +import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive; +import static io.netty.handler.codec.http.HttpMethod.GET; +import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.handler.codec.http.DefaultHttpResponse; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.logging.InternalLogger; +import io.netty.logging.InternalLoggerFactory; + +/** + * Handles the HTTP handshake (the HTTP Upgrade request) + */ +class WebSocketServerHandshakeHandler extends ChannelInboundMessageHandlerAdapter { + + private static final InternalLogger logger = + InternalLoggerFactory.getInstance(WebSocketServerHandshakeHandler.class); + private final String websocketPath; + private final String subprotocols; + private final boolean allowExtensions; + + public WebSocketServerHandshakeHandler(String websocketPath, String subprotocols, boolean allowExtensions) { + this.websocketPath = websocketPath; + this.subprotocols = subprotocols; + this.allowExtensions = allowExtensions; + } + + @Override + public void messageReceived(final ChannelHandlerContext ctx, HttpRequest req) throws Exception { + if (req.getMethod() != GET) { + sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN)); + return; + } + + final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( + getWebSocketLocation(req, websocketPath), subprotocols, allowExtensions); + final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req); + if (handshaker == null) { + wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel()); + } else { + try { + 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()); + } + } + }); + WebSocketServerProtocolHandler.setHandshaker(ctx, handshaker); + ctx.pipeline().replace(this, "WS403Responder", + WebSocketServerProtocolHandler.forbiddenHttpRequestResponder()); + } catch (WebSocketHandshakeException e) { + ctx.fireExceptionCaught(e); + } + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + logger.error("Exception Caught", cause); + ctx.close(); + } + + private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) { + ChannelFuture f = ctx.channel().write(res); + if (!isKeepAlive(req) || res.getStatus().getCode() != 200) { + f.addListener(ChannelFutureListener.CLOSE); + } + } + + private static String getWebSocketLocation(HttpRequest req, String path) { + return "ws://" + req.getHeader(HttpHeaders.Names.HOST) + path; + } + +} 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 new file mode 100644 index 0000000000..acdacbc6db --- /dev/null +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.java @@ -0,0 +1,112 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.http.websocketx; + +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.handler.codec.http.DefaultHttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.util.AttributeKey; + +/** + * Handles WebSocket control frames (Close, Ping, Pong) and data frames (Text and Binary) are passed + * to the next handler in the pipeline. + */ +public class WebSocketServerProtocolHandler extends ChannelInboundMessageHandlerAdapter { + + private static final AttributeKey HANDSHAKER_ATTR_KEY = + new AttributeKey(WebSocketServerHandshaker.class.getName()); + + private final String websocketPath; + private final String subprotocols; + private final boolean allowExtensions; + + public WebSocketServerProtocolHandler(String websocketPath) { + this(websocketPath, null, false); + } + + public WebSocketServerProtocolHandler(String websocketPath, String subprotocols) { + this(websocketPath, subprotocols, false); + } + + public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, boolean allowExtensions) { + this.websocketPath = websocketPath; + this.subprotocols = subprotocols; + this.allowExtensions = allowExtensions; + } + + @Override + public void afterAdd(ChannelHandlerContext ctx) { + // Add the WebSocketHandshakeHandler before this one. + ctx.pipeline().addBefore(ctx.name(), WebSocketServerHandshakeHandler.class.getName(), + new WebSocketServerHandshakeHandler(websocketPath, subprotocols, allowExtensions)); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { + if (frame instanceof CloseWebSocketFrame) { + WebSocketServerHandshaker handshaker = WebSocketServerProtocolHandler.getHandshaker(ctx); + handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame); + return; + } else if (frame instanceof PingWebSocketFrame) { + ctx.channel().write(new PongWebSocketFrame(frame.getBinaryData())); + return; + } + + ctx.nextInboundMessageBuffer().add(frame); + ctx.fireInboundBufferUpdated(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + try { + if (cause instanceof WebSocketHandshakeException) { + DefaultHttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.BAD_REQUEST); + response.setContent(Unpooled.wrappedBuffer(cause.getMessage().getBytes())); + ctx.channel().write(response); + } + } finally { + ctx.close(); + } + } + + static WebSocketServerHandshaker getHandshaker(ChannelHandlerContext ctx) { + return ctx.attr(HANDSHAKER_ATTR_KEY).get(); + } + + static void setHandshaker(ChannelHandlerContext ctx, WebSocketServerHandshaker handshaker) { + ctx.attr(HANDSHAKER_ATTR_KEY).set(handshaker); + } + + static ChannelHandler forbiddenHttpRequestResponder() { + return new ChannelInboundMessageHandlerAdapter() { + @Override + public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + if (!(msg instanceof WebSocketFrame)) { + DefaultHttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN); + ctx.channel().write(response); + } else { + ctx.nextInboundMessageBuffer().add(msg); + ctx.fireInboundBufferUpdated(); + } + } + }; + } + +} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketRequestBuilder.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketRequestBuilder.java new file mode 100644 index 0000000000..0bec4da3b5 --- /dev/null +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketRequestBuilder.java @@ -0,0 +1,131 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, version + * 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package io.netty.handler.codec.http.websocketx; + +import static io.netty.handler.codec.http.HttpHeaders.Values.WEBSOCKET; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; +import io.netty.handler.codec.http.DefaultHttpRequest; +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.HttpHeaders.Names; + +public class WebSocketRequestBuilder { + + private HttpVersion httpVersion; + private HttpMethod method; + private String uri; + private String host; + private String upgrade; + private String connection; + private String key; + private String origin; + private WebSocketVersion version; + + public WebSocketRequestBuilder httpVersion(HttpVersion httpVersion) { + this.httpVersion = httpVersion; + return this; + } + + public WebSocketRequestBuilder method(HttpMethod method) { + this.method = method; + return this; + } + + public WebSocketRequestBuilder uri(String uri) { + this.uri = uri; + return this; + } + + public WebSocketRequestBuilder host(String host) { + this.host = host; + return this; + } + + public WebSocketRequestBuilder upgrade(String upgrade) { + this.upgrade = upgrade; + return this; + } + + public WebSocketRequestBuilder connection(String connection) { + this.connection = connection; + return this; + } + + public WebSocketRequestBuilder key(String key) { + this.key = key; + return this; + } + + public WebSocketRequestBuilder origin(String origin) { + this.origin = origin; + return this; + } + + public WebSocketRequestBuilder version13() { + this.version = WebSocketVersion.V13; + return this; + } + + public WebSocketRequestBuilder version8() { + this.version = WebSocketVersion.V08; + return this; + } + + public WebSocketRequestBuilder version00() { + this.version = null; + return this; + } + + public WebSocketRequestBuilder noVersion() { + return this; + } + + public HttpRequest build() { + HttpRequest req = new DefaultHttpRequest(httpVersion, method, uri); + if (host != null) { + req.setHeader(Names.HOST, host); + } + if (upgrade != null) { + req.setHeader(Names.UPGRADE, upgrade); + } + if (connection != null) { + req.setHeader(Names.CONNECTION, connection); + } + if (key != null) { + req.setHeader(Names.SEC_WEBSOCKET_KEY, key); + } + if (origin != null) { + req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, origin); + } + if (version != null) { + req.setHeader(Names.SEC_WEBSOCKET_VERSION, version.toHttpHeaderValue()); + } + return req; + } + + public static HttpRequest sucessful() { + return new WebSocketRequestBuilder().httpVersion(HTTP_1_1) + .method(HttpMethod.GET) + .uri("/test") + .host("server.example.com") + .upgrade(WEBSOCKET.toLowerCase()) + .key("dGhlIHNhbXBsZSBub25jZQ==") + .origin("http://example.com") + .version13() + .build(); + } +} 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 new file mode 100644 index 0000000000..16e15a9f34 --- /dev/null +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandlerTest.java @@ -0,0 +1,164 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, version + * 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package io.netty.handler.codec.http.websocketx; + +import static io.netty.handler.codec.http.HttpHeaders.Values.WEBSOCKET; +import static io.netty.handler.codec.http.HttpResponseStatus.SWITCHING_PROTOCOLS; +import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import io.netty.buffer.MessageBuf; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelOutboundMessageHandlerAdapter; +import io.netty.channel.embedded.EmbeddedMessageChannel; +import io.netty.handler.codec.http.DefaultHttpRequest; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseEncoder; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; + +import org.junit.Test; + +public class WebSocketServerProtocolHandlerTest { + + @Test + public void testHttpUpgradeRequest() throws Exception { + EmbeddedMessageChannel ch = createChannel(new MockOutboundHandler()); + ChannelHandlerContext handshakerCtx = ch.pipeline().context(WebSocketServerHandshakeHandler.class); + + writeUpgradeRequest(ch); + + assertEquals(SWITCHING_PROTOCOLS, ((HttpResponse) ch.outboundMessageBuffer().poll()).getStatus()); + assertNotNull(WebSocketServerProtocolHandler.getHandshaker(handshakerCtx)); + } + + @Test + public void testSubsequentHttpRequestsAfterUpgradeShouldReturn403() throws Exception { + EmbeddedMessageChannel ch = createChannel(new MockOutboundHandler()); + + writeUpgradeRequest(ch); + assertEquals(SWITCHING_PROTOCOLS, ((HttpResponse) ch.outboundMessageBuffer().poll()).getStatus()); + + ch.writeInbound(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/test")); + assertEquals(FORBIDDEN, ((HttpResponse) ch.outboundMessageBuffer().poll()).getStatus()); + } + + @Test + public void testHttpUpgradeRequestInvalidUpgradeHeader() { + EmbeddedMessageChannel ch = createChannel(); + HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1) + .method(HttpMethod.GET) + .uri("/test") + .connection("Upgrade") + .version00() + .upgrade("BogusSocket") + .build(); + + ch.writeInbound(httpRequest); + + HttpResponse response = getHttpResponse(ch); + assertEquals(HttpResponseStatus.BAD_REQUEST, response.getStatus()); + assertEquals("not a WebSocket handshake request: missing upgrade", getResponseMessage(response)); + + } + + @Test + public void testHttpUpgradeRequestMissingWSKeyHeader() { + EmbeddedMessageChannel ch = createChannel(); + HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1) + .method(HttpMethod.GET) + .uri("/test") + .key(null) + .connection("Upgrade") + .upgrade(WEBSOCKET.toLowerCase()) + .version13() + .build(); + + ch.writeInbound(httpRequest); + + HttpResponse response = getHttpResponse(ch); + assertEquals(HttpResponseStatus.BAD_REQUEST, response.getStatus()); + assertEquals("not a WebSocket request: missing key", getResponseMessage(response)); + } + + @Test + public void testHandleTextFrame() { + CustomTextFrameHandler customTextFrameHandler = new CustomTextFrameHandler(); + EmbeddedMessageChannel ch = createChannel(customTextFrameHandler); + writeUpgradeRequest(ch); + // Removing the HttpRequestDecoder as we are writing a TextWebSocketFrame so decoding is not neccessary. + ch.pipeline().remove(HttpRequestDecoder.class); + + ch.writeInbound(new TextWebSocketFrame("payload")); + + assertEquals("processed: payload", customTextFrameHandler.getContent()); + } + + private EmbeddedMessageChannel createChannel() { + return createChannel(null); + } + + private EmbeddedMessageChannel createChannel(ChannelHandler handler) { + return new EmbeddedMessageChannel( + new WebSocketServerProtocolHandler("/test", null, false), + new HttpRequestDecoder(), + new HttpResponseEncoder(), + new MockOutboundHandler(), + handler); + } + + private void writeUpgradeRequest(EmbeddedMessageChannel ch) { + ch.writeInbound(WebSocketRequestBuilder.sucessful()); + } + + private String getResponseMessage(HttpResponse response) { + return new String(response.getContent().array()); + } + + private HttpResponse getHttpResponse(EmbeddedMessageChannel ch) { + MessageBuf outbound = ch.pipeline().context(MockOutboundHandler.class).outboundMessageBuffer(); + return (HttpResponse) outbound.poll(); + } + + private static class MockOutboundHandler extends ChannelOutboundMessageHandlerAdapter { + @Override + public void flush(ChannelHandlerContext ctx, ChannelFuture future) throws Exception { + //NoOp + } + } + + private static class CustomTextFrameHandler extends ChannelInboundMessageHandlerAdapter { + private String content; + + @Override + public void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { + content = "processed: " + msg.getText(); + } + + public String getContent() { + return content; + } + + } + +} From 416c026efe5c6c584e7fa4bf2836208162bb151c Mon Sep 17 00:00:00 2001 From: Jeff Smick Date: Sun, 9 Sep 2012 08:54:46 -0700 Subject: [PATCH 06/24] Upgrade client connection after completed response --- .../http/websocketx/WebSocketClientHandshaker00.java | 10 +++++++++- .../http/websocketx/WebSocketClientHandshaker08.java | 10 +++++++++- .../http/websocketx/WebSocketClientHandshaker13.java | 10 +++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java index a3ca41229f..fdf2a17f2a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java @@ -18,6 +18,8 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Values; @@ -168,7 +170,13 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { ChannelFuture future = channel.write(request); - channel.pipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket00FrameEncoder()); + future.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) { + ChannelPipeline p = future.channel().pipeline(); + p.replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket00FrameEncoder()); + } + }); return future; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java index bb1a33c29c..05bff21925 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java @@ -17,6 +17,8 @@ package io.netty.handler.codec.http.websocketx; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Values; @@ -147,7 +149,13 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { ChannelFuture future = channel.write(request); - channel.pipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket08FrameEncoder(true)); + future.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) { + ChannelPipeline p = future.channel().pipeline(); + p.replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket08FrameEncoder(true)); + } + }); return future; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java index 68c8446fd5..a5a3668eaa 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java @@ -17,6 +17,8 @@ package io.netty.handler.codec.http.websocketx; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Values; @@ -147,7 +149,13 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { ChannelFuture future = channel.write(request); - channel.pipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket13FrameEncoder(true)); + future.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) { + ChannelPipeline p = future.channel().pipeline(); + p.replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket13FrameEncoder(true)); + } + }); return future; } From bd557a6330fc542f4344f01adb2fb12a6a1ceffc Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 9 Sep 2012 19:35:56 +0200 Subject: [PATCH 07/24] Remove not necessary try / catch block. See #587 --- .../WebSocketServerHandshakeHandler.java | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java index 972615e6df..0fa6de826f 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java @@ -60,22 +60,18 @@ class WebSocketServerHandshakeHandler extends ChannelInboundMessageHandlerAdapte if (handshaker == null) { wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel()); } else { - try { - 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()); - } + 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()); } - }); - WebSocketServerProtocolHandler.setHandshaker(ctx, handshaker); - ctx.pipeline().replace(this, "WS403Responder", - WebSocketServerProtocolHandler.forbiddenHttpRequestResponder()); - } catch (WebSocketHandshakeException e) { - ctx.fireExceptionCaught(e); - } + } + }); + WebSocketServerProtocolHandler.setHandshaker(ctx, handshaker); + ctx.pipeline().replace(this, "WS403Responder", + WebSocketServerProtocolHandler.forbiddenHttpRequestResponder()); } } From 4353aa679491f7e7e30f107176bb054ca23b499e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 9 Sep 2012 19:48:53 +0200 Subject: [PATCH 08/24] Use Secure websockets if SslHandler is present in the ChannelPipeline. See #587 --- codec-http/pom.xml | 5 +++++ .../websocketx/WebSocketServerHandshakeHandler.java | 13 ++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/codec-http/pom.xml b/codec-http/pom.xml index c1ae7cef75..fee59284b7 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -34,6 +34,11 @@ netty-codec ${project.version} + + ${project.groupId} + netty-handler + ${project.version} + diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java index 0fa6de826f..ad14917bb1 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java @@ -23,10 +23,12 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.ssl.SslHandler; import io.netty.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; @@ -55,7 +57,7 @@ class WebSocketServerHandshakeHandler extends ChannelInboundMessageHandlerAdapte } final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( - getWebSocketLocation(req, websocketPath), subprotocols, allowExtensions); + getWebSocketLocation(ctx.pipeline(), req, websocketPath), subprotocols, allowExtensions); final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req); if (handshaker == null) { wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel()); @@ -88,8 +90,13 @@ class WebSocketServerHandshakeHandler extends ChannelInboundMessageHandlerAdapte } } - private static String getWebSocketLocation(HttpRequest req, String path) { - return "ws://" + req.getHeader(HttpHeaders.Names.HOST) + path; + private static String getWebSocketLocation(ChannelPipeline cp, HttpRequest req, String path) { + String protocol = "ws"; + if (cp.get(SslHandler.class) != null) { + // SSL in use so use Secure WebSockets + protocol = "wss"; + } + return protocol + "://" + req.getHeader(HttpHeaders.Names.HOST) + path; } } From 6e3919246226b2c05ee8e2b263e30482c51a28fb Mon Sep 17 00:00:00 2001 From: norman Date: Mon, 10 Sep 2012 07:15:59 +0200 Subject: [PATCH 09/24] Rename WebSocketServerHandshakeHandler to WebSocketServerProtocolHnadshakeHandler and also make it public. See #587 --- .../http/websocketx/WebSocketServerProtocolHandler.java | 4 ++-- ...er.java => WebSocketServerProtocolHandshakeHandler.java} | 6 +++--- .../http/websocketx/WebSocketServerProtocolHandlerTest.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) rename codec-http/src/main/java/io/netty/handler/codec/http/websocketx/{WebSocketServerHandshakeHandler.java => WebSocketServerProtocolHandshakeHandler.java} (92%) 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 acdacbc6db..1edc552003 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 @@ -54,8 +54,8 @@ public class WebSocketServerProtocolHandler extends ChannelInboundMessageHandler @Override public void afterAdd(ChannelHandlerContext ctx) { // Add the WebSocketHandshakeHandler before this one. - ctx.pipeline().addBefore(ctx.name(), WebSocketServerHandshakeHandler.class.getName(), - new WebSocketServerHandshakeHandler(websocketPath, subprotocols, allowExtensions)); + ctx.pipeline().addBefore(ctx.name(), WebSocketServerProtocolHandshakeHandler.class.getName(), + new WebSocketServerProtocolHandshakeHandler(websocketPath, subprotocols, allowExtensions)); } @Override diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java similarity index 92% rename from codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java rename to codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java index ad14917bb1..d89f2d62fa 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java @@ -35,15 +35,15 @@ import io.netty.logging.InternalLoggerFactory; /** * Handles the HTTP handshake (the HTTP Upgrade request) */ -class WebSocketServerHandshakeHandler extends ChannelInboundMessageHandlerAdapter { +public class WebSocketServerProtocolHandshakeHandler extends ChannelInboundMessageHandlerAdapter { private static final InternalLogger logger = - InternalLoggerFactory.getInstance(WebSocketServerHandshakeHandler.class); + InternalLoggerFactory.getInstance(WebSocketServerProtocolHandshakeHandler.class); private final String websocketPath; private final String subprotocols; private final boolean allowExtensions; - public WebSocketServerHandshakeHandler(String websocketPath, String subprotocols, boolean allowExtensions) { + public WebSocketServerProtocolHandshakeHandler(String websocketPath, String subprotocols, boolean allowExtensions) { this.websocketPath = websocketPath; this.subprotocols = subprotocols; this.allowExtensions = allowExtensions; 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 16e15a9f34..d75ae96f5e 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 @@ -44,7 +44,7 @@ public class WebSocketServerProtocolHandlerTest { @Test public void testHttpUpgradeRequest() throws Exception { EmbeddedMessageChannel ch = createChannel(new MockOutboundHandler()); - ChannelHandlerContext handshakerCtx = ch.pipeline().context(WebSocketServerHandshakeHandler.class); + ChannelHandlerContext handshakerCtx = ch.pipeline().context(WebSocketServerProtocolHandshakeHandler.class); writeUpgradeRequest(ch); From 6c2053bae57cd09684433ec640708a17b41ce42f Mon Sep 17 00:00:00 2001 From: norman Date: Mon, 10 Sep 2012 07:18:26 +0200 Subject: [PATCH 10/24] Check if WebSocketServerProtocolHandshakeHandler is already in the pipeline before adding it. See #587 --- .../websocketx/WebSocketServerProtocolHandler.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 1edc552003..e94f2567ae 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 @@ -20,6 +20,7 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.util.AttributeKey; @@ -53,9 +54,12 @@ public class WebSocketServerProtocolHandler extends ChannelInboundMessageHandler @Override public void afterAdd(ChannelHandlerContext ctx) { - // Add the WebSocketHandshakeHandler before this one. - ctx.pipeline().addBefore(ctx.name(), WebSocketServerProtocolHandshakeHandler.class.getName(), - new WebSocketServerProtocolHandshakeHandler(websocketPath, subprotocols, allowExtensions)); + ChannelPipeline cp = ctx.pipeline(); + if (cp.get(WebSocketServerProtocolHandshakeHandler.class) == null) { + // Add the WebSocketHandshakeHandler before this one. + ctx.pipeline().addBefore(ctx.name(), WebSocketServerProtocolHandshakeHandler.class.getName(), + new WebSocketServerProtocolHandshakeHandler(websocketPath, subprotocols, allowExtensions)); + } } @Override From 0233a3dd631284cfbc10786c96c1cfa63316b5b3 Mon Sep 17 00:00:00 2001 From: norman Date: Mon, 10 Sep 2012 07:22:35 +0200 Subject: [PATCH 11/24] Close the channel after the response was written to the client on a WebSocketHandshakeException. See #587 --- .../websocketx/WebSocketServerProtocolHandler.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) 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 e94f2567ae..e1bdc80198 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 @@ -17,6 +17,7 @@ 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.ChannelInboundMessageHandlerAdapter; @@ -79,13 +80,11 @@ public class WebSocketServerProtocolHandler extends ChannelInboundMessageHandler @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - try { - if (cause instanceof WebSocketHandshakeException) { - DefaultHttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.BAD_REQUEST); - response.setContent(Unpooled.wrappedBuffer(cause.getMessage().getBytes())); - ctx.channel().write(response); - } - } finally { + if (cause instanceof WebSocketHandshakeException) { + DefaultHttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.BAD_REQUEST); + response.setContent(Unpooled.wrappedBuffer(cause.getMessage().getBytes())); + ctx.channel().write(response).addListener(ChannelFutureListener.CLOSE); + } else { ctx.close(); } } From 8ab1ef143208c582b2f57e9dae66f49a737a03fc Mon Sep 17 00:00:00 2001 From: norman Date: Mon, 10 Sep 2012 12:10:24 +0200 Subject: [PATCH 12/24] Fix checkstyle --- .../websocketx/WebSocketServerProtocolHandshakeHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 d89f2d62fa..9bf620fcd3 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 @@ -43,7 +43,8 @@ public class WebSocketServerProtocolHandshakeHandler extends ChannelInboundMessa private final String subprotocols; private final boolean allowExtensions; - public WebSocketServerProtocolHandshakeHandler(String websocketPath, String subprotocols, boolean allowExtensions) { + public WebSocketServerProtocolHandshakeHandler(String websocketPath, String subprotocols, + boolean allowExtensions) { this.websocketPath = websocketPath; this.subprotocols = subprotocols; this.allowExtensions = allowExtensions; From 4ce85827eda444b7703717c7948f7b247227e655 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 11 Sep 2012 08:31:20 +0200 Subject: [PATCH 13/24] Start to refactor bootstraps to share more code and allow for reuse --- .../java/io/netty/bootstrap/Bootstrap.java | 280 ++++++++---------- .../io/netty/bootstrap/ClientBootstrap.java | 133 +++++++++ .../io/netty/bootstrap/ServerBootstrap.java | 145 +++------ .../java/io/netty/channel/ChannelHandler.java | 4 +- .../local/LocalChannelRegistryTest.java | 8 +- .../local/LocalTransportThreadModelTest.java | 2 +- 6 files changed, 294 insertions(+), 278 deletions(-) create mode 100644 transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java diff --git a/transport/src/main/java/io/netty/bootstrap/Bootstrap.java b/transport/src/main/java/io/netty/bootstrap/Bootstrap.java index 501ec522f2..a4cea1d3b2 100644 --- a/transport/src/main/java/io/netty/bootstrap/Bootstrap.java +++ b/transport/src/main/java/io/netty/bootstrap/Bootstrap.java @@ -13,39 +13,32 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelException; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.logging.InternalLogger; -import io.netty.logging.InternalLoggerFactory; +package io.netty.bootstrap; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.nio.channels.ClosedChannelException; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Map.Entry; -public class Bootstrap { +import io.netty.channel.Channel; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelException; - private static final InternalLogger logger = InternalLoggerFactory.getInstance(Bootstrap.class); - private final Map, Object> options = new LinkedHashMap, Object>(); +public abstract class Bootstrap> { private EventLoopGroup group; - private Channel channel; - private ChannelHandler handler; + private ChannelFactory factory; private SocketAddress localAddress; - private SocketAddress remoteAddress; + private final Map, Object> options = new LinkedHashMap, Object>(); + private ChannelHandler handler; - public Bootstrap group(EventLoopGroup group) { + @SuppressWarnings("unchecked") + public B group(EventLoopGroup group) { if (group == null) { throw new NullPointerException("group"); } @@ -53,21 +46,54 @@ public class Bootstrap { throw new IllegalStateException("group set already"); } this.group = group; - return this; + return (B) this; } - public Bootstrap channel(Channel channel) { - if (channel == null) { - throw new NullPointerException("channel"); + public B channel(Class channelClass) { + if (channelClass == null) { + throw new NullPointerException("channelClass"); } - if (this.channel != null) { - throw new IllegalStateException("channel set already"); - } - this.channel = channel; - return this; + return channelFactory(new BootstrapChannelFactory(channelClass)); } - public Bootstrap option(ChannelOption option, T value) { + @SuppressWarnings("unchecked") + public B channelFactory(ChannelFactory factory) { + if (factory == null) { + throw new NullPointerException("factory"); + } + if (this.factory != null) { + throw new IllegalStateException("factory set already"); + } + this.factory = factory; + return (B) this; + } + + @SuppressWarnings("unchecked") + public B localAddress(SocketAddress localAddress) { + this.localAddress = localAddress; + return (B) this; + } + + @SuppressWarnings("unchecked") + public B localAddress(int port) { + localAddress = new InetSocketAddress(port); + return (B) this; + } + + @SuppressWarnings("unchecked") + public B localAddress(String host, int port) { + localAddress = new InetSocketAddress(host, port); + return (B) this; + } + + @SuppressWarnings("unchecked") + public B localAddress(InetAddress host, int port) { + localAddress = new InetSocketAddress(host, port); + return (B) this; + } + + @SuppressWarnings("unchecked") + public B option(ChannelOption option, T value) { if (option == null) { throw new NullPointerException("option"); } @@ -76,135 +102,50 @@ public class Bootstrap { } else { options.put(option, value); } - return this; + return (B) this; } - public Bootstrap handler(ChannelHandler handler) { - if (handler == null) { - throw new NullPointerException("handler"); + public void shutdown() { + if (group != null) { + group.shutdown(); } - this.handler = handler; - return this; } - public Bootstrap localAddress(SocketAddress localAddress) { - this.localAddress = localAddress; - return this; + protected void validate() { + if (group == null) { + throw new IllegalStateException("group not set"); + } + if (factory == null) { + throw new IllegalStateException("factory not set"); + } + if (handler == null) { + throw new IllegalStateException("handler not set"); + } } - public Bootstrap localAddress(int port) { - localAddress = new InetSocketAddress(port); - return this; - } - - public Bootstrap localAddress(String host, int port) { - localAddress = new InetSocketAddress(host, port); - return this; - } - - public Bootstrap localAddress(InetAddress host, int port) { - localAddress = new InetSocketAddress(host, port); - return this; - } - - public Bootstrap remoteAddress(SocketAddress remoteAddress) { - this.remoteAddress = remoteAddress; - return this; - } - - public Bootstrap remoteAddress(String host, int port) { - remoteAddress = new InetSocketAddress(host, port); - return this; - } - - public Bootstrap remoteAddress(InetAddress host, int port) { - remoteAddress = new InetSocketAddress(host, port); - return this; + protected final void validate(ChannelFuture future) { + if (future == null) { + throw new NullPointerException("future"); + } + validate(); } public ChannelFuture bind() { validate(); + Channel channel = factory().newChannel(); return bind(channel.newFuture()); } - public ChannelFuture bind(ChannelFuture future) { - validate(future); - if (localAddress == null) { - throw new IllegalStateException("localAddress not set"); + @SuppressWarnings("unchecked") + public B handler(ChannelHandler handler) { + if (handler == null) { + throw new NullPointerException("handler"); } - - try { - init(); - } catch (Throwable t) { - future.setFailure(t); - return future; - } - - if (!ensureOpen(future)) { - return future; - } - - return channel.bind(localAddress, future).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); + this.handler = handler; + return (B) this; } - public ChannelFuture connect() { - validate(); - return connect(channel.newFuture()); - } - - public ChannelFuture connect(ChannelFuture future) { - validate(future); - if (remoteAddress == null) { - throw new IllegalStateException("remoteAddress not set"); - } - - try { - init(); - } catch (Throwable t) { - future.setFailure(t); - return future; - } - - if (!ensureOpen(future)) { - return future; - } - - if (localAddress == null) { - channel.connect(remoteAddress, future); - } else { - channel.connect(remoteAddress, localAddress, future); - } - return future.addListener(ChannelFutureListener.CLOSE_ON_FAILURE); - } - - private void init() throws Exception { - if (channel.isActive()) { - throw new IllegalStateException("channel already active:: " + channel); - } - if (channel.isRegistered()) { - throw new IllegalStateException("channel already registered: " + channel); - } - if (!channel.isOpen()) { - throw new ClosedChannelException(); - } - - ChannelPipeline p = channel.pipeline(); - p.addLast(handler); - - for (Entry, Object> e: options.entrySet()) { - try { - if (!channel.config().setOption((ChannelOption) e.getKey(), e.getValue())) { - logger.warn("Unknown channel option: " + e); - } - } catch (Throwable t) { - logger.warn("Failed to set a channel option: " + channel, t); - } - } - - group.register(channel).syncUninterruptibly(); - } - - private static boolean ensureOpen(ChannelFuture future) { + protected static boolean ensureOpen(ChannelFuture future) { if (!future.channel().isOpen()) { // Registration was successful but the channel was closed due to some failure in // handler. @@ -214,32 +155,47 @@ public class Bootstrap { return true; } - public void shutdown() { - if (group != null) { - group.shutdown(); - } + public abstract ChannelFuture bind(ChannelFuture future); + + protected final SocketAddress localAddress() { + return localAddress; } - private void validate() { - if (group == null) { - throw new IllegalStateException("group not set"); - } - if (channel == null) { - throw new IllegalStateException("channel not set"); - } - if (handler == null) { - throw new IllegalStateException("handler not set"); - } + protected final ChannelFactory factory() { + return factory; } - private void validate(ChannelFuture future) { - if (future == null) { - throw new NullPointerException("future"); + protected final ChannelHandler handler() { + return handler; + } + + protected final EventLoopGroup group() { + return group; + } + + protected final Map, Object> options() { + return options; + } + + private final class BootstrapChannelFactory implements ChannelFactory { + private final Class clazz; + + BootstrapChannelFactory(Class clazz) { + this.clazz = clazz; } - if (future.channel() != channel) { - throw new IllegalArgumentException("future.channel() must be the same channel."); + @Override + public Channel newChannel() { + try { + return clazz.newInstance(); + } catch (Throwable t) { + throw new ChannelException("Unable to create Channel from class " + clazz, t); + } } - validate(); + + } + + public interface ChannelFactory { + Channel newChannel(); } } diff --git a/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java b/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java new file mode 100644 index 0000000000..2853860940 --- /dev/null +++ b/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java @@ -0,0 +1,133 @@ +/* + * 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.bootstrap; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelOption; +import io.netty.logging.InternalLogger; +import io.netty.logging.InternalLoggerFactory; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.channels.ClosedChannelException; +import java.util.Map.Entry; + +public class ClientBootstrap extends Bootstrap { + + private static final InternalLogger logger = InternalLoggerFactory.getInstance(ClientBootstrap.class); + private SocketAddress remoteAddress; + + + public ClientBootstrap remoteAddress(SocketAddress remoteAddress) { + this.remoteAddress = remoteAddress; + return this; + } + + public ClientBootstrap remoteAddress(String host, int port) { + remoteAddress = new InetSocketAddress(host, port); + return this; + } + + public ClientBootstrap remoteAddress(InetAddress host, int port) { + remoteAddress = new InetSocketAddress(host, port); + return this; + } + + @Override + public ChannelFuture bind(ChannelFuture future) { + validate(future); + if (localAddress() == null) { + throw new IllegalStateException("localAddress not set"); + } + + try { + init(future.channel()); + } catch (Throwable t) { + future.setFailure(t); + return future; + } + + if (!ensureOpen(future)) { + return future; + } + + return future.channel().bind(localAddress(), future).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); + } + + public ChannelFuture connect() { + validate(); + Channel channel = factory().newChannel(); + return connect(channel.newFuture()); + } + + public ChannelFuture connect(ChannelFuture future) { + validate(future); + if (remoteAddress == null) { + throw new IllegalStateException("remoteAddress not set"); + } + + try { + init(future.channel()); + } catch (Throwable t) { + future.setFailure(t); + return future; + } + + if (!ensureOpen(future)) { + return future; + } + + if (localAddress() == null) { + future.channel().connect(remoteAddress, future); + } else { + future.channel().connect(remoteAddress, localAddress(), future); + } + return future.addListener(ChannelFutureListener.CLOSE_ON_FAILURE); + } + + @SuppressWarnings("unchecked") + private void init(Channel channel) throws Exception { + if (channel.isActive()) { + throw new IllegalStateException("channel already active:: " + channel); + } + if (channel.isRegistered()) { + throw new IllegalStateException("channel already registered: " + channel); + } + if (!channel.isOpen()) { + throw new ClosedChannelException(); + } + + ChannelPipeline p = channel.pipeline(); + p.addLast(handler()); + + for (Entry, Object> e: options().entrySet()) { + try { + if (!channel.config().setOption((ChannelOption) e.getKey(), e.getValue())) { + logger.warn("Unknown channel option: " + e); + } + } catch (Throwable t) { + logger.warn("Failed to set a channel option: " + channel, t); + } + } + + group().register(channel).syncUninterruptibly(); + } + +} diff --git a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java index 3b86b808c8..0a7fe6a358 100644 --- a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java +++ b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java @@ -17,32 +17,30 @@ package io.netty.bootstrap; import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; + import io.netty.channel.Channel; -import io.netty.channel.ChannelException; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInboundMessageHandler; +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.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; import io.netty.util.NetworkConstants; -import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.SocketAddress; import java.nio.channels.ClosedChannelException; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; -public class ServerBootstrap { +public class ServerBootstrap extends Bootstrap { private static final InternalLogger logger = InternalLoggerFactory.getInstance(ServerBootstrap.class); private static final InetSocketAddress DEFAULT_LOCAL_ADDR = new InetSocketAddress(NetworkConstants.LOCALHOST, 0); @@ -54,60 +52,37 @@ public class ServerBootstrap { } }; - private final Map, Object> parentOptions = new LinkedHashMap, Object>(); private final Map, Object> childOptions = new LinkedHashMap, Object>(); - private EventLoopGroup parentGroup; private EventLoopGroup childGroup; - private ServerChannel channel; private ChannelHandler handler; private ChannelHandler childHandler; - private SocketAddress localAddress; + @Override public ServerBootstrap group(EventLoopGroup group) { - if (group == null) { - throw new NullPointerException("group"); - } - if (parentGroup != null) { - throw new IllegalStateException("parentGroup set already"); - } - parentGroup = group; - childGroup = group; - return this; + return group(group, group); } public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) { - if (parentGroup == null) { - throw new NullPointerException("parentGroup"); + super.group(parentGroup); + if (childGroup == null) { + throw new NullPointerException("childGroup"); } - if (this.parentGroup != null) { - throw new IllegalStateException("parentGroup set already"); + if (this.childGroup != null) { + throw new IllegalStateException("childGroup set already"); } - this.parentGroup = parentGroup; this.childGroup = childGroup; return this; } - public ServerBootstrap channel(ServerChannel channel) { - if (channel == null) { - throw new NullPointerException("channel"); + @Override + public ServerBootstrap channel(Class channelClass) { + if (channelClass == null) { + throw new NullPointerException("channelClass"); } - if (this.channel != null) { - throw new IllegalStateException("channel set already"); + if (!ServerChannel.class.isAssignableFrom(channelClass)) { + throw new IllegalArgumentException(); } - this.channel = channel; - return this; - } - - public ServerBootstrap option(ChannelOption parentOption, T value) { - if (parentOption == null) { - throw new NullPointerException("parentOption"); - } - if (value == null) { - parentOptions.remove(parentOption); - } else { - parentOptions.put(parentOption, value); - } - return this; + return super.channel(channelClass); } public ServerBootstrap childOption(ChannelOption childOption, T value) { @@ -122,11 +97,6 @@ public class ServerBootstrap { return this; } - public ServerBootstrap handler(ChannelHandler handler) { - this.handler = handler; - return this; - } - public ServerBootstrap childHandler(ChannelHandler childHandler) { if (childHandler == null) { throw new NullPointerException("childHandler"); @@ -135,36 +105,10 @@ public class ServerBootstrap { return this; } - public ServerBootstrap localAddress(SocketAddress localAddress) { - if (localAddress == null) { - throw new NullPointerException("localAddress"); - } - this.localAddress = localAddress; - return this; - } - - public ServerBootstrap localAddress(int port) { - localAddress = new InetSocketAddress(port); - return this; - } - - public ServerBootstrap localAddress(String host, int port) { - localAddress = new InetSocketAddress(host, port); - return this; - } - - public ServerBootstrap localAddress(InetAddress host, int port) { - localAddress = new InetSocketAddress(host, port); - return this; - } - - public ChannelFuture bind() { - validate(); - return bind(channel.newFuture()); - } - + @Override public ChannelFuture bind(ChannelFuture future) { validate(future); + Channel channel = future.channel(); if (channel.isActive()) { future.setFailure(new IllegalStateException("channel already bound: " + channel)); return future; @@ -179,75 +123,57 @@ public class ServerBootstrap { } try { - channel.config().setOptions(parentOptions); + channel.config().setOptions(options()); } catch (Exception e) { future.setFailure(e); return future; } - ChannelPipeline p = channel.pipeline(); + ChannelPipeline p = future.channel().pipeline(); if (handler != null) { p.addLast(handler); } p.addLast(acceptor); - ChannelFuture f = parentGroup.register(channel).awaitUninterruptibly(); + ChannelFuture f = group().register(channel).awaitUninterruptibly(); if (!f.isSuccess()) { future.setFailure(f.cause()); return future; } - if (!channel.isOpen()) { - // Registration was successful but the channel was closed due to some failure in - // handler. - future.setFailure(new ChannelException("initialization failure")); + if (!ensureOpen(future)) { return future; } - channel.bind(localAddress, future).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); + channel.bind(localAddress(), future).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); return future; } + @Override public void shutdown() { - if (parentGroup != null) { - parentGroup.shutdown(); - } + super.shutdown(); if (childGroup != null) { childGroup.shutdown(); } } - private void validate() { - if (parentGroup == null) { - throw new IllegalStateException("parentGroup not set"); - } - if (channel == null) { - throw new IllegalStateException("channel not set"); - } + @Override + protected void validate() { + super.validate(); if (childHandler == null) { throw new IllegalStateException("childHandler not set"); } if (childGroup == null) { logger.warn("childGroup is not set. Using parentGroup instead."); - childGroup = parentGroup; + childGroup = group(); } - if (localAddress == null) { + if (localAddress() == null) { logger.warn("localAddress is not set. Using " + DEFAULT_LOCAL_ADDR + " instead."); - localAddress = DEFAULT_LOCAL_ADDR; + localAddress(DEFAULT_LOCAL_ADDR); } } - private void validate(ChannelFuture future) { - if (future == null) { - throw new NullPointerException("future"); - } - - if (future.channel() != channel) { - throw new IllegalArgumentException("future.channel() must be the same channel."); - } - validate(); - } private class Acceptor extends ChannelInboundHandlerAdapter implements ChannelInboundMessageHandler { @@ -257,6 +183,7 @@ public class ServerBootstrap { return Unpooled.messageBuffer(); } + @SuppressWarnings("unchecked") @Override public void inboundBufferUpdated(ChannelHandlerContext ctx) { MessageBuf in = ctx.inboundMessageBuffer(); diff --git a/transport/src/main/java/io/netty/channel/ChannelHandler.java b/transport/src/main/java/io/netty/channel/ChannelHandler.java index 358c413d90..2b93ff0e52 100644 --- a/transport/src/main/java/io/netty/channel/ChannelHandler.java +++ b/transport/src/main/java/io/netty/channel/ChannelHandler.java @@ -15,7 +15,7 @@ */ package io.netty.channel; -import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ClientBootstrap; import io.netty.channel.group.ChannelGroup; import java.lang.annotation.Documented; @@ -87,7 +87,7 @@ import java.nio.channels.Channels; * the confidential information: *
  * // Create a new handler instance per channel.
- * // See {@link Bootstrap#setPipelineFactory(ChannelPipelineFactory)}.
+ * // See {@link ClientBootstrap#setPipelineFactory(ChannelPipelineFactory)}.
  * public class DataServerPipelineFactory implements {@link ChannelPipelineFactory} {
  *     public {@link ChannelPipeline} getPipeline() {
  *         return {@link Channels}.pipeline(new DataServerHandler());
diff --git a/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java b/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java
index b7eef46d2d..d0871733db 100644
--- a/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java
+++ b/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java
@@ -15,7 +15,7 @@
  */
 package io.netty.channel.local;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
@@ -39,16 +39,16 @@ public class LocalChannelRegistryTest {
 
         for (int i = 0; i < 2; i ++) {
             LocalAddress addr = new LocalAddress(LOCAL_ADDR_ID);
-            Bootstrap cb = new Bootstrap();
+            ClientBootstrap cb = new ClientBootstrap();
             ServerBootstrap sb = new ServerBootstrap();
 
             cb.group(new LocalEventLoopGroup())
-              .channel(new LocalChannel())
+              .channel(LocalChannel.class)
               .remoteAddress(addr)
               .handler(new TestHandler());
 
             sb.group(new LocalEventLoopGroup())
-              .channel(new LocalServerChannel())
+              .channel(LocalServerChannel.class)
               .localAddress(addr)
               .childHandler(new ChannelInitializer() {
                   @Override
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 6e5c339ccc..84ebb499c9 100644
--- a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java
+++ b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java
@@ -58,7 +58,7 @@ public class LocalTransportThreadModelTest {
         // Configure a test server
         sb = new ServerBootstrap();
         sb.group(new LocalEventLoopGroup())
-          .channel(new LocalServerChannel())
+          .channel(LocalServerChannel.class)
           .localAddress(LocalAddress.ANY)
           .childHandler(new ChannelInitializer() {
               @Override

From ec1339d77557d68ff29eb0e93a3332ddca307134 Mon Sep 17 00:00:00 2001
From: norman 
Date: Tue, 11 Sep 2012 09:34:51 +0200
Subject: [PATCH 14/24] Finish the refactoring of bootstrap

---
 .../netty/example/discard/DiscardClient.java  |   6 +-
 .../netty/example/discard/DiscardServer.java  |   2 +-
 .../io/netty/example/echo/EchoClient.java     |   6 +-
 .../io/netty/example/echo/EchoServer.java     |   2 +-
 .../example/factorial/FactorialClient.java    |   6 +-
 .../example/factorial/FactorialServer.java    |   2 +-
 .../http/file/HttpStaticFileServer.java       |   2 +-
 .../example/http/snoop/HttpSnoopClient.java   |   6 +-
 .../example/http/snoop/HttpSnoopServer.java   |   2 +-
 .../websocketx/autobahn/AutobahnServer.java   |   2 +-
 .../websocketx/client/WebSocketClient.java    |   6 +-
 .../websocketx/server/WebSocketServer.java    |   2 +-
 .../sslserver/WebSocketSslServer.java         |   2 +-
 .../io/netty/example/localecho/LocalEcho.java |   8 +-
 .../example/localtime/LocalTimeClient.java    |   6 +-
 .../example/localtime/LocalTimeServer.java    |   2 +-
 .../example/objectecho/ObjectEchoClient.java  |   6 +-
 .../example/objectecho/ObjectEchoServer.java  |   2 +-
 .../PortUnificationServer.java                |   2 +-
 .../io/netty/example/proxy/HexDumpProxy.java  |   2 +-
 .../proxy/HexDumpProxyFrontendHandler.java    |   6 +-
 .../example/qotm/QuoteOfTheMomentClient.java  |   6 +-
 .../example/qotm/QuoteOfTheMomentServer.java  |   6 +-
 .../io/netty/example/sctp/SctpEchoClient.java |   5 +-
 .../io/netty/example/sctp/SctpEchoServer.java |   2 +-
 .../example/securechat/SecureChatClient.java  |   6 +-
 .../example/securechat/SecureChatServer.java  |   2 +-
 .../io/netty/example/telnet/TelnetClient.java |   6 +-
 .../io/netty/example/telnet/TelnetServer.java |   2 +-
 .../io/netty/example/uptime/UptimeClient.java |  10 +-
 .../example/uptime/UptimeClientHandler.java   |   4 +-
 .../socket/AbstractClientSocketTest.java      |  10 +-
 .../socket/AbstractDatagramTest.java          |   9 +-
 .../transport/socket/AbstractSocketTest.java  |  12 +-
 .../transport/socket/SocketEchoTest.java      |   8 +-
 .../socket/SocketFixedLengthEchoTest.java     |   4 +-
 .../socket/SocketObjectEchoTest.java          |   4 +-
 .../SocketShutdownOutputBySelfTest.java       |   4 +-
 .../transport/socket/SocketSpdyEchoTest.java  |   4 +-
 .../transport/socket/SocketSslEchoTest.java   |   4 +-
 .../socket/SocketStringEchoTest.java          |   4 +-
 .../socket/SocketTestPermutation.java         | 113 ++++++++++--------
 .../java/io/netty/bootstrap/Bootstrap.java    |   3 -
 .../io/netty/bootstrap/ClientBootstrap.java   |   8 ++
 44 files changed, 169 insertions(+), 147 deletions(-)

diff --git a/example/src/main/java/io/netty/example/discard/DiscardClient.java b/example/src/main/java/io/netty/example/discard/DiscardClient.java
index 10742a5bd6..b470a13041 100644
--- a/example/src/main/java/io/netty/example/discard/DiscardClient.java
+++ b/example/src/main/java/io/netty/example/discard/DiscardClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.discard;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioSocketChannel;
@@ -36,10 +36,10 @@ public class DiscardClient {
     }
 
     public void run() throws Exception {
-        Bootstrap b = new Bootstrap();
+        ClientBootstrap b = new ClientBootstrap();
         try {
             b.group(new NioEventLoopGroup())
-             .channel(new NioSocketChannel())
+             .channel(NioSocketChannel.class)
              .remoteAddress(host, port)
              .handler(new DiscardClientHandler(firstMessageSize));
 
diff --git a/example/src/main/java/io/netty/example/discard/DiscardServer.java b/example/src/main/java/io/netty/example/discard/DiscardServer.java
index 2b1678c520..df560a3f26 100644
--- a/example/src/main/java/io/netty/example/discard/DiscardServer.java
+++ b/example/src/main/java/io/netty/example/discard/DiscardServer.java
@@ -37,7 +37,7 @@ public class DiscardServer {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .localAddress(port)
              .childHandler(new ChannelInitializer() {
                 @Override
diff --git a/example/src/main/java/io/netty/example/echo/EchoClient.java b/example/src/main/java/io/netty/example/echo/EchoClient.java
index c0853454ae..ce5cb67b43 100644
--- a/example/src/main/java/io/netty/example/echo/EchoClient.java
+++ b/example/src/main/java/io/netty/example/echo/EchoClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.echo;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelOption;
@@ -47,10 +47,10 @@ public class EchoClient {
 
     public void run() throws Exception {
         // Configure the client.
-        Bootstrap b = new Bootstrap();
+        ClientBootstrap b = new ClientBootstrap();
         try {
             b.group(new NioEventLoopGroup())
-             .channel(new NioSocketChannel())
+             .channel(NioSocketChannel.class)
              .option(ChannelOption.TCP_NODELAY, true)
              .remoteAddress(new InetSocketAddress(host, port))
              .handler(new ChannelInitializer() {
diff --git a/example/src/main/java/io/netty/example/echo/EchoServer.java b/example/src/main/java/io/netty/example/echo/EchoServer.java
index 638ba8bb49..d4776638fc 100644
--- a/example/src/main/java/io/netty/example/echo/EchoServer.java
+++ b/example/src/main/java/io/netty/example/echo/EchoServer.java
@@ -43,7 +43,7 @@ public class EchoServer {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .option(ChannelOption.SO_BACKLOG, 100)
              .localAddress(new InetSocketAddress(port))
              .childOption(ChannelOption.TCP_NODELAY, true)
diff --git a/example/src/main/java/io/netty/example/factorial/FactorialClient.java b/example/src/main/java/io/netty/example/factorial/FactorialClient.java
index 6c17c5ec3a..ba163fa891 100644
--- a/example/src/main/java/io/netty/example/factorial/FactorialClient.java
+++ b/example/src/main/java/io/netty/example/factorial/FactorialClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.factorial;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioSocketChannel;
@@ -37,10 +37,10 @@ public class FactorialClient {
     }
 
     public void run() throws Exception {
-        Bootstrap b = new Bootstrap();
+        ClientBootstrap b = new ClientBootstrap();
         try {
             b.group(new NioEventLoopGroup())
-             .channel(new NioSocketChannel())
+             .channel(NioSocketChannel.class)
              .remoteAddress(host, port)
              .handler(new FactorialClientInitializer(count));
 
diff --git a/example/src/main/java/io/netty/example/factorial/FactorialServer.java b/example/src/main/java/io/netty/example/factorial/FactorialServer.java
index 1c68264ef0..de07935b64 100644
--- a/example/src/main/java/io/netty/example/factorial/FactorialServer.java
+++ b/example/src/main/java/io/netty/example/factorial/FactorialServer.java
@@ -35,7 +35,7 @@ public class FactorialServer {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .localAddress(port)
              .childHandler(new FactorialServerInitializer());
 
diff --git a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServer.java b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServer.java
index f228653d83..314c3c99f6 100644
--- a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServer.java
+++ b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServer.java
@@ -31,7 +31,7 @@ public class HttpStaticFileServer {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .localAddress(port)
              .childHandler(new HttpStaticFileServerInitializer());
 
diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java
index bff8050742..02313baea1 100644
--- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java
+++ b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.http.snoop;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioSocketChannel;
@@ -62,10 +62,10 @@ public class HttpSnoopClient {
         boolean ssl = scheme.equalsIgnoreCase("https");
 
         // Configure the client.
-        Bootstrap b = new Bootstrap();
+        ClientBootstrap b = new ClientBootstrap();
         try {
             b.group(new NioEventLoopGroup())
-             .channel(new NioSocketChannel())
+             .channel(NioSocketChannel.class)
              .handler(new HttpSnoopClientInitializer(ssl))
              .remoteAddress(new InetSocketAddress(host, port));
 
diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServer.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServer.java
index aed54afc01..b7b53b6d55 100644
--- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServer.java
+++ b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServer.java
@@ -40,7 +40,7 @@ public class HttpSnoopServer {
 
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .childHandler(new HttpSnoopServerInitializer())
              .localAddress(new InetSocketAddress(port));
 
diff --git a/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServer.java b/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServer.java
index 95f42c9a19..d1d45ba35c 100644
--- a/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServer.java
+++ b/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServer.java
@@ -36,7 +36,7 @@ public class AutobahnServer {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .localAddress(port)
              .childHandler(new AutobahnServerInitializer());
 
diff --git a/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClient.java b/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClient.java
index b0d7fe5260..a4beb8bde8 100644
--- a/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClient.java
+++ b/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClient.java
@@ -36,7 +36,7 @@
 //THE SOFTWARE.
 package io.netty.example.http.websocketx.client;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelInitializer;
@@ -65,7 +65,7 @@ public class WebSocketClient {
     }
 
     public void run() throws Exception {
-        Bootstrap b = new Bootstrap();
+        ClientBootstrap b = new ClientBootstrap();
         try {
 
             String protocol = uri.getScheme();
@@ -84,7 +84,7 @@ public class WebSocketClient {
                             uri, WebSocketVersion.V13, null, false, customHeaders);
 
             b.group(new NioEventLoopGroup())
-             .channel(new NioSocketChannel())
+             .channel(NioSocketChannel.class)
              .remoteAddress(uri.getHost(), uri.getPort())
              .handler(new ChannelInitializer() {
                  @Override
diff --git a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServer.java b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServer.java
index d57a97f29e..e40a8ff6e5 100644
--- a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServer.java
+++ b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServer.java
@@ -51,7 +51,7 @@ public class WebSocketServer {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .localAddress(port)
              .childHandler(new WebSocketServerInitializer());
 
diff --git a/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServer.java b/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServer.java
index b8a8bb4240..d0d697ed47 100644
--- a/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServer.java
+++ b/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServer.java
@@ -50,7 +50,7 @@ public class WebSocketSslServer {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .localAddress(port)
              .childHandler(new WebSocketSslServerInitializer());
 
diff --git a/example/src/main/java/io/netty/example/localecho/LocalEcho.java b/example/src/main/java/io/netty/example/localecho/LocalEcho.java
index 7a1410d73e..21d74614f4 100644
--- a/example/src/main/java/io/netty/example/localecho/LocalEcho.java
+++ b/example/src/main/java/io/netty/example/localecho/LocalEcho.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.localecho;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
@@ -43,14 +43,14 @@ public class LocalEcho {
         // Address to bind on / connect to.
         final LocalAddress addr = new LocalAddress(port);
 
-        Bootstrap cb = new Bootstrap();
+        ClientBootstrap cb = new ClientBootstrap();
         ServerBootstrap sb = new ServerBootstrap();
         try {
             // Note that we can use any event loop to ensure certain local channels
             // are handled by the same event loop thread which drives a certain socket channel
             // to reduce the communication latency between socket channels and local channels.
             sb.group(new LocalEventLoopGroup())
-              .channel(new LocalServerChannel())
+              .channel(LocalServerChannel.class)
               .localAddress(addr)
               .handler(new ChannelInitializer() {
                   @Override
@@ -68,7 +68,7 @@ public class LocalEcho {
               });
 
             cb.group(new NioEventLoopGroup()) // NIO event loops are also OK
-              .channel(new LocalChannel())
+              .channel(LocalChannel.class)
               .remoteAddress(addr)
               .handler(new ChannelInitializer() {
                   @Override
diff --git a/example/src/main/java/io/netty/example/localtime/LocalTimeClient.java b/example/src/main/java/io/netty/example/localtime/LocalTimeClient.java
index 4e2a4ad5a1..d20c4aed16 100644
--- a/example/src/main/java/io/netty/example/localtime/LocalTimeClient.java
+++ b/example/src/main/java/io/netty/example/localtime/LocalTimeClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.localtime;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioSocketChannel;
@@ -43,10 +43,10 @@ public class LocalTimeClient {
     }
 
     public void run() throws Exception {
-        Bootstrap b = new Bootstrap();
+        ClientBootstrap b = new ClientBootstrap();
         try {
             b.group(new NioEventLoopGroup())
-             .channel(new NioSocketChannel())
+             .channel(NioSocketChannel.class)
              .remoteAddress(host, port)
              .handler(new LocalTimeClientInitializer());
 
diff --git a/example/src/main/java/io/netty/example/localtime/LocalTimeServer.java b/example/src/main/java/io/netty/example/localtime/LocalTimeServer.java
index 14411f43fb..bcfebcb490 100644
--- a/example/src/main/java/io/netty/example/localtime/LocalTimeServer.java
+++ b/example/src/main/java/io/netty/example/localtime/LocalTimeServer.java
@@ -35,7 +35,7 @@ public class LocalTimeServer {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .localAddress(port)
              .childHandler(new LocalTimeServerInitializer());
 
diff --git a/example/src/main/java/io/netty/example/objectecho/ObjectEchoClient.java b/example/src/main/java/io/netty/example/objectecho/ObjectEchoClient.java
index 8ac8a8e712..24f980ca13 100644
--- a/example/src/main/java/io/netty/example/objectecho/ObjectEchoClient.java
+++ b/example/src/main/java/io/netty/example/objectecho/ObjectEchoClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.objectecho;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.socket.SocketChannel;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
@@ -41,10 +41,10 @@ public class ObjectEchoClient {
     }
 
     public void run() throws Exception {
-        Bootstrap b = new Bootstrap();
+        ClientBootstrap b = new ClientBootstrap();
         try {
             b.group(new NioEventLoopGroup())
-             .channel(new NioSocketChannel())
+             .channel(NioSocketChannel.class)
              .remoteAddress(host, port)
              .handler(new ChannelInitializer() {
                 @Override
diff --git a/example/src/main/java/io/netty/example/objectecho/ObjectEchoServer.java b/example/src/main/java/io/netty/example/objectecho/ObjectEchoServer.java
index 20adddb6d9..df0c2c2283 100644
--- a/example/src/main/java/io/netty/example/objectecho/ObjectEchoServer.java
+++ b/example/src/main/java/io/netty/example/objectecho/ObjectEchoServer.java
@@ -40,7 +40,7 @@ public class ObjectEchoServer {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .localAddress(port)
              .childHandler(new ChannelInitializer() {
                 @Override
diff --git a/example/src/main/java/io/netty/example/portunification/PortUnificationServer.java b/example/src/main/java/io/netty/example/portunification/PortUnificationServer.java
index 6037e83a32..fc6d6b6c39 100644
--- a/example/src/main/java/io/netty/example/portunification/PortUnificationServer.java
+++ b/example/src/main/java/io/netty/example/portunification/PortUnificationServer.java
@@ -40,7 +40,7 @@ public class PortUnificationServer {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .localAddress(port)
              .childHandler(new ChannelInitializer() {
                 @Override
diff --git a/example/src/main/java/io/netty/example/proxy/HexDumpProxy.java b/example/src/main/java/io/netty/example/proxy/HexDumpProxy.java
index f5f2e33512..a8b926c6d7 100644
--- a/example/src/main/java/io/netty/example/proxy/HexDumpProxy.java
+++ b/example/src/main/java/io/netty/example/proxy/HexDumpProxy.java
@@ -40,7 +40,7 @@ public class HexDumpProxy {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .localAddress(localPort)
              .childHandler(new HexDumpProxyInitializer(remoteHost, remotePort));
 
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 49e7925392..ff64c96e06 100644
--- a/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java
+++ b/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.proxy;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
@@ -43,9 +43,9 @@ public class HexDumpProxyFrontendHandler extends ChannelInboundByteHandlerAdapte
         final Channel inboundChannel = ctx.channel();
 
         // Start the connection attempt.
-        Bootstrap b = new Bootstrap();
+        ClientBootstrap b = new ClientBootstrap();
         b.group(inboundChannel.eventLoop())
-         .channel(new NioSocketChannel())
+         .channel(NioSocketChannel.class)
          .remoteAddress(remoteHost, remotePort)
          .handler(new HexDumpProxyBackendHandler(inboundChannel));
 
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 aaeb49217b..05e4f38110 100644
--- a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClient.java
+++ b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.qotm;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelOption;
@@ -41,10 +41,10 @@ public class QuoteOfTheMomentClient {
     }
 
     public void run() throws Exception {
-        Bootstrap b = new Bootstrap();
+        ClientBootstrap b = new ClientBootstrap();
         try {
             b.group(new NioEventLoopGroup())
-             .channel(new NioDatagramChannel())
+             .channel(NioDatagramChannel.class)
              .localAddress(new InetSocketAddress(0))
              .option(ChannelOption.SO_BROADCAST, true)
              .handler(new QuoteOfTheMomentClientHandler());
diff --git a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServer.java b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServer.java
index cf9abc02b2..21221a7002 100644
--- a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServer.java
+++ b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServer.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.qotm;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.channel.ChannelOption;
 import io.netty.channel.socket.nio.NioDatagramChannel;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
@@ -37,10 +37,10 @@ public class QuoteOfTheMomentServer {
     }
 
     public void run() throws Exception {
-        Bootstrap b = new Bootstrap();
+        ClientBootstrap b = new ClientBootstrap();
         try {
             b.group(new NioEventLoopGroup())
-             .channel(new NioDatagramChannel())
+             .channel(NioDatagramChannel.class)
              .localAddress(new InetSocketAddress(port))
              .option(ChannelOption.SO_BROADCAST, true)
              .handler(new QuoteOfTheMomentServerHandler());
diff --git a/example/src/main/java/io/netty/example/sctp/SctpEchoClient.java b/example/src/main/java/io/netty/example/sctp/SctpEchoClient.java
index f2a4321cb3..831fb78aa6 100644
--- a/example/src/main/java/io/netty/example/sctp/SctpEchoClient.java
+++ b/example/src/main/java/io/netty/example/sctp/SctpEchoClient.java
@@ -16,6 +16,7 @@
 package io.netty.example.sctp;
 
 import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelOption;
@@ -51,10 +52,10 @@ public class SctpEchoClient {
 
     public void run() throws Exception {
         // Configure the client.
-        Bootstrap b = new Bootstrap();
+        ClientBootstrap b = new ClientBootstrap();
         try {
             b.group(new NioEventLoopGroup())
-             .channel(new NioSctpChannel())
+             .channel(NioSctpChannel.class)
              .option(ChannelOption.SCTP_NODELAY, true)
              .remoteAddress(new InetSocketAddress(host, port))
              .handler(new ChannelInitializer() {
diff --git a/example/src/main/java/io/netty/example/sctp/SctpEchoServer.java b/example/src/main/java/io/netty/example/sctp/SctpEchoServer.java
index 0658ac1010..3195695f84 100644
--- a/example/src/main/java/io/netty/example/sctp/SctpEchoServer.java
+++ b/example/src/main/java/io/netty/example/sctp/SctpEchoServer.java
@@ -45,7 +45,7 @@ public class SctpEchoServer {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioSctpServerChannel())
+             .channel(NioSctpServerChannel.class)
              .option(ChannelOption.SO_BACKLOG, 100)
              .localAddress(new InetSocketAddress(port))
              .childOption(ChannelOption.SCTP_NODELAY, true)
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 76099b1fab..23b7704d2d 100644
--- a/example/src/main/java/io/netty/example/securechat/SecureChatClient.java
+++ b/example/src/main/java/io/netty/example/securechat/SecureChatClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.securechat;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
@@ -39,10 +39,10 @@ public class SecureChatClient {
     }
 
     public void run() throws Exception {
-        Bootstrap b = new Bootstrap();
+        ClientBootstrap b = new ClientBootstrap();
         try {
             b.group(new NioEventLoopGroup())
-             .channel(new NioSocketChannel())
+             .channel(NioSocketChannel.class)
              .remoteAddress(host, port)
              .handler(new SecureChatClientInitializer());
 
diff --git a/example/src/main/java/io/netty/example/securechat/SecureChatServer.java b/example/src/main/java/io/netty/example/securechat/SecureChatServer.java
index 3731eda2df..7de8517004 100644
--- a/example/src/main/java/io/netty/example/securechat/SecureChatServer.java
+++ b/example/src/main/java/io/netty/example/securechat/SecureChatServer.java
@@ -35,7 +35,7 @@ public class SecureChatServer {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .localAddress(port)
              .childHandler(new SecureChatServerInitializer());
 
diff --git a/example/src/main/java/io/netty/example/telnet/TelnetClient.java b/example/src/main/java/io/netty/example/telnet/TelnetClient.java
index befac65b8a..7b8d4e9960 100644
--- a/example/src/main/java/io/netty/example/telnet/TelnetClient.java
+++ b/example/src/main/java/io/netty/example/telnet/TelnetClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.telnet;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
@@ -38,10 +38,10 @@ public class TelnetClient {
     }
 
     public void run() throws Exception {
-        Bootstrap b = new Bootstrap();
+        ClientBootstrap b = new ClientBootstrap();
         try {
             b.group(new NioEventLoopGroup())
-             .channel(new NioSocketChannel())
+             .channel(NioSocketChannel.class)
              .remoteAddress(host, port)
              .handler(new TelnetClientInitializer());
 
diff --git a/example/src/main/java/io/netty/example/telnet/TelnetServer.java b/example/src/main/java/io/netty/example/telnet/TelnetServer.java
index 784260bf6a..49b62a1a32 100644
--- a/example/src/main/java/io/netty/example/telnet/TelnetServer.java
+++ b/example/src/main/java/io/netty/example/telnet/TelnetServer.java
@@ -34,7 +34,7 @@ public class TelnetServer {
         ServerBootstrap b = new ServerBootstrap();
         try {
             b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-             .channel(new NioServerSocketChannel())
+             .channel(NioServerSocketChannel.class)
              .localAddress(port)
              .childHandler(new TelnetServerPipelineFactory());
 
diff --git a/example/src/main/java/io/netty/example/uptime/UptimeClient.java b/example/src/main/java/io/netty/example/uptime/UptimeClient.java
index ea5c434442..b95d59be73 100644
--- a/example/src/main/java/io/netty/example/uptime/UptimeClient.java
+++ b/example/src/main/java/io/netty/example/uptime/UptimeClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.uptime;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.socket.SocketChannel;
@@ -50,16 +50,16 @@ public class UptimeClient {
     }
 
     public void run() {
-        configureBootstrap(new Bootstrap()).connect();
+        configureBootstrap(new ClientBootstrap()).connect();
     }
 
-    private Bootstrap configureBootstrap(Bootstrap b) {
+    private ClientBootstrap configureBootstrap(ClientBootstrap b) {
         return configureBootstrap(b, new NioEventLoopGroup());
     }
 
-    Bootstrap configureBootstrap(Bootstrap b, EventLoopGroup g) {
+    ClientBootstrap configureBootstrap(ClientBootstrap b, EventLoopGroup g) {
         b.group(g)
-         .channel(new NioSocketChannel())
+         .channel(NioSocketChannel.class)
          .remoteAddress(host, port)
          .handler(new ChannelInitializer() {
             @Override
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 4cf69426a5..8d278637c7 100644
--- a/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java
+++ b/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.uptime;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
@@ -84,7 +84,7 @@ public class UptimeClientHandler extends ChannelInboundByteHandlerAdapter {
             @Override
             public void run() {
                 println("Reconnecting to: " + ctx.channel().remoteAddress());
-                client.configureBootstrap(new Bootstrap(), loop).connect();
+                client.configureBootstrap(new ClientBootstrap(), loop).connect();
             }
         }, UptimeClient.RECONNECT_DELAY, TimeUnit.SECONDS);
     }
diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractClientSocketTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractClientSocketTest.java
index 50171a7c06..7f918e2957 100644
--- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractClientSocketTest.java
+++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractClientSocketTest.java
@@ -15,7 +15,7 @@
  */
 package io.netty.testsuite.transport.socket;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.logging.InternalLogger;
 import io.netty.logging.InternalLoggerFactory;
 import io.netty.testsuite.transport.socket.SocketTestPermutation.Factory;
@@ -32,19 +32,19 @@ import org.junit.rules.TestName;
 
 public abstract class AbstractClientSocketTest {
 
-    private static final List> COMBO = SocketTestPermutation.clientSocket();
+    private static final List> COMBO = SocketTestPermutation.clientSocket();
 
     @Rule
     public final TestName testName = new TestName();
 
     protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
 
-    protected volatile Bootstrap cb;
+    protected volatile ClientBootstrap cb;
     protected volatile InetSocketAddress addr;
 
     protected void run() throws Throwable {
         int i = 0;
-        for (Factory e: COMBO) {
+        for (Factory e: COMBO) {
             cb = e.newInstance();
             addr = new InetSocketAddress(
                     NetworkConstants.LOCALHOST, TestUtils.getFreePort());
@@ -54,7 +54,7 @@ public abstract class AbstractClientSocketTest {
                     "Running: %s %d of %d", testName.getMethodName(), ++ i, COMBO.size()));
             try {
                 Method m = getClass().getDeclaredMethod(
-                        testName.getMethodName(), Bootstrap.class);
+                        testName.getMethodName(), ClientBootstrap.class);
                 m.invoke(this, cb);
             } catch (InvocationTargetException ex) {
                 throw ex.getCause();
diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractDatagramTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractDatagramTest.java
index 69403301cf..9ba72e78e5 100644
--- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractDatagramTest.java
+++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractDatagramTest.java
@@ -16,6 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.logging.InternalLogger;
 import io.netty.logging.InternalLoggerFactory;
 import io.netty.testsuite.transport.socket.SocketTestPermutation.Factory;
@@ -33,7 +34,7 @@ import org.junit.rules.TestName;
 
 public abstract class AbstractDatagramTest {
 
-    private static final List, Factory>> COMBO =
+    private static final List, Factory>> COMBO =
             SocketTestPermutation.datagram();
 
     @Rule
@@ -41,13 +42,13 @@ public abstract class AbstractDatagramTest {
 
     protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
 
-    protected volatile Bootstrap sb;
-    protected volatile Bootstrap cb;
+    protected volatile ClientBootstrap sb;
+    protected volatile ClientBootstrap cb;
     protected volatile InetSocketAddress addr;
 
     protected void run() throws Throwable {
         int i = 0;
-        for (Entry, Factory> e: COMBO) {
+        for (Entry, Factory> e: COMBO) {
             sb = e.getKey().newInstance();
             cb = e.getValue().newInstance();
             addr = new InetSocketAddress(
diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSocketTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSocketTest.java
index 5651235d34..dc6b8ff791 100644
--- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSocketTest.java
+++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSocketTest.java
@@ -15,7 +15,7 @@
  */
 package io.netty.testsuite.transport.socket;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.logging.InternalLogger;
 import io.netty.logging.InternalLoggerFactory;
@@ -34,7 +34,7 @@ import org.junit.rules.TestName;
 
 public abstract class AbstractSocketTest {
 
-    private static final List, Factory>> COMBO =
+    private static final List, Factory>> COMBO =
             SocketTestPermutation.socket();
 
     @Rule
@@ -43,13 +43,13 @@ public abstract class AbstractSocketTest {
     protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
 
     protected volatile ServerBootstrap sb;
-    protected volatile Bootstrap cb;
+    protected volatile ClientBootstrap cb;
     protected volatile InetSocketAddress addr;
-    protected volatile Factory currentBootstrap;
+    protected volatile Factory currentBootstrap;
     
     protected void run() throws Throwable {
         int i = 0;
-        for (Entry, Factory> e: COMBO) {
+        for (Entry, Factory> e: COMBO) {
             currentBootstrap = e.getValue();
             sb = e.getKey().newInstance();
             cb = e.getValue().newInstance();
@@ -62,7 +62,7 @@ public abstract class AbstractSocketTest {
                     "Running: %s %d of %d", testName.getMethodName(), ++ i, COMBO.size()));
             try {
                 Method m = getClass().getDeclaredMethod(
-                        testName.getMethodName(), ServerBootstrap.class, Bootstrap.class);
+                        testName.getMethodName(), ServerBootstrap.class, ClientBootstrap.class);
                 m.invoke(this, sb, cb);
             } catch (InvocationTargetException ex) {
                 throw ex.getCause();
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 3c0eaf37da..def906d2e2 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
@@ -44,7 +44,7 @@ public class SocketEchoTest extends AbstractSocketTest {
         run();
     }
 
-    public void testSimpleEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable {
+    public void testSimpleEcho(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
         testSimpleEcho0(sb, cb, Integer.MAX_VALUE);
     }
 
@@ -53,11 +53,11 @@ public class SocketEchoTest extends AbstractSocketTest {
         run();
     }
 
-    public void testSimpleEchoWithBoundedBuffer(ServerBootstrap sb, Bootstrap cb) throws Throwable {
+    public void testSimpleEchoWithBoundedBuffer(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
         testSimpleEcho0(sb, cb, 32);
     }
 
-    private static void testSimpleEcho0(ServerBootstrap sb, Bootstrap cb, int maxInboundBufferSize) throws Throwable {
+    private static void testSimpleEcho0(ServerBootstrap sb, ClientBootstrap cb, int maxInboundBufferSize) throws Throwable {
         EchoHandler sh = new EchoHandler(maxInboundBufferSize);
         EchoHandler ch = new EchoHandler(maxInboundBufferSize);
 
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 edc5d746ec..a5a5eb6e68 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
@@ -47,7 +47,7 @@ public class SocketFixedLengthEchoTest extends AbstractSocketTest {
         run();
     }
 
-    public void testFixedLengthEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable {
+    public void testFixedLengthEcho(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
         final EchoHandler sh = new EchoHandler();
         final EchoHandler ch = new EchoHandler();
 
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 5df1cd5c8a..97d0dc67f0 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
@@ -55,7 +55,7 @@ public class SocketObjectEchoTest extends AbstractSocketTest {
         run();
     }
 
-    public void testObjectEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable {
+    public void testObjectEcho(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
         final EchoHandler sh = new EchoHandler();
         final EchoHandler ch = new EchoHandler();
 
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 c61e857b3b..5ffb5fa377 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
@@ -37,7 +37,7 @@ public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest {
         run();
     }
 
-    public void testShutdownOutput(Bootstrap cb) throws Throwable {
+    public void testShutdownOutput(ClientBootstrap cb) throws Throwable {
         TestHandler h = new TestHandler();
         ServerSocket ss = new ServerSocket();
         Socket s = null;
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 6cecdb098c..a1c4dccc67 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
@@ -175,7 +175,7 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
         }
     }
 
-    public void testSpdyEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable {
+    public void testSpdyEcho(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
 
         ByteBuf frames = createFrames(version);
 
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 f521916a58..0676d1ebfb 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
@@ -67,7 +67,7 @@ public class SocketSslEchoTest extends AbstractSocketTest {
         run();
     }
 
-    public void testSslEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable {
+    public void testSslEcho(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
         final EchoHandler sh = new EchoHandler(true);
         final EchoHandler ch = new EchoHandler(false);
 
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 641c1d4df2..6236b11af0 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
@@ -57,7 +57,7 @@ public class SocketStringEchoTest extends AbstractSocketTest {
         run();
     }
 
-    public void testStringEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable {
+    public void testStringEcho(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
         final StringEchoHandler sh = new StringEchoHandler();
         final StringEchoHandler ch = new StringEchoHandler();
 
diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java
index 9ec4d6e81c..994272594b 100644
--- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java
+++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java
@@ -15,8 +15,10 @@
  */
 package io.netty.testsuite.transport.socket;
 
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.Bootstrap.ChannelFactory;
+import io.netty.bootstrap.ClientBootstrap;
 import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
 import io.netty.channel.socket.InternetProtocolFamily;
 import io.netty.channel.socket.aio.AioEventLoopGroup;
 import io.netty.channel.socket.aio.AioServerSocketChannel;
@@ -36,34 +38,34 @@ import java.util.Map.Entry;
 
 final class SocketTestPermutation {
 
-    static List, Factory>> socket() {
-        List, Factory>> list =
-                new ArrayList, Factory>>();
+    static List, Factory>> socket() {
+        List, Factory>> list =
+                new ArrayList, Factory>>();
 
         // Make the list of ServerBootstrap factories.
         List> sbfs = serverSocket();
 
         // Make the list of Bootstrap factories.
-        List> cbfs = clientSocket();
+        List> cbfs = clientSocket();
 
         // Populate the combinations
         for (Factory sbf: sbfs) {
-            for (Factory cbf: cbfs) {
+            for (Factory cbf: cbfs) {
                 final Factory sbf0 = sbf;
-                final Factory cbf0 = cbf;
-                list.add(new Entry, Factory>() {
+                final Factory cbf0 = cbf;
+                list.add(new Entry, Factory>() {
                     @Override
                     public Factory getKey() {
                         return sbf0;
                     }
 
                     @Override
-                    public Factory getValue() {
+                    public Factory getValue() {
                         return cbf0;
                     }
 
                     @Override
-                    public Factory setValue(Factory value) {
+                    public Factory setValue(Factory value) {
                         throw new UnsupportedOperationException();
                     }
                 });
@@ -76,45 +78,49 @@ final class SocketTestPermutation {
         return list;
     }
 
-    static List, Factory>> datagram() {
-        List, Factory>> list =
-                new ArrayList, Factory>>();
+    static List, Factory>> datagram() {
+        List, Factory>> list =
+                new ArrayList, Factory>>();
 
         // Make the list of Bootstrap factories.
-        List> bfs =
-                new ArrayList>();
-        bfs.add(new Factory() {
+        List> bfs =
+                new ArrayList>();
+        bfs.add(new Factory() {
             @Override
-            public Bootstrap newInstance() {
-                return new Bootstrap().group(new NioEventLoopGroup()).channel(
-                        new NioDatagramChannel(InternetProtocolFamily.IPv4));
+            public ClientBootstrap newInstance() {
+                return new ClientBootstrap().group(new NioEventLoopGroup()).channelFactory(new ChannelFactory() {
+                    @Override
+                    public Channel newChannel() {
+                       return new NioDatagramChannel(InternetProtocolFamily.IPv4);
+                    }
+                });
             }
         });
-        bfs.add(new Factory() {
+        bfs.add(new Factory() {
             @Override
-            public Bootstrap newInstance() {
-                return new Bootstrap().group(new OioEventLoopGroup()).channel(new OioDatagramChannel());
+            public ClientBootstrap newInstance() {
+                return new ClientBootstrap().group(new OioEventLoopGroup()).channel(OioDatagramChannel.class);
             }
         });
 
         // Populate the combinations
-        for (Factory sbf: bfs) {
-            for (Factory cbf: bfs) {
-                final Factory sbf0 = sbf;
-                final Factory cbf0 = cbf;
-                list.add(new Entry, Factory>() {
+        for (Factory sbf: bfs) {
+            for (Factory cbf: bfs) {
+                final Factory sbf0 = sbf;
+                final Factory cbf0 = cbf;
+                list.add(new Entry, Factory>() {
                     @Override
-                    public Factory getKey() {
+                    public Factory getKey() {
                         return sbf0;
                     }
 
                     @Override
-                    public Factory getValue() {
+                    public Factory getValue() {
                         return cbf0;
                     }
 
                     @Override
-                    public Factory setValue(Factory value) {
+                    public Factory setValue(Factory value) {
                         throw new UnsupportedOperationException();
                     }
                 });
@@ -133,17 +139,21 @@ final class SocketTestPermutation {
             public ServerBootstrap newInstance() {
                 return new ServerBootstrap().
                                 group(new NioEventLoopGroup(), new NioEventLoopGroup()).
-                                channel(new NioServerSocketChannel());
+                                channel(NioServerSocketChannel.class);
             }
         });
         list.add(new Factory() {
             @Override
             public ServerBootstrap newInstance() {
-                AioEventLoopGroup parentGroup = new AioEventLoopGroup();
-                AioEventLoopGroup childGroup = new AioEventLoopGroup();
-                return new ServerBootstrap().
-                                group(parentGroup, childGroup).
-                                channel(new AioServerSocketChannel(parentGroup, childGroup));
+                final AioEventLoopGroup parentGroup = new AioEventLoopGroup();
+                final AioEventLoopGroup childGroup = new AioEventLoopGroup();
+                return new ServerBootstrap().group(parentGroup, childGroup).channelFactory(new ChannelFactory() {
+
+                    @Override
+                    public Channel newChannel() {
+                        return new AioServerSocketChannel(parentGroup, childGroup);
+                    }
+                });
             }
         });
         list.add(new Factory() {
@@ -151,32 +161,37 @@ final class SocketTestPermutation {
             public ServerBootstrap newInstance() {
                 return new ServerBootstrap().
                                 group(new OioEventLoopGroup(), new OioEventLoopGroup()).
-                                channel(new OioServerSocketChannel());
+                                channel(OioServerSocketChannel.class);
             }
         });
 
         return list;
     }
 
-    static List> clientSocket() {
-        List> list = new ArrayList>();
-        list.add(new Factory() {
+    static List> clientSocket() {
+        List> list = new ArrayList>();
+        list.add(new Factory() {
             @Override
-            public Bootstrap newInstance() {
-                return new Bootstrap().group(new NioEventLoopGroup()).channel(new NioSocketChannel());
+            public ClientBootstrap newInstance() {
+                return new ClientBootstrap().group(new NioEventLoopGroup()).channel(NioSocketChannel.class);
             }
         });
-        list.add(new Factory() {
+        list.add(new Factory() {
             @Override
-            public Bootstrap newInstance() {
-                AioEventLoopGroup loop = new AioEventLoopGroup();
-                return new Bootstrap().group(loop).channel(new AioSocketChannel(loop));
+            public ClientBootstrap newInstance() {
+                final AioEventLoopGroup loop = new AioEventLoopGroup();
+                return new ClientBootstrap().group(loop).channelFactory(new ChannelFactory() {
+                    @Override
+                    public Channel newChannel() {
+                        return new AioSocketChannel(loop);
+                    }
+                });
             }
         });
-        list.add(new Factory() {
+        list.add(new Factory() {
             @Override
-            public Bootstrap newInstance() {
-                return new Bootstrap().group(new OioEventLoopGroup()).channel(new OioSocketChannel());
+            public ClientBootstrap newInstance() {
+                return new ClientBootstrap().group(new OioEventLoopGroup()).channel(OioSocketChannel.class);
             }
         });
         return list;
diff --git a/transport/src/main/java/io/netty/bootstrap/Bootstrap.java b/transport/src/main/java/io/netty/bootstrap/Bootstrap.java
index a4cea1d3b2..e1e6f5bdd4 100644
--- a/transport/src/main/java/io/netty/bootstrap/Bootstrap.java
+++ b/transport/src/main/java/io/netty/bootstrap/Bootstrap.java
@@ -118,9 +118,6 @@ public abstract class Bootstrap> {
         if (factory == null) {
             throw new IllegalStateException("factory not set");
         }
-        if (handler == null) {
-            throw new IllegalStateException("handler not set");
-        }
     }
 
     protected final void validate(ChannelFuture future) {
diff --git a/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java b/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java
index 2853860940..f3f2e806bf 100644
--- a/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java
+++ b/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java
@@ -130,4 +130,12 @@ public class ClientBootstrap extends Bootstrap {
         group().register(channel).syncUninterruptibly();
     }
 
+    @Override
+    protected void validate() {
+        super.validate();
+        if (handler() == null) {
+            throw new IllegalStateException("handler not set");
+        }
+    }
+
 }

From 281f73fe1ae3f1c054f19eb1df8312f7910a400c Mon Sep 17 00:00:00 2001
From: norman 
Date: Tue, 11 Sep 2012 10:04:05 +0200
Subject: [PATCH 15/24] Some javadocs love

---
 .../java/io/netty/bootstrap/Bootstrap.java    | 78 ++++++++++++++++---
 .../io/netty/bootstrap/ServerBootstrap.java   | 24 ++++++
 2 files changed, 92 insertions(+), 10 deletions(-)

diff --git a/transport/src/main/java/io/netty/bootstrap/Bootstrap.java b/transport/src/main/java/io/netty/bootstrap/Bootstrap.java
index e1e6f5bdd4..a300e9730b 100644
--- a/transport/src/main/java/io/netty/bootstrap/Bootstrap.java
+++ b/transport/src/main/java/io/netty/bootstrap/Bootstrap.java
@@ -29,7 +29,11 @@ import io.netty.channel.ChannelOption;
 import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelException;
 
-
+/**
+ * {@link Bootstrap} is a helper class that makes it easy to bootstrap a {@link Channel}. It support
+ * method-chaining to provide an easy way to configure the {@link Bootstrap}.
+ *
+ */
 public abstract class Bootstrap> {
     private EventLoopGroup group;
     private ChannelFactory factory;
@@ -37,6 +41,10 @@ public abstract class Bootstrap> {
     private final Map, Object> options = new LinkedHashMap, Object>();
     private ChannelHandler handler;
 
+    /**
+     * The {@link EventLoopGroup} which is used to handle all the events for the to-be-creates
+     * {@link Channel}
+     */
     @SuppressWarnings("unchecked")
     public B group(EventLoopGroup group) {
         if (group == null) {
@@ -49,6 +57,11 @@ public abstract class Bootstrap> {
         return (B) this;
     }
 
+    /**
+     * The {@link Class} which is used to create {@link Channel} instances from.
+     * You either use this or {@link #channelFactory(ChannelFactory)} if your
+     * {@link Channel} implementation has no no-args constructor.
+     */
     public B channel(Class channelClass) {
         if (channelClass == null) {
             throw new NullPointerException("channelClass");
@@ -56,6 +69,13 @@ public abstract class Bootstrap> {
         return channelFactory(new BootstrapChannelFactory(channelClass));
     }
 
+    /**
+     * {@link ChannelFactory} which is used to create {@link Channel} instances from
+     * when calling {@link #bind()}. This method is usually only used if {@link #channel(Class)}
+     * is not working for you because of some more complex needs. If your {@link Channel} implementation
+     * has a no-args constructor, its highly recommend to just use {@link #channel(Class)} for
+     * simplify your code.
+     */
     @SuppressWarnings("unchecked")
     public B channelFactory(ChannelFactory factory) {
         if (factory == null) {
@@ -64,34 +84,46 @@ public abstract class Bootstrap> {
         if (this.factory != null) {
             throw new IllegalStateException("factory set already");
         }
+
         this.factory = factory;
         return (B) this;
     }
 
+    /**
+     * The {@link SocketAddress} which is used to bind the local "end" to.
+     *
+     */
     @SuppressWarnings("unchecked")
     public B localAddress(SocketAddress localAddress) {
         this.localAddress = localAddress;
         return (B) this;
     }
 
-    @SuppressWarnings("unchecked")
+    /**
+     * See {@link #localAddress(SocketAddress)}
+     */
     public B localAddress(int port) {
-        localAddress = new InetSocketAddress(port);
-        return (B) this;
+        return localAddress(new InetSocketAddress(port));
     }
 
-    @SuppressWarnings("unchecked")
+    /**
+     * See {@link #localAddress(SocketAddress)}
+     */
     public B localAddress(String host, int port) {
-        localAddress = new InetSocketAddress(host, port);
-        return (B) this;
+        return localAddress(new InetSocketAddress(host, port));
     }
 
-    @SuppressWarnings("unchecked")
+    /**
+     * See {@link #localAddress(SocketAddress)}
+     */
     public B localAddress(InetAddress host, int port) {
-        localAddress = new InetSocketAddress(host, port);
-        return (B) this;
+        return localAddress(new InetSocketAddress(host, port));
     }
 
+    /**
+     * Allow to specify a {@link ChannelOption} which is used for the {@link Channel} instances once they got
+     * created. Use a value of null to remove a previous set {@link ChannelOption}.
+     */
     @SuppressWarnings("unchecked")
     public  B option(ChannelOption option, T value) {
         if (option == null) {
@@ -105,12 +137,21 @@ public abstract class Bootstrap> {
         return (B) this;
     }
 
+    /**
+     * Shutdown the {@link Bootstrap} and the {@link EventLoopGroup} which is
+     * used by it. Only call this if you don't share the {@link EventLoopGroup}
+     * between different {@link Bootstrap}'s.
+     */
     public void shutdown() {
         if (group != null) {
             group.shutdown();
         }
     }
 
+    /**
+     * Validate all the parameters. Sub-classes may override this, but should
+     * call the super method in that case.
+     */
     protected void validate() {
         if (group == null) {
             throw new IllegalStateException("group not set");
@@ -127,12 +168,18 @@ public abstract class Bootstrap> {
         validate();
     }
 
+    /**
+     * Create a new {@link Channel} and bind it.
+     */
     public ChannelFuture bind() {
         validate();
         Channel channel = factory().newChannel();
         return bind(channel.newFuture());
     }
 
+    /**
+     * the {@link ChannelHandler} to use for serving the requests.
+     */
     @SuppressWarnings("unchecked")
     public B handler(ChannelHandler handler) {
         if (handler == null) {
@@ -152,6 +199,9 @@ public abstract class Bootstrap> {
         return true;
     }
 
+    /**
+     * Bind the {@link Channel} of the given {@link ChannelFactory}.
+     */
     public abstract ChannelFuture bind(ChannelFuture future);
 
     protected final SocketAddress localAddress() {
@@ -192,7 +242,15 @@ public abstract class Bootstrap> {
 
     }
 
+    /**
+     * Factory that is responsible to create new {@link Channel}'s on {@link Bootstrap#bind()}
+     * requests.
+     *
+     */
     public interface ChannelFactory {
+        /**
+         * {@link Channel} to use in the {@link Bootstrap}
+         */
         Channel newChannel();
     }
 }
diff --git a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java
index 0a7fe6a358..6c06357053 100644
--- a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java
+++ b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java
@@ -30,6 +30,7 @@ import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelOption;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.ServerChannel;
+import io.netty.channel.socket.SocketChannel;
 import io.netty.logging.InternalLogger;
 import io.netty.logging.InternalLoggerFactory;
 import io.netty.util.NetworkConstants;
@@ -40,6 +41,10 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 
+/**
+ * {@link Bootstrap} sub-class which allows easy bootstrap of {@link ServerChannel}
+ *
+ */
 public class ServerBootstrap extends Bootstrap {
 
     private static final InternalLogger logger = InternalLoggerFactory.getInstance(ServerBootstrap.class);
@@ -57,11 +62,19 @@ public class ServerBootstrap extends Bootstrap {
     private ChannelHandler handler;
     private ChannelHandler childHandler;
 
+    /**
+     * Specify the {@link EventLoopGroup} which is used for the parent (acceptor) and the child (client).
+     */
     @Override
     public ServerBootstrap group(EventLoopGroup group) {
         return group(group, group);
     }
 
+    /**
+     * Set the {@link EventLoopGroup} for the parent (acceptor) and the child (client). These
+     * {@link EventLoopGroup}'s are used to handle all the events and IO for {@link SocketChannel} and
+     * {@link Channel}'s.
+     */
     public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
         super.group(parentGroup);
         if (childGroup == null) {
@@ -74,6 +87,9 @@ public class ServerBootstrap extends Bootstrap {
         return this;
     }
 
+    /**
+     * The {@link Class} which is used to create the {@link ServerChannel} from (for the acceptor).
+     */
     @Override
     public ServerBootstrap channel(Class channelClass) {
         if (channelClass == null) {
@@ -85,6 +101,11 @@ public class ServerBootstrap extends Bootstrap {
         return super.channel(channelClass);
     }
 
+    /**
+     * Allow to specify a {@link ChannelOption} which is used for the {@link Channel} instances once they get created
+     * (after the acceptor accepted the {@link Channel}). Use a value of null to remove a previous set
+     * {@link ChannelOption}.
+     */
     public  ServerBootstrap childOption(ChannelOption childOption, T value) {
         if (childOption == null) {
             throw new NullPointerException("childOption");
@@ -97,6 +118,9 @@ public class ServerBootstrap extends Bootstrap {
         return this;
     }
 
+    /**
+     * Set the {@link ChannelHandler} which is used to server the request for the {@link Channel}'s.
+     */
     public ServerBootstrap childHandler(ChannelHandler childHandler) {
         if (childHandler == null) {
             throw new NullPointerException("childHandler");

From d22480c0f4ae441f68252da15e50a5adb244633e Mon Sep 17 00:00:00 2001
From: norman 
Date: Tue, 11 Sep 2012 10:32:59 +0200
Subject: [PATCH 16/24] Add more javadocs and also two helper methods that make
 it easy to create new Clientbootstrap instances from an existing one

---
 .../io/netty/bootstrap/ClientBootstrap.java   | 41 +++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java b/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java
index f3f2e806bf..083922cfc0 100644
--- a/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java
+++ b/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java
@@ -16,6 +16,7 @@
 package io.netty.bootstrap;
 
 import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelPipeline;
 import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelFuture;
@@ -29,22 +30,37 @@ import java.net.SocketAddress;
 import java.nio.channels.ClosedChannelException;
 import java.util.Map.Entry;
 
+/**
+ * A {@link Bootstrap} that makes it easy to bootstrap a {@link Channel} to use
+ * for clients.
+ *
+ */
 public class ClientBootstrap extends Bootstrap {
 
     private static final InternalLogger logger = InternalLoggerFactory.getInstance(ClientBootstrap.class);
     private SocketAddress remoteAddress;
 
 
+    /**
+     * The {@link SocketAddress} to connect to once the {@link #connect()} method
+     * is called.
+     */
     public ClientBootstrap remoteAddress(SocketAddress remoteAddress) {
         this.remoteAddress = remoteAddress;
         return this;
     }
 
+    /**
+     * See {@link #remoteAddress(SocketAddress)}
+     */
     public ClientBootstrap remoteAddress(String host, int port) {
         remoteAddress = new InetSocketAddress(host, port);
         return this;
     }
 
+    /**
+     * See {@link #remoteAddress(SocketAddress)}
+     */
     public ClientBootstrap remoteAddress(InetAddress host, int port) {
         remoteAddress = new InetSocketAddress(host, port);
         return this;
@@ -71,12 +87,18 @@ public class ClientBootstrap extends Bootstrap {
         return future.channel().bind(localAddress(), future).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
     }
 
+    /**
+     * Connect a {@link Channel} to the remote peer.
+     */
     public ChannelFuture connect() {
         validate();
         Channel channel = factory().newChannel();
         return connect(channel.newFuture());
     }
 
+    /**
+     * See {@link #connect()}
+     */
     public ChannelFuture connect(ChannelFuture future) {
         validate(future);
         if (remoteAddress == null) {
@@ -138,4 +160,23 @@ public class ClientBootstrap extends Bootstrap {
         }
     }
 
+    /**
+     * Create a new {@link ClientBootstrap} using this "full-setup" {@link ClientBootstrap} as template. 
+     * Only the given parameters are replaced, the rest is configured exactly the same way as the template.
+     */
+    public ClientBootstrap newBootstrap(SocketAddress localAddress, SocketAddress remoteAddress, ChannelHandler handler) {
+        validate();
+        ClientBootstrap cb = new ClientBootstrap().handler(handler).channelFactory(factory()).group(group()).localAddress(localAddress).remoteAddress(remoteAddress);
+        cb.options().putAll(options());
+        
+        return cb;
+    }
+
+    /**
+     * Create a new {@link ClientBootstrap} using this "full-setup" {@link ClientBootstrap} as template. 
+     * Only the given parameters are replaced, the rest is configured exactly the same way as the template.
+     */
+    public ClientBootstrap newBootstrap(SocketAddress localAddress, SocketAddress remoteAddress) {
+        return newBootstrap(localAddress, remoteAddress, handler());
+    }
 }

From df72356d7d03882a2272fb03406051b7426e3319 Mon Sep 17 00:00:00 2001
From: norman 
Date: Wed, 12 Sep 2012 14:04:41 +0200
Subject: [PATCH 17/24] Rename classes as result of descussion on #594

---
 .../netty/example/discard/DiscardClient.java  |   4 +-
 .../io/netty/example/echo/EchoClient.java     |   4 +-
 .../example/factorial/FactorialClient.java    |   4 +-
 .../example/http/snoop/HttpSnoopClient.java   |   4 +-
 .../websocketx/client/WebSocketClient.java    |   4 +-
 .../io/netty/example/localecho/LocalEcho.java |   4 +-
 .../example/localtime/LocalTimeClient.java    |   4 +-
 .../example/objectecho/ObjectEchoClient.java  |   4 +-
 .../proxy/HexDumpProxyFrontendHandler.java    |   4 +-
 .../example/qotm/QuoteOfTheMomentClient.java  |   4 +-
 .../example/qotm/QuoteOfTheMomentServer.java  |   4 +-
 .../io/netty/example/sctp/SctpEchoClient.java |   4 +-
 .../example/securechat/SecureChatClient.java  |   4 +-
 .../io/netty/example/telnet/TelnetClient.java |   4 +-
 .../io/netty/example/uptime/UptimeClient.java |   8 +-
 .../example/uptime/UptimeClientHandler.java   |   4 +-
 .../socket/AbstractClientSocketTest.java      |  10 +-
 .../socket/AbstractDatagramTest.java          |  12 +-
 .../transport/socket/AbstractSocketTest.java  |  12 +-
 .../socket/DatagramMulticastTest.java         |   4 +-
 .../transport/socket/DatagramUnicastTest.java |   4 +-
 .../transport/socket/SocketEchoTest.java      |   8 +-
 .../socket/SocketFixedLengthEchoTest.java     |   4 +-
 .../socket/SocketObjectEchoTest.java          |   4 +-
 .../SocketShutdownOutputBySelfTest.java       |   4 +-
 .../transport/socket/SocketSpdyEchoTest.java  |   4 +-
 .../transport/socket/SocketSslEchoTest.java   |   4 +-
 .../socket/SocketStringEchoTest.java          |   4 +-
 .../socket/SocketTestPermutation.java         |  82 ++---
 .../io/netty/bootstrap/AbstractBootstrap.java | 256 ++++++++++++++
 .../java/io/netty/bootstrap/Bootstrap.java    | 312 +++++++-----------
 .../io/netty/bootstrap/ClientBootstrap.java   | 182 ----------
 .../io/netty/bootstrap/ServerBootstrap.java   |   2 +-
 .../java/io/netty/channel/ChannelFuture.java  |   4 +-
 .../java/io/netty/channel/ChannelHandler.java |   1 -
 .../local/LocalChannelRegistryTest.java       |   4 +-
 36 files changed, 492 insertions(+), 493 deletions(-)
 create mode 100644 transport/src/main/java/io/netty/bootstrap/AbstractBootstrap.java
 delete mode 100644 transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java

diff --git a/example/src/main/java/io/netty/example/discard/DiscardClient.java b/example/src/main/java/io/netty/example/discard/DiscardClient.java
index b470a13041..d7837d2f4f 100644
--- a/example/src/main/java/io/netty/example/discard/DiscardClient.java
+++ b/example/src/main/java/io/netty/example/discard/DiscardClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.discard;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioSocketChannel;
@@ -36,7 +36,7 @@ public class DiscardClient {
     }
 
     public void run() throws Exception {
-        ClientBootstrap b = new ClientBootstrap();
+        Bootstrap b = new Bootstrap();
         try {
             b.group(new NioEventLoopGroup())
              .channel(NioSocketChannel.class)
diff --git a/example/src/main/java/io/netty/example/echo/EchoClient.java b/example/src/main/java/io/netty/example/echo/EchoClient.java
index ce5cb67b43..4d1e924328 100644
--- a/example/src/main/java/io/netty/example/echo/EchoClient.java
+++ b/example/src/main/java/io/netty/example/echo/EchoClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.echo;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelOption;
@@ -47,7 +47,7 @@ public class EchoClient {
 
     public void run() throws Exception {
         // Configure the client.
-        ClientBootstrap b = new ClientBootstrap();
+        Bootstrap b = new Bootstrap();
         try {
             b.group(new NioEventLoopGroup())
              .channel(NioSocketChannel.class)
diff --git a/example/src/main/java/io/netty/example/factorial/FactorialClient.java b/example/src/main/java/io/netty/example/factorial/FactorialClient.java
index ba163fa891..bb700d5b18 100644
--- a/example/src/main/java/io/netty/example/factorial/FactorialClient.java
+++ b/example/src/main/java/io/netty/example/factorial/FactorialClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.factorial;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioSocketChannel;
@@ -37,7 +37,7 @@ public class FactorialClient {
     }
 
     public void run() throws Exception {
-        ClientBootstrap b = new ClientBootstrap();
+        Bootstrap b = new Bootstrap();
         try {
             b.group(new NioEventLoopGroup())
              .channel(NioSocketChannel.class)
diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java
index 02313baea1..7f54373053 100644
--- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java
+++ b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.http.snoop;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioSocketChannel;
@@ -62,7 +62,7 @@ public class HttpSnoopClient {
         boolean ssl = scheme.equalsIgnoreCase("https");
 
         // Configure the client.
-        ClientBootstrap b = new ClientBootstrap();
+        Bootstrap b = new Bootstrap();
         try {
             b.group(new NioEventLoopGroup())
              .channel(NioSocketChannel.class)
diff --git a/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClient.java b/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClient.java
index a4beb8bde8..34e28e8617 100644
--- a/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClient.java
+++ b/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClient.java
@@ -36,7 +36,7 @@
 //THE SOFTWARE.
 package io.netty.example.http.websocketx.client;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelInitializer;
@@ -65,7 +65,7 @@ public class WebSocketClient {
     }
 
     public void run() throws Exception {
-        ClientBootstrap b = new ClientBootstrap();
+        Bootstrap b = new Bootstrap();
         try {
 
             String protocol = uri.getScheme();
diff --git a/example/src/main/java/io/netty/example/localecho/LocalEcho.java b/example/src/main/java/io/netty/example/localecho/LocalEcho.java
index 21d74614f4..c49a31e95d 100644
--- a/example/src/main/java/io/netty/example/localecho/LocalEcho.java
+++ b/example/src/main/java/io/netty/example/localecho/LocalEcho.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.localecho;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
@@ -43,7 +43,7 @@ public class LocalEcho {
         // Address to bind on / connect to.
         final LocalAddress addr = new LocalAddress(port);
 
-        ClientBootstrap cb = new ClientBootstrap();
+        Bootstrap cb = new Bootstrap();
         ServerBootstrap sb = new ServerBootstrap();
         try {
             // Note that we can use any event loop to ensure certain local channels
diff --git a/example/src/main/java/io/netty/example/localtime/LocalTimeClient.java b/example/src/main/java/io/netty/example/localtime/LocalTimeClient.java
index d20c4aed16..18590c147a 100644
--- a/example/src/main/java/io/netty/example/localtime/LocalTimeClient.java
+++ b/example/src/main/java/io/netty/example/localtime/LocalTimeClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.localtime;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioSocketChannel;
@@ -43,7 +43,7 @@ public class LocalTimeClient {
     }
 
     public void run() throws Exception {
-        ClientBootstrap b = new ClientBootstrap();
+        Bootstrap b = new Bootstrap();
         try {
             b.group(new NioEventLoopGroup())
              .channel(NioSocketChannel.class)
diff --git a/example/src/main/java/io/netty/example/objectecho/ObjectEchoClient.java b/example/src/main/java/io/netty/example/objectecho/ObjectEchoClient.java
index 24f980ca13..759055a6b2 100644
--- a/example/src/main/java/io/netty/example/objectecho/ObjectEchoClient.java
+++ b/example/src/main/java/io/netty/example/objectecho/ObjectEchoClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.objectecho;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.socket.SocketChannel;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
@@ -41,7 +41,7 @@ public class ObjectEchoClient {
     }
 
     public void run() throws Exception {
-        ClientBootstrap b = new ClientBootstrap();
+        Bootstrap b = new Bootstrap();
         try {
             b.group(new NioEventLoopGroup())
              .channel(NioSocketChannel.class)
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 ff64c96e06..f365cec7f2 100644
--- a/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java
+++ b/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.proxy;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
@@ -43,7 +43,7 @@ public class HexDumpProxyFrontendHandler extends ChannelInboundByteHandlerAdapte
         final Channel inboundChannel = ctx.channel();
 
         // Start the connection attempt.
-        ClientBootstrap b = new ClientBootstrap();
+        Bootstrap b = new Bootstrap();
         b.group(inboundChannel.eventLoop())
          .channel(NioSocketChannel.class)
          .remoteAddress(remoteHost, remotePort)
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 05e4f38110..2b18435c98 100644
--- a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClient.java
+++ b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.qotm;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelOption;
@@ -41,7 +41,7 @@ public class QuoteOfTheMomentClient {
     }
 
     public void run() throws Exception {
-        ClientBootstrap b = new ClientBootstrap();
+        Bootstrap b = new Bootstrap();
         try {
             b.group(new NioEventLoopGroup())
              .channel(NioDatagramChannel.class)
diff --git a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServer.java b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServer.java
index 21221a7002..0c67c18007 100644
--- a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServer.java
+++ b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServer.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.qotm;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.ChannelOption;
 import io.netty.channel.socket.nio.NioDatagramChannel;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
@@ -37,7 +37,7 @@ public class QuoteOfTheMomentServer {
     }
 
     public void run() throws Exception {
-        ClientBootstrap b = new ClientBootstrap();
+        Bootstrap b = new Bootstrap();
         try {
             b.group(new NioEventLoopGroup())
              .channel(NioDatagramChannel.class)
diff --git a/example/src/main/java/io/netty/example/sctp/SctpEchoClient.java b/example/src/main/java/io/netty/example/sctp/SctpEchoClient.java
index 831fb78aa6..d0a1735744 100644
--- a/example/src/main/java/io/netty/example/sctp/SctpEchoClient.java
+++ b/example/src/main/java/io/netty/example/sctp/SctpEchoClient.java
@@ -15,8 +15,8 @@
  */
 package io.netty.example.sctp;
 
+import io.netty.bootstrap.AbstractBootstrap;
 import io.netty.bootstrap.Bootstrap;
-import io.netty.bootstrap.ClientBootstrap;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelOption;
@@ -52,7 +52,7 @@ public class SctpEchoClient {
 
     public void run() throws Exception {
         // Configure the client.
-        ClientBootstrap b = new ClientBootstrap();
+        Bootstrap b = new Bootstrap();
         try {
             b.group(new NioEventLoopGroup())
              .channel(NioSctpChannel.class)
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 23b7704d2d..0c0b2acb25 100644
--- a/example/src/main/java/io/netty/example/securechat/SecureChatClient.java
+++ b/example/src/main/java/io/netty/example/securechat/SecureChatClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.securechat;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
@@ -39,7 +39,7 @@ public class SecureChatClient {
     }
 
     public void run() throws Exception {
-        ClientBootstrap b = new ClientBootstrap();
+        Bootstrap b = new Bootstrap();
         try {
             b.group(new NioEventLoopGroup())
              .channel(NioSocketChannel.class)
diff --git a/example/src/main/java/io/netty/example/telnet/TelnetClient.java b/example/src/main/java/io/netty/example/telnet/TelnetClient.java
index 7b8d4e9960..29e57bad5a 100644
--- a/example/src/main/java/io/netty/example/telnet/TelnetClient.java
+++ b/example/src/main/java/io/netty/example/telnet/TelnetClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.telnet;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.socket.nio.NioEventLoopGroup;
@@ -38,7 +38,7 @@ public class TelnetClient {
     }
 
     public void run() throws Exception {
-        ClientBootstrap b = new ClientBootstrap();
+        Bootstrap b = new Bootstrap();
         try {
             b.group(new NioEventLoopGroup())
              .channel(NioSocketChannel.class)
diff --git a/example/src/main/java/io/netty/example/uptime/UptimeClient.java b/example/src/main/java/io/netty/example/uptime/UptimeClient.java
index b95d59be73..5de19707ae 100644
--- a/example/src/main/java/io/netty/example/uptime/UptimeClient.java
+++ b/example/src/main/java/io/netty/example/uptime/UptimeClient.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.uptime;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.socket.SocketChannel;
@@ -50,14 +50,14 @@ public class UptimeClient {
     }
 
     public void run() {
-        configureBootstrap(new ClientBootstrap()).connect();
+        configureBootstrap(new Bootstrap()).connect();
     }
 
-    private ClientBootstrap configureBootstrap(ClientBootstrap b) {
+    private Bootstrap configureBootstrap(Bootstrap b) {
         return configureBootstrap(b, new NioEventLoopGroup());
     }
 
-    ClientBootstrap configureBootstrap(ClientBootstrap b, EventLoopGroup g) {
+    Bootstrap configureBootstrap(Bootstrap b, EventLoopGroup g) {
         b.group(g)
          .channel(NioSocketChannel.class)
          .remoteAddress(host, port)
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 8d278637c7..4cf69426a5 100644
--- a/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java
+++ b/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java
@@ -15,7 +15,7 @@
  */
 package io.netty.example.uptime;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandler.Sharable;
 import io.netty.channel.ChannelHandlerContext;
@@ -84,7 +84,7 @@ public class UptimeClientHandler extends ChannelInboundByteHandlerAdapter {
             @Override
             public void run() {
                 println("Reconnecting to: " + ctx.channel().remoteAddress());
-                client.configureBootstrap(new ClientBootstrap(), loop).connect();
+                client.configureBootstrap(new Bootstrap(), loop).connect();
             }
         }, UptimeClient.RECONNECT_DELAY, TimeUnit.SECONDS);
     }
diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractClientSocketTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractClientSocketTest.java
index 7f918e2957..50171a7c06 100644
--- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractClientSocketTest.java
+++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractClientSocketTest.java
@@ -15,7 +15,7 @@
  */
 package io.netty.testsuite.transport.socket;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.logging.InternalLogger;
 import io.netty.logging.InternalLoggerFactory;
 import io.netty.testsuite.transport.socket.SocketTestPermutation.Factory;
@@ -32,19 +32,19 @@ import org.junit.rules.TestName;
 
 public abstract class AbstractClientSocketTest {
 
-    private static final List> COMBO = SocketTestPermutation.clientSocket();
+    private static final List> COMBO = SocketTestPermutation.clientSocket();
 
     @Rule
     public final TestName testName = new TestName();
 
     protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
 
-    protected volatile ClientBootstrap cb;
+    protected volatile Bootstrap cb;
     protected volatile InetSocketAddress addr;
 
     protected void run() throws Throwable {
         int i = 0;
-        for (Factory e: COMBO) {
+        for (Factory e: COMBO) {
             cb = e.newInstance();
             addr = new InetSocketAddress(
                     NetworkConstants.LOCALHOST, TestUtils.getFreePort());
@@ -54,7 +54,7 @@ public abstract class AbstractClientSocketTest {
                     "Running: %s %d of %d", testName.getMethodName(), ++ i, COMBO.size()));
             try {
                 Method m = getClass().getDeclaredMethod(
-                        testName.getMethodName(), ClientBootstrap.class);
+                        testName.getMethodName(), Bootstrap.class);
                 m.invoke(this, cb);
             } catch (InvocationTargetException ex) {
                 throw ex.getCause();
diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractDatagramTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractDatagramTest.java
index 9ba72e78e5..53cdc3665b 100644
--- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractDatagramTest.java
+++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractDatagramTest.java
@@ -15,8 +15,8 @@
  */
 package io.netty.testsuite.transport.socket;
 
+import io.netty.bootstrap.AbstractBootstrap;
 import io.netty.bootstrap.Bootstrap;
-import io.netty.bootstrap.ClientBootstrap;
 import io.netty.logging.InternalLogger;
 import io.netty.logging.InternalLoggerFactory;
 import io.netty.testsuite.transport.socket.SocketTestPermutation.Factory;
@@ -34,7 +34,7 @@ import org.junit.rules.TestName;
 
 public abstract class AbstractDatagramTest {
 
-    private static final List, Factory>> COMBO =
+    private static final List, Factory>> COMBO =
             SocketTestPermutation.datagram();
 
     @Rule
@@ -42,13 +42,13 @@ public abstract class AbstractDatagramTest {
 
     protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
 
-    protected volatile ClientBootstrap sb;
-    protected volatile ClientBootstrap cb;
+    protected volatile Bootstrap sb;
+    protected volatile Bootstrap cb;
     protected volatile InetSocketAddress addr;
 
     protected void run() throws Throwable {
         int i = 0;
-        for (Entry, Factory> e: COMBO) {
+        for (Entry, Factory> e: COMBO) {
             sb = e.getKey().newInstance();
             cb = e.getValue().newInstance();
             addr = new InetSocketAddress(
@@ -60,7 +60,7 @@ public abstract class AbstractDatagramTest {
                     "Running: %s %d of %d", testName.getMethodName(), ++ i, COMBO.size()));
             try {
                 Method m = getClass().getDeclaredMethod(
-                        testName.getMethodName(), Bootstrap.class, Bootstrap.class);
+                        testName.getMethodName(), AbstractBootstrap.class, AbstractBootstrap.class);
                 m.invoke(this, sb, cb);
             } catch (InvocationTargetException ex) {
                 throw ex.getCause();
diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSocketTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSocketTest.java
index dc6b8ff791..5651235d34 100644
--- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSocketTest.java
+++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/AbstractSocketTest.java
@@ -15,7 +15,7 @@
  */
 package io.netty.testsuite.transport.socket;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.logging.InternalLogger;
 import io.netty.logging.InternalLoggerFactory;
@@ -34,7 +34,7 @@ import org.junit.rules.TestName;
 
 public abstract class AbstractSocketTest {
 
-    private static final List, Factory>> COMBO =
+    private static final List, Factory>> COMBO =
             SocketTestPermutation.socket();
 
     @Rule
@@ -43,13 +43,13 @@ public abstract class AbstractSocketTest {
     protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
 
     protected volatile ServerBootstrap sb;
-    protected volatile ClientBootstrap cb;
+    protected volatile Bootstrap cb;
     protected volatile InetSocketAddress addr;
-    protected volatile Factory currentBootstrap;
+    protected volatile Factory currentBootstrap;
     
     protected void run() throws Throwable {
         int i = 0;
-        for (Entry, Factory> e: COMBO) {
+        for (Entry, Factory> e: COMBO) {
             currentBootstrap = e.getValue();
             sb = e.getKey().newInstance();
             cb = e.getValue().newInstance();
@@ -62,7 +62,7 @@ public abstract class AbstractSocketTest {
                     "Running: %s %d of %d", testName.getMethodName(), ++ i, COMBO.size()));
             try {
                 Method m = getClass().getDeclaredMethod(
-                        testName.getMethodName(), ServerBootstrap.class, ClientBootstrap.class);
+                        testName.getMethodName(), ServerBootstrap.class, Bootstrap.class);
                 m.invoke(this, sb, cb);
             } catch (InvocationTargetException ex) {
                 throw ex.getCause();
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 b37ddbfd3b..79489ea71f 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.AbstractBootstrap;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
@@ -40,7 +40,7 @@ public class DatagramMulticastTest extends AbstractDatagramTest {
         run();
     }
 
-    public void testMulticast(Bootstrap sb, Bootstrap cb) throws Throwable {
+    public void testMulticast(AbstractBootstrap sb, AbstractBootstrap cb) throws Throwable {
         MulticastTestHandler mhandler = new MulticastTestHandler();
 
         sb.handler(new ChannelInboundMessageHandlerAdapter() {
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 3388d9d9ba..e8603816aa 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.AbstractBootstrap;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
@@ -36,7 +36,7 @@ public class DatagramUnicastTest extends AbstractDatagramTest {
         run();
     }
 
-    public void testSimpleSend(Bootstrap sb, Bootstrap cb) throws Throwable {
+    public void testSimpleSend(AbstractBootstrap sb, AbstractBootstrap cb) throws Throwable {
         final CountDownLatch latch = new CountDownLatch(1);
 
         sb.handler(new ChannelInboundMessageHandlerAdapter() {
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 def906d2e2..3c0eaf37da 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
@@ -44,7 +44,7 @@ public class SocketEchoTest extends AbstractSocketTest {
         run();
     }
 
-    public void testSimpleEcho(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
+    public void testSimpleEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable {
         testSimpleEcho0(sb, cb, Integer.MAX_VALUE);
     }
 
@@ -53,11 +53,11 @@ public class SocketEchoTest extends AbstractSocketTest {
         run();
     }
 
-    public void testSimpleEchoWithBoundedBuffer(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
+    public void testSimpleEchoWithBoundedBuffer(ServerBootstrap sb, Bootstrap cb) throws Throwable {
         testSimpleEcho0(sb, cb, 32);
     }
 
-    private static void testSimpleEcho0(ServerBootstrap sb, ClientBootstrap cb, int maxInboundBufferSize) throws Throwable {
+    private static void testSimpleEcho0(ServerBootstrap sb, Bootstrap cb, int maxInboundBufferSize) throws Throwable {
         EchoHandler sh = new EchoHandler(maxInboundBufferSize);
         EchoHandler ch = new EchoHandler(maxInboundBufferSize);
 
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 a5a5eb6e68..edc5d746ec 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
@@ -47,7 +47,7 @@ public class SocketFixedLengthEchoTest extends AbstractSocketTest {
         run();
     }
 
-    public void testFixedLengthEcho(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
+    public void testFixedLengthEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable {
         final EchoHandler sh = new EchoHandler();
         final EchoHandler ch = new EchoHandler();
 
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 97d0dc67f0..5df1cd5c8a 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
@@ -55,7 +55,7 @@ public class SocketObjectEchoTest extends AbstractSocketTest {
         run();
     }
 
-    public void testObjectEcho(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
+    public void testObjectEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable {
         final EchoHandler sh = new EchoHandler();
         final EchoHandler ch = new EchoHandler();
 
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 5ffb5fa377..c61e857b3b 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
@@ -37,7 +37,7 @@ public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest {
         run();
     }
 
-    public void testShutdownOutput(ClientBootstrap cb) throws Throwable {
+    public void testShutdownOutput(Bootstrap cb) throws Throwable {
         TestHandler h = new TestHandler();
         ServerSocket ss = new ServerSocket();
         Socket s = null;
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 a1c4dccc67..6cecdb098c 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
@@ -175,7 +175,7 @@ public class SocketSpdyEchoTest extends AbstractSocketTest {
         }
     }
 
-    public void testSpdyEcho(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
+    public void testSpdyEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable {
 
         ByteBuf frames = createFrames(version);
 
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 0676d1ebfb..f521916a58 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
@@ -67,7 +67,7 @@ public class SocketSslEchoTest extends AbstractSocketTest {
         run();
     }
 
-    public void testSslEcho(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
+    public void testSslEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable {
         final EchoHandler sh = new EchoHandler(true);
         final EchoHandler ch = new EchoHandler(false);
 
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 6236b11af0..641c1d4df2 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
@@ -16,7 +16,7 @@
 package io.netty.testsuite.transport.socket;
 
 import static org.junit.Assert.*;
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
@@ -57,7 +57,7 @@ public class SocketStringEchoTest extends AbstractSocketTest {
         run();
     }
 
-    public void testStringEcho(ServerBootstrap sb, ClientBootstrap cb) throws Throwable {
+    public void testStringEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable {
         final StringEchoHandler sh = new StringEchoHandler();
         final StringEchoHandler ch = new StringEchoHandler();
 
diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java
index 994272594b..ab35cb658c 100644
--- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java
+++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java
@@ -15,8 +15,8 @@
  */
 package io.netty.testsuite.transport.socket;
 
-import io.netty.bootstrap.Bootstrap.ChannelFactory;
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.AbstractBootstrap.ChannelFactory;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.socket.InternetProtocolFamily;
@@ -38,34 +38,34 @@ import java.util.Map.Entry;
 
 final class SocketTestPermutation {
 
-    static List, Factory>> socket() {
-        List, Factory>> list =
-                new ArrayList, Factory>>();
+    static List, Factory>> socket() {
+        List, Factory>> list =
+                new ArrayList, Factory>>();
 
         // Make the list of ServerBootstrap factories.
         List> sbfs = serverSocket();
 
         // Make the list of Bootstrap factories.
-        List> cbfs = clientSocket();
+        List> cbfs = clientSocket();
 
         // Populate the combinations
         for (Factory sbf: sbfs) {
-            for (Factory cbf: cbfs) {
+            for (Factory cbf: cbfs) {
                 final Factory sbf0 = sbf;
-                final Factory cbf0 = cbf;
-                list.add(new Entry, Factory>() {
+                final Factory cbf0 = cbf;
+                list.add(new Entry, Factory>() {
                     @Override
                     public Factory getKey() {
                         return sbf0;
                     }
 
                     @Override
-                    public Factory getValue() {
+                    public Factory getValue() {
                         return cbf0;
                     }
 
                     @Override
-                    public Factory setValue(Factory value) {
+                    public Factory setValue(Factory value) {
                         throw new UnsupportedOperationException();
                     }
                 });
@@ -78,17 +78,17 @@ final class SocketTestPermutation {
         return list;
     }
 
-    static List, Factory>> datagram() {
-        List, Factory>> list =
-                new ArrayList, Factory>>();
+    static List, Factory>> datagram() {
+        List, Factory>> list =
+                new ArrayList, Factory>>();
 
         // Make the list of Bootstrap factories.
-        List> bfs =
-                new ArrayList>();
-        bfs.add(new Factory() {
+        List> bfs =
+                new ArrayList>();
+        bfs.add(new Factory() {
             @Override
-            public ClientBootstrap newInstance() {
-                return new ClientBootstrap().group(new NioEventLoopGroup()).channelFactory(new ChannelFactory() {
+            public Bootstrap newInstance() {
+                return new Bootstrap().group(new NioEventLoopGroup()).channelFactory(new ChannelFactory() {
                     @Override
                     public Channel newChannel() {
                        return new NioDatagramChannel(InternetProtocolFamily.IPv4);
@@ -96,31 +96,31 @@ final class SocketTestPermutation {
                 });
             }
         });
-        bfs.add(new Factory() {
+        bfs.add(new Factory() {
             @Override
-            public ClientBootstrap newInstance() {
-                return new ClientBootstrap().group(new OioEventLoopGroup()).channel(OioDatagramChannel.class);
+            public Bootstrap newInstance() {
+                return new Bootstrap().group(new OioEventLoopGroup()).channel(OioDatagramChannel.class);
             }
         });
 
         // Populate the combinations
-        for (Factory sbf: bfs) {
-            for (Factory cbf: bfs) {
-                final Factory sbf0 = sbf;
-                final Factory cbf0 = cbf;
-                list.add(new Entry, Factory>() {
+        for (Factory sbf: bfs) {
+            for (Factory cbf: bfs) {
+                final Factory sbf0 = sbf;
+                final Factory cbf0 = cbf;
+                list.add(new Entry, Factory>() {
                     @Override
-                    public Factory getKey() {
+                    public Factory getKey() {
                         return sbf0;
                     }
 
                     @Override
-                    public Factory getValue() {
+                    public Factory getValue() {
                         return cbf0;
                     }
 
                     @Override
-                    public Factory setValue(Factory value) {
+                    public Factory setValue(Factory value) {
                         throw new UnsupportedOperationException();
                     }
                 });
@@ -168,19 +168,19 @@ final class SocketTestPermutation {
         return list;
     }
 
-    static List> clientSocket() {
-        List> list = new ArrayList>();
-        list.add(new Factory() {
+    static List> clientSocket() {
+        List> list = new ArrayList>();
+        list.add(new Factory() {
             @Override
-            public ClientBootstrap newInstance() {
-                return new ClientBootstrap().group(new NioEventLoopGroup()).channel(NioSocketChannel.class);
+            public Bootstrap newInstance() {
+                return new Bootstrap().group(new NioEventLoopGroup()).channel(NioSocketChannel.class);
             }
         });
-        list.add(new Factory() {
+        list.add(new Factory() {
             @Override
-            public ClientBootstrap newInstance() {
+            public Bootstrap newInstance() {
                 final AioEventLoopGroup loop = new AioEventLoopGroup();
-                return new ClientBootstrap().group(loop).channelFactory(new ChannelFactory() {
+                return new Bootstrap().group(loop).channelFactory(new ChannelFactory() {
                     @Override
                     public Channel newChannel() {
                         return new AioSocketChannel(loop);
@@ -188,10 +188,10 @@ final class SocketTestPermutation {
                 });
             }
         });
-        list.add(new Factory() {
+        list.add(new Factory() {
             @Override
-            public ClientBootstrap newInstance() {
-                return new ClientBootstrap().group(new OioEventLoopGroup()).channel(OioSocketChannel.class);
+            public Bootstrap newInstance() {
+                return new Bootstrap().group(new OioEventLoopGroup()).channel(OioSocketChannel.class);
             }
         });
         return list;
diff --git a/transport/src/main/java/io/netty/bootstrap/AbstractBootstrap.java b/transport/src/main/java/io/netty/bootstrap/AbstractBootstrap.java
new file mode 100644
index 0000000000..b2b5dd2e45
--- /dev/null
+++ b/transport/src/main/java/io/netty/bootstrap/AbstractBootstrap.java
@@ -0,0 +1,256 @@
+/*
+ * 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.bootstrap;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import io.netty.channel.Channel;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelException;
+
+/**
+ * {@link AbstractBootstrap} is a helper class that makes it easy to bootstrap a {@link Channel}. It support
+ * method-chaining to provide an easy way to configure the {@link AbstractBootstrap}.
+ *
+ */
+public abstract class AbstractBootstrap> {
+    private EventLoopGroup group;
+    private ChannelFactory factory;
+    private SocketAddress localAddress;
+    private final Map, Object> options = new LinkedHashMap, Object>();
+    private ChannelHandler handler;
+
+    /**
+     * The {@link EventLoopGroup} which is used to handle all the events for the to-be-creates
+     * {@link Channel}
+     */
+    @SuppressWarnings("unchecked")
+    public B group(EventLoopGroup group) {
+        if (group == null) {
+            throw new NullPointerException("group");
+        }
+        if (this.group != null) {
+            throw new IllegalStateException("group set already");
+        }
+        this.group = group;
+        return (B) this;
+    }
+
+    /**
+     * The {@link Class} which is used to create {@link Channel} instances from.
+     * You either use this or {@link #channelFactory(ChannelFactory)} if your
+     * {@link Channel} implementation has no no-args constructor.
+     */
+    public B channel(Class channelClass) {
+        if (channelClass == null) {
+            throw new NullPointerException("channelClass");
+        }
+        return channelFactory(new BootstrapChannelFactory(channelClass));
+    }
+
+    /**
+     * {@link ChannelFactory} which is used to create {@link Channel} instances from
+     * when calling {@link #bind()}. This method is usually only used if {@link #channel(Class)}
+     * is not working for you because of some more complex needs. If your {@link Channel} implementation
+     * has a no-args constructor, its highly recommend to just use {@link #channel(Class)} for
+     * simplify your code.
+     */
+    @SuppressWarnings("unchecked")
+    public B channelFactory(ChannelFactory factory) {
+        if (factory == null) {
+            throw new NullPointerException("factory");
+        }
+        if (this.factory != null) {
+            throw new IllegalStateException("factory set already");
+        }
+
+        this.factory = factory;
+        return (B) this;
+    }
+
+    /**
+     * The {@link SocketAddress} which is used to bind the local "end" to.
+     *
+     */
+    @SuppressWarnings("unchecked")
+    public B localAddress(SocketAddress localAddress) {
+        this.localAddress = localAddress;
+        return (B) this;
+    }
+
+    /**
+     * See {@link #localAddress(SocketAddress)}
+     */
+    public B localAddress(int port) {
+        return localAddress(new InetSocketAddress(port));
+    }
+
+    /**
+     * See {@link #localAddress(SocketAddress)}
+     */
+    public B localAddress(String host, int port) {
+        return localAddress(new InetSocketAddress(host, port));
+    }
+
+    /**
+     * See {@link #localAddress(SocketAddress)}
+     */
+    public B localAddress(InetAddress host, int port) {
+        return localAddress(new InetSocketAddress(host, port));
+    }
+
+    /**
+     * Allow to specify a {@link ChannelOption} which is used for the {@link Channel} instances once they got
+     * created. Use a value of null to remove a previous set {@link ChannelOption}.
+     */
+    @SuppressWarnings("unchecked")
+    public  B option(ChannelOption option, T value) {
+        if (option == null) {
+            throw new NullPointerException("option");
+        }
+        if (value == null) {
+            options.remove(option);
+        } else {
+            options.put(option, value);
+        }
+        return (B) this;
+    }
+
+    /**
+     * Shutdown the {@link AbstractBootstrap} and the {@link EventLoopGroup} which is
+     * used by it. Only call this if you don't share the {@link EventLoopGroup}
+     * between different {@link AbstractBootstrap}'s.
+     */
+    public void shutdown() {
+        if (group != null) {
+            group.shutdown();
+        }
+    }
+
+    /**
+     * Validate all the parameters. Sub-classes may override this, but should
+     * call the super method in that case.
+     */
+    protected void validate() {
+        if (group == null) {
+            throw new IllegalStateException("group not set");
+        }
+        if (factory == null) {
+            throw new IllegalStateException("factory not set");
+        }
+    }
+
+    protected final void validate(ChannelFuture future) {
+        if (future == null) {
+            throw new NullPointerException("future");
+        }
+        validate();
+    }
+
+    /**
+     * Create a new {@link Channel} and bind it.
+     */
+    public ChannelFuture bind() {
+        validate();
+        Channel channel = factory().newChannel();
+        return bind(channel.newFuture());
+    }
+
+    /**
+     * the {@link ChannelHandler} to use for serving the requests.
+     */
+    @SuppressWarnings("unchecked")
+    public B handler(ChannelHandler handler) {
+        if (handler == null) {
+            throw new NullPointerException("handler");
+        }
+        this.handler = handler;
+        return (B) this;
+    }
+
+    protected static boolean ensureOpen(ChannelFuture future) {
+        if (!future.channel().isOpen()) {
+            // Registration was successful but the channel was closed due to some failure in
+            // handler.
+            future.setFailure(new ChannelException("initialization failure"));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Bind the {@link Channel} of the given {@link ChannelFactory}.
+     */
+    public abstract ChannelFuture bind(ChannelFuture future);
+
+    protected final SocketAddress localAddress() {
+        return localAddress;
+    }
+
+    protected final ChannelFactory factory() {
+        return factory;
+    }
+
+    protected final ChannelHandler handler() {
+        return handler;
+    }
+
+    protected final EventLoopGroup group() {
+        return group;
+    }
+
+    protected final Map, Object> options() {
+        return options;
+    }
+
+    private final class BootstrapChannelFactory implements ChannelFactory {
+        private final Class clazz;
+
+        BootstrapChannelFactory(Class clazz) {
+            this.clazz = clazz;
+        }
+
+        @Override
+        public Channel newChannel() {
+            try {
+                return clazz.newInstance();
+            } catch (Throwable t) {
+                throw new ChannelException("Unable to create Channel from class " + clazz, t);
+            }
+        }
+
+    }
+
+    /**
+     * Factory that is responsible to create new {@link Channel}'s on {@link AbstractBootstrap#bind()}
+     * requests.
+     *
+     */
+    public interface ChannelFactory {
+        /**
+         * {@link Channel} to use in the {@link AbstractBootstrap}
+         */
+        Channel newChannel();
+    }
+}
diff --git a/transport/src/main/java/io/netty/bootstrap/Bootstrap.java b/transport/src/main/java/io/netty/bootstrap/Bootstrap.java
index a300e9730b..a466274120 100644
--- a/transport/src/main/java/io/netty/bootstrap/Bootstrap.java
+++ b/transport/src/main/java/io/netty/bootstrap/Bootstrap.java
@@ -13,244 +13,170 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-
 package io.netty.bootstrap;
 
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelOption;
+import io.netty.logging.InternalLogger;
+import io.netty.logging.InternalLoggerFactory;
+
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import io.netty.channel.Channel;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelOption;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelException;
+import java.nio.channels.ClosedChannelException;
+import java.util.Map.Entry;
 
 /**
- * {@link Bootstrap} is a helper class that makes it easy to bootstrap a {@link Channel}. It support
- * method-chaining to provide an easy way to configure the {@link Bootstrap}.
+ * A {@link Bootstrap} that makes it easy to bootstrap a {@link Channel} to use
+ * for clients.
  *
  */
-public abstract class Bootstrap> {
-    private EventLoopGroup group;
-    private ChannelFactory factory;
-    private SocketAddress localAddress;
-    private final Map, Object> options = new LinkedHashMap, Object>();
-    private ChannelHandler handler;
+public class Bootstrap extends AbstractBootstrap {
+
+    private static final InternalLogger logger = InternalLoggerFactory.getInstance(Bootstrap.class);
+    private SocketAddress remoteAddress;
+
 
     /**
-     * The {@link EventLoopGroup} which is used to handle all the events for the to-be-creates
-     * {@link Channel}
+     * The {@link SocketAddress} to connect to once the {@link #connect()} method
+     * is called.
      */
-    @SuppressWarnings("unchecked")
-    public B group(EventLoopGroup group) {
-        if (group == null) {
-            throw new NullPointerException("group");
-        }
-        if (this.group != null) {
-            throw new IllegalStateException("group set already");
-        }
-        this.group = group;
-        return (B) this;
+    public Bootstrap remoteAddress(SocketAddress remoteAddress) {
+        this.remoteAddress = remoteAddress;
+        return this;
     }
 
     /**
-     * The {@link Class} which is used to create {@link Channel} instances from.
-     * You either use this or {@link #channelFactory(ChannelFactory)} if your
-     * {@link Channel} implementation has no no-args constructor.
+     * See {@link #remoteAddress(SocketAddress)}
      */
-    public B channel(Class channelClass) {
-        if (channelClass == null) {
-            throw new NullPointerException("channelClass");
-        }
-        return channelFactory(new BootstrapChannelFactory(channelClass));
+    public Bootstrap remoteAddress(String host, int port) {
+        remoteAddress = new InetSocketAddress(host, port);
+        return this;
     }
 
     /**
-     * {@link ChannelFactory} which is used to create {@link Channel} instances from
-     * when calling {@link #bind()}. This method is usually only used if {@link #channel(Class)}
-     * is not working for you because of some more complex needs. If your {@link Channel} implementation
-     * has a no-args constructor, its highly recommend to just use {@link #channel(Class)} for
-     * simplify your code.
+     * See {@link #remoteAddress(SocketAddress)}
      */
-    @SuppressWarnings("unchecked")
-    public B channelFactory(ChannelFactory factory) {
-        if (factory == null) {
-            throw new NullPointerException("factory");
-        }
-        if (this.factory != null) {
-            throw new IllegalStateException("factory set already");
+    public Bootstrap remoteAddress(InetAddress host, int port) {
+        remoteAddress = new InetSocketAddress(host, port);
+        return this;
+    }
+
+    @Override
+    public ChannelFuture bind(ChannelFuture future) {
+        validate(future);
+        if (localAddress() == null) {
+            throw new IllegalStateException("localAddress not set");
         }
 
-        this.factory = factory;
-        return (B) this;
+        try {
+            init(future.channel());
+        } catch (Throwable t) {
+            future.setFailure(t);
+            return future;
+        }
+
+        if (!ensureOpen(future)) {
+            return future;
+        }
+
+        return future.channel().bind(localAddress(), future).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
     }
 
     /**
-     * The {@link SocketAddress} which is used to bind the local "end" to.
-     *
+     * Connect a {@link Channel} to the remote peer.
      */
-    @SuppressWarnings("unchecked")
-    public B localAddress(SocketAddress localAddress) {
-        this.localAddress = localAddress;
-        return (B) this;
-    }
-
-    /**
-     * See {@link #localAddress(SocketAddress)}
-     */
-    public B localAddress(int port) {
-        return localAddress(new InetSocketAddress(port));
-    }
-
-    /**
-     * See {@link #localAddress(SocketAddress)}
-     */
-    public B localAddress(String host, int port) {
-        return localAddress(new InetSocketAddress(host, port));
-    }
-
-    /**
-     * See {@link #localAddress(SocketAddress)}
-     */
-    public B localAddress(InetAddress host, int port) {
-        return localAddress(new InetSocketAddress(host, port));
-    }
-
-    /**
-     * Allow to specify a {@link ChannelOption} which is used for the {@link Channel} instances once they got
-     * created. Use a value of null to remove a previous set {@link ChannelOption}.
-     */
-    @SuppressWarnings("unchecked")
-    public  B option(ChannelOption option, T value) {
-        if (option == null) {
-            throw new NullPointerException("option");
-        }
-        if (value == null) {
-            options.remove(option);
-        } else {
-            options.put(option, value);
-        }
-        return (B) this;
-    }
-
-    /**
-     * Shutdown the {@link Bootstrap} and the {@link EventLoopGroup} which is
-     * used by it. Only call this if you don't share the {@link EventLoopGroup}
-     * between different {@link Bootstrap}'s.
-     */
-    public void shutdown() {
-        if (group != null) {
-            group.shutdown();
-        }
-    }
-
-    /**
-     * Validate all the parameters. Sub-classes may override this, but should
-     * call the super method in that case.
-     */
-    protected void validate() {
-        if (group == null) {
-            throw new IllegalStateException("group not set");
-        }
-        if (factory == null) {
-            throw new IllegalStateException("factory not set");
-        }
-    }
-
-    protected final void validate(ChannelFuture future) {
-        if (future == null) {
-            throw new NullPointerException("future");
-        }
-        validate();
-    }
-
-    /**
-     * Create a new {@link Channel} and bind it.
-     */
-    public ChannelFuture bind() {
+    public ChannelFuture connect() {
         validate();
         Channel channel = factory().newChannel();
-        return bind(channel.newFuture());
+        return connect(channel.newFuture());
     }
 
     /**
-     * the {@link ChannelHandler} to use for serving the requests.
+     * See {@link #connect()}
      */
+    public ChannelFuture connect(ChannelFuture future) {
+        validate(future);
+        if (remoteAddress == null) {
+            throw new IllegalStateException("remoteAddress not set");
+        }
+
+        try {
+            init(future.channel());
+        } catch (Throwable t) {
+            future.setFailure(t);
+            return future;
+        }
+
+        if (!ensureOpen(future)) {
+            return future;
+        }
+
+        if (localAddress() == null) {
+            future.channel().connect(remoteAddress, future);
+        } else {
+            future.channel().connect(remoteAddress, localAddress(), future);
+        }
+        return future.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
+    }
+
     @SuppressWarnings("unchecked")
-    public B handler(ChannelHandler handler) {
-        if (handler == null) {
-            throw new NullPointerException("handler");
+    private void init(Channel channel) throws Exception {
+        if (channel.isActive()) {
+            throw new IllegalStateException("channel already active:: " + channel);
         }
-        this.handler = handler;
-        return (B) this;
-    }
-
-    protected static boolean ensureOpen(ChannelFuture future) {
-        if (!future.channel().isOpen()) {
-            // Registration was successful but the channel was closed due to some failure in
-            // handler.
-            future.setFailure(new ChannelException("initialization failure"));
-            return false;
+        if (channel.isRegistered()) {
+            throw new IllegalStateException("channel already registered: " + channel);
         }
-        return true;
-    }
-
-    /**
-     * Bind the {@link Channel} of the given {@link ChannelFactory}.
-     */
-    public abstract ChannelFuture bind(ChannelFuture future);
-
-    protected final SocketAddress localAddress() {
-        return localAddress;
-    }
-
-    protected final ChannelFactory factory() {
-        return factory;
-    }
-
-    protected final ChannelHandler handler() {
-        return handler;
-    }
-
-    protected final EventLoopGroup group() {
-        return group;
-    }
-
-    protected final Map, Object> options() {
-        return options;
-    }
-
-    private final class BootstrapChannelFactory implements ChannelFactory {
-        private final Class clazz;
-
-        BootstrapChannelFactory(Class clazz) {
-            this.clazz = clazz;
+        if (!channel.isOpen()) {
+            throw new ClosedChannelException();
         }
 
-        @Override
-        public Channel newChannel() {
+        ChannelPipeline p = channel.pipeline();
+        p.addLast(handler());
+
+        for (Entry, Object> e: options().entrySet()) {
             try {
-                return clazz.newInstance();
+                if (!channel.config().setOption((ChannelOption) e.getKey(), e.getValue())) {
+                    logger.warn("Unknown channel option: " + e);
+                }
             } catch (Throwable t) {
-                throw new ChannelException("Unable to create Channel from class " + clazz, t);
+                logger.warn("Failed to set a channel option: " + channel, t);
             }
         }
 
+        group().register(channel).syncUninterruptibly();
+    }
+
+    @Override
+    protected void validate() {
+        super.validate();
+        if (handler() == null) {
+            throw new IllegalStateException("handler not set");
+        }
     }
 
     /**
-     * Factory that is responsible to create new {@link Channel}'s on {@link Bootstrap#bind()}
-     * requests.
-     *
+     * Create a new {@link Bootstrap} using this "full-setup" {@link Bootstrap} as template.
+     * Only the given parameters are replaced, the rest is configured exactly the same way as the template.
      */
-    public interface ChannelFactory {
-        /**
-         * {@link Channel} to use in the {@link Bootstrap}
-         */
-        Channel newChannel();
+    public Bootstrap newBootstrap(SocketAddress localAddress, SocketAddress remoteAddress, ChannelHandler handler) {
+        validate();
+        Bootstrap cb = new Bootstrap().handler(handler).channelFactory(factory()).group(group())
+                .localAddress(localAddress).remoteAddress(remoteAddress);
+        cb.options().putAll(options());
+        return cb;
+    }
+
+    /**
+     * Create a new {@link Bootstrap} using this "full-setup" {@link Bootstrap} as template.
+     * Only the given parameters are replaced, the rest is configured exactly the same way as the template.
+     */
+    public Bootstrap newBootstrap(SocketAddress localAddress, SocketAddress remoteAddress) {
+        return newBootstrap(localAddress, remoteAddress, handler());
     }
 }
diff --git a/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java b/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java
deleted file mode 100644
index 083922cfc0..0000000000
--- a/transport/src/main/java/io/netty/bootstrap/ClientBootstrap.java
+++ /dev/null
@@ -1,182 +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.bootstrap;
-
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelPipeline;
-import io.netty.channel.ChannelFutureListener;
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelOption;
-import io.netty.logging.InternalLogger;
-import io.netty.logging.InternalLoggerFactory;
-
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.nio.channels.ClosedChannelException;
-import java.util.Map.Entry;
-
-/**
- * A {@link Bootstrap} that makes it easy to bootstrap a {@link Channel} to use
- * for clients.
- *
- */
-public class ClientBootstrap extends Bootstrap {
-
-    private static final InternalLogger logger = InternalLoggerFactory.getInstance(ClientBootstrap.class);
-    private SocketAddress remoteAddress;
-
-
-    /**
-     * The {@link SocketAddress} to connect to once the {@link #connect()} method
-     * is called.
-     */
-    public ClientBootstrap remoteAddress(SocketAddress remoteAddress) {
-        this.remoteAddress = remoteAddress;
-        return this;
-    }
-
-    /**
-     * See {@link #remoteAddress(SocketAddress)}
-     */
-    public ClientBootstrap remoteAddress(String host, int port) {
-        remoteAddress = new InetSocketAddress(host, port);
-        return this;
-    }
-
-    /**
-     * See {@link #remoteAddress(SocketAddress)}
-     */
-    public ClientBootstrap remoteAddress(InetAddress host, int port) {
-        remoteAddress = new InetSocketAddress(host, port);
-        return this;
-    }
-
-    @Override
-    public ChannelFuture bind(ChannelFuture future) {
-        validate(future);
-        if (localAddress() == null) {
-            throw new IllegalStateException("localAddress not set");
-        }
-
-        try {
-            init(future.channel());
-        } catch (Throwable t) {
-            future.setFailure(t);
-            return future;
-        }
-
-        if (!ensureOpen(future)) {
-            return future;
-        }
-
-        return future.channel().bind(localAddress(), future).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
-    }
-
-    /**
-     * Connect a {@link Channel} to the remote peer.
-     */
-    public ChannelFuture connect() {
-        validate();
-        Channel channel = factory().newChannel();
-        return connect(channel.newFuture());
-    }
-
-    /**
-     * See {@link #connect()}
-     */
-    public ChannelFuture connect(ChannelFuture future) {
-        validate(future);
-        if (remoteAddress == null) {
-            throw new IllegalStateException("remoteAddress not set");
-        }
-
-        try {
-            init(future.channel());
-        } catch (Throwable t) {
-            future.setFailure(t);
-            return future;
-        }
-
-        if (!ensureOpen(future)) {
-            return future;
-        }
-
-        if (localAddress() == null) {
-            future.channel().connect(remoteAddress, future);
-        } else {
-            future.channel().connect(remoteAddress, localAddress(), future);
-        }
-        return future.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
-    }
-
-    @SuppressWarnings("unchecked")
-    private void init(Channel channel) throws Exception {
-        if (channel.isActive()) {
-            throw new IllegalStateException("channel already active:: " + channel);
-        }
-        if (channel.isRegistered()) {
-            throw new IllegalStateException("channel already registered: " + channel);
-        }
-        if (!channel.isOpen()) {
-            throw new ClosedChannelException();
-        }
-
-        ChannelPipeline p = channel.pipeline();
-        p.addLast(handler());
-
-        for (Entry, Object> e: options().entrySet()) {
-            try {
-                if (!channel.config().setOption((ChannelOption) e.getKey(), e.getValue())) {
-                    logger.warn("Unknown channel option: " + e);
-                }
-            } catch (Throwable t) {
-                logger.warn("Failed to set a channel option: " + channel, t);
-            }
-        }
-
-        group().register(channel).syncUninterruptibly();
-    }
-
-    @Override
-    protected void validate() {
-        super.validate();
-        if (handler() == null) {
-            throw new IllegalStateException("handler not set");
-        }
-    }
-
-    /**
-     * Create a new {@link ClientBootstrap} using this "full-setup" {@link ClientBootstrap} as template. 
-     * Only the given parameters are replaced, the rest is configured exactly the same way as the template.
-     */
-    public ClientBootstrap newBootstrap(SocketAddress localAddress, SocketAddress remoteAddress, ChannelHandler handler) {
-        validate();
-        ClientBootstrap cb = new ClientBootstrap().handler(handler).channelFactory(factory()).group(group()).localAddress(localAddress).remoteAddress(remoteAddress);
-        cb.options().putAll(options());
-        
-        return cb;
-    }
-
-    /**
-     * Create a new {@link ClientBootstrap} using this "full-setup" {@link ClientBootstrap} as template. 
-     * Only the given parameters are replaced, the rest is configured exactly the same way as the template.
-     */
-    public ClientBootstrap newBootstrap(SocketAddress localAddress, SocketAddress remoteAddress) {
-        return newBootstrap(localAddress, remoteAddress, handler());
-    }
-}
diff --git a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java
index 6c06357053..c57b4b2afc 100644
--- a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java
+++ b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java
@@ -45,7 +45,7 @@ import java.util.Map.Entry;
  * {@link Bootstrap} sub-class which allows easy bootstrap of {@link ServerChannel}
  *
  */
-public class ServerBootstrap extends Bootstrap {
+public class ServerBootstrap extends AbstractBootstrap {
 
     private static final InternalLogger logger = InternalLoggerFactory.getInstance(ServerBootstrap.class);
     private static final InetSocketAddress DEFAULT_LOCAL_ADDR = new InetSocketAddress(NetworkConstants.LOCALHOST, 0);
diff --git a/transport/src/main/java/io/netty/channel/ChannelFuture.java b/transport/src/main/java/io/netty/channel/ChannelFuture.java
index 98dca98a07..a464d04bcb 100644
--- a/transport/src/main/java/io/netty/channel/ChannelFuture.java
+++ b/transport/src/main/java/io/netty/channel/ChannelFuture.java
@@ -129,7 +129,7 @@ import java.util.concurrent.TimeUnit;
  * connect timeout should be configured via a transport-specific option:
  * 
  * // BAD - NEVER DO THIS
- * {@link ClientBootstrap} b = ...;
+ * {@link Bootstrap} b = ...;
  * {@link ChannelFuture} f = b.connect(...);
  * f.awaitUninterruptibly(10, TimeUnit.SECONDS);
  * if (f.isCancelled()) {
@@ -143,7 +143,7 @@ import java.util.concurrent.TimeUnit;
  * }
  *
  * // GOOD
- * {@link ClientBootstrap} b = ...;
+ * {@link Bootstrap} b = ...;
  * // Configure the connect timeout option.
  * b.setOption("connectTimeoutMillis", 10000);
  * {@link ChannelFuture} f = b.connect(...);
diff --git a/transport/src/main/java/io/netty/channel/ChannelHandler.java b/transport/src/main/java/io/netty/channel/ChannelHandler.java
index 2b93ff0e52..436b0f18b8 100644
--- a/transport/src/main/java/io/netty/channel/ChannelHandler.java
+++ b/transport/src/main/java/io/netty/channel/ChannelHandler.java
@@ -15,7 +15,6 @@
  */
 package io.netty.channel;
 
-import io.netty.bootstrap.ClientBootstrap;
 import io.netty.channel.group.ChannelGroup;
 
 import java.lang.annotation.Documented;
diff --git a/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java b/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java
index d0871733db..c617844f7e 100644
--- a/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java
+++ b/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java
@@ -15,7 +15,7 @@
  */
 package io.netty.channel.local;
 
-import io.netty.bootstrap.ClientBootstrap;
+import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
@@ -39,7 +39,7 @@ public class LocalChannelRegistryTest {
 
         for (int i = 0; i < 2; i ++) {
             LocalAddress addr = new LocalAddress(LOCAL_ADDR_ID);
-            ClientBootstrap cb = new ClientBootstrap();
+            Bootstrap cb = new Bootstrap();
             ServerBootstrap sb = new ServerBootstrap();
 
             cb.group(new LocalEventLoopGroup())

From 058dfd0a78214c794cfaad39f25a5c43f7587b78 Mon Sep 17 00:00:00 2001
From: norman 
Date: Thu, 13 Sep 2012 10:25:59 +0200
Subject: [PATCH 18/24] Just add a comment to show that the code is related to
 #597

---
 .../main/java/io/netty/channel/socket/nio/NioEventLoop.java    | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/socket/nio/NioEventLoop.java
index e8c643b224..113db6d8b9 100644
--- a/transport/src/main/java/io/netty/channel/socket/nio/NioEventLoop.java
+++ b/transport/src/main/java/io/netty/channel/socket/nio/NioEventLoop.java
@@ -253,6 +253,9 @@ final class NioEventLoop extends SingleThreadEventLoop {
 
     private void processSelectedKeys() {
         Set selectedKeys = selector.selectedKeys();
+        // check if the set is empty and if so just return to not create garbage by
+        // creating a new Iterator every time even if there is nothing to process.
+        // See https://github.com/netty/netty/issues/597
         if (selectedKeys.isEmpty()) {
             return;
         }

From 42685759dec119e6c992774b824dd653bdaa80ab Mon Sep 17 00:00:00 2001
From: norman 
Date: Thu, 13 Sep 2012 10:40:44 +0200
Subject: [PATCH 19/24] [maven-release-plugin] prepare release
 netty-4.0.0.Alpha4

---
 all/pom.xml        | 2 +-
 buffer/pom.xml     | 2 +-
 codec-http/pom.xml | 2 +-
 codec/pom.xml      | 2 +-
 common/pom.xml     | 2 +-
 example/pom.xml    | 2 +-
 handler/pom.xml    | 2 +-
 pom.xml            | 4 ++--
 tarball/pom.xml    | 2 +-
 testsuite/pom.xml  | 2 +-
 transport/pom.xml  | 2 +-
 11 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/all/pom.xml b/all/pom.xml
index ad6b5d7a77..dd09f9ebab 100644
--- a/all/pom.xml
+++ b/all/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4-SNAPSHOT
+    4.0.0.Alpha4
   
 
   netty
diff --git a/buffer/pom.xml b/buffer/pom.xml
index 0fec61d47b..700dd58974 100644
--- a/buffer/pom.xml
+++ b/buffer/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4-SNAPSHOT
+    4.0.0.Alpha4
   
 
   netty-buffer
diff --git a/codec-http/pom.xml b/codec-http/pom.xml
index fee59284b7..c746c601d0 100644
--- a/codec-http/pom.xml
+++ b/codec-http/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4-SNAPSHOT
+    4.0.0.Alpha4
   
 
   netty-codec-http
diff --git a/codec/pom.xml b/codec/pom.xml
index 872b70d6c2..8890873228 100644
--- a/codec/pom.xml
+++ b/codec/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4-SNAPSHOT
+    4.0.0.Alpha4
   
 
   netty-codec
diff --git a/common/pom.xml b/common/pom.xml
index 1bb68454f4..2883b3f140 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4-SNAPSHOT
+    4.0.0.Alpha4
   
 
   netty-common
diff --git a/example/pom.xml b/example/pom.xml
index 02c9ba450d..b2d31d0815 100644
--- a/example/pom.xml
+++ b/example/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4-SNAPSHOT
+    4.0.0.Alpha4
   
 
   netty-example
diff --git a/handler/pom.xml b/handler/pom.xml
index ab6c19f39e..fae6bc2e53 100644
--- a/handler/pom.xml
+++ b/handler/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4-SNAPSHOT
+    4.0.0.Alpha4
   
 
   netty-handler
diff --git a/pom.xml b/pom.xml
index c2214cc9a5..a68ecf2f69 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
   io.netty
   netty-parent
   pom
-  4.0.0.Alpha4-SNAPSHOT
+  4.0.0.Alpha4
 
   Netty
   http://netty.io/
@@ -53,7 +53,7 @@
     https://github.com/netty/netty
     scm:git:git://github.com/netty/netty.git
     scm:git:ssh://git@github.com/netty/netty.git
-    HEAD
+    netty-4.0.0.Alpha4
   
 
   
diff --git a/tarball/pom.xml b/tarball/pom.xml
index e33a9a6405..67b6a056e0 100644
--- a/tarball/pom.xml
+++ b/tarball/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4-SNAPSHOT
+    4.0.0.Alpha4
   
 
   netty-tarball
diff --git a/testsuite/pom.xml b/testsuite/pom.xml
index e68cc22de5..44632fcb7f 100644
--- a/testsuite/pom.xml
+++ b/testsuite/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4-SNAPSHOT
+    4.0.0.Alpha4
   
 
   netty-testsuite
diff --git a/transport/pom.xml b/transport/pom.xml
index 296aec1a68..a0c72eab4f 100644
--- a/transport/pom.xml
+++ b/transport/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4-SNAPSHOT
+    4.0.0.Alpha4
   
 
   netty-transport

From 3295145e88de2d7bd6ca45c0966641e5d75123b1 Mon Sep 17 00:00:00 2001
From: norman 
Date: Thu, 13 Sep 2012 10:40:52 +0200
Subject: [PATCH 20/24] [maven-release-plugin] prepare for next development
 iteration

---
 all/pom.xml        | 2 +-
 buffer/pom.xml     | 2 +-
 codec-http/pom.xml | 2 +-
 codec/pom.xml      | 2 +-
 common/pom.xml     | 2 +-
 example/pom.xml    | 2 +-
 handler/pom.xml    | 2 +-
 pom.xml            | 4 ++--
 tarball/pom.xml    | 2 +-
 testsuite/pom.xml  | 2 +-
 transport/pom.xml  | 2 +-
 11 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/all/pom.xml b/all/pom.xml
index dd09f9ebab..a85be0c8d4 100644
--- a/all/pom.xml
+++ b/all/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4
+    4.0.0.Alpha5-SNAPSHOT
   
 
   netty
diff --git a/buffer/pom.xml b/buffer/pom.xml
index 700dd58974..17574e422b 100644
--- a/buffer/pom.xml
+++ b/buffer/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4
+    4.0.0.Alpha5-SNAPSHOT
   
 
   netty-buffer
diff --git a/codec-http/pom.xml b/codec-http/pom.xml
index c746c601d0..7c369d4d0d 100644
--- a/codec-http/pom.xml
+++ b/codec-http/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4
+    4.0.0.Alpha5-SNAPSHOT
   
 
   netty-codec-http
diff --git a/codec/pom.xml b/codec/pom.xml
index 8890873228..e795254a0a 100644
--- a/codec/pom.xml
+++ b/codec/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4
+    4.0.0.Alpha5-SNAPSHOT
   
 
   netty-codec
diff --git a/common/pom.xml b/common/pom.xml
index 2883b3f140..6e641f43ef 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4
+    4.0.0.Alpha5-SNAPSHOT
   
 
   netty-common
diff --git a/example/pom.xml b/example/pom.xml
index b2d31d0815..aeae757e55 100644
--- a/example/pom.xml
+++ b/example/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4
+    4.0.0.Alpha5-SNAPSHOT
   
 
   netty-example
diff --git a/handler/pom.xml b/handler/pom.xml
index fae6bc2e53..b1f902e878 100644
--- a/handler/pom.xml
+++ b/handler/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4
+    4.0.0.Alpha5-SNAPSHOT
   
 
   netty-handler
diff --git a/pom.xml b/pom.xml
index a68ecf2f69..20cbf96dc6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
   io.netty
   netty-parent
   pom
-  4.0.0.Alpha4
+  4.0.0.Alpha5-SNAPSHOT
 
   Netty
   http://netty.io/
@@ -53,7 +53,7 @@
     https://github.com/netty/netty
     scm:git:git://github.com/netty/netty.git
     scm:git:ssh://git@github.com/netty/netty.git
-    netty-4.0.0.Alpha4
+    HEAD
   
 
   
diff --git a/tarball/pom.xml b/tarball/pom.xml
index 67b6a056e0..880371f9aa 100644
--- a/tarball/pom.xml
+++ b/tarball/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4
+    4.0.0.Alpha5-SNAPSHOT
   
 
   netty-tarball
diff --git a/testsuite/pom.xml b/testsuite/pom.xml
index 44632fcb7f..bd7b5e5c23 100644
--- a/testsuite/pom.xml
+++ b/testsuite/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4
+    4.0.0.Alpha5-SNAPSHOT
   
 
   netty-testsuite
diff --git a/transport/pom.xml b/transport/pom.xml
index a0c72eab4f..e04bb88f74 100644
--- a/transport/pom.xml
+++ b/transport/pom.xml
@@ -20,7 +20,7 @@
   
     io.netty
     netty-parent
-    4.0.0.Alpha4
+    4.0.0.Alpha5-SNAPSHOT
   
 
   netty-transport

From cc1e4d3bc7f328545291f14707aad36cdebd62a6 Mon Sep 17 00:00:00 2001
From: Norman Maurer 
Date: Fri, 14 Sep 2012 07:22:02 +0200
Subject: [PATCH 21/24] Fix BindException on OSX. See #600

---
 .../src/test/java/io/netty/testsuite/util/TestUtils.java     | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/testsuite/src/test/java/io/netty/testsuite/util/TestUtils.java b/testsuite/src/test/java/io/netty/testsuite/util/TestUtils.java
index 6aa963d130..33c8242c01 100644
--- a/testsuite/src/test/java/io/netty/testsuite/util/TestUtils.java
+++ b/testsuite/src/test/java/io/netty/testsuite/util/TestUtils.java
@@ -20,8 +20,8 @@ import java.net.ServerSocket;
 
 public class TestUtils {
 
-    private final static int START_PORT = 20000;
-    private final static int END_PORT = 30000;
+    private static int START_PORT = 20000;
+    private static int END_PORT = 30000;
 
     /**
      * Return a free port which can be used to bind to
@@ -34,6 +34,7 @@ public class TestUtils {
                 ServerSocket socket = new ServerSocket(start);
                 socket.setReuseAddress(true);
                 socket.close();
+                START_PORT = start + 1;
                 return start;
             } catch (IOException e) {
                 // ignore

From 516351b82cd3d4f5c1313ffc99cc13194973c02b Mon Sep 17 00:00:00 2001
From: Norman Maurer 
Date: Sat, 15 Sep 2012 19:14:59 +0200
Subject: [PATCH 22/24] Add a basic unit test that I wrote while porting
 niosmtp to netty 4

---
 .../codec/DelimiterBasedFrameDecoderTest.java | 69 +++++++++++++++++++
 1 file changed, 69 insertions(+)
 create mode 100644 codec/src/test/java/io/netty/handler/codec/DelimiterBasedFrameDecoderTest.java

diff --git a/codec/src/test/java/io/netty/handler/codec/DelimiterBasedFrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/DelimiterBasedFrameDecoderTest.java
new file mode 100644
index 0000000000..61f216614c
--- /dev/null
+++ b/codec/src/test/java/io/netty/handler/codec/DelimiterBasedFrameDecoderTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec;
+
+import static org.junit.Assert.*;
+
+import java.nio.charset.Charset;
+
+import org.junit.Test;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.embedded.EmbeddedByteChannel;
+
+public class DelimiterBasedFrameDecoderTest {
+
+    @Test
+    public void testMultipleLinesStrippedDelimiters() {
+        EmbeddedByteChannel ch = new EmbeddedByteChannel(new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));
+        ch.writeInbound(Unpooled.copiedBuffer("TestLine\r\ng\r\n", Charset.defaultCharset()));
+        assertEquals("TestLine", ((ByteBuf)ch.readInbound()).toString(Charset.defaultCharset()));
+        assertEquals("g", ((ByteBuf)ch.readInbound()).toString(Charset.defaultCharset()));
+        assertNull(ch.readInbound());
+    }
+
+    @Test
+    public void testIncompleteLinesStrippedDelimiters() {
+        EmbeddedByteChannel ch = new EmbeddedByteChannel(new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));
+        ch.writeInbound(Unpooled.copiedBuffer("Test", Charset.defaultCharset()));
+        assertNull(ch.readInbound());
+        ch.writeInbound(Unpooled.copiedBuffer("Line\r\ng\r\n", Charset.defaultCharset()));
+        assertEquals("TestLine", ((ByteBuf)ch.readInbound()).toString(Charset.defaultCharset()));
+        assertEquals("g", ((ByteBuf)ch.readInbound()).toString(Charset.defaultCharset()));
+        assertNull(ch.readInbound());
+    }
+
+    @Test
+    public void testMultipleLines() {
+        EmbeddedByteChannel ch = new EmbeddedByteChannel(new DelimiterBasedFrameDecoder(8192, false, Delimiters.lineDelimiter()));
+        ch.writeInbound(Unpooled.copiedBuffer("TestLine\r\ng\r\n", Charset.defaultCharset()));
+        assertEquals("TestLine\r\n", ((ByteBuf)ch.readInbound()).toString(Charset.defaultCharset()));
+        assertEquals("g\r\n", ((ByteBuf)ch.readInbound()).toString(Charset.defaultCharset()));
+        assertNull(ch.readInbound());
+    }
+
+    @Test
+    public void testIncompleteLines() {
+        EmbeddedByteChannel ch = new EmbeddedByteChannel(new DelimiterBasedFrameDecoder(8192, false, Delimiters.lineDelimiter()));
+        ch.writeInbound(Unpooled.copiedBuffer("Test", Charset.defaultCharset()));
+        assertNull(ch.readInbound());
+        ch.writeInbound(Unpooled.copiedBuffer("Line\r\ng\r\n", Charset.defaultCharset()));
+        assertEquals("TestLine\r\n", ((ByteBuf)ch.readInbound()).toString(Charset.defaultCharset()));
+        assertEquals("g\r\n", ((ByteBuf)ch.readInbound()).toString(Charset.defaultCharset()));
+        assertNull(ch.readInbound());
+    }
+}

From 0c157a94cbeece8b00de00994701724f83523f04 Mon Sep 17 00:00:00 2001
From: Norman Maurer 
Date: Sat, 15 Sep 2012 20:12:39 +0200
Subject: [PATCH 23/24] Only call ByteToMessageDecoder.decode(..) if the
 inbound buffer is readable. See #607

---
 .../main/java/io/netty/handler/codec/ByteToMessageDecoder.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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 ef802be984..1fe097bf7b 100644
--- a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java
@@ -69,7 +69,7 @@ public abstract class ByteToMessageDecoder
         ByteBuf in = ctx.inboundByteBuffer();
 
         boolean decoded = false;
-        for (;;) {
+        while (in.readable()) {
             try {
                 int oldInputLength = in.readableBytes();
                 O o = decode(ctx, in);

From fd8db4eba2ea4b7fac17a189328ea7ae66c76d2a Mon Sep 17 00:00:00 2001
From: Norman Maurer 
Date: Tue, 18 Sep 2012 21:25:29 +0200
Subject: [PATCH 24/24] Check if the remoteAddress is null before set it on
 DatagramPacket to prevent IllegalArgumentException. See #615

---
 .../java/io/netty/channel/socket/oio/OioDatagramChannel.java | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

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 6c45ad8fcb..dca7040bc6 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
@@ -205,7 +205,10 @@ public class OioDatagramChannel extends AbstractOioMessageChannel
         DatagramPacket p = (DatagramPacket) buf.poll();
         ByteBuf data = p.data();
         int length = data.readableBytes();
-        tmpPacket.setSocketAddress(p.remoteAddress());
+        InetSocketAddress remote = p.remoteAddress();
+        if (remote != null) {
+            tmpPacket.setSocketAddress(remote);
+        }
         if (data.hasArray()) {
             tmpPacket.setData(data.array(), data.arrayOffset() + data.readerIndex(), length);
         } else {