View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.example.proxy;
17  
18  import io.netty.bootstrap.Bootstrap;
19  import io.netty.buffer.Unpooled;
20  import io.netty.channel.Channel;
21  import io.netty.channel.ChannelFuture;
22  import io.netty.channel.ChannelFutureListener;
23  import io.netty.channel.ChannelHandlerContext;
24  import io.netty.channel.ChannelInboundHandlerAdapter;
25  import io.netty.channel.ChannelOption;
26  
27  public class HexDumpProxyFrontendHandler extends ChannelInboundHandlerAdapter {
28  
29      private final String remoteHost;
30      private final int remotePort;
31  
32      // As we use inboundChannel.eventLoop() when building the Bootstrap this does not need to be volatile as
33      // the outboundChannel will use the same EventLoop (and therefore Thread) as the inboundChannel.
34      private Channel outboundChannel;
35  
36      public HexDumpProxyFrontendHandler(String remoteHost, int remotePort) {
37          this.remoteHost = remoteHost;
38          this.remotePort = remotePort;
39      }
40  
41      @Override
42      public void channelActive(ChannelHandlerContext ctx) {
43          final Channel inboundChannel = ctx.channel();
44  
45          // Start the connection attempt.
46          Bootstrap b = new Bootstrap();
47          b.group(inboundChannel.eventLoop())
48           .channel(ctx.channel().getClass())
49           .handler(new HexDumpProxyBackendHandler(inboundChannel))
50           .option(ChannelOption.AUTO_READ, false);
51          ChannelFuture f = b.connect(remoteHost, remotePort);
52          outboundChannel = f.channel();
53          f.addListener(future -> {
54              if (future.isSuccess()) {
55                  // connection complete start to read first data
56                  inboundChannel.read();
57              } else {
58                  // Close the connection if the connection attempt has failed.
59                  inboundChannel.close();
60              }
61          });
62      }
63  
64      @Override
65      public void channelRead(final ChannelHandlerContext ctx, Object msg) {
66          if (outboundChannel.isActive()) {
67              outboundChannel.writeAndFlush(msg).addListener(future -> {
68                  if (future.isSuccess()) {
69                      // was able to flush out data, start to read the next chunk
70                      ctx.read();
71                  } else {
72                      ctx.close();
73                  }
74              });
75          }
76      }
77  
78      @Override
79      public void channelInactive(ChannelHandlerContext ctx) {
80          if (outboundChannel != null) {
81              closeOnFlush(outboundChannel);
82          }
83      }
84  
85      @Override
86      public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
87          cause.printStackTrace();
88          closeOnFlush(ctx.channel());
89      }
90  
91      /**
92       * Closes the specified channel after all queued write requests are flushed.
93       */
94      static void closeOnFlush(Channel ch) {
95          if (ch.isActive()) {
96              ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
97          }
98      }
99  }