diff --git a/src/Enyim.Caching/Configuration/EndPointExtensions.cs b/src/Enyim.Caching/Configuration/EndPointExtensions.cs new file mode 100644 index 00000000..2c7ccc18 --- /dev/null +++ b/src/Enyim.Caching/Configuration/EndPointExtensions.cs @@ -0,0 +1,28 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Sockets; + +namespace Enyim.Caching.Configuration; +public static class EndPointExtensions +{ + public static IPEndPoint GetIPEndPoint(this EndPoint endpoint, bool useIPv6) + { + if (endpoint is IPEndPoint ipEndPoint) + { + return ipEndPoint; + } + else if (endpoint is DnsEndPoint dnsEndPoint) + { + var address = Dns.GetHostAddresses(dnsEndPoint.Host).FirstOrDefault(ip => + ip.AddressFamily == (useIPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork)); + return address == null + ? throw new ArgumentException(string.Format("Could not resolve host '{0}'.", endpoint)) + : new IPEndPoint(address, dnsEndPoint.Port); + } + else + { + throw new Exception("Not supported EndPoint type"); + } + } +} diff --git a/src/Enyim.Caching/Memcached/PooledSocket.cs b/src/Enyim.Caching/Memcached/PooledSocket.cs index 79d2b79b..0949c376 100755 --- a/src/Enyim.Caching/Memcached/PooledSocket.cs +++ b/src/Enyim.Caching/Memcached/PooledSocket.cs @@ -1,3 +1,4 @@ +using Enyim.Caching.Configuration; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; @@ -430,7 +431,7 @@ public async Task ReadAsync(byte[] buffer, int offset, int count) ? _sslStream.ReadAsync(buffer, offset, shouldRead) : _inputStream.ReadAsync(buffer, offset, shouldRead); var timeoutTask = Task.Delay(_receiveTimeout); - + if (await Task.WhenAny(readTask, timeoutTask).ConfigureAwait(false) == readTask) { int currentRead = await readTask.ConfigureAwait(false); @@ -438,7 +439,7 @@ public async Task ReadAsync(byte[] buffer, int offset, int count) break; if (currentRead < 1) throw new IOException("The socket seems to be disconnected"); - + read += currentRead; offset += currentRead; shouldRead -= currentRead; @@ -618,23 +619,7 @@ public async Task WriteAsync(IList> buffers) private IPEndPoint GetIPEndPoint(EndPoint endpoint) { - if (endpoint is DnsEndPoint) - { - var dnsEndPoint = (DnsEndPoint)endpoint; - var address = Dns.GetHostAddresses(dnsEndPoint.Host).FirstOrDefault(ip => - ip.AddressFamily == (_useIPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork)); - return address == null - ? throw new ArgumentException(string.Format("Could not resolve host '{0}'.", endpoint)) - : new IPEndPoint(address, dnsEndPoint.Port); - } - else if (endpoint is IPEndPoint) - { - return endpoint as IPEndPoint; - } - else - { - throw new Exception("Not supported EndPoint type"); - } + return endpoint.GetIPEndPoint(_useIPv6); } } } diff --git a/src/Enyim.Caching/Memcached/ServerStats.cs b/src/Enyim.Caching/Memcached/ServerStats.cs index 837b4110..e07f4b3e 100755 --- a/src/Enyim.Caching/Memcached/ServerStats.cs +++ b/src/Enyim.Caching/Memcached/ServerStats.cs @@ -1,6 +1,7 @@ +using Enyim.Caching.Configuration; using System; -using System.Linq; using System.Collections.Generic; +using System.Linq; using System.Net; namespace Enyim.Caching.Memcached @@ -17,6 +18,7 @@ public sealed class ServerStats /// Defines a value which indicates that the statstics should be retrieved for all servers in the pool. /// public static readonly IPEndPoint All = new IPEndPoint(IPAddress.Any, 0); + #region [ readonly int[] Optable ] // defines which values can be summed and which not private static readonly int[] Optable = @@ -25,6 +27,7 @@ public sealed class ServerStats 1, 1, 1, 1, 1, 1, 1, 1 }; #endregion + #region [ readonly string[] StatKeys ] private static readonly string[] StatKeys = { @@ -47,11 +50,14 @@ public sealed class ServerStats }; #endregion - private Dictionary> _results; + private readonly Dictionary> _results; - internal ServerStats(Dictionary> results) + private readonly bool _useIPv6; + + internal ServerStats(Dictionary> results, bool useIPv6) { _results = results; + _useIPv6 = useIPv6; } /// @@ -62,12 +68,14 @@ internal ServerStats(Dictionary> results) /// The value of the specified stat item public long GetValue(EndPoint server, StatItem item) { + server = server.GetIPEndPoint(_useIPv6); + // asked for a specific server if (server is not IPEndPoint || ((IPEndPoint)server).Address != IPAddress.Any) { // error check string tmp = GetRaw(server, item); - if (String.IsNullOrEmpty(tmp)) + if (string.IsNullOrEmpty(tmp)) throw new ArgumentException("Item was not found: " + item); long value; @@ -100,8 +108,9 @@ public long GetValue(EndPoint server, StatItem item) /// The version of memcached public Version GetVersion(EndPoint server) { + server = server.GetIPEndPoint(_useIPv6); string version = GetRaw(server, StatItem.Version); - if (String.IsNullOrEmpty(version)) + if (string.IsNullOrEmpty(version)) throw new ArgumentException("No version found for the server " + server); return new Version(version); @@ -114,12 +123,13 @@ public Version GetVersion(EndPoint server) /// A value indicating how long the server is running public TimeSpan GetUptime(EndPoint server) { + server = server.GetIPEndPoint(_useIPv6); string uptime = GetRaw(server, StatItem.Uptime); - if (String.IsNullOrEmpty(uptime)) + if (string.IsNullOrEmpty(uptime)) throw new ArgumentException("No uptime found for the server " + server); long value; - if (!Int64.TryParse(uptime, out value)) + if (!long.TryParse(uptime, out value)) throw new ArgumentException("Invalid uptime string was returned: " + uptime); return TimeSpan.FromSeconds(value); @@ -133,12 +143,11 @@ public TimeSpan GetUptime(EndPoint server) /// The value of the stat item public string GetRaw(EndPoint server, string key) { - Dictionary serverValues; - string retval; + server = server.GetIPEndPoint(_useIPv6); - if (_results.TryGetValue(server, out serverValues)) + if (_results.TryGetValue(server, out Dictionary serverValues)) { - if (serverValues.TryGetValue(key, out retval)) + if (serverValues.TryGetValue(key, out string retval)) return retval; if (_log.IsDebugEnabled) @@ -161,17 +170,17 @@ public string GetRaw(EndPoint server, string key) /// The value of the stat item public string GetRaw(EndPoint server, StatItem item) { + server = server.GetIPEndPoint(_useIPv6); + if ((int)item < StatKeys.Length && (int)item >= 0) return GetRaw(server, StatKeys[(int)item]); - throw new ArgumentOutOfRangeException("item"); + throw new ArgumentOutOfRangeException(nameof(item)); } public IEnumerable> GetRaw(string key) { - string tmp; - - return _results.Select(kvp => new KeyValuePair(kvp.Key, kvp.Value.TryGetValue(key, out tmp) ? tmp : null)).ToList(); + return _results.Select(kvp => new KeyValuePair(kvp.Key, kvp.Value.TryGetValue(key, out string tmp) ? tmp : null)).ToList(); } } } diff --git a/src/Enyim.Caching/MemcachedClient.cs b/src/Enyim.Caching/MemcachedClient.cs index cff2c889..671ed8bc 100755 --- a/src/Enyim.Caching/MemcachedClient.cs +++ b/src/Enyim.Caching/MemcachedClient.cs @@ -22,12 +22,13 @@ public partial class MemcachedClient : IMemcachedClient, IMemcachedResultsClient /// Represents a value which indicates that an item should never expire. /// public static readonly TimeSpan Infinite = TimeSpan.Zero; - private ILogger _logger; - private bool _suppressException; + private readonly ILogger _logger; + private readonly bool _suppressException; + private readonly IMemcachedKeyTransformer _keyTransformer; + private readonly ITranscoder _transcoder; + private readonly bool _userIPv6; private IServerPool _pool; - private IMemcachedKeyTransformer _keyTransformer; - private ITranscoder _transcoder; public IStoreOperationResultFactory StoreOperationResultFactory { get; set; } public IGetOperationResultFactory GetOperationResultFactory { get; set; } @@ -48,6 +49,7 @@ public MemcachedClient(ILoggerFactory loggerFactory, IMemcachedClientConfigurati throw new ArgumentNullException(nameof(configuration)); } + _userIPv6 = configuration.UseIPv6; _suppressException = configuration.SuppressException; _keyTransformer = configuration.CreateKeyTransformer() ?? new DefaultKeyTransformer(); _transcoder = configuration.CreateTranscoder() ?? new DefaultTranscoder(); @@ -1199,7 +1201,7 @@ public ServerStats Stats(string type) Task.WaitAll(tasks.ToArray()); } - return new ServerStats(results); + return new ServerStats(results, _userIPv6); } /// diff --git a/src/Enyim.Caching/NullMemcachedClient.cs b/src/Enyim.Caching/NullMemcachedClient.cs index 39100de6..095d8f0c 100644 --- a/src/Enyim.Caching/NullMemcachedClient.cs +++ b/src/Enyim.Caching/NullMemcachedClient.cs @@ -1,10 +1,10 @@ -using System; +using Enyim.Caching.Memcached; +using Enyim.Caching.Memcached.Results; +using Enyim.Caching.Memcached.Results.Factories; +using System; using System.Collections.Generic; using System.Net; using System.Threading.Tasks; -using Enyim.Caching.Memcached; -using Enyim.Caching.Memcached.Results; -using Enyim.Caching.Memcached.Results.Factories; namespace Enyim.Caching { @@ -200,7 +200,7 @@ public Task RemoveMultiAsync(params string[] keys) public ServerStats Stats() { - return new ServerStats(new Dictionary>()); + return new ServerStats(new Dictionary>(), false); } public ServerStats Stats(string type) diff --git a/test/MemcachedTest/MemcachedClientTest.cs b/test/MemcachedTest/MemcachedClientTest.cs index b7e41561..ef0c59d5 100755 --- a/test/MemcachedTest/MemcachedClientTest.cs +++ b/test/MemcachedTest/MemcachedClientTest.cs @@ -89,37 +89,31 @@ public void GetObjectTest() [Fact] public void DeleteObjectTest() { - using (MemcachedClient client = GetClient()) - { - TestData td = new TestData(); - Assert.True(client.Store(StoreMode.Set, TestObjectKey, td), "Initialization failed."); + using MemcachedClient client = GetClient(); + TestData td = new TestData(); + Assert.True(client.Store(StoreMode.Set, TestObjectKey, td), "Initialization failed."); - Assert.True(client.Remove(TestObjectKey), "Remove failed."); - Assert.Null(client.Get(TestObjectKey)); - } + Assert.True(client.Remove(TestObjectKey), "Remove failed."); + Assert.Null(client.Get(TestObjectKey)); } [Fact] public async Task StoreStringTest() { - using (MemcachedClient client = GetClient()) - { - Assert.True(await client.StoreAsync(StoreMode.Set, "TestString", "Hello world!", DateTime.Now.AddSeconds(10)), "StoreString failed."); + using MemcachedClient client = GetClient(); + Assert.True(await client.StoreAsync(StoreMode.Set, "TestString", "Hello world!", DateTime.Now.AddSeconds(10)), "StoreString failed."); - Assert.Equal("Hello world!", await client.GetValueAsync("TestString")); - } + Assert.Equal("Hello world!", await client.GetValueAsync("TestString")); } [Fact] public void StoreLongTest() { - using (MemcachedClient client = GetClient()) - { - Assert.True(client.Store(StoreMode.Set, "TestLong", 65432123456L), "StoreLong failed."); + using MemcachedClient client = GetClient(); + Assert.True(client.Store(StoreMode.Set, "TestLong", 65432123456L), "StoreLong failed."); - Assert.Equal(65432123456L, client.Get("TestLong")); - } + Assert.Equal(65432123456L, client.Get("TestLong")); } [Fact] @@ -232,7 +226,7 @@ public void AddSetReplaceTest() } } - private string[] keyParts = { "multi", "get", "test", "key", "parts", "test", "values" }; + private readonly string[] keyParts = { "multi", "get", "test", "key", "parts", "test", "values" }; protected string MakeRandomKey(int partCount) { @@ -252,44 +246,42 @@ protected string MakeRandomKey(int partCount) [Fact] public async Task MultiGetTest() { - using (var client = GetClient()) - { - var keys = new List(); - - for (int i = 0; i < 10; i++) - { - string k = $"Hello_Multi_Get_{Guid.NewGuid()}_" + i; - keys.Add(k); + using var client = GetClient(); + var keys = new List(); - Assert.True(await client.StoreAsync(StoreMode.Set, k, i, DateTime.Now.AddSeconds(30)), "Store of " + k + " failed"); - } + for (int i = 0; i < 10; i++) + { + string k = $"Hello_Multi_Get_{Guid.NewGuid()}_" + i; + keys.Add(k); - IDictionary retvals = await client.GetAsync(keys); + Assert.True(await client.StoreAsync(StoreMode.Set, k, i, DateTime.Now.AddSeconds(30)), "Store of " + k + " failed"); + } - Assert.NotEmpty(retvals); - Assert.Equal(keys.Count, retvals.Count); + IDictionary retvals = await client.GetAsync(keys); - int value = 0; - for (int i = 0; i < keys.Count; i++) - { - string key = keys[i]; + Assert.NotEmpty(retvals); + Assert.Equal(keys.Count, retvals.Count); - Assert.True(retvals.TryGetValue(key, out value), "missing key: " + key); - Assert.Equal(value, i); - } + int value = 0; + for (int i = 0; i < keys.Count; i++) + { + string key = keys[i]; - var key1 = $"test_key1_{Guid.NewGuid()}"; - var key2 = $"test_key2_{Guid.NewGuid()}"; - var obj1 = new HashSet { 1, 2 }; - var obj2 = new HashSet { 3, 4 }; - await client.StoreAsync(StoreMode.Set, key1, obj1, DateTime.Now.AddSeconds(10)); - await client.StoreAsync(StoreMode.Set, key2, obj2, DateTime.Now.AddSeconds(10)); - - var multiResult = await client.GetAsync>(new string[] { key1, key2 }); - Assert.Equal(2, multiResult.Count); - Assert.Equal(obj1.First(), multiResult[key1].First()); - Assert.Equal(obj2.First(), multiResult[key2].First()); + Assert.True(retvals.TryGetValue(key, out value), "missing key: " + key); + Assert.Equal(value, i); } + + var key1 = $"test_key1_{Guid.NewGuid()}"; + var key2 = $"test_key2_{Guid.NewGuid()}"; + var obj1 = new HashSet { 1, 2 }; + var obj2 = new HashSet { 3, 4 }; + await client.StoreAsync(StoreMode.Set, key1, obj1, DateTime.Now.AddSeconds(10)); + await client.StoreAsync(StoreMode.Set, key2, obj2, DateTime.Now.AddSeconds(10)); + + var multiResult = await client.GetAsync>(new string[] { key1, key2 }); + Assert.Equal(2, multiResult.Count); + Assert.Equal(obj1.First(), multiResult[key1].First()); + Assert.Equal(obj2.First(), multiResult[key2].First()); } [Fact] @@ -333,31 +325,27 @@ public void IncrementLongTest() { var initialValue = 56UL * (ulong)Math.Pow(10, 11) + 1234; - using (MemcachedClient client = GetClient()) - { - Assert.Equal(initialValue, client.Increment("VALUE", initialValue, 2UL)); - Assert.Equal(initialValue + 24, client.Increment("VALUE", 10UL, 24UL)); - } + using MemcachedClient client = GetClient(); + Assert.Equal(initialValue, client.Increment("VALUE", initialValue, 2UL)); + Assert.Equal(initialValue + 24, client.Increment("VALUE", 10UL, 24UL)); } [Fact] public async Task FlushTest() { - using (MemcachedClient client = GetClient()) + using MemcachedClient client = GetClient(); + for (int i = 0; i < 10; i++) { - for (int i = 0; i < 10; i++) - { - string cacheKey = $"Hello_Flush_{i}"; - Assert.True(await client.StoreAsync(StoreMode.Set, cacheKey, i, DateTime.Now.AddSeconds(30))); - } + string cacheKey = $"Hello_Flush_{i}"; + Assert.True(await client.StoreAsync(StoreMode.Set, cacheKey, i, DateTime.Now.AddSeconds(30))); + } - await client.FlushAllAsync(); + await client.FlushAllAsync(); - for (int i = 0; i < 10; i++) - { - string cacheKey = $"Hello_Flush_{i}"; - Assert.Null(await client.GetValueAsync(cacheKey)); - } + for (int i = 0; i < 10; i++) + { + string cacheKey = $"Hello_Flush_{i}"; + Assert.Null(await client.GetValueAsync(cacheKey)); } } }