Search code examples
stringhttprequestnettyhandlerpipeline

How to handle both String and HttpRequest using channelhandler in Netty in java?


I want to handle two different clients. One is simple tcp client which sends string packets. Another one is http client which sends httprequest msg. I am a beginner in Netty, I don't know how handlers in pipelines flow.

This is my server coding:

public class TCPServer {

    int port;

    public static void main(String[] args) {
        new TCPServer().start();
    }

    public void start() {
        port = 1222;
        EventLoopGroup producer = new NioEventLoopGroup();
        EventLoopGroup consumer = new NioEventLoopGroup();

        try {

            ServerBootstrap bootstrap = new ServerBootstrap()
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .group(producer, consumer)//separate event loop groups to handle for parent and child for handling all chanel events
                    .channel(NioServerSocketChannel.class)//select type of chanel
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ServerAdapterInitializer());//configure chanel pipeline
            System.out.println("Server started");// configuring server channel
            bootstrap.bind(port).sync().channel().closeFuture().sync();//start the server and Wait until the server socket is closed. Thread gets blocked. 

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            producer.shutdownGracefully();
            consumer.shutdownGracefully();
        }

    }

}

This is my serverInitializer:

<pre>public class ServerAdapterInitializer extends ChannelInitializer<SocketChannel> {//special chanel handler configures registered chanel pipeline

    @Override
    protected void initChannel(SocketChannel channel) throws Exception {//this method is called once the chanel was registered
        ChannelPipeline pipeline = channel.pipeline();
        pipeline.addLast("decoder", new StringDecoder());//chanel inbound handler
        pipeline.addLast("encoder", new StringEncoder());
        pipeline.addLast("handler", new TCPServerHandler());

    }

}

And this my handler to handle both httprequest and string. But my handler never handle httprequest packet.

class TCPServerHandler extends SimpleChannelInboundHandler<Object> {
    private static final byte[] CONTENT = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };

    private static final ChannelGroup channels = new DefaultChannelGroup("tasks", GlobalEventExecutor.INSTANCE);                                                               

    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg)
            throws Exception {       

         if (msg instanceof HttpRequest) {
             System.out.println("http request");
            HttpRequest req = (HttpRequest) msg;

            boolean keepAlive = HttpUtil.isKeepAlive(req);
            FullHttpResponse response = new DefaultFullHttpResponse(req.protocolVersion(), OK,Unpooled.wrappedBuffer(CONTENT));
            response.headers()
                    .set(CONTENT_TYPE, TEXT_PLAIN)
                    .setInt(CONTENT_LENGTH, response.content().readableBytes());

            if (keepAlive) {
                if (!req.protocolVersion().isKeepAliveDefault()) {
                    response.headers().set(CONNECTION, KEEP_ALIVE);
                }
            } else {
                // Tell the client we're going to close the connection.
                response.headers().set(CONNECTION, CLOSE);
            }

            ChannelFuture f = ctx.write(response);

            if (!keepAlive) {
                f.addListener(ChannelFutureListener.CLOSE);
            }
        }

          if(msg instanceof String){
               System.out.println("String request");
            String arg1=(String)msg;
            Channel currentChannel = ctx.channel();
        if(arg1.equals("quit")){
            System.out.println("[INFO] - " + currentChannel.remoteAddress() + " is quitting... ");
        }else{
        System.out.println("[INFO] - " + currentChannel.remoteAddress() + " - "+ arg1);
        currentChannel.writeAndFlush("Server Said Hii "+ arg1);
        }
        }
    }

}

Solution

  • I don't think it is possible to configure the same server bootstrap to handle both HTTP requests and raw String messages. You need two server bootstraps (one for HTTP, one for String messages) each with its own pipeline. You already have the decoder/encoder for String message handling.

    EventLoopGroup producer = new NioEventLoopGroup();
    EventLoopGroup consumer = new NioEventLoopGroup();
    ServerBootstrap httpSb = new ServerBootstrap();
    ServerBootstrap strSb  = new ServerBootstrap();
    
    httpSb.group(producer, consumer).bind(<port for http>).<other methods>...
    strSb.group(producer, consumer).bind(<port for strings>).<other methods>...
    

    For HTTP, you need to add handlers HttpServerCodec and HttpObjectAggregator to be able to read FullHttpRequest from the channel and write FullHttpResponse into the channel.

    (aggregator is optional, it helps you to avoid the task of combining fragmented incoming HTTP data into a single (full) HTTP request as well as write a combined (full) HTTP response into the channel)

    In bootstrap for HTTP:

    ch.pipeline().addLast("httpcodec"     , new HttpServerCodec());
    ch.pipeline().addLast("httpaggregator", new HttpObjectAggregator(512 * 1024));
    ch.pipeline().addLast("yourhandler"   , new YourHttpRequestHandler());
    

    Example handler for FullHttpRequest processing:

    public class YourHttpRequestHandler extends ChannelInboundHandlerAdapter  {
    
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg_arg) 
        {
                FullHttpRequest msg = (FullHttpRequest)msg_arg;
    
                System.out.println("URI: " + msg.getUri());
                System.out.println("method: " + msg.getMethod().toString());
                System.out.println("protocol version: " + msg.getProtocolVersion()); 
                System.out.println("header1: " + msg.headers().get("header1"));
                System.out.println("header2: " + msg.headers().get("header2"));
                System.out.println("header3: " + msg.headers().get("header3"));
                System.out.println("content: " + msg.content().toString(CharsetUtil.UTF_8));
    
        }//end read
    
    }//end handler