From 4504633f36c3e1d533d1587635f9669256b444ea Mon Sep 17 00:00:00 2001 From: Gregory Woods Date: Wed, 13 May 2020 13:10:50 +0100 Subject: [PATCH 1/4] Adding routing context into HELLO message so that server can make routing decisions --- .../neo4j/driver/internal/DriverFactory.java | 11 ++--- .../connection/ChannelConnectorImpl.java | 11 +++-- .../HandshakeCompletedListener.java | 7 +++- .../internal/cluster/RoutingContext.java | 10 ++++- .../internal/messaging/BoltProtocol.java | 4 +- .../messaging/request/HelloMessage.java | 8 ++-- .../internal/messaging/v1/BoltProtocolV1.java | 4 +- .../internal/messaging/v3/BoltProtocolV3.java | 15 ++++++- .../integration/ChannelConnectorImplIT.java | 3 +- .../integration/ConnectionHandlingIT.java | 6 ++- .../integration/RoutingDriverBoltKitTest.java | 40 +++++++++++++++++++ .../internal/CustomSecurityPlanTest.java | 6 ++- .../internal/DirectDriverBoltKitTest.java | 18 +++++++++ .../driver/internal/DriverFactoryTest.java | 8 ++-- .../HandshakeCompletedListenerTest.java | 10 +++-- .../async/pool/ConnectionPoolImplIT.java | 3 +- .../async/pool/NettyChannelPoolIT.java | 3 +- .../internal/cluster/RoutingContextTest.java | 18 +++++++++ .../encode/HelloMessageEncoderTest.java | 26 +++++++++++- .../messaging/request/HelloMessageTest.java | 27 ++++++++++++- .../messaging/v1/BoltProtocolV1Test.java | 5 ++- .../messaging/v1/MessageWriterV1Test.java | 3 +- .../messaging/v2/MessageWriterV2Test.java | 3 +- .../messaging/v3/BoltProtocolV3Test.java | 5 ++- .../messaging/v3/MessageWriterV3Test.java | 3 +- .../messaging/v4/MessageWriterV4Test.java | 3 +- .../util/FailingConnectionDriverFactory.java | 7 +++- .../util/MessageRecordingDriverFactory.java | 6 ++- .../util/io/ChannelTrackingDriverFactory.java | 13 +++--- ...DriverFactoryWithFailingMessageFormat.java | 6 ++- ...mpty_routing_context_in_hello_neo4j.script | 17 ++++++++ .../src/test/resources/hello_run_exit.script | 2 +- .../hello_with_routing_context_bolt.script | 12 ++++++ .../routing_context_in_hello_neo4j.script | 17 ++++++++ .../test/resources/untrusted_server.script | 2 +- 35 files changed, 284 insertions(+), 58 deletions(-) create mode 100644 driver/src/test/resources/empty_routing_context_in_hello_neo4j.script create mode 100644 driver/src/test/resources/hello_with_routing_context_bolt.script create mode 100644 driver/src/test/resources/routing_context_in_hello_neo4j.script diff --git a/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java b/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java index 13dfadeadb..694aa2039b 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java +++ b/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java @@ -93,17 +93,18 @@ public final Driver newInstance ( URI uri, AuthToken authToken, RoutingSettings RetryLogic retryLogic = createRetryLogic( retrySettings, eventExecutorGroup, config.logging() ); MetricsProvider metricsProvider = createDriverMetrics( config, createClock() ); - ConnectionPool connectionPool = createConnectionPool( authToken, securityPlan, bootstrap, metricsProvider, config, ownsEventLoopGroup ); + ConnectionPool connectionPool = createConnectionPool( authToken, securityPlan, bootstrap, metricsProvider, config, + ownsEventLoopGroup, newRoutingSettings.routingContext() ); return createDriver( uri, securityPlan, address, connectionPool, eventExecutorGroup, newRoutingSettings, retryLogic, metricsProvider, config ); } protected ConnectionPool createConnectionPool( AuthToken authToken, SecurityPlan securityPlan, Bootstrap bootstrap, - MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup ) + MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup, RoutingContext routingContext ) { Clock clock = createClock(); ConnectionSettings settings = new ConnectionSettings( authToken, config.connectionTimeoutMillis() ); - ChannelConnector connector = createConnector( settings, securityPlan, config, clock ); + ChannelConnector connector = createConnector( settings, securityPlan, config, clock, routingContext ); PoolSettings poolSettings = new PoolSettings( config.maxConnectionPoolSize(), config.connectionAcquisitionTimeoutMillis(), config.maxConnectionLifetimeMillis(), config.idleTimeBeforeConnectionTest() @@ -124,9 +125,9 @@ protected static MetricsProvider createDriverMetrics( Config config, Clock clock } protected ChannelConnector createConnector( ConnectionSettings settings, SecurityPlan securityPlan, - Config config, Clock clock ) + Config config, Clock clock, RoutingContext routingContext ) { - return new ChannelConnectorImpl( settings, securityPlan, config.logging(), clock ); + return new ChannelConnectorImpl( settings, securityPlan, config.logging(), clock, routingContext ); } private InternalDriver createDriver( URI uri, SecurityPlan securityPlan, BoltServerAddress address, ConnectionPool connectionPool, diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImpl.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImpl.java index 79c43cc93d..4674a6c3f8 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImpl.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImpl.java @@ -30,6 +30,7 @@ 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.cluster.RoutingContext; import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.util.Clock; @@ -45,6 +46,7 @@ public class ChannelConnectorImpl implements ChannelConnector { private final String userAgent; private final Map authToken; + private final RoutingContext routingContext; private final SecurityPlan securityPlan; private final ChannelPipelineBuilder pipelineBuilder; private final int connectTimeoutMillis; @@ -52,16 +54,17 @@ public class ChannelConnectorImpl implements ChannelConnector private final Clock clock; public ChannelConnectorImpl( ConnectionSettings connectionSettings, SecurityPlan securityPlan, Logging logging, - Clock clock ) + Clock clock, RoutingContext routingContext ) { - this( connectionSettings, securityPlan, new ChannelPipelineBuilderImpl(), logging, clock ); + this( connectionSettings, securityPlan, new ChannelPipelineBuilderImpl(), logging, clock, routingContext ); } public ChannelConnectorImpl( ConnectionSettings connectionSettings, SecurityPlan securityPlan, - ChannelPipelineBuilder pipelineBuilder, Logging logging, Clock clock ) + ChannelPipelineBuilder pipelineBuilder, Logging logging, Clock clock, RoutingContext routingContext ) { this.userAgent = connectionSettings.userAgent(); this.authToken = tokenAsMap( connectionSettings.authToken() ); + this.routingContext = routingContext; this.connectTimeoutMillis = connectionSettings.connectTimeoutMillis(); this.securityPlan = requireNonNull( securityPlan ); this.pipelineBuilder = pipelineBuilder; @@ -113,7 +116,7 @@ private void installHandshakeCompletedListeners( ChannelPromise handshakeComplet // 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 ) ); + handshakeCompleted.addListener( new HandshakeCompletedListener( userAgent, authToken, routingContext, connectionInitialized ) ); } private static Map tokenAsMap( AuthToken token ) diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListener.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListener.java index 79c7207328..83608c41e2 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListener.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListener.java @@ -24,6 +24,7 @@ import java.util.Map; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.messaging.BoltProtocol; import org.neo4j.driver.Value; @@ -33,13 +34,15 @@ public class HandshakeCompletedListener implements ChannelFutureListener { private final String userAgent; private final Map authToken; + private final RoutingContext routingContext; private final ChannelPromise connectionInitializedPromise; public HandshakeCompletedListener( String userAgent, Map authToken, - ChannelPromise connectionInitializedPromise ) + RoutingContext routingContext, ChannelPromise connectionInitializedPromise ) { this.userAgent = requireNonNull( userAgent ); this.authToken = requireNonNull( authToken ); + this.routingContext = routingContext; this.connectionInitializedPromise = requireNonNull( connectionInitializedPromise ); } @@ -49,7 +52,7 @@ public void operationComplete( ChannelFuture future ) if ( future.isSuccess() ) { BoltProtocol protocol = BoltProtocol.forChannel( future.channel() ); - protocol.initializeChannel( userAgent, authToken, connectionInitializedPromise ); + protocol.initializeChannel( userAgent, authToken, routingContext, connectionInitializedPromise ); } else { diff --git a/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java b/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java index aaa81aa64c..301d1d1c71 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java @@ -33,14 +33,17 @@ public class RoutingContext private static final String ROUTING_ADDRESS_KEY = "address"; private final Map context; + private final boolean isServerRoutingEnabled; private RoutingContext() { + this.isServerRoutingEnabled = true; this.context = emptyMap(); } public RoutingContext( URI uri ) { + this.isServerRoutingEnabled = uri.getScheme().startsWith( "neo4j" ); this.context = unmodifiableMap( parseParameters( uri ) ); } @@ -54,10 +57,15 @@ public Map asMap() return context; } + public boolean isServerRoutingEnabled() + { + return isServerRoutingEnabled; + } + @Override public String toString() { - return "RoutingContext" + context; + return "RoutingContext" + context + " isServerRoutingEnabled=" + isServerRoutingEnabled; } private static Map parseParameters( URI uri ) diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java index 01869a004d..092803ee6c 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java @@ -34,6 +34,7 @@ import org.neo4j.driver.internal.BookmarkHolder; import org.neo4j.driver.internal.InternalBookmark; import org.neo4j.driver.internal.async.UnmanagedTransaction; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.cursor.ResultCursorFactory; import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2; @@ -58,9 +59,10 @@ public interface BoltProtocol * * @param userAgent the user agent string. * @param authToken the authentication token. + * @param routingContext the configured routing context * @param channelInitializedPromise the promise to be notified when initialization is completed. */ - void initializeChannel( String userAgent, Map authToken, ChannelPromise channelInitializedPromise ); + void initializeChannel( String userAgent, Map authToken, RoutingContext routingContext, ChannelPromise channelInitializedPromise ); /** * Prepare to close channel before it is closed. diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/request/HelloMessage.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/request/HelloMessage.java index 1c9ad31b56..133a66e628 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/request/HelloMessage.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/request/HelloMessage.java @@ -32,10 +32,11 @@ public class HelloMessage extends MessageWithMetadata public final static byte SIGNATURE = 0x01; private static final String USER_AGENT_METADATA_KEY = "user_agent"; + private static final String ROUTING_CONTEXT_METADATA_KEY = "routing"; - public HelloMessage( String userAgent, Map authToken ) + public HelloMessage( String userAgent, Map authToken, Map routingContext ) { - super( buildMetadata( userAgent, authToken ) ); + super( buildMetadata( userAgent, authToken, routingContext ) ); } @Override @@ -73,10 +74,11 @@ public String toString() return "HELLO " + metadataCopy; } - private static Map buildMetadata( String userAgent, Map authToken ) + private static Map buildMetadata( String userAgent, Map authToken, Map routingContext ) { Map result = new HashMap<>( authToken ); result.put( USER_AGENT_METADATA_KEY, value( userAgent ) ); + result.put( ROUTING_CONTEXT_METADATA_KEY, value( routingContext ) ); return result; } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java index f89407065e..0c77b3c245 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java @@ -35,6 +35,7 @@ import org.neo4j.driver.internal.DatabaseName; import org.neo4j.driver.internal.InternalBookmark; import org.neo4j.driver.internal.async.UnmanagedTransaction; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.cursor.AsyncResultCursorOnlyFactory; import org.neo4j.driver.internal.cursor.ResultCursorFactory; import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; @@ -84,7 +85,8 @@ public MessageFormat createMessageFormat() } @Override - public void initializeChannel( String userAgent, Map authToken, ChannelPromise channelInitializedPromise ) + public void initializeChannel( String userAgent, Map authToken, RoutingContext routingContext, + ChannelPromise channelInitializedPromise ) { Channel channel = channelInitializedPromise.channel(); diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3.java index 75e007fc4e..0f3b4ce11a 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3.java @@ -21,6 +21,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelPromise; +import java.util.Collections; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -33,6 +34,7 @@ import org.neo4j.driver.internal.DatabaseName; import org.neo4j.driver.internal.InternalBookmark; import org.neo4j.driver.internal.async.UnmanagedTransaction; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.cursor.AsyncResultCursorOnlyFactory; import org.neo4j.driver.internal.cursor.ResultCursorFactory; import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; @@ -76,11 +78,20 @@ public MessageFormat createMessageFormat() } @Override - public void initializeChannel( String userAgent, Map authToken, ChannelPromise channelInitializedPromise ) + public void initializeChannel( String userAgent, Map authToken, RoutingContext routingContext, ChannelPromise channelInitializedPromise ) { Channel channel = channelInitializedPromise.channel(); + HelloMessage message; + + if ( routingContext.isServerRoutingEnabled() ) + { + message = new HelloMessage( userAgent, authToken, routingContext.asMap() ); + } + else + { + message = new HelloMessage( userAgent, authToken, null ); + } - HelloMessage message = new HelloMessage( userAgent, authToken ); HelloResponseHandler handler = new HelloResponseHandler( channelInitializedPromise, version() ); messageDispatcher( channel ).enqueue( handler ); diff --git a/driver/src/test/java/org/neo4j/driver/integration/ChannelConnectorImplIT.java b/driver/src/test/java/org/neo4j/driver/integration/ChannelConnectorImplIT.java index a6a9695d71..8942adb4d5 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/ChannelConnectorImplIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/ChannelConnectorImplIT.java @@ -46,6 +46,7 @@ import org.neo4j.driver.internal.async.connection.ChannelConnector; import org.neo4j.driver.internal.async.connection.ChannelConnectorImpl; import org.neo4j.driver.internal.async.inbound.ConnectTimeoutHandler; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.security.SecurityPlanImpl; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.util.FakeClock; @@ -231,7 +232,7 @@ private ChannelConnectorImpl newConnector( AuthToken authToken, SecurityPlan sec int connectTimeoutMillis ) { ConnectionSettings settings = new ConnectionSettings( authToken, connectTimeoutMillis ); - return new ChannelConnectorImpl( settings, securityPlan, DEV_NULL_LOGGING, new FakeClock() ); + return new ChannelConnectorImpl( settings, securityPlan, DEV_NULL_LOGGING, new FakeClock(), RoutingContext.EMPTY ); } private static SecurityPlan trustAllCertificates() throws GeneralSecurityException diff --git a/driver/src/test/java/org/neo4j/driver/integration/ConnectionHandlingIT.java b/driver/src/test/java/org/neo4j/driver/integration/ConnectionHandlingIT.java index a3e150c149..fc61e0b878 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/ConnectionHandlingIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/ConnectionHandlingIT.java @@ -49,6 +49,7 @@ import org.neo4j.driver.internal.async.connection.ChannelConnector; import org.neo4j.driver.internal.async.pool.ConnectionPoolImpl; import org.neo4j.driver.internal.async.pool.PoolSettings; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.cluster.RoutingSettings; import org.neo4j.driver.internal.metrics.MetricsProvider; import org.neo4j.driver.internal.retry.RetrySettings; @@ -445,14 +446,15 @@ private static class DriverFactoryWithConnectionPool extends DriverFactory @Override protected ConnectionPool createConnectionPool( AuthToken authToken, SecurityPlan securityPlan, Bootstrap bootstrap, - MetricsProvider ignored, Config config, boolean ownsEventLoopGroup ) + MetricsProvider ignored, Config config, boolean ownsEventLoopGroup, + RoutingContext routingContext ) { ConnectionSettings connectionSettings = new ConnectionSettings( authToken, 1000 ); PoolSettings poolSettings = new PoolSettings( config.maxConnectionPoolSize(), config.connectionAcquisitionTimeoutMillis(), config.maxConnectionLifetimeMillis(), config.idleTimeBeforeConnectionTest() ); Clock clock = createClock(); - ChannelConnector connector = super.createConnector( connectionSettings, securityPlan, config, clock ); + ChannelConnector connector = super.createConnector( connectionSettings, securityPlan, config, clock, routingContext ); connectionPool = new MemorizingConnectionPool( connector, bootstrap, poolSettings, config.logging(), clock, ownsEventLoopGroup ); return connectionPool; } diff --git a/driver/src/test/java/org/neo4j/driver/integration/RoutingDriverBoltKitTest.java b/driver/src/test/java/org/neo4j/driver/integration/RoutingDriverBoltKitTest.java index f31e5da9c4..31f1f10c16 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/RoutingDriverBoltKitTest.java +++ b/driver/src/test/java/org/neo4j/driver/integration/RoutingDriverBoltKitTest.java @@ -892,6 +892,46 @@ void shouldSendRoutingContextToServer() throws Exception } } + @Test + void shouldSendRoutingContextInHelloMessage() throws Exception + { + // stub server is both a router and reader + StubServer server = StubServer.start( "routing_context_in_hello_neo4j.script", 9001 ); + + URI uri = URI.create( "neo4j://127.0.0.1:9001/?policy=my_policy®ion=china" ); + try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); Session session = driver.session() ) + { + List records = session.run( "MATCH (n) RETURN n.name AS name" ).list(); + assertEquals( 2, records.size() ); + assertEquals( "Alice", records.get( 0 ).get( "name" ).asString() ); + assertEquals( "Bob", records.get( 1 ).get( "name" ).asString() ); + } + finally + { + assertEquals( 0, server.exitStatus() ); + } + } + + @Test + void shouldSendEmptyRoutingContextInHelloMessage() throws Exception + { + // stub server is both a router and reader + StubServer server = StubServer.start( "empty_routing_context_in_hello_neo4j.script", 9001 ); + + URI uri = URI.create( "neo4j://127.0.0.1:9001/" ); + try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); Session session = driver.session() ) + { + List records = session.run( "MATCH (n) RETURN n.name AS name" ).list(); + assertEquals( 2, records.size() ); + assertEquals( "Alice", records.get( 0 ).get( "name" ).asString() ); + assertEquals( "Bob", records.get( 1 ).get( "name" ).asString() ); + } + finally + { + assertEquals( 0, server.exitStatus() ); + } + } + @Test void shouldServeReadsButFailWritesWhenNoWritersAvailable() throws Exception { diff --git a/driver/src/test/java/org/neo4j/driver/internal/CustomSecurityPlanTest.java b/driver/src/test/java/org/neo4j/driver/internal/CustomSecurityPlanTest.java index 4c8aae679e..9a721d00c7 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/CustomSecurityPlanTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/CustomSecurityPlanTest.java @@ -28,6 +28,7 @@ import org.neo4j.driver.AuthToken; import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Config; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.cluster.RoutingSettings; import org.neo4j.driver.internal.metrics.MetricsProvider; import org.neo4j.driver.internal.retry.RetrySettings; @@ -74,10 +75,11 @@ protected InternalDriver createDriver( SecurityPlan securityPlan, SessionFactory @Override protected ConnectionPool createConnectionPool( AuthToken authToken, SecurityPlan securityPlan, Bootstrap bootstrap, - MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup ) + MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup, + RoutingContext routingContext ) { capturedSecurityPlans.add( securityPlan ); - return super.createConnectionPool( authToken, securityPlan, bootstrap, metricsProvider, config, ownsEventLoopGroup ); + return super.createConnectionPool( authToken, securityPlan, bootstrap, metricsProvider, config, ownsEventLoopGroup, routingContext ); } } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/DirectDriverBoltKitTest.java b/driver/src/test/java/org/neo4j/driver/internal/DirectDriverBoltKitTest.java index 301cafc0a8..c5aa794945 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/DirectDriverBoltKitTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/DirectDriverBoltKitTest.java @@ -125,6 +125,24 @@ void shouldSendMultipleBookmarks() throws Exception } } + @Test + void shouldSendNullRoutingContextForBoltUri() throws Exception + { + StubServer server = StubServer.start( "hello_with_routing_context_bolt.script", 9001 ); + + try ( Driver driver = GraphDatabase.driver( "bolt://localhost:9001", INSECURE_CONFIG ); + Session session = driver.session() ) + { + List names = session.run( "MATCH (n) RETURN n.name" ).list( record -> record.get( 0 ).asString() ); + assertEquals( asList( "Foo", "Bar" ), names ); + + } + finally + { + assertEquals( 0, server.exitStatus() ); + } + } + @Test void shouldLogConnectionIdInDebugMode() throws Exception { diff --git a/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java b/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java index 4df791dace..a9cffdeef2 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java @@ -36,6 +36,7 @@ import org.neo4j.driver.internal.async.LeakLoggingNetworkSession; import org.neo4j.driver.internal.async.NetworkSession; import org.neo4j.driver.internal.async.connection.BootstrapFactory; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.cluster.RoutingSettings; import org.neo4j.driver.internal.cluster.loadbalancing.LoadBalancer; import org.neo4j.driver.internal.metrics.InternalMetricsProvider; @@ -211,7 +212,8 @@ protected InternalDriver createRoutingDriver( SecurityPlan securityPlan, BoltSer @Override protected ConnectionPool createConnectionPool( AuthToken authToken, SecurityPlan securityPlan, Bootstrap bootstrap, - MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup ) + MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup, + RoutingContext routingContext ) { return connectionPool; } @@ -247,7 +249,7 @@ protected SessionFactory createSessionFactory( ConnectionProvider connectionProv @Override protected ConnectionPool createConnectionPool( AuthToken authToken, SecurityPlan securityPlan, Bootstrap bootstrap, - MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup ) + MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup, RoutingContext routingContext ) { return connectionPoolMock(); } @@ -270,7 +272,7 @@ protected Bootstrap createBootstrap( int ignored ) @Override protected ConnectionPool createConnectionPool( AuthToken authToken, SecurityPlan securityPlan, Bootstrap bootstrap, - MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup ) + MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup, RoutingContext routingContext ) { return connectionPoolMock(); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java index f0d339d6c2..dbb5fccab5 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java @@ -24,11 +24,13 @@ import org.junit.jupiter.api.Test; import java.io.IOException; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.neo4j.driver.Value; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.handlers.HelloResponseHandler; import org.neo4j.driver.internal.handlers.InitResponseHandler; import org.neo4j.driver.internal.messaging.BoltProtocolVersion; @@ -67,7 +69,7 @@ void tearDown() void shouldFailConnectionInitializedPromiseWhenHandshakeFails() { ChannelPromise channelInitializedPromise = channel.newPromise(); - HandshakeCompletedListener listener = new HandshakeCompletedListener( "user-agent", authToken(), + HandshakeCompletedListener listener = new HandshakeCompletedListener( "user-agent", authToken(), RoutingContext.EMPTY, channelInitializedPromise ); ChannelPromise handshakeCompletedPromise = channel.newPromise(); @@ -95,7 +97,7 @@ void shouldWriteInitializationMessageInBoltV2WhenHandshakeCompleted() @Test void shouldWriteInitializationMessageInBoltV3WhenHandshakeCompleted() { - testWritingOfInitializationMessage( BoltProtocolV3.VERSION, new HelloMessage( USER_AGENT, authToken() ), HelloResponseHandler.class ); + testWritingOfInitializationMessage( BoltProtocolV3.VERSION, new HelloMessage( USER_AGENT, authToken(), Collections.emptyMap() ), HelloResponseHandler.class ); } private void testWritingOfInitializationMessage( BoltProtocolVersion protocolVersion, Message expectedMessage, Class handlerType ) @@ -105,8 +107,8 @@ private void testWritingOfInitializationMessage( BoltProtocolVersion protocolVer setMessageDispatcher( channel, messageDispatcher ); ChannelPromise channelInitializedPromise = channel.newPromise(); - HandshakeCompletedListener listener = new HandshakeCompletedListener( USER_AGENT, authToken(), - channelInitializedPromise ); + HandshakeCompletedListener listener = new HandshakeCompletedListener( USER_AGENT, authToken(), RoutingContext.EMPTY, + channelInitializedPromise ); ChannelPromise handshakeCompletedPromise = channel.newPromise(); handshakeCompletedPromise.setSuccess(); diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImplIT.java b/driver/src/test/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImplIT.java index 04f396d645..d8974bb977 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImplIT.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImplIT.java @@ -33,6 +33,7 @@ import org.neo4j.driver.internal.async.connection.BootstrapFactory; import org.neo4j.driver.internal.async.connection.ChannelConnector; import org.neo4j.driver.internal.async.connection.ChannelConnectorImpl; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.security.SecurityPlanImpl; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.util.FakeClock; @@ -146,7 +147,7 @@ private ConnectionPoolImpl newPool() throws Exception FakeClock clock = new FakeClock(); ConnectionSettings connectionSettings = new ConnectionSettings( neo4j.authToken(), 5000 ); ChannelConnector connector = new ChannelConnectorImpl( connectionSettings, SecurityPlanImpl.insecure(), - DEV_NULL_LOGGING, clock ); + DEV_NULL_LOGGING, clock, RoutingContext.EMPTY ); PoolSettings poolSettings = newSettings(); Bootstrap bootstrap = BootstrapFactory.newBootstrap( 1 ); return new ConnectionPoolImpl( connector, bootstrap, poolSettings, DEV_NULL_METRICS, DEV_NULL_LOGGING, clock, true ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelPoolIT.java b/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelPoolIT.java index 3e5c27a444..de66394c85 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelPoolIT.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelPoolIT.java @@ -37,6 +37,7 @@ import org.neo4j.driver.internal.ConnectionSettings; import org.neo4j.driver.internal.async.connection.BootstrapFactory; import org.neo4j.driver.internal.async.connection.ChannelConnectorImpl; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.security.SecurityPlanImpl; import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.util.FakeClock; @@ -183,7 +184,7 @@ private NettyChannelPool newPool( AuthToken authToken, int maxConnections ) { ConnectionSettings settings = new ConnectionSettings( authToken, 5_000 ); ChannelConnectorImpl connector = new ChannelConnectorImpl( settings, SecurityPlanImpl.insecure(), DEV_NULL_LOGGING, - new FakeClock() ); + new FakeClock(), RoutingContext.EMPTY ); return new NettyChannelPool( neo4j.address(), connector, bootstrap, poolHandler, ChannelHealthChecker.ACTIVE, 1_000, maxConnections ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/cluster/RoutingContextTest.java b/driver/src/test/java/org/neo4j/driver/internal/cluster/RoutingContextTest.java index ae8f1f07f3..22eb2ae9b8 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/cluster/RoutingContextTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/cluster/RoutingContextTest.java @@ -72,6 +72,24 @@ void uriWithQueryIsParsed() assertEquals( expectedMap, context.asMap() ); } + @Test + void boltUriDisablesServerSideRouting() + { + URI uri = URI.create( "bolt://localhost:7687/?key1=value1&key2=value2&key3=value3" ); + RoutingContext context = new RoutingContext( uri ); + + assertEquals( false, context.isServerRoutingEnabled() ); + } + + @Test + void neo4jUriEnablesServerSideRouting() + { + URI uri = URI.create( "neo4j://localhost:7687/?key1=value1&key2=value2&key3=value3" ); + RoutingContext context = new RoutingContext( uri ); + + assertEquals( true, context.isServerRoutingEnabled() ); + } + @Test void throwsForInvalidUriQuery() { diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/encode/HelloMessageEncoderTest.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/encode/HelloMessageEncoderTest.java index a5b8efb0cb..6f7e6a79d8 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/encode/HelloMessageEncoderTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/encode/HelloMessageEncoderTest.java @@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test; import org.mockito.InOrder; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -31,6 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; +import static org.neo4j.driver.Values.NULL; import static org.neo4j.driver.internal.messaging.request.PullAllMessage.PULL_ALL; import static org.neo4j.driver.Values.value; @@ -46,13 +48,35 @@ void shouldEncodeHelloMessage() throws Exception authToken.put( "username", value( "bob" ) ); authToken.put( "password", value( "secret" ) ); - encoder.encode( new HelloMessage( "MyDriver", authToken ), packer ); + encoder.encode( new HelloMessage( "MyDriver", authToken, null ), packer ); InOrder order = inOrder( packer ); order.verify( packer ).packStructHeader( 1, HelloMessage.SIGNATURE ); Map expectedMetadata = new HashMap<>( authToken ); expectedMetadata.put( "user_agent", value( "MyDriver" ) ); + expectedMetadata.put( "routing", NULL ); + order.verify( packer ).pack( expectedMetadata ); + } + + @Test + void shouldEncodeHelloMessageWithRoutingContext() throws Exception + { + Map authToken = new HashMap<>(); + authToken.put( "username", value( "bob" ) ); + authToken.put( "password", value( "secret" ) ); + + Map routingContext = new HashMap<>(); + routingContext.put( "policy", "eu-fast" ); + + encoder.encode( new HelloMessage( "MyDriver", authToken, routingContext ), packer ); + + InOrder order = inOrder( packer ); + order.verify( packer ).packStructHeader( 1, HelloMessage.SIGNATURE ); + + Map expectedMetadata = new HashMap<>( authToken ); + expectedMetadata.put( "user_agent", value( "MyDriver" ) ); + expectedMetadata.put( "routing", value( routingContext ) ); order.verify( packer ).pack( expectedMetadata ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/request/HelloMessageTest.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/request/HelloMessageTest.java index 99ffb457e1..8d8c859dea 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/request/HelloMessageTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/request/HelloMessageTest.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -29,6 +30,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.neo4j.driver.Values.NULL; import static org.neo4j.driver.internal.security.InternalAuthToken.CREDENTIALS_KEY; import static org.neo4j.driver.internal.security.InternalAuthToken.PRINCIPAL_KEY; import static org.neo4j.driver.Values.value; @@ -42,13 +44,34 @@ void shouldHaveCorrectMetadata() authToken.put( "user", value( "Alice" ) ); authToken.put( "credentials", value( "SecretPassword" ) ); - HelloMessage message = new HelloMessage( "MyDriver/1.0.2", authToken ); + HelloMessage message = new HelloMessage( "MyDriver/1.0.2", authToken, Collections.emptyMap() ); Map expectedMetadata = new HashMap<>( authToken ); expectedMetadata.put( "user_agent", value( "MyDriver/1.0.2" ) ); + expectedMetadata.put( "routing", value ( Collections.emptyMap() ) ); assertEquals( expectedMetadata, message.metadata() ); } + @Test + void shouldHaveCorrectRoutingContext() + { + Map authToken = new HashMap<>(); + authToken.put( "user", value( "Alice" ) ); + authToken.put( "credentials", value( "SecretPassword" ) ); + + Map routingContext = new HashMap<>(); + routingContext.put( "region", "China" ); + routingContext.put( "speed", "Slow" ); + + HelloMessage message = new HelloMessage( "MyDriver/1.0.2", authToken, routingContext ); + + Map expectedMetadata = new HashMap<>( authToken ); + expectedMetadata.put( "user_agent", value( "MyDriver/1.0.2" ) ); + expectedMetadata.put( "routing", value( routingContext ) ); + assertEquals( expectedMetadata, message.metadata() ); + } + + @Test void shouldNotExposeCredentialsInToString() { @@ -56,7 +79,7 @@ void shouldNotExposeCredentialsInToString() authToken.put( PRINCIPAL_KEY, value( "Alice" ) ); authToken.put( CREDENTIALS_KEY, value( "SecretPassword" ) ); - HelloMessage message = new HelloMessage( "MyDriver/1.0.2", authToken ); + HelloMessage message = new HelloMessage( "MyDriver/1.0.2", authToken, Collections.emptyMap() ); assertThat( message.toString(), not( containsString( "SecretPassword" ) ) ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java index 70741f8570..5abaa7f5ad 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java @@ -42,6 +42,7 @@ import org.neo4j.driver.internal.async.UnmanagedTransaction; import org.neo4j.driver.internal.async.connection.ChannelAttributes; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.cursor.AsyncResultCursor; import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; import org.neo4j.driver.internal.handlers.CommitTxResponseHandler; @@ -117,7 +118,7 @@ void shouldInitializeChannel() { ChannelPromise promise = channel.newPromise(); - protocol.initializeChannel( "MyDriver/5.3", dummyAuthToken(), promise ); + protocol.initializeChannel( "MyDriver/5.3", dummyAuthToken(), RoutingContext.EMPTY, promise ); assertThat( channel.outboundMessages(), hasSize( 1 ) ); assertThat( channel.outboundMessages().poll(), instanceOf( InitMessage.class ) ); @@ -135,7 +136,7 @@ void shouldFailToInitializeChannelWhenErrorIsReceived() { ChannelPromise promise = channel.newPromise(); - protocol.initializeChannel( "MyDriver/3.1", dummyAuthToken(), promise ); + protocol.initializeChannel( "MyDriver/3.1", dummyAuthToken(), RoutingContext.EMPTY, promise ); assertThat( channel.outboundMessages(), hasSize( 1 ) ); assertThat( channel.outboundMessages().poll(), instanceOf( InitMessage.class ) ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1Test.java index 4d6b197924..fd32269853 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1Test.java @@ -19,6 +19,7 @@ package org.neo4j.driver.internal.messaging.v1; import java.time.LocalDateTime; +import java.util.Collections; import java.util.stream.Stream; import org.neo4j.driver.internal.util.messaging.AbstractMessageWriterTestBase; @@ -69,7 +70,7 @@ protected Stream unsupportedMessages() new RunMessage( "RETURN $here", singletonMap( "now", point( 42, 1, 1 ) ) ), // Bolt V3 messages - new HelloMessage( "Driver/2.3.4", emptyMap() ), + new HelloMessage( "Driver/2.3.4", emptyMap(), Collections.emptyMap() ), GOODBYE, COMMIT, ROLLBACK diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2Test.java index 6fab7be143..b5b02381dc 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2Test.java @@ -19,6 +19,7 @@ package org.neo4j.driver.internal.messaging.v2; import java.time.LocalDateTime; +import java.util.Collections; import java.util.stream.Stream; import org.neo4j.driver.internal.util.messaging.AbstractMessageWriterTestBase; @@ -66,7 +67,7 @@ protected Stream supportedMessages() protected Stream unsupportedMessages() { return Stream.of( - new HelloMessage( "JavaDriver/1.1.0", emptyMap() ), + new HelloMessage( "JavaDriver/1.1.0", emptyMap(), Collections.emptyMap() ), GOODBYE, COMMIT, ROLLBACK diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java index 916700cf2c..c043c70480 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java @@ -45,6 +45,7 @@ import org.neo4j.driver.internal.async.UnmanagedTransaction; import org.neo4j.driver.internal.async.connection.ChannelAttributes; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.cursor.AsyncResultCursor; import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; import org.neo4j.driver.internal.handlers.CommitTxResponseHandler; @@ -139,7 +140,7 @@ void shouldInitializeChannel() { ChannelPromise promise = channel.newPromise(); - protocol.initializeChannel( "MyDriver/0.0.1", dummyAuthToken(), promise ); + protocol.initializeChannel( "MyDriver/0.0.1", dummyAuthToken(), RoutingContext.EMPTY, promise ); assertThat( channel.outboundMessages(), hasSize( 1 ) ); assertThat( channel.outboundMessages().poll(), instanceOf( HelloMessage.class ) ); @@ -171,7 +172,7 @@ void shouldFailToInitializeChannelWhenErrorIsReceived() { ChannelPromise promise = channel.newPromise(); - protocol.initializeChannel( "MyDriver/2.2.1", dummyAuthToken(), promise ); + protocol.initializeChannel( "MyDriver/2.2.1", dummyAuthToken(), RoutingContext.EMPTY, promise ); assertThat( channel.outboundMessages(), hasSize( 1 ) ); assertThat( channel.outboundMessages().poll(), instanceOf( HelloMessage.class ) ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageWriterV3Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageWriterV3Test.java index a3650c2503..678aff290a 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageWriterV3Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageWriterV3Test.java @@ -19,6 +19,7 @@ package org.neo4j.driver.internal.messaging.v3; import java.time.ZonedDateTime; +import java.util.Collections; import java.util.stream.Stream; import org.neo4j.driver.Query; @@ -64,7 +65,7 @@ protected Stream supportedMessages() { return Stream.of( // Bolt V3 messages - new HelloMessage( "MyDriver/1.2.3", ((InternalAuthToken) basic( "neo4j", "neo4j" )).toMap() ), + new HelloMessage( "MyDriver/1.2.3", ((InternalAuthToken) basic( "neo4j", "neo4j" )).toMap(), Collections.emptyMap() ), GOODBYE, new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), READ, defaultDatabase() ), new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), WRITE, defaultDatabase() ), diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageWriterV4Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageWriterV4Test.java index ea44378b60..0df66da9c4 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageWriterV4Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageWriterV4Test.java @@ -19,6 +19,7 @@ package org.neo4j.driver.internal.messaging.v4; import java.time.ZonedDateTime; +import java.util.Collections; import java.util.stream.Stream; import org.neo4j.driver.Query; @@ -72,7 +73,7 @@ protected Stream supportedMessages() new DiscardMessage( 300, 400 ), // Bolt V3 messages - new HelloMessage( "MyDriver/1.2.3", ((InternalAuthToken) basic( "neo4j", "neo4j" )).toMap() ), + new HelloMessage( "MyDriver/1.2.3", ((InternalAuthToken) basic( "neo4j", "neo4j" )).toMap(), Collections.emptyMap() ), GOODBYE, new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), READ, defaultDatabase() ), new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), WRITE, database( "foo" ) ), diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/FailingConnectionDriverFactory.java b/driver/src/test/java/org/neo4j/driver/internal/util/FailingConnectionDriverFactory.java index 86574cbf66..45cf13a57f 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/FailingConnectionDriverFactory.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/FailingConnectionDriverFactory.java @@ -27,6 +27,7 @@ import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.DriverFactory; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.messaging.BoltProtocol; import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.metrics.MetricsProvider; @@ -43,9 +44,11 @@ public class FailingConnectionDriverFactory extends DriverFactory @Override protected ConnectionPool createConnectionPool( AuthToken authToken, SecurityPlan securityPlan, Bootstrap bootstrap, - MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup ) + MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup, + RoutingContext routingContext ) { - ConnectionPool pool = super.createConnectionPool( authToken, securityPlan, bootstrap, metricsProvider, config, ownsEventLoopGroup ); + ConnectionPool pool = super.createConnectionPool( authToken, securityPlan, bootstrap, metricsProvider, config, + ownsEventLoopGroup, routingContext ); return new ConnectionPoolWithFailingConnections( pool, nextRunFailure ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/MessageRecordingDriverFactory.java b/driver/src/test/java/org/neo4j/driver/internal/util/MessageRecordingDriverFactory.java index 9db50f7585..aac6f00d6f 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/MessageRecordingDriverFactory.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/MessageRecordingDriverFactory.java @@ -35,6 +35,7 @@ import org.neo4j.driver.internal.async.connection.ChannelPipelineBuilder; import org.neo4j.driver.internal.async.connection.ChannelPipelineBuilderImpl; import org.neo4j.driver.internal.async.outbound.OutboundMessageHandler; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.messaging.MessageFormat; import org.neo4j.driver.internal.security.SecurityPlan; @@ -51,10 +52,11 @@ public Map> getMessagesByChannel() } @Override - protected ChannelConnector createConnector( ConnectionSettings settings, SecurityPlan securityPlan, Config config, Clock clock ) + protected ChannelConnector createConnector( ConnectionSettings settings, SecurityPlan securityPlan, Config config, Clock clock, + RoutingContext routingContext ) { ChannelPipelineBuilder pipelineBuilder = new MessageRecordingChannelPipelineBuilder(); - return new ChannelConnectorImpl( settings, securityPlan, pipelineBuilder, config.logging(), clock ); + return new ChannelConnectorImpl( settings, securityPlan, pipelineBuilder, config.logging(), clock, routingContext ); } private class MessageRecordingChannelPipelineBuilder extends ChannelPipelineBuilderImpl diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactory.java b/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactory.java index 374cb46e15..81c8d36429 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactory.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactory.java @@ -29,6 +29,7 @@ import org.neo4j.driver.internal.ConnectionSettings; import org.neo4j.driver.internal.async.connection.BootstrapFactory; import org.neo4j.driver.internal.async.connection.ChannelConnector; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.metrics.MetricsProvider; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.spi.ConnectionPool; @@ -67,23 +68,23 @@ protected Bootstrap createBootstrap( int size ) @Override protected final ChannelConnector createConnector( ConnectionSettings settings, SecurityPlan securityPlan, - Config config, Clock clock ) + Config config, Clock clock, RoutingContext routingContext ) { - return createChannelTrackingConnector( createRealConnector( settings, securityPlan, config, clock ) ); + return createChannelTrackingConnector( createRealConnector( settings, securityPlan, config, clock, routingContext ) ); } @Override protected final ConnectionPool createConnectionPool( AuthToken authToken, SecurityPlan securityPlan, Bootstrap bootstrap, - MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup ) + MetricsProvider metricsProvider, Config config, boolean ownsEventLoopGroup, RoutingContext routingContext ) { - pool = super.createConnectionPool( authToken, securityPlan, bootstrap, metricsProvider, config, ownsEventLoopGroup ); + pool = super.createConnectionPool( authToken, securityPlan, bootstrap, metricsProvider, config, ownsEventLoopGroup, routingContext ); return pool; } protected ChannelConnector createRealConnector( ConnectionSettings settings, SecurityPlan securityPlan, - Config config, Clock clock ) + Config config, Clock clock, RoutingContext routingContext ) { - return super.createConnector( settings, securityPlan, config, clock ); + return super.createConnector( settings, securityPlan, config, clock, routingContext ); } private ChannelTrackingConnector createChannelTrackingConnector( ChannelConnector connector ) diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactoryWithFailingMessageFormat.java b/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactoryWithFailingMessageFormat.java index 5b9f063e6a..c6bfe616bf 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactoryWithFailingMessageFormat.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/io/ChannelTrackingDriverFactoryWithFailingMessageFormat.java @@ -21,6 +21,7 @@ import org.neo4j.driver.internal.ConnectionSettings; import org.neo4j.driver.internal.async.connection.ChannelConnector; import org.neo4j.driver.internal.async.connection.ChannelConnectorImpl; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.util.Clock; import org.neo4j.driver.internal.util.FailingMessageFormat; @@ -36,9 +37,10 @@ public ChannelTrackingDriverFactoryWithFailingMessageFormat( Clock clock ) } @Override - protected ChannelConnector createRealConnector( ConnectionSettings settings, SecurityPlan securityPlan, Config config, Clock clock ) + protected ChannelConnector createRealConnector( ConnectionSettings settings, SecurityPlan securityPlan, + Config config, Clock clock, RoutingContext routingContext ) { - return new ChannelConnectorImpl( settings, securityPlan, pipelineBuilder, config.logging(), clock ); + return new ChannelConnectorImpl( settings, securityPlan, pipelineBuilder, config.logging(), clock, routingContext ); } public FailingMessageFormat getFailingMessageFormat() diff --git a/driver/src/test/resources/empty_routing_context_in_hello_neo4j.script b/driver/src/test/resources/empty_routing_context_in_hello_neo4j.script new file mode 100644 index 0000000000..f23e4a20b6 --- /dev/null +++ b/driver/src/test/resources/empty_routing_context_in_hello_neo4j.script @@ -0,0 +1,17 @@ +!: BOLT 3 +!: AUTO RESET +!: AUTO GOODBYE + +C: HELLO {"scheme": "none", "user_agent": "neo4j-java/dev", "routing": { "address": "127.0.0.1:9001"} } +S: SUCCESS {"server": "Neo4j/3.5.0", "connection_id": "bolt-123456789"} +C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": { "address": "127.0.0.1:9001"} } {} + PULL_ALL +S: SUCCESS {"fields": ["ttl", "servers"]} + RECORD [9223372036854775807, [{"addresses": ["127.0.0.1:9001", "127.0.0.1:9002"],"role": "WRITE"}, {"addresses": ["127.0.0.1:9001", "127.0.0.1:9002"], "role": "READ"},{"addresses": ["127.0.0.1:9001", "127.0.0.1:9002"], "role": "ROUTE"}]] + SUCCESS {} +C: RUN "MATCH (n) RETURN n.name AS name" {} {} + PULL_ALL +S: SUCCESS {"fields": ["name"]} + RECORD ["Alice"] + RECORD ["Bob"] + SUCCESS {} diff --git a/driver/src/test/resources/hello_run_exit.script b/driver/src/test/resources/hello_run_exit.script index cb4fe6f95e..76f4004823 100644 --- a/driver/src/test/resources/hello_run_exit.script +++ b/driver/src/test/resources/hello_run_exit.script @@ -2,7 +2,7 @@ !: AUTO RESET !: AUTO GOODBYE -C: HELLO {"scheme": "none", "user_agent": "neo4j-java/dev"} +C: HELLO {"scheme": "none", "user_agent": "neo4j-java/dev", "routing": null} S: SUCCESS {"server": "Neo4j/3.5.0", "connection_id": "bolt-123456789"} C: RUN "MATCH (n) RETURN n.name" {} {} PULL_ALL diff --git a/driver/src/test/resources/hello_with_routing_context_bolt.script b/driver/src/test/resources/hello_with_routing_context_bolt.script new file mode 100644 index 0000000000..1f762f2913 --- /dev/null +++ b/driver/src/test/resources/hello_with_routing_context_bolt.script @@ -0,0 +1,12 @@ +!: BOLT 3 +!: AUTO RESET +!: AUTO GOODBYE + +C: HELLO {"scheme": "none", "user_agent": "neo4j-java/dev", "routing": null} +S: SUCCESS {"server": "Neo4j/3.5.0", "connection_id": "bolt-123456789"} +C: RUN "MATCH (n) RETURN n.name" {} {} + PULL_ALL +S: SUCCESS {"fields": ["n.name"]} + RECORD ["Foo"] + RECORD ["Bar"] + SUCCESS {} diff --git a/driver/src/test/resources/routing_context_in_hello_neo4j.script b/driver/src/test/resources/routing_context_in_hello_neo4j.script new file mode 100644 index 0000000000..57db70c3b7 --- /dev/null +++ b/driver/src/test/resources/routing_context_in_hello_neo4j.script @@ -0,0 +1,17 @@ +!: BOLT 3 +!: AUTO RESET +!: AUTO GOODBYE + +C: HELLO {"scheme": "none", "user_agent": "neo4j-java/dev", "routing": { "address": "127.0.0.1:9001", "region": "china", "policy": "my_policy"}} +S: SUCCESS {"server": "Neo4j/3.5.0", "connection_id": "bolt-123456789"} +C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": { "address": "127.0.0.1:9001", "policy": "my_policy", "region": "china"}} {} + PULL_ALL +S: SUCCESS {"fields": ["ttl", "servers"]} + RECORD [9223372036854775807, [{"addresses": ["127.0.0.1:9001", "127.0.0.1:9002"],"role": "WRITE"}, {"addresses": ["127.0.0.1:9001", "127.0.0.1:9002"], "role": "READ"},{"addresses": ["127.0.0.1:9001", "127.0.0.1:9002"], "role": "ROUTE"}]] + SUCCESS {} +C: RUN "MATCH (n) RETURN n.name AS name" {} {} + PULL_ALL +S: SUCCESS {"fields": ["name"]} + RECORD ["Alice"] + RECORD ["Bob"] + SUCCESS {} diff --git a/driver/src/test/resources/untrusted_server.script b/driver/src/test/resources/untrusted_server.script index ee7ced5708..49716f6784 100644 --- a/driver/src/test/resources/untrusted_server.script +++ b/driver/src/test/resources/untrusted_server.script @@ -2,5 +2,5 @@ !: AUTO RESET !: AUTO GOODBYE -C: HELLO {"scheme": "none", "user_agent": "neo4j-java/dev"} +C: HELLO {"scheme": "none", "user_agent": "neo4j-java/dev", "routing": null} S: SUCCESS {"server": "AgentSmith/0.0.1", "connection_id": "bolt-123456789"} From f3287ad6d4deb92485a4d8153de10ab9e0cedbcc Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Thu, 28 May 2020 15:50:14 +0200 Subject: [PATCH 2/4] Minor polishing. --- .../org/neo4j/driver/internal/cluster/RoutingContext.java | 5 +++-- .../driver/internal/messaging/v1/MessageWriterV1Test.java | 2 +- .../driver/internal/messaging/v2/MessageWriterV2Test.java | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java b/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java index 301d1d1c71..92c2624b5e 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java @@ -23,6 +23,7 @@ import java.util.Map; import org.neo4j.driver.internal.BoltServerAddress; +import org.neo4j.driver.internal.Scheme; import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; @@ -43,13 +44,13 @@ private RoutingContext() public RoutingContext( URI uri ) { - this.isServerRoutingEnabled = uri.getScheme().startsWith( "neo4j" ); + this.isServerRoutingEnabled = Scheme.isRoutingScheme( uri.getScheme() ); this.context = unmodifiableMap( parseParameters( uri ) ); } public boolean isDefined() { - return context.size() > 1; + return !context.isEmpty(); } public Map asMap() diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1Test.java index fd32269853..17a9a3c13c 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1Test.java @@ -70,7 +70,7 @@ protected Stream unsupportedMessages() new RunMessage( "RETURN $here", singletonMap( "now", point( 42, 1, 1 ) ) ), // Bolt V3 messages - new HelloMessage( "Driver/2.3.4", emptyMap(), Collections.emptyMap() ), + new HelloMessage( "Driver/2.3.4", emptyMap(), emptyMap() ), GOODBYE, COMMIT, ROLLBACK diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2Test.java index b5b02381dc..77d1995e56 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2Test.java @@ -67,7 +67,7 @@ protected Stream supportedMessages() protected Stream unsupportedMessages() { return Stream.of( - new HelloMessage( "JavaDriver/1.1.0", emptyMap(), Collections.emptyMap() ), + new HelloMessage( "JavaDriver/1.1.0", emptyMap(), emptyMap() ), GOODBYE, COMMIT, ROLLBACK From 26b30980f5d5c613f1a4174e8a9bea92146633e2 Mon Sep 17 00:00:00 2001 From: Gregory Woods Date: Thu, 28 May 2020 15:28:04 +0100 Subject: [PATCH 3/4] Use AuthToken instead of Map in BoltProtocol --- .../async/connection/ChannelConnectorImpl.java | 8 ++++---- .../connection/HandshakeCompletedListener.java | 5 +++-- .../MultiDatabasesRoutingProcedureRunner.java | 2 +- .../driver/internal/cluster/RoutingContext.java | 2 +- .../internal/cluster/RoutingProcedureRunner.java | 2 +- .../driver/internal/messaging/BoltProtocol.java | 3 ++- .../internal/messaging/v1/BoltProtocolV1.java | 6 ++++-- .../internal/messaging/v3/BoltProtocolV3.java | 10 +++++----- .../HandshakeCompletedListenerTest.java | 16 ++++++++-------- ...MultiDatabasesRoutingProcedureRunnerTest.java | 2 +- .../internal/cluster/RoutingContextTest.java | 14 +++++++------- .../cluster/RoutingProcedureRunnerTest.java | 2 +- .../messaging/v1/BoltProtocolV1Test.java | 9 ++++----- .../messaging/v3/BoltProtocolV3Test.java | 10 +++++----- 14 files changed, 47 insertions(+), 44 deletions(-) diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImpl.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImpl.java index 4674a6c3f8..e53840973f 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImpl.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelConnectorImpl.java @@ -45,7 +45,7 @@ public class ChannelConnectorImpl implements ChannelConnector { private final String userAgent; - private final Map authToken; + private final AuthToken authToken; private final RoutingContext routingContext; private final SecurityPlan securityPlan; private final ChannelPipelineBuilder pipelineBuilder; @@ -63,7 +63,7 @@ public ChannelConnectorImpl( ConnectionSettings connectionSettings, SecurityPlan ChannelPipelineBuilder pipelineBuilder, Logging logging, Clock clock, RoutingContext routingContext ) { this.userAgent = connectionSettings.userAgent(); - this.authToken = tokenAsMap( connectionSettings.authToken() ); + this.authToken = requireValidAuthToken( connectionSettings.authToken() ); this.routingContext = routingContext; this.connectTimeoutMillis = connectionSettings.connectTimeoutMillis(); this.securityPlan = requireNonNull( securityPlan ); @@ -119,11 +119,11 @@ private void installHandshakeCompletedListeners( ChannelPromise handshakeComplet handshakeCompleted.addListener( new HandshakeCompletedListener( userAgent, authToken, routingContext, connectionInitialized ) ); } - private static Map tokenAsMap( AuthToken token ) + private static AuthToken requireValidAuthToken( AuthToken token ) { if ( token instanceof InternalAuthToken ) { - return ((InternalAuthToken) token).toMap(); + return token; } else { diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListener.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListener.java index 83608c41e2..8a4ff5362c 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListener.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListener.java @@ -24,6 +24,7 @@ import java.util.Map; +import org.neo4j.driver.AuthToken; import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.messaging.BoltProtocol; import org.neo4j.driver.Value; @@ -33,11 +34,11 @@ public class HandshakeCompletedListener implements ChannelFutureListener { private final String userAgent; - private final Map authToken; + private final AuthToken authToken; private final RoutingContext routingContext; private final ChannelPromise connectionInitializedPromise; - public HandshakeCompletedListener( String userAgent, Map authToken, + public HandshakeCompletedListener( String userAgent, AuthToken authToken, RoutingContext routingContext, ChannelPromise connectionInitializedPromise ) { this.userAgent = requireNonNull( userAgent ); diff --git a/driver/src/main/java/org/neo4j/driver/internal/cluster/MultiDatabasesRoutingProcedureRunner.java b/driver/src/main/java/org/neo4j/driver/internal/cluster/MultiDatabasesRoutingProcedureRunner.java index b5495c2da7..83937076e9 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/cluster/MultiDatabasesRoutingProcedureRunner.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cluster/MultiDatabasesRoutingProcedureRunner.java @@ -54,7 +54,7 @@ BookmarkHolder bookmarkHolder( Bookmark bookmark ) Query procedureQuery(ServerVersion serverVersion, DatabaseName databaseName ) { HashMap map = new HashMap<>(); - map.put( ROUTING_CONTEXT, value( context.asMap() ) ); + map.put( ROUTING_CONTEXT, value( context.toMap() ) ); map.put( DATABASE_NAME, value( (Object) databaseName.databaseName().orElse( null ) ) ); return new Query( MULTI_DB_GET_ROUTING_TABLE, value( map ) ); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java b/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java index 92c2624b5e..66f18c9f76 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java @@ -53,7 +53,7 @@ public boolean isDefined() return !context.isEmpty(); } - public Map asMap() + public Map toMap() { return context; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingProcedureRunner.java b/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingProcedureRunner.java index cf05f0a02c..1747006c40 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingProcedureRunner.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingProcedureRunner.java @@ -76,7 +76,7 @@ Query procedureQuery(ServerVersion serverVersion, DatabaseName databaseName ) "Refreshing routing table for multi-databases is not supported in server version lower than 4.0. " + "Current server version: %s. Database name: '%s'", serverVersion, databaseName.description() ) ); } - return new Query( GET_ROUTING_TABLE, parameters( ROUTING_CONTEXT, context.asMap() ) ); + return new Query( GET_ROUTING_TABLE, parameters( ROUTING_CONTEXT, context.toMap() ) ); } BookmarkHolder bookmarkHolder( Bookmark ignored ) diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java index 092803ee6c..713123ffc9 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.concurrent.CompletionStage; +import org.neo4j.driver.AuthToken; import org.neo4j.driver.Bookmark; import org.neo4j.driver.Query; import org.neo4j.driver.Session; @@ -62,7 +63,7 @@ public interface BoltProtocol * @param routingContext the configured routing context * @param channelInitializedPromise the promise to be notified when initialization is completed. */ - void initializeChannel( String userAgent, Map authToken, RoutingContext routingContext, ChannelPromise channelInitializedPromise ); + void initializeChannel( String userAgent, AuthToken authToken, RoutingContext routingContext, ChannelPromise channelInitializedPromise ); /** * Prepare to close channel before it is closed. diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java index 0c77b3c245..289a4bace7 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java @@ -26,6 +26,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import org.neo4j.driver.AuthToken; import org.neo4j.driver.Bookmark; import org.neo4j.driver.Query; import org.neo4j.driver.TransactionConfig; @@ -53,6 +54,7 @@ import org.neo4j.driver.internal.messaging.request.InitMessage; import org.neo4j.driver.internal.messaging.request.PullAllMessage; import org.neo4j.driver.internal.messaging.request.RunMessage; +import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; import org.neo4j.driver.internal.util.Futures; @@ -85,12 +87,12 @@ public MessageFormat createMessageFormat() } @Override - public void initializeChannel( String userAgent, Map authToken, RoutingContext routingContext, + public void initializeChannel( String userAgent, AuthToken authToken, RoutingContext routingContext, ChannelPromise channelInitializedPromise ) { Channel channel = channelInitializedPromise.channel(); - InitMessage message = new InitMessage( userAgent, authToken ); + InitMessage message = new InitMessage( userAgent, ((InternalAuthToken) authToken).toMap() ); InitResponseHandler handler = new InitResponseHandler( channelInitializedPromise ); messageDispatcher( channel ).enqueue( handler ); diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3.java index 0f3b4ce11a..dbcbc75a02 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3.java @@ -21,18 +21,17 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelPromise; -import java.util.Collections; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import org.neo4j.driver.AuthToken; import org.neo4j.driver.Bookmark; import org.neo4j.driver.Query; import org.neo4j.driver.TransactionConfig; import org.neo4j.driver.Value; import org.neo4j.driver.internal.BookmarkHolder; import org.neo4j.driver.internal.DatabaseName; -import org.neo4j.driver.internal.InternalBookmark; import org.neo4j.driver.internal.async.UnmanagedTransaction; import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.cursor.AsyncResultCursorOnlyFactory; @@ -51,6 +50,7 @@ import org.neo4j.driver.internal.messaging.request.GoodbyeMessage; import org.neo4j.driver.internal.messaging.request.HelloMessage; import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; +import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.util.Futures; import org.neo4j.driver.internal.util.MetadataExtractor; @@ -78,18 +78,18 @@ public MessageFormat createMessageFormat() } @Override - public void initializeChannel( String userAgent, Map authToken, RoutingContext routingContext, ChannelPromise channelInitializedPromise ) + public void initializeChannel( String userAgent, AuthToken authToken, RoutingContext routingContext, ChannelPromise channelInitializedPromise ) { Channel channel = channelInitializedPromise.channel(); HelloMessage message; if ( routingContext.isServerRoutingEnabled() ) { - message = new HelloMessage( userAgent, authToken, routingContext.asMap() ); + message = new HelloMessage( userAgent, ( ( InternalAuthToken ) authToken ).toMap(), routingContext.toMap() ); } else { - message = new HelloMessage( userAgent, authToken, null ); + message = new HelloMessage( userAgent, ( ( InternalAuthToken ) authToken ).toMap(), null ); } HelloResponseHandler handler = new HelloResponseHandler( channelInitializedPromise, version() ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java index dbb5fccab5..2f92487920 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java @@ -28,6 +28,8 @@ import java.util.HashMap; import java.util.Map; +import org.neo4j.driver.AuthToken; +import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Value; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; import org.neo4j.driver.internal.cluster.RoutingContext; @@ -40,6 +42,7 @@ 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.security.InternalAuthToken; import org.neo4j.driver.internal.spi.ResponseHandler; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -85,19 +88,19 @@ void shouldFailConnectionInitializedPromiseWhenHandshakeFails() @Test void shouldWriteInitializationMessageInBoltV1WhenHandshakeCompleted() { - testWritingOfInitializationMessage( BoltProtocolV1.VERSION, new InitMessage( USER_AGENT, authToken() ), InitResponseHandler.class ); + testWritingOfInitializationMessage( BoltProtocolV1.VERSION, new InitMessage( USER_AGENT, authToken().toMap() ), InitResponseHandler.class ); } @Test void shouldWriteInitializationMessageInBoltV2WhenHandshakeCompleted() { - testWritingOfInitializationMessage( BoltProtocolV2.VERSION, new InitMessage( USER_AGENT, authToken() ), InitResponseHandler.class ); + testWritingOfInitializationMessage( BoltProtocolV2.VERSION, new InitMessage( USER_AGENT, authToken().toMap() ), InitResponseHandler.class ); } @Test void shouldWriteInitializationMessageInBoltV3WhenHandshakeCompleted() { - testWritingOfInitializationMessage( BoltProtocolV3.VERSION, new HelloMessage( USER_AGENT, authToken(), Collections.emptyMap() ), HelloResponseHandler.class ); + testWritingOfInitializationMessage( BoltProtocolV3.VERSION, new HelloMessage( USER_AGENT, authToken().toMap(), Collections.emptyMap() ), HelloResponseHandler.class ); } private void testWritingOfInitializationMessage( BoltProtocolVersion protocolVersion, Message expectedMessage, Class handlerType ) @@ -121,11 +124,8 @@ private void testWritingOfInitializationMessage( BoltProtocolVersion protocolVer assertEquals( expectedMessage, outboundMessage ); } - private static Map authToken() + private static InternalAuthToken authToken() { - Map authToken = new HashMap<>(); - authToken.put( "username", value( "neo4j" ) ); - authToken.put( "password", value( "secret" ) ); - return authToken; + return (InternalAuthToken) AuthTokens.basic( "neo4j", "secret" ); } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/cluster/MultiDatabasesRoutingProcedureRunnerTest.java b/driver/src/test/java/org/neo4j/driver/internal/cluster/MultiDatabasesRoutingProcedureRunnerTest.java index cc7b1710e0..9a00a395f8 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/cluster/MultiDatabasesRoutingProcedureRunnerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/cluster/MultiDatabasesRoutingProcedureRunnerTest.java @@ -90,7 +90,7 @@ void shouldCallGetRoutingTableWithParamOnSystemDatabaseForDatabase( String db ) assertThat( runner.connection.databaseName(), equalTo( systemDatabase() ) ); assertThat( runner.connection.mode(), equalTo( AccessMode.READ ) ); - Query query = generateMultiDatabaseRoutingQuery( context.asMap(), db ); + Query query = generateMultiDatabaseRoutingQuery( context.toMap(), db ); assertThat( response.procedure(), equalTo(query) ); assertThat( runner.procedure, equalTo(query) ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/cluster/RoutingContextTest.java b/driver/src/test/java/org/neo4j/driver/internal/cluster/RoutingContextTest.java index 22eb2ae9b8..742e5cbc5d 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/cluster/RoutingContextTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/cluster/RoutingContextTest.java @@ -41,7 +41,7 @@ void emptyContextIsNotDefined() @Test void emptyContextInEmptyMap() { - assertTrue( RoutingContext.EMPTY.asMap().isEmpty() ); + assertTrue( RoutingContext.EMPTY.toMap().isEmpty() ); } @Test @@ -69,7 +69,7 @@ void uriWithQueryIsParsed() expectedMap.put( "key2", "value2" ); expectedMap.put( "key3", "value3" ); expectedMap.put( "address", "localhost:7687" ); - assertEquals( expectedMap, context.asMap() ); + assertEquals( expectedMap, context.toMap() ); } @Test @@ -124,10 +124,10 @@ void mapRepresentationIsUnmodifiable() expectedMap.put( "key1", "value1" ); expectedMap.put( "address", "localhost:7687" ); - assertEquals( expectedMap, context.asMap() ); + assertEquals( expectedMap, context.toMap() ); - assertThrows( UnsupportedOperationException.class, () -> context.asMap().put( "key2", "value2" ) ); - assertEquals( expectedMap, context.asMap() ); + assertThrows( UnsupportedOperationException.class, () -> context.toMap().put( "key2", "value2" ) ); + assertEquals( expectedMap, context.toMap() ); } @Test @@ -136,7 +136,7 @@ void populateAddressWithDefaultPort() URI uri = URI.create( "neo4j://localhost/" ); RoutingContext context = new RoutingContext( uri ); - assertEquals( singletonMap( "address", "localhost:7687" ), context.asMap() ); + assertEquals( singletonMap( "address", "localhost:7687" ), context.toMap() ); } @Test @@ -161,6 +161,6 @@ private static void testEmptyRoutingContext( URI uri ) expectedMap.put( "address", "localhost:7687" ); assertFalse( context.isDefined() ); - assertEquals( singletonMap( "address", "localhost:7687" ), context.asMap() ); + assertEquals( singletonMap( "address", "localhost:7687" ), context.toMap() ); } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/cluster/RoutingProcedureRunnerTest.java b/driver/src/test/java/org/neo4j/driver/internal/cluster/RoutingProcedureRunnerTest.java index decbdbee06..b0100ecb54 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/cluster/RoutingProcedureRunnerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/cluster/RoutingProcedureRunnerTest.java @@ -89,7 +89,7 @@ void shouldCallGetRoutingTableWithParam() assertThat( runner.connection.databaseName(), equalTo( defaultDatabase() ) ); assertThat( runner.connection.mode(), equalTo( AccessMode.WRITE ) ); - Query query = generateRoutingQuery( context.asMap() ); + Query query = generateRoutingQuery( context.toMap() ); assertThat( response.procedure(), equalTo(query) ); assertThat( runner.procedure, equalTo(query) ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java index 5abaa7f5ad..8685a07e66 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java @@ -31,6 +31,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Bookmark; import org.neo4j.driver.Logging; import org.neo4j.driver.Query; @@ -55,6 +56,7 @@ import org.neo4j.driver.internal.messaging.request.InitMessage; import org.neo4j.driver.internal.messaging.request.PullAllMessage; import org.neo4j.driver.internal.messaging.request.RunMessage; +import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; import org.neo4j.driver.internal.util.Futures; @@ -380,11 +382,8 @@ private static ResponseHandler verifyRunInvoked( Connection connection ) return runHandlerCaptor.getValue(); } - private static Map dummyAuthToken() + private static InternalAuthToken dummyAuthToken() { - Map authToken = new HashMap<>(); - authToken.put( "username", value( "hello" ) ); - authToken.put( "password", value( "world" ) ); - return authToken; + return (InternalAuthToken) AuthTokens.basic( "hello", "world" ); } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java index c043c70480..96dda7eecc 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java @@ -33,6 +33,8 @@ import java.util.concurrent.CompletionStage; import org.neo4j.driver.AccessMode; +import org.neo4j.driver.AuthToken; +import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Bookmark; import org.neo4j.driver.Logging; import org.neo4j.driver.Query; @@ -62,6 +64,7 @@ import org.neo4j.driver.internal.messaging.request.PullAllMessage; import org.neo4j.driver.internal.messaging.request.RollbackMessage; import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; +import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; @@ -452,12 +455,9 @@ protected void testFailedRunInAutoCommitTxWithWaitingForResponse( Bookmark bookm assertNotNull( cursorFuture.get() ); } - private static Map dummyAuthToken() + private static InternalAuthToken dummyAuthToken() { - Map authToken = new HashMap<>(); - authToken.put( "username", value( "hello" ) ); - authToken.put( "password", value( "world" ) ); - return authToken; + return (InternalAuthToken) AuthTokens.basic( "hello", "world"); } private static ResponseHandlers verifyRunInvoked( Connection connection, boolean session, Bookmark bookmark, TransactionConfig config, AccessMode mode ) From 43636950c794ed8bfc504e18ba0ef5b15dbbe6d7 Mon Sep 17 00:00:00 2001 From: Gregory Woods Date: Fri, 29 May 2020 08:49:29 +0100 Subject: [PATCH 4/4] change RoutingContext.isDefined() to ignore address in context --- .../java/org/neo4j/driver/internal/cluster/RoutingContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java b/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java index 66f18c9f76..98b305c0b4 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingContext.java @@ -50,7 +50,7 @@ public RoutingContext( URI uri ) public boolean isDefined() { - return !context.isEmpty(); + return context.size() > 1; } public Map toMap()