tokuhirom's Blog

netty で http server

RxNetty 触ってると、素の Netty だとどうなんだっけ? というところも把握しておかないと厳しい面もありますので、一応ひさびさに netty で素の http server を書いてみる。

keep-alive まわりの処理とか chunked の処理とか手で全部書かないといけないからやはり生で使うものではないな、という感じ。

package com.example;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;

import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;

/**
 * もっとも基本的な HTTP server の実装。
 */
@Slf4j
public class HttpServer {
    public static void main(String[] args) throws InterruptedException {
        int port = 3000;

        NioEventLoopGroup group = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(group)
                .channel(NioServerSocketChannel.class)
                .localAddress(new InetSocketAddress(port))
                .handler(new LoggingHandler(LogLevel.DEBUG))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(
                                new HttpServerCodec(),
                                new HttpObjectAggregator(512 * 1024),
                                new HttpServerHandler());
                    }
                });
        try {
            ChannelFuture f = bootstrap.bind().syncUninterruptibly();
            log.info("Listening: {}", f.channel().localAddress());
            f.channel().closeFuture().syncUninterruptibly();
        } finally {
            group.shutdownGracefully().syncUninterruptibly();
        }
    }

    @Slf4j
    public static class HttpServerHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            FullHttpRequest request = (FullHttpRequest) msg;
            log.info("[{}] {} {} {}",
                    ctx.channel().remoteAddress(),
                    request.protocolVersion(),
                    request.method(),
                    request.uri());
            byte[] content = "Hello".getBytes(StandardCharsets.UTF_8);
            DefaultHttpResponse defaultHttpResponse = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1,
                    HttpResponseStatus.OK,
                    Unpooled.buffer()
                            .writeBytes(content),
                    new DefaultHttpHeaders()
                            .add(HttpHeaderNames.CONTENT_TYPE, "text/plain")
                            .add(HttpHeaderNames.CONTENT_LENGTH, content.length),
                    EmptyHttpHeaders.INSTANCE);

            if (!HttpUtil.isKeepAlive(request)) {
                ctx.writeAndFlush(defaultHttpResponse)
                        .addListener(ChannelFutureListener.CLOSE);
            } else {
                defaultHttpResponse.headers()
                        .add(HttpHeaderNames.CONNECTION, KEEP_ALIVE);
                ctx.writeAndFlush(defaultHttpResponse);
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx,
                                    Throwable cause) {
            log.warn("Caught unhandled exception", cause);
            ctx.close();
        }
    }
}

一通り書いてから気づいたけど https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/http/helloworld に実装例が載っているわ。