Skip to content

Commit 810ea50

Browse files
smagellanmarcosnils
authored andcommitted
Fixed IPv6 parsing logic (#1342)
Introduced tests for HostAndPort Introduced comments on IPv6 parsing logic Introduced 'aliases' of localhost address Conflicts: src/main/java/redis/clients/jedis/HostAndPort.java
1 parent 60a27e1 commit 810ea50

File tree

6 files changed

+132
-14
lines changed

6 files changed

+132
-14
lines changed

src/main/java/redis/clients/jedis/HostAndPort.java

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package redis.clients.jedis;
22

33
import java.io.Serializable;
4+
import java.net.InetAddress;
5+
import java.util.logging.Level;
6+
import java.util.logging.Logger;
47

58
public class HostAndPort implements Serializable {
69
private static final long serialVersionUID = -519876229978427751L;
710

8-
public static final String LOCALHOST_STR = "localhost";
11+
protected static Logger log = Logger.getLogger(HostAndPort.class.getName());
12+
public static final String LOCALHOST_STR = getLocalHostQuietly();
13+
914

1015
private String host;
1116
private int port;
@@ -47,10 +52,61 @@ public String toString() {
4752
return host + ":" + port;
4853
}
4954

50-
private String convertHost(String host) {
51-
if (host.equals("127.0.0.1")) return LOCALHOST_STR;
52-
else if (host.equals("::1")) return LOCALHOST_STR;
55+
/**
56+
* Splits String into host and port parts.
57+
* String must be in ( host + ":" + port ) format.
58+
* Port is optional
59+
* @param from String to parse
60+
* @return array of host and port strings
61+
*/
62+
public static String[] extractParts(String from){
63+
int idx = from.lastIndexOf(":");
64+
String host = idx != -1 ? from.substring(0, idx) : from;
65+
String port = idx != -1 ? from.substring(idx + 1) : "";
66+
return new String[] { host, port };
67+
}
5368

54-
return host;
69+
/**
70+
* Creates HostAndPort instance from string.
71+
* String must be in ( host + ":" + port ) format.
72+
* Port is mandatory. Can convert host part.
73+
* @see #convertHost(String)
74+
* @param from String to parse
75+
* @return HostAndPort instance
76+
*/
77+
public static HostAndPort parseString(String from){
78+
// NOTE: redis answers with
79+
// '99aa9999aa9a99aa099aaa990aa99a09aa9a9999 9a09:9a9:a090:9a::99a slave 8c88888888cc08088cc8c8c888c88c8888c88cc8 0 1468251272993 37 connected'
80+
// for CLUSTER NODES, ASK and MOVED scenarios. That's why there is no possibility to parse address in 'correct' way.
81+
// Redis should switch to 'bracketized' (RFC 3986) IPv6 address.
82+
try {
83+
String[] parts = extractParts(from);
84+
String host = parts[0];
85+
int port = Integer.valueOf(parts[1]);
86+
return new HostAndPort(convertHost(host), port);
87+
} catch (NumberFormatException ex) {
88+
throw new IllegalArgumentException(ex);
89+
}
90+
}
91+
92+
public static String convertHost(String host) {
93+
if (host.equals("127.0.0.1") || host.startsWith("localhost") || host.equals("0.0.0.0") ||
94+
host.startsWith("169.254") ||
95+
host.startsWith("::1") || host.startsWith("0:0:0:0:0:0:0:1")) {
96+
return LOCALHOST_STR;
97+
} else {
98+
return host;
99+
}
100+
}
101+
102+
public static String getLocalHostQuietly() {
103+
String localAddress;
104+
try {
105+
localAddress = InetAddress.getLocalHost().getHostAddress();
106+
} catch (Exception ex) {
107+
log.logp(Level.SEVERE, HostAndPort.class.getName(), "getLocalHostQuietly", "cant resolve localhost address", ex);
108+
localAddress = "localhost";
109+
}
110+
return localAddress;
55111
}
56112
}

src/main/java/redis/clients/jedis/JedisSentinelPool.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ private HostAndPort initSentinels(Set<String> sentinels, final String masterName
136136
log.info("Trying to find master from available Sentinels...");
137137

138138
for (String sentinel : sentinels) {
139-
final HostAndPort hap = toHostAndPort(Arrays.asList(sentinel.split(":")));
139+
final HostAndPort hap = HostAndPort.parseString(sentinel);
140140

141141
log.fine("Connecting to Sentinel " + hap);
142142

@@ -185,7 +185,7 @@ private HostAndPort initSentinels(Set<String> sentinels, final String masterName
185185
log.info("Redis master running at " + master + ", starting Sentinel listeners...");
186186

187187
for (String sentinel : sentinels) {
188-
final HostAndPort hap = toHostAndPort(Arrays.asList(sentinel.split(":")));
188+
final HostAndPort hap = HostAndPort.parseString(sentinel);
189189
MasterListener masterListener = new MasterListener(masterName, hap.getHost(), hap.getPort());
190190
// whether MasterListener threads are alive or not, process can be stopped
191191
masterListener.setDaemon(true);

src/main/java/redis/clients/jedis/Protocol.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public static String readErrorLineIfPossible(RedisInputStream is) {
135135
private static String[] parseTargetHostAndSlot(String clusterRedirectResponse) {
136136
String[] response = new String[3];
137137
String[] messageInfo = clusterRedirectResponse.split(" ");
138-
String[] targetHostAndPort = messageInfo[2].split(":");
138+
String[] targetHostAndPort = HostAndPort.extractParts(messageInfo[2]);
139139
response[0] = messageInfo[1];
140140
response[1] = targetHostAndPort[0];
141141
response[2] = targetHostAndPort[1];
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package redis.clients.jedis;
2+
3+
import org.junit.Assert;
4+
import org.junit.Test;
5+
6+
import java.util.ArrayList;
7+
import java.util.Arrays;
8+
import java.util.Collections;
9+
10+
import static org.junit.Assert.*;
11+
12+
/**
13+
* Created by smagellan on 7/11/16.
14+
*/
15+
public class HostAndPortTest {
16+
@Test
17+
public void checkExtractParts() throws Exception {
18+
String host = "2a11:1b1:0:111:e111:1f11:1111:1f1e:1999";
19+
String port = "6379";
20+
21+
Assert.assertEquals(Arrays.asList(HostAndPort.extractParts(host + ":" + port)),
22+
Arrays.asList(host, port));
23+
24+
host = "";
25+
port = "";
26+
Assert.assertEquals(Arrays.asList(HostAndPort.extractParts(host + ":" + port)),
27+
Arrays.asList(host, port));
28+
29+
host = "localhost";
30+
port = "";
31+
Assert.assertEquals(Arrays.asList(HostAndPort.extractParts(host + ":" + port)),
32+
Arrays.asList(host, port));
33+
34+
35+
host = "";
36+
port = "6379";
37+
Assert.assertEquals(Arrays.asList(HostAndPort.extractParts(host + ":" + port)),
38+
Arrays.asList(host, port));
39+
40+
host = "11:22:33:44:55";
41+
port = "";
42+
Assert.assertEquals(Arrays.asList(HostAndPort.extractParts(host + ":" + port)),
43+
Arrays.asList(host, port));
44+
}
45+
46+
@Test
47+
public void checkParseString() throws Exception {
48+
String host = "2a11:1b1:0:111:e111:1f11:1111:1f1e:1999";
49+
int port = 6379;
50+
HostAndPort hp = HostAndPort.parseString(host + ":" + Integer.toString(port));
51+
Assert.assertEquals(host, hp.getHost());
52+
Assert.assertEquals(port, hp.getPort());
53+
}
54+
55+
@Test(expected = IllegalArgumentException.class)
56+
public void checkParseStringWithoutPort() throws Exception {
57+
String host = "localhost";
58+
HostAndPort.parseString(host + ":");
59+
}
60+
}

src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,14 @@ public static List<HostAndPort> parseHosts(String envHosts,
5858

5959
for (String hostDef : hostDefs) {
6060

61-
String[] hostAndPort = hostDef.split(":");
61+
String[] hostAndPortParts = HostAndPort.extractParts(hostDef);
6262

63-
if (null != hostAndPort && 2 == hostAndPort.length) {
64-
String host = hostAndPort[0];
63+
if (null != hostAndPortParts && 2 == hostAndPortParts.length) {
64+
String host = hostAndPortParts[0];
6565
int port = Protocol.DEFAULT_PORT;
6666

6767
try {
68-
port = Integer.parseInt(hostAndPort[1]);
68+
port = Integer.parseInt(hostAndPortParts[1]);
6969
} catch (final NumberFormatException nfe) {
7070
}
7171

src/test/java/redis/clients/jedis/tests/utils/ClientKillerUtil.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
package redis.clients.jedis.tests.utils;
22

3+
import redis.clients.jedis.HostAndPort;
34
import redis.clients.jedis.Jedis;
45

56
public class ClientKillerUtil {
67
public static void killClient(Jedis jedis, String clientName) {
78
for (String clientInfo : jedis.clientList().split("\n")) {
89
if (clientInfo.contains("name=" + clientName)) {
910
// Ugly, but cmon, it's a test.
10-
String[] hostAndPort = clientInfo.split(" ")[1].split("=")[1].split(":");
11+
String hostAndPortString = clientInfo.split(" ")[1].split("=")[1];
12+
String[] hostAndPortParts = HostAndPort.extractParts(hostAndPortString);
1113
// It would be better if we kill the client by Id as it's safer but jedis doesn't implement
1214
// the command yet.
13-
jedis.clientKill(hostAndPort[0] + ":" + hostAndPort[1]);
15+
jedis.clientKill(hostAndPortParts[0] + ":" + hostAndPortParts[1]);
1416
}
1517
}
1618
}

0 commit comments

Comments
 (0)