Skip to content

Commit 6133884

Browse files
committed
Further improve address parsing in BoltServerAddress
To support IPv6 with and without brackets and zone ids.
1 parent cfa1c86 commit 6133884

File tree

5 files changed

+176
-47
lines changed

5 files changed

+176
-47
lines changed

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

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ private static String hostFrom( URI uri )
127127
String host = uri.getHost();
128128
if ( host == null )
129129
{
130-
throw new IllegalArgumentException( "Invalid URI format `" + uri.toString() + "`" );
130+
throw invalidAddressFormat( uri );
131131
}
132132
return host;
133133
}
@@ -140,9 +140,56 @@ private static int portFrom( URI uri )
140140

141141
private static URI uriFrom( String address )
142142
{
143-
// URI can't parse addresses without scheme, prepend fake "bolt://" to reuse the parsing facility
144-
boolean hasScheme = address.contains( "://" );
145-
String addressWithScheme = hasScheme ? address : "bolt://" + address;
146-
return URI.create( addressWithScheme );
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 + "`" );
147194
}
148195
}

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
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;
@@ -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( new BoltServerAddress( 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 & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@
2323
import java.net.SocketAddress;
2424

2525
import static org.hamcrest.CoreMatchers.equalTo;
26-
import static org.junit.Assert.assertEquals;
2726
import static org.junit.Assert.assertNotSame;
2827
import static org.junit.Assert.assertThat;
28+
import static org.neo4j.driver.internal.net.BoltServerAddress.DEFAULT_PORT;
2929

3030
public class BoltServerAddressTest
3131
{
3232
@Test
3333
public void defaultPortShouldBe7687()
3434
{
35-
assertThat( BoltServerAddress.DEFAULT_PORT, equalTo( 7687 ) );
35+
assertThat( DEFAULT_PORT, equalTo( 7687 ) );
3636
}
3737

3838
@Test
@@ -51,37 +51,4 @@ public void shouldAlwaysResolveAddress()
5151

5252
assertNotSame( socketAddress1, socketAddress2 );
5353
}
54-
55-
@Test
56-
public void shouldParseIPv4Addresses()
57-
{
58-
BoltServerAddress address1 = new BoltServerAddress( "127.0.0.1:1234" );
59-
assertEquals( "127.0.0.1", address1.host() );
60-
assertEquals( 1234, address1.port() );
61-
62-
BoltServerAddress address2 = new BoltServerAddress( "8.8.8.8:8080" );
63-
assertEquals( "8.8.8.8", address2.host() );
64-
assertEquals( 8080, address2.port() );
65-
}
66-
67-
@Test
68-
public void shouldParseIPv6Addresses()
69-
{
70-
BoltServerAddress address1 = new BoltServerAddress( "[::1]:7688" );
71-
assertEquals( "[::1]", address1.host() );
72-
assertEquals( 7688, address1.port() );
73-
74-
BoltServerAddress address2 = new BoltServerAddress( "[1afc:0:a33:85a3::ff2f]:9001" );
75-
assertEquals( "[1afc:0:a33:85a3::ff2f]", address2.host() );
76-
assertEquals( 9001, address2.port() );
77-
}
78-
79-
@Test
80-
public void shouldParseBoltAddresses()
81-
{
82-
BoltServerAddress address = new BoltServerAddress( "bolt://host:6565" );
83-
84-
assertEquals( "host", address.host() );
85-
assertEquals( 6565, address.port() );
86-
}
8754
}

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( new BoltServerAddress( 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<>();

0 commit comments

Comments
 (0)