com.vmware.xenon.common.http.netty.NettyHttpServerInitializer.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.xenon.common.http.netty.NettyHttpServerInitializer.java

Source

/*
 * Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy of
 * the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, without warranties or
 * conditions of any kind, EITHER EXPRESS OR IMPLIED.  See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.vmware.xenon.common.http.netty;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
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.UpgradeCodec;
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory;
import io.netty.handler.codec.http2.DefaultHttp2Connection;
import io.netty.handler.codec.http2.DelegatingDecompressorFrameListener;
import io.netty.handler.codec.http2.Http2CodecUtil;
import io.netty.handler.codec.http2.Http2Connection;
import io.netty.handler.codec.http2.Http2FrameLogger;
import io.netty.handler.codec.http2.Http2ServerUpgradeCodec;
import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler;
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.logging.LogLevel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.AsciiString;

import com.vmware.xenon.common.ServiceHost;
import com.vmware.xenon.common.ServiceHost.ServiceHostState.SslClientAuthMode;
import com.vmware.xenon.services.common.ServiceUriPaths;

public class NettyHttpServerInitializer extends ChannelInitializer<SocketChannel> {
    public static final String AGGREGATOR_HANDLER = "aggregator";
    public static final String HTTP_REQUEST_HANDLER = "http-request-handler";
    public static final String WEBSOCKET_HANDLER = "websocket-request-handler";
    public static final String HTTP1_CODEC = "http1-codec";
    public static final String HTTP2_UPGRADE_HANDLER = "http2-upgrade-handler";
    public static final String SSL_HANDLER = "ssl";

    private final SslContext sslContext;
    private ServiceHost host;
    private int responsePayloadSizeLimit;
    private static final boolean debugLogging = false;

    public NettyHttpServerInitializer(ServiceHost host, SslContext sslContext, int responsePayloadSizeLimit) {
        this.sslContext = sslContext;
        this.host = host;
        this.responsePayloadSizeLimit = responsePayloadSizeLimit;
        NettyLoggingUtil.setupNettyLogging();
    }

    /**
     * initChannel is called by Netty when a channel is first used.
     */
    @Override
    public void initChannel(SocketChannel ch) {
        ChannelPipeline p = ch.pipeline();
        ch.config().setAllocator(NettyChannelContext.ALLOCATOR);
        ch.config().setSendBufferSize(NettyChannelContext.BUFFER_SIZE);
        ch.config().setReceiveBufferSize(NettyChannelContext.BUFFER_SIZE);

        SslHandler sslHandler = null;
        if (this.sslContext != null) {
            sslHandler = this.sslContext.newHandler(ch.alloc());
            SslClientAuthMode mode = this.host.getState().sslClientAuthMode;
            if (mode != null) {
                switch (mode) {
                case NEED:
                    sslHandler.engine().setNeedClientAuth(true);
                    break;
                case WANT:
                    sslHandler.engine().setWantClientAuth(true);
                    break;
                default:
                    break;
                }
            }
            p.addLast(SSL_HANDLER, sslHandler);
        }

        // The HttpServerCodec combines the HttpRequestDecoder and the HttpResponseEncoder, and it
        // also provides a method for upgrading the protocol, which we use to support HTTP/2. It
        // also supports a couple other minor features (support for HEAD and CONNECT), which
        // probably don't matter to us.
        HttpServerCodec http1_codec = new HttpServerCodec(NettyChannelContext.MAX_INITIAL_LINE_LENGTH,
                NettyChannelContext.MAX_HEADER_SIZE, NettyChannelContext.MAX_CHUNK_SIZE, false);
        p.addLast(HTTP1_CODEC, http1_codec);
        if (this.sslContext == null) {
            // Today we only use HTTP/2 when SSL is disabled
            final HttpToHttp2ConnectionHandler connectionHandler = makeHttp2ConnectionHandler();
            UpgradeCodecFactory upgradeCodecFactory = new UpgradeCodecFactory() {
                @Override
                public UpgradeCodec newUpgradeCodec(CharSequence protocol) {
                    if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) {
                        return new Http2ServerUpgradeCodec(connectionHandler);
                    } else {
                        return null;
                    }
                }
            };
            // On upgrade, the upgradeHandler will remove the http1_codec and replace it
            // with the connectionHandler. Ideally we'd remove the aggregator (chunked transfer
            // isn't allowed in HTTP/2) and the WebSocket handler (we don't support it over HTTP/2 yet)
            // but we're not doing that yet.

            HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(http1_codec,
                    upgradeCodecFactory);

            p.addLast(HTTP2_UPGRADE_HANDLER, upgradeHandler);
        }

        p.addLast(AGGREGATOR_HANDLER, new HttpObjectAggregator(this.responsePayloadSizeLimit));
        p.addLast(WEBSOCKET_HANDLER, new NettyWebSocketRequestHandler(this.host,
                ServiceUriPaths.CORE_WEB_SOCKET_ENDPOINT, ServiceUriPaths.WEB_SOCKET_SERVICE_PREFIX));
        p.addLast(HTTP_REQUEST_HANDLER,
                new NettyHttpClientRequestHandler(this.host, sslHandler, this.responsePayloadSizeLimit));
    }

    /**
     * For HTTP/2 we don't have anything as simple as the HttpServerCodec (at least, not in Netty
     * 5.0alpha 2), so we create the equivalent.
     *
     * @return
     */
    private HttpToHttp2ConnectionHandler makeHttp2ConnectionHandler() {
        // DefaultHttp2Connection is for client or server. True means "server".
        Http2Connection connection = new DefaultHttp2Connection(true);
        InboundHttp2ToHttpAdapter inboundAdapter = new InboundHttp2ToHttpAdapterBuilder(connection)
                .maxContentLength(this.responsePayloadSizeLimit).propagateSettings(false).build();
        DelegatingDecompressorFrameListener frameListener = new DelegatingDecompressorFrameListener(connection,
                inboundAdapter);

        Http2Settings settings = new Http2Settings();
        //settings.maxConcurrentStreams(NettyHttpServiceClient.DEFAULT_HTTP2_STREAMS_PER_HOST);
        settings.initialWindowSize(NettyChannelContext.INITIAL_HTTP2_WINDOW_SIZE);

        HttpToHttp2ConnectionHandlerBuilder builder = new HttpToHttp2ConnectionHandlerBuilder()
                .frameListener(frameListener).initialSettings(settings).connection(connection);

        if (debugLogging) {
            Http2FrameLogger frameLogger = new Http2FrameLogger(LogLevel.INFO,
                    NettyHttpClientRequestInitializer.class);
            builder.frameLogger(frameLogger);
        }

        HttpToHttp2ConnectionHandler connectionHandler = builder.build();

        return connectionHandler;
    }
}