Skip to content

Foundational Changes for Driver and Bolt 4.1 #698

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion driver/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<parent>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver-parent</artifactId>
<version>4.0-SNAPSHOT</version>
<version>4.1-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,32 @@

import io.netty.buffer.ByteBuf;

import org.neo4j.driver.internal.messaging.BoltProtocolVersion;
import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1;
import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2;
import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3;
import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4;
import org.neo4j.driver.internal.messaging.v41.BoltProtocolV41;

import static io.netty.buffer.Unpooled.copyInt;
import static io.netty.buffer.Unpooled.unreleasableBuffer;
import static java.lang.Integer.toHexString;

public final class BoltProtocolUtil
{
public static final int HTTP = 1213486160; //== 0x48545450 == "HTTP"

public static final int BOLT_MAGIC_PREAMBLE = 0x6060B017;
public static final int NO_PROTOCOL_VERSION = 0;
public static final BoltProtocolVersion NO_PROTOCOL_VERSION = new BoltProtocolVersion( 0 , 0 );

public static final int CHUNK_HEADER_SIZE_BYTES = 2;

public static final int DEFAULT_MAX_OUTBOUND_CHUNK_SIZE_BYTES = Short.MAX_VALUE / 2;

private static final ByteBuf HANDSHAKE_BUF = unreleasableBuffer( copyInt(
BOLT_MAGIC_PREAMBLE,
BoltProtocolV4.VERSION,
BoltProtocolV3.VERSION,
BoltProtocolV2.VERSION,
BoltProtocolV1.VERSION ) ).asReadOnly();
BoltProtocolV41.VERSION.toInt(),
BoltProtocolV4.VERSION.toInt(),
BoltProtocolV3.VERSION.toInt(),
BoltProtocolV2.VERSION.toInt() ) ).asReadOnly();

private static final String HANDSHAKE_STRING = createHandshakeString();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import org.neo4j.driver.internal.BoltServerAddress;
import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher;
import org.neo4j.driver.internal.messaging.BoltProtocolVersion;
import org.neo4j.driver.internal.util.ServerVersion;

import static io.netty.util.AttributeKey.newInstance;
Expand All @@ -31,7 +32,7 @@ public final class ChannelAttributes
{
private static final AttributeKey<String> CONNECTION_ID = newInstance( "connectionId" );
private static final AttributeKey<String> POOL_ID = newInstance( "poolId" );
private static final AttributeKey<Integer> PROTOCOL_VERSION = newInstance( "protocolVersion" );
private static final AttributeKey<BoltProtocolVersion> PROTOCOL_VERSION = newInstance( "protocolVersion" );
private static final AttributeKey<BoltServerAddress> ADDRESS = newInstance( "serverAddress" );
private static final AttributeKey<ServerVersion> SERVER_VERSION = newInstance( "serverVersion" );
private static final AttributeKey<Long> CREATION_TIMESTAMP = newInstance( "creationTimestamp" );
Expand Down Expand Up @@ -63,12 +64,12 @@ public static void setPoolId( Channel channel, String id )
setOnce( channel, POOL_ID, id );
}

public static int protocolVersion( Channel channel )
public static BoltProtocolVersion protocolVersion( Channel channel )
{
return get( channel, PROTOCOL_VERSION );
}

public static void setProtocolVersion( Channel channel, int version )
public static void setProtocolVersion( Channel channel, BoltProtocolVersion version )
{
setOnce( channel, PROTOCOL_VERSION, version );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import org.neo4j.driver.internal.logging.ChannelActivityLogger;
import org.neo4j.driver.internal.messaging.BoltProtocol;
import org.neo4j.driver.internal.messaging.BoltProtocolVersion;
import org.neo4j.driver.internal.messaging.MessageFormat;
import org.neo4j.driver.internal.util.ErrorUtil;
import org.neo4j.driver.Logger;
Expand All @@ -37,8 +38,8 @@
import org.neo4j.driver.exceptions.SecurityException;
import org.neo4j.driver.exceptions.ServiceUnavailableException;

import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.HTTP;
import static org.neo4j.driver.internal.async.connection.BoltProtocolUtil.NO_PROTOCOL_VERSION;
import static org.neo4j.driver.internal.messaging.BoltProtocolVersion.isHttp;

public class HandshakeHandler extends ReplayingDecoder<Void>
{
Expand Down Expand Up @@ -101,8 +102,8 @@ public void exceptionCaught( ChannelHandlerContext ctx, Throwable error )
@Override
protected void decode( ChannelHandlerContext ctx, ByteBuf in, List<Object> out )
{
int serverSuggestedVersion = in.readInt();
log.debug( "S: [Bolt Handshake] %d", serverSuggestedVersion );
BoltProtocolVersion serverSuggestedVersion = BoltProtocolVersion.fromRawBytes( in.readInt() );
log.debug( "S: [Bolt Handshake] %s", serverSuggestedVersion );

// this is a one-time handler, remove it when protocol version has been read
ctx.pipeline().remove( this );
Expand All @@ -118,7 +119,7 @@ protected void decode( ChannelHandlerContext ctx, ByteBuf in, List<Object> out )
}
}

private BoltProtocol protocolForVersion( int version )
private BoltProtocol protocolForVersion( BoltProtocolVersion version )
{
try
{
Expand All @@ -130,26 +131,26 @@ private BoltProtocol protocolForVersion( int version )
}
}

private void protocolSelected( int version, MessageFormat messageFormat, ChannelHandlerContext ctx )
private void protocolSelected( BoltProtocolVersion version, MessageFormat messageFormat, ChannelHandlerContext ctx )
{
ChannelAttributes.setProtocolVersion( ctx.channel(), version );
pipelineBuilder.build( messageFormat, ctx.pipeline(), logging );
handshakeCompletedPromise.setSuccess();
}

private void handleUnknownSuggestedProtocolVersion( int version, ChannelHandlerContext ctx )
private void handleUnknownSuggestedProtocolVersion( BoltProtocolVersion version, ChannelHandlerContext ctx )
{
switch ( version )
if ( NO_PROTOCOL_VERSION.equals( version ) )
{
case NO_PROTOCOL_VERSION:
fail( ctx, protocolNoSupportedByServerError() );
break;
case HTTP:
}
else if ( isHttp( version ) )
{
fail( ctx, httpEndpointError() );
break;
default:
}
else
{
fail( ctx, protocolNoSupportedByDriverError( version ) );
break;
}
}

Expand All @@ -172,7 +173,7 @@ private static Throwable httpEndpointError()
"(HTTP defaults to port 7474 whereas BOLT defaults to port 7687)" );
}

private static Throwable protocolNoSupportedByDriverError( int suggestedProtocolVersion )
private static Throwable protocolNoSupportedByDriverError( BoltProtocolVersion suggestedProtocolVersion )
{
return new ClientException(
"Protocol error, server suggested unexpected protocol version: " + suggestedProtocolVersion );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2;
import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3;
import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4;
import org.neo4j.driver.internal.messaging.v41.BoltProtocolV41;
import org.neo4j.driver.internal.spi.Connection;

import static org.neo4j.driver.internal.async.connection.ChannelAttributes.protocolVersion;
Expand Down Expand Up @@ -128,7 +129,7 @@ ResultCursorFactory runInUnmanagedTransaction(Connection connection, Query query
* Returns the protocol version. It can be used for version specific error messages.
* @return the protocol version.
*/
int version();
BoltProtocolVersion version();

/**
* Obtain an instance of the protocol for the given channel.
Expand All @@ -149,20 +150,27 @@ static BoltProtocol forChannel( Channel channel )
* @return the protocol.
* @throws ClientException when unable to find protocol with the given version.
*/
static BoltProtocol forVersion( int version )
static BoltProtocol forVersion( BoltProtocolVersion version )
{
switch ( version )
if ( BoltProtocolV1.VERSION.equals( version ) )
{
case BoltProtocolV1.VERSION:
return BoltProtocolV1.INSTANCE;
case BoltProtocolV2.VERSION:
}
else if ( BoltProtocolV2.VERSION.equals( version ) )
{
return BoltProtocolV2.INSTANCE;
case BoltProtocolV3.VERSION:
}
else if ( BoltProtocolV3.VERSION.equals( version ) )
{
return BoltProtocolV3.INSTANCE;
case BoltProtocolV4.VERSION:
}
else if ( BoltProtocolV4.VERSION.equals( version ) )
{
return BoltProtocolV4.INSTANCE;
default:
throw new ClientException( "Unknown protocol version: " + version );
} else if ( BoltProtocolV41.VERSION.equals( version ) )
{
return BoltProtocolV41.INSTANCE;
}
throw new ClientException( "Unknown protocol version: " + version );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright (c) 2002-2020 "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.messaging;

import java.util.Objects;

public class BoltProtocolVersion implements Comparable<BoltProtocolVersion>
{
private final int majorVersion;
private final int minorVersion;

public BoltProtocolVersion( int majorVersion, int minorVersion )
{
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
}

public static BoltProtocolVersion fromRawBytes( int rawVersion )
{
int major = rawVersion & 0x000000FF;
int minor = ( rawVersion >> 8 ) & 0x000000FF;

return new BoltProtocolVersion( major, minor );
}

public long getMinorVersion()
{
return minorVersion;
}

public long getMajorVersion()
{
return majorVersion;
}

public int toInt()
{
int shiftedMinor = minorVersion << 8;
return shiftedMinor | majorVersion;
}

/**
* @return the version in format X.Y where X is the major version and Y is the minor version
*/
@Override
public String toString()
{
return String.format( "%d.%d", majorVersion, minorVersion );
}

@Override
public int hashCode()
{
return Objects.hash( minorVersion, majorVersion );
}

@Override
public boolean equals( Object o )
{
if ( o == this )
{
return true;
}
else if ( !(o instanceof BoltProtocolVersion) )
{
return false;
}
else
{
BoltProtocolVersion other = (BoltProtocolVersion) o;
return this.getMajorVersion() == other.getMajorVersion() && this.getMinorVersion() == other.getMinorVersion();
}
}

@Override
public int compareTo( BoltProtocolVersion other )
{
int result = Integer.compare( majorVersion, other.majorVersion );

if ( result == 0 )
{
return Integer.compare( minorVersion, other.minorVersion );
}

return result;
}

public static boolean isHttp( BoltProtocolVersion protocolVersion )
{
// server would respond with `HTTP..` We read 4 bytes to figure out the version. The first two are not used
// and therefore parse the `P` (80) for major and `T` (84) for minor.
return protocolVersion.getMajorVersion() == 80 && protocolVersion.getMinorVersion() == 84;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@

import org.neo4j.driver.exceptions.ClientException;
import org.neo4j.driver.internal.DatabaseName;
import org.neo4j.driver.internal.messaging.BoltProtocolVersion;
import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4;
import org.neo4j.driver.internal.spi.Connection;
import org.neo4j.driver.internal.util.ServerVersion;

public final class MultiDatabaseUtil
{
public static void assertEmptyDatabaseName( DatabaseName databaseName, int boltVersion )
public static void assertEmptyDatabaseName( DatabaseName databaseName, BoltProtocolVersion boltVersion )
{
if ( databaseName.databaseName().isPresent() )
{
Expand All @@ -37,6 +38,7 @@ public static void assertEmptyDatabaseName( DatabaseName databaseName, int boltV

public static boolean supportsMultiDatabase( Connection connection )
{
return connection.serverVersion().greaterThanOrEqual( ServerVersion.v4_0_0 ) && connection.protocol().version() >= BoltProtocolV4.VERSION;
return connection.serverVersion().greaterThanOrEqual( ServerVersion.v4_0_0 ) &&
connection.protocol().version().compareTo( BoltProtocolV4.VERSION ) >= 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.neo4j.driver.internal.handlers.RollbackTxResponseHandler;
import org.neo4j.driver.internal.handlers.RunResponseHandler;
import org.neo4j.driver.internal.messaging.BoltProtocol;
import org.neo4j.driver.internal.messaging.BoltProtocolVersion;
import org.neo4j.driver.internal.messaging.Message;
import org.neo4j.driver.internal.messaging.MessageFormat;
import org.neo4j.driver.internal.messaging.request.InitMessage;
Expand All @@ -65,7 +66,7 @@

public class BoltProtocolV1 implements BoltProtocol
{
public static final int VERSION = 1;
public static final BoltProtocolVersion VERSION = new BoltProtocolVersion( 1, 0 );

public static final BoltProtocol INSTANCE = new BoltProtocolV1();

Expand Down Expand Up @@ -176,7 +177,7 @@ public ResultCursorFactory runInUnmanagedTransaction(Connection connection, Quer
}

@Override
public int version()
public BoltProtocolVersion version()
{
return VERSION;
}
Expand Down
Loading