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 ...@@ -25,8 +25,7 @@ private static class OptionKeys
{ {
public static int ParseInt32(string key, string value, int minValue = int.MinValue, int maxValue = int.MaxValue) public static int ParseInt32(string key, string value, int minValue = int.MinValue, int maxValue = int.MaxValue)
{ {
int tmp; if (!Format.TryParseInt32(value, out int tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires an integer value");
if (!Format.TryParseInt32(value, out 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 < 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); if (tmp > maxValue) throw new ArgumentOutOfRangeException("Keyword '" + key + "' has a maximum value of " + maxValue);
return tmp; return tmp;
...@@ -34,30 +33,28 @@ public static int ParseInt32(string key, string value, int minValue = int.MinVal ...@@ -34,30 +33,28 @@ public static int ParseInt32(string key, string value, int minValue = int.MinVal
internal static bool ParseBoolean(string key, string value) internal static bool ParseBoolean(string key, string value)
{ {
bool tmp; if (!Format.TryParseBoolean(value, out bool tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires a boolean value");
if (!Format.TryParseBoolean(value, out tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires a boolean value");
return tmp; return tmp;
} }
internal static Version ParseVersion(string key, string value) internal static Version ParseVersion(string key, string value)
{ {
Version tmp; if (!System.Version.TryParse(value, out Version tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires a version value");
if (!System.Version.TryParse(value, out tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires a version value");
return tmp; return tmp;
} }
internal static Proxy ParseProxy(string key, string value) internal static Proxy ParseProxy(string key, string value)
{ {
Proxy tmp; if (!Enum.TryParse(value, true, out Proxy tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires a proxy value");
if (!Enum.TryParse(value, true, out tmp)) throw new ArgumentOutOfRangeException("Keyword '" + key + "' requires a proxy value");
return tmp; return tmp;
} }
internal static SslProtocols ParseSslProtocols(string key, string value) 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 //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;
} }
...@@ -67,32 +64,62 @@ internal static void Unknown(string key) ...@@ -67,32 +64,62 @@ internal static void Unknown(string key)
throw new ArgumentException("Keyword '" + key + "' is not supported"); throw new ArgumentException("Keyword '" + key + "' is not supported");
} }
internal const string AllowAdmin = "allowAdmin", SyncTimeout = "syncTimeout", internal const string
ServiceName = "serviceName", ClientName = "name", KeepAlive = "keepAlive", AbortOnConnectFail = "abortConnect",
Version = "version", ConnectTimeout = "connectTimeout", Password = "password", AllowAdmin = "allowAdmin",
TieBreaker = "tiebreaker", WriteBuffer = "writeBuffer", Ssl = "ssl", SslHost = "sslHost", HighPrioritySocketThreads = "highPriorityThreads", ChannelPrefix = "channelPrefix",
ConfigChannel = "configChannel", AbortOnConnectFail = "abortConnect", ResolveDns = "resolveDns", ConfigChannel = "configChannel",
ChannelPrefix = "channelPrefix", Proxy = "proxy", ConnectRetry = "connectRetry", ConfigCheckSeconds = "configCheckSeconds",
ConfigCheckSeconds = "configCheckSeconds", ResponseTimeout = "responseTimeout", DefaultDatabase = "defaultDatabase"; ConnectRetry = "connectRetry",
internal const string SslProtocols = "sslProtocols"; ConnectTimeout = "connectTimeout",
internal const string PreserveAsyncOrder = "preserveAsyncOrder"; 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[] private static readonly Dictionary<string, string> normalizedOptions = new[]
{ {
AllowAdmin, SyncTimeout, AbortOnConnectFail,
ServiceName, ClientName, KeepAlive, AllowAdmin,
Version, ConnectTimeout, Password, ChannelPrefix,
TieBreaker, WriteBuffer, Ssl, SslHost, HighPrioritySocketThreads, ClientName,
ConfigChannel, AbortOnConnectFail, ResolveDns, ConfigChannel,
ChannelPrefix, Proxy, ConnectRetry, ConfigCheckSeconds,
ConfigCheckSeconds, DefaultDatabase, ConnectRetry,
SslProtocols, PreserveAsyncOrder ConnectTimeout,
DefaultDatabase,
HighPrioritySocketThreads,
KeepAlive,
Password,
PreserveAsyncOrder,
Proxy,
ResolveDns,
ServiceName,
Ssl,
SslHost,
SslProtocols,
SyncTimeout,
TieBreaker,
Version,
WriteBuffer,
}.ToDictionary(x => x, StringComparer.OrdinalIgnoreCase); }.ToDictionary(x => x, StringComparer.OrdinalIgnoreCase);
public static string TryNormalize(string value) public static string TryNormalize(string value)
{ {
string tmp; if (value != null && normalizedOptions.TryGetValue(value, out string tmp))
if(value != null && normalizedOptions.TryGetValue(value, out tmp))
{ {
return tmp ?? ""; return tmp ?? "";
} }
...@@ -100,12 +127,9 @@ public static string TryNormalize(string value) ...@@ -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 bool? allowAdmin, abortOnConnectFail, highPrioritySocketThreads, resolveDns, ssl, preserveAsyncOrder;
private string clientName, serviceName, password, tieBreaker, sslHost, configChannel; private string tieBreaker, sslHost, configChannel;
private CommandMap commandMap; private CommandMap commandMap;
...@@ -149,24 +173,15 @@ public static string TryNormalize(string value) ...@@ -149,24 +173,15 @@ public static string TryNormalize(string value)
EditorBrowsable(EditorBrowsableState.Never)] EditorBrowsable(EditorBrowsableState.Never)]
public bool UseSsl { get { return Ssl; } set { Ssl = value; } } 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> /// <summary>
/// Automatically encodes and decodes channels /// Automatically encodes and decodes channels
/// </summary> /// </summary>
public RedisChannel ChannelPrefix { get;set; } public RedisChannel ChannelPrefix { get; set; }
/// <summary> /// <summary>
/// The client name to use for all connections /// The client name to use for all connections
/// </summary> /// </summary>
public string ClientName { get { return clientName; } set { clientName = value; } } public string ClientName { get; set; }
/// <summary> /// <summary>
/// The number of times to repeat the initial connect cycle if no servers respond promptly /// The number of times to repeat the initial connect cycle if no servers respond promptly
...@@ -191,8 +206,7 @@ public CommandMap CommandMap ...@@ -191,8 +206,7 @@ public CommandMap CommandMap
} }
set set
{ {
if (value == null) throw new ArgumentNullException(nameof(value)); commandMap = value ?? throw new ArgumentNullException(nameof(value));
commandMap = value;
} }
} }
...@@ -204,8 +218,10 @@ public CommandMap CommandMap ...@@ -204,8 +218,10 @@ public CommandMap CommandMap
/// <summary> /// <summary>
/// Specifies the time in milliseconds that should be allowed for connection (defaults to 5 seconds unless SyncTimeout is higher) /// Specifies the time in milliseconds that should be allowed for connection (defaults to 5 seconds unless SyncTimeout is higher)
/// </summary> /// </summary>
public int ConnectTimeout { public int ConnectTimeout
get { {
get
{
if (connectTimeout.HasValue) return connectTimeout.GetValueOrDefault(); if (connectTimeout.HasValue) return connectTimeout.GetValueOrDefault();
return Math.Max(5000, SyncTimeout); return Math.Max(5000, SyncTimeout);
} }
...@@ -213,10 +229,9 @@ public CommandMap CommandMap ...@@ -213,10 +229,9 @@ public CommandMap CommandMap
} }
/// <summary> /// <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> /// </summary>
public IReconnectRetryPolicy ReconnectRetryPolicy { get { return reconnectRetryPolicy ?? (reconnectRetryPolicy = new LinearRetry(ConnectTimeout)); } set { reconnectRetryPolicy = value; } } public int? DefaultDatabase { get; set; }
/// <summary> /// <summary>
/// The server version to assume /// The server version to assume
...@@ -226,7 +241,7 @@ public CommandMap CommandMap ...@@ -226,7 +241,7 @@ public CommandMap CommandMap
/// <summary> /// <summary>
/// The endpoints defined for this configuration /// The endpoints defined for this configuration
/// </summary> /// </summary>
public EndPointCollection EndPoints => endpoints; public EndPointCollection EndPoints { get; } = new EndPointCollection();
/// <summary> /// <summary>
/// Use ThreadPriority.AboveNormal for SocketManager reader and writer threads (true by default). If false, ThreadPriority.Normal will be used. /// 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 ...@@ -239,15 +254,25 @@ public CommandMap CommandMap
public int KeepAlive { get { return keepAlive.GetValueOrDefault(-1); } set { keepAlive = value; } } public int KeepAlive { get { return keepAlive.GetValueOrDefault(-1); } set { keepAlive = value; } }
/// <summary> /// <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> /// </summary>
public string Password { get { return password; } set { password = value; } } public bool PreserveAsyncOrder { get { return preserveAsyncOrder.GetValueOrDefault(true); } set { preserveAsyncOrder = value; } }
/// <summary> /// <summary>
/// Type of proxy to use (if any); for example Proxy.Twemproxy /// Type of proxy to use (if any); for example Proxy.Twemproxy.
/// </summary> /// </summary>
public Proxy Proxy { get { return proxy.GetValueOrDefault(); } set { proxy = value; } } 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> /// <summary>
/// Indicates whether endpoints should be resolved via DNS before connecting. /// Indicates whether endpoints should be resolved via DNS before connecting.
/// If enabled the ConnectionMultiplexer will re-resolve DNS /// If enabled the ConnectionMultiplexer will re-resolve DNS
...@@ -256,30 +281,41 @@ public CommandMap CommandMap ...@@ -256,30 +281,41 @@ public CommandMap CommandMap
public bool ResolveDns { get { return resolveDns.GetValueOrDefault(); } set { resolveDns = value; } } public bool ResolveDns { get { return resolveDns.GetValueOrDefault(); } set { resolveDns = value; } }
/// <summary> /// <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> /// </summary>
public string ServiceName { get { return serviceName; } set { serviceName = value; } } public string ServiceName { get; set; }
/// <summary> /// <summary>
/// Gets or sets the SocketManager instance to be used with these options; if this is null a per-multiplexer /// Gets or sets the SocketManager instance to be used with these options; if this is null a per-multiplexer
/// SocketManager is created automatically. /// SocketManager is created automatically.
/// </summary> /// </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> /// <summary>
/// The target-host to use when validating SSL certificate; setting a value here enables SSL mode /// The target-host to use when validating SSL certificate; setting a value here enables SSL mode
/// </summary> /// </summary>
public string SslHost { get { return sslHost ?? InferSslHostFromEndpoints(); } set { sslHost = value; } } public string SslHost { get { return sslHost ?? InferSslHostFromEndpoints(); } set { sslHost = value; } }
/// <summary> /// <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> /// </summary>
public int SyncTimeout { get { return syncTimeout.GetValueOrDefault(1000); } set { syncTimeout = value; } } public SslProtocols? SslProtocols { get; set; }
/// <summary> /// <summary>
/// Specifies the time in milliseconds that the system should allow for responses before concluding that the socket is unhealthy /// Specifies the time in milliseconds that the system should allow for synchronous operations (defaults to 1 second)
/// (defaults to SyncTimeout)
/// </summary> /// </summary>
public int ResponseTimeout { get { return responseTimeout ?? SyncTimeout; } set { responseTimeout = value; } } public int SyncTimeout { get { return syncTimeout.GetValueOrDefault(1000); } set { syncTimeout = value; } }
/// <summary> /// <summary>
/// Tie-breaker used to choose between masters (must match the endpoint exactly) /// Tie-breaker used to choose between masters (must match the endpoint exactly)
...@@ -290,16 +326,6 @@ public CommandMap CommandMap ...@@ -290,16 +326,6 @@ public CommandMap CommandMap
/// </summary> /// </summary>
public int WriteBuffer { get { return writeBuffer.GetValueOrDefault(4096); } set { writeBuffer = value; } } 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; } } 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 // these just rip out the underlying handlers, bypassing the event accessors - needed when creating the SSL stream
...@@ -313,6 +339,7 @@ public CommandMap CommandMap ...@@ -313,6 +339,7 @@ public CommandMap CommandMap
/// <summary> /// <summary>
/// Parse the configuration from a comma-delimited configuration string /// Parse the configuration from a comma-delimited configuration string
/// </summary> /// </summary>
/// <param name="configuration">The configuration string to parse.</param>
/// <exception cref="ArgumentNullException"><paramref name="configuration"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="configuration"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException"><paramref name="configuration"/> is empty.</exception> /// <exception cref="ArgumentException"><paramref name="configuration"/> is empty.</exception>
public static ConfigurationOptions Parse(string configuration) public static ConfigurationOptions Parse(string configuration)
...@@ -321,9 +348,12 @@ public static ConfigurationOptions Parse(string configuration) ...@@ -321,9 +348,12 @@ public static ConfigurationOptions Parse(string configuration)
options.DoParse(configuration, false); options.DoParse(configuration, false);
return options; return options;
} }
/// <summary> /// <summary>
/// Parse the configuration from a comma-delimited configuration string /// Parse the configuration from a comma-delimited configuration string
/// </summary> /// </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="ArgumentNullException"><paramref name="configuration"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException"><paramref name="configuration"/> is empty.</exception> /// <exception cref="ArgumentException"><paramref name="configuration"/> is empty.</exception>
public static ConfigurationOptions Parse(string configuration, bool ignoreUnknown) public static ConfigurationOptions Parse(string configuration, bool ignoreUnknown)
...@@ -340,14 +370,14 @@ public ConfigurationOptions Clone() ...@@ -340,14 +370,14 @@ public ConfigurationOptions Clone()
{ {
var options = new ConfigurationOptions var options = new ConfigurationOptions
{ {
clientName = clientName, ClientName = ClientName,
serviceName = serviceName, ServiceName = ServiceName,
keepAlive = keepAlive, keepAlive = keepAlive,
syncTimeout = syncTimeout, syncTimeout = syncTimeout,
allowAdmin = allowAdmin, allowAdmin = allowAdmin,
defaultVersion = defaultVersion, defaultVersion = defaultVersion,
connectTimeout = connectTimeout, connectTimeout = connectTimeout,
password = password, Password = Password,
tieBreaker = tieBreaker, tieBreaker = tieBreaker,
writeBuffer = writeBuffer, writeBuffer = writeBuffer,
ssl = ssl, ssl = ssl,
...@@ -372,10 +402,9 @@ public ConfigurationOptions Clone() ...@@ -372,10 +402,9 @@ public ConfigurationOptions Clone()
SslProtocols = SslProtocols, SslProtocols = SslProtocols,
#endif #endif
}; };
foreach (var item in endpoints) foreach (var item in EndPoints)
options.endpoints.Add(item); options.EndPoints.Add(item);
return options; return options;
} }
/// <summary> /// <summary>
...@@ -383,38 +412,37 @@ public ConfigurationOptions Clone() ...@@ -383,38 +412,37 @@ public ConfigurationOptions Clone()
/// </summary> /// </summary>
public void SetDefaultPorts() public void SetDefaultPorts()
{ {
endpoints.SetDefaultPorts(Ssl ? 6380 : 6379); EndPoints.SetDefaultPorts(Ssl ? 6380 : 6379);
} }
/// <summary> /// <summary>
/// Returns the effective configuration string for this configuration, including Redis credentials. /// Returns the effective configuration string for this configuration, including Redis credentials.
/// </summary> /// </summary>
public override string ToString() /// <remarks>
{ /// Includes password to allow generation of configuration strings used for connecting multiplexer.
// include password to allow generation of configuration strings /// </remarks>
// used for connecting multiplexer public override string ToString() => ToString(includePassword: true);
return ToString(includePassword: true);
}
/// <summary> /// <summary>
/// Returns the effective configuration string for this configuration /// Returns the effective configuration string for this configuration
/// with the option to include or exclude the password from the string. /// with the option to include or exclude the password from the string.
/// </summary> /// </summary>
/// <param name="includePassword">Whether to include the password.</param>
public string ToString(bool includePassword) public string ToString(bool includePassword)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
foreach (var endpoint in endpoints) foreach (var endpoint in EndPoints)
{ {
Append(sb, Format.ToString(endpoint)); Append(sb, Format.ToString(endpoint));
} }
Append(sb, OptionKeys.ClientName, clientName); Append(sb, OptionKeys.ClientName, ClientName);
Append(sb, OptionKeys.ServiceName, serviceName); Append(sb, OptionKeys.ServiceName, ServiceName);
Append(sb, OptionKeys.KeepAlive, keepAlive); Append(sb, OptionKeys.KeepAlive, keepAlive);
Append(sb, OptionKeys.SyncTimeout, syncTimeout); Append(sb, OptionKeys.SyncTimeout, syncTimeout);
Append(sb, OptionKeys.AllowAdmin, allowAdmin); Append(sb, OptionKeys.AllowAdmin, allowAdmin);
Append(sb, OptionKeys.Version, defaultVersion); Append(sb, OptionKeys.Version, defaultVersion);
Append(sb, OptionKeys.ConnectTimeout, connectTimeout); 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.TieBreaker, tieBreaker);
Append(sb, OptionKeys.WriteBuffer, writeBuffer); Append(sb, OptionKeys.WriteBuffer, writeBuffer);
Append(sb, OptionKeys.Ssl, ssl); Append(sb, OptionKeys.Ssl, ssl);
...@@ -436,28 +464,26 @@ public string ToString(bool includePassword) ...@@ -436,28 +464,26 @@ public string ToString(bool includePassword)
internal bool HasDnsEndPoints() 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; return false;
} }
internal async Task ResolveEndPointsAsync(ConnectionMultiplexer multiplexer, TextWriter log) internal async Task ResolveEndPointsAsync(ConnectionMultiplexer multiplexer, TextWriter log)
{ {
Dictionary<string, IPAddress> cache = new Dictionary<string, IPAddress>(StringComparer.OrdinalIgnoreCase); var cache = new Dictionary<string, IPAddress>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < endpoints.Count; i++) for (int i = 0; i < EndPoints.Count; i++)
{ {
var dns = endpoints[i] as DnsEndPoint; if (EndPoints[i] is DnsEndPoint dns)
if (dns != null)
{ {
try try
{ {
IPAddress ip;
if (dns.Host == ".") 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 { // use cache
endpoints[i] = new IPEndPoint(ip, dns.Port); EndPoints[i] = new IPEndPoint(ip, dns.Port);
} }
else else
{ {
...@@ -468,7 +494,7 @@ internal async Task ResolveEndPointsAsync(ConnectionMultiplexer multiplexer, Tex ...@@ -468,7 +494,7 @@ internal async Task ResolveEndPointsAsync(ConnectionMultiplexer multiplexer, Tex
ip = ips[0]; ip = ips[0];
multiplexer.LogLocked(log, "'{0}' => {1}", dns.Host, ip); multiplexer.LogLocked(log, "'{0}' => {1}", dns.Host, ip);
cache[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 ...@@ -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; if (value == null) return;
string s = Format.ToString(value); string s = Format.ToString(value);
...@@ -492,13 +518,13 @@ static void Append(StringBuilder sb, object 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(); string s = value?.ToString();
if (!string.IsNullOrWhiteSpace(s)) if (!string.IsNullOrWhiteSpace(s))
{ {
if (sb.Length != 0) sb.Append(','); if (sb.Length != 0) sb.Append(',');
if(!string.IsNullOrEmpty(prefix)) if (!string.IsNullOrEmpty(prefix))
{ {
sb.Append(prefix).Append('='); sb.Append(prefix).Append('=');
} }
...@@ -506,20 +532,13 @@ static void Append(StringBuilder sb, string prefix, object value) ...@@ -506,20 +532,13 @@ static void Append(StringBuilder sb, string prefix, object value)
} }
} }
#if !NETSTANDARD1_5 private void Clear()
static bool IsOption(string option, string prefix)
{ {
return option.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase); ClientName = ServiceName = Password = tieBreaker = sslHost = configChannel = null;
}
#endif
void Clear()
{
clientName = serviceName = password = tieBreaker = sslHost = configChannel = null;
keepAlive = syncTimeout = connectTimeout = writeBuffer = connectRetry = configCheckSeconds = defaultDatabase = null; keepAlive = syncTimeout = connectTimeout = writeBuffer = connectRetry = configCheckSeconds = defaultDatabase = null;
allowAdmin = abortOnConnectFail = highPrioritySocketThreads = resolveDns = ssl = preserveAsyncOrder = null; allowAdmin = abortOnConnectFail = highPrioritySocketThreads = resolveDns = ssl = preserveAsyncOrder = null;
defaultVersion = null; defaultVersion = null;
endpoints.Clear(); EndPoints.Clear();
commandMap = null; commandMap = null;
CertificateSelection = null; CertificateSelection = null;
...@@ -641,9 +660,8 @@ private void DoParse(string configuration, bool ignoreUnknown) ...@@ -641,9 +660,8 @@ private void DoParse(string configuration, bool ignoreUnknown)
default: default:
if (!string.IsNullOrEmpty(key) && key[0] == '$') if (!string.IsNullOrEmpty(key) && key[0] == '$')
{ {
RedisCommand cmd;
var cmdName = option.Substring(1, idx - 1); 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); if (map == null) map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
map[cmdName] = value; map[cmdName] = value;
...@@ -651,7 +669,7 @@ private void DoParse(string configuration, bool ignoreUnknown) ...@@ -651,7 +669,7 @@ private void DoParse(string configuration, bool ignoreUnknown)
} }
else else
{ {
if(!ignoreUnknown) OptionKeys.Unknown(key); if (!ignoreUnknown) OptionKeys.Unknown(key);
} }
break; break;
} }
...@@ -659,7 +677,7 @@ private void DoParse(string configuration, bool ignoreUnknown) ...@@ -659,7 +677,7 @@ private void DoParse(string configuration, bool ignoreUnknown)
else else
{ {
var ep = Format.TryParseEndPoint(option); 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) if (map != null && map.Count != 0)
...@@ -679,15 +697,14 @@ private bool GetDefaultAbortOnConnectFailSetting() ...@@ -679,15 +697,14 @@ private bool GetDefaultAbortOnConnectFailSetting()
private bool IsAzureEndpoint() private bool IsAzureEndpoint()
{ {
var result = false; foreach (var ep in EndPoints)
var dnsEndpoints = endpoints.Select(endpoint => endpoint as DnsEndPoint).Where(ep => ep != null); {
foreach(var ep in dnsEndpoints) if (ep is DnsEndPoint dnsEp)
{ {
int firstDot = ep.Host.IndexOf('.'); int firstDot = dnsEp.Host.IndexOf('.');
if (firstDot >= 0) if (firstDot >= 0)
{ {
var domain = ep.Host.Substring(firstDot).ToLowerInvariant(); switch (dnsEp.Host.Substring(firstDot).ToLowerInvariant())
switch(domain)
{ {
case ".redis.cache.windows.net": case ".redis.cache.windows.net":
case ".redis.cache.chinacloudapi.cn": case ".redis.cache.chinacloudapi.cn":
...@@ -697,14 +714,17 @@ private bool IsAzureEndpoint() ...@@ -697,14 +714,17 @@ private bool IsAzureEndpoint()
} }
} }
} }
}
return result; return false;
} }
private string InferSslHostFromEndpoints() { private string InferSslHostFromEndpoints()
var dnsEndpoints = endpoints.Select(endpoint => endpoint as DnsEndPoint); {
var dnsEndpoints = EndPoints.Select(endpoint => endpoint as DnsEndPoint);
string dnsHost = dnsEndpoints.FirstOrDefault()?.Host; 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 dnsHost;
} }
......
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