title: How to write a http proxy using netty
body: <p>I want to write a simple program using netty to proxy http request send by browser.
  I think it can be divided into 3 steps</p>
  
  <ol>
  <li>get request send by browser</li>
  <li>send it to the website</li>
  <li>receive data from website and send it back to the browser.  </li>
  </ol>
  
  <p>Question: </p>
  
  <ol>
  <li>How to translate url into host and port when I using Bootstrap.connect(host, port);  </li>
  <li>When I using HttpServerResponseHandler.connect and ChannelHandlerContext.writeAndFlush(httpMessage); to send data to website, how can I get the response data from the website and send it back to the browser?  </li>
  </ol>
  
  <p>It's my first day studying netty, so please try to answer as easy as possible. Thank you very much.</p>
  
  <pre><code>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&lt;SocketChannel&gt;() {
                          @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();
          }
      }
  } 
  </code></pre>
  
   
  
  <pre><code>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&lt;SocketChannel&gt;() {
                  @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();
          }
      }
  }
  </code></pre>
  
  
  
  <pre><code>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);
      }
  }
  </code></pre>