0

这与这个问题有关,但我试图将我的问题分解成更小的步骤。

我正在尝试使用接收http请求的netty编写一个简单的http服务器(服务器A),向另一台服务器(服务器B)发出http请求,然后将响应中的内容复制到对初始请求的响应中。我知道有一些如何做到这一点的例子,比如 LittleProxy,但代码相当复杂,而且由于我是 netty 的 n00b,我试图让我的第一个代码尽可能简单,而不会进入杂草。

现在,我忽略了所有关于并发的问题,只建立了一个从服务器 A 到服务器 B 的通道(我知道这会因并发请求而严重中断,但它使我的初始任务更简单)。

我的方法如下:

  1. 设置客户端引导程序并连接到在 localhost 端口 18080 上运行的服务器 B。获取相应的通道。

  2. 启动服务器 A 在端口 2080 上侦听,使用管道解码 http 请求,然后写入到服务器 B 的通道。

  3. 向生成的通道 future 添加一个侦听器,它将从服务器 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)
4

1 回答 1

3

您需要设置一个客户端管道来等待响应,然后将其写入您的响应。

请参阅 snoop 客户端示例;具体来说,HttpSnoopClientHandler

于 2013-04-02T22:57:48.800 回答