Skip to content

Support Redis URI #208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 8 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
41 changes: 0 additions & 41 deletions src/NRedisStack/Auxiliary.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
using NRedisStack.Core;
using NRedisStack.RedisStackCommands;
using StackExchange.Redis;

namespace NRedisStack
{
public static class Auxiliary
{
private static string? _libraryName = $"NRedisStack;.NET-{Environment.Version}";
private static bool _setInfo = true;
public static void ResetInfoDefaults()
{
_setInfo = true;
_libraryName = $"NRedisStack;.NET-{Environment.Version}";
}
public static List<object> MergeArgs(RedisKey key, params RedisValue[] items)
{
var args = new List<object>(items.Length + 1) { key };
Expand All @@ -36,48 +28,15 @@ public static object[] AssembleNonNullArguments(params object?[] arguments)

// public static IDatabase GetDatabase(this ConnectionMultiplexer redis) => redis.GetDatabase("", "");

// TODO: add all the signatures of GetDatabase
public static IDatabase GetDatabase(this ConnectionMultiplexer redis,
string? LibraryName)
{
var _db = redis.GetDatabase();
if (LibraryName == null) // the user wants to disable the library name and version sending
_setInfo = false;

else // the user set his own the library name
_libraryName = $"NRedisStack({LibraryName});.NET-{Environment.Version})";

return _db;
}

private static void SetInfoInPipeline(this IDatabase db)
{
if (_libraryName == null) return;
Pipeline pipeline = new Pipeline(db);
_ = pipeline.Db.ClientSetInfoAsync(SetInfoAttr.LibraryName, _libraryName!);
_ = pipeline.Db.ClientSetInfoAsync(SetInfoAttr.LibraryVersion, GetNRedisStackVersion());
pipeline.Execute();
}

public static RedisResult Execute(this IDatabase db, SerializedCommand command)
{
var compareVersions = db.Multiplexer.GetServer(db.Multiplexer.GetEndPoints()[0]).Version.CompareTo(new Version(7, 1, 242));
if (_setInfo && compareVersions >= 0)
{
_setInfo = false;
db.SetInfoInPipeline();
}
return db.Execute(command.Command, command.Args);
}

public async static Task<RedisResult> ExecuteAsync(this IDatabaseAsync db, SerializedCommand command)
{
var compareVersions = db.Multiplexer.GetServer(db.Multiplexer.GetEndPoints()[0]).Version.CompareTo(new Version(7, 1, 242));
if (_setInfo && compareVersions >= 0)
{
_setInfo = false;
((IDatabase)db).SetInfoInPipeline();
}
return await db.ExecuteAsync(command.Command, command.Args);
}

Expand Down
47 changes: 47 additions & 0 deletions src/NRedisStack/Configuration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using NRedisStack.RedisStackCommands;
using StackExchange.Redis;

namespace NRedisStack
{
public class Configuration
{
private ConfigurationOptions _options = new ConfigurationOptions();

public static Configuration Parse(string redisConnectionString) =>
new Configuration().DoParse(redisConnectionString);

public Configuration DoParse(string redisConnectionString)
{
try // Redis URI parsing
{
_options = RedisUriParser.FromUri(redisConnectionString);
}
catch (UriFormatException) // StackExchange.Redis connection string parsing
{
_options = ConfigurationOptions.Parse(redisConnectionString);
}
SetLibName(_options);
return this;
}


Configuration()
{
SetLibName(_options);
}

public ConfigurationOptions GetOptions()
{
return _options;
}

private static void SetLibName(ConfigurationOptions options)
{
if (options.LibraryName != null) // the user set his own the library name
options.LibraryName = $"NRedisStack({options.LibraryName});.NET-{Environment.Version})";
else // the default library name and version sending
options.LibraryName = $"NRedisStack;.NET-{Environment.Version}";
}

}
}
37 changes: 37 additions & 0 deletions src/NRedisStack/ConnectionManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using StackExchange.Redis;

namespace NRedisStack
{
public static class ConnectionManager
{
public static IConnectionMultiplexer Connect(string redisConnectionString)
{
return Connect(Configuration.Parse(redisConnectionString));
}

public static async Task<IConnectionMultiplexer> ConnectAsync(string redisConnectionString)
{
return await ConnectAsync(Configuration.Parse(redisConnectionString));
}

public static IConnectionMultiplexer Connect(Configuration options)
{
return ConnectionMultiplexer.Connect(options.GetOptions());
}

public static async Task<IConnectionMultiplexer> ConnectAsync(Configuration options)
{
return await ConnectionMultiplexer.ConnectAsync(options.GetOptions());
}

public static IConnectionMultiplexer Connect(ConfigurationOptions options)
{
return ConnectionMultiplexer.Connect(options);
}

public static async Task<IConnectionMultiplexer> ConnectAsync(ConfigurationOptions options)
{
return await ConnectionMultiplexer.ConnectAsync(options);
}
}
}
2 changes: 1 addition & 1 deletion src/NRedisStack/NRedisStack.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<ItemGroup>
<PackageReference Include="NetTopologySuite" Version="2.5.0" />
<PackageReference Include="System.Text.Json" Version="7.0.2" Condition="'$(TargetFramework)' == 'netstandard2.0'" />
<PackageReference Include="StackExchange.Redis" Version="2.6.122" />
<PackageReference Include="StackExchange.Redis" Version="2.7.4" />
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>

Expand Down
134 changes: 134 additions & 0 deletions src/NRedisStack/RedisUriParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using StackExchange.Redis;

[assembly: InternalsVisibleTo("NRedisStack.Tests")]

namespace NRedisStack
{
/// <summary>
/// URI parsing utility.
/// </summary>
internal static class RedisUriParser
{
/// <summary>
/// Parses a Config options for StackExchange Redis from the URI.
/// </summary>
/// <param name="redisUri">Redis Uri string</param>
/// <returns>A configuration options result for SE.Redis.</returns>
internal static ConfigurationOptions FromUri(string redisUri)
{
var options = new ConfigurationOptions();

if (string.IsNullOrEmpty(redisUri))
{
options.EndPoints.Add("localhost:6379");
return options;
}

var uri = new Uri(redisUri);
ParseHost(options, uri);
ParseUserInfo(options, uri);
ParseQueryArguments(options, uri);
ParseDefaultDatabase(options, uri);
options.Ssl = uri.Scheme == "rediss";
options.AbortOnConnectFail = false;
return options;
}

private static void ParseDefaultDatabase(ConfigurationOptions options, Uri uri)
{
if (string.IsNullOrEmpty(uri.AbsolutePath))
{
return;
}

var dbNumStr = Regex.Match(uri.AbsolutePath, "[0-9]+").Value;
int dbNum;
if (int.TryParse(dbNumStr, out dbNum))
{
options.DefaultDatabase = dbNum;
}
}

private static IList<KeyValuePair<string, string>> ParseQuery(string query) =>
query.Split('&').Select(x =>
new KeyValuePair<string, string>(x.Split('=').First(), x.Split('=').Last())).ToList();

private static void ParseUserInfo(ConfigurationOptions options, Uri uri)
{
if (!string.IsNullOrEmpty(uri.UserInfo))
{
var userInfo = uri.UserInfo.Split(':');
if (userInfo.Length > 1)
{
options.User = Uri.UnescapeDataString(userInfo[0]);
options.Password = Uri.UnescapeDataString(userInfo[1]);
}
else
{
throw new FormatException(
"Username and password must be in the form username:password - if there is no username use the format :password");
}
}
}

private static void ParseHost(ConfigurationOptions options, Uri uri)
{
var port = uri.Port >= 0 ? uri.Port : 6379;
var host = !string.IsNullOrEmpty(uri.Host) ? uri.Host : "localhost";
options.EndPoints.Add($"{host}:{port}");
}

private static void ParseQueryArguments(ConfigurationOptions options, Uri uri)
{
if (string.IsNullOrEmpty(uri.Query))
{
return;
}

var queryArgs = ParseQuery(uri.Query.Substring(1));

var actions = new Dictionary<string, Action<string>>(StringComparer.OrdinalIgnoreCase)
{
{ "timeout", value => SetTimeoutOptions(options, value) },
{ "clientname", value => options.ClientName = value },
{ "sentinel_primary_name", value => options.ServiceName = value },
{ "endpoint", value => options.EndPoints.Add(value) },
{ "allowadmin", value => options.AllowAdmin = bool.Parse(value) },
{ "abortConnect", value => options.AbortOnConnectFail = bool.Parse(value) },
{ "asynctimeout", value => options.AsyncTimeout = int.Parse(value) },
{ "retry", value => options.ConnectRetry = int.Parse(value) },
{ "protocol", value => ParseRedisProtocol(options, value) }
// TODO: add more options
};

foreach (var arg in queryArgs.Where(arg => actions.ContainsKey(arg.Key)))
{
actions[arg.Key.ToLower()](arg.Value);
}
}

private static RedisProtocol ParseRedisProtocol(ConfigurationOptions options, string value)
{
switch (value)
{
case "2":
return RedisProtocol.Resp2;
case "3":
return RedisProtocol.Resp3;
default:
throw new FormatException("Invalid protocol specified");
}
}

private static void SetTimeoutOptions(ConfigurationOptions options, string value)
{
var timeout = int.Parse(value);
options.AsyncTimeout = timeout;
options.SyncTimeout = timeout;
options.ConnectTimeout = timeout;
}

}
}
2 changes: 1 addition & 1 deletion tests/Doc/Doc.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="StackExchange.Redis" Version="2.6.122" />
<PackageReference Include="StackExchange.Redis" Version="2.7.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\NRedisStack\NRedisStack.csproj" />
Expand Down
Loading