org.pidome.server.system.network.http.HttpProtocolNegotiator.java Source code

Java tutorial

Introduction

Here is the source code for org.pidome.server.system.network.http.HttpProtocolNegotiator.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.pidome.server.system.network.http;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory;
import io.netty.handler.codec.http2.DefaultHttp2Connection;
import io.netty.handler.codec.http2.Http2CodecUtil;
import io.netty.handler.codec.http2.Http2ServerUpgradeCodec;
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter;
import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
import io.netty.handler.codec.spdy.SpdyFrameCodec;
import io.netty.handler.codec.spdy.SpdyHttpDecoder;
import io.netty.handler.codec.spdy.SpdyHttpEncoder;
import io.netty.handler.codec.spdy.SpdyHttpResponseStreamIdHandler;
import io.netty.handler.codec.spdy.SpdySessionHandler;
import io.netty.handler.codec.spdy.SpdyVersion;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
import io.netty.util.AsciiString;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 *
 * @author John
 */
public class HttpProtocolNegotiator extends ApplicationProtocolNegotiationHandler {

    /**
     * Negotiator logger.
     */
    static Logger LOG = LogManager.getLogger(HttpProtocolNegotiator.class);

    /**
     * If SSL or not, only used with http not spdy
     */
    private final boolean ssl;
    /**
     * The port the services run on.
     */
    private final int port;

    /**
     * Upgrade facory to go from http1.1 to http2
     */
    private static final UpgradeCodecFactory upgradeCodecFactory = (CharSequence protocol) -> {
        if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) {
            return new Http2ServerUpgradeCodec(new HttpToHttp2ConnectionHandlerBuilder().build());
        } else {
            return null;
        }
    };

    /**
     * Negotiator constructow.
     * @param ssl Indicator for client handlers if SSL is used.
     * @param port The port used.
     */
    protected HttpProtocolNegotiator(boolean ssl, int port) {
        super(ApplicationProtocolNames.HTTP_1_1);
        this.ssl = ssl;
        this.port = port;
    }

    /**
     * Configures the HTTP pipeline.
     * @param ctx Channel handler context.
     * @param protocol Protocol initiated. either http 1.1 or spdy (http 2)
     * @throws Exception 
     */
    @Override
    protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
        LOG.debug("Got protocol: {}", protocol);
        if (ApplicationProtocolNames.SPDY_3_1.equals(protocol)) {
            configureSpdy(ctx, SpdyVersion.SPDY_3_1, port);
            return;
        }

        if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
            configureHttp1(ctx, ssl, port);
            return;
        }

        if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
            configureHttp2(ctx, port);
            return;
        }

        LOG.warn("unknown protocol: " + protocol);
    }

    /**
     * Configures the channel to use spdy.
     * @param ctx The channel handler.
     * @param version The spdy version.
     * @param port The port used.
     * @throws Exception When the channel can not be configured.
     */
    private static void configureSpdy(ChannelHandlerContext ctx, SpdyVersion version, int port) throws Exception {
        ctx.pipeline().addLast(new SpdyFrameCodec(version)).addLast(new SpdySessionHandler(version, true))
                .addLast(new SpdyHttpEncoder(version)).addLast(new SpdyHttpDecoder(version, 5242880))
                .addLast(new SpdyHttpResponseStreamIdHandler()).addLast(new HttpClientHandler(true, port))
                .addLast(new UserEventLogger());
    }

    /**
     * Configures an http2 channel.
     * @param ctx 
     */
    private static void configureHttp2(ChannelHandlerContext ctx, int port) {
        DefaultHttp2Connection connection = new DefaultHttp2Connection(true);
        InboundHttp2ToHttpAdapter listener = new InboundHttp2ToHttpAdapterBuilder(connection)
                .propagateSettings(true).validateHttpHeaders(false).maxContentLength(5242880).build();

        ctx.pipeline().addLast(
                new HttpToHttp2ConnectionHandlerBuilder().frameListener(listener).connection(connection).build());

        ctx.pipeline().addLast(new Http2ClientHandler(port));
    }

    /**
     * Configures the channel to use http.
     * This defaults to http 1.1
     * @param ctx The channel context
     * @param ssl if the channel should be used with ssl enabled.
     * @param port The port the channel lives on.
     * @throws Exception When the http channel can not be configured.
     */
    protected static void configureHttp1(ChannelHandlerContext ctx, boolean ssl, int port) throws Exception {
        setHttp1PipeLine(ctx.pipeline(), ssl, port);
    }

    /**
     * Set's the http 1 pipeline.
     * @param pipe The pipeline to configure.
     * @param ssl SSL enabled or not.
     * @param port The port in use.
     */
    protected static final void setHttp1PipeLine(ChannelPipeline pipe, boolean ssl, int port) {
        final HttpServerCodec sourceCodec = new HttpServerCodec();
        pipe.addLast(sourceCodec);
        if (ssl) {
            pipe.addLast(new HttpServerUpgradeHandler(sourceCodec, upgradeCodecFactory));
        }
        //pipe.addLast("encoder", new HttpResponseEncoder())
        //    .addLast("decoder", new HttpRequestDecoder())
        pipe.addLast("aggregator", new HttpObjectAggregator(5242880))
                //    .addLast(new HttpContentCompressor())
                .addLast("handler", new Http1EntryPacketHandler(ssl, port))
                ///.addLast(new WebSocketServerProtocolHandler(HttpWebsocketHelper.WEBSOCKET_ADDRESS))
                .addLast(new UserEventLogger());
    }

    /**
     * Class that logs any User Events triggered on this channel.
     */
    private static class UserEventLogger extends ChannelInboundHandlerAdapter {
        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
            LOG.debug("User Event Triggered: " + evt);
            ctx.fireUserEventTriggered(evt);
        }
    }

}