Java 如何使用netty编写http代理
我想写一个简单的程序,使用netty代理浏览器发送的http请求。 我认为它可以分为三个步骤Java 如何使用netty编写http代理,java,proxy,netty,Java,Proxy,Netty,我想写一个简单的程序,使用netty代理浏览器发送的http请求。 我认为它可以分为三个步骤 获取通过浏览器发送的请求 将其发送到网站 从网站接收数据并将其发送回浏览器 问题: 当我使用Bootstrap.connect(主机,端口)时,如何将url转换为主机和端口 当我使用HttpServerResponseHandler.connect和ChannelHandlerContext.writeAndFlush(httpMessage)时;要向网站发送数据,我如何从网站获取响应数据并将其发送回浏
public class Server {
public static void main(String[] args) throws InterruptedException {
final int port = 8888;
// copy from https://github.com/netty/netty/wiki/User-guide-for-4.x
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new HttpRequestDecoder(), new HttpServerRequestHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync();
// Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to gracefully
// shut down your server.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
巧合的是,为了学习,我也一直在开发一个Netty代理服务器。我有一个完整的工作代码,你可以在我的上找到,但我会在这里回答你的问题。Netty也有一个官方的代理服务器示例,但与我的代码不同,他们没有单元测试 (仅供参考,我的代码是Kotlin) 核心理念: 创建代理服务器时,需要一台服务器来接受客户端请求,还需要一台远程代理服务器的客户端。您创建了服务器,但没有创建客户端。最好重用由服务器创建的
EventLoop
,而不是为客户端创建一个新的循环。每个事件循环都在一个专用线程上运行,因此创建更多的事件循环将产生额外的线程,在接受的通道
和客户端通道
之间交换数据时需要进行上下文切换
如何将url转换为主机和端口
为了简单起见,我使用了一个将HttpMessage
及其后续HttpContents
聚合为单个FullHttpRequest
或FullHttpResponse
(取决于它是否用于处理请求或响应)。设置URL很简单:只需调用
要获取主机和端口,请调用客户端通道,并将生成的SocketAddress
强制转换为InetSocketAddress
,从中可以获取主机和端口。如果存在主机
标题,请不要忘记以类似方式重置
如何获取响应数据
在建立客户端通道(您缺少的)之后,您需要在该通道上发出请求。客户机通道有一个引用原始服务器通道的处理程序。处理程序收到响应后,会将其写入服务器通道。您能修复它吗?@AcidBurn几乎无法修复它。你可以看到答案“呼救”。@Abhijit你能看到你对这个问题有什么意见吗?
public class HttpServerRequestHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// step 1 get data from browser
if (msg instanceof LastHttpContent) {
ctx.close();
return;
}
DefaultHttpRequest httpMessage = (DefaultHttpRequest) msg;
System.out.println("浏览器请求====================");
System.out.println(msg);
System.out.println();
doWork(ctx, httpMessage);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
private void doWork(ChannelHandlerContext ctx, final DefaultHttpRequest msg) {
// step 2 send data to website
// translate url into host and port
String host = msg.uri();
int port = 80;
if (host.startsWith("https://")) {
host = host.replaceFirst("https://", "");
port = 443;
} else if (host.startsWith("http://")) {
host = host.replaceFirst("http://", "");
port = 80;
}
if (host.contains(":443")) {
host = host.replace(":443", "");
port = 443;
}
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
//b.option(ChannelOption.AUTO_READ, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new HttpServerResponseHandler(msg), new HttpRequestEncoder());
}
});
// question 1
ChannelFuture f = b.connect(host, port).sync();
//ChannelFuture f = b.connect("www.baidu.com", 443).sync();
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
}
}
}
public class HttpServerResponseHandler extends ChannelOutboundHandlerAdapter {
private Object httpMessage;
public HttpServerResponseHandler(Object o) {
this.httpMessage = o;
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
System.out.println("网页请求结果=========================");
System.out.println(httpMessage);
System.out.println();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
cause.printStackTrace();
ctx.close();
}
@Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, ChannelPromise promise) throws Exception {
System.out.println("connect !!!!!!!!!!!");
// question 2
ctx.writeAndFlush(httpMessage);
}
}