Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/StackExchange.Redis/Format.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,18 @@ internal static bool TryParseEndPoint(string? addressWithPort, [NotNullWhen(true
}

#if UNIX_SOCKET
endpoint = new UnixDomainSocketEndPoint(addressWithPort.Substring(1));
var path = addressWithPort.Substring(1);
if (path[0] == '@' && OperatingSystem.IsLinux())
{
// "!@name" is the Linux ABSTRACT namespace, using the same '@' convention as
// socat/systemd (and redis-cli/redis-benchmark where supported): the kernel
// spelling is a leading NUL, which cannot appear in a config string. Linux-gated
// because no other platform has the namespace — elsewhere '@' stays a literal
// (strange) filename, matching what the other tools do. Note ToString() round-trips
// for free: UnixDomainSocketEndPoint renders abstract names back as "@name".
path = "\0" + path.Substring(1);
}
endpoint = new UnixDomainSocketEndPoint(path);
return true;
#else
throw new PlatformNotSupportedException("Unix domain sockets require .NET Core 3 or above");
Expand Down
27 changes: 27 additions & 0 deletions tests/StackExchange.Redis.Tests/FormatTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,33 @@ public void ParseEndPoint(string data, EndPoint expected, string? expectedFormat
Assert.Equal(expectedFormat, s);
}

// UDS endpoints live outside EndpointData because UnixDomainSocketEndPoint does not implement
// value equality — these compare via ToString instead.
#if NET
[Fact]
public void ParseUnixDomainSocketEndPoint()
{
Assert.True(Format.TryParseEndPoint("!/tmp/redis.sock", out var ep));
var uds = Assert.IsType<System.Net.Sockets.UnixDomainSocketEndPoint>(ep);
Assert.Equal("/tmp/redis.sock", uds.ToString());
Assert.Equal("!/tmp/redis.sock", Format.ToString(ep));
}

[Fact]
public void ParseAbstractUnixDomainSocketEndPoint()
{
Assert.SkipUnless(OperatingSystem.IsLinux(), "the abstract socket namespace is Linux-only");

// "!@name": socat/systemd '@' convention for the Linux abstract namespace. The parse maps it
// to the kernel's leading-NUL spelling; UnixDomainSocketEndPoint.ToString renders that back
// as '@name', so the config string round-trips exactly.
Assert.True(Format.TryParseEndPoint("!@redis-abstract", out var ep));
var uds = Assert.IsType<System.Net.Sockets.UnixDomainSocketEndPoint>(ep);
Assert.Equal("@redis-abstract", uds.ToString());
Assert.Equal("!@redis-abstract", Format.ToString(ep));
}
#endif

[Theory]
[InlineData(CommandFlags.None, "None")]
#if NETFRAMEWORK
Expand Down
Loading