Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
db4a3a4789
@ -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
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-parent</artifactId>
|
||||
<version>4.0.0.Alpha4-SNAPSHOT</version>
|
||||
<version>4.0.0.Alpha5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>netty</artifactId>
|
||||
|
@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-parent</artifactId>
|
||||
<version>4.0.0.Alpha4-SNAPSHOT</version>
|
||||
<version>4.0.0.Alpha5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>netty-buffer</artifactId>
|
||||
|
@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-parent</artifactId>
|
||||
<version>4.0.0.Alpha4-SNAPSHOT</version>
|
||||
<version>4.0.0.Alpha5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
@ -34,6 +34,11 @@
|
||||
<artifactId>netty-codec</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
@ -171,16 +172,21 @@ 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);
|
||||
|
||||
p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket00FrameEncoder());
|
||||
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());
|
||||
}
|
||||
});
|
||||
|
||||
return future;
|
||||
}
|
||||
|
@ -147,14 +147,19 @@ 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() {
|
||||
@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 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;
|
||||
}
|
||||
|
@ -147,14 +147,19 @@ 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() {
|
||||
@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 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;
|
||||
}
|
||||
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.ChannelFutureListener;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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<WebSocketFrame> {
|
||||
|
||||
private static final AttributeKey<WebSocketServerHandshaker> HANDSHAKER_ATTR_KEY =
|
||||
new AttributeKey<WebSocketServerHandshaker>(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) {
|
||||
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
|
||||
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 {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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<Object>() {
|
||||
@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();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.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;
|
||||
|
||||
/**
|
||||
* Handles the HTTP handshake (the HTTP Upgrade request)
|
||||
*/
|
||||
public class WebSocketServerProtocolHandshakeHandler extends ChannelInboundMessageHandlerAdapter<HttpRequest> {
|
||||
|
||||
private static final InternalLogger logger =
|
||||
InternalLoggerFactory.getInstance(WebSocketServerProtocolHandshakeHandler.class);
|
||||
private final String websocketPath;
|
||||
private final String subprotocols;
|
||||
private final boolean allowExtensions;
|
||||
|
||||
public WebSocketServerProtocolHandshakeHandler(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(ctx.pipeline(), req, websocketPath), subprotocols, allowExtensions);
|
||||
final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req);
|
||||
if (handshaker == null) {
|
||||
wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
|
||||
} else {
|
||||
final ChannelFuture handshakeFuture = handshaker.handshake(ctx.channel(), req);
|
||||
handshakeFuture.addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (!future.isSuccess()) {
|
||||
ctx.fireExceptionCaught(future.cause());
|
||||
}
|
||||
}
|
||||
});
|
||||
WebSocketServerProtocolHandler.setHandshaker(ctx, handshaker);
|
||||
ctx.pipeline().replace(this, "WS403Responder",
|
||||
WebSocketServerProtocolHandler.forbiddenHttpRequestResponder());
|
||||
}
|
||||
}
|
||||
|
||||
@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(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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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(WebSocketServerProtocolHandshakeHandler.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<Object> outbound = ch.pipeline().context(MockOutboundHandler.class).outboundMessageBuffer();
|
||||
return (HttpResponse) outbound.poll();
|
||||
}
|
||||
|
||||
private static class MockOutboundHandler extends ChannelOutboundMessageHandlerAdapter<Object> {
|
||||
@Override
|
||||
public void flush(ChannelHandlerContext ctx, ChannelFuture future) throws Exception {
|
||||
//NoOp
|
||||
}
|
||||
}
|
||||
|
||||
private static class CustomTextFrameHandler extends ChannelInboundMessageHandlerAdapter<TextWebSocketFrame> {
|
||||
private String content;
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
|
||||
content = "processed: " + msg.getText();
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-parent</artifactId>
|
||||
<version>4.0.0.Alpha4-SNAPSHOT</version>
|
||||
<version>4.0.0.Alpha5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>netty-codec</artifactId>
|
||||
|
@ -69,7 +69,7 @@ public abstract class ByteToMessageDecoder<O>
|
||||
ByteBuf in = ctx.inboundByteBuffer();
|
||||
|
||||
boolean decoded = false;
|
||||
for (;;) {
|
||||
while (in.readable()) {
|
||||
try {
|
||||
int oldInputLength = in.readableBytes();
|
||||
O o = decode(ctx, in);
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-parent</artifactId>
|
||||
<version>4.0.0.Alpha4-SNAPSHOT</version>
|
||||
<version>4.0.0.Alpha5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>netty-common</artifactId>
|
||||
|
@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-parent</artifactId>
|
||||
<version>4.0.0.Alpha4-SNAPSHOT</version>
|
||||
<version>4.0.0.Alpha5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>netty-example</artifactId>
|
||||
|
@ -39,7 +39,7 @@ public class DiscardClient {
|
||||
Bootstrap b = new Bootstrap();
|
||||
try {
|
||||
b.group(new NioEventLoopGroup())
|
||||
.channel(new NioSocketChannel())
|
||||
.channel(NioSocketChannel.class)
|
||||
.remoteAddress(host, port)
|
||||
.handler(new DiscardClientHandler(firstMessageSize));
|
||||
|
||||
|
@ -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<SocketChannel>() {
|
||||
@Override
|
||||
|
@ -50,7 +50,7 @@ public class EchoClient {
|
||||
Bootstrap b = new Bootstrap();
|
||||
try {
|
||||
b.group(new NioEventLoopGroup())
|
||||
.channel(new NioSocketChannel())
|
||||
.channel(NioSocketChannel.class)
|
||||
.option(ChannelOption.TCP_NODELAY, true)
|
||||
.remoteAddress(new InetSocketAddress(host, port))
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
|
@ -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)
|
||||
|
@ -40,7 +40,7 @@ public class FactorialClient {
|
||||
Bootstrap b = new Bootstrap();
|
||||
try {
|
||||
b.group(new NioEventLoopGroup())
|
||||
.channel(new NioSocketChannel())
|
||||
.channel(NioSocketChannel.class)
|
||||
.remoteAddress(host, port)
|
||||
.handler(new FactorialClientInitializer(count));
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
@ -65,7 +65,7 @@ public class HttpSnoopClient {
|
||||
Bootstrap b = new Bootstrap();
|
||||
try {
|
||||
b.group(new NioEventLoopGroup())
|
||||
.channel(new NioSocketChannel())
|
||||
.channel(NioSocketChannel.class)
|
||||
.handler(new HttpSnoopClientInitializer(ssl))
|
||||
.remoteAddress(new InetSocketAddress(host, port));
|
||||
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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<SocketChannel>() {
|
||||
@Override
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
@ -50,7 +50,7 @@ public class LocalEcho {
|
||||
// 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<LocalServerChannel>() {
|
||||
@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<LocalChannel>() {
|
||||
@Override
|
||||
|
@ -46,7 +46,7 @@ public class LocalTimeClient {
|
||||
Bootstrap b = new Bootstrap();
|
||||
try {
|
||||
b.group(new NioEventLoopGroup())
|
||||
.channel(new NioSocketChannel())
|
||||
.channel(NioSocketChannel.class)
|
||||
.remoteAddress(host, port)
|
||||
.handler(new LocalTimeClientInitializer());
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
@ -44,7 +44,7 @@ public class ObjectEchoClient {
|
||||
Bootstrap b = new Bootstrap();
|
||||
try {
|
||||
b.group(new NioEventLoopGroup())
|
||||
.channel(new NioSocketChannel())
|
||||
.channel(NioSocketChannel.class)
|
||||
.remoteAddress(host, port)
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
|
@ -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<SocketChannel>() {
|
||||
@Override
|
||||
|
@ -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<SocketChannel>() {
|
||||
@Override
|
||||
|
@ -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));
|
||||
|
||||
|
@ -45,7 +45,7 @@ public class HexDumpProxyFrontendHandler extends ChannelInboundByteHandlerAdapte
|
||||
// Start the connection attempt.
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(inboundChannel.eventLoop())
|
||||
.channel(new NioSocketChannel())
|
||||
.channel(NioSocketChannel.class)
|
||||
.remoteAddress(remoteHost, remotePort)
|
||||
.handler(new HexDumpProxyBackendHandler(inboundChannel));
|
||||
|
||||
|
@ -44,7 +44,7 @@ public class QuoteOfTheMomentClient {
|
||||
Bootstrap b = new Bootstrap();
|
||||
try {
|
||||
b.group(new NioEventLoopGroup())
|
||||
.channel(new NioDatagramChannel())
|
||||
.channel(NioDatagramChannel.class)
|
||||
.localAddress(new InetSocketAddress(0))
|
||||
.option(ChannelOption.SO_BROADCAST, true)
|
||||
.handler(new QuoteOfTheMomentClientHandler());
|
||||
|
@ -40,7 +40,7 @@ public class QuoteOfTheMomentServer {
|
||||
Bootstrap b = new Bootstrap();
|
||||
try {
|
||||
b.group(new NioEventLoopGroup())
|
||||
.channel(new NioDatagramChannel())
|
||||
.channel(NioDatagramChannel.class)
|
||||
.localAddress(new InetSocketAddress(port))
|
||||
.option(ChannelOption.SO_BROADCAST, true)
|
||||
.handler(new QuoteOfTheMomentServerHandler());
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package io.netty.example.sctp;
|
||||
|
||||
import io.netty.bootstrap.AbstractBootstrap;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
@ -52,7 +53,7 @@ public class NioSctpEchoClient {
|
||||
Bootstrap b = new Bootstrap();
|
||||
try {
|
||||
b.group(new NioEventLoopGroup())
|
||||
.channel(new NioSctpChannel())
|
||||
.channel(NioSctpChannel.class)
|
||||
.option(ChannelOption.SCTP_NODELAY, true)
|
||||
.remoteAddress(new InetSocketAddress(host, port))
|
||||
.handler(new ChannelInitializer<SctpChannel>() {
|
||||
|
@ -43,7 +43,7 @@ public class NioSctpEchoServer {
|
||||
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)
|
||||
|
@ -42,7 +42,7 @@ public class SecureChatClient {
|
||||
Bootstrap b = new Bootstrap();
|
||||
try {
|
||||
b.group(new NioEventLoopGroup())
|
||||
.channel(new NioSocketChannel())
|
||||
.channel(NioSocketChannel.class)
|
||||
.remoteAddress(host, port)
|
||||
.handler(new SecureChatClientInitializer());
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
@ -41,7 +41,7 @@ public class TelnetClient {
|
||||
Bootstrap b = new Bootstrap();
|
||||
try {
|
||||
b.group(new NioEventLoopGroup())
|
||||
.channel(new NioSocketChannel())
|
||||
.channel(NioSocketChannel.class)
|
||||
.remoteAddress(host, port)
|
||||
.handler(new TelnetClientInitializer());
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
@ -59,7 +59,7 @@ public class UptimeClient {
|
||||
|
||||
Bootstrap configureBootstrap(Bootstrap b, EventLoopGroup g) {
|
||||
b.group(g)
|
||||
.channel(new NioSocketChannel())
|
||||
.channel(NioSocketChannel.class)
|
||||
.remoteAddress(host, port)
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
|
@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-parent</artifactId>
|
||||
<version>4.0.0.Alpha4-SNAPSHOT</version>
|
||||
<version>4.0.0.Alpha5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>netty-handler</artifactId>
|
||||
|
2
pom.xml
2
pom.xml
@ -26,7 +26,7 @@
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>4.0.0.Alpha4-SNAPSHOT</version>
|
||||
<version>4.0.0.Alpha5-SNAPSHOT</version>
|
||||
|
||||
<name>Netty</name>
|
||||
<url>http://netty.io/</url>
|
||||
|
@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-parent</artifactId>
|
||||
<version>4.0.0.Alpha4-SNAPSHOT</version>
|
||||
<version>4.0.0.Alpha5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>netty-tarball</artifactId>
|
||||
|
@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-parent</artifactId>
|
||||
<version>4.0.0.Alpha4-SNAPSHOT</version>
|
||||
<version>4.0.0.Alpha5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>netty-testsuite</artifactId>
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package io.netty.testsuite.transport.socket;
|
||||
|
||||
import io.netty.bootstrap.AbstractBootstrap;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.logging.InternalLogger;
|
||||
import io.netty.logging.InternalLoggerFactory;
|
||||
@ -59,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();
|
||||
|
@ -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<DatagramPacket>() {
|
||||
|
@ -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<DatagramPacket>() {
|
||||
|
@ -15,8 +15,10 @@
|
||||
*/
|
||||
package io.netty.testsuite.transport.socket;
|
||||
|
||||
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;
|
||||
import io.netty.channel.socket.aio.AioEventLoopGroup;
|
||||
import io.netty.channel.socket.aio.AioServerSocketChannel;
|
||||
@ -86,14 +88,18 @@ final class SocketTestPermutation {
|
||||
bfs.add(new Factory<Bootstrap>() {
|
||||
@Override
|
||||
public Bootstrap newInstance() {
|
||||
return new Bootstrap().group(new NioEventLoopGroup()).channel(
|
||||
new NioDatagramChannel(InternetProtocolFamily.IPv4));
|
||||
return new Bootstrap().group(new NioEventLoopGroup()).channelFactory(new ChannelFactory() {
|
||||
@Override
|
||||
public Channel newChannel() {
|
||||
return new NioDatagramChannel(InternetProtocolFamily.IPv4);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
bfs.add(new Factory<Bootstrap>() {
|
||||
@Override
|
||||
public Bootstrap newInstance() {
|
||||
return new Bootstrap().group(new OioEventLoopGroup()).channel(new OioDatagramChannel());
|
||||
return new Bootstrap().group(new OioEventLoopGroup()).channel(OioDatagramChannel.class);
|
||||
}
|
||||
});
|
||||
|
||||
@ -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<ServerBootstrap>() {
|
||||
@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<ServerBootstrap>() {
|
||||
@ -151,7 +161,7 @@ final class SocketTestPermutation {
|
||||
public ServerBootstrap newInstance() {
|
||||
return new ServerBootstrap().
|
||||
group(new OioEventLoopGroup(), new OioEventLoopGroup()).
|
||||
channel(new OioServerSocketChannel());
|
||||
channel(OioServerSocketChannel.class);
|
||||
}
|
||||
});
|
||||
|
||||
@ -163,20 +173,25 @@ final class SocketTestPermutation {
|
||||
list.add(new Factory<Bootstrap>() {
|
||||
@Override
|
||||
public Bootstrap newInstance() {
|
||||
return new Bootstrap().group(new NioEventLoopGroup()).channel(new NioSocketChannel());
|
||||
return new Bootstrap().group(new NioEventLoopGroup()).channel(NioSocketChannel.class);
|
||||
}
|
||||
});
|
||||
list.add(new Factory<Bootstrap>() {
|
||||
@Override
|
||||
public Bootstrap newInstance() {
|
||||
AioEventLoopGroup loop = new AioEventLoopGroup();
|
||||
return new Bootstrap().group(loop).channel(new AioSocketChannel(loop));
|
||||
final AioEventLoopGroup loop = new AioEventLoopGroup();
|
||||
return new Bootstrap().group(loop).channelFactory(new ChannelFactory() {
|
||||
@Override
|
||||
public Channel newChannel() {
|
||||
return new AioSocketChannel(loop);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
list.add(new Factory<Bootstrap>() {
|
||||
@Override
|
||||
public Bootstrap newInstance() {
|
||||
return new Bootstrap().group(new OioEventLoopGroup()).channel(new OioSocketChannel());
|
||||
return new Bootstrap().group(new OioEventLoopGroup()).channel(OioSocketChannel.class);
|
||||
}
|
||||
});
|
||||
return list;
|
||||
|
@ -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
|
||||
|
@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-parent</artifactId>
|
||||
<version>4.0.0.Alpha4-SNAPSHOT</version>
|
||||
<version>4.0.0.Alpha5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>netty-transport</artifactId>
|
||||
|
@ -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<B extends AbstractBootstrap<?>> {
|
||||
private EventLoopGroup group;
|
||||
private ChannelFactory factory;
|
||||
private SocketAddress localAddress;
|
||||
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, 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<? extends Channel> 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 <code>null</code> to remove a previous set {@link ChannelOption}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> B option(ChannelOption<T> 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<ChannelOption<?>, Object> options() {
|
||||
return options;
|
||||
}
|
||||
|
||||
private final class BootstrapChannelFactory implements ChannelFactory {
|
||||
private final Class<? extends Channel> clazz;
|
||||
|
||||
BootstrapChannelFactory(Class<? extends Channel> 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();
|
||||
}
|
||||
}
|
@ -16,13 +16,11 @@
|
||||
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.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.logging.InternalLogger;
|
||||
import io.netty.logging.InternalLoggerFactory;
|
||||
|
||||
@ -30,111 +28,53 @@ 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 {
|
||||
/**
|
||||
* A {@link Bootstrap} that makes it easy to bootstrap a {@link Channel} to use
|
||||
* for clients.
|
||||
*
|
||||
*/
|
||||
public class Bootstrap extends AbstractBootstrap<Bootstrap> {
|
||||
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Bootstrap.class);
|
||||
|
||||
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
|
||||
private EventLoopGroup group;
|
||||
private Channel channel;
|
||||
private ChannelHandler handler;
|
||||
private SocketAddress localAddress;
|
||||
private SocketAddress remoteAddress;
|
||||
|
||||
public Bootstrap group(EventLoopGroup group) {
|
||||
if (group == null) {
|
||||
throw new NullPointerException("group");
|
||||
}
|
||||
if (this.group != null) {
|
||||
throw new IllegalStateException("group set already");
|
||||
}
|
||||
this.group = group;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Bootstrap channel(Channel channel) {
|
||||
if (channel == null) {
|
||||
throw new NullPointerException("channel");
|
||||
}
|
||||
if (this.channel != null) {
|
||||
throw new IllegalStateException("channel set already");
|
||||
}
|
||||
this.channel = channel;
|
||||
return this;
|
||||
}
|
||||
|
||||
public <T> Bootstrap option(ChannelOption<T> option, T value) {
|
||||
if (option == null) {
|
||||
throw new NullPointerException("option");
|
||||
}
|
||||
if (value == null) {
|
||||
options.remove(option);
|
||||
} else {
|
||||
options.put(option, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Bootstrap handler(ChannelHandler handler) {
|
||||
if (handler == null) {
|
||||
throw new NullPointerException("handler");
|
||||
}
|
||||
this.handler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Bootstrap localAddress(SocketAddress localAddress) {
|
||||
this.localAddress = localAddress;
|
||||
return this;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link SocketAddress} to connect to once the {@link #connect()} method
|
||||
* is called.
|
||||
*/
|
||||
public Bootstrap remoteAddress(SocketAddress remoteAddress) {
|
||||
this.remoteAddress = remoteAddress;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #remoteAddress(SocketAddress)}
|
||||
*/
|
||||
public Bootstrap remoteAddress(String host, int port) {
|
||||
remoteAddress = new InetSocketAddress(host, port);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #remoteAddress(SocketAddress)}
|
||||
*/
|
||||
public Bootstrap remoteAddress(InetAddress host, int port) {
|
||||
remoteAddress = new InetSocketAddress(host, port);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChannelFuture bind() {
|
||||
validate();
|
||||
return bind(channel.newFuture());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture bind(ChannelFuture future) {
|
||||
validate(future);
|
||||
if (localAddress == null) {
|
||||
if (localAddress() == null) {
|
||||
throw new IllegalStateException("localAddress not set");
|
||||
}
|
||||
|
||||
try {
|
||||
init();
|
||||
init(future.channel());
|
||||
} catch (Throwable t) {
|
||||
future.setFailure(t);
|
||||
return future;
|
||||
@ -144,14 +84,21 @@ public class Bootstrap {
|
||||
return future;
|
||||
}
|
||||
|
||||
return channel.bind(localAddress, future).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
|
||||
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) {
|
||||
@ -159,7 +106,7 @@ public class Bootstrap {
|
||||
}
|
||||
|
||||
try {
|
||||
init();
|
||||
init(future.channel());
|
||||
} catch (Throwable t) {
|
||||
future.setFailure(t);
|
||||
return future;
|
||||
@ -169,15 +116,16 @@ public class Bootstrap {
|
||||
return future;
|
||||
}
|
||||
|
||||
if (localAddress == null) {
|
||||
channel.connect(remoteAddress, future);
|
||||
if (localAddress() == null) {
|
||||
future.channel().connect(remoteAddress, future);
|
||||
} else {
|
||||
channel.connect(remoteAddress, localAddress, future);
|
||||
future.channel().connect(remoteAddress, localAddress(), future);
|
||||
}
|
||||
return future.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
|
||||
}
|
||||
|
||||
private void init() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
private void init(Channel channel) throws Exception {
|
||||
if (channel.isActive()) {
|
||||
throw new IllegalStateException("channel already active:: " + channel);
|
||||
}
|
||||
@ -189,9 +137,9 @@ public class Bootstrap {
|
||||
}
|
||||
|
||||
ChannelPipeline p = channel.pipeline();
|
||||
p.addLast(handler);
|
||||
p.addLast(handler());
|
||||
|
||||
for (Entry<ChannelOption<?>, Object> e: options.entrySet()) {
|
||||
for (Entry<ChannelOption<?>, Object> e: options().entrySet()) {
|
||||
try {
|
||||
if (!channel.config().setOption((ChannelOption<Object>) e.getKey(), e.getValue())) {
|
||||
logger.warn("Unknown channel option: " + e);
|
||||
@ -201,45 +149,34 @@ public class Bootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
group.register(channel).syncUninterruptibly();
|
||||
group().register(channel).syncUninterruptibly();
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
if (group != null) {
|
||||
group.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
if (group == null) {
|
||||
throw new IllegalStateException("group not set");
|
||||
}
|
||||
if (channel == null) {
|
||||
throw new IllegalStateException("channel not set");
|
||||
}
|
||||
if (handler == null) {
|
||||
@Override
|
||||
protected void validate() {
|
||||
super.validate();
|
||||
if (handler() == null) {
|
||||
throw new IllegalStateException("handler not set");
|
||||
}
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
/**
|
||||
* 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, 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());
|
||||
}
|
||||
}
|
||||
|
@ -17,32 +17,35 @@ 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.channel.socket.SocketChannel;
|
||||
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 {
|
||||
/**
|
||||
* {@link Bootstrap} sub-class which allows easy bootstrap of {@link ServerChannel}
|
||||
*
|
||||
*/
|
||||
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap> {
|
||||
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(ServerBootstrap.class);
|
||||
private static final InetSocketAddress DEFAULT_LOCAL_ADDR = new InetSocketAddress(NetworkConstants.LOCALHOST, 0);
|
||||
@ -54,62 +57,55 @@ public class ServerBootstrap {
|
||||
}
|
||||
};
|
||||
|
||||
private final Map<ChannelOption<?>, Object> parentOptions = new LinkedHashMap<ChannelOption<?>, Object>();
|
||||
private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
|
||||
private EventLoopGroup parentGroup;
|
||||
private EventLoopGroup childGroup;
|
||||
private ServerChannel channel;
|
||||
private ChannelHandler handler;
|
||||
private ChannelHandler childHandler;
|
||||
private SocketAddress localAddress;
|
||||
|
||||
/**
|
||||
* Specify the {@link EventLoopGroup} which is used for the parent (acceptor) and the child (client).
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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");
|
||||
/**
|
||||
* The {@link Class} which is used to create the {@link ServerChannel} from (for the acceptor).
|
||||
*/
|
||||
@Override
|
||||
public ServerBootstrap channel(Class<? extends Channel> 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 <T> ServerBootstrap option(ChannelOption<T> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <code>null</code> to remove a previous set
|
||||
* {@link ChannelOption}.
|
||||
*/
|
||||
public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
|
||||
if (childOption == null) {
|
||||
throw new NullPointerException("childOption");
|
||||
@ -122,11 +118,9 @@ public class ServerBootstrap {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ServerBootstrap handler(ChannelHandler handler) {
|
||||
this.handler = handler;
|
||||
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");
|
||||
@ -135,36 +129,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 +147,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<Channel> {
|
||||
@ -257,6 +207,7 @@ public class ServerBootstrap {
|
||||
return Unpooled.messageBuffer();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void inboundBufferUpdated(ChannelHandlerContext ctx) {
|
||||
MessageBuf<Channel> in = ctx.inboundMessageBuffer();
|
||||
|
@ -129,7 +129,7 @@ import java.util.concurrent.TimeUnit;
|
||||
* connect timeout should be configured via a transport-specific option:
|
||||
* <pre>
|
||||
* // 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>b.setOption("connectTimeoutMillis", 10000);</b>
|
||||
* {@link ChannelFuture} f = b.connect(...);
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package io.netty.channel;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.group.ChannelGroup;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
@ -87,7 +86,7 @@ import java.nio.channels.Channels;
|
||||
* the confidential information:
|
||||
* <pre>
|
||||
* // 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(<b>new DataServerHandler()</b>);
|
||||
|
@ -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<SocketAddress> 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);
|
||||
}
|
||||
|
@ -253,6 +253,9 @@ final class NioEventLoop extends SingleThreadEventLoop {
|
||||
|
||||
private void processSelectedKeys() {
|
||||
Set<SelectionKey> 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;
|
||||
}
|
||||
|
@ -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() {
|
||||
@ -215,6 +218,7 @@ public class NioSctpChannel extends AbstractNioMessageChannel implements io.nett
|
||||
javaChannel().close();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected int doReadMessages(MessageBuf<Object> buf) throws Exception {
|
||||
SctpChannel ch = javaChannel();
|
||||
@ -276,4 +280,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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -43,12 +43,12 @@ public class LocalChannelRegistryTest {
|
||||
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<LocalChannel>() {
|
||||
@Override
|
||||
|
@ -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<LocalChannel>() {
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user