Skip to content

Commit

Permalink
Add support to 4.2 by implementing the extended handshake
Browse files Browse the repository at this point in the history
The Bolt handshake is a short exchange that occurs after the opening of every Bolt connection. It is used to establish a protocol version for subsequent messaging activity. The sequence is currently limited to four fixed protocol versions; these changes implement a mechanism to extend that to protocol version ranges.

In Bolt v1, the four bytes were treated as a single big-endian unsigned 32-bit integer, with the protocol versioned only via a single number. In Bolt 4.0, the scheme evolved such that bytes D and C represented the major and minor version numbers respectively; bytes A and B were ignored, reserved for future use.

The scheme is to be extended such that byte B can be used to allow the sequence to encode an inclusive range of versions in the client-to-server request. The server-to-client response would remain the same, specifying only a single version.

Specifically, the range minimum and maximum can be computed as follows:

Range maximum = D.C
Range minimum = D.(C - B)

This denotes byte B as a minor version difference, backwards from the minor version in byte C. In other words, zero denotes "zero versions back", one denotes "one version back", and so on. Note that this specifically requires the range to exist within the same major version, but does provide backwards compatibility with existing implementations, which fill bytes A and B with zeroes.

Note also that a range with an identical min and max should be considered semantically equivalent to a single version.
  • Loading branch information
bigmontz committed Dec 9, 2020
1 parent a51a145 commit c987d9a
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ public final class BoltProtocolUtil

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

private static final String HANDSHAKE_STRING = createHandshakeString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public BoltProtocolVersion( int majorVersion, int minorVersion )
public static BoltProtocolVersion fromRawBytes( int rawVersion )
{
int major = rawVersion & 0x000000FF;
int minor = ( rawVersion >> 8 ) & 0x000000FF;
int minor = (rawVersion >> 8) & 0x000000FF;

return new BoltProtocolVersion( major, minor );
}
Expand All @@ -55,6 +55,21 @@ public int toInt()
return shiftedMinor | majorVersion;
}

public int toIntRange( BoltProtocolVersion minVersion )
{
if ( majorVersion != minVersion.majorVersion )
{
throw new IllegalArgumentException( "Versions should be from the same major version" );
}
else if ( minorVersion < minVersion.minorVersion )
{
throw new IllegalArgumentException( "Max version should be newer than min version" );
}
int range = minorVersion - minVersion.minorVersion;
int shiftedRange = range << 16;
return shiftedRange | toInt();
}

/**
* @return the version in format X.Y where X is the major version and Y is the minor version
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
import org.junit.jupiter.api.Test;

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.messaging.v42.BoltProtocolV42;
import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43;

import static org.junit.jupiter.api.Assertions.assertEquals;
Expand All @@ -43,15 +43,15 @@ void shouldReturnHandshakeBuf()
{
assertByteBufContains(
handshakeBuf(),
BOLT_MAGIC_PREAMBLE, BoltProtocolV43.VERSION.toInt(), BoltProtocolV42.VERSION.toInt(),
BoltProtocolV41.VERSION.toInt(), BoltProtocolV3.VERSION.toInt()
BOLT_MAGIC_PREAMBLE, (1 << 16) | BoltProtocolV43.VERSION.toInt(), BoltProtocolV41.VERSION.toInt(),
BoltProtocolV4.VERSION.toInt(), BoltProtocolV3.VERSION.toInt()
);
}

@Test
void shouldReturnHandshakeString()
{
assertEquals( "[0x6060b017, 772, 516, 260, 3]", handshakeString() );
assertEquals( "[0x6060b017, 66308, 260, 4, 3]", handshakeString() );
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.junit.jupiter.params.provider.CsvSource;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

class BoltProtocolVersionTest
{
Expand Down Expand Up @@ -50,6 +51,40 @@ void shouldCompareTo(int majorA, int minorA, int majorB, int minorB, int expecte

}

@ParameterizedTest( name = "V{0}.{1} toIntRange V{2}.{3}")
@CsvSource({
"1, 0, 1, 0, 0x000001",
"4, 3, 4, 2, 0x010304",
"4, 3, 4, 1, 0x020304",
"4, 3, 4, 0, 0x030304",
"100, 100, 100, 0, 0x646464",
"255, 255, 255, 0, 0xFFFFFF"
} )
void shouldOutputCorrectIntRange(int majorA, int minorA, int majorB, int minorB, int expectedResult)
{
BoltProtocolVersion versionA = new BoltProtocolVersion( majorA, minorA );
BoltProtocolVersion versionB = new BoltProtocolVersion( majorB, minorB );

assertEquals( expectedResult, versionA.toIntRange( versionB ) );
}

@ParameterizedTest( name = "V{0}.{1} toIntRange V{2}.{3}")
@CsvSource({
"1, 0, 2, 0",
"2, 0, 1, 0",
"4, 3, 4, 5",
"4, 6, 3, 7",
"3, 7, 4, 6",
"255, 255, 100, 0"
} )
void shouldThrowsIllegalArgumentExceptionForIncorrectIntRange(int majorA, int minorA, int majorB, int minorB)
{
BoltProtocolVersion versionA = new BoltProtocolVersion( majorA, minorA );
BoltProtocolVersion versionB = new BoltProtocolVersion( majorB, minorB );

assertThrows( IllegalArgumentException.class, () -> versionA.toIntRange( versionB ));
}

@Test
void shouldOutputCorrectLongFormatForMajorVersionOnly()
{
Expand Down

0 comments on commit c987d9a

Please sign in to comment.