2017-09-11 9 views
3

私はブラウザで送信するプロキシのhttp要求にnettyを使って簡単なプログラムを書いてみたいです。私はそれが3に分けることができると思い はブラウザ nettyを使ってhttpプロキシを書くには

  • でお送り

    1. get要求は、ウェブサイト
    2. に送信する手順Webサイトからデータを受信し、ブラウザに送り返します。

    質問:私はBootstrap.connect(ホスト、ポート)を使用した場合に、ホストとポートにURLを変換する方法

    1. 私はHttpServerResponseHandler.connectとChannelHandlerContext.writeAndFlush(httpMessage)を使用しています。データをWebサイトに送信するには、Webサイトから返信データを取得してブラウザに送り返すにはどうすればよいですか?

    私はネットで勉強していたので、できるだけ簡単に答えてください。どうもありがとうございました。

    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(); 
         } 
        } 
    } 
    
    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); 
        } 
    } 
    
  • +0

    修正できましたか? – AcidBurn

    +0

    @AcidBurnはそれを修正することはほとんどありません。あなたは、助けのための答え打撃を見ることができます。 – Jet

    答えて

    0

    偶然、私はまた、学習の目的のためにネッティープロキシサーバに取り組んできました。私はGitHubで見つけることができる完全に動作するコードを持っていますが、私はここであなたの質問に答えます。 Nettyにも公式のプロキシサーバーの例があるhereがありますが、私のコードとは異なり、単体テストはありません。

    (参考:私のコードはKotlinにあります)

    核となるアイデア

    プロキシサーバーを作成し、あなたがプロキシするよりも、あなたは、クライアントの要求を受け入れるだけでなく、リモートのクライアントするサーバーが必要です。 サーバーは作成しましたが、クライアントは作成しません。クライアントのために新しいものを作成するのではなく、サーバーによって作成されたEventLoopを再利用することが最善です。各イベントループは専用のスレッド上で実行されるため、より多くのスレッドを作成すると追加のスレッドが生成され、受け入れられたChannelとクライアントChannelの間でデータを交換する際にコンテキスト切り替えが必要になります。物事をシンプルに保つためにホストとポート

    にURLを変換するために、私はHttpMessageおよび単一FullHttpRequestまたはFullHttpResponseにその次HttpContentsそれならば(上の依存を集約HttpObjectAggregatorを使用しましたどのように

    要求または応答を処理するために使用されます)。 URLを設定するのは簡単です:FullHttpRequest.setUriに電話してください。

    、ホストとポートを取得するクライアントチャネル上Channel.remoteAddress()を呼び出して、あなたがホストとポートを取得することができ、そこからInetSocketAddress、その結果得られるSocketAddressをキャストします。同様にHostヘッダーがある場合はリセットすることを忘れないでください。

    は、どのように私は(あなたが不足していること)、あなたはそのチャンネルにリクエストを行う必要があり、クライアントチャネルを確立した後、応答データに

    を得ることができます。クライアントチャネルには、元のサーバーチャネルを参照するハンドラがあります。ハンドラは応答を受け取ると、それをサーバチャネルに書き込みます。

    +0

    ありがとうございます...後であなたのコードを勉強します – Jet

    関連する問題