Created
January 18, 2016 19:36
-
-
Save fzakaria/61c8efef664aa631b76f to your computer and use it in GitHub Desktop.
Netty HTTP Echo Server & Client
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import io.netty.bootstrap.Bootstrap; | |
import io.netty.bootstrap.ServerBootstrap; | |
import io.netty.buffer.ByteBuf; | |
import io.netty.buffer.Unpooled; | |
import io.netty.channel.Channel; | |
import io.netty.channel.ChannelFuture; | |
import io.netty.channel.ChannelFutureListener; | |
import io.netty.channel.ChannelHandlerContext; | |
import io.netty.channel.ChannelInitializer; | |
import io.netty.channel.EventLoopGroup; | |
import io.netty.channel.SimpleChannelInboundHandler; | |
import io.netty.channel.nio.NioEventLoopGroup; | |
import io.netty.channel.socket.SocketChannel; | |
import io.netty.channel.socket.nio.NioServerSocketChannel; | |
import io.netty.channel.socket.nio.NioSocketChannel; | |
import io.netty.handler.codec.http.DefaultFullHttpRequest; | |
import io.netty.handler.codec.http.DefaultFullHttpResponse; | |
import io.netty.handler.codec.http.FullHttpRequest; | |
import io.netty.handler.codec.http.FullHttpResponse; | |
import io.netty.handler.codec.http.HttpClientCodec; | |
import io.netty.handler.codec.http.HttpMethod; | |
import io.netty.handler.codec.http.HttpObjectAggregator; | |
import io.netty.handler.codec.http.HttpRequest; | |
import io.netty.handler.codec.http.HttpServerCodec; | |
import io.netty.handler.codec.http.HttpVersion; | |
import io.netty.util.CharsetUtil; | |
import io.netty.util.ResourceLeakDetector; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import java.net.InetSocketAddress; | |
import static io.netty.handler.codec.http.HttpResponseStatus.OK; | |
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; | |
/** | |
* A simple HTTP ECHO application | |
*/ | |
public class EchoApplication { | |
private static final Logger log = LoggerFactory.getLogger(EchoApplication.class); | |
public ChannelFuture server(EventLoopGroup workerGroup) { | |
ServerBootstrap b = new ServerBootstrap(); | |
b.group(workerGroup).channel(NioServerSocketChannel.class) | |
//Setting InetSocketAddress to port 0 will assign one at random | |
.localAddress(new InetSocketAddress(0)) | |
.childHandler(new ChannelInitializer<SocketChannel>() { | |
@Override | |
protected void initChannel(SocketChannel ch) throws Exception { | |
//HttpServerCodec is a helper ChildHandler that encompasses | |
//both HTTP request decoding and HTTP response encoding | |
ch.pipeline().addLast(new HttpServerCodec()); | |
//HttpObjectAggregator helps collect chunked HttpRequest pieces into | |
//a single FullHttpRequest. If you don't make use of streaming, this is | |
//much simpler to work with. | |
ch.pipeline().addLast(new HttpObjectAggregator(1048576)); | |
//Finally add your FullHttpRequest handler. Real examples might replace this | |
//with a request router | |
ch.pipeline().addLast(new SimpleChannelInboundHandler<FullHttpRequest>() { | |
@Override | |
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { | |
ctx.flush(); | |
//The close is important here in an HTTP request as it sets the Content-Length of a | |
//response body back to the client. | |
ctx.close(); | |
} | |
@Override | |
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception { | |
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, msg.content().copy()); | |
ctx.write(response); | |
} | |
}); | |
} | |
}); | |
// Start the server & bind to a random port. | |
return b.bind(); | |
} | |
public Bootstrap client() { | |
EventLoopGroup workerGroup = new NioEventLoopGroup(); | |
Bootstrap b = new Bootstrap(); | |
b.group(workerGroup).channel(NioSocketChannel.class) | |
.handler(new ChannelInitializer<SocketChannel>() { | |
@Override | |
protected void initChannel(SocketChannel ch) throws Exception { | |
//HttpClient codec is a helper ChildHandler that encompasses | |
//both HTTP response decoding and HTTP request encoding | |
ch.pipeline().addLast(new HttpClientCodec()); | |
//HttpObjectAggregator helps collect chunked HttpRequest pieces into | |
//a single FullHttpRequest. If you don't make use of streaming, this is | |
//much simpler to work with. | |
ch.pipeline().addLast(new HttpObjectAggregator(1048576)); | |
ch.pipeline().addLast(new SimpleChannelInboundHandler<FullHttpResponse>() { | |
@Override | |
protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception { | |
final String echo = msg.content().toString(CharsetUtil.UTF_8); | |
log.info("Response: {}", echo); | |
} | |
}); | |
} | |
}); | |
return b; | |
} | |
public static void main(String[] args) throws Exception { | |
//I find while learning Netty to keep resource leak detecting at Paranoid, | |
//however *DO NOT* ship with this level. | |
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID); | |
EventLoopGroup serverWorkgroup = new NioEventLoopGroup(); | |
EventLoopGroup clientWorkgroup = new NioEventLoopGroup(); | |
EchoApplication app = new EchoApplication(); | |
try { | |
Channel serverChannel = app.server(serverWorkgroup).sync().channel(); | |
int PORT = ((InetSocketAddress) serverChannel.localAddress()).getPort(); | |
System.err.println("Open your web browser and navigate to " + | |
"://127.0.0.1:" + PORT + '/'); | |
System.err.println("Echo back any TEXT with POST HTTP requests"); | |
Bootstrap clientBootstrap = app.client(); | |
final ByteBuf content = Unpooled.copiedBuffer("Hello World!", CharsetUtil.UTF_8); | |
clientBootstrap | |
.connect(serverChannel.localAddress()) | |
.addListener(new ChannelFutureListener() { | |
@Override | |
public void operationComplete(ChannelFuture future) throws Exception { | |
// Prepare the HTTP request. | |
HttpRequest request = new DefaultFullHttpRequest( | |
HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content); | |
// If we don't set a content length from the client, HTTP RFC | |
// dictates that the body must be be empty then and Netty won't read it. | |
request.headers().set("Content-Length", content.readableBytes()); | |
future.channel().writeAndFlush(request); | |
} | |
}); | |
serverChannel.closeFuture().sync(); | |
}finally { | |
//Gracefully shutdown both event loop groups | |
serverWorkgroup.shutdownGracefully(); | |
clientWorkgroup.shutdownGracefully(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment