diff --git a/driver/src/main/java/org/neo4j/driver/Config.java b/driver/src/main/java/org/neo4j/driver/Config.java index 1f1b2b3b16..ef5094441f 100644 --- a/driver/src/main/java/org/neo4j/driver/Config.java +++ b/driver/src/main/java/org/neo4j/driver/Config.java @@ -24,12 +24,14 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; +import org.neo4j.driver.internal.DefaultDomainNameResolver; import org.neo4j.driver.internal.RevocationStrategy; import org.neo4j.driver.internal.SecuritySettings; import org.neo4j.driver.internal.async.pool.PoolSettings; import org.neo4j.driver.internal.cluster.RoutingSettings; import org.neo4j.driver.internal.handlers.pulln.FetchSizeUtil; import org.neo4j.driver.internal.retry.RetrySettings; +import org.neo4j.driver.net.DomainNameResolver; import org.neo4j.driver.net.ServerAddressResolver; import org.neo4j.driver.util.Immutable; @@ -88,6 +90,7 @@ public class Config private final int connectionTimeoutMillis; private final RetrySettings retrySettings; private final ServerAddressResolver resolver; + private final DomainNameResolver domainNameResolver; private final boolean isMetricsEnabled; private final int eventLoopThreads; @@ -112,6 +115,7 @@ private Config( ConfigBuilder builder ) this.routingTablePurgeDelayMillis = builder.routingTablePurgeDelayMillis; this.retrySettings = builder.retrySettings; this.resolver = builder.resolver; + this.domainNameResolver = builder.domainNameResolver; this.fetchSize = builder.fetchSize; this.eventLoopThreads = builder.eventLoopThreads; @@ -202,6 +206,16 @@ public ServerAddressResolver resolver() return resolver; } + /** + * Domain name resolver. + * + * @return the resolver to use. + */ + public DomainNameResolver domainNameResolver() + { + return domainNameResolver; + } + /** * Start building a {@link Config} object using a newly created builder. * @@ -283,6 +297,7 @@ public static class ConfigBuilder private int connectionTimeoutMillis = (int) TimeUnit.SECONDS.toMillis( 30 ); private RetrySettings retrySettings = RetrySettings.DEFAULT; private ServerAddressResolver resolver; + private DomainNameResolver domainNameResolver = new DefaultDomainNameResolver(); private boolean isMetricsEnabled = false; private long fetchSize = FetchSizeUtil.DEFAULT_FETCH_SIZE; private int eventLoopThreads = 0; @@ -695,8 +710,23 @@ public ConfigBuilder withResolver( ServerAddressResolver resolver ) return this; } + /** + * Specify a custom domain name resolver used by the driver to resolve domain names. + *

+ * Default implementation uses the {@link InetAddress#getAllByName(String)}. + * + * @param domainNameResolver the resolver to use. + * @return this builder. + */ + public ConfigBuilder withDomainNameResolver( DomainNameResolver domainNameResolver ) + { + this.domainNameResolver = Objects.requireNonNull( domainNameResolver, "domainNameResolver" ); + return this; + } + /** * Enable driver metrics. The metrics can be obtained afterwards via {@link Driver#metrics()}. + * * @return this builder. */ public ConfigBuilder withDriverMetrics() diff --git a/driver/src/main/java/org/neo4j/driver/internal/BoltServerAddress.java b/driver/src/main/java/org/neo4j/driver/internal/BoltServerAddress.java index e851ba20eb..40d7b1759f 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/BoltServerAddress.java +++ b/driver/src/main/java/org/neo4j/driver/internal/BoltServerAddress.java @@ -22,15 +22,11 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.URI; -import java.net.UnknownHostException; -import java.util.List; import java.util.Objects; -import java.util.stream.Stream; import org.neo4j.driver.net.ServerAddress; import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.toList; /** * Holds a host and port pair that denotes a Bolt server address. @@ -44,8 +40,6 @@ public class BoltServerAddress implements ServerAddress private final int port; private final String stringValue; - private InetAddress resolved; - public BoltServerAddress( String address ) { this( uriFrom( address ) ); @@ -57,16 +51,10 @@ public BoltServerAddress( URI uri ) } public BoltServerAddress( String host, int port ) - { - this( host, null, port ); - } - - private BoltServerAddress( String host, InetAddress resolved, int port ) { this.host = requireNonNull( host, "host" ); - this.resolved = resolved; this.port = requireValidPort( port ); - this.stringValue = resolved != null ? String.format( "%s(%s):%d", host, resolved.getHostAddress(), port ) : String.format( "%s:%d", host, port ); + this.stringValue = String.format( "%s:%d", host, port ); } public static BoltServerAddress from( ServerAddress address ) @@ -112,33 +100,7 @@ public String toString() */ public SocketAddress toSocketAddress() { - return resolved == null ? new InetSocketAddress( host, port ) : new InetSocketAddress( resolved, port ); - } - - /** - * Resolve the host name down to an IP address - * - * @return a new address instance - * @throws UnknownHostException if no IP address for the host could be found - * @see InetAddress#getByName(String) - */ - public BoltServerAddress resolve() throws UnknownHostException - { - return new BoltServerAddress( host, InetAddress.getByName( host ), port ); - } - - /** - * Resolve the host name down to all IP addresses that can be resolved to - * - * @return an array of new address instances that holds resolved addresses - * @throws UnknownHostException if no IP address for the host could be found - * @see InetAddress#getAllByName(String) - */ - public List resolveAll() throws UnknownHostException - { - return Stream.of( InetAddress.getAllByName( host ) ) - .map( address -> new BoltServerAddress( host, address, port ) ) - .collect( toList() ); + return new InetSocketAddress( host, port ); } @Override @@ -153,11 +115,6 @@ public int port() return port; } - public boolean isResolved() - { - return resolved != null; - } - private static String hostFrom( URI uri ) { String host = uri.getHost(); diff --git a/driver/src/main/java/org/neo4j/driver/internal/DefaultDomainNameResolver.java b/driver/src/main/java/org/neo4j/driver/internal/DefaultDomainNameResolver.java new file mode 100644 index 0000000000..665a523bcf --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/DefaultDomainNameResolver.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import org.neo4j.driver.net.DomainNameResolver; + +public class DefaultDomainNameResolver implements DomainNameResolver +{ + @Override + public InetAddress[] resolve( String name ) throws UnknownHostException + { + return InetAddress.getAllByName( name ); + } +} 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 df6f919b52..1f9f115923 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java +++ b/driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java @@ -127,7 +127,7 @@ protected static MetricsProvider createDriverMetrics( Config config, Clock clock protected ChannelConnector createConnector( ConnectionSettings settings, SecurityPlan securityPlan, Config config, Clock clock, RoutingContext routingContext ) { - return new ChannelConnectorImpl( settings, securityPlan, config.logging(), clock, routingContext ); + return new ChannelConnectorImpl( settings, securityPlan, config.logging(), clock, routingContext, config.domainNameResolver() ); } private InternalDriver createDriver( URI uri, SecurityPlan securityPlan, BoltServerAddress address, ConnectionPool connectionPool, @@ -210,7 +210,7 @@ protected LoadBalancer createLoadBalancer( BoltServerAddress address, Connection LoadBalancingStrategy loadBalancingStrategy = new LeastConnectedLoadBalancingStrategy( connectionPool, config.logging() ); ServerAddressResolver resolver = createResolver( config ); return new LoadBalancer( address, routingSettings, connectionPool, eventExecutorGroup, createClock(), - config.logging(), loadBalancingStrategy, resolver ); + config.logging(), loadBalancingStrategy, resolver, config.domainNameResolver() ); } private static ServerAddressResolver createResolver( Config config ) 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 d927b8c9ec..339270fe11 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 @@ -24,9 +24,14 @@ import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; +import io.netty.resolver.AddressResolverGroup; -import java.util.Map; +import java.net.InetSocketAddress; +import org.neo4j.driver.AuthToken; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Logging; +import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.ConnectionSettings; import org.neo4j.driver.internal.async.inbound.ConnectTimeoutHandler; @@ -34,11 +39,7 @@ import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.security.SecurityPlan; import org.neo4j.driver.internal.util.Clock; -import org.neo4j.driver.AuthToken; -import org.neo4j.driver.AuthTokens; -import org.neo4j.driver.Logging; -import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.net.DomainNameResolver; import static java.util.Objects.requireNonNull; @@ -52,15 +53,17 @@ public class ChannelConnectorImpl implements ChannelConnector private final int connectTimeoutMillis; private final Logging logging; private final Clock clock; + private final AddressResolverGroup addressResolverGroup; public ChannelConnectorImpl( ConnectionSettings connectionSettings, SecurityPlan securityPlan, Logging logging, - Clock clock, RoutingContext routingContext ) + Clock clock, RoutingContext routingContext, DomainNameResolver domainNameResolver ) { - this( connectionSettings, securityPlan, new ChannelPipelineBuilderImpl(), logging, clock, routingContext ); + this( connectionSettings, securityPlan, new ChannelPipelineBuilderImpl(), logging, clock, routingContext, domainNameResolver ); } public ChannelConnectorImpl( ConnectionSettings connectionSettings, SecurityPlan securityPlan, - ChannelPipelineBuilder pipelineBuilder, Logging logging, Clock clock, RoutingContext routingContext ) + ChannelPipelineBuilder pipelineBuilder, Logging logging, Clock clock, RoutingContext routingContext, + DomainNameResolver domainNameResolver ) { this.userAgent = connectionSettings.userAgent(); this.authToken = requireValidAuthToken( connectionSettings.authToken() ); @@ -70,6 +73,7 @@ public ChannelConnectorImpl( ConnectionSettings connectionSettings, SecurityPlan this.pipelineBuilder = pipelineBuilder; this.logging = requireNonNull( logging ); this.clock = requireNonNull( clock ); + this.addressResolverGroup = new NettyDomainNameResolverGroup( requireNonNull( domainNameResolver ) ); } @Override @@ -77,6 +81,7 @@ public ChannelFuture connect( BoltServerAddress address, Bootstrap bootstrap ) { bootstrap.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeoutMillis ); bootstrap.handler( new NettyChannelInitializer( address, securityPlan, connectTimeoutMillis, clock, logging ) ); + bootstrap.resolver( addressResolverGroup ); ChannelFuture channelConnected = bootstrap.connect( address.toSocketAddress() ); diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/connection/NettyDomainNameResolver.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/NettyDomainNameResolver.java new file mode 100644 index 0000000000..d06bcdd0b2 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/NettyDomainNameResolver.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal.async.connection; + +import io.netty.resolver.InetNameResolver; +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.Promise; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.List; + +import org.neo4j.driver.net.DomainNameResolver; + +public class NettyDomainNameResolver extends InetNameResolver +{ + private final DomainNameResolver domainNameResolver; + + public NettyDomainNameResolver( EventExecutor executor, DomainNameResolver domainNameResolver ) + { + super( executor ); + this.domainNameResolver = domainNameResolver; + } + + @Override + protected void doResolve( String inetHost, Promise promise ) + { + try + { + promise.setSuccess( domainNameResolver.resolve( inetHost )[0] ); + } + catch ( UnknownHostException e ) + { + promise.setFailure( e ); + } + } + + @Override + protected void doResolveAll( String inetHost, Promise> promise ) + { + try + { + promise.setSuccess( Arrays.asList( domainNameResolver.resolve( inetHost ) ) ); + } + catch ( UnknownHostException e ) + { + promise.setFailure( e ); + } + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/connection/NettyDomainNameResolverGroup.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/NettyDomainNameResolverGroup.java new file mode 100644 index 0000000000..1ce5b15a50 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/NettyDomainNameResolverGroup.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.internal.async.connection; + +import io.netty.resolver.AddressResolver; +import io.netty.resolver.AddressResolverGroup; +import io.netty.util.concurrent.EventExecutor; + +import java.net.InetSocketAddress; + +import org.neo4j.driver.net.DomainNameResolver; + +public class NettyDomainNameResolverGroup extends AddressResolverGroup +{ + private final DomainNameResolver domainNameResolver; + + public NettyDomainNameResolverGroup( DomainNameResolver domainNameResolver ) + { + this.domainNameResolver = domainNameResolver; + } + + @Override + protected AddressResolver newResolver( EventExecutor executor ) throws Exception + { + return new NettyDomainNameResolver( executor, domainNameResolver ).asAddressResolver(); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/cluster/Rediscovery.java b/driver/src/main/java/org/neo4j/driver/internal/cluster/Rediscovery.java index c9288c54cd..82a062d7c9 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/cluster/Rediscovery.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cluster/Rediscovery.java @@ -18,6 +18,7 @@ */ package org.neo4j.driver.internal.cluster; +import java.net.UnknownHostException; import java.util.List; import java.util.concurrent.CompletionStage; @@ -29,5 +30,5 @@ public interface Rediscovery { CompletionStage lookupClusterComposition( RoutingTable routingTable, ConnectionPool connectionPool, Bookmark bookmark ); - List resolve(); + List resolve() throws UnknownHostException; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/cluster/RediscoveryImpl.java b/driver/src/main/java/org/neo4j/driver/internal/cluster/RediscoveryImpl.java index 777d97c213..db8224150c 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/cluster/RediscoveryImpl.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cluster/RediscoveryImpl.java @@ -20,15 +20,16 @@ import io.netty.util.concurrent.EventExecutorGroup; +import java.net.InetAddress; import java.net.UnknownHostException; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; import java.util.concurrent.TimeUnit; -import java.util.stream.Stream; import org.neo4j.driver.Bookmark; import org.neo4j.driver.Logger; @@ -40,12 +41,14 @@ import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ConnectionPool; import org.neo4j.driver.internal.util.Futures; +import org.neo4j.driver.net.DomainNameResolver; +import org.neo4j.driver.net.ServerAddress; import org.neo4j.driver.net.ServerAddressResolver; import static java.lang.String.format; import static java.util.Collections.emptySet; +import static java.util.Objects.requireNonNull; import static java.util.concurrent.CompletableFuture.completedFuture; -import static java.util.stream.Collectors.toList; import static org.neo4j.driver.internal.util.Futures.completedWithNull; import static org.neo4j.driver.internal.util.Futures.failedFuture; @@ -64,9 +67,10 @@ public class RediscoveryImpl implements Rediscovery private final ClusterCompositionProvider provider; private final ServerAddressResolver resolver; private final EventExecutorGroup eventExecutorGroup; + private final DomainNameResolver domainNameResolver; public RediscoveryImpl( BoltServerAddress initialRouter, RoutingSettings settings, ClusterCompositionProvider provider, - EventExecutorGroup eventExecutorGroup, ServerAddressResolver resolver, Logger logger ) + EventExecutorGroup eventExecutorGroup, ServerAddressResolver resolver, Logger logger, DomainNameResolver domainNameResolver ) { this.initialRouter = initialRouter; this.settings = settings; @@ -74,6 +78,7 @@ public RediscoveryImpl( BoltServerAddress initialRouter, RoutingSettings setting this.provider = provider; this.resolver = resolver; this.eventExecutorGroup = eventExecutorGroup; + this.domainNameResolver = requireNonNull( domainNameResolver ); } /** @@ -265,24 +270,17 @@ private ClusterComposition handleRoutingProcedureError( Throwable error, Routing } @Override - public List resolve() + public List resolve() throws UnknownHostException { - return resolver.resolve( initialRouter ) - .stream() - .map( BoltServerAddress::from ) - .collect( toList() ); // collect to list to preserve the order - } - - private Stream resolveAll( BoltServerAddress address ) - { - try + List resolvedAddresses = new LinkedList<>(); + for ( ServerAddress serverAddress : resolver.resolve( initialRouter ) ) { - return address.resolveAll().stream(); - } - catch ( UnknownHostException e ) - { - logger.error( "Failed to resolve address `" + address + "` to IPs due to error: " + e.getMessage(), e ); - return Stream.of( address ); + for ( InetAddress inetAddress : domainNameResolver.resolve( serverAddress.host() ) ) + { + resolvedAddresses.add( new BoltServerAddress( inetAddress.getHostAddress(), serverAddress.port() ) ); + } } + + return resolvedAddresses; } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancer.java b/driver/src/main/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancer.java index 34b4dce032..356ec5ce0a 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancer.java +++ b/driver/src/main/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancer.java @@ -47,9 +47,11 @@ import org.neo4j.driver.internal.spi.ConnectionProvider; import org.neo4j.driver.internal.util.Clock; import org.neo4j.driver.internal.util.Futures; +import org.neo4j.driver.net.DomainNameResolver; import org.neo4j.driver.net.ServerAddressResolver; import static java.lang.String.format; +import static java.util.Objects.requireNonNull; import static org.neo4j.driver.internal.async.ImmutableConnectionContext.simple; import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.supportsMultiDatabase; import static org.neo4j.driver.internal.util.Futures.completedWithNull; @@ -68,22 +70,24 @@ public class LoadBalancer implements ConnectionProvider private final Rediscovery rediscovery; public LoadBalancer( BoltServerAddress initialRouter, RoutingSettings settings, ConnectionPool connectionPool, - EventExecutorGroup eventExecutorGroup, Clock clock, Logging logging, - LoadBalancingStrategy loadBalancingStrategy, ServerAddressResolver resolver ) + EventExecutorGroup eventExecutorGroup, Clock clock, Logging logging, + LoadBalancingStrategy loadBalancingStrategy, ServerAddressResolver resolver, DomainNameResolver domainNameResolver ) { - this( connectionPool, createRediscovery( eventExecutorGroup, initialRouter, resolver, settings, clock, logging ), settings, loadBalancingStrategy, - eventExecutorGroup, clock, loadBalancerLogger( logging ) ); + this( connectionPool, createRediscovery( eventExecutorGroup, initialRouter, resolver, settings, clock, logging, requireNonNull( domainNameResolver ) ), + settings, + loadBalancingStrategy, + eventExecutorGroup, clock, loadBalancerLogger( logging ) ); } private LoadBalancer( ConnectionPool connectionPool, Rediscovery rediscovery, RoutingSettings settings, LoadBalancingStrategy loadBalancingStrategy, - EventExecutorGroup eventExecutorGroup, Clock clock, Logger log ) + EventExecutorGroup eventExecutorGroup, Clock clock, Logger log ) { this( connectionPool, createRoutingTables( connectionPool, rediscovery, settings, clock, log ), rediscovery, loadBalancingStrategy, eventExecutorGroup, - log ); + log ); } LoadBalancer( ConnectionPool connectionPool, RoutingTableRegistry routingTables, Rediscovery rediscovery, LoadBalancingStrategy loadBalancingStrategy, - EventExecutorGroup eventExecutorGroup, Logger log ) + EventExecutorGroup eventExecutorGroup, Logger log ) { this.connectionPool = connectionPool; this.routingTables = routingTables; @@ -252,11 +256,11 @@ private static RoutingTableRegistry createRoutingTables( ConnectionPool connecti } private static Rediscovery createRediscovery( EventExecutorGroup eventExecutorGroup, BoltServerAddress initialRouter, ServerAddressResolver resolver, - RoutingSettings settings, Clock clock, Logging logging ) + RoutingSettings settings, Clock clock, Logging logging, DomainNameResolver domainNameResolver ) { Logger log = loadBalancerLogger( logging ); ClusterCompositionProvider clusterCompositionProvider = new RoutingProcedureClusterCompositionProvider( clock, settings.routingContext() ); - return new RediscoveryImpl( initialRouter, settings, clusterCompositionProvider, eventExecutorGroup, resolver, log ); + return new RediscoveryImpl( initialRouter, settings, clusterCompositionProvider, eventExecutorGroup, resolver, log, domainNameResolver ); } private static Logger loadBalancerLogger( Logging logging ) diff --git a/driver/src/main/java/org/neo4j/driver/net/DomainNameResolver.java b/driver/src/main/java/org/neo4j/driver/net/DomainNameResolver.java new file mode 100644 index 0000000000..55e15992e5 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/net/DomainNameResolver.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.neo4j.driver.net; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * A resolver function used by the driver to resolve domain names. + */ +@FunctionalInterface +public interface DomainNameResolver +{ + /** + * Resolve the given domain name to a set of addresses. + * + * @param name the name to resolve. + * @return the resolved addresses. + * @throws UnknownHostException must be thrown if the given name can not be resolved to at least one address. + */ + InetAddress[] resolve( String name ) throws UnknownHostException; +} 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 a8b6c8051b..8aff7f99c7 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/ChannelConnectorImplIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/ChannelConnectorImplIT.java @@ -42,14 +42,15 @@ import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.ConnectionSettings; +import org.neo4j.driver.internal.DefaultDomainNameResolver; import org.neo4j.driver.internal.RevocationStrategy; 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.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.security.SecurityPlanImpl; import org.neo4j.driver.internal.util.FakeClock; import org.neo4j.driver.util.DatabaseExtension; import org.neo4j.driver.util.ParallelizableIT; @@ -233,7 +234,7 @@ private ChannelConnectorImpl newConnector( AuthToken authToken, SecurityPlan sec int connectTimeoutMillis ) { ConnectionSettings settings = new ConnectionSettings( authToken, "test", connectTimeoutMillis ); - return new ChannelConnectorImpl( settings, securityPlan, DEV_NULL_LOGGING, new FakeClock(), RoutingContext.EMPTY ); + return new ChannelConnectorImpl( settings, securityPlan, DEV_NULL_LOGGING, new FakeClock(), RoutingContext.EMPTY, new DefaultDomainNameResolver() ); } private static SecurityPlan trustAllCertificates() throws GeneralSecurityException 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 12b1e493f8..4a51124a3c 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 @@ -30,6 +30,7 @@ import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.ConnectionSettings; +import org.neo4j.driver.internal.DefaultDomainNameResolver; import org.neo4j.driver.internal.async.connection.BootstrapFactory; import org.neo4j.driver.internal.async.connection.ChannelConnector; import org.neo4j.driver.internal.async.connection.ChannelConnectorImpl; @@ -147,7 +148,7 @@ private ConnectionPoolImpl newPool() throws Exception FakeClock clock = new FakeClock(); ConnectionSettings connectionSettings = new ConnectionSettings( neo4j.authToken(), "test", 5000 ); ChannelConnector connector = new ChannelConnectorImpl( connectionSettings, SecurityPlanImpl.insecure(), - DEV_NULL_LOGGING, clock, RoutingContext.EMPTY ); + DEV_NULL_LOGGING, clock, RoutingContext.EMPTY, new DefaultDomainNameResolver() ); 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 693738af70..990d793b11 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 @@ -35,11 +35,12 @@ import org.neo4j.driver.Value; import org.neo4j.driver.exceptions.AuthenticationException; import org.neo4j.driver.internal.ConnectionSettings; +import org.neo4j.driver.internal.DefaultDomainNameResolver; 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.security.SecurityPlanImpl; import org.neo4j.driver.internal.util.FakeClock; import org.neo4j.driver.internal.util.ImmediateSchedulingEventExecutor; import org.neo4j.driver.util.DatabaseExtension; @@ -184,7 +185,7 @@ private NettyChannelPool newPool( AuthToken authToken, int maxConnections ) { ConnectionSettings settings = new ConnectionSettings( authToken, "test", 5_000 ); ChannelConnectorImpl connector = new ChannelConnectorImpl( settings, SecurityPlanImpl.insecure(), DEV_NULL_LOGGING, - new FakeClock(), RoutingContext.EMPTY ); + new FakeClock(), RoutingContext.EMPTY, new DefaultDomainNameResolver() ); 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/RediscoveryTest.java b/driver/src/test/java/org/neo4j/driver/internal/cluster/RediscoveryTest.java index 19b1174909..0a70ca19ac 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/cluster/RediscoveryTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/cluster/RediscoveryTest.java @@ -23,6 +23,8 @@ import org.mockito.ArgumentCaptor; import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -35,11 +37,13 @@ import org.neo4j.driver.exceptions.SessionExpiredException; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.DatabaseName; +import org.neo4j.driver.internal.DefaultDomainNameResolver; import org.neo4j.driver.internal.InternalBookmark; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ConnectionPool; import org.neo4j.driver.internal.util.FakeClock; import org.neo4j.driver.internal.util.ImmediateSchedulingEventExecutor; +import org.neo4j.driver.net.DomainNameResolver; import org.neo4j.driver.net.ServerAddressResolver; import static java.util.Arrays.asList; @@ -50,7 +54,6 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -375,8 +378,9 @@ void shouldRetryConfiguredNumberOfTimesWithDelay() ImmediateSchedulingEventExecutor eventExecutor = new ImmediateSchedulingEventExecutor(); RoutingSettings settings = new RoutingSettings( maxRoutingFailures, retryTimeoutDelay, 0 ); - Rediscovery rediscovery = new RediscoveryImpl( A, settings, compositionProvider, eventExecutor, resolver, DEV_NULL_LOGGER ); - RoutingTable table = routingTableMock(A, B ); + Rediscovery rediscovery = + new RediscoveryImpl( A, settings, compositionProvider, eventExecutor, resolver, DEV_NULL_LOGGER, new DefaultDomainNameResolver() ); + RoutingTable table = routingTableMock( A, B ); ClusterComposition actualComposition = await( rediscovery.lookupClusterComposition( table, pool, empty() ) ); @@ -399,7 +403,7 @@ void shouldNotLogWhenSingleRetryAttemptFails() ImmediateSchedulingEventExecutor eventExecutor = new ImmediateSchedulingEventExecutor(); RoutingSettings settings = new RoutingSettings( maxRoutingFailures, retryTimeoutDelay, 0 ); Logger logger = mock( Logger.class ); - Rediscovery rediscovery = new RediscoveryImpl( A, settings, compositionProvider, eventExecutor, resolver, logger ); + Rediscovery rediscovery = new RediscoveryImpl( A, settings, compositionProvider, eventExecutor, resolver, logger, new DefaultDomainNameResolver() ); RoutingTable table = routingTableMock( A ); ServiceUnavailableException e = @@ -412,16 +416,20 @@ void shouldNotLogWhenSingleRetryAttemptFails() } @Test - void shouldNotResolveToIPs() + void shouldNotResolveToIPs() throws UnknownHostException { ServerAddressResolver resolver = resolverMock( A, A ); - Rediscovery rediscovery = new RediscoveryImpl( A, null, null, null, resolver, null ); + DomainNameResolver domainNameResolver = mock( DomainNameResolver.class ); + InetAddress localhost = InetAddress.getLocalHost(); + when( domainNameResolver.resolve( A.host() ) ).thenReturn( new InetAddress[]{localhost} ); + Rediscovery rediscovery = new RediscoveryImpl( A, null, null, null, resolver, null, domainNameResolver ); List addresses = rediscovery.resolve(); verify( resolver, times( 1 ) ).resolve( A ); + verify( domainNameResolver, times( 1 ) ).resolve( A.host() ); assertEquals( 1, addresses.size() ); - assertFalse( addresses.get( 0 ).isResolved() ); + assertEquals( addresses.get( 0 ), new BoltServerAddress( localhost.getHostAddress(), A.port() ) ); } private Rediscovery newRediscovery( BoltServerAddress initialRouter, ClusterCompositionProvider compositionProvider, @@ -434,7 +442,8 @@ private Rediscovery newRediscovery( BoltServerAddress initialRouter, ClusterComp ServerAddressResolver resolver, Logger logger ) { RoutingSettings settings = new RoutingSettings( 1, 0, 0 ); - return new RediscoveryImpl( initialRouter, settings, compositionProvider, GlobalEventExecutor.INSTANCE, resolver, logger ); + return new RediscoveryImpl( initialRouter, settings, compositionProvider, GlobalEventExecutor.INSTANCE, resolver, logger, + new DefaultDomainNameResolver() ); } @SuppressWarnings( "unchecked" ) diff --git a/driver/src/test/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancerTest.java b/driver/src/test/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancerTest.java index be9e1449e5..520c81946c 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/cluster/loadbalancing/LoadBalancerTest.java @@ -414,7 +414,7 @@ private static LoadBalancer newLoadBalancer( ConnectionPool connectionPool, Rout when( routingTables.ensureRoutingTable( any( ConnectionContext.class ) ) ).thenReturn( CompletableFuture.completedFuture( handler ) ); Rediscovery rediscovery = mock( Rediscovery.class ); return new LoadBalancer( connectionPool, routingTables, rediscovery, new LeastConnectedLoadBalancingStrategy( connectionPool, DEV_NULL_LOGGING ), - GlobalEventExecutor.INSTANCE, DEV_NULL_LOGGER ); + GlobalEventExecutor.INSTANCE, DEV_NULL_LOGGER ); } private static LoadBalancer newLoadBalancer( ConnectionPool connectionPool, Rediscovery rediscovery ) @@ -428,6 +428,6 @@ private static LoadBalancer newLoadBalancer( ConnectionPool connectionPool, Rout { // Used only in testing return new LoadBalancer( connectionPool, routingTables, rediscovery, new LeastConnectedLoadBalancingStrategy( connectionPool, DEV_NULL_LOGGING ), - GlobalEventExecutor.INSTANCE, DEV_NULL_LOGGER ); + GlobalEventExecutor.INSTANCE, DEV_NULL_LOGGER ); } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/cluster/loadbalancing/RoutingTableAndConnectionPoolTest.java b/driver/src/test/java/org/neo4j/driver/internal/cluster/loadbalancing/RoutingTableAndConnectionPoolTest.java index 61172275c0..5cf047d49c 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/cluster/loadbalancing/RoutingTableAndConnectionPoolTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/cluster/loadbalancing/RoutingTableAndConnectionPoolTest.java @@ -326,7 +326,7 @@ private LoadBalancer newLoadBalancer( ConnectionPool connectionPool, RoutingTabl { Rediscovery rediscovery = mock( Rediscovery.class ); return new LoadBalancer( connectionPool, routingTables, rediscovery, new LeastConnectedLoadBalancingStrategy( connectionPool, logging ), - GlobalEventExecutor.INSTANCE, logging.getLog( "LB" ) ); + GlobalEventExecutor.INSTANCE, logging.getLog( "LB" ) ); } private CompletableFuture clusterComposition( BoltServerAddress... addresses ) diff --git a/driver/src/test/java/org/neo4j/driver/internal/net/BoltServerAddressTest.java b/driver/src/test/java/org/neo4j/driver/internal/net/BoltServerAddressTest.java index 8b92004c29..5f3af8eede 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/net/BoltServerAddressTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/net/BoltServerAddressTest.java @@ -22,20 +22,11 @@ import java.net.SocketAddress; import java.net.URI; -import java.util.List; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.net.ServerAddress; import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.Matchers.anyOf; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.everyItem; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.junit.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; @@ -146,58 +137,10 @@ void shouldUseUriWithHostAndPort() assertEquals( 12345, address.port() ); } - @Test - void shouldResolveDNSToIPs() throws Exception - { - BoltServerAddress address = new BoltServerAddress( "google.com", 80 ); - List resolved = address.resolveAll(); - assertThat( resolved, hasSize( greaterThanOrEqualTo( 1 ) ) ); - assertThat( resolved, everyItem( equalTo( address ) ) ); - } - - @Test - void shouldResolveLocalhostIPDNSToIPs() throws Exception - { - BoltServerAddress address = new BoltServerAddress( "127.0.0.1", 80 ); - List resolved = address.resolveAll(); - assertThat( resolved, hasSize( 1 ) ); - assertThat( resolved, everyItem( equalTo( address ) ) ); - } - - @Test - void shouldResolveLocalhostDNSToIPs() throws Exception - { - BoltServerAddress address = new BoltServerAddress( "localhost", 80 ); - List resolved = address.resolveAll(); - assertThat( resolved, hasSize( greaterThanOrEqualTo( 1 ) ) ); - assertThat( resolved, everyItem( equalTo( address ) ) ); - } - - @Test - void shouldResolveIPv6LocalhostDNSToIPs() throws Exception - { - BoltServerAddress address = new BoltServerAddress( "[::1]", 80 ); - List resolved = address.resolveAll(); - assertThat( resolved, hasSize( greaterThanOrEqualTo( 1 ) ) ); - assertThat( resolved, everyItem( equalTo( address ) ) ); - } - @Test void shouldIncludeHostAndPortInToString() { BoltServerAddress address = new BoltServerAddress( "localhost", 8081 ); assertThat( address.toString(), equalTo( "localhost:8081" ) ); } - - @Test - void shouldIncludeHostResolvedIPAndPortInToStringWhenResolved() throws Exception - { - BoltServerAddress address = new BoltServerAddress( "localhost", 8081 ); - BoltServerAddress resolved = address.resolve(); - - assertThat( resolved.toString(), not( equalTo( "localhost:8081" ) ) ); - assertThat( resolved.toString(), anyOf( containsString( "(127.0.0.1)" ), containsString( "(::1)" ) ) ); - assertThat( resolved.toString(), startsWith( "localhost" ) ); - assertThat( resolved.toString(), endsWith( "8081" ) ); - } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/ClusterCompositionUtil.java b/driver/src/test/java/org/neo4j/driver/internal/util/ClusterCompositionUtil.java index 53804f82b1..86698f0bc6 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/ClusterCompositionUtil.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/ClusterCompositionUtil.java @@ -29,16 +29,18 @@ public final class ClusterCompositionUtil { - private ClusterCompositionUtil() {} + private ClusterCompositionUtil() + { + } public static final long NEVER_EXPIRE = System.currentTimeMillis() + TimeUnit.HOURS.toMillis( 1 ); - public static final BoltServerAddress A = new BoltServerAddress( "1111:11" ); - public static final BoltServerAddress B = new BoltServerAddress( "2222:22" ); - public static final BoltServerAddress C = new BoltServerAddress( "3333:33" ); - public static final BoltServerAddress D = new BoltServerAddress( "4444:44" ); - public static final BoltServerAddress E = new BoltServerAddress( "5555:55" ); - public static final BoltServerAddress F = new BoltServerAddress( "6666:66" ); + public static final BoltServerAddress A = new BoltServerAddress( "192.168.100.100:11" ); + public static final BoltServerAddress B = new BoltServerAddress( "192.168.100.101:22" ); + public static final BoltServerAddress C = new BoltServerAddress( "192.168.100.102:33" ); + public static final BoltServerAddress D = new BoltServerAddress( "192.168.100.103:44" ); + public static final BoltServerAddress E = new BoltServerAddress( "192.168.100.104:55" ); + public static final BoltServerAddress F = new BoltServerAddress( "192.168.100.105:66" ); public static final List EMPTY = new ArrayList<>(); 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 fc650673ca..5bdd1a73d1 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 @@ -28,7 +28,10 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import org.neo4j.driver.Config; +import org.neo4j.driver.Logging; import org.neo4j.driver.internal.ConnectionSettings; +import org.neo4j.driver.internal.DefaultDomainNameResolver; import org.neo4j.driver.internal.DriverFactory; import org.neo4j.driver.internal.async.connection.ChannelConnector; import org.neo4j.driver.internal.async.connection.ChannelConnectorImpl; @@ -39,8 +42,6 @@ import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.messaging.MessageFormat; import org.neo4j.driver.internal.security.SecurityPlan; -import org.neo4j.driver.Config; -import org.neo4j.driver.Logging; public class MessageRecordingDriverFactory extends DriverFactory { @@ -56,7 +57,7 @@ protected ChannelConnector createConnector( ConnectionSettings settings, Securit RoutingContext routingContext ) { ChannelPipelineBuilder pipelineBuilder = new MessageRecordingChannelPipelineBuilder(); - return new ChannelConnectorImpl( settings, securityPlan, pipelineBuilder, config.logging(), clock, routingContext ); + return new ChannelConnectorImpl( settings, securityPlan, pipelineBuilder, config.logging(), clock, routingContext, new DefaultDomainNameResolver() ); } private class MessageRecordingChannelPipelineBuilder extends ChannelPipelineBuilderImpl 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 e36720ffbd..97da437117 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 @@ -18,14 +18,15 @@ */ package org.neo4j.driver.internal.util.io; +import org.neo4j.driver.Config; import org.neo4j.driver.internal.ConnectionSettings; +import org.neo4j.driver.internal.DefaultDomainNameResolver; 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; -import org.neo4j.driver.Config; public class ChannelTrackingDriverFactoryWithFailingMessageFormat extends ChannelTrackingDriverFactory { @@ -40,7 +41,7 @@ public ChannelTrackingDriverFactoryWithFailingMessageFormat( Clock clock ) protected ChannelConnector createRealConnector( ConnectionSettings settings, SecurityPlan securityPlan, Config config, Clock clock, RoutingContext routingContext ) { - return new ChannelConnectorImpl( settings, securityPlan, pipelineBuilder, config.logging(), clock, routingContext ); + return new ChannelConnectorImpl( settings, securityPlan, pipelineBuilder, config.logging(), clock, routingContext, new DefaultDomainNameResolver() ); } public FailingMessageFormat getFailingMessageFormat() diff --git a/driver/src/test/java/org/neo4j/driver/util/cc/Cluster.java b/driver/src/test/java/org/neo4j/driver/util/cc/Cluster.java index 8f02f3207d..9fdd6cdbb6 100644 --- a/driver/src/test/java/org/neo4j/driver/util/cc/Cluster.java +++ b/driver/src/test/java/org/neo4j/driver/util/cc/Cluster.java @@ -19,6 +19,7 @@ package org.neo4j.driver.util.cc; import java.io.FileNotFoundException; +import java.net.InetAddress; import java.net.URI; import java.net.UnknownHostException; import java.nio.file.Path; @@ -29,10 +30,10 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import org.neo4j.driver.Bookmark; import org.neo4j.driver.Driver; import org.neo4j.driver.Record; import org.neo4j.driver.internal.BoltServerAddress; -import org.neo4j.driver.Bookmark; import org.neo4j.driver.util.TestUtil; import org.neo4j.driver.util.cc.ClusterMemberRoleDiscoveryFactory.ClusterMemberRoleDiscovery; @@ -400,7 +401,7 @@ private static BoltServerAddress newBoltServerAddress( URI uri ) { try { - return new BoltServerAddress( uri ).resolve(); + return new BoltServerAddress( InetAddress.getByName( uri.getHost() ).getHostAddress(), uri.getPort() ); } catch ( UnknownHostException e ) { diff --git a/driver/src/test/java/org/neo4j/driver/util/cc/ClusterMember.java b/driver/src/test/java/org/neo4j/driver/util/cc/ClusterMember.java index e64b2d551f..68ec80aa1f 100644 --- a/driver/src/test/java/org/neo4j/driver/util/cc/ClusterMember.java +++ b/driver/src/test/java/org/neo4j/driver/util/cc/ClusterMember.java @@ -20,6 +20,7 @@ import java.io.File; import java.io.FileNotFoundException; +import java.net.InetAddress; import java.net.URI; import java.net.UnknownHostException; import java.nio.file.Path; @@ -111,7 +112,7 @@ private static BoltServerAddress newBoltServerAddress( URI uri ) { try { - return new BoltServerAddress( uri ).resolve(); + return new BoltServerAddress( InetAddress.getByName( uri.getHost() ).getHostAddress(), uri.getPort() ); } catch ( UnknownHostException e ) { diff --git a/driver/src/test/java/org/neo4j/driver/util/cc/ClusterMemberRoleDiscoveryFactory.java b/driver/src/test/java/org/neo4j/driver/util/cc/ClusterMemberRoleDiscoveryFactory.java index 6803df576a..0b48f2a299 100644 --- a/driver/src/test/java/org/neo4j/driver/util/cc/ClusterMemberRoleDiscoveryFactory.java +++ b/driver/src/test/java/org/neo4j/driver/util/cc/ClusterMemberRoleDiscoveryFactory.java @@ -18,6 +18,7 @@ */ package org.neo4j.driver.util.cc; +import java.net.InetAddress; import java.net.URI; import java.net.UnknownHostException; import java.util.HashMap; @@ -27,14 +28,14 @@ import org.neo4j.driver.AccessMode; import org.neo4j.driver.Driver; import org.neo4j.driver.Record; -import org.neo4j.driver.Session; import org.neo4j.driver.Result; +import org.neo4j.driver.Session; import org.neo4j.driver.Values; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.util.ServerVersion; -import static org.neo4j.driver.Values.parameters; import static org.neo4j.driver.SessionConfig.builder; +import static org.neo4j.driver.Values.parameters; import static org.neo4j.driver.internal.util.Iterables.single; public class ClusterMemberRoleDiscoveryFactory @@ -145,7 +146,7 @@ private static BoltServerAddress newBoltServerAddress( URI uri ) { try { - return new BoltServerAddress( uri ).resolve(); + return new BoltServerAddress( InetAddress.getByName( uri.getHost() ).getHostAddress(), uri.getPort() ); } catch ( UnknownHostException e ) { diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java index 1d966da508..1fa3cbee34 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java @@ -21,7 +21,9 @@ import lombok.Getter; import neo4j.org.testkit.backend.messages.responses.TestkitResponse; +import java.net.InetAddress; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Consumer; @@ -45,6 +47,7 @@ public class TestkitState private final Consumer responseWriter; private final Supplier processor; private final Map> idToServerAddresses = new HashMap<>(); + private final Map> idToResolvedAddresses = new HashMap<>(); public TestkitState( Consumer responseWriter, Supplier processor ) { diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/DomainNameResolutionCompleted.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/DomainNameResolutionCompleted.java new file mode 100644 index 0000000000..92fe4397cb --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/DomainNameResolutionCompleted.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +@Setter +@Getter +@NoArgsConstructor +public class DomainNameResolutionCompleted implements TestkitRequest +{ + private DomainNameResolutionCompletedBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + testkitState.getIdToResolvedAddresses().put( + data.getRequestId(), + data.getAddresses() + .stream() + .map( + addr -> + { + try + { + return InetAddress.getByName( addr ); + } + catch ( UnknownHostException e ) + { + throw new RuntimeException( e ); + } + } ) + .collect( Collectors.toCollection( LinkedList::new ) ) ); + return null; + } + + @Setter + @Getter + @NoArgsConstructor + private static class DomainNameResolutionCompletedBody + { + private String requestId; + private List addresses; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewDriver.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewDriver.java index c34bafc40d..e5bc28e29d 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewDriver.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewDriver.java @@ -22,17 +22,20 @@ import lombok.NoArgsConstructor; import lombok.Setter; import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.DomainNameResolutionRequired; import neo4j.org.testkit.backend.messages.responses.Driver; import neo4j.org.testkit.backend.messages.responses.ResolverResolutionRequired; import neo4j.org.testkit.backend.messages.responses.TestkitErrorResponse; import neo4j.org.testkit.backend.messages.responses.TestkitResponse; import java.util.Optional; +import java.util.concurrent.TimeUnit; import org.neo4j.driver.AuthToken; import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Config; import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.net.DomainNameResolver; import org.neo4j.driver.net.ServerAddressResolver; @Setter @@ -65,7 +68,12 @@ public TestkitResponse process( TestkitState testkitState ) { configBuilder.withResolver( callbackResolver( testkitState ) ); } + if ( data.isDomainNameResolverRegistered() ) + { + configBuilder.withDomainNameResolver( callbackDomainNameResolver( testkitState ) ); + } Optional.ofNullable( data.userAgent ).ifPresent( configBuilder::withUserAgent ); + Optional.ofNullable( data.connectionTimeoutMs ).ifPresent( timeout -> configBuilder.withConnectionTimeout( timeout, TimeUnit.MILLISECONDS ) ); testkitState.getDrivers().putIfAbsent( id, GraphDatabase.driver( data.uri, authToken, configBuilder.build() ) ); return Driver.builder().data( Driver.DriverBody.builder().id( id ).build() ).build(); } @@ -90,6 +98,26 @@ private ServerAddressResolver callbackResolver( TestkitState testkitState ) }; } + private DomainNameResolver callbackDomainNameResolver( TestkitState testkitState ) + { + return address -> + { + String callbackId = testkitState.newId(); + DomainNameResolutionRequired.DomainNameResolutionRequiredBody body = + DomainNameResolutionRequired.DomainNameResolutionRequiredBody.builder() + .id( callbackId ) + .name( address ) + .build(); + DomainNameResolutionRequired response = + DomainNameResolutionRequired.builder() + .data( body ) + .build(); + testkitState.getResponseWriter().accept( response ); + testkitState.getProcessor().get(); + return testkitState.getIdToResolvedAddresses().remove( callbackId ); + }; + } + @Setter @Getter @NoArgsConstructor @@ -99,5 +127,7 @@ public static class NewDriverBody private AuthorizationToken authorizationToken; private String userAgent; private boolean resolverRegistered; + private boolean domainNameResolverRegistered; + private Long connectionTimeoutMs; } } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java index 7d74966fa5..4f7d053948 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java @@ -33,7 +33,8 @@ @JsonSubTypes.Type( TransactionRun.class ), @JsonSubTypes.Type( RetryablePositive.class ), @JsonSubTypes.Type( SessionBeginTransaction.class ), @JsonSubTypes.Type( TransactionCommit.class ), @JsonSubTypes.Type( SessionLastBookmarks.class ), @JsonSubTypes.Type( SessionWriteTransaction.class ), - @JsonSubTypes.Type( ResolverResolutionCompleted.class ), @JsonSubTypes.Type( CheckMultiDBSupport.class ) + @JsonSubTypes.Type( ResolverResolutionCompleted.class ), @JsonSubTypes.Type( CheckMultiDBSupport.class ), + @JsonSubTypes.Type( DomainNameResolutionCompleted.class ) } ) public interface TestkitRequest { diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/DomainNameResolutionRequired.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/DomainNameResolutionRequired.java new file mode 100644 index 0000000000..3f803cc021 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/DomainNameResolutionRequired.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class DomainNameResolutionRequired implements TestkitResponse +{ + private DomainNameResolutionRequiredBody data; + + @Override + public String testkitName() + { + return "DomainNameResolutionRequired"; + } + + @Setter + @Getter + @Builder + public static class DomainNameResolutionRequiredBody + { + private String id; + + private String name; + } +}