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

org.neo4j.driver.internal.async.ChannelConnectorImpl Maven / Gradle / Ivy

There is a newer version: 5.27.0
Show newest version
/*
 * Copyright (c) 2002-2018 "Neo4j,"
 * Neo4j Sweden AB [http://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * 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 org.neo4j.driver.internal.async;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;

import java.util.Map;

import org.neo4j.driver.internal.BoltServerAddress;
import org.neo4j.driver.internal.ConnectionSettings;
import org.neo4j.driver.internal.async.inbound.ConnectTimeoutHandler;
import org.neo4j.driver.internal.security.InternalAuthToken;
import org.neo4j.driver.internal.security.SecurityPlan;
import org.neo4j.driver.internal.util.Clock;
import org.neo4j.driver.v1.AuthToken;
import org.neo4j.driver.v1.AuthTokens;
import org.neo4j.driver.v1.Logging;
import org.neo4j.driver.v1.Value;
import org.neo4j.driver.v1.exceptions.ClientException;

import static java.util.Objects.requireNonNull;

public class ChannelConnectorImpl implements ChannelConnector
{
    private final String userAgent;
    private final Map authToken;
    private final SecurityPlan securityPlan;
    private final ChannelPipelineBuilder pipelineBuilder;
    private final int connectTimeoutMillis;
    private final Logging logging;
    private final Clock clock;

    public ChannelConnectorImpl( ConnectionSettings connectionSettings, SecurityPlan securityPlan, Logging logging,
            Clock clock )
    {
        this( connectionSettings, securityPlan, new ChannelPipelineBuilderImpl(), logging, clock );
    }

    public ChannelConnectorImpl( ConnectionSettings connectionSettings, SecurityPlan securityPlan,
            ChannelPipelineBuilder pipelineBuilder, Logging logging, Clock clock )
    {
        this.userAgent = connectionSettings.userAgent();
        this.authToken = tokenAsMap( connectionSettings.authToken() );
        this.connectTimeoutMillis = connectionSettings.connectTimeoutMillis();
        this.securityPlan = requireNonNull( securityPlan );
        this.pipelineBuilder = pipelineBuilder;
        this.logging = requireNonNull( logging );
        this.clock = requireNonNull( clock );
    }

    @Override
    public ChannelFuture connect( BoltServerAddress address, Bootstrap bootstrap )
    {
        bootstrap.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeoutMillis );
        bootstrap.handler( new NettyChannelInitializer( address, securityPlan, connectTimeoutMillis, clock, logging ) );

        ChannelFuture channelConnected = bootstrap.connect( address.toSocketAddress() );

        Channel channel = channelConnected.channel();
        ChannelPromise handshakeCompleted = channel.newPromise();
        ChannelPromise connectionInitialized = channel.newPromise();

        installChannelConnectedListeners( address, channelConnected, handshakeCompleted );
        installHandshakeCompletedListeners( handshakeCompleted, connectionInitialized );

        return connectionInitialized;
    }

    private void installChannelConnectedListeners( BoltServerAddress address, ChannelFuture channelConnected,
            ChannelPromise handshakeCompleted )
    {
        ChannelPipeline pipeline = channelConnected.channel().pipeline();

        // add timeout handler to the pipeline when channel is connected. it's needed to limit amount of time code
        // spends in TLS and Bolt handshakes. prevents infinite waiting when database does not respond
        channelConnected.addListener( future ->
                pipeline.addFirst( new ConnectTimeoutHandler( connectTimeoutMillis ) ) );

        // add listener that sends Bolt handshake bytes when channel is connected
        channelConnected.addListener(
                new ChannelConnectedListener( address, pipelineBuilder, handshakeCompleted, logging ) );
    }

    private void installHandshakeCompletedListeners( ChannelPromise handshakeCompleted,
            ChannelPromise connectionInitialized )
    {
        ChannelPipeline pipeline = handshakeCompleted.channel().pipeline();

        // remove timeout handler from the pipeline once TLS and Bolt handshakes are completed. regular protocol
        // messages will flow next and we do not want to have read timeout for them
        handshakeCompleted.addListener( future -> pipeline.remove( ConnectTimeoutHandler.class ) );

        // add listener that sends an INIT message. connection is now fully established. channel pipeline if fully
        // set to send/receive messages for a selected protocol version
        handshakeCompleted.addListener( new HandshakeCompletedListener( userAgent, authToken, connectionInitialized ) );
    }

    private static Map tokenAsMap( AuthToken token )
    {
        if ( token instanceof InternalAuthToken )
        {
            return ((InternalAuthToken) token).toMap();
        }
        else
        {
            throw new ClientException(
                    "Unknown authentication token, `" + token + "`. Please use one of the supported " +
                    "tokens from `" + AuthTokens.class.getSimpleName() + "`." );
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy