JAVA写HTTP代理服务器(二)-netty实现
接上一篇,因为是用BIO的一个连接一个线程去处理,对于HTTP这种短连接协议来说CPU开销是非常大的,就算加入了线程池也不能完美解决BIO的缺陷,所以可以用NIO进行服务器的优化,NIO基于IO多路复用以实现单线程处理大量连接,但是编写起来比较复杂,所以就选择了netty,这里就不在多叙netty是什么了。
思路
netty有内置的http编解码器,那就可以轻易做到不只是转发原始数据,而是可以修改响应内容,当然仅限http代理,因为https代理的话私钥都存在客户端和目标服务器上,代理服务器只能捕获到双方的公钥,无法解密成明文,除非代理服务器制作证书,并实现SSL/TLS握手。
实现
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 只转发数据,不做处理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);}
}
后记
netty框架下开发简单的高性能http代理服务器,并且用内置的http编解码器实现了响应体的修改。后续会再深入HTTPS明文捕获,由代理服务器制作CA证书,并实现与目标服务器SSL/TLS握手。
代码托管在github上,欢迎start
JAVA写HTTP代理服务器(二)-netty实现相关推荐
- JAVA写HTTP代理服务器(三)-https明文捕获
上一篇用netty实现的http代理服务器还无法对https报文进行解密,原因也说了,就是服务器的私钥不在我们这,根据RSA公钥加密私钥解密的特性,如果我们没有私钥的话是不可能获取到https的真实内 ...
- JAVA写HTTP代理服务器(一)-socket实现
HTTP代理服务器是一种特殊的网络服务,允许一个网络终端(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接.一些网关.路由器等网络设备具备网络代理功能.一般认为代理服务有利于 ...
- java写算法之二叉搜索树查找
700 class Solution {public TreeNode searchBST(TreeNode root, int val) {if(root==null){return null;}i ...
- java实验报告系统分析怎么写_20155218 《Java程序设计》实验二(Java面向对象程序设计)实验报告...
20155218 <Java程序设计>实验二(Java面向对象程序设计)实验报告 一.实验内容及步骤 (一)单元测试 主要学习安装和使用junit来测试编写的程序,并学习以TDD(Test ...
- Java游戏服务器系列之Netty详解
今天带大家来学习Java游戏服务器的相关知识,文中对Netty作了非常详细的介绍,对正在学习java的小伙伴们有很好的帮助,需要的朋友可以参考下 一.简介 Java的底层API逐渐复杂,而开发者面对的 ...
- java transferto_小六六学Netty系列之Java 零拷贝
前言 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/bin392328206/six-finger种一棵树最好的时间是十年前,其次是现在 我知道很多人不玩qq了 ...
- Java基础学习笔记(二)_Java核心技术(进阶)
本篇文章的学习资源来自Java学习视频教程:Java核心技术(进阶)_华东师范大学_中国大学MOOC(慕课) 本篇文章的学习笔记即是对Java核心技术课程的总结,也是对自己学习的总结 文章目录 Jav ...
- java qq聊天界面_【附源码】用Java写了一个类QQ界面聊天小项目,可在线聊天!...
原标题:[附源码]用Java写了一个类QQ界面聊天小项目,可在线聊天! 目录: 1.功能实现 2.模块划分 3.使用到知识 4.部分代码实现 5.运行例图 1.功能实现 1.修改功能(密码.昵称.个性 ...
- 学了编译原理能否用 Java 写一个编译器或解释器?
16 个回答 默认排序 RednaxelaFX JavaScript.编译原理.编程 等 7 个话题的优秀回答者 282 人赞同了该回答 能.我一开始学编译原理的时候就是用Java写了好多小编译器和 ...
最新文章
- luoguP2479 [SDOI2010]捉迷藏
- rabbitmq一:基本概念
- Jquery通过ajax请求NodeJS返回json数据
- 计算机语言学习app,学习之编程语言
- 【数据库系统概论】考研第二部分重点分析【2.2】
- postman cookie设置_是时候抛弃Postman了,因为REST Client更香
- python协程与多线程比较_python-协程、多线程、多进程性能比较
- OpenShift 4 之Knative(2) - 第一个Serverless应用
- docker nginx部署web应用_实战docker,编写Dockerfile定制tomcat镜像,实现web应用在线部署...
- Java程序员是如何面试上阿里巴巴,如何拿到年薪50W
- 【job】面试中常见的笔试梳理
- php根据类名字符串,PHP 5.5 新特性 ::class 获取类名字符串
- Arcgis Javascript那些事儿(三)---arcgis sever服务器注册关于数据拷贝问题
- CTF__(1)Web之SQL手工注入
- 【论文笔记】HyperFace: ADeep Multi-task Learning Framework for Face Detection
- insert php code test
- java开发手册 - 码出规范 - 要点整理
- python学习使用easyocr识别图片文字
- 【自动化】手把手教你一个1秒钟归纳整理海量文件的python小技巧
- driftingblues4靶机(zbarimg二维码破解)