Skip to content

Commit bec13b3

Browse files
author
Zhen Li
authored
Merge pull request #332 from zhenlineo/1.2-dns-to-ips
Resolve DNS to IP & IPv6 address support
2 parents 5578383 + 9a6ca7b commit bec13b3

File tree

14 files changed

+269
-30
lines changed

14 files changed

+269
-30
lines changed

driver/src/main/java/org/neo4j/driver/internal/cluster/ClusterRoutingTable.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,7 @@ public synchronized Set<BoltServerAddress> update( ClusterComposition cluster )
7878
@Override
7979
public synchronized void forget( BoltServerAddress address )
8080
{
81-
// Don't remove it from the set of routers, since that might mean we lose our ability to re-discover,
82-
// just remove it from the set of readers and writers, so that we don't use it for actual work without
83-
// performing discovery first.
81+
routers.remove( address );
8482
readers.remove( address );
8583
writers.remove( address );
8684
}
@@ -115,11 +113,6 @@ public void removeWriter( BoltServerAddress toRemove )
115113
writers.remove( toRemove );
116114
}
117115

118-
@Override
119-
public void removeRouter( BoltServerAddress toRemove )
120-
{
121-
routers.remove( toRemove );
122-
}
123116

124117
@Override
125118
public String toString()
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) 2002-2017 "Neo Technology,"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver.internal.cluster;
20+
21+
import java.net.InetAddress;
22+
import java.net.UnknownHostException;
23+
import java.util.HashSet;
24+
import java.util.Set;
25+
26+
import org.neo4j.driver.internal.net.BoltServerAddress;
27+
import org.neo4j.driver.v1.Logger;
28+
29+
public class DnsResolver implements HostNameResolver
30+
{
31+
private final Logger logger;
32+
33+
public DnsResolver( Logger logger )
34+
{
35+
this.logger = logger;
36+
}
37+
38+
@Override
39+
public Set<BoltServerAddress> resolve( BoltServerAddress initialRouter )
40+
{
41+
Set<BoltServerAddress> addresses = new HashSet<>();
42+
try
43+
{
44+
InetAddress[] ipAddresses = InetAddress.getAllByName( initialRouter.host() );
45+
46+
for ( InetAddress ipAddress : ipAddresses )
47+
{
48+
addresses.add( new BoltServerAddress( ipAddress.getHostAddress(), initialRouter.port() ) );
49+
}
50+
51+
return addresses;
52+
}
53+
catch ( UnknownHostException e )
54+
{
55+
logger.error( "Failed to resolve URI `" + initialRouter + "` to IPs due to error: " + e.getMessage(), e );
56+
57+
addresses.add( initialRouter );
58+
return addresses;
59+
}
60+
}
61+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2002-2017 "Neo Technology,"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver.internal.cluster;
20+
21+
import java.util.Set;
22+
23+
import org.neo4j.driver.internal.net.BoltServerAddress;
24+
25+
public interface HostNameResolver
26+
{
27+
Set<BoltServerAddress> resolve( BoltServerAddress initialRouter );
28+
}

driver/src/main/java/org/neo4j/driver/internal/cluster/LoadBalancer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,6 @@ private static Rediscovery createRediscovery( BoltServerAddress initialRouter, R
157157
Clock clock, Logger log )
158158
{
159159
ClusterCompositionProvider clusterComposition = new GetServersProcedureClusterCompositionProvider( clock, log );
160-
return new Rediscovery( initialRouter, settings, clock, log, clusterComposition );
160+
return new Rediscovery( initialRouter, settings, clock, log, clusterComposition, new DnsResolver( log ) );
161161
}
162162
}

driver/src/main/java/org/neo4j/driver/internal/cluster/Rediscovery.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
*/
1919
package org.neo4j.driver.internal.cluster;
2020

21+
import java.util.HashSet;
22+
import java.util.Set;
23+
2124
import org.neo4j.driver.internal.net.BoltServerAddress;
2225
import org.neo4j.driver.internal.spi.Connection;
2326
import org.neo4j.driver.internal.spi.ConnectionPool;
@@ -37,15 +40,17 @@ public class Rediscovery
3740
private final Clock clock;
3841
private final Logger logger;
3942
private final ClusterCompositionProvider provider;
43+
private final HostNameResolver hostNameResolver;
4044

4145
public Rediscovery( BoltServerAddress initialRouter, RoutingSettings settings, Clock clock, Logger logger,
42-
ClusterCompositionProvider provider )
46+
ClusterCompositionProvider provider, HostNameResolver hostNameResolver )
4347
{
4448
this.initialRouter = initialRouter;
4549
this.settings = settings;
4650
this.clock = clock;
4751
this.logger = logger;
4852
this.provider = provider;
53+
this.hostNameResolver = hostNameResolver;
4954
}
5055

5156
// Given the current routing table and connection pool, use the connection composition provider to fetch a new
@@ -76,8 +81,8 @@ public ClusterComposition lookupClusterComposition( ConnectionPool connections,
7681
private ClusterComposition lookupClusterCompositionOnKnownRouters( ConnectionPool connections,
7782
RoutingTable routingTable )
7883
{
79-
boolean triedInitialRouter = false;
8084
int size = routingTable.routerSize();
85+
Set<BoltServerAddress> triedServers = new HashSet<>();
8186
for ( int i = 0; i < size; i++ )
8287
{
8388
BoltServerAddress address = routingTable.nextRouter();
@@ -86,23 +91,29 @@ private ClusterComposition lookupClusterCompositionOnKnownRouters( ConnectionPoo
8691
break;
8792
}
8893

89-
if ( address.equals( initialRouter ) )
94+
ClusterComposition composition = lookupClusterCompositionOnRouter( address, connections, routingTable );
95+
if ( composition != null )
96+
{
97+
return composition;
98+
}
99+
else
90100
{
91-
triedInitialRouter = true;
101+
triedServers.add( address );
92102
}
103+
}
93104

105+
Set<BoltServerAddress> ips = hostNameResolver.resolve( initialRouter );
106+
ips.removeAll( triedServers );
107+
for ( BoltServerAddress address : ips )
108+
{
94109
ClusterComposition composition = lookupClusterCompositionOnRouter( address, connections, routingTable );
95110
if ( composition != null )
96111
{
97112
return composition;
98113
}
99114
}
100115

101-
if ( triedInitialRouter )
102-
{
103-
return null;
104-
}
105-
return lookupClusterCompositionOnRouter( initialRouter, connections, routingTable );
116+
return null;
106117
}
107118

108119
private ClusterComposition lookupClusterCompositionOnRouter( BoltServerAddress routerAddress,
@@ -122,7 +133,7 @@ private ClusterComposition lookupClusterCompositionOnRouter( BoltServerAddress r
122133
{
123134
// connection turned out to be broken
124135
logger.error( format( "Failed to connect to routing server '%s'.", routerAddress ), t );
125-
routingTable.removeRouter( routerAddress );
136+
routingTable.forget( routerAddress );
126137
return null;
127138
}
128139

driver/src/main/java/org/neo4j/driver/internal/cluster/RoutingTable.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,4 @@ public interface RoutingTable
3939
int routerSize();
4040

4141
void removeWriter( BoltServerAddress toRemove );
42-
43-
void removeRouter( BoltServerAddress toRemove );
4442
}

driver/src/main/java/org/neo4j/driver/internal/net/BoltServerAddress.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ public static BoltServerAddress from( URI uri )
4141
{
4242
port = DEFAULT_PORT;
4343
}
44+
45+
if( uri.getHost() == null )
46+
{
47+
throw new IllegalArgumentException( "Invalid URI format `" + uri.toString() + "`");
48+
}
49+
4450
return new BoltServerAddress( uri.getHost(), port );
4551
}
4652

driver/src/test/java/org/neo4j/driver/internal/DirectDriverTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import static org.hamcrest.Matchers.is;
3333
import static org.hamcrest.core.IsEqual.equalTo;
3434
import static org.junit.Assert.assertThat;
35+
import static org.junit.Assert.fail;
3536
import static org.neo4j.driver.internal.net.BoltServerAddress.LOCAL_DEFAULT;
3637
import static org.neo4j.driver.internal.util.Matchers.directDriverWithAddress;
3738
import static org.neo4j.driver.v1.Values.parameters;
@@ -52,6 +53,38 @@ public void shouldUseDefaultPortIfMissing()
5253
assertThat( driver, is( directDriverWithAddress( LOCAL_DEFAULT ) ) );
5354
}
5455

56+
@Test
57+
public void shouldAllowIPv6Address()
58+
{
59+
// Given
60+
URI uri = URI.create( "bolt://[::1]" );
61+
BoltServerAddress address = BoltServerAddress.from( uri );
62+
63+
// When
64+
Driver driver = GraphDatabase.driver( uri );
65+
66+
// Then
67+
assertThat( driver, is( directDriverWithAddress( address ) ) );
68+
}
69+
70+
@Test
71+
public void shouldRejectInvalidAddress()
72+
{
73+
// Given
74+
URI uri = URI.create( "*" );
75+
76+
// When & Then
77+
try
78+
{
79+
Driver driver = GraphDatabase.driver( uri );
80+
fail("Expecting error for wrong uri");
81+
}
82+
catch( IllegalArgumentException e )
83+
{
84+
assertThat( e.getMessage(), equalTo( "Invalid URI format `*`" ) );
85+
}
86+
}
87+
5588
@Test
5689
public void shouldRegisterSingleServer()
5790
{
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (c) 2002-2017 "Neo Technology,"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver.internal.cluster;
20+
21+
import org.junit.Test;
22+
23+
import java.net.UnknownHostException;
24+
import java.util.Set;
25+
26+
import org.neo4j.driver.internal.net.BoltServerAddress;
27+
import org.neo4j.driver.v1.Logger;
28+
29+
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
30+
import static org.junit.Assert.assertThat;
31+
import static org.mockito.Matchers.any;
32+
import static org.mockito.Mockito.mock;
33+
import static org.mockito.Mockito.verify;
34+
35+
public class DnsResolverTest
36+
{
37+
private DnsResolver resolver = new DnsResolver( mock( Logger.class ) );
38+
39+
@Test
40+
public void shouldResolveDNSToIPs()
41+
{
42+
Set<BoltServerAddress> resolve = resolver.resolve( new BoltServerAddress( "google.com", 80 ) );
43+
assertThat( resolve.size(), greaterThanOrEqualTo( 1 ) );
44+
}
45+
46+
@Test
47+
public void shouldResolveLocalhostIPDNSToIPs()
48+
{
49+
Set<BoltServerAddress> resolve = resolver.resolve( new BoltServerAddress( "127.0.0.1", 80 ) );
50+
assertThat( resolve.size(), greaterThanOrEqualTo( 1 ) );
51+
}
52+
53+
@Test
54+
public void shouldResolveLocalhostDNSToIPs()
55+
{
56+
Set<BoltServerAddress> resolve = resolver.resolve( new BoltServerAddress( "localhost", 80 ) );
57+
assertThat( resolve.size(), greaterThanOrEqualTo( 1 ) );
58+
}
59+
60+
@Test
61+
public void shouldResolveIPv6LocalhostDNSToIPs()
62+
{
63+
Set<BoltServerAddress> resolve = resolver.resolve( new BoltServerAddress( "[::1]", 80 ) );
64+
assertThat( resolve.size(), greaterThanOrEqualTo( 1 ) );
65+
}
66+
67+
@Test
68+
public void shouldExceptionAndGiveDefaultValue()
69+
{
70+
Logger logger = mock( Logger.class );
71+
DnsResolver resolver = new DnsResolver( logger );
72+
Set<BoltServerAddress> resolve = resolver.resolve( new BoltServerAddress( "[/]", 80 ) );
73+
verify( logger ).error( any( String.class ), any( UnknownHostException.class ) );
74+
assertThat( resolve.size(), greaterThanOrEqualTo( 1 ) );
75+
}
76+
}

0 commit comments

Comments
 (0)