Netty实现Http/Https服务器

2020-03-01  本文已影响0人  jimband

1、Http服务器实现

引导服务

@Slf4j
 public class HttpServer {
    static final int PORT = 8080;

    public static void main(String[] args) throws Exception {
        start();
    }
    public static void start() throws Exception {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workGroup = new NioEventLoopGroup(10);
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024)
                .option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.SO_KEEPALIVE, true);
        try {
            serverBootstrap.group(bossGroup, workGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new HttpServerInitializer());
            Channel channel = serverBootstrap.bind(PORT).sync().channel();
            log.info("HttpServer listening on port " + PORT);
            channel.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

添加对Http的支持

@Slf4j
public class HttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {

    private static final String FAVICON_ICO = "/favicon.ico";

    private HttpHeaders headers;
    private HttpRequest request;
    private FullHttpRequest fullRequest;



    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject msg) throws Exception {
        if (msg instanceof HttpRequest) {
            request = (HttpRequest) msg;
            headers = request.headers();
            String uri = request.uri();
            log.info("request uri:" + uri);
            if (FAVICON_ICO.equals(uri)) return;
            HttpMethod method = request.method();
            if (HttpMethod.GET.equals(method)) {
                QueryStringDecoder queryStringDecoder = new QueryStringDecoder(uri, Charsets.UTF_8);
                Map<String, List<String>> parameters = queryStringDecoder.parameters();
                log.info("requestParam----> {}", parameters);
            } else if (HttpMethod.POST.equals(method)) {
                fullRequest = (FullHttpRequest) msg;
                dealWithContentType();
            }

            Student student = new Student();
            student.setId("12");
            student.setClassNo("05");
            student.setName("jim");
            String resp = JSONObject.toJSONString(student);
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(resp.getBytes()));
            response.headers().set("Content-Type", "application/json");
            response.headers().set("Content-Length", response.content().readableBytes());
            boolean keepAlive = HttpUtil.isKeepAlive(request);
            if (!keepAlive) {
                channelHandlerContext.write(response).addListener(ChannelFutureListener.CLOSE);
            } else {
                response.headers().set("Connection", "true");
                channelHandlerContext.write(response);
            }

        } else {
            log.info("非法请求");
        }
    }

    private void dealWithContentType() {
        String contentType = headers.get("Content-Type").split(";")[0];
        if (contentType.equals("application/json")) {
            String jsonStr = fullRequest.content().toString(Charsets.UTF_8);
            log.info("requestContent--->{}", jsonStr);
        } else if (contentType.equals("application/x-www-form-urlencoded")) {
            String requestStr = fullRequest.content().toString(Charsets.UTF_8);
            QueryStringDecoder queryStringDecoder = new QueryStringDecoder(requestStr, Charsets.UTF_8);
            log.info("requestContent--->{}", queryStringDecoder);
        } else {
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("HttpServerHandler happened exception", cause);
        ctx.close();
    }

实现对Https的支持

public class HttpsServerInitializer extends ChannelInitializer {

    private SSLContext sslContext;

    public HttpsServerInitializer(SSLContext sslContext) {
        this.sslContext = sslContext;
    }

    @Override
    protected void initChannel(Channel channel) throws Exception {
        ChannelPipeline pipeline = channel.pipeline();
        SSLEngine sslEngine = sslContext.createSSLEngine();
        sslEngine.setUseClientMode(false);
        channel.pipeline().addLast("ssl", new SslHandler(sslEngine))
                .addLast("codec", new HttpServerCodec())
                .addLast(new HttpObjectAggregator(1024 * 1024))
                .addLast(new HttpServerHandler());
    }
}

public class SSLContextFactory {

    public static SSLContext getSslContext() throws Exception {
        char[] filePass = "123456".toCharArray();
        SSLContext sslContext = SSLContext.getInstance("TLSv1.1");
        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(Files.newInputStream(Paths.get("/tmp/key/test.jks"), StandardOpenOption.READ), filePass);
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(keyStore, filePass);
        sslContext.init(kmf.getKeyManagers(), null, null);
        return sslContext;
    }
}

服务端的秘钥可以用JDK提供的keytool生成
在使用Https的需要注意,如果服务端不支持客户端使用的TLS版本,会报javax.net.ssl.SSLException: Received fatal alert: certificate_unknown异常。
可以在启动服务的时候添加JVM参数 -Djavax.net.debug=all,可以查看http握手过程以及客户端使用的TLS版本。


image.png
上一篇 下一篇

猜你喜欢

热点阅读