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.google.common.base.Preconditions;
import com.netflix.config.CachedDynamicIntProperty;
import com.netflix.netty.common.CloseOnIdleStateHandler;
import com.netflix.netty.common.Http1ConnectionCloseHandler;
import com.netflix.netty.common.Http1ConnectionExpiryHandler;
import com.netflix.netty.common.HttpRequestReadTimeoutHandler;
import com.netflix.netty.common.HttpServerLifecycleChannelHandler;
import com.netflix.netty.common.SourceAddressChannelHandler;
import com.netflix.netty.common.SslExceptionsHandler;
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.proxyprotocol.ElbProxyProtocolChannelHandler;
import com.netflix.netty.common.proxyprotocol.StripUntrustedProxyHeadersHandler;
import com.netflix.netty.common.throttle.MaxInboundConnectionsHandler;
import com.netflix.spectator.api.Counter;
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.filters.passport.InboundPassportStampingFilter;
import com.netflix.zuul.filters.passport.OutboundPassportStampingFilter;
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.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.insights.PassportLoggingHandler;
import com.netflix.zuul.netty.insights.PassportStateHttpServerHandler;
import com.netflix.zuul.netty.insights.ServerStateHandler;
import com.netflix.zuul.netty.server.ssl.SslHandshakeInfoHandler;
import com.netflix.zuul.passport.PassportState;
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 io.netty.util.AttributeKey;
import java.util.SortedSet;
import java.util.concurrent.TimeUnit;

/**
 * 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";
    public static final AttributeKey ATTR_CHANNEL_CONFIG = AttributeKey.newInstance("channel_config");

    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);

    /**
     * The port that the server intends to listen on.  Subclasses should NOT use this field, as it may not be set, and
     * may differ from the actual listening port.  For example:
     *
     * 
    *
  • When binding the server to port `0`, the actual port will be different from the one provided here. *
  • If there is no port (such as in a LocalSocket, or DomainSocket), the port number may be `-1`. *
* *

Instead, subclasses should read the local address on channel initialization, and decide to take action then. */ @Deprecated protected final int port; protected final String metricId; 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 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 Counter httpRequestReadTimeoutCounter; protected final FilterLoader filterLoader; protected final FilterUsageNotifier filterUsageNotifier; protected final SourceAddressChannelHandler sourceAddressChannelHandler; /** A collection of all the active channels that we can use to things like graceful shutdown */ protected final ChannelGroup channels; /** * After calling this method, child classes should not reference {@link #port} any more. */ protected BaseZuulChannelInitializer( String metricId, ChannelConfig channelConfig, ChannelConfig channelDependencies, ChannelGroup channels) { this(-1, metricId, channelConfig, channelDependencies, channels); } /** * Call {@link #BaseZuulChannelInitializer(String, ChannelConfig, ChannelConfig, ChannelGroup)} instead. */ @Deprecated protected BaseZuulChannelInitializer( int port, ChannelConfig channelConfig, ChannelConfig channelDependencies, ChannelGroup channels) { this(port, String.valueOf(port), channelConfig, channelDependencies, channels); } private BaseZuulChannelInitializer( int port, String metricId, ChannelConfig channelConfig, ChannelConfig channelDependencies, ChannelGroup channels) { this.port = port; Preconditions.checkNotNull(metricId, "metricId"); this.metricId = metricId; 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.registry = channelDependencies.get(ZuulDependencyKeys.registry); this.httpMetricsHandler = new HttpMetricsChannelHandler(registry, "server", "http-" + metricId); 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(registry, metricId, 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); this.sourceAddressChannelHandler = new SourceAddressChannelHandler(); } protected void storeChannel(Channel ch) { this.channels.add(ch); // Also add the ChannelConfig as an attribute on each channel. So interested filters/channel-handlers can // introspect // and potentially act differently based on the config. ch.attr(ATTR_CHANNEL_CONFIG).set(channelConfig); } protected void addPassportHandler(ChannelPipeline pipeline) { pipeline.addLast(new ServerStateHandler.InboundHandler(registry, "http-" + metricId)); pipeline.addLast(new ServerStateHandler.OutboundHandler(registry)); } protected void addTcpRelatedHandlers(ChannelPipeline pipeline) { pipeline.addLast(sourceAddressChannelHandler); pipeline.addLast(perEventLoopConnectionMetricsHandler); new ElbProxyProtocolChannelHandler(registry, withProxyProtocol).addProxyProtocol(pipeline); pipeline.addLast(maxConnectionsHandler); } protected void addHttp1Handlers(ChannelPipeline pipeline) { pipeline.addLast(HTTP_CODEC_HANDLER_NAME, createHttpServerCodec()); pipeline.addLast(new Http1ConnectionCloseHandler()); 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.InboundHandler()); pipeline.addLast(new PassportStateHttpServerHandler.OutboundHandler()); if (httpRequestReadTimeout > -1) { HttpRequestReadTimeoutHandler.addLast( pipeline, httpRequestReadTimeout, TimeUnit.MILLISECONDS, httpRequestReadTimeoutCounter); } pipeline.addLast(new HttpServerLifecycleChannelHandler.HttpServerLifecycleInboundChannelHandler()); pipeline.addLast(new HttpServerLifecycleChannelHandler.HttpServerLifecycleOutboundChannelHandler()); pipeline.addLast(new HttpBodySizeRecordingChannelHandler.InboundChannelHandler()); pipeline.addLast(new HttpBodySizeRecordingChannelHandler.OutboundChannelHandler()); pipeline.addLast(httpMetricsHandler); pipeline.addLast(perEventLoopRequestsMetricsHandler); if (accessLogPublisher != null) { pipeline.addLast(new AccessLogChannelHandler.AccessLogInboundChannelHandler(accessLogPublisher)); pipeline.addLast(new AccessLogChannelHandler.AccessLogOutboundChannelHandler()); } 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(registry, metricId)); } protected void addSslInfoHandlers(ChannelPipeline pipeline, boolean isSSlFromIntermediary) { pipeline.addLast("ssl_info", new SslHandshakeInfoHandler(registry, isSSlFromIntermediary)); pipeline.addLast("ssl_exceptions", new SslExceptionsHandler(registry)); } 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(PassportState.FILTERS_OUTBOUND_START), new OutboundPassportStampingFilter(PassportState.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(PassportState.FILTERS_INBOUND_START), new InboundPassportStampingFilter(PassportState.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, registry); } protected ZuulFilterChainRunner getFilterChainRunner( ZuulFilter[] filters, FilterUsageNotifier filterUsageNotifier) { return new ZuulFilterChainRunner<>(filters, filterUsageNotifier, registry); } protected ZuulFilterChainRunner getFilterChainRunner( ZuulFilter[] filters, FilterUsageNotifier filterUsageNotifier, FilterRunner filterRunner) { return new ZuulFilterChainRunner<>(filters, filterUsageNotifier, filterRunner, registry); } @SuppressWarnings("unchecked") // For the conversion from getFiltersByType. It's not safe, sorry. public ZuulFilter[] getFilters(ZuulFilter start, ZuulFilter stop) { final SortedSet> zuulFilters = filterLoader.getFiltersByType(start.filterType()); final ZuulFilter[] filters = new ZuulFilter[zuulFilters.size() + 2]; filters[0] = start; int i = 1; for (ZuulFilter filter : zuulFilters) { // TODO(carl-mastrangelo): find some way to make this cast not needed. filters[i++] = (ZuulFilter) filter; } filters[filters.length - 1] = stop; return filters; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy