Skip to content

Commit c8a7265

Browse files
authored
format ipv6 endpoints correctly (#2813)
1 parent d7ca832 commit c8a7265

File tree

4 files changed

+49
-32
lines changed

4 files changed

+49
-32
lines changed

src/StackExchange.Redis/Format.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,20 @@ internal static string ToString(EndPoint? endpoint)
9797
if (dns.Port == 0) return dns.Host;
9898
return dns.Host + ":" + Format.ToString(dns.Port);
9999
case IPEndPoint ip:
100-
if (ip.Port == 0) return ip.Address.ToString();
100+
var addr = ip.Address.ToString();
101+
102+
if (ip.Port == 0)
103+
{
104+
// no port specified; use naked IP
105+
return addr;
106+
}
107+
108+
if (addr.IndexOf(':') >= 0)
109+
{
110+
// ipv6 with port; use "[IP]:port" notation
111+
return "[" + addr + "]:" + Format.ToString(ip.Port);
112+
}
113+
// ipv4 with port; use "IP:port" notation
101114
return ip.Address + ":" + Format.ToString(ip.Port);
102115
#if UNIX_SOCKET
103116
case UnixDomainSocketEndPoint uds:

tests/StackExchange.Redis.Tests/ConnectionFailedErrorsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ public async Task CheckFailureRecovered()
178178
{
179179
using var conn = Create(keepAlive: 1, connectTimeout: 10000, allowAdmin: true, log: Writer, shared: false);
180180

181-
await RunBlockingSynchronousWithExtraThreadAsync(innerScenario).ForAwait();
182-
void innerScenario()
181+
await RunBlockingSynchronousWithExtraThreadAsync(InnerScenario).ForAwait();
182+
void InnerScenario()
183183
{
184184
conn.GetDatabase();
185185
var server = conn.GetServer(conn.GetEndPoints()[0]);

tests/StackExchange.Redis.Tests/FailoverTests.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,7 @@ public async Task SubscriptionsSurviveConnectionFailureAsync()
208208
var sub = conn.GetSubscriber();
209209
int counter = 0;
210210
Assert.True(sub.IsConnected());
211-
await sub.SubscribeAsync(channel, delegate
212-
{
213-
Interlocked.Increment(ref counter);
214-
}).ConfigureAwait(false);
211+
await sub.SubscribeAsync(channel, (arg1, arg2) => Interlocked.Increment(ref counter)).ConfigureAwait(false);
215212

216213
var profile1 = Log(profiler);
217214

@@ -257,8 +254,7 @@ await sub.SubscribeAsync(channel, delegate
257254

258255
// Ensure we've sent the subscribe command after reconnecting
259256
var profile2 = Log(profiler);
260-
//Assert.Equal(1, profile2.Count(p => p.Command == nameof(RedisCommand.SUBSCRIBE)));
261-
257+
// Assert.Equal(1, profile2.Count(p => p.Command == nameof(RedisCommand.SUBSCRIBE)));
262258
Log("Issuing ping after reconnected");
263259
sub.Ping();
264260

@@ -395,7 +391,7 @@ public async Task SubscriptionsSurvivePrimarySwitchAsync()
395391
Log(" IsReplica: " + !server.IsReplica);
396392
Log(" Type: " + server.ServerType);
397393
}
398-
//Skip.Inconclusive("Not enough latency.");
394+
// Skip.Inconclusive("Not enough latency.");
399395
}
400396
Assert.True(sanityCheck, $"B Connection: {TestConfig.Current.FailoverPrimaryServerAndPort} should be a replica");
401397
Assert.False(bConn.GetServer(TestConfig.Current.FailoverReplicaServerAndPort).IsReplica, $"B Connection: {TestConfig.Current.FailoverReplicaServerAndPort} should be a primary");

tests/StackExchange.Redis.Tests/FormatTests.cs

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,46 @@ public class FormatTests : TestBase
1111
{
1212
public FormatTests(ITestOutputHelper output) : base(output) { }
1313

14-
public static IEnumerable<object[]> EndpointData()
14+
public static IEnumerable<object?[]> EndpointData()
1515
{
16+
// note: the 3rd arg is for formatting; null means "expect the original string"
17+
1618
// DNS
17-
yield return new object[] { "localhost", new DnsEndPoint("localhost", 0) };
18-
yield return new object[] { "localhost:6390", new DnsEndPoint("localhost", 6390) };
19-
yield return new object[] { "bob.the.builder.com", new DnsEndPoint("bob.the.builder.com", 0) };
20-
yield return new object[] { "bob.the.builder.com:6390", new DnsEndPoint("bob.the.builder.com", 6390) };
19+
yield return new object?[] { "localhost", new DnsEndPoint("localhost", 0), null };
20+
yield return new object?[] { "localhost:6390", new DnsEndPoint("localhost", 6390), null };
21+
yield return new object?[] { "bob.the.builder.com", new DnsEndPoint("bob.the.builder.com", 0), null };
22+
yield return new object?[] { "bob.the.builder.com:6390", new DnsEndPoint("bob.the.builder.com", 6390), null };
2123
// IPv4
22-
yield return new object[] { "0.0.0.0", new IPEndPoint(IPAddress.Parse("0.0.0.0"), 0) };
23-
yield return new object[] { "127.0.0.1", new IPEndPoint(IPAddress.Parse("127.0.0.1"), 0) };
24-
yield return new object[] { "127.1", new IPEndPoint(IPAddress.Parse("127.1"), 0) };
25-
yield return new object[] { "127.1:6389", new IPEndPoint(IPAddress.Parse("127.1"), 6389) };
26-
yield return new object[] { "127.0.0.1:6389", new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6389) };
27-
yield return new object[] { "127.0.0.1:1", new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1) };
28-
yield return new object[] { "127.0.0.1:2", new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2) };
29-
yield return new object[] { "10.10.9.18:2", new IPEndPoint(IPAddress.Parse("10.10.9.18"), 2) };
24+
yield return new object?[] { "0.0.0.0", new IPEndPoint(IPAddress.Parse("0.0.0.0"), 0), null };
25+
yield return new object?[] { "127.0.0.1", new IPEndPoint(IPAddress.Parse("127.0.0.1"), 0), null };
26+
yield return new object?[] { "127.1", new IPEndPoint(IPAddress.Parse("127.1"), 0), "127.0.0.1" };
27+
yield return new object?[] { "127.1:6389", new IPEndPoint(IPAddress.Parse("127.1"), 6389), "127.0.0.1:6389" };
28+
yield return new object?[] { "127.0.0.1:6389", new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6389), null };
29+
yield return new object?[] { "127.0.0.1:1", new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1), null };
30+
yield return new object?[] { "127.0.0.1:2", new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2), null };
31+
yield return new object?[] { "10.10.9.18:2", new IPEndPoint(IPAddress.Parse("10.10.9.18"), 2), null };
3032
// IPv6
31-
yield return new object[] { "::1", new IPEndPoint(IPAddress.Parse("::1"), 0) };
32-
yield return new object[] { "::1:6379", new IPEndPoint(IPAddress.Parse("::0.1.99.121"), 0) }; // remember your brackets!
33-
yield return new object[] { "[::1]:6379", new IPEndPoint(IPAddress.Parse("::1"), 6379) };
34-
yield return new object[] { "[::1]", new IPEndPoint(IPAddress.Parse("::1"), 0) };
35-
yield return new object[] { "[::1]:1000", new IPEndPoint(IPAddress.Parse("::1"), 1000) };
36-
yield return new object[] { "[2001:db7:85a3:8d2:1319:8a2e:370:7348]", new IPEndPoint(IPAddress.Parse("2001:db7:85a3:8d2:1319:8a2e:370:7348"), 0) };
37-
yield return new object[] { "[2001:db7:85a3:8d2:1319:8a2e:370:7348]:1000", new IPEndPoint(IPAddress.Parse("2001:db7:85a3:8d2:1319:8a2e:370:7348"), 1000) };
33+
yield return new object?[] { "::1", new IPEndPoint(IPAddress.Parse("::1"), 0), null };
34+
yield return new object?[] { "::1:6379", new IPEndPoint(IPAddress.Parse("::0.1.99.121"), 0), "::0.1.99.121" }; // remember your brackets!
35+
yield return new object?[] { "[::1]:6379", new IPEndPoint(IPAddress.Parse("::1"), 6379), null };
36+
yield return new object?[] { "[::1]", new IPEndPoint(IPAddress.Parse("::1"), 0), "::1" };
37+
yield return new object?[] { "[::1]:1000", new IPEndPoint(IPAddress.Parse("::1"), 1000), null };
38+
yield return new object?[] { "2001:db7:85a3:8d2:1319:8a2e:370:7348", new IPEndPoint(IPAddress.Parse("2001:db7:85a3:8d2:1319:8a2e:370:7348"), 0), null };
39+
yield return new object?[] { "[2001:db7:85a3:8d2:1319:8a2e:370:7348]", new IPEndPoint(IPAddress.Parse("2001:db7:85a3:8d2:1319:8a2e:370:7348"), 0), "2001:db7:85a3:8d2:1319:8a2e:370:7348" };
40+
yield return new object?[] { "[2001:db7:85a3:8d2:1319:8a2e:370:7348]:1000", new IPEndPoint(IPAddress.Parse("2001:db7:85a3:8d2:1319:8a2e:370:7348"), 1000), null };
3841
}
3942

4043
[Theory]
4144
[MemberData(nameof(EndpointData))]
42-
public void ParseEndPoint(string data, EndPoint expected)
45+
public void ParseEndPoint(string data, EndPoint expected, string? expectedFormat)
4346
{
44-
_ = Format.TryParseEndPoint(data, out var result);
47+
Assert.True(Format.TryParseEndPoint(data, out var result));
4548
Assert.Equal(expected, result);
49+
50+
// and write again
51+
var s = Format.ToString(result);
52+
expectedFormat ??= data;
53+
Assert.Equal(expectedFormat, s);
4654
}
4755

4856
[Theory]

0 commit comments

Comments
 (0)