Skip to content

Commit 31d3495

Browse files
authored
Since Server v4.0 the server agent received in response to the Hello message is unreliable. Instead, since the server and Bolt protocol versions are aligned, we use the protocol version instead for all connections to Server 4.0 and higher (neo4j#708) (neo4j#718)
Note: A future refactoring is required when support for 3.5 is dropped. The name serverVersion filed of the netty channel no longer reflects its true purpose since it essentially becomes protocolVersion. We keep the current naming for now since the split in meaning between 3.5 and 4.X+
1 parent ee624fe commit 31d3495

File tree

5 files changed

+88
-13
lines changed

5 files changed

+88
-13
lines changed

driver/src/main/java/org/neo4j/driver/internal/handlers/HelloResponseHandler.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,34 +23,46 @@
2323

2424
import java.util.Map;
2525

26+
import org.neo4j.driver.internal.messaging.BoltProtocolVersion;
27+
import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3;
2628
import org.neo4j.driver.internal.spi.ResponseHandler;
27-
import org.neo4j.driver.internal.util.ServerVersion;
2829
import org.neo4j.driver.Value;
2930

3031
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setConnectionId;
3132
import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerVersion;
3233
import static org.neo4j.driver.internal.util.MetadataExtractor.extractNeo4jServerVersion;
34+
import static org.neo4j.driver.internal.util.ServerVersion.fromBoltProtocolVersion;
3335

3436
public class HelloResponseHandler implements ResponseHandler
3537
{
3638
private static final String CONNECTION_ID_METADATA_KEY = "connection_id";
3739

3840
private final ChannelPromise connectionInitializedPromise;
3941
private final Channel channel;
42+
private final BoltProtocolVersion protocolVersion;
4043

41-
public HelloResponseHandler( ChannelPromise connectionInitializedPromise )
44+
public HelloResponseHandler( ChannelPromise connectionInitializedPromise, BoltProtocolVersion protocolVersion )
4245
{
4346
this.connectionInitializedPromise = connectionInitializedPromise;
4447
this.channel = connectionInitializedPromise.channel();
48+
this.protocolVersion = protocolVersion;
4549
}
4650

4751
@Override
4852
public void onSuccess( Map<String,Value> metadata )
4953
{
5054
try
5155
{
52-
ServerVersion serverVersion = extractNeo4jServerVersion( metadata );
53-
setServerVersion( channel, serverVersion );
56+
// From Server V4 extracting server from metadata in the success message is unreliable
57+
// so we fix the Server version against the Bolt Protocol version for Server V4 and above.
58+
if ( BoltProtocolV3.VERSION.equals( protocolVersion ) )
59+
{
60+
setServerVersion( channel, extractNeo4jServerVersion( metadata ) );
61+
}
62+
else
63+
{
64+
setServerVersion( channel, fromBoltProtocolVersion( protocolVersion ) );
65+
}
5466

5567
String connectionId = extractConnectionId( metadata );
5668
setConnectionId( channel, connectionId );

driver/src/main/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public void initializeChannel( String userAgent, Map<String,Value> authToken, Ch
8181
Channel channel = channelInitializedPromise.channel();
8282

8383
HelloMessage message = new HelloMessage( userAgent, authToken );
84-
HelloResponseHandler handler = new HelloResponseHandler( channelInitializedPromise );
84+
HelloResponseHandler handler = new HelloResponseHandler( channelInitializedPromise, version() );
8585

8686
messageDispatcher( channel ).enqueue( handler );
8787
channel.writeAndFlush( message, channel.voidPromise() );

driver/src/main/java/org/neo4j/driver/internal/util/ServerVersion.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424

2525
import org.neo4j.driver.Driver;
2626
import org.neo4j.driver.Session;
27+
import org.neo4j.driver.internal.messaging.BoltProtocolVersion;
28+
import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4;
29+
import org.neo4j.driver.internal.messaging.v41.BoltProtocolV41;
2730

2831
import static java.lang.Integer.compare;
2932

@@ -174,4 +177,19 @@ private static String stringValue( String product, int major, int minor, int pat
174177
}
175178
return String.format( "%s/%s.%s.%s", product, major, minor, patch );
176179
}
180+
181+
public static ServerVersion fromBoltProtocolVersion( BoltProtocolVersion protocolVersion )
182+
{
183+
184+
if ( BoltProtocolV4.VERSION.equals( protocolVersion ) )
185+
{
186+
return ServerVersion.v4_0_0;
187+
}
188+
else if ( BoltProtocolV41.VERSION.equals( protocolVersion ) )
189+
{
190+
return ServerVersion.v4_1_0;
191+
}
192+
193+
return ServerVersion.vInDev;
194+
}
177195
}

driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher;
3838
import org.neo4j.driver.internal.async.outbound.OutboundMessageHandler;
3939
import org.neo4j.driver.internal.messaging.v1.MessageFormatV1;
40+
import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3;
41+
import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4;
42+
import org.neo4j.driver.internal.messaging.v41.BoltProtocolV41;
43+
import org.neo4j.driver.internal.util.ServerVersion;
4044

4145
import static org.junit.jupiter.api.Assertions.assertEquals;
4246
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -73,7 +77,7 @@ void tearDown()
7377
void shouldSetServerVersionOnChannel()
7478
{
7579
ChannelPromise channelPromise = channel.newPromise();
76-
HelloResponseHandler handler = new HelloResponseHandler( channelPromise );
80+
HelloResponseHandler handler = new HelloResponseHandler( channelPromise, BoltProtocolV3.VERSION );
7781

7882
Map<String,Value> metadata = metadata( anyServerVersion(), "bolt-1" );
7983
handler.onSuccess( metadata );
@@ -86,7 +90,7 @@ void shouldSetServerVersionOnChannel()
8690
void shouldThrowWhenServerVersionNotReturned()
8791
{
8892
ChannelPromise channelPromise = channel.newPromise();
89-
HelloResponseHandler handler = new HelloResponseHandler( channelPromise );
93+
HelloResponseHandler handler = new HelloResponseHandler( channelPromise, BoltProtocolV3.VERSION );
9094

9195
Map<String,Value> metadata = metadata( null, "bolt-1" );
9296
assertThrows( UntrustedServerException.class, () -> handler.onSuccess( metadata ) );
@@ -99,7 +103,7 @@ void shouldThrowWhenServerVersionNotReturned()
99103
void shouldThrowWhenServerVersionIsNull()
100104
{
101105
ChannelPromise channelPromise = channel.newPromise();
102-
HelloResponseHandler handler = new HelloResponseHandler( channelPromise );
106+
HelloResponseHandler handler = new HelloResponseHandler( channelPromise, BoltProtocolV3.VERSION );
103107

104108
Map<String,Value> metadata = metadata( Values.NULL, "bolt-x" );
105109
assertThrows( UntrustedServerException.class, () -> handler.onSuccess( metadata ) );
@@ -112,7 +116,7 @@ void shouldThrowWhenServerVersionIsNull()
112116
void shouldThrowWhenServerVersionCantBeParsed()
113117
{
114118
ChannelPromise channelPromise = channel.newPromise();
115-
HelloResponseHandler handler = new HelloResponseHandler( channelPromise );
119+
HelloResponseHandler handler = new HelloResponseHandler( channelPromise, BoltProtocolV3.VERSION );
116120

117121
Map<String,Value> metadata = metadata( "WrongServerVersion", "bolt-x" );
118122
assertThrows( IllegalArgumentException.class, () -> handler.onSuccess( metadata ) );
@@ -121,11 +125,39 @@ void shouldThrowWhenServerVersionCantBeParsed()
121125
assertTrue( channel.closeFuture().isDone() ); // channel was closed
122126
}
123127

128+
@Test
129+
void shouldUseProtocolVersionForServerVersionWhenConnectedWithBoltV4()
130+
{
131+
ChannelPromise channelPromise = channel.newPromise();
132+
HelloResponseHandler handler = new HelloResponseHandler( channelPromise, BoltProtocolV4.VERSION );
133+
134+
// server used in metadata should be ignored
135+
Map<String,Value> metadata = metadata( ServerVersion.vInDev, "bolt-1" );
136+
handler.onSuccess( metadata );
137+
138+
assertTrue( channelPromise.isSuccess() );
139+
assertEquals( ServerVersion.v4_0_0, serverVersion( channel ) );
140+
}
141+
142+
@Test
143+
void shouldUseProtocolVersionForServerVersionWhenConnectedWithBoltV41()
144+
{
145+
ChannelPromise channelPromise = channel.newPromise();
146+
HelloResponseHandler handler = new HelloResponseHandler( channelPromise, BoltProtocolV41.VERSION );
147+
148+
// server used in metadata should be ignored
149+
Map<String,Value> metadata = metadata( ServerVersion.vInDev, "bolt-1" );
150+
handler.onSuccess( metadata );
151+
152+
assertTrue( channelPromise.isSuccess() );
153+
assertEquals( ServerVersion.v4_1_0, serverVersion( channel ) );
154+
}
155+
124156
@Test
125157
void shouldSetConnectionIdOnChannel()
126158
{
127159
ChannelPromise channelPromise = channel.newPromise();
128-
HelloResponseHandler handler = new HelloResponseHandler( channelPromise );
160+
HelloResponseHandler handler = new HelloResponseHandler( channelPromise, BoltProtocolV3.VERSION );
129161

130162
Map<String,Value> metadata = metadata( anyServerVersion(), "bolt-42" );
131163
handler.onSuccess( metadata );
@@ -138,7 +170,7 @@ void shouldSetConnectionIdOnChannel()
138170
void shouldThrowWhenConnectionIdNotReturned()
139171
{
140172
ChannelPromise channelPromise = channel.newPromise();
141-
HelloResponseHandler handler = new HelloResponseHandler( channelPromise );
173+
HelloResponseHandler handler = new HelloResponseHandler( channelPromise, BoltProtocolV3.VERSION );
142174

143175
Map<String,Value> metadata = metadata( anyServerVersion(), null );
144176
assertThrows( IllegalStateException.class, () -> handler.onSuccess( metadata ) );
@@ -151,7 +183,7 @@ void shouldThrowWhenConnectionIdNotReturned()
151183
void shouldThrowWhenConnectionIdIsNull()
152184
{
153185
ChannelPromise channelPromise = channel.newPromise();
154-
HelloResponseHandler handler = new HelloResponseHandler( channelPromise );
186+
HelloResponseHandler handler = new HelloResponseHandler( channelPromise, BoltProtocolV3.VERSION );
155187

156188
Map<String,Value> metadata = metadata( anyServerVersion(), Values.NULL );
157189
assertThrows( IllegalStateException.class, () -> handler.onSuccess( metadata ) );
@@ -164,7 +196,7 @@ void shouldThrowWhenConnectionIdIsNull()
164196
void shouldCloseChannelOnFailure() throws Exception
165197
{
166198
ChannelPromise channelPromise = channel.newPromise();
167-
HelloResponseHandler handler = new HelloResponseHandler( channelPromise );
199+
HelloResponseHandler handler = new HelloResponseHandler( channelPromise, BoltProtocolV3.VERSION );
168200

169201
RuntimeException error = new RuntimeException( "Hi!" );
170202
handler.onFailure( error );

driver/src/test/java/org/neo4j/driver/internal/util/ServerVersionTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020

2121
import org.junit.jupiter.api.Test;
2222

23+
import org.neo4j.driver.internal.messaging.BoltProtocolVersion;
24+
import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4;
25+
import org.neo4j.driver.internal.messaging.v41.BoltProtocolV41;
26+
27+
import static java.lang.Integer.MAX_VALUE;
2328
import static org.hamcrest.MatcherAssert.assertThat;
2429
import static org.hamcrest.Matchers.is;
2530
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -60,4 +65,12 @@ void shouldFailToCompareDifferentProducts()
6065

6166
assertThrows( IllegalArgumentException.class, () -> version1.greaterThanOrEqual( version2 ) );
6267
}
68+
69+
@Test
70+
void shouldReturnCorrectServerVersionFromBoltProtocolVersion()
71+
{
72+
assertEquals( ServerVersion.v4_0_0, ServerVersion.fromBoltProtocolVersion( BoltProtocolV4.VERSION ) );
73+
assertEquals( ServerVersion.v4_1_0, ServerVersion.fromBoltProtocolVersion( BoltProtocolV41.VERSION ) );
74+
assertEquals( ServerVersion.vInDev, ServerVersion.fromBoltProtocolVersion( new BoltProtocolVersion( MAX_VALUE, MAX_VALUE ) ) );
75+
}
6376
}

0 commit comments

Comments
 (0)