org.apache.cassandra.transport.PipelineConfigurator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cassandra-all Show documentation
Show all versions of cassandra-all Show documentation
The Apache Cassandra Project develops a highly scalable second-generation distributed database, bringing together Dynamo's fully distributed design and Bigtable's ColumnFamily-based data model.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.cassandra.transport;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.google.common.base.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.Version;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.EncryptionOptions;
import org.apache.cassandra.net.*;
import org.apache.cassandra.security.ISslContextFactory;
import org.apache.cassandra.security.SSLFactory;
import org.apache.cassandra.transport.messages.StartupMessage;
import static org.apache.cassandra.config.CassandraRelevantProperties.TEST_UNSAFE_VERBOSE_DEBUG_CLIENT_PROTOCOL;
import static org.apache.cassandra.net.SocketFactory.newSslHandler;
/**
* Takes care of intializing a Netty Channel and Pipeline for client protocol connections.
* The pipeline is first set up with some common handlers for connection limiting, dropping
* idle connections and optionally SSL, along with a handler to deal with the handshake
* between client and server. That handshake handler calls back to this class to reconfigure
* the pipeline once the protocol version for the connection has been established.
*/
public class PipelineConfigurator
{
private static final Logger logger = LoggerFactory.getLogger(PipelineConfigurator.class);
// Not to be used in production, this causes a Netty logging handler to be added to the pipeline,
// which will throttle a system under any normal load.
private static final boolean DEBUG = TEST_UNSAFE_VERBOSE_DEBUG_CLIENT_PROTOCOL.getBoolean();
// Stateless handlers
private static final ConnectionLimitHandler connectionLimitHandler = new ConnectionLimitHandler();
// Names of handlers used regardless of protocol version
private static final String CONNECTION_LIMIT_HANDLER = "connectionLimitHandler";
private static final String IDLE_STATE_HANDLER = "idleStateHandler";
private static final String INITIAL_HANDLER = "initialHandler";
private static final String EXCEPTION_HANDLER = "exceptionHandler";
private static final String DEBUG_HANDLER = "debugHandler";
private static final String SSL_HANDLER = "ssl";
// Names of handlers used in pre-V5 pipelines only
private static final String ENVELOPE_DECODER = "envelopeDecoder";
private static final String ENVELOPE_ENCODER = "envelopeEncoder";
private static final String MESSAGE_DECOMPRESSOR = "decompressor";
private static final String MESSAGE_COMPRESSOR = "compressor";
private static final String MESSAGE_DECODER = "messageDecoder";
private static final String MESSAGE_ENCODER = "messageEncoder";
private static final String LEGACY_MESSAGE_PROCESSOR = "legacyCqlProcessor";
// Names of handlers used in V5 and later pipelines
private static final String FRAME_DECODER = "frameDecoder";
private static final String FRAME_ENCODER = "frameEncoder";
private static final String MESSAGE_PROCESSOR = "cqlProcessor";
private final boolean epoll;
private final boolean keepAlive;
private final EncryptionOptions.TlsEncryptionPolicy tlsEncryptionPolicy;
private final Dispatcher dispatcher;
public PipelineConfigurator(boolean epoll,
boolean keepAlive,
boolean legacyFlusher,
EncryptionOptions.TlsEncryptionPolicy encryptionPolicy)
{
this.epoll = epoll;
this.keepAlive = keepAlive;
this.tlsEncryptionPolicy = encryptionPolicy;
this.dispatcher = dispatcher(legacyFlusher);
}
public ChannelFuture initializeChannel(final EventLoopGroup workerGroup,
final InetSocketAddress socket,
final Connection.Factory connectionFactory)
{
ServerBootstrap bootstrap = new ServerBootstrap()
.channel(epoll ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_LINGER, 0)
.childOption(ChannelOption.SO_KEEPALIVE, keepAlive)
.childOption(ChannelOption.ALLOCATOR, CBUtil.allocator)
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(8 * 1024, 32 * 1024));
if (workerGroup != null)
bootstrap = bootstrap.group(workerGroup);
ChannelInitializer initializer = initializer(connectionFactory);
bootstrap.childHandler(initializer);
// Bind and start to accept incoming connections.
logger.info("Using Netty Version: {}", Version.identify().entrySet());
logger.info("Starting listening for CQL clients on {} ({})...", socket, tlsEncryptionPolicy.description());
return bootstrap.bind(socket);
}
protected ChannelInitializer initializer(Connection.Factory connectionFactory)
{
// the initializer will perform the common initial setup
// then any additional steps mandated by the encryption options
final EncryptionConfig encryptionConfig = encryptionConfig();
return new ChannelInitializer()
{
protected void initChannel(Channel channel) throws Exception
{
configureInitialPipeline(channel, connectionFactory);
encryptionConfig.applyTo(channel);
}
};
}
// Essentially just a Consumer which may throw
interface EncryptionConfig
{
void applyTo(Channel channel) throws Exception;
}
protected EncryptionConfig encryptionConfig()
{
final EncryptionOptions encryptionOptions = DatabaseDescriptor.getNativeProtocolEncryptionOptions();
switch (tlsEncryptionPolicy)
{
case UNENCRYPTED:
// if encryption is not enabled, no further steps are required after the initial setup
return channel -> {};
case OPTIONAL:
// If optional, install a handler which detects whether or not the client is sending
// encrypted bytes. If so, on receipt of the next bytes, replace that handler with
// an SSL Handler, otherwise just remove it and proceed with an unencrypted channel.
logger.debug("Enabling optionally encrypted CQL connections between client and server");
return channel -> {
SslContext sslContext = SSLFactory.getOrCreateSslContext(encryptionOptions,
encryptionOptions.require_client_auth,
ISslContextFactory.SocketType.SERVER);
channel.pipeline().addFirst(SSL_HANDLER, new ByteToMessageDecoder()
{
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy