All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.zuul.netty.server.BaseZuulChannelInitializer Maven / Gradle / Ivy

There is a newer version: 2.5.13
Show newest version
/*
 * Copyright 2018 Netflix, Inc.
 *
 *      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.netflix.zuul.netty.server;

import com.netflix.config.CachedDynamicIntProperty;
import com.netflix.netty.common.*;
import com.netflix.netty.common.accesslog.AccessLogChannelHandler;
import com.netflix.netty.common.accesslog.AccessLogPublisher;
import com.netflix.netty.common.channel.config.ChannelConfig;
import com.netflix.netty.common.channel.config.CommonChannelConfigKeys;
import com.netflix.netty.common.metrics.EventLoopGroupMetrics;
import com.netflix.netty.common.metrics.HttpBodySizeRecordingChannelHandler;
import com.netflix.netty.common.metrics.HttpMetricsChannelHandler;
import com.netflix.netty.common.metrics.PerEventLoopMetricsChannelHandler;
import com.netflix.netty.common.metrics.ServerChannelMetrics;
import com.netflix.netty.common.proxyprotocol.ElbProxyProtocolChannelHandler;
import com.netflix.netty.common.proxyprotocol.StripUntrustedProxyHeadersHandler;
import com.netflix.netty.common.status.ServerStatusHeaderHandler;
import com.netflix.netty.common.status.ServerStatusManager;
import com.netflix.netty.common.throttle.MaxInboundConnectionsHandler;
import com.netflix.servo.monitor.BasicCounter;
import com.netflix.spectator.api.Registry;
import com.netflix.zuul.FilterLoader;
import com.netflix.zuul.FilterUsageNotifier;
import com.netflix.zuul.RequestCompleteHandler;
import com.netflix.zuul.context.SessionContextDecorator;
import com.netflix.zuul.filters.ZuulFilter;
import com.netflix.zuul.message.ZuulMessage;
import com.netflix.zuul.message.http.HttpRequestMessage;
import com.netflix.zuul.message.http.HttpResponseMessage;
import com.netflix.zuul.netty.insights.PassportLoggingHandler;
import com.netflix.zuul.netty.insights.PassportStateHttpServerHandler;
import com.netflix.zuul.netty.insights.PassportStateServerHandler;
import com.netflix.zuul.filters.passport.InboundPassportStampingFilter;
import com.netflix.zuul.filters.passport.OutboundPassportStampingFilter;
import com.netflix.zuul.netty.filter.FilterRunner;
import com.netflix.zuul.netty.filter.ZuulEndPointRunner;
import com.netflix.zuul.netty.filter.ZuulFilterChainHandler;
import com.netflix.zuul.netty.filter.ZuulFilterChainRunner;
import com.netflix.zuul.netty.server.ssl.SslHandshakeInfoHandler;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.netflix.zuul.passport.PassportState.FILTERS_INBOUND_END;
import static com.netflix.zuul.passport.PassportState.FILTERS_INBOUND_START;
import static com.netflix.zuul.passport.PassportState.FILTERS_OUTBOUND_END;
import static com.netflix.zuul.passport.PassportState.FILTERS_OUTBOUND_START;

/**
 * User: Mike Smith
 * Date: 3/5/16
 * Time: 6:26 PM
 */
public abstract class BaseZuulChannelInitializer extends ChannelInitializer
{
    public static final String HTTP_CODEC_HANDLER_NAME = "codec";

    protected static final LoggingHandler nettyLogger = new LoggingHandler("zuul.server.nettylog", LogLevel.INFO);

    public static final CachedDynamicIntProperty MAX_INITIAL_LINE_LENGTH = new CachedDynamicIntProperty("server.http.decoder.maxInitialLineLength", 16384);
    public static final CachedDynamicIntProperty MAX_HEADER_SIZE = new CachedDynamicIntProperty("server.http.decoder.maxHeaderSize", 32768);
    public static final CachedDynamicIntProperty MAX_CHUNK_SIZE = new CachedDynamicIntProperty("server.http.decoder.maxChunkSize", 32768);

    protected final int port;
    protected final ChannelConfig channelConfig;
    protected final ChannelConfig channelDependencies;
    protected final int idleTimeout;
    protected final int httpRequestReadTimeout;
    protected final int maxRequestsPerConnection;
    protected final int maxRequestsPerConnectionInBrownout;
    protected final int connectionExpiry;
    protected final int maxConnections;
    private final int connCloseDelay;
    
    protected final Registry registry;
    protected final ServerChannelMetrics channelMetrics;
    protected final HttpMetricsChannelHandler httpMetricsHandler;
    protected final PerEventLoopMetricsChannelHandler.Connections perEventLoopConnectionMetricsHandler;
    protected final PerEventLoopMetricsChannelHandler.HttpRequests perEventLoopRequestsMetricsHandler;
    protected final MaxInboundConnectionsHandler maxConnectionsHandler;
    protected final AccessLogPublisher accessLogPublisher;
    protected final PassportLoggingHandler passportLoggingHandler;
    protected final boolean withProxyProtocol;
    protected final StripUntrustedProxyHeadersHandler stripInboundProxyHeadersHandler;
    // TODO
    //protected final HttpRequestThrottleChannelHandler requestThrottleHandler;
    protected final ChannelHandler rateLimitingChannelHandler;
    protected final ChannelHandler sslClientCertCheckChannelHandler;
    //protected final RequestRejectedChannelHandler requestRejectedChannelHandler;
    protected final SessionContextDecorator sessionContextDecorator;
    protected final RequestCompleteHandler requestCompleteHandler;
    protected final BasicCounter httpRequestReadTimeoutCounter;
    protected final FilterLoader filterLoader;
    protected final FilterUsageNotifier filterUsageNotifier;
    protected final ServerStatusHeaderHandler serverStatusHeaderHandler;

    /** A collection of all the active channels that we can use to things like graceful shutdown */
    protected final ChannelGroup channels; 


    protected BaseZuulChannelInitializer(
            int port,
            ChannelConfig channelConfig,
            ChannelConfig channelDependencies,
            ChannelGroup channels)
    {
        this.port = port;
        this.channelConfig = channelConfig;
        this.channelDependencies = channelDependencies;
        this.channels = channels;

        this.accessLogPublisher = channelDependencies.get(ZuulDependencyKeys.accessLogPublisher);

        this.withProxyProtocol = channelConfig.get(CommonChannelConfigKeys.withProxyProtocol);

        this.idleTimeout = channelConfig.get(CommonChannelConfigKeys.idleTimeout);
        this.httpRequestReadTimeout = channelConfig.get(CommonChannelConfigKeys.httpRequestReadTimeout);
        this.channelMetrics = new ServerChannelMetrics("http-" + port);
        this.registry = channelDependencies.get(ZuulDependencyKeys.registry);
        this.httpMetricsHandler = new HttpMetricsChannelHandler(registry, "server", "http-" + port);

        EventLoopGroupMetrics eventLoopGroupMetrics = channelDependencies.get(ZuulDependencyKeys.eventLoopGroupMetrics);
        PerEventLoopMetricsChannelHandler perEventLoopMetricsHandler = new PerEventLoopMetricsChannelHandler(eventLoopGroupMetrics);
        this.perEventLoopConnectionMetricsHandler = perEventLoopMetricsHandler.new Connections();
        this.perEventLoopRequestsMetricsHandler = perEventLoopMetricsHandler.new HttpRequests();

        this.maxConnections = channelConfig.get(CommonChannelConfigKeys.maxConnections);
        this.maxConnectionsHandler = new MaxInboundConnectionsHandler(maxConnections);
        this.maxRequestsPerConnection = channelConfig.get(CommonChannelConfigKeys.maxRequestsPerConnection);
        this.maxRequestsPerConnectionInBrownout = channelConfig.get(CommonChannelConfigKeys.maxRequestsPerConnectionInBrownout);
        this.connectionExpiry = channelConfig.get(CommonChannelConfigKeys.connectionExpiry);
        this.connCloseDelay = channelConfig.get(CommonChannelConfigKeys.connCloseDelay);

        StripUntrustedProxyHeadersHandler.AllowWhen allowProxyHeadersWhen = channelConfig.get(CommonChannelConfigKeys.allowProxyHeadersWhen);
        this.stripInboundProxyHeadersHandler = new StripUntrustedProxyHeadersHandler(allowProxyHeadersWhen);

        this.rateLimitingChannelHandler = channelDependencies.get(ZuulDependencyKeys.rateLimitingChannelHandlerProvider).get();

        this.sslClientCertCheckChannelHandler = channelDependencies.get(ZuulDependencyKeys.sslClientCertCheckChannelHandlerProvider).get();

        this.passportLoggingHandler = new PassportLoggingHandler(registry);

        this.sessionContextDecorator = channelDependencies.get(ZuulDependencyKeys.sessionCtxDecorator);
        this.requestCompleteHandler = channelDependencies.get(ZuulDependencyKeys.requestCompleteHandler);
        this.httpRequestReadTimeoutCounter = channelDependencies.get(ZuulDependencyKeys.httpRequestReadTimeoutCounter);

        this.filterLoader = channelDependencies.get(ZuulDependencyKeys.filterLoader);
        this.filterUsageNotifier = channelDependencies.get(ZuulDependencyKeys.filterUsageNotifier);

        ServerStatusManager serverStatusManager = channelDependencies.get(ZuulDependencyKeys.serverStatusManager);
        this.serverStatusHeaderHandler = new ServerStatusHeaderHandler(serverStatusManager);
    }

    protected void storeChannel(Channel ch)
    {
        this.channels.add(ch);
    }

    protected void addPassportHandler(ChannelPipeline pipeline)
    {
        pipeline.addLast(new PassportStateServerHandler());
    }
    
    protected void addTcpRelatedHandlers(ChannelPipeline pipeline)
    {
        pipeline.addLast(new SourceAddressChannelHandler());
        pipeline.addLast("channelMetrics", channelMetrics);
        pipeline.addLast(perEventLoopConnectionMetricsHandler);

        new ElbProxyProtocolChannelHandler(withProxyProtocol).addProxyProtocol(pipeline);

        pipeline.addLast(maxConnectionsHandler);
    }

    protected void addHttp1Handlers(ChannelPipeline pipeline)
    {
        pipeline.addLast(HTTP_CODEC_HANDLER_NAME, createHttpServerCodec());

        pipeline.addLast(new Http1ConnectionCloseHandler(connCloseDelay));
        pipeline.addLast("conn_expiry_handler",
                new Http1ConnectionExpiryHandler(maxRequestsPerConnection, maxRequestsPerConnectionInBrownout, connectionExpiry));
    }

    protected HttpServerCodec createHttpServerCodec()
    {
        return new HttpServerCodec(
                MAX_INITIAL_LINE_LENGTH.get(),
                MAX_HEADER_SIZE.get(),
                MAX_CHUNK_SIZE.get(),
                false
        );
    }
    
    protected void addHttpRelatedHandlers(ChannelPipeline pipeline)
    {
        pipeline.addLast(new PassportStateHttpServerHandler());
        if (httpRequestReadTimeout > -1) {
            HttpRequestReadTimeoutHandler.addLast(pipeline, httpRequestReadTimeout, TimeUnit.MILLISECONDS, httpRequestReadTimeoutCounter);
        }
        pipeline.addLast(new HttpServerLifecycleChannelHandler());
        pipeline.addLast(new HttpBodySizeRecordingChannelHandler());
        pipeline.addLast(httpMetricsHandler);
        pipeline.addLast(perEventLoopRequestsMetricsHandler);

        if (accessLogPublisher != null) {
            pipeline.addLast(new AccessLogChannelHandler(accessLogPublisher));
        }

        pipeline.addLast(serverStatusHeaderHandler);
        //pipeline.addLast(requestThrottleHandler);
        pipeline.addLast(stripInboundProxyHeadersHandler);

        if (rateLimitingChannelHandler != null) {
            pipeline.addLast(rateLimitingChannelHandler);
        }

        //pipeline.addLast(requestRejectedChannelHandler);
    }

    protected void addTimeoutHandlers(ChannelPipeline pipeline) {
        pipeline.addLast(new IdleStateHandler(0, 0, idleTimeout, TimeUnit.MILLISECONDS));
        pipeline.addLast(new CloseOnIdleStateHandler());
    }

    protected void addSslInfoHandlers(ChannelPipeline pipeline, boolean isSSlFromIntermediary) {
        pipeline.addLast("ssl_info", new SslHandshakeInfoHandler(registry, isSSlFromIntermediary));
    }

    protected void addSslClientCertChecks(ChannelPipeline pipeline)
    {
        if (channelConfig.get(ZuulDependencyKeys.SSL_CLIENT_CERT_CHECK_REQUIRED)) {
            if (this.sslClientCertCheckChannelHandler == null) {
                throw new IllegalArgumentException("A sslClientCertCheckChannelHandler is required!");
            }
            pipeline.addLast(this.sslClientCertCheckChannelHandler);
        }
    }

    protected void addZuulHandlers(final ChannelPipeline pipeline)
    {
        pipeline.addLast("logger", nettyLogger);
        pipeline.addLast(new ClientRequestReceiver(sessionContextDecorator));
        pipeline.addLast(passportLoggingHandler);
        addZuulFilterChainHandler(pipeline);
        pipeline.addLast(new ClientResponseWriter(requestCompleteHandler, registry));
    }

    protected void addZuulFilterChainHandler(final ChannelPipeline pipeline) {
        final ZuulFilter[] responseFilters = getFilters(
                new OutboundPassportStampingFilter(FILTERS_OUTBOUND_START),
                new OutboundPassportStampingFilter(FILTERS_OUTBOUND_END));

        // response filter chain
        final ZuulFilterChainRunner responseFilterChain = getFilterChainRunner(responseFilters,
                filterUsageNotifier);

        // endpoint | response filter chain
        final FilterRunner endPoint = getEndpointRunner(responseFilterChain,
                filterUsageNotifier, filterLoader);

        final ZuulFilter[] requestFilters = getFilters(
                new InboundPassportStampingFilter(FILTERS_INBOUND_START),
                new InboundPassportStampingFilter(FILTERS_INBOUND_END));

        // request filter chain | end point | response filter chain
        final ZuulFilterChainRunner requestFilterChain = getFilterChainRunner(requestFilters,
                filterUsageNotifier, endPoint);

        pipeline.addLast(new ZuulFilterChainHandler(requestFilterChain, responseFilterChain));
    }

    protected ZuulEndPointRunner getEndpointRunner(ZuulFilterChainRunner responseFilterChain,
                                                   FilterUsageNotifier filterUsageNotifier, FilterLoader filterLoader) {
        return new ZuulEndPointRunner(filterUsageNotifier, filterLoader, responseFilterChain);
    }

    protected  ZuulFilterChainRunner getFilterChainRunner(ZuulFilter[] filters,
                                                                                    FilterUsageNotifier filterUsageNotifier) {
        return new ZuulFilterChainRunner<>(filters, filterUsageNotifier);
    }

    protected  ZuulFilterChainRunner getFilterChainRunner(ZuulFilter[] filters,
                                                                                    FilterUsageNotifier filterUsageNotifier,
                                                                                    FilterRunner filterRunner) {
        return new ZuulFilterChainRunner<>(filters, filterUsageNotifier, filterRunner);
    }

    public  ZuulFilter [] getFilters(final ZuulFilter start, final ZuulFilter stop) {
        final List zuulFilters = filterLoader.getFiltersByType(start.filterType());
        final ZuulFilter[] filters = new ZuulFilter[zuulFilters.size() + 2];
        filters[0] = start;
        for (int i=1, j=0; i < filters.length && j < zuulFilters.size(); i++,j++) {
            filters[i] = zuulFilters.get(j);
        }
        filters[filters.length -1] = stop;
        return filters;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy