Commit a9ba66de authored by Nick Craver's avatar Nick Craver

Cleanup: ConfigurationOptions

Lots of cleanup here to make additional bits later more straightforward and less conflicting.
parent 2413f06e
......@@ -25,8 +25,7 @@ private static class OptionKeys
{
public static int ParseInt32(string key, string value, int minValue = int.MinValue, int maxValue = int.MaxValue)
{
int tmp;
if (!Format.TryParseInt32(value, out tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires an integer value");
if (!Format.TryParseInt32(value, out int tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires an integer value");
if (tmp < minValue) throw new ArgumentOutOfRangeException("Keyword '" + key + "' has a minimum value of " + minValue);
if (tmp > maxValue) throw new ArgumentOutOfRangeException("Keyword '" + key + "' has a maximum value of " + maxValue);
return tmp;
......@@ -34,32 +33,30 @@ public static int ParseInt32(string key, string value, int minValue = int.MinVal
internal static bool ParseBoolean(string key, string value)
{
bool tmp;
if (!Format.TryParseBoolean(value, out tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires a boolean value");
if (!Format.TryParseBoolean(value, out bool tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires a boolean value");
return tmp;
}
internal static Version ParseVersion(string key, string value)
{
Version tmp;
if (!System.Version.TryParse(value, out tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires a version value");
if (!System.Version.TryParse(value, out Version tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires a version value");
return tmp;
}
internal static Proxy ParseProxy(string key, string value)
{
Proxy tmp;
if (!Enum.TryParse(value, true, out tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires a proxy value");
if (!Enum.TryParse(value, true, out Proxy tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires a proxy value");
return tmp;
}
internal static SslProtocols ParseSslProtocols(string key, string value)
{
SslProtocols tmp;
//Flags expect commas as separators, but we need to use '|' since commas are already used in the connection string to mean something else
value = value?.Replace("|", ",");
value = value?.Replace("|", ",");
if (!Enum.TryParse(value, true, out tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires an SslProtocol value (multiple values separated by '|').");
if (!Enum.TryParse(value, true, out SslProtocols tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires an SslProtocol value (multiple values separated by '|').");
return tmp;
return tmp;
}
internal static void Unknown(string key)
......@@ -67,32 +64,62 @@ internal static void Unknown(string key)
throw new ArgumentException("Keyword '" + key + "' is not supported");
}
internal const string AllowAdmin = "allowAdmin", SyncTimeout = "syncTimeout",
ServiceName = "serviceName", ClientName = "name", KeepAlive = "keepAlive",
Version = "version", ConnectTimeout = "connectTimeout", Password = "password",
TieBreaker = "tiebreaker", WriteBuffer = "writeBuffer", Ssl = "ssl", SslHost = "sslHost", HighPrioritySocketThreads = "highPriorityThreads",
ConfigChannel = "configChannel", AbortOnConnectFail = "abortConnect", ResolveDns = "resolveDns",
ChannelPrefix = "channelPrefix", Proxy = "proxy", ConnectRetry = "connectRetry",
ConfigCheckSeconds = "configCheckSeconds", ResponseTimeout = "responseTimeout", DefaultDatabase = "defaultDatabase";
internal const string SslProtocols = "sslProtocols";
internal const string PreserveAsyncOrder = "preserveAsyncOrder";
internal const string
AbortOnConnectFail = "abortConnect",
AllowAdmin = "allowAdmin",
ChannelPrefix = "channelPrefix",
ConfigChannel = "configChannel",
ConfigCheckSeconds = "configCheckSeconds",
ConnectRetry = "connectRetry",
ConnectTimeout = "connectTimeout",
DefaultDatabase = "defaultDatabase",
HighPrioritySocketThreads = "highPriorityThreads",
KeepAlive = "keepAlive",
ClientName = "name",
Password = "password",
PreserveAsyncOrder = "preserveAsyncOrder",
Proxy = "proxy",
ResolveDns = "resolveDns",
ResponseTimeout = "responseTimeout",
ServiceName = "serviceName",
Ssl = "ssl",
SslHost = "sslHost",
SslProtocols = "sslProtocols",
SyncTimeout = "syncTimeout",
TieBreaker = "tiebreaker",
Version = "version",
WriteBuffer = "writeBuffer";
private static readonly Dictionary<string, string> normalizedOptions = new[]
{
AllowAdmin, SyncTimeout,
ServiceName, ClientName, KeepAlive,
Version, ConnectTimeout, Password,
TieBreaker, WriteBuffer, Ssl, SslHost, HighPrioritySocketThreads,
ConfigChannel, AbortOnConnectFail, ResolveDns,
ChannelPrefix, Proxy, ConnectRetry,
ConfigCheckSeconds, DefaultDatabase,
SslProtocols, PreserveAsyncOrder
AbortOnConnectFail,
AllowAdmin,
ChannelPrefix,
ClientName,
ConfigChannel,
ConfigCheckSeconds,
ConnectRetry,
ConnectTimeout,
DefaultDatabase,
HighPrioritySocketThreads,
KeepAlive,
Password,
PreserveAsyncOrder,
Proxy,
ResolveDns,
ServiceName,
Ssl,
SslHost,
SslProtocols,
SyncTimeout,
TieBreaker,
Version,
WriteBuffer,
}.ToDictionary(x => x, StringComparer.OrdinalIgnoreCase);
public static string TryNormalize(string value)
{
string tmp;
if(value != null && normalizedOptions.TryGetValue(value, out tmp))
if (value != null && normalizedOptions.TryGetValue(value, out string tmp))
{
return tmp ?? "";
}
......@@ -100,12 +127,9 @@ public static string TryNormalize(string value)
}
}
private readonly EndPointCollection endpoints = new EndPointCollection();
private bool? allowAdmin, abortOnConnectFail, highPrioritySocketThreads, resolveDns, ssl, preserveAsyncOrder;
private string clientName, serviceName, password, tieBreaker, sslHost, configChannel;
private string tieBreaker, sslHost, configChannel;
private CommandMap commandMap;
......@@ -115,7 +139,7 @@ public static string TryNormalize(string value)
private Proxy? proxy;
private IReconnectRetryPolicy reconnectRetryPolicy;
private IReconnectRetryPolicy reconnectRetryPolicy;
/// <summary>
/// A LocalCertificateSelectionCallback delegate responsible for selecting the certificate used for authentication; note
......@@ -140,7 +164,7 @@ public static string TryNormalize(string value)
/// Indicates whether admin operations should be allowed
/// </summary>
public bool AllowAdmin { get { return allowAdmin.GetValueOrDefault(); } set { allowAdmin = value; } }
/// <summary>
/// Indicates whether the connection should be encrypted
/// </summary>
......@@ -149,24 +173,15 @@ public static string TryNormalize(string value)
EditorBrowsable(EditorBrowsableState.Never)]
public bool UseSsl { get { return Ssl; } set { Ssl = value; } }
/// <summary>
/// Indicates whether the connection should be encrypted
/// </summary>
public bool Ssl { get { return ssl.GetValueOrDefault(); } set { ssl = value; } }
/// <summary>
/// Configures which Ssl/TLS protocols should be allowed. If not set, defaults are chosen by the .NET framework.
/// </summary>
public SslProtocols? SslProtocols { get; set; }
/// <summary>
/// Automatically encodes and decodes channels
/// </summary>
public RedisChannel ChannelPrefix { get;set; }
public RedisChannel ChannelPrefix { get; set; }
/// <summary>
/// The client name to use for all connections
/// </summary>
public string ClientName { get { return clientName; } set { clientName = value; } }
public string ClientName { get; set; }
/// <summary>
/// The number of times to repeat the initial connect cycle if no servers respond promptly
......@@ -191,8 +206,7 @@ public CommandMap CommandMap
}
set
{
if (value == null) throw new ArgumentNullException(nameof(value));
commandMap = value;
commandMap = value ?? throw new ArgumentNullException(nameof(value));
}
}
......@@ -204,8 +218,10 @@ public CommandMap CommandMap
/// <summary>
/// Specifies the time in milliseconds that should be allowed for connection (defaults to 5 seconds unless SyncTimeout is higher)
/// </summary>
public int ConnectTimeout {
get {
public int ConnectTimeout
{
get
{
if (connectTimeout.HasValue) return connectTimeout.GetValueOrDefault();
return Math.Max(5000, SyncTimeout);
}
......@@ -213,10 +229,9 @@ public CommandMap CommandMap
}
/// <summary>
/// The retry policy to be used for connection reconnects
/// Specifies the default database to be used when calling ConnectionMultiplexer.GetDatabase() without any parameters
/// </summary>
public IReconnectRetryPolicy ReconnectRetryPolicy { get { return reconnectRetryPolicy ?? (reconnectRetryPolicy = new LinearRetry(ConnectTimeout)); } set { reconnectRetryPolicy = value; } }
public int? DefaultDatabase { get; set; }
/// <summary>
/// The server version to assume
......@@ -226,7 +241,7 @@ public CommandMap CommandMap
/// <summary>
/// The endpoints defined for this configuration
/// </summary>
public EndPointCollection EndPoints => endpoints;
public EndPointCollection EndPoints { get; } = new EndPointCollection();
/// <summary>
/// Use ThreadPriority.AboveNormal for SocketManager reader and writer threads (true by default). If false, ThreadPriority.Normal will be used.
......@@ -239,15 +254,25 @@ public CommandMap CommandMap
public int KeepAlive { get { return keepAlive.GetValueOrDefault(-1); } set { keepAlive = value; } }
/// <summary>
/// The password to use to authenticate with the server
/// The password to use to authenticate with the server.
/// </summary>
public string Password { get; set; }
/// <summary>
/// Specifies whether asynchronous operations should be invoked in a way that guarantees their original delivery order
/// </summary>
public string Password { get { return password; } set { password = value; } }
public bool PreserveAsyncOrder { get { return preserveAsyncOrder.GetValueOrDefault(true); } set { preserveAsyncOrder = value; } }
/// <summary>
/// Type of proxy to use (if any); for example Proxy.Twemproxy
/// Type of proxy to use (if any); for example Proxy.Twemproxy.
/// </summary>
public Proxy Proxy { get { return proxy.GetValueOrDefault(); } set { proxy = value; } }
/// <summary>
/// The retry policy to be used for connection reconnects
/// </summary>
public IReconnectRetryPolicy ReconnectRetryPolicy { get { return reconnectRetryPolicy ?? (reconnectRetryPolicy = new LinearRetry(ConnectTimeout)); } set { reconnectRetryPolicy = value; } }
/// <summary>
/// Indicates whether endpoints should be resolved via DNS before connecting.
/// If enabled the ConnectionMultiplexer will re-resolve DNS
......@@ -256,30 +281,41 @@ public CommandMap CommandMap
public bool ResolveDns { get { return resolveDns.GetValueOrDefault(); } set { resolveDns = value; } }
/// <summary>
/// The service name used to resolve a service via sentinel
/// Specifies the time in milliseconds that the system should allow for responses before concluding that the socket is unhealthy
/// (defaults to SyncTimeout)
/// </summary>
public int ResponseTimeout { get { return responseTimeout ?? SyncTimeout; } set { responseTimeout = value; } }
/// <summary>
/// The service name used to resolve a service via sentinel.
/// </summary>
public string ServiceName { get { return serviceName; } set { serviceName = value; } }
public string ServiceName { get; set; }
/// <summary>
/// Gets or sets the SocketManager instance to be used with these options; if this is null a per-multiplexer
/// SocketManager is created automatically.
/// </summary>
public SocketManager SocketManager { get;set; }
public SocketManager SocketManager { get; set; }
/// <summary>
/// Indicates whether the connection should be encrypted
/// </summary>
public bool Ssl { get { return ssl.GetValueOrDefault(); } set { ssl = value; } }
/// <summary>
/// The target-host to use when validating SSL certificate; setting a value here enables SSL mode
/// </summary>
public string SslHost { get { return sslHost ?? InferSslHostFromEndpoints(); } set { sslHost = value; } }
/// <summary>
/// Specifies the time in milliseconds that the system should allow for synchronous operations (defaults to 1 second)
/// Configures which Ssl/TLS protocols should be allowed. If not set, defaults are chosen by the .NET framework.
/// </summary>
public int SyncTimeout { get { return syncTimeout.GetValueOrDefault(1000); } set { syncTimeout = value; } }
public SslProtocols? SslProtocols { get; set; }
/// <summary>
/// Specifies the time in milliseconds that the system should allow for responses before concluding that the socket is unhealthy
/// (defaults to SyncTimeout)
/// Specifies the time in milliseconds that the system should allow for synchronous operations (defaults to 1 second)
/// </summary>
public int ResponseTimeout { get { return responseTimeout ?? SyncTimeout; } set { responseTimeout = value; } }
public int SyncTimeout { get { return syncTimeout.GetValueOrDefault(1000); } set { syncTimeout = value; } }
/// <summary>
/// Tie-breaker used to choose between masters (must match the endpoint exactly)
......@@ -290,16 +326,6 @@ public CommandMap CommandMap
/// </summary>
public int WriteBuffer { get { return writeBuffer.GetValueOrDefault(4096); } set { writeBuffer = value; } }
/// <summary>
/// Specifies the default database to be used when calling ConnectionMultiplexer.GetDatabase() without any parameters
/// </summary>
public int? DefaultDatabase { get { return defaultDatabase; } set { defaultDatabase = value; } }
/// <summary>
/// Specifies whether asynchronous operations should be invoked in a way that guarantees their original delivery order
/// </summary>
public bool PreserveAsyncOrder { get { return preserveAsyncOrder.GetValueOrDefault(true); } set { preserveAsyncOrder = value; } }
internal LocalCertificateSelectionCallback CertificateSelectionCallback { get { return CertificateSelection; } private set { CertificateSelection = value; } }
// these just rip out the underlying handlers, bypassing the event accessors - needed when creating the SSL stream
......@@ -313,17 +339,21 @@ public CommandMap CommandMap
/// <summary>
/// Parse the configuration from a comma-delimited configuration string
/// </summary>
/// <param name="configuration">The configuration string to parse.</param>
/// <exception cref="ArgumentNullException"><paramref name="configuration"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException"><paramref name="configuration"/> is empty.</exception>
public static ConfigurationOptions Parse(string configuration)
{
{
var options = new ConfigurationOptions();
options.DoParse(configuration, false);
return options;
}
/// <summary>
/// Parse the configuration from a comma-delimited configuration string
/// </summary>
/// <param name="configuration">The configuration string to parse.</param>
/// <param name="ignoreUnknown">Whether to ignore unknown elements in <paramref name="configuration"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="configuration"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException"><paramref name="configuration"/> is empty.</exception>
public static ConfigurationOptions Parse(string configuration, bool ignoreUnknown)
......@@ -340,14 +370,14 @@ public ConfigurationOptions Clone()
{
var options = new ConfigurationOptions
{
clientName = clientName,
serviceName = serviceName,
ClientName = ClientName,
ServiceName = ServiceName,
keepAlive = keepAlive,
syncTimeout = syncTimeout,
allowAdmin = allowAdmin,
defaultVersion = defaultVersion,
connectTimeout = connectTimeout,
password = password,
Password = Password,
tieBreaker = tieBreaker,
writeBuffer = writeBuffer,
ssl = ssl,
......@@ -372,10 +402,9 @@ public ConfigurationOptions Clone()
SslProtocols = SslProtocols,
#endif
};
foreach (var item in endpoints)
options.endpoints.Add(item);
foreach (var item in EndPoints)
options.EndPoints.Add(item);
return options;
}
/// <summary>
......@@ -383,42 +412,41 @@ public ConfigurationOptions Clone()
/// </summary>
public void SetDefaultPorts()
{
endpoints.SetDefaultPorts(Ssl ? 6380 : 6379);
EndPoints.SetDefaultPorts(Ssl ? 6380 : 6379);
}
/// <summary>
/// Returns the effective configuration string for this configuration, including Redis credentials.
/// </summary>
public override string ToString()
{
// include password to allow generation of configuration strings
// used for connecting multiplexer
return ToString(includePassword: true);
}
/// <remarks>
/// Includes password to allow generation of configuration strings used for connecting multiplexer.
/// </remarks>
public override string ToString() => ToString(includePassword: true);
/// <summary>
/// Returns the effective configuration string for this configuration
/// with the option to include or exclude the password from the string.
/// </summary>
/// <param name="includePassword">Whether to include the password.</param>
public string ToString(bool includePassword)
{
var sb = new StringBuilder();
foreach (var endpoint in endpoints)
foreach (var endpoint in EndPoints)
{
Append(sb, Format.ToString(endpoint));
}
Append(sb, OptionKeys.ClientName, clientName);
Append(sb, OptionKeys.ServiceName, serviceName);
Append(sb, OptionKeys.ClientName, ClientName);
Append(sb, OptionKeys.ServiceName, ServiceName);
Append(sb, OptionKeys.KeepAlive, keepAlive);
Append(sb, OptionKeys.SyncTimeout, syncTimeout);
Append(sb, OptionKeys.AllowAdmin, allowAdmin);
Append(sb, OptionKeys.Version, defaultVersion);
Append(sb, OptionKeys.ConnectTimeout, connectTimeout);
Append(sb, OptionKeys.Password, includePassword ? password : "*****");
Append(sb, OptionKeys.Password, includePassword ? Password : "*****");
Append(sb, OptionKeys.TieBreaker, tieBreaker);
Append(sb, OptionKeys.WriteBuffer, writeBuffer);
Append(sb, OptionKeys.Ssl, ssl);
Append(sb, OptionKeys.SslHost, sslHost);
Append(sb, OptionKeys.SslHost, sslHost);
Append(sb, OptionKeys.HighPrioritySocketThreads, highPrioritySocketThreads);
Append(sb, OptionKeys.ConfigChannel, configChannel);
Append(sb, OptionKeys.AbortOnConnectFail, abortOnConnectFail);
......@@ -436,28 +464,26 @@ public string ToString(bool includePassword)
internal bool HasDnsEndPoints()
{
foreach (var endpoint in endpoints) if (endpoint is DnsEndPoint) return true;
foreach (var endpoint in EndPoints) if (endpoint is DnsEndPoint) return true;
return false;
}
internal async Task ResolveEndPointsAsync(ConnectionMultiplexer multiplexer, TextWriter log)
{
Dictionary<string, IPAddress> cache = new Dictionary<string, IPAddress>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < endpoints.Count; i++)
var cache = new Dictionary<string, IPAddress>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < EndPoints.Count; i++)
{
var dns = endpoints[i] as DnsEndPoint;
if (dns != null)
if (EndPoints[i] is DnsEndPoint dns)
{
try
{
IPAddress ip;
if (dns.Host == ".")
{
endpoints[i] = new IPEndPoint(IPAddress.Loopback, dns.Port);
EndPoints[i] = new IPEndPoint(IPAddress.Loopback, dns.Port);
}
else if (cache.TryGetValue(dns.Host, out ip))
else if (cache.TryGetValue(dns.Host, out IPAddress ip))
{ // use cache
endpoints[i] = new IPEndPoint(ip, dns.Port);
EndPoints[i] = new IPEndPoint(ip, dns.Port);
}
else
{
......@@ -468,7 +494,7 @@ internal async Task ResolveEndPointsAsync(ConnectionMultiplexer multiplexer, Tex
ip = ips[0];
multiplexer.LogLocked(log, "'{0}' => {1}", dns.Host, ip);
cache[dns.Host] = ip;
endpoints[i] = new IPEndPoint(ip, dns.Port);
EndPoints[i] = new IPEndPoint(ip, dns.Port);
}
}
}
......@@ -481,7 +507,7 @@ internal async Task ResolveEndPointsAsync(ConnectionMultiplexer multiplexer, Tex
}
}
static void Append(StringBuilder sb, object value)
private static void Append(StringBuilder sb, object value)
{
if (value == null) return;
string s = Format.ToString(value);
......@@ -492,13 +518,13 @@ static void Append(StringBuilder sb, object value)
}
}
static void Append(StringBuilder sb, string prefix, object value)
private static void Append(StringBuilder sb, string prefix, object value)
{
string s = value?.ToString();
if (!string.IsNullOrWhiteSpace(s))
{
if (sb.Length != 0) sb.Append(',');
if(!string.IsNullOrEmpty(prefix))
if (!string.IsNullOrEmpty(prefix))
{
sb.Append(prefix).Append('=');
}
......@@ -506,24 +532,17 @@ static void Append(StringBuilder sb, string prefix, object value)
}
}
#if !NETSTANDARD1_5
static bool IsOption(string option, string prefix)
{
return option.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase);
}
#endif
void Clear()
private void Clear()
{
clientName = serviceName = password = tieBreaker = sslHost = configChannel = null;
ClientName = ServiceName = Password = tieBreaker = sslHost = configChannel = null;
keepAlive = syncTimeout = connectTimeout = writeBuffer = connectRetry = configCheckSeconds = defaultDatabase = null;
allowAdmin = abortOnConnectFail = highPrioritySocketThreads = resolveDns = ssl = preserveAsyncOrder = null;
defaultVersion = null;
endpoints.Clear();
EndPoints.Clear();
commandMap = null;
CertificateSelection = null;
CertificateValidation = null;
CertificateValidation = null;
ChannelPrefix = default(RedisChannel);
SocketManager = null;
}
......@@ -559,7 +578,7 @@ private void DoParse(string configuration, bool ignoreUnknown)
int idx = option.IndexOf('=');
if (idx > 0)
{
var key = option.Substring(0, idx).Trim();
var key = option.Substring(0, idx).Trim();
var value = option.Substring(idx + 1).Trim();
switch (OptionKeys.TryNormalize(key))
......@@ -641,9 +660,8 @@ private void DoParse(string configuration, bool ignoreUnknown)
default:
if (!string.IsNullOrEmpty(key) && key[0] == '$')
{
RedisCommand cmd;
var cmdName = option.Substring(1, idx - 1);
if (Enum.TryParse(cmdName, true, out cmd))
if (Enum.TryParse(cmdName, true, out RedisCommand cmd))
{
if (map == null) map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
map[cmdName] = value;
......@@ -651,7 +669,7 @@ private void DoParse(string configuration, bool ignoreUnknown)
}
else
{
if(!ignoreUnknown) OptionKeys.Unknown(key);
if (!ignoreUnknown) OptionKeys.Unknown(key);
}
break;
}
......@@ -659,7 +677,7 @@ private void DoParse(string configuration, bool ignoreUnknown)
else
{
var ep = Format.TryParseEndPoint(option);
if (ep != null && !endpoints.Contains(ep)) endpoints.Add(ep);
if (ep != null && !EndPoints.Contains(ep)) EndPoints.Add(ep);
}
}
if (map != null && map.Count != 0)
......@@ -676,39 +694,41 @@ private bool GetDefaultAbortOnConnectFailSetting()
return true;
}
private bool IsAzureEndpoint()
{
var result = false;
var dnsEndpoints = endpoints.Select(endpoint => endpoint as DnsEndPoint).Where(ep => ep != null);
foreach(var ep in dnsEndpoints)
foreach (var ep in EndPoints)
{
int firstDot = ep.Host.IndexOf('.');
if (firstDot >= 0)
if (ep is DnsEndPoint dnsEp)
{
var domain = ep.Host.Substring(firstDot).ToLowerInvariant();
switch(domain)
int firstDot = dnsEp.Host.IndexOf('.');
if (firstDot >= 0)
{
case ".redis.cache.windows.net":
case ".redis.cache.chinacloudapi.cn":
case ".redis.cache.usgovcloudapi.net":
case ".redis.cache.cloudapi.de":
return true;
switch (dnsEp.Host.Substring(firstDot).ToLowerInvariant())
{
case ".redis.cache.windows.net":
case ".redis.cache.chinacloudapi.cn":
case ".redis.cache.usgovcloudapi.net":
case ".redis.cache.cloudapi.de":
return true;
}
}
}
}
return result;
return false;
}
private string InferSslHostFromEndpoints() {
var dnsEndpoints = endpoints.Select(endpoint => endpoint as DnsEndPoint);
private string InferSslHostFromEndpoints()
{
var dnsEndpoints = EndPoints.Select(endpoint => endpoint as DnsEndPoint);
string dnsHost = dnsEndpoints.FirstOrDefault()?.Host;
if (dnsEndpoints.All(dnsEndpoint => (dnsEndpoint != null && dnsEndpoint.Host == dnsHost))) {
if (dnsEndpoints.All(dnsEndpoint => (dnsEndpoint != null && dnsEndpoint.Host == dnsHost)))
{
return dnsHost;
}
return null;
}
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment