/* * Copyright 2017 LinkedIn Corp. 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. */ package com.github.ambry.rest; import com.github.ambry.commons.SSLFactory; import com.github.ambry.config.NettyConfig; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.ssl.SslHandler; import io.netty.handler.stream.ChunkedWriteHandler; import io.netty.handler.timeout.IdleStateHandler; import java.net.InetSocketAddress; /** * A {@link ChannelInitializer} to be used with {@link NettyServer}. Calling {@link #initChannel(SocketChannel)} adds * the necessary handlers to a channel's pipeline so that it may handle requests. */ class NettyServerChannelInitializer extends ChannelInitializer<SocketChannel> { private final NettyConfig nettyConfig; private final NettyMetrics nettyMetrics; private final ConnectionStatsHandler connectionStatsHandler; private final RestRequestHandler requestHandler; private final PublicAccessLogger publicAccessLogger; private final RestServerState restServerState; private final SSLFactory sslFactory; /** * Construct a {@link NettyServerChannelInitializer}. * @param nettyConfig the config to use when instantiating certain handlers on this pipeline. * @param nettyMetrics the {@link NettyMetrics} object to use. * @param connectionStatsHandler the {@link ConnectionStatsHandler} to use. * @param requestHandler the {@link RestRequestHandler} to handle requests on this pipeline. * @param publicAccessLogger the {@link PublicAccessLogger} to use. * @param restServerState the {@link RestServerState} object to use. * @param sslFactory the {@link SSLFactory} to use for generating {@link javax.net.ssl.SSLEngine} instances, * or {@code null} if SSL is not enabled in this pipeline. */ public NettyServerChannelInitializer(NettyConfig nettyConfig, NettyMetrics nettyMetrics, ConnectionStatsHandler connectionStatsHandler, RestRequestHandler requestHandler, PublicAccessLogger publicAccessLogger, RestServerState restServerState, SSLFactory sslFactory) { this.nettyConfig = nettyConfig; this.nettyMetrics = nettyMetrics; this.connectionStatsHandler = connectionStatsHandler; this.requestHandler = requestHandler; this.publicAccessLogger = publicAccessLogger; this.restServerState = restServerState; this.sslFactory = sslFactory; } @Override protected void initChannel(SocketChannel ch) throws Exception { // If channel handler implementations are not annotated with @Sharable, Netty creates a new instance of every class // in the pipeline for every connection. // i.e. if there are a 1000 active connections there will be a 1000 NettyMessageProcessor instances. ChannelPipeline pipeline = ch.pipeline(); // connection stats handler to track connection related metrics pipeline.addLast("connectionStatsHandler", connectionStatsHandler); // if SSL is enabled, add an SslHandler before the HTTP codec if (sslFactory != null) { InetSocketAddress peerAddress = ch.remoteAddress(); pipeline.addLast("sslHandler", new SslHandler( sslFactory.createSSLEngine(peerAddress.getHostName(), peerAddress.getPort(), SSLFactory.Mode.SERVER))); } pipeline // for http encoding/decoding. .addLast("codec", new HttpServerCodec(nettyConfig.nettyServerMaxInitialLineLength, nettyConfig.nettyServerMaxHeaderSize, nettyConfig.nettyServerMaxChunkSize)) // for health check request handling .addLast("healthCheckHandler", new HealthCheckHandler(restServerState, nettyMetrics)) // for public access logging .addLast("publicAccessLogHandler", new PublicAccessLogHandler(publicAccessLogger, nettyMetrics)) // for detecting connections that have been idle too long - probably because of an error. .addLast("idleStateHandler", new IdleStateHandler(0, 0, nettyConfig.nettyServerIdleTimeSeconds)) // for safe writing of chunks for responses .addLast("chunker", new ChunkedWriteHandler()) // custom processing class that interfaces with a BlobStorageService. .addLast("processor", new NettyMessageProcessor(nettyMetrics, nettyConfig, requestHandler)); } }