java通過netty實現(xiàn)代理服務器
來源: libraryhu
發(fā)布時間:2018-11-21 15:52:32
閱讀量:1222
思路
netty有內置的http編解碼器,那就可以輕易做到不只是轉發(fā)原始數(shù)據(jù),而是可以修改響應內容,當然僅限http代理,因為https代理的話私鑰都存在客戶端和目標服務器上,代理服務器只能捕獲到雙方的公鑰,無法解密成明文,除非代理服務器制作證書,并實現(xiàn)SSL/TLS握手。
實現(xiàn)
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>() {@Overrideprotected 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;@Overridepublic 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 只轉發(fā)數(shù)據(jù),不做處理if (cf == null) {//連接至目標服務器Bootstrap bootstrap = new Bootstrap();bootstrap.group(ctx.channel().eventLoop()) // 復用客戶端連接線程池.channel(ctx.channel().getClass()) // 使用NioSocketChannel來作為連接用的channel類.handler(new ChannelInitializer() {@Overrideprotected void initChannel(Channel ch) throws Exception {ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {@Overridepublic 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;}@Overrideprotected 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;}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {FullHttpResponse response = (FullHttpResponse) msg;//修改http響應體返回至客戶端response.headers().add("test","from proxy");clientChannel.writeAndFlush(msg);}}