Skip to content

Commit 75cb8a4

Browse files
authored
Merge pull request #392 from lutovich/1.4-ipv6-in-routing
Support IPv6 in routing procedure responses
2 parents 45c2930 + 804f1fe commit 75cb8a4

File tree

10 files changed

+211
-48
lines changed

10 files changed

+211
-48
lines changed

driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public class DriverFactory
5656
public final Driver newInstance( URI uri, AuthToken authToken, RoutingSettings routingSettings,
5757
RetrySettings retrySettings, Config config )
5858
{
59-
BoltServerAddress address = BoltServerAddress.from( uri );
59+
BoltServerAddress address = new BoltServerAddress( uri );
6060
RoutingSettings newRoutingSettings = routingSettings.withRoutingContext( new RoutingContext( uri ) );
6161
SecurityPlan securityPlan = createSecurityPlan( address, config );
6262
ConnectionPool connectionPool = createConnectionPool( authToken, securityPlan, config );

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

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,46 +34,23 @@ public class BoltServerAddress
3434
public static final int DEFAULT_PORT = 7687;
3535
public static final BoltServerAddress LOCAL_DEFAULT = new BoltServerAddress( "localhost", DEFAULT_PORT );
3636

37-
public static BoltServerAddress from( URI uri )
38-
{
39-
int port = uri.getPort();
40-
if ( port == -1 )
41-
{
42-
port = DEFAULT_PORT;
43-
}
44-
45-
if( uri.getHost() == null )
46-
{
47-
throw new IllegalArgumentException( "Invalid URI format `" + uri.toString() + "`");
48-
}
49-
50-
return new BoltServerAddress( uri.getHost(), port );
51-
}
52-
5337
private final String host;
5438
private final int port;
5539

56-
private SocketAddress socketAddress = null; // created lazily if required
40+
public BoltServerAddress( String address )
41+
{
42+
this( uriFrom( address ) );
43+
}
5744

58-
public BoltServerAddress( String host, int port )
45+
public BoltServerAddress( URI uri )
5946
{
60-
this.host = host;
61-
this.port = port;
47+
this( hostFrom( uri ), portFrom( uri ) );
6248
}
6349

64-
public BoltServerAddress( String host )
50+
public BoltServerAddress( String host, int port )
6551
{
66-
int colon = host.indexOf( ':' );
67-
if ( colon >= 0 )
68-
{
69-
this.port = Integer.parseInt( host.substring( colon + 1 ) );
70-
this.host = host.substring( 0, colon );
71-
}
72-
else
73-
{
74-
this.host = host;
75-
this.port = DEFAULT_PORT;
76-
}
52+
this.host = host;
53+
this.port = port;
7754
}
7855

7956
@Override
@@ -145,4 +122,74 @@ public int port()
145122
return port;
146123
}
147124

125+
private static String hostFrom( URI uri )
126+
{
127+
String host = uri.getHost();
128+
if ( host == null )
129+
{
130+
throw invalidAddressFormat( uri );
131+
}
132+
return host;
133+
}
134+
135+
private static int portFrom( URI uri )
136+
{
137+
int port = uri.getPort();
138+
return port == -1 ? DEFAULT_PORT : port;
139+
}
140+
141+
private static URI uriFrom( String address )
142+
{
143+
String scheme;
144+
String hostPort;
145+
146+
String[] schemeSplit = address.split( "://" );
147+
if ( schemeSplit.length == 1 )
148+
{
149+
// URI can't parse addresses without scheme, prepend fake "bolt://" to reuse the parsing facility
150+
scheme = "bolt://";
151+
hostPort = hostPortFrom( schemeSplit[0] );
152+
}
153+
else if ( schemeSplit.length == 2 )
154+
{
155+
scheme = schemeSplit[0] + "://";
156+
hostPort = hostPortFrom( schemeSplit[1] );
157+
}
158+
else
159+
{
160+
throw invalidAddressFormat( address );
161+
}
162+
163+
return URI.create( scheme + hostPort );
164+
}
165+
166+
private static String hostPortFrom( String address )
167+
{
168+
if ( address.startsWith( "[" ) )
169+
{
170+
// expected to be an IPv6 address like [::1] or [::1]:7687
171+
return address;
172+
}
173+
174+
boolean containsSingleColon = address.indexOf( ":" ) == address.lastIndexOf( ":" );
175+
if ( containsSingleColon )
176+
{
177+
// expected to be an IPv4 address with or without port like 127.0.0.1 or 127.0.0.1:7687
178+
return address;
179+
}
180+
181+
// address contains multiple colons and does not start with '['
182+
// expected to be an IPv6 address without brackets
183+
return "[" + address + "]";
184+
}
185+
186+
private static RuntimeException invalidAddressFormat( URI uri )
187+
{
188+
return invalidAddressFormat( uri.toString() );
189+
}
190+
191+
private static RuntimeException invalidAddressFormat( String address )
192+
{
193+
return new IllegalArgumentException( "Invalid address format `" + address + "`" );
194+
}
148195
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public void shouldAllowIPv6Address()
8383

8484
// Given
8585
URI uri = URI.create( "bolt://[::1]" );
86-
BoltServerAddress address = BoltServerAddress.from( uri );
86+
BoltServerAddress address = new BoltServerAddress( uri );
8787

8888
// When
8989
driver = GraphDatabase.driver( uri, neo4j.authToken() );
@@ -106,7 +106,7 @@ public void shouldRejectInvalidAddress()
106106
}
107107
catch ( IllegalArgumentException e )
108108
{
109-
assertThat( e.getMessage(), equalTo( "Invalid URI format `*`" ) );
109+
assertThat( e.getMessage(), equalTo( "Invalid address format `*`" ) );
110110
}
111111
}
112112

@@ -115,7 +115,7 @@ public void shouldRegisterSingleServer()
115115
{
116116
// Given
117117
URI uri = URI.create( "bolt://localhost:7687" );
118-
BoltServerAddress address = BoltServerAddress.from( uri );
118+
BoltServerAddress address = new BoltServerAddress( uri );
119119

120120
// When
121121
driver = GraphDatabase.driver( uri, neo4j.authToken() );

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,12 @@
2020

2121
import org.junit.Test;
2222

23-
import java.net.URI;
24-
2523
import org.neo4j.driver.internal.net.BoltServerAddress;
2624
import org.neo4j.driver.internal.summary.InternalServerInfo;
2725
import org.neo4j.driver.internal.summary.InternalSummaryCounters;
2826
import org.neo4j.driver.internal.summary.SummaryBuilder;
29-
import org.neo4j.driver.v1.summary.ResultSummary;
3027
import org.neo4j.driver.v1.Statement;
28+
import org.neo4j.driver.v1.summary.ResultSummary;
3129
import org.neo4j.driver.v1.summary.ServerInfo;
3230
import org.neo4j.driver.v1.summary.SummaryCounters;
3331

@@ -89,9 +87,8 @@ public void shouldReturnNullIfNoStatementTypeProvided() throws Throwable
8987
public void shouldObtainStatementAndServerInfoFromSummaryBuilder() throws Throwable
9088
{
9189
// Given
92-
SummaryBuilder builder = new SummaryBuilder( new Statement( "This is a test statement"), new
93-
InternalServerInfo( BoltServerAddress.from( URI.create( "http://neo4j.com" ) ),
94-
"super-awesome" ) );
90+
SummaryBuilder builder = new SummaryBuilder( new Statement( "This is a test statement" ),
91+
new InternalServerInfo( new BoltServerAddress( "neo4j.com" ), "super-awesome" ) );
9592

9693
// When
9794
ResultSummary summary = builder.build();
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
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.net;
20+
21+
import org.junit.Test;
22+
import org.junit.runner.RunWith;
23+
import org.junit.runners.Parameterized;
24+
import org.junit.runners.Parameterized.Parameter;
25+
import org.junit.runners.Parameterized.Parameters;
26+
27+
import static org.junit.Assert.assertEquals;
28+
import static org.neo4j.driver.internal.net.BoltServerAddress.DEFAULT_PORT;
29+
30+
@RunWith( Parameterized.class )
31+
public class BoltServerAddressParsingTest
32+
{
33+
@Parameter
34+
public String address;
35+
@Parameter( 1 )
36+
public String expectedHost;
37+
@Parameter( 2 )
38+
public int expectedPort;
39+
40+
@Parameters( name = "{0}" )
41+
public static Object[][] addressesToParse()
42+
{
43+
return new Object[][]{
44+
// Hostname
45+
{"localhost", "localhost", DEFAULT_PORT},
46+
{"localhost:9193", "localhost", 9193},
47+
{"neo4j.com", "neo4j.com", DEFAULT_PORT},
48+
{"royal-server.com.uk", "royal-server.com.uk", DEFAULT_PORT},
49+
{"royal-server.com.uk:4546", "royal-server.com.uk", 4546},
50+
51+
// Hostname with scheme
52+
{"bolt://localhost", "localhost", DEFAULT_PORT},
53+
{"bolt+routing://localhost", "localhost", DEFAULT_PORT},
54+
{"bolt://localhost:9193", "localhost", 9193},
55+
{"bolt+routing://localhost:9193", "localhost", 9193},
56+
{"bolt://neo4j.com", "neo4j.com", DEFAULT_PORT},
57+
{"bolt+routing://neo4j.com", "neo4j.com", DEFAULT_PORT},
58+
{"bolt://royal-server.com.uk", "royal-server.com.uk", DEFAULT_PORT},
59+
{"bolt+routing://royal-server.com.uk", "royal-server.com.uk", DEFAULT_PORT},
60+
{"bolt://royal-server.com.uk:4546", "royal-server.com.uk", 4546},
61+
{"bolt+routing://royal-server.com.uk:4546", "royal-server.com.uk", 4546},
62+
63+
// IPv4
64+
{"127.0.0.1", "127.0.0.1", DEFAULT_PORT},
65+
{"8.8.8.8:8080", "8.8.8.8", 8080},
66+
{"0.0.0.0", "0.0.0.0", DEFAULT_PORT},
67+
{"192.0.2.235:4329", "192.0.2.235", 4329},
68+
{"172.31.255.255:255", "172.31.255.255", 255},
69+
70+
// IPv4 with scheme
71+
{"bolt://198.51.100.0", "198.51.100.0", DEFAULT_PORT},
72+
{"bolt://65.21.10.12:5656", "65.21.10.12", 5656},
73+
{"bolt+routing://12.0.0.5", "12.0.0.5", DEFAULT_PORT},
74+
{"bolt+routing://155.55.20.6:9191", "155.55.20.6", 9191},
75+
76+
// IPv6
77+
{"::1", "[::1]", DEFAULT_PORT},
78+
{"ff02::2:ff00:0", "[ff02::2:ff00:0]", DEFAULT_PORT},
79+
{"[1afc:0:a33:85a3::ff2f]", "[1afc:0:a33:85a3::ff2f]", DEFAULT_PORT},
80+
{"[::1]:1515", "[::1]", 1515},
81+
{"[ff0a::101]:8989", "[ff0a::101]", 8989},
82+
83+
// IPv6 with scheme
84+
{"bolt://[::1]", "[::1]", DEFAULT_PORT},
85+
{"bolt+routing://[::1]", "[::1]", DEFAULT_PORT},
86+
{"bolt://[ff02::d]", "[ff02::d]", DEFAULT_PORT},
87+
{"bolt+routing://[fe80::b279:2f]", "[fe80::b279:2f]", DEFAULT_PORT},
88+
{"bolt://[::1]:8687", "[::1]", 8687},
89+
{"bolt+routing://[::1]:1212", "[::1]", 1212},
90+
{"bolt://[ff02::d]:9090", "[ff02::d]", 9090},
91+
{"bolt+routing://[fe80::b279:2f]:7878", "[fe80::b279:2f]", 7878},
92+
93+
// IPv6 with zone id
94+
{"::1%eth0", "[::1%eth0]", DEFAULT_PORT},
95+
{"ff02::2:ff00:0%12", "[ff02::2:ff00:0%12]", DEFAULT_PORT},
96+
{"[1afc:0:a33:85a3::ff2f%eth1]", "[1afc:0:a33:85a3::ff2f%eth1]", DEFAULT_PORT},
97+
{"[::1%eth0]:3030", "[::1%eth0]", 3030},
98+
{"[ff0a::101%8]:4040", "[ff0a::101%8]", 4040},
99+
100+
// IPv6 with scheme and zone id
101+
{"bolt://[::1%eth5]", "[::1%eth5]", DEFAULT_PORT},
102+
{"bolt+routing://[::1%12]", "[::1%12]", DEFAULT_PORT},
103+
{"bolt://[ff02::d%3]", "[ff02::d%3]", DEFAULT_PORT},
104+
{"bolt+routing://[fe80::b279:2f%eth0]", "[fe80::b279:2f%eth0]", DEFAULT_PORT},
105+
{"bolt://[::1%eth3]:8687", "[::1%eth3]", 8687},
106+
{"bolt+routing://[::1%2]:1212", "[::1%2]", 1212},
107+
{"bolt://[ff02::d%3]:9090", "[ff02::d%3]", 9090},
108+
{"bolt+routing://[fe80::b279:2f%eth1]:7878", "[fe80::b279:2f%eth1]", 7878},
109+
};
110+
}
111+
112+
@Test
113+
public void shouldParseAddress()
114+
{
115+
BoltServerAddress parsed = new BoltServerAddress( address );
116+
assertEquals( expectedHost, parsed.host() );
117+
assertEquals( expectedPort, parsed.port() );
118+
}
119+
}

driver/src/test/java/org/neo4j/driver/internal/net/BoltServerAddressTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@
2525
import static org.hamcrest.CoreMatchers.equalTo;
2626
import static org.junit.Assert.assertNotSame;
2727
import static org.junit.Assert.assertThat;
28+
import static org.neo4j.driver.internal.net.BoltServerAddress.DEFAULT_PORT;
2829

2930
public class BoltServerAddressTest
3031
{
3132
@Test
3233
public void defaultPortShouldBe7687()
3334
{
34-
assertThat( BoltServerAddress.DEFAULT_PORT, equalTo( 7687 ) );
35+
assertThat( DEFAULT_PORT, equalTo( 7687 ) );
3536
}
3637

3738
@Test

driver/src/test/java/org/neo4j/driver/internal/net/SocketConnectionTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.mockito.stubbing.Answer;
2424

2525
import java.io.IOException;
26-
import java.net.URI;
2726
import java.util.ArrayList;
2827
import java.util.Iterator;
2928
import java.util.Queue;
@@ -63,7 +62,7 @@ public void shouldReceiveServerInfoAfterInit() throws Throwable
6362
SocketClient socket = mock( SocketClient.class );
6463
SocketConnection conn = new SocketConnection( socket, SERVER_INFO, DEV_NULL_LOGGER );
6564

66-
when( socket.address() ).thenReturn( BoltServerAddress.from( URI.create( "http://neo4j.com:9000" ) ) );
65+
when( socket.address() ).thenReturn( new BoltServerAddress( "neo4j.com:9000" ) );
6766

6867
// set up response messages
6968
ArrayList<Message> serverResponses = new ArrayList<>();

driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public class Neo4jRunner
5454
private static final String DEFAULT_NEOCTRL_ARGS = "-e 3.2.0";
5555
public static final String NEOCTRL_ARGS = System.getProperty( "neoctrl.args", DEFAULT_NEOCTRL_ARGS );
5656
public static final URI DEFAULT_URI = URI.create( "bolt://localhost:7687" );
57-
public static final BoltServerAddress DEFAULT_ADDRESS = BoltServerAddress.from( DEFAULT_URI );
57+
public static final BoltServerAddress DEFAULT_ADDRESS = new BoltServerAddress( DEFAULT_URI );
5858

5959
public static final String USER = "neo4j";
6060
public static final String PASSWORD = "password";

driver/src/test/java/org/neo4j/driver/v1/util/cc/Cluster.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ private static BoltServerAddress newBoltServerAddress( URI uri )
380380
{
381381
try
382382
{
383-
return BoltServerAddress.from( uri ).resolve();
383+
return new BoltServerAddress( uri ).resolve();
384384
}
385385
catch ( UnknownHostException e )
386386
{

driver/src/test/java/org/neo4j/driver/v1/util/cc/ClusterMember.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ private static BoltServerAddress newBoltServerAddress( URI uri )
7575
{
7676
try
7777
{
78-
return BoltServerAddress.from( uri ).resolve();
78+
return new BoltServerAddress( uri ).resolve();
7979
}
8080
catch ( UnknownHostException e )
8181
{

0 commit comments

Comments
 (0)