接上一篇,因为是用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实现相关推荐

  1. JAVA写HTTP代理服务器(三)-https明文捕获

    上一篇用netty实现的http代理服务器还无法对https报文进行解密,原因也说了,就是服务器的私钥不在我们这,根据RSA公钥加密私钥解密的特性,如果我们没有私钥的话是不可能获取到https的真实内 ...

  2. JAVA写HTTP代理服务器(一)-socket实现

    HTTP代理服务器是一种特殊的网络服务,允许一个网络终端(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接.一些网关.路由器等网络设备具备网络代理功能.一般认为代理服务有利于 ...

  3. java写算法之二叉搜索树查找

    700 class Solution {public TreeNode searchBST(TreeNode root, int val) {if(root==null){return null;}i ...

  4. java实验报告系统分析怎么写_20155218 《Java程序设计》实验二(Java面向对象程序设计)实验报告...

    20155218 <Java程序设计>实验二(Java面向对象程序设计)实验报告 一.实验内容及步骤 (一)单元测试 主要学习安装和使用junit来测试编写的程序,并学习以TDD(Test ...

  5. Java游戏服务器系列之Netty详解

    今天带大家来学习Java游戏服务器的相关知识,文中对Netty作了非常详细的介绍,对正在学习java的小伙伴们有很好的帮助,需要的朋友可以参考下 一.简介 Java的底层API逐渐复杂,而开发者面对的 ...

  6. java transferto_小六六学Netty系列之Java 零拷贝

    前言 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/bin392328206/six-finger种一棵树最好的时间是十年前,其次是现在 我知道很多人不玩qq了 ...

  7. Java基础学习笔记(二)_Java核心技术(进阶)

    本篇文章的学习资源来自Java学习视频教程:Java核心技术(进阶)_华东师范大学_中国大学MOOC(慕课) 本篇文章的学习笔记即是对Java核心技术课程的总结,也是对自己学习的总结 文章目录 Jav ...

  8. java qq聊天界面_【附源码】用Java写了一个类QQ界面聊天小项目,可在线聊天!...

    原标题:[附源码]用Java写了一个类QQ界面聊天小项目,可在线聊天! 目录: 1.功能实现 2.模块划分 3.使用到知识 4.部分代码实现 5.运行例图 1.功能实现 1.修改功能(密码.昵称.个性 ...

  9. 学了编译原理能否用 Java 写一个编译器或解释器?

    16 个回答 默认排序​ RednaxelaFX JavaScript.编译原理.编程 等 7 个话题的优秀回答者 282 人赞同了该回答 能.我一开始学编译原理的时候就是用Java写了好多小编译器和 ...

最新文章

  1. luoguP2479 [SDOI2010]捉迷藏
  2. rabbitmq一:基本概念
  3. Jquery通过ajax请求NodeJS返回json数据
  4. 计算机语言学习app,学习之编程语言
  5. 【数据库系统概论】考研第二部分重点分析【2.2】
  6. postman cookie设置_是时候抛弃Postman了,因为REST Client更香
  7. python协程与多线程比较_python-协程、多线程、多进程性能比较
  8. OpenShift 4 之Knative(2) - 第一个Serverless应用
  9. docker nginx部署web应用_实战docker,编写Dockerfile定制tomcat镜像,实现web应用在线部署...
  10. Java程序员是如何面试上阿里巴巴,如何拿到年薪50W
  11. 【job】面试中常见的笔试梳理
  12. php根据类名字符串,PHP 5.5 新特性 ::class 获取类名字符串
  13. Arcgis Javascript那些事儿(三)---arcgis sever服务器注册关于数据拷贝问题
  14. CTF__(1)Web之SQL手工注入
  15. 【论文笔记】HyperFace: ADeep Multi-task Learning Framework for Face Detection
  16. insert php code test
  17. java开发手册 - 码出规范 - 要点整理
  18. python学习使用easyocr识别图片文字
  19. 【自动化】手把手教你一个1秒钟归纳整理海量文件的python小技巧
  20. driftingblues4靶机(zbarimg二维码破解)

热门文章

  1. GMQ Wallet 跨时代产品,全面体现区块链数字资产价值
  2. mysql update语句很慢_缓慢的update语句性能分析
  3. 2023年复旦大学新闻与传播考研考情与难度、参考书及上岸前辈初复试备考经验
  4. picoCTF - la cifra de
  5. mysql双机热备数据库_MySQL 数据库双机热备方案
  6. 无字天书之Python第八页(基础数结构—下)
  7. class mate
  8. unity文字加密解密
  9. Nand Flash结构及错误机制
  10. Android Edittext焦点处理;