介绍说明
普通代理(Http)
Http,RFC 7230中描述为普通代理,其代理扮演的是“中间人”角色,对于连接到它的客户端来说,它是服务端,对于要连接的服务端来说,它是客户端,代理服务器负责在两端之间来回传送HTTP报文。
隧道代理(Https)
Https,代理服务器是一个web服务器,影响了客户端和服务器的TLS加密连接的。此时主要使用RFC中定义的通过 Web 代理服务器用隧道方式传输基于 TCP 的协议的隧道代理方式,它的主要流程为:
1、浏览器首先发送Http Connect请求给代理服务器,发送目标主机信息。
2、代理服务器建立和目标主机的tcp链接,并向浏览器回应Connection Established应答。
3、浏览器将请求发送给代理服务器,代理服务器透传给目标主机。
4、目标主机将响应回给代理服务器,代理服务器将响应回给浏览器。
Netty实现
Netty高性能网络框架,支持OIO(阻塞式传输)/NIO(非阻塞式传输)等能力,同时屏蔽了网络底层现,使开发人员专注于应用逻辑开发。
Netty框架中大量运用了直接内存用来提高效率,编写代码时要注意及时释放资源,避免内存泄漏问题。
代码分享
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.70</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15to18</artifactId>
<version>1.70</version>
</dependency>
<!-- netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.60.Final</version>
</dependency>package com.what21.netty03.demo01;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.net.InetSocketAddress;
public class HttpServer {
private String serverName;
private String bindHost;
private int bindPort;
public HttpServer(String serverName, String bindHost, int bindPort) {
this.serverName = serverName;
this.bindHost = bindHost;
this.bindPort = bindPort;
}
public void start() throws Exception {
// 用于Acceptor的主"线程池"
EventLoopGroup bossEventGroup = new NioEventLoopGroup();
// 初始化==>用于I/O工作的从"线程池"
EventLoopGroup workerEventGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
// group方法设置主从线程池
serverBootstrap.group(bossEventGroup, workerEventGroup);
// 指定通道channel类型,服务端为:NioServerSocketChannel
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);
serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);
serverBootstrap.handler(new LoggingHandler(LogLevel.INFO));
serverBootstrap.childHandler(new HttpServerInitializer());
ChannelFuture bindFuture = null;
if ("0.0.0.0".equalsIgnoreCase(this.bindHost)) {
bindFuture = serverBootstrap.bind(this.bindPort).sync();
} else {
InetSocketAddress bindAddress = new InetSocketAddress(this.bindHost, this.bindPort);
bindFuture = serverBootstrap.bind(bindAddress).sync();
}
Channel parentChannel = bindFuture.channel();
bindFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (channelFuture.isSuccess()) {
System.out.println(HttpServer.this.serverName + ",绑定监听成功," + channelFuture.channel().localAddress());
} else {
System.err.println(HttpServer.this.serverName + ",绑定监听失败!" + channelFuture.cause());
}
}
});
ChannelFuture closeFuture = bindFuture.channel().closeFuture().sync();
closeFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (channelFuture.isSuccess()) {
System.out.println(HttpServer.this.serverName + ",停止监听成功," + channelFuture.channel().localAddress());
} else {
System.err.println(HttpServer.this.serverName + ",停止监听失败!" + channelFuture.cause());
}
}
});
} catch (Exception e) {
e.printStackTrace();
} finally {
// 优雅退出,释放"线程池"
if (bossEventGroup != null) {
bossEventGroup.shutdownGracefully();
}
if (workerEventGroup != null) {
workerEventGroup.shutdownGracefully();
}
}
}
public static void main(String[] args) {
try {
new HttpServer("HTTP代理服务器", "0.0.0.0", 8888).start();
} catch (Exception e) {
e.printStackTrace();
}
}
}package com.what21.netty03.demo01;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.ipfilter.IpFilterRule;
import io.netty.handler.ipfilter.IpFilterRuleType;
import io.netty.handler.ipfilter.IpSubnetFilterRule;
import io.netty.handler.ipfilter.RuleBasedIpFilter;
import java.net.InetSocketAddress;
public class HttpServerInitializer extends ChannelInitializer<Channel> {
@Override
protected void initChannel(Channel ch) throws Exception {
IpSubnetFilterRule rule1 = new IpSubnetFilterRule("192.168.119.1", 24, IpFilterRuleType.ACCEPT);
IpSubnetFilterRule rule2 = new IpSubnetFilterRule("127.0.0.1", 32, IpFilterRuleType.REJECT);
IpFilterRule rejectAll = new IpFilterRule() {
@Override
public boolean matches(InetSocketAddress remoteAddress) {
return true;
}
@Override
public IpFilterRuleType ruleType() {
return IpFilterRuleType.REJECT;
}
};
RuleBasedIpFilter filter = new RuleBasedIpFilter(rule1, rejectAll);
ch.pipeline().addLast("ipFilter", filter);
ch.pipeline().addLast("httpServerCodec", new HttpServerCodec());
ch.pipeline().addLast("httpObjectAggregator", new HttpObjectAggregator(65536));
ch.pipeline().addLast("httpProxyServerHandler", new HttpProxyServerHandler());
}
}package com.what21.netty03.demo01;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
public class HttpProxyServerHandler 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;
String authorization = request.headers().get("Authorization");
System.out.println("http basic认证信息:" + authorization);
if ("CONNECT".equalsIgnoreCase(request.method().name())) { // HTTPS建立代理握手
HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
ctx.writeAndFlush(response);
ctx.pipeline().remove("httpServerCodec");
ctx.pipeline().remove("httpObjectAggregator");
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);
}
}
}
}package com.what21.netty03.demo01;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
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(512 * 1024));
ch.pipeline().addLast(new HttpProxyClientHandler(clientChannel));
}
}package com.what21.netty03.demo01;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.FullHttpResponse;
public class HttpProxyClientHandler extends ChannelInboundHandlerAdapter {
private Channel clientChannel;
public HttpProxyClientHandler(Channel clientChannel) {
this.clientChannel = clientChannel;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
FullHttpResponse response = (FullHttpResponse) msg;
//修改http响应体返回至客户端
response.headers().add("response", "from proxy");
clientChannel.writeAndFlush(msg);
}
}