diff --git a/Enyim.Caching/Configuration/IMemcachedClientConfiguration.cs b/Enyim.Caching/Configuration/IMemcachedClientConfiguration.cs index b9cc125e..2951d33e 100755 --- a/Enyim.Caching/Configuration/IMemcachedClientConfiguration.cs +++ b/Enyim.Caching/Configuration/IMemcachedClientConfiguration.cs @@ -42,6 +42,8 @@ public interface IMemcachedClientConfiguration IServerPool CreatePool(); + bool UseSslStream { get; } + } } diff --git a/Enyim.Caching/Configuration/MemcachedClientConfiguration.cs b/Enyim.Caching/Configuration/MemcachedClientConfiguration.cs index 9e93bcc5..d28b9504 100755 --- a/Enyim.Caching/Configuration/MemcachedClientConfiguration.cs +++ b/Enyim.Caching/Configuration/MemcachedClientConfiguration.cs @@ -1,352 +1,363 @@ -using System; -using System.Collections.Generic; -using System.Net; -using Enyim.Caching.Memcached; -using Enyim.Reflection; -using Enyim.Caching.Memcached.Protocol.Binary; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Microsoft.Extensions.Configuration; -using System.Linq; -using System.Net.Sockets; - -namespace Enyim.Caching.Configuration -{ - /// - /// Configuration class - /// - public class MemcachedClientConfiguration : IMemcachedClientConfiguration - { - // these are lazy initialized in the getters - private Type nodeLocator; - private ITranscoder _transcoder; - private IMemcachedKeyTransformer _keyTransformer; - private ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - public MemcachedClientConfiguration( - ILoggerFactory loggerFactory, - IOptions optionsAccessor, - IConfiguration configuration = null, - ITranscoder transcoder = null, - IMemcachedKeyTransformer keyTransformer = null) - { - if (optionsAccessor == null) - { - throw new ArgumentNullException(nameof(optionsAccessor)); - } - - _logger = loggerFactory.CreateLogger(); - - var options = optionsAccessor.Value; - if ((options == null || options.Servers.Count == 0) && configuration != null) - { - var section = configuration.GetSection("enyimMemcached"); - if (section.Exists()) - { - section.Bind(options); - } - else - { - _logger.LogWarning($"No enyimMemcached setting in appsetting.json. Use default configuration"); - options.AddDefaultServer(); - } - } - - ConfigureServers(options); - - SocketPool = new SocketPoolConfiguration(); - if (options.SocketPool != null) - { - options.SocketPool.CheckPoolSize(); - options.SocketPool.CheckTimeout(); - - SocketPool.MinPoolSize = options.SocketPool.MinPoolSize; - _logger.LogInformation($"{nameof(SocketPool.MinPoolSize)}: {SocketPool.MinPoolSize}"); - - SocketPool.MaxPoolSize = options.SocketPool.MaxPoolSize; - _logger.LogInformation($"{nameof(SocketPool.MaxPoolSize)}: {SocketPool.MaxPoolSize}"); - - SocketPool.ConnectionTimeout = options.SocketPool.ConnectionTimeout; - _logger.LogInformation($"{nameof(SocketPool.ConnectionTimeout)}: {SocketPool.ConnectionTimeout}"); - - SocketPool.ReceiveTimeout = options.SocketPool.ReceiveTimeout; - _logger.LogInformation($"{nameof(SocketPool.ReceiveTimeout)}: {SocketPool.ReceiveTimeout}"); - - SocketPool.DeadTimeout = options.SocketPool.DeadTimeout; - _logger.LogInformation($"{nameof(SocketPool.DeadTimeout)}: {SocketPool.DeadTimeout}"); - - SocketPool.QueueTimeout = options.SocketPool.QueueTimeout; - _logger.LogInformation($"{nameof(SocketPool.QueueTimeout)}: {SocketPool.QueueTimeout}"); - - SocketPool.InitPoolTimeout = options.SocketPool.InitPoolTimeout; - - SocketPool.FailurePolicyFactory = options.SocketPool.FailurePolicyFactory; - } - - Protocol = options.Protocol; - - if (options.Authentication != null && !string.IsNullOrEmpty(options.Authentication.Type)) - { - try - { - var authenticationType = Type.GetType(options.Authentication.Type); - if (authenticationType != null) - { - _logger.LogDebug($"Authentication type is {authenticationType}."); - - Authentication = new AuthenticationConfiguration(); - Authentication.Type = authenticationType; - foreach (var parameter in options.Authentication.Parameters) - { - Authentication.Parameters[parameter.Key] = parameter.Value; - _logger.LogDebug($"Authentication {parameter.Key} is '{parameter.Value}'."); - } - } - else - { - _logger.LogError($"Unable to load authentication type {options.Authentication.Type}."); - } - } - catch (Exception ex) - { - _logger.LogError(new EventId(), ex, $"Unable to load authentication type {options.Authentication.Type}."); - } - } - - if (!string.IsNullOrEmpty(options.KeyTransformer)) - { - try - { - var keyTransformerType = Type.GetType(options.KeyTransformer); - if (keyTransformerType != null) - { - KeyTransformer = Activator.CreateInstance(keyTransformerType) as IMemcachedKeyTransformer; - _logger.LogDebug($"Use '{options.KeyTransformer}' KeyTransformer"); - } - } - catch (Exception ex) - { - _logger.LogError(new EventId(), ex, $"Unable to load '{options.KeyTransformer}' KeyTransformer"); - } - } - else if (keyTransformer != null) - { - this._keyTransformer = keyTransformer; - _logger.LogDebug($"Use KeyTransformer Type : '{keyTransformer.ToString()}'"); - } - - if (NodeLocator == null) - { - NodeLocator = options.Servers.Count > 1 ? typeof(DefaultNodeLocator) : typeof(SingleNodeLocator); - } - - if (!string.IsNullOrEmpty(options.Transcoder)) - { - try - { - if (options.Transcoder == "BinaryFormatterTranscoder") - options.Transcoder = "Enyim.Caching.Memcached.Transcoders.BinaryFormatterTranscoder"; - - var transcoderType = Type.GetType(options.Transcoder); - if (transcoderType != null) - { - Transcoder = Activator.CreateInstance(transcoderType) as ITranscoder; - _logger.LogDebug($"Use '{options.Transcoder}'"); - } - } - catch (Exception ex) - { - _logger.LogError(new EventId(), ex, $"Unable to load '{options.Transcoder}'"); - } - } - else if (transcoder != null) - { - this._transcoder = transcoder; - _logger.LogDebug($"Use Transcoder Type : '{transcoder.ToString()}'"); - } - - if (options.NodeLocatorFactory != null) - { - NodeLocatorFactory = options.NodeLocatorFactory; - } - } - - private void ConfigureServers(MemcachedClientOptions options) - { - Servers = new List(); - foreach (var server in options.Servers) - { - if (!IPAddress.TryParse(server.Address, out var address)) - { - address = Dns.GetHostAddresses(server.Address) - .FirstOrDefault(i => i.AddressFamily == AddressFamily.InterNetwork); - - if (address == null) - { - _logger.LogError($"Could not resolve host '{server.Address}'."); - } - else - { - _logger.LogInformation($"Memcached server address - {address}"); - } - } - else - { - _logger.LogInformation($"Memcached server address - {server.Address }:{server.Port}"); - } - - Servers.Add(new IPEndPoint(address, server.Port)); - } - } - - /// - /// Adds a new server to the pool. - /// - /// The address and the port of the server in the format 'host:port'. - public void AddServer(string address) - { - this.Servers.Add(ConfigurationHelper.ResolveToEndPoint(address)); - } - - /// - /// Adds a new server to the pool. - /// - /// The host name or IP address of the server. - /// The port number of the memcached instance. - public void AddServer(string host, int port) - { - this.Servers.Add(new DnsEndPoint(host, port)); - } - - /// - /// Gets a list of each representing a Memcached server in the pool. - /// - public IList Servers { get; private set; } - - /// - /// Gets the configuration of the socket pool. - /// - public ISocketPoolConfiguration SocketPool { get; private set; } - - /// - /// Gets the authentication settings. - /// - public IAuthenticationConfiguration Authentication { get; private set; } - - /// - /// Gets or sets the which will be used to convert item keys for Memcached. - /// - public IMemcachedKeyTransformer KeyTransformer - { - get { return this._keyTransformer ?? (this._keyTransformer = new DefaultKeyTransformer()); } - set { this._keyTransformer = value; } - } - - /// - /// Gets or sets the Type of the which will be used to assign items to Memcached nodes. - /// - /// If both and are assigned then the latter takes precedence. - public Type NodeLocator - { - get { return this.nodeLocator; } - set - { - ConfigurationHelper.CheckForInterface(value, typeof(IMemcachedNodeLocator)); - this.nodeLocator = value; - } - } - - /// - /// Gets or sets the NodeLocatorFactory instance which will be used to create a new IMemcachedNodeLocator instances. - /// - /// If both and are assigned then the latter takes precedence. - public IProviderFactory NodeLocatorFactory { get; set; } - - /// - /// Gets or sets the which will be used serialize or deserialize items. - /// - public ITranscoder Transcoder - { - get { return _transcoder ?? (_transcoder = new DefaultTranscoder()); } - set { _transcoder = value; } - } - - /// - /// Gets or sets the type of the communication between client and server. - /// - public MemcachedProtocol Protocol { get; set; } - - #region [ interface ] - - IList IMemcachedClientConfiguration.Servers - { - get { return this.Servers; } - } - - ISocketPoolConfiguration IMemcachedClientConfiguration.SocketPool - { - get { return this.SocketPool; } - } - - IAuthenticationConfiguration IMemcachedClientConfiguration.Authentication - { - get { return this.Authentication; } - } - - IMemcachedKeyTransformer IMemcachedClientConfiguration.CreateKeyTransformer() - { - return this.KeyTransformer; - } - - IMemcachedNodeLocator IMemcachedClientConfiguration.CreateNodeLocator() - { - var f = this.NodeLocatorFactory; - if (f != null) return f.Create(); - - return this.NodeLocator == null - ? new SingleNodeLocator() - : (IMemcachedNodeLocator)FastActivator.Create(this.NodeLocator); - } - - ITranscoder IMemcachedClientConfiguration.CreateTranscoder() - { - return this.Transcoder; - } - - IServerPool IMemcachedClientConfiguration.CreatePool() - { - switch (this.Protocol) - { - case MemcachedProtocol.Text: return new DefaultServerPool(this, new Memcached.Protocol.Text.TextOperationFactory(), _logger); - case MemcachedProtocol.Binary: return new BinaryPool(this, _logger); - } - - throw new ArgumentOutOfRangeException("Unknown protocol: " + (int)this.Protocol); - } - - #endregion - } -} - -#region [ License information ] -/* ************************************************************ - * - * Copyright (c) 2010 Attila Kisk? enyim.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ************************************************************/ -#endregion +using System; +using System.Collections.Generic; +using System.Net; +using Enyim.Caching.Memcached; +using Enyim.Reflection; +using Enyim.Caching.Memcached.Protocol.Binary; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.Configuration; +using System.Linq; +using System.Net.Sockets; + +namespace Enyim.Caching.Configuration +{ + /// + /// Configuration class + /// + public class MemcachedClientConfiguration : IMemcachedClientConfiguration + { + // these are lazy initialized in the getters + private Type nodeLocator; + private ITranscoder _transcoder; + private IMemcachedKeyTransformer _keyTransformer; + private ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + public MemcachedClientConfiguration( + ILoggerFactory loggerFactory, + IOptions optionsAccessor, + IConfiguration configuration = null, + ITranscoder transcoder = null, + IMemcachedKeyTransformer keyTransformer = null) + { + if (optionsAccessor == null) + { + throw new ArgumentNullException(nameof(optionsAccessor)); + } + + _logger = loggerFactory.CreateLogger(); + + var options = optionsAccessor.Value; + if ((options == null || options.Servers.Count == 0) && configuration != null) + { + var section = configuration.GetSection("enyimMemcached"); + if (section.Exists()) + { + section.Bind(options); + } + else + { + _logger.LogWarning($"No enyimMemcached setting in appsetting.json. Use default configuration"); + options.AddDefaultServer(); + } + } + + ConfigureServers(options); + + SocketPool = new SocketPoolConfiguration(); + if (options.SocketPool != null) + { + options.SocketPool.CheckPoolSize(); + options.SocketPool.CheckTimeout(); + + SocketPool.MinPoolSize = options.SocketPool.MinPoolSize; + _logger.LogInformation($"{nameof(SocketPool.MinPoolSize)}: {SocketPool.MinPoolSize}"); + + SocketPool.MaxPoolSize = options.SocketPool.MaxPoolSize; + _logger.LogInformation($"{nameof(SocketPool.MaxPoolSize)}: {SocketPool.MaxPoolSize}"); + + SocketPool.ConnectionTimeout = options.SocketPool.ConnectionTimeout; + _logger.LogInformation($"{nameof(SocketPool.ConnectionTimeout)}: {SocketPool.ConnectionTimeout}"); + + SocketPool.ReceiveTimeout = options.SocketPool.ReceiveTimeout; + _logger.LogInformation($"{nameof(SocketPool.ReceiveTimeout)}: {SocketPool.ReceiveTimeout}"); + + SocketPool.DeadTimeout = options.SocketPool.DeadTimeout; + _logger.LogInformation($"{nameof(SocketPool.DeadTimeout)}: {SocketPool.DeadTimeout}"); + + SocketPool.QueueTimeout = options.SocketPool.QueueTimeout; + _logger.LogInformation($"{nameof(SocketPool.QueueTimeout)}: {SocketPool.QueueTimeout}"); + + SocketPool.InitPoolTimeout = options.SocketPool.InitPoolTimeout; + + SocketPool.FailurePolicyFactory = options.SocketPool.FailurePolicyFactory; + } + + Protocol = options.Protocol; + + if (options.Authentication != null && !string.IsNullOrEmpty(options.Authentication.Type)) + { + try + { + var authenticationType = Type.GetType(options.Authentication.Type); + if (authenticationType != null) + { + _logger.LogDebug($"Authentication type is {authenticationType}."); + + Authentication = new AuthenticationConfiguration(); + Authentication.Type = authenticationType; + foreach (var parameter in options.Authentication.Parameters) + { + Authentication.Parameters[parameter.Key] = parameter.Value; + _logger.LogDebug($"Authentication {parameter.Key} is '{parameter.Value}'."); + } + } + else + { + _logger.LogError($"Unable to load authentication type {options.Authentication.Type}."); + } + } + catch (Exception ex) + { + _logger.LogError(new EventId(), ex, $"Unable to load authentication type {options.Authentication.Type}."); + } + } + + UseSslStream = options.UseSslStream; + + if (!string.IsNullOrEmpty(options.KeyTransformer)) + { + try + { + var keyTransformerType = Type.GetType(options.KeyTransformer); + if (keyTransformerType != null) + { + KeyTransformer = Activator.CreateInstance(keyTransformerType) as IMemcachedKeyTransformer; + _logger.LogDebug($"Use '{options.KeyTransformer}' KeyTransformer"); + } + } + catch (Exception ex) + { + _logger.LogError(new EventId(), ex, $"Unable to load '{options.KeyTransformer}' KeyTransformer"); + } + } + else if (keyTransformer != null) + { + this._keyTransformer = keyTransformer; + _logger.LogDebug($"Use KeyTransformer Type : '{keyTransformer.ToString()}'"); + } + + if (NodeLocator == null) + { + NodeLocator = options.Servers.Count > 1 ? typeof(DefaultNodeLocator) : typeof(SingleNodeLocator); + } + + if (!string.IsNullOrEmpty(options.Transcoder)) + { + try + { + if (options.Transcoder == "BinaryFormatterTranscoder") + options.Transcoder = "Enyim.Caching.Memcached.Transcoders.BinaryFormatterTranscoder"; + + var transcoderType = Type.GetType(options.Transcoder); + if (transcoderType != null) + { + Transcoder = Activator.CreateInstance(transcoderType) as ITranscoder; + _logger.LogDebug($"Use '{options.Transcoder}'"); + } + } + catch (Exception ex) + { + _logger.LogError(new EventId(), ex, $"Unable to load '{options.Transcoder}'"); + } + } + else if (transcoder != null) + { + this._transcoder = transcoder; + _logger.LogDebug($"Use Transcoder Type : '{transcoder.ToString()}'"); + } + + if (options.NodeLocatorFactory != null) + { + NodeLocatorFactory = options.NodeLocatorFactory; + } + } + + private void ConfigureServers(MemcachedClientOptions options) + { + Servers = new List(); + foreach (var server in options.Servers) + { + if (options.UseSslStream) + { + AddServer(server.Address, server.Port); + } + else + { + if (!IPAddress.TryParse(server.Address, out var address)) + { + address = Dns.GetHostAddresses(server.Address) + .FirstOrDefault(i => i.AddressFamily == AddressFamily.InterNetwork); + + if (address == null) + { + _logger.LogError($"Could not resolve host '{server.Address}'."); + } + else + { + _logger.LogInformation($"Memcached server address - {address}"); + } + } + else + { + _logger.LogInformation($"Memcached server address - {server.Address }:{server.Port}"); + } + + Servers.Add(new IPEndPoint(address, server.Port)); + } + } + } + + /// + /// Adds a new server to the pool. + /// + /// The address and the port of the server in the format 'host:port'. + public void AddServer(string address) + { + this.Servers.Add(ConfigurationHelper.ResolveToEndPoint(address)); + } + + /// + /// Adds a new server to the pool. + /// + /// The host name or IP address of the server. + /// The port number of the memcached instance. + public void AddServer(string host, int port) + { + this.Servers.Add(new DnsEndPoint(host, port)); + } + + /// + /// Gets a list of each representing a Memcached server in the pool. + /// + public IList Servers { get; private set; } + + /// + /// Gets the configuration of the socket pool. + /// + public ISocketPoolConfiguration SocketPool { get; private set; } + + /// + /// Gets the authentication settings. + /// + public IAuthenticationConfiguration Authentication { get; private set; } + + /// + /// Gets or sets the which will be used to convert item keys for Memcached. + /// + public IMemcachedKeyTransformer KeyTransformer + { + get { return this._keyTransformer ?? (this._keyTransformer = new DefaultKeyTransformer()); } + set { this._keyTransformer = value; } + } + + /// + /// Gets or sets the Type of the which will be used to assign items to Memcached nodes. + /// + /// If both and are assigned then the latter takes precedence. + public Type NodeLocator + { + get { return this.nodeLocator; } + set + { + ConfigurationHelper.CheckForInterface(value, typeof(IMemcachedNodeLocator)); + this.nodeLocator = value; + } + } + + /// + /// Gets or sets the NodeLocatorFactory instance which will be used to create a new IMemcachedNodeLocator instances. + /// + /// If both and are assigned then the latter takes precedence. + public IProviderFactory NodeLocatorFactory { get; set; } + + /// + /// Gets or sets the which will be used serialize or deserialize items. + /// + public ITranscoder Transcoder + { + get { return _transcoder ?? (_transcoder = new DefaultTranscoder()); } + set { _transcoder = value; } + } + + /// + /// Gets or sets the type of the communication between client and server. + /// + public MemcachedProtocol Protocol { get; set; } + + #region [ interface ] + + IList IMemcachedClientConfiguration.Servers + { + get { return this.Servers; } + } + + ISocketPoolConfiguration IMemcachedClientConfiguration.SocketPool + { + get { return this.SocketPool; } + } + + IAuthenticationConfiguration IMemcachedClientConfiguration.Authentication + { + get { return this.Authentication; } + } + + IMemcachedKeyTransformer IMemcachedClientConfiguration.CreateKeyTransformer() + { + return this.KeyTransformer; + } + + IMemcachedNodeLocator IMemcachedClientConfiguration.CreateNodeLocator() + { + var f = this.NodeLocatorFactory; + if (f != null) return f.Create(); + + return this.NodeLocator == null + ? new SingleNodeLocator() + : (IMemcachedNodeLocator)FastActivator.Create(this.NodeLocator); + } + + ITranscoder IMemcachedClientConfiguration.CreateTranscoder() + { + return this.Transcoder; + } + + IServerPool IMemcachedClientConfiguration.CreatePool() + { + switch (this.Protocol) + { + case MemcachedProtocol.Text: return new DefaultServerPool(this, new Memcached.Protocol.Text.TextOperationFactory(), _logger); + case MemcachedProtocol.Binary: return new BinaryPool(this, _logger); + } + + throw new ArgumentOutOfRangeException("Unknown protocol: " + (int)this.Protocol); + } + + public bool UseSslStream { get; private set; } + + #endregion + } +} + +#region [ License information ] +/* ************************************************************ + * + * Copyright (c) 2010 Attila Kisk? enyim.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ************************************************************/ +#endregion diff --git a/Enyim.Caching/Configuration/MemcachedClientOptions.cs b/Enyim.Caching/Configuration/MemcachedClientOptions.cs index 0a9306d9..4f58ab85 100644 --- a/Enyim.Caching/Configuration/MemcachedClientOptions.cs +++ b/Enyim.Caching/Configuration/MemcachedClientOptions.cs @@ -21,6 +21,8 @@ public class MemcachedClientOptions : IOptions public string Transcoder { get; set; } + public bool UseSslStream { get; set; } + public IProviderFactory NodeLocatorFactory { get; set; } public MemcachedClientOptions Value => this; diff --git a/Enyim.Caching/Configuration/SocketPoolConfiguration.cs b/Enyim.Caching/Configuration/SocketPoolConfiguration.cs index 0e72845c..05f5c6cc 100644 --- a/Enyim.Caching/Configuration/SocketPoolConfiguration.cs +++ b/Enyim.Caching/Configuration/SocketPoolConfiguration.cs @@ -10,6 +10,7 @@ public class SocketPoolConfiguration : ISocketPoolConfiguration { private int minPoolSize = 5; private int maxPoolSize = 100; + private bool useSslStream = false; private TimeSpan connectionTimeout = new TimeSpan(0, 0, 10); private TimeSpan receiveTimeout = new TimeSpan(0, 0, 10); private TimeSpan deadTimeout = new TimeSpan(0, 0, 10); diff --git a/Enyim.Caching/Enyim.Caching.csproj b/Enyim.Caching/Enyim.Caching.csproj index cb2c1e1f..47ef98e7 100755 --- a/Enyim.Caching/Enyim.Caching.csproj +++ b/Enyim.Caching/Enyim.Caching.csproj @@ -3,7 +3,7 @@ EnyimMemcachedCore is a Memcached client library for .NET Core. Usage: Add services.AddEnyimMemcached(...) and app.UseEnyimMemcached() in Startup. Add IMemcachedClient into constructor. cnblogs.com - netstandard2.0;netstandard2.1;net5.0 + netstandard2.0;netstandard2.1;net6.0 true EnyimMemcachedCore EnyimMemcachedCore @@ -16,11 +16,11 @@ - - - - - + + + + + diff --git a/Enyim.Caching/Memcached/DefaultServerPool.cs b/Enyim.Caching/Memcached/DefaultServerPool.cs index 0dac96ff..2eb90c18 100644 --- a/Enyim.Caching/Memcached/DefaultServerPool.cs +++ b/Enyim.Caching/Memcached/DefaultServerPool.cs @@ -50,7 +50,7 @@ public DefaultServerPool( protected virtual IMemcachedNode CreateNode(EndPoint endpoint) { - return new MemcachedNode(endpoint, this.configuration.SocketPool, _logger); + return new MemcachedNode(endpoint, this.configuration.SocketPool, _logger, this.configuration.UseSslStream); } private void rezCallback(object state) diff --git a/Enyim.Caching/Memcached/MemcachedNode.cs b/Enyim.Caching/Memcached/MemcachedNode.cs index 84c5201d..c54d9166 100755 --- a/Enyim.Caching/Memcached/MemcachedNode.cs +++ b/Enyim.Caching/Memcached/MemcachedNode.cs @@ -35,13 +35,16 @@ public class MemcachedNode : IMemcachedNode private bool isInitialized = false; private SemaphoreSlim poolInitSemaphore = new SemaphoreSlim(1, 1); private readonly TimeSpan _initPoolTimeout; + private bool _useSslStream; public MemcachedNode( EndPoint endpoint, ISocketPoolConfiguration socketPoolConfig, - ILogger logger) + ILogger logger, + bool useSslStream) { _endPoint = endpoint; + _useSslStream = useSslStream; EndPointString = endpoint?.ToString().Replace("Unspecified/", string.Empty); _config = socketPoolConfig; @@ -800,7 +803,7 @@ protected internal virtual PooledSocket CreateSocket() { try { - var ps = new PooledSocket(_endPoint, _config.ConnectionTimeout, _config.ReceiveTimeout, _logger); + var ps = new PooledSocket(_endPoint, _config.ConnectionTimeout, _config.ReceiveTimeout, _logger, _useSslStream); ps.Connect(); return ps; } @@ -816,7 +819,7 @@ protected internal virtual async Task CreateSocketAsync() { try { - var ps = new PooledSocket(_endPoint, _config.ConnectionTimeout, _config.ReceiveTimeout, _logger); + var ps = new PooledSocket(_endPoint, _config.ConnectionTimeout, _config.ReceiveTimeout, _logger, _useSslStream); await ps.ConnectAsync(); return ps; } diff --git a/Enyim.Caching/Memcached/PooledSocket.cs b/Enyim.Caching/Memcached/PooledSocket.cs index f8df8747..ec98791a 100755 --- a/Enyim.Caching/Memcached/PooledSocket.cs +++ b/Enyim.Caching/Memcached/PooledSocket.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Net; +using System.Net.Security; using System.Net.Sockets; using System.Text; using System.Threading; @@ -18,16 +19,19 @@ public partial class PooledSocket : IDisposable private readonly ILogger _logger; private bool _isAlive; + private bool _useSslStream; private Socket _socket; private readonly EndPoint _endpoint; private readonly int _connectionTimeout; private NetworkStream _inputStream; + private SslStream _sslStream; - public PooledSocket(EndPoint endpoint, TimeSpan connectionTimeout, TimeSpan receiveTimeout, ILogger logger) + public PooledSocket(EndPoint endpoint, TimeSpan connectionTimeout, TimeSpan receiveTimeout, ILogger logger, bool useSslStream) { _logger = logger; _isAlive = true; + _useSslStream = useSslStream; var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); @@ -90,7 +94,15 @@ void Cancel() if (success) { - _inputStream = new NetworkStream(_socket); + if (_useSslStream) + { + _sslStream = new SslStream(new NetworkStream(_socket)); + _sslStream.AuthenticateAsClient(((DnsEndPoint)_endpoint).Host); + } + else + { + _inputStream = new NetworkStream(_socket); + } } else { @@ -141,7 +153,15 @@ public async Task ConnectAsync() if (success) { - _inputStream = new NetworkStream(_socket); + if (_useSslStream) + { + _sslStream = new SslStream(new NetworkStream(_socket)); + await _sslStream.AuthenticateAsClientAsync(((DnsEndPoint)_endpoint).Host); + } + else + { + _inputStream = new NetworkStream(_socket); + } } else { @@ -251,7 +271,13 @@ protected void Dispose(bool disposing) _inputStream.Dispose(); } + if (_sslStream != null) + { + _sslStream.Dispose(); + } + _inputStream = null; + _sslStream = null; _socket = null; this.CleanupCallback = null; } @@ -290,7 +316,7 @@ public int ReadByte() try { - return _inputStream.ReadByte(); + return (_useSslStream ? _sslStream.ReadByte() : _inputStream.ReadByte()); } catch (Exception ex) { @@ -309,7 +335,7 @@ public int ReadByteAsync() try { - return _inputStream.ReadByte(); + return (_useSslStream ? _sslStream.ReadByte() : _inputStream.ReadByte()); } catch (Exception ex) { @@ -332,7 +358,7 @@ public async Task ReadAsync(byte[] buffer, int offset, int count) { try { - int currentRead = await _inputStream.ReadAsync(buffer, offset, shouldRead); + int currentRead = (_useSslStream ? await _sslStream.ReadAsync(buffer, offset, shouldRead) : await _inputStream.ReadAsync(buffer, offset, shouldRead)); if (currentRead == count) break; if (currentRead < 1) @@ -372,7 +398,7 @@ public void Read(byte[] buffer, int offset, int count) { try { - int currentRead = _inputStream.Read(buffer, offset, shouldRead); + int currentRead = (_useSslStream ? _sslStream.Read(buffer, offset, shouldRead) : _inputStream.Read(buffer, offset, shouldRead)); if (currentRead == count) break; if (currentRead < 1) @@ -397,15 +423,34 @@ public void Write(byte[] data, int offset, int length) { this.CheckDisposed(); - SocketError status; + if (_useSslStream) + { + try + { + _sslStream.Write(data, offset, length); + _sslStream.Flush(); + } + catch (Exception ex) + { + if (ex is IOException || ex is SocketException) + { + _isAlive = false; + } + throw; + } + } + else + { + SocketError status; - _socket.Send(data, offset, length, SocketFlags.None, out status); + _socket.Send(data, offset, length, SocketFlags.None, out status); - if (status != SocketError.Success) - { - _isAlive = false; + if (status != SocketError.Success) + { + _isAlive = false; - ThrowHelper.ThrowSocketWriteError(_endpoint, status); + ThrowHelper.ThrowSocketWriteError(_endpoint, status); + } } } @@ -417,11 +462,22 @@ public void Write(IList> buffers) try { - _socket.Send(buffers, SocketFlags.None, out status); - if (status != SocketError.Success) + if (_useSslStream) { - _isAlive = false; - ThrowHelper.ThrowSocketWriteError(_endpoint, status); + foreach (var buf in buffers) + { + _sslStream.Write(buf.Array); + } + _sslStream.Flush(); + } + else + { + _socket.Send(buffers, SocketFlags.None, out status); + if (status != SocketError.Success) + { + _isAlive = false; + ThrowHelper.ThrowSocketWriteError(_endpoint, status); + } } } catch (Exception ex) @@ -441,12 +497,23 @@ public async Task WriteAsync(IList> buffers) try { - var bytesTransferred = await _socket.SendAsync(buffers, SocketFlags.None); - if (bytesTransferred <= 0) + if (_useSslStream) { - _isAlive = false; - _logger.LogError($"Failed to {nameof(PooledSocket.WriteAsync)}. bytesTransferred: {bytesTransferred}"); - ThrowHelper.ThrowSocketWriteError(_endpoint); + foreach (var buf in buffers) + { + await _sslStream.WriteAsync(buf.Array,0,buf.Count); + } + await _sslStream.FlushAsync(); + } + else + { + var bytesTransferred = await _socket.SendAsync(buffers, SocketFlags.None); + if (bytesTransferred <= 0) + { + _isAlive = false; + _logger.LogError($"Failed to {nameof(PooledSocket.WriteAsync)}. bytesTransferred: {bytesTransferred}"); + ThrowHelper.ThrowSocketWriteError(_endpoint); + } } } catch (Exception ex) diff --git a/Enyim.Caching/Memcached/Protocol/Binary/BinaryNode.cs b/Enyim.Caching/Memcached/Protocol/Binary/BinaryNode.cs index 10356fb4..d7191b16 100644 --- a/Enyim.Caching/Memcached/Protocol/Binary/BinaryNode.cs +++ b/Enyim.Caching/Memcached/Protocol/Binary/BinaryNode.cs @@ -23,8 +23,9 @@ public BinaryNode( EndPoint endpoint, ISocketPoolConfiguration config, ISaslAuthenticationProvider authenticationProvider, - ILogger logger) - : base(endpoint, config, logger) + ILogger logger, + bool useSslStream) + : base(endpoint, config, logger, useSslStream) { this.authenticationProvider = authenticationProvider; _logger = logger; diff --git a/Enyim.Caching/Memcached/Protocol/Binary/BinaryPool.cs b/Enyim.Caching/Memcached/Protocol/Binary/BinaryPool.cs index 8f4e1763..d134eff3 100644 --- a/Enyim.Caching/Memcached/Protocol/Binary/BinaryPool.cs +++ b/Enyim.Caching/Memcached/Protocol/Binary/BinaryPool.cs @@ -29,7 +29,7 @@ public BinaryPool(IMemcachedClientConfiguration configuration, ILogger logger) protected override IMemcachedNode CreateNode(EndPoint endpoint) { - return new BinaryNode(endpoint, this.configuration.SocketPool, this.authenticationProvider, _logger); + return new BinaryNode(endpoint, this.configuration.SocketPool, this.authenticationProvider, _logger, this.configuration.UseSslStream); } private static ISaslAuthenticationProvider GetProvider(IMemcachedClientConfiguration configuration)