Skip to content

Commit c987d9a

Browse files
authored
Add support to 4.2 by implementing the extended handshake
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.
1 parent a51a145 commit c987d9a

File tree

4 files changed

+57
-7
lines changed

4 files changed

+57
-7
lines changed

driver/src/main/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtil.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ public final class BoltProtocolUtil
4242

4343
private static final ByteBuf HANDSHAKE_BUF = unreleasableBuffer( copyInt(
4444
BOLT_MAGIC_PREAMBLE,
45-
BoltProtocolV43.VERSION.toInt(),
46-
BoltProtocolV42.VERSION.toInt(),
45+
BoltProtocolV43.VERSION.toIntRange( BoltProtocolV42.VERSION ),
4746
BoltProtocolV41.VERSION.toInt(),
47+
BoltProtocolV4.VERSION.toInt(),
4848
BoltProtocolV3.VERSION.toInt() ) ).asReadOnly();
4949

5050
private static final String HANDSHAKE_STRING = createHandshakeString();

driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocolVersion.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public BoltProtocolVersion( int majorVersion, int minorVersion )
3434
public static BoltProtocolVersion fromRawBytes( int rawVersion )
3535
{
3636
int major = rawVersion & 0x000000FF;
37-
int minor = ( rawVersion >> 8 ) & 0x000000FF;
37+
int minor = (rawVersion >> 8) & 0x000000FF;
3838

3939
return new BoltProtocolVersion( major, minor );
4040
}
@@ -55,6 +55,21 @@ public int toInt()
5555
return shiftedMinor | majorVersion;
5656
}
5757

58+
public int toIntRange( BoltProtocolVersion minVersion )
59+
{
60+
if ( majorVersion != minVersion.majorVersion )
61+
{
62+
throw new IllegalArgumentException( "Versions should be from the same major version" );
63+
}
64+
else if ( minorVersion < minVersion.minorVersion )
65+
{
66+
throw new IllegalArgumentException( "Max version should be newer than min version" );
67+
}
68+
int range = minorVersion - minVersion.minorVersion;
69+
int shiftedRange = range << 16;
70+
return shiftedRange | toInt();
71+
}
72+
5873
/**
5974
* @return the version in format X.Y where X is the major version and Y is the minor version
6075
*/

driver/src/test/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtilTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
import org.junit.jupiter.api.Test;
2424

2525
import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3;
26+
import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4;
2627
import org.neo4j.driver.internal.messaging.v41.BoltProtocolV41;
27-
import org.neo4j.driver.internal.messaging.v42.BoltProtocolV42;
2828
import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43;
2929

3030
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -43,15 +43,15 @@ void shouldReturnHandshakeBuf()
4343
{
4444
assertByteBufContains(
4545
handshakeBuf(),
46-
BOLT_MAGIC_PREAMBLE, BoltProtocolV43.VERSION.toInt(), BoltProtocolV42.VERSION.toInt(),
47-
BoltProtocolV41.VERSION.toInt(), BoltProtocolV3.VERSION.toInt()
46+
BOLT_MAGIC_PREAMBLE, (1 << 16) | BoltProtocolV43.VERSION.toInt(), BoltProtocolV41.VERSION.toInt(),
47+
BoltProtocolV4.VERSION.toInt(), BoltProtocolV3.VERSION.toInt()
4848
);
4949
}
5050

5151
@Test
5252
void shouldReturnHandshakeString()
5353
{
54-
assertEquals( "[0x6060b017, 772, 516, 260, 3]", handshakeString() );
54+
assertEquals( "[0x6060b017, 66308, 260, 4, 3]", handshakeString() );
5555
}
5656

5757
@Test

driver/src/test/java/org/neo4j/driver/internal/messaging/BoltProtocolVersionTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.junit.jupiter.params.provider.CsvSource;
2424

2525
import static org.junit.jupiter.api.Assertions.assertEquals;
26+
import static org.junit.jupiter.api.Assertions.assertThrows;
2627

2728
class BoltProtocolVersionTest
2829
{
@@ -50,6 +51,40 @@ void shouldCompareTo(int majorA, int minorA, int majorB, int minorB, int expecte
5051

5152
}
5253

54+
@ParameterizedTest( name = "V{0}.{1} toIntRange V{2}.{3}")
55+
@CsvSource({
56+
"1, 0, 1, 0, 0x000001",
57+
"4, 3, 4, 2, 0x010304",
58+
"4, 3, 4, 1, 0x020304",
59+
"4, 3, 4, 0, 0x030304",
60+
"100, 100, 100, 0, 0x646464",
61+
"255, 255, 255, 0, 0xFFFFFF"
62+
} )
63+
void shouldOutputCorrectIntRange(int majorA, int minorA, int majorB, int minorB, int expectedResult)
64+
{
65+
BoltProtocolVersion versionA = new BoltProtocolVersion( majorA, minorA );
66+
BoltProtocolVersion versionB = new BoltProtocolVersion( majorB, minorB );
67+
68+
assertEquals( expectedResult, versionA.toIntRange( versionB ) );
69+
}
70+
71+
@ParameterizedTest( name = "V{0}.{1} toIntRange V{2}.{3}")
72+
@CsvSource({
73+
"1, 0, 2, 0",
74+
"2, 0, 1, 0",
75+
"4, 3, 4, 5",
76+
"4, 6, 3, 7",
77+
"3, 7, 4, 6",
78+
"255, 255, 100, 0"
79+
} )
80+
void shouldThrowsIllegalArgumentExceptionForIncorrectIntRange(int majorA, int minorA, int majorB, int minorB)
81+
{
82+
BoltProtocolVersion versionA = new BoltProtocolVersion( majorA, minorA );
83+
BoltProtocolVersion versionB = new BoltProtocolVersion( majorB, minorB );
84+
85+
assertThrows( IllegalArgumentException.class, () -> versionA.toIntRange( versionB ));
86+
}
87+
5388
@Test
5489
void shouldOutputCorrectLongFormatForMajorVersionOnly()
5590
{

0 commit comments

Comments
 (0)