Skip to content
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

Support Redis URI #208

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
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
40 changes: 40 additions & 0 deletions src/NRedisStack/NRedisStackConnectionMultiplexer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using StackExchange.Redis;

namespace NRedisStack
{
public static class NRedisStackConnectionMultiplexer // check if I can use this name to ConnectionMultiplexer
shacharPash marked this conversation as resolved.
Show resolved Hide resolved
{
public static ConnectionMultiplexer Connect(string redisConnectionString)
shacharPash marked this conversation as resolved.
Show resolved Hide resolved
{
var options = RedisUriParser.ParseConfigFromUri(redisConnectionString);
return Connect(options);
}

public static Task<ConnectionMultiplexer> ConnectAsync(string redisConnectionString)
{
var options = RedisUriParser.ParseConfigFromUri(redisConnectionString);
return ConnectAsync(options);
}
public static ConnectionMultiplexer Connect(ConfigurationOptions options)
{
SetLibName(options);
// TODO: set here the library version when it will be available
return ConnectionMultiplexer.Connect(options);
}
public static Task<ConnectionMultiplexer> ConnectAsync(ConfigurationOptions options)
{
SetLibName(options);
// TODO: set here the library version when it will be available
return ConnectionMultiplexer.ConnectAsync(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}";

}
}
}
113 changes: 113 additions & 0 deletions src/NRedisStack/RedisUriParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
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 ParseConfigFromUri(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)
shacharPash marked this conversation as resolved.
Show resolved Hide resolved
{
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;
shacharPash marked this conversation as resolved.
Show resolved Hide resolved
var host = !string.IsNullOrEmpty(uri.Host) ? uri.Host : "localhost";
shacharPash marked this conversation as resolved.
Show resolved Hide resolved
options.EndPoints.Add($"{host}:{port}");
}

private static void ParseQueryArguments(ConfigurationOptions options, Uri uri)
{
if (!string.IsNullOrEmpty(uri.Query))
{
var queryArgs = ParseQuery(uri.Query.Substring(1));
if (queryArgs.Any(x => x.Key == "timeout"))
{
var timeout = int.Parse(queryArgs.First(x => x.Key == "timeout").Value);
options.AsyncTimeout = timeout;
options.SyncTimeout = timeout;
options.ConnectTimeout = timeout;
}

if (queryArgs.Any(x => x.Key.ToLower() == "clientname"))
{
options.ClientName = queryArgs.First(x => x.Key.ToLower() == "clientname").Value;
}

if (queryArgs.Any(x => x.Key.ToLower() == "sentinel_primary_name"))
{
options.ServiceName = queryArgs.First(x => x.Key.ToLower() == "sentinel_primary_name").Value;
}

foreach (var endpoint in queryArgs.Where(x => x.Key == "endpoint").Select(x => x.Value))
{
options.EndPoints.Add(endpoint);
}
}
}
}
}
Loading