如何解码来自http请求的响应并使用netty中的内容?

时间:2013-04-02 19:25:20

标签: java netty

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

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

目前,我忽略了所有关于并发的问题,并且只有一个从服务器A到服务器B建立的通道(我知道这会对并发请求造成可怕的破坏,但它使我的初始任务更简单)。

我的方法如下:

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

  2. 启动服务器A侦听端口2080,其中包含解码http请求的管道,然后写入到服务器B的通道。

  3. 在生成的通道中添加一个侦听器,将来自服务器B的响应内容复制到对原始客户端对服务器A的请求的响应。

  4. 这是我所拥有的代码(非常简短),我正在尝试完全按照上面的描述进行操作。我的问题是我不知道如何将服务器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)
    

1 个答案:

答案 0 :(得分:3)

您需要设置客户端管道以等待响应,然后将其写为您的响应。

查看snoop客户端example;具体而言,HttpSnoopClientHandler