From ecd118950a14857723d9affdd2154194720c6fe8 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Mon, 9 Feb 2026 18:51:46 +0000 Subject: [PATCH 1/7] test --- tests/ConsoleTest/Program.cs | 213 ++++------------------------------- 1 file changed, 25 insertions(+), 188 deletions(-) diff --git a/tests/ConsoleTest/Program.cs b/tests/ConsoleTest/Program.cs index 98d96f259..27f615be2 100644 --- a/tests/ConsoleTest/Program.cs +++ b/tests/ConsoleTest/Program.cs @@ -1,188 +1,25 @@ -using System.Diagnostics; -using System.Reflection; -using StackExchange.Redis; - -Stopwatch stopwatch = new Stopwatch(); -stopwatch.Start(); - -var options = ConfigurationOptions.Parse("127.0.0.1"); -#if !SEREDIS_BASELINE -options.HighIntegrity = false; // as needed -Console.WriteLine($"{nameof(options.HighIntegrity)}: {options.HighIntegrity}"); -#endif - -// options.SocketManager = SocketManager.ThreadPool; -Console.WriteLine("Connecting..."); -var connection = ConnectionMultiplexer.Connect(options); -Console.WriteLine("Connected"); -connection.ConnectionFailed += Connection_ConnectionFailed; - -void Connection_ConnectionFailed(object? sender, ConnectionFailedEventArgs e) -{ - Console.Error.WriteLine($"CONNECTION FAILED: {e.ConnectionType}, {e.FailureType}, {e.Exception}"); -} - -var startTime = DateTime.UtcNow; -var startCpuUsage = Process.GetCurrentProcess().TotalProcessorTime; - -var scenario = args?.Length > 0 ? args[0] : "mass-insert-async"; - -switch (scenario) -{ - case "parallel": - Console.WriteLine("Parallel task test..."); - ParallelTasks(connection); - break; - case "mass-insert": - Console.WriteLine("Mass insert test..."); - MassInsert(connection); - break; - case "mass-insert-async": - Console.WriteLine("Mass insert (async/pipelined) test..."); - await MassInsertAsync(connection); - break; - case "mass-publish": - Console.WriteLine("Mass publish test..."); - MassPublish(connection); - break; - default: - Console.WriteLine("Scenario " + scenario + " is not recognized"); - break; -} - -stopwatch.Stop(); - -Console.WriteLine(""); -Console.WriteLine($"Done. {stopwatch.ElapsedMilliseconds} ms"); - -var endTime = DateTime.UtcNow; -var endCpuUsage = Process.GetCurrentProcess().TotalProcessorTime; -var cpuUsedMs = (endCpuUsage - startCpuUsage).TotalMilliseconds; -var totalMsPassed = (endTime - startTime).TotalMilliseconds; -var cpuUsageTotal = cpuUsedMs / (Environment.ProcessorCount * totalMsPassed); -Console.WriteLine("Avg CPU: " + (cpuUsageTotal * 100)); -Console.WriteLine("Lib Version: " + GetLibVersion()); - -static void MassInsert(ConnectionMultiplexer connection) -{ - const int NUM_INSERTIONS = 100_000; - const int BATCH = 5000; - int matchErrors = 0; - - var database = connection.GetDatabase(0); - - for (int i = 0; i < NUM_INSERTIONS; i++) - { - var key = $"StackExchange.Redis.Test.{i}"; - var value = i.ToString(); - - database.StringSet(key, value); - var retrievedValue = database.StringGet(key); - - if (retrievedValue != value) - { - matchErrors++; - } - - if (i > 0 && i % BATCH == 0) - { - Console.WriteLine(i); - } - } - - Console.WriteLine($"Match errors: {matchErrors}"); -} - -static async Task MassInsertAsync(ConnectionMultiplexer connection) -{ - const int NUM_INSERTIONS = 100_000; - const int BATCH = 5000; - int matchErrors = 0; - - var database = connection.GetDatabase(0); - - var outstanding = new List<(Task, Task, string)>(BATCH); - - for (int i = 0; i < NUM_INSERTIONS; i++) - { - var key = $"StackExchange.Redis.Test.{i}"; - var value = i.ToString(); - - var set = database.StringSetAsync(key, value); - var get = database.StringGetAsync(key); - - outstanding.Add((set, get, value)); - - if (i > 0 && i % BATCH == 0) - { - matchErrors += await ValidateAsync(outstanding); - Console.WriteLine(i); - } - } - - matchErrors += await ValidateAsync(outstanding); - - Console.WriteLine($"Match errors: {matchErrors}"); - - static async Task ValidateAsync(List<(Task, Task, string)> outstanding) - { - int matchErrors = 0; - foreach (var row in outstanding) - { - var s = await row.Item2; - await row.Item1; - if (s != row.Item3) - { - matchErrors++; - } - } - outstanding.Clear(); - return matchErrors; - } -} - -static void ParallelTasks(ConnectionMultiplexer connection) -{ - static void ParallelRun(int taskId, ConnectionMultiplexer connection) - { - Console.Write($"{taskId} Started, "); - var database = connection.GetDatabase(0); - - for (int i = 0; i < 100000; i++) - { - database.StringSet(i.ToString(), i.ToString()); - } - - Console.Write($"{taskId} Insert completed, "); - - for (int i = 0; i < 100000; i++) - { - var result = database.StringGet(i.ToString()); - } - Console.Write($"{taskId} Completed, "); - } - - var taskList = new List(); - for (int i = 0; i < 10; i++) - { - var i1 = i; - var task = new Task(() => ParallelRun(i1, connection)); - task.Start(); - taskList.Add(task); - } - Task.WaitAll(taskList.ToArray()); -} - -static void MassPublish(ConnectionMultiplexer connection) -{ - var subscriber = connection.GetSubscriber(); - Parallel.For(0, 1000, _ => subscriber.Publish(new RedisChannel("cache-events:cache-testing", RedisChannel.PatternMode.Literal), "hey")); -} - -static string GetLibVersion() -{ - var assembly = typeof(ConnectionMultiplexer).Assembly; - return (Attribute.GetCustomAttribute(assembly, typeof(AssemblyFileVersionAttribute)) as AssemblyFileVersionAttribute)?.Version - ?? assembly.GetName().Version?.ToString() - ?? "Unknown"; -} +using StackExchange.Redis; + +// example server: +// docker run -d --name redis-tls -p 6379:3000 -p 6380:4430 -e TLS_ENABLED=yes -v ./my-tls:/redis/work/tls -e "TLS_CLIENT_CNS=MyUser1 MyUser2 MyUser3" redislabs/client-libs-test:custom-21183968220-debian-amd64 +// +// mimic: +// redis-cli +// -p 6380 +// --tls +// --cacert /home/marc/tls/my-tls/ca.crt +// --cert /home/marc/tls/my-tls/MyUser2.crt +// --key /home/marc/tls/my-tls/MyUser2.key client info +string certRoot = "/home/marc/tls/my-tls/"; +var config = ConfigurationOptions.Parse("localhost:6380"); +config.SetUserPemCertificate(// automatically enables TLS + userCertificatePath: Path.Combine(certRoot, "MyUser2.crt"), + userKeyPath: Path.Combine(certRoot, "MyUser2.key")); +config.TrustIssuer(Path.Combine(certRoot, "ca.crt")); + +await using var conn = await ConnectionMultiplexer.ConnectAsync(config, Console.Out); + +// prove we are connected as MyUser2 +var info = (string?)await conn.GetDatabase().ExecuteAsync("CLIENT", "INFO"); +Console.WriteLine(); +Console.WriteLine(info); From a3a5671b632acea18c6c2377809daf09dbf0121a Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Tue, 10 Feb 2026 07:39:37 +0000 Subject: [PATCH 2/7] docs --- docs/Authentication.md | 124 +++++++++++++++++++++++++++++++++++++++++ docs/index.md | 1 + 2 files changed, 125 insertions(+) create mode 100644 docs/Authentication.md diff --git a/docs/Authentication.md b/docs/Authentication.md new file mode 100644 index 000000000..3a8cad4c6 --- /dev/null +++ b/docs/Authentication.md @@ -0,0 +1,124 @@ +Using Client Certificates +=== + +There are multiple ways of connecting to a Redis server, depending on the authentication model. The simplest +(but least secure) approach is to use the `default` user, with no authentication, and no transport security. +This is as simple as: + +``` csharp +var muxer = ConnectionMultiplexer.Connect("myserver"); // or myserver:1241 to use a custom port +``` + +This approach is often used for local transient servers - it is simple, but insecure. But from there, +we can get more complex! + +TLS +=== + +If your server has TLS enabled, SE.Redis can be instructed to use it. In some cases (AMR, etc), the +library will recognize the endpoint address, meaning: *you do not need to do anything*. To +*manually* enable TLS, the `ssl` token can be used: + +``` csharp +var muxer = ConnectionMultiplexer.Connect("myserver,ssl=true"); +``` + +This will work fine if the server is using a server-certificate that is already trusted by the local +machine. If this is *not* the case, we need to tell the library about the server. This requires +the `ConfigurationOptions` type: + +``` csharp +var options = ConfigurationOptions.Parse("myserver,ssl=true"); +// or: var options = new ConfigurationOptions { Endpoints = { "myserver" }, Ssl = true }; +// TODO configure +var muxer = ConnectionMultiplexer.Connect(options); +``` + +If we have a local *issuer* public certificate (commonly `ca.crt`), we can use: + +``` csharp +options.TrustIssuer(caPath); +``` + +Alternatively, in advanced scenarios: to provide your own custom server validation, the `options.CertificateValidation` callback +can be used; this uses the normal [`RemoteCertificateValidationCallback`](https://learn.microsoft.com/dotnet/api/system.net.security.remotecertificatevalidationcallback) +API. + +Usernames and Passwords +=== + +Usernames and passwords can be specified with the `user` and `password` tokens, respectively: + +``` csharp +var muxer = ConnectionMultiplexer.Connect("myserver,ssl=true,user=myuser,password=mypassword"); +``` + +If no `user` is provided, the `default` user is assumed. In some cases, an authentication-token can be +used in place of a classic password. + +Client certificates +=== + +If the server is configured to require a client certificate, this can be supplied in multiple ways. +If you have a local public / private key pair (such as `MyUser2.crt` and `MyUser2.key`), the +`options.SetUserPemCertificate(...)` method can be used: + +``` csharp +config.SetUserPemCertificate( + userCertificatePath: userCrtPatah, + userKeyPath: userKeyPath +); +``` + +If you have a single `pfx` file that contains the public / private pair, the `options.SetUserPfxCertificate(...)` +method can be used: + +``` csharp +config.SetUserPfxCertificate( + userCertificatePath: userCrtPatah, + password: filePassword // optional +); +``` + +Alternatively, in advanced scenarios: to provide your own custom client-certificate lookup, the `options.CertificateSelection` callback +can be used; this uses the normal +[`LocalCertificateSelectionCallback`](https://learn.microsoft.com/dotnet/api/system.net.security.remotecertificatevalidationcallback) +API. + +User certificates with implicit user authentication +=== + +Historically, the client certificate only provided access to the server, but as the `default` user. From 8.6, +the server can be configured to use client certificates to provide user identity. This replaces the +usage of passwords, and requires: + +- An 8.6+ server, configured to use TLS with client certificates mapped - typically using the `CN` of the certificate as the user. +- A matching `ACL` user account configured on the server, that is enabled (`on`) - i.e. the `ACL LIST` command should + display something like `user MyUser2 on sanitize-payload ~* &* +@all` (the details will vary depending on the user permissions). +- At the client: access to the client certificate pair. + +For example: + +``` csharp +string certRoot = // some path to a folder with ca.crt, MyUSer2.crt and MyUser2.key + +var options = ConfigurationOptions.Parse("myserver:6380"); +options.SetUserPemCertificate(// automatically enables TLS + userCertificatePath: Path.Combine(certRoot, "MyUser2.crt"), + userKeyPath: Path.Combine(certRoot, "MyUser2.key")); +options.TrustIssuer(Path.Combine(certRoot, "ca.crt")); +await using var conn = await ConnectionMultiplexer.ConnectAsync(options); + +// prove we are connected as MyUser2 +var user = (string?)await conn.GetDatabase().ExecuteAsync("acl", "whoami"); +Console.WriteLine(user); // writes "MyUser2" +``` + +More info +=== + +For more information: + +- [Redis Security](https://redis.io/docs/latest/operate/oss_and_stack/management/security/) + - [ACL](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/) + - [TLS](https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption/) \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index b1498d878..00b78a642 100644 --- a/docs/index.md +++ b/docs/index.md @@ -31,6 +31,7 @@ Documentation --- - [Server](Server) - running a redis server +- [Authentication](Authentication.md) - connecting to a Redis server with user authentication - [Basic Usage](Basics) - getting started and basic usage - [Async Timeouts](AsyncTimeouts) - async timeouts and cancellation - [Configuration](Configuration) - options available when connecting to redis From 10799436f41f55991a8ba4c9e480d7619c341384 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Tue, 10 Feb 2026 07:42:05 +0000 Subject: [PATCH 3/7] Revert "test" This reverts commit ecd118950a14857723d9affdd2154194720c6fe8. --- tests/ConsoleTest/Program.cs | 213 +++++++++++++++++++++++++++++++---- 1 file changed, 188 insertions(+), 25 deletions(-) diff --git a/tests/ConsoleTest/Program.cs b/tests/ConsoleTest/Program.cs index 27f615be2..98d96f259 100644 --- a/tests/ConsoleTest/Program.cs +++ b/tests/ConsoleTest/Program.cs @@ -1,25 +1,188 @@ -using StackExchange.Redis; - -// example server: -// docker run -d --name redis-tls -p 6379:3000 -p 6380:4430 -e TLS_ENABLED=yes -v ./my-tls:/redis/work/tls -e "TLS_CLIENT_CNS=MyUser1 MyUser2 MyUser3" redislabs/client-libs-test:custom-21183968220-debian-amd64 -// -// mimic: -// redis-cli -// -p 6380 -// --tls -// --cacert /home/marc/tls/my-tls/ca.crt -// --cert /home/marc/tls/my-tls/MyUser2.crt -// --key /home/marc/tls/my-tls/MyUser2.key client info -string certRoot = "/home/marc/tls/my-tls/"; -var config = ConfigurationOptions.Parse("localhost:6380"); -config.SetUserPemCertificate(// automatically enables TLS - userCertificatePath: Path.Combine(certRoot, "MyUser2.crt"), - userKeyPath: Path.Combine(certRoot, "MyUser2.key")); -config.TrustIssuer(Path.Combine(certRoot, "ca.crt")); - -await using var conn = await ConnectionMultiplexer.ConnectAsync(config, Console.Out); - -// prove we are connected as MyUser2 -var info = (string?)await conn.GetDatabase().ExecuteAsync("CLIENT", "INFO"); -Console.WriteLine(); -Console.WriteLine(info); +using System.Diagnostics; +using System.Reflection; +using StackExchange.Redis; + +Stopwatch stopwatch = new Stopwatch(); +stopwatch.Start(); + +var options = ConfigurationOptions.Parse("127.0.0.1"); +#if !SEREDIS_BASELINE +options.HighIntegrity = false; // as needed +Console.WriteLine($"{nameof(options.HighIntegrity)}: {options.HighIntegrity}"); +#endif + +// options.SocketManager = SocketManager.ThreadPool; +Console.WriteLine("Connecting..."); +var connection = ConnectionMultiplexer.Connect(options); +Console.WriteLine("Connected"); +connection.ConnectionFailed += Connection_ConnectionFailed; + +void Connection_ConnectionFailed(object? sender, ConnectionFailedEventArgs e) +{ + Console.Error.WriteLine($"CONNECTION FAILED: {e.ConnectionType}, {e.FailureType}, {e.Exception}"); +} + +var startTime = DateTime.UtcNow; +var startCpuUsage = Process.GetCurrentProcess().TotalProcessorTime; + +var scenario = args?.Length > 0 ? args[0] : "mass-insert-async"; + +switch (scenario) +{ + case "parallel": + Console.WriteLine("Parallel task test..."); + ParallelTasks(connection); + break; + case "mass-insert": + Console.WriteLine("Mass insert test..."); + MassInsert(connection); + break; + case "mass-insert-async": + Console.WriteLine("Mass insert (async/pipelined) test..."); + await MassInsertAsync(connection); + break; + case "mass-publish": + Console.WriteLine("Mass publish test..."); + MassPublish(connection); + break; + default: + Console.WriteLine("Scenario " + scenario + " is not recognized"); + break; +} + +stopwatch.Stop(); + +Console.WriteLine(""); +Console.WriteLine($"Done. {stopwatch.ElapsedMilliseconds} ms"); + +var endTime = DateTime.UtcNow; +var endCpuUsage = Process.GetCurrentProcess().TotalProcessorTime; +var cpuUsedMs = (endCpuUsage - startCpuUsage).TotalMilliseconds; +var totalMsPassed = (endTime - startTime).TotalMilliseconds; +var cpuUsageTotal = cpuUsedMs / (Environment.ProcessorCount * totalMsPassed); +Console.WriteLine("Avg CPU: " + (cpuUsageTotal * 100)); +Console.WriteLine("Lib Version: " + GetLibVersion()); + +static void MassInsert(ConnectionMultiplexer connection) +{ + const int NUM_INSERTIONS = 100_000; + const int BATCH = 5000; + int matchErrors = 0; + + var database = connection.GetDatabase(0); + + for (int i = 0; i < NUM_INSERTIONS; i++) + { + var key = $"StackExchange.Redis.Test.{i}"; + var value = i.ToString(); + + database.StringSet(key, value); + var retrievedValue = database.StringGet(key); + + if (retrievedValue != value) + { + matchErrors++; + } + + if (i > 0 && i % BATCH == 0) + { + Console.WriteLine(i); + } + } + + Console.WriteLine($"Match errors: {matchErrors}"); +} + +static async Task MassInsertAsync(ConnectionMultiplexer connection) +{ + const int NUM_INSERTIONS = 100_000; + const int BATCH = 5000; + int matchErrors = 0; + + var database = connection.GetDatabase(0); + + var outstanding = new List<(Task, Task, string)>(BATCH); + + for (int i = 0; i < NUM_INSERTIONS; i++) + { + var key = $"StackExchange.Redis.Test.{i}"; + var value = i.ToString(); + + var set = database.StringSetAsync(key, value); + var get = database.StringGetAsync(key); + + outstanding.Add((set, get, value)); + + if (i > 0 && i % BATCH == 0) + { + matchErrors += await ValidateAsync(outstanding); + Console.WriteLine(i); + } + } + + matchErrors += await ValidateAsync(outstanding); + + Console.WriteLine($"Match errors: {matchErrors}"); + + static async Task ValidateAsync(List<(Task, Task, string)> outstanding) + { + int matchErrors = 0; + foreach (var row in outstanding) + { + var s = await row.Item2; + await row.Item1; + if (s != row.Item3) + { + matchErrors++; + } + } + outstanding.Clear(); + return matchErrors; + } +} + +static void ParallelTasks(ConnectionMultiplexer connection) +{ + static void ParallelRun(int taskId, ConnectionMultiplexer connection) + { + Console.Write($"{taskId} Started, "); + var database = connection.GetDatabase(0); + + for (int i = 0; i < 100000; i++) + { + database.StringSet(i.ToString(), i.ToString()); + } + + Console.Write($"{taskId} Insert completed, "); + + for (int i = 0; i < 100000; i++) + { + var result = database.StringGet(i.ToString()); + } + Console.Write($"{taskId} Completed, "); + } + + var taskList = new List(); + for (int i = 0; i < 10; i++) + { + var i1 = i; + var task = new Task(() => ParallelRun(i1, connection)); + task.Start(); + taskList.Add(task); + } + Task.WaitAll(taskList.ToArray()); +} + +static void MassPublish(ConnectionMultiplexer connection) +{ + var subscriber = connection.GetSubscriber(); + Parallel.For(0, 1000, _ => subscriber.Publish(new RedisChannel("cache-events:cache-testing", RedisChannel.PatternMode.Literal), "hey")); +} + +static string GetLibVersion() +{ + var assembly = typeof(ConnectionMultiplexer).Assembly; + return (Attribute.GetCustomAttribute(assembly, typeof(AssemblyFileVersionAttribute)) as AssemblyFileVersionAttribute)?.Version + ?? assembly.GetName().Version?.ToString() + ?? "Unknown"; +} From f8d30921730649e1e3f3f055919e9030c9716491 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Tue, 10 Feb 2026 08:09:39 +0000 Subject: [PATCH 4/7] Update Authentication.md --- docs/Authentication.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Authentication.md b/docs/Authentication.md index 3a8cad4c6..42e0d460f 100644 --- a/docs/Authentication.md +++ b/docs/Authentication.md @@ -100,7 +100,7 @@ usage of passwords, and requires: For example: ``` csharp -string certRoot = // some path to a folder with ca.crt, MyUSer2.crt and MyUser2.key +string certRoot = // some path to a folder with ca.crt, MyUser2.crt and MyUser2.key var options = ConfigurationOptions.Parse("myserver:6380"); options.SetUserPemCertificate(// automatically enables TLS @@ -121,4 +121,4 @@ For more information: - [Redis Security](https://redis.io/docs/latest/operate/oss_and_stack/management/security/) - [ACL](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/) - - [TLS](https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption/) \ No newline at end of file + - [TLS](https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption/) From 9a511a5fcc15a4fa175962a1807a5cc7864f61ec Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Tue, 10 Feb 2026 08:26:46 +0000 Subject: [PATCH 5/7] Update Authentication.md --- docs/Authentication.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Authentication.md b/docs/Authentication.md index 42e0d460f..5c86a0591 100644 --- a/docs/Authentication.md +++ b/docs/Authentication.md @@ -1,4 +1,4 @@ -Using Client Certificates +Authentication === There are multiple ways of connecting to a Redis server, depending on the authentication model. The simplest @@ -65,7 +65,7 @@ If you have a local public / private key pair (such as `MyUser2.crt` and `MyUser ``` csharp config.SetUserPemCertificate( - userCertificatePath: userCrtPatah, + userCertificatePath: userCrtPath, userKeyPath: userKeyPath ); ``` @@ -75,7 +75,7 @@ method can be used: ``` csharp config.SetUserPfxCertificate( - userCertificatePath: userCrtPatah, + userCertificatePath: userCrtPath, password: filePassword // optional ); ``` From dd04387d29173a2cd3494a39c445251d45567bb8 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Tue, 10 Feb 2026 08:30:09 +0000 Subject: [PATCH 6/7] Update index.md --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 00b78a642..323c09fd8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -31,7 +31,7 @@ Documentation --- - [Server](Server) - running a redis server -- [Authentication](Authentication.md) - connecting to a Redis server with user authentication +- [Authentication](Authentication) - connecting to a Redis server with user authentication - [Basic Usage](Basics) - getting started and basic usage - [Async Timeouts](AsyncTimeouts) - async timeouts and cancellation - [Configuration](Configuration) - options available when connecting to redis From 45cc35465223c2d220ba5bfd9d18085474282633 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Wed, 11 Feb 2026 16:17:08 +0000 Subject: [PATCH 7/7] prefer async --- docs/Authentication.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/Authentication.md b/docs/Authentication.md index 5c86a0591..f5551559d 100644 --- a/docs/Authentication.md +++ b/docs/Authentication.md @@ -6,7 +6,7 @@ There are multiple ways of connecting to a Redis server, depending on the authen This is as simple as: ``` csharp -var muxer = ConnectionMultiplexer.Connect("myserver"); // or myserver:1241 to use a custom port +var muxer = await ConnectionMultiplexer.ConnectAsync("myserver"); // or myserver:1241 to use a custom port ``` This approach is often used for local transient servers - it is simple, but insecure. But from there, @@ -20,7 +20,7 @@ library will recognize the endpoint address, meaning: *you do not need to do any *manually* enable TLS, the `ssl` token can be used: ``` csharp -var muxer = ConnectionMultiplexer.Connect("myserver,ssl=true"); +var muxer = await ConnectionMultiplexer.ConnectAsync("myserver,ssl=true"); ``` This will work fine if the server is using a server-certificate that is already trusted by the local @@ -31,7 +31,7 @@ the `ConfigurationOptions` type: var options = ConfigurationOptions.Parse("myserver,ssl=true"); // or: var options = new ConfigurationOptions { Endpoints = { "myserver" }, Ssl = true }; // TODO configure -var muxer = ConnectionMultiplexer.Connect(options); +var muxer = await ConnectionMultiplexer.ConnectAsync(options); ``` If we have a local *issuer* public certificate (commonly `ca.crt`), we can use: @@ -50,7 +50,7 @@ Usernames and Passwords Usernames and passwords can be specified with the `user` and `password` tokens, respectively: ``` csharp -var muxer = ConnectionMultiplexer.Connect("myserver,ssl=true,user=myuser,password=mypassword"); +var muxer = await ConnectionMultiplexer.ConnectAsync("myserver,ssl=true,user=myuser,password=mypassword"); ``` If no `user` is provided, the `default` user is assumed. In some cases, an authentication-token can be