这与this question有关,但我试图将问题分解为更小的步骤。
我正在尝试编写一个简单的http服务器(服务器A),使用netty接收http请求,向另一个服务器(服务器B)发出http请求,然后将响应中的内容复制到响应中初始请求。我知道有一些如何做到这一点的例子,比如LittleProxy,但是代码相当复杂,而且因为我是netty的n00b,所以我试图让我的第一个代码尽可能简单而不会进入杂草。
目前,我忽略了所有关于并发的问题,并且只有一个从服务器A到服务器B建立的通道(我知道这会对并发请求造成可怕的破坏,但它使我的初始任务更简单)。
我的方法如下:
设置客户端引导程序并连接到在localhost端口18080上运行的服务器B.获取相应的通道。
启动服务器A侦听端口2080,其中包含解码http请求的管道,然后写入到服务器B的通道。
在生成的通道中添加一个侦听器,将来自服务器B的响应内容复制到对原始客户端对服务器A的请求的响应。
这是我所拥有的代码(非常简短),我正在尝试完全按照上面的描述进行操作。我的问题是我不知道如何将服务器B的响应复制到服务器的响应。当我在服务器A发送的响应中写入原始客户端时,我想通知的一种方法会导致IllegalArgumentException(我检查了ChannelBuffer的内容,并且代理服务器返回了正确的文本)。我已经粘贴了下面例外的部分堆栈跟踪。其他评论表示欢迎,因为除了明显缺乏对服务器B的频道锁定之外,我可能还有其他错误:
public class NettyExample {
private static Channel channel;
private static Map<Channel, Channel> proxyToClient = new ConcurrentHashMap<Channel, Channel>();
public static void main(String[] args) throws Exception {
ChannelFactory clientFactory =
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
final ClientBootstrap cb = new ClientBootstrap(clientFactory);
cb.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
return Channels.pipeline(
new HttpRequestEncoder(),
new HttpResponseDecoder(),
new ResponseHandler());
}
});
ChannelFuture cf = cb.connect(new InetSocketAddress("localhost", 18080));
channel = cf.awaitUninterruptibly().getChannel();
ChannelFactory factory =
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
ServerBootstrap sb = new ServerBootstrap(factory);
sb.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
return Channels.pipeline(
new HttpRequestDecoder(),
new RequestHandler());
}
});
sb.setOption("child.tcpNoDelay", true);
sb.setOption("child.keepAlive", true);
sb.bind(new InetSocketAddress(2080));
}
private static class ResponseHandler extends SimpleChannelHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e) {
final HttpResponse proxyResponse = (HttpResponse) e.getMessage();
Channel clientChannel = proxyToClient.get(e.getChannel());
HttpResponse clientResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
clientResponse.setContent(proxyResponse.getContent());
clientChannel.write(clientResponse).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) {
Channel ch = future.getChannel();
ch.close();
}
});
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
Channel ch = e.getChannel();
ch.close();
}
}
private static class RequestHandler extends SimpleChannelHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e) {
final HttpRequest request = (HttpRequest) e.getMessage();
System.out.println("calling client channel");
proxyToClient.put(channel, e.getChannel());
channel.write(request);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
Channel ch = e.getChannel();
ch.close();
}
}
}
这个中继调用似乎有效,直到调用clientChannel.write(clientResponse)。在那里,生成以下异常:
java.lang.IllegalArgumentException: unsupported message type: class org.jboss.netty.handler.codec.http.DefaultHttpResponse
at org.jboss.netty.channel.socket.nio.SocketSendBufferPool.acquire(SocketSendBufferPool.java:53)
at org.jboss.netty.channel.socket.nio.AbstractNioWorker.write0(AbstractNioWorker.java:468)
at org.jboss.netty.channel.socket.nio.AbstractNioWorker.writeFromTaskLoop(AbstractNioWorker.java:432)