接上一篇,由于是用BIO的一个链接一个线程去处理,对于HTTP这种短链接协议来讲CPU开销是很是大的,就算加入了线程池也不能完美解决BIO的缺陷,因此能够用NIO进行服务器的优化,NIO基于IO多路复用以实现单线程处理大量链接,可是编写起来比较复杂,因此就选择了netty,这里就不在多叙netty是什么了。git
netty有内置的http编解码器,那就能够轻易作到不仅是转发原始数据,而是能够修改响应内容,固然仅限http代理,由于https代理的话私钥都存在客户端和目标服务器上,代理服务器只能捕获到双方的公钥,没法解密成明文,除非代理服务器制做证书,并实现SSL/TLS握手。github
EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(2); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) .option(ChannelOption.TCP_NODELAY, true) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast("httpCodec",new HttpServerCodec()); ch.pipeline().addLast("httpObject",new HttpObjectAggregator(65536)); ch.pipeline().addLast("serverHandle",new HttpProxyServerHandle()); } }); ChannelFuture f = b .bind(port) .sync(); f.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }
public class HttpProxyServerHandle extends ChannelInboundHandlerAdapter { private ChannelFuture cf; private String host; private int port; @Override public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { if (msg instanceof FullHttpRequest) { FullHttpRequest request = (FullHttpRequest) msg; String host = request.headers().get("host"); String[] temp = host.split(":"); int port = 80; if (temp.length > 1) { port = Integer.parseInt(temp[1]); } else { if (request.uri().indexOf("https") == 0) { port = 443; } } this.host = temp[0]; this.port = port; if ("CONNECT".equalsIgnoreCase(request.method().name())) {//HTTPS创建代理握手 HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, NettyHttpProxyServer.SUCCESS); ctx.writeAndFlush(response); ctx.pipeline().remove("httpCodec"); ctx.pipeline().remove("httpObject"); return; } //链接至目标服务器 Bootstrap bootstrap = new Bootstrap(); bootstrap.group(ctx.channel().eventLoop()) // 注册线程池 .channel(ctx.channel().getClass()) // 使用NioSocketChannel来做为链接用的channel类 .handler(new HttpProxyInitializer(ctx.channel())); ChannelFuture cf = bootstrap.connect(temp[0], port); cf.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { future.channel().writeAndFlush(msg); } else { ctx.channel().close(); } } }); // ChannelFuture cf = bootstrap.connect(temp[0], port).sync(); // cf.channel().writeAndFlush(request); } else { // https 只转发数据,不作处理 if (cf == null) { //链接至目标服务器 Bootstrap bootstrap = new Bootstrap(); bootstrap.group(ctx.channel().eventLoop()) // 复用客户端链接线程池 .channel(ctx.channel().getClass()) // 使用NioSocketChannel来做为链接用的channel类 .handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx0, Object msg) throws Exception { ctx.channel().writeAndFlush(msg); } }); } }); cf = bootstrap.connect(host, port); cf.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { future.channel().writeAndFlush(msg); } else { ctx.channel().close(); } } }); } else { cf.channel().writeAndFlush(msg); } } } }
public class HttpProxyInitializer extends ChannelInitializer{ private Channel clientChannel; public HttpProxyInitializer(Channel clientChannel) { this.clientChannel = clientChannel; } @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast(new HttpClientCodec()); ch.pipeline().addLast(new HttpObjectAggregator(6553600)); ch.pipeline().addLast(new HttpProxyClientHandle(clientChannel)); } }
public class HttpProxyClientHandle extends ChannelInboundHandlerAdapter { private Channel clientChannel; public HttpProxyClientHandle(Channel clientChannel) { this.clientChannel = clientChannel; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { FullHttpResponse response = (FullHttpResponse) msg; //修改http响应体返回至客户端 response.headers().add("test","from proxy"); clientChannel.writeAndFlush(msg); } }
netty框架下开发简单的高性能http代理服务器,而且用内置的http编解码器实现了响应体的修改。后续会再深刻HTTPS明文捕获,由代理服务器制做CA证书,并实现与目标服务器SSL/TLS握手。
代码托管在github上,欢迎startbootstrap