Summary

Class:CBAM.Abstractions.Implementation.NetworkStream.StatelessProtocolConnectionFactory`9
Assembly:CBAM.Abstractions.Implementation.NetworkStream
File(s):/repo-dir/contents/Source/Code/CBAM.Abstractions.Implementation.NetworkStream/ConnectionFactory.Stateless.cs
Covered lines:36
Uncovered lines:5
Coverable lines:41
Total lines:350
Line coverage:87.8%
Branch coverage:40%

Coverage History

Metrics

MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage
.ctor(...)201%1%
IsDedicatedStringPool(...)201%0.5%
ResetFactoryState()400%0%
GetStringPoolForNewConnection()401%0.25%
CreateConnectionFunctionality()800.933%1%

File(s)

/repo-dir/contents/Source/Code/CBAM.Abstractions.Implementation.NetworkStream/ConnectionFactory.Stateless.cs

#LineLine coverage
 1/*
 2 * Copyright 2018 Stanislav Muhametsin. All rights Reserved.
 3 *
 4 * Licensed  under the  Apache License,  Version 2.0  (the "License");
 5 * you may not use  this file  except in  compliance with the License.
 6 * You may obtain a copy of the License at
 7 *
 8 *   http://www.apache.org/licenses/LICENSE-2.0
 9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed  under the  License is distributed on an "AS IS" BASIS,
 12 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 13 * implied.
 14 *
 15 * See the License for the specific language governing permissions and
 16 * limitations under the License.
 17 */
 18using IOUtils.Network.Configuration;
 19using ResourcePooling.Async.Abstractions;
 20using System;
 21using System.Threading;
 22using System.Threading.Tasks;
 23using UtilPack;
 24using System.Net;
 25using System.IO;
 26
 27#if !NETSTANDARD1_0
 28using IOUtils.Network.ResourcePooling;
 29#endif
 30
 31namespace CBAM.Abstractions.Implementation.NetworkStream
 32{
 33
 34   /// <summary>
 35   /// This class extends <see cref="ConnectionFactoryStream{TConnection, TPrivateConnection, TConnectionFunctionality, 
 36   /// Notice that this need for state does not always correlate whether the underlying protocol itself is stateless or 
 37   /// </summary>
 38   /// <typeparam name="TConnection">The public type of <see cref="Connection{TStatement, TStatementInformation, TStatem
 39   /// <typeparam name="TPrivateConnection">The actual type of <see cref="Connection{TStatement, TStatementInformation, 
 40   /// <typeparam name="TConnectionFunctionality">The actual type of <see cref="PooledConnectionFunctionality{TStatement
 41   /// <typeparam name="TConnectionCreationParameters">The type of parameter containing enough information to create an 
 42   /// <typeparam name="TCreationData">The type holding passive data about the remote endpoint and protocol configuratio
 43   /// <typeparam name="TConnectionConfiguration">The type holding passive data about the socket connection.</typeparam>
 44   /// <typeparam name="TInitializationConfiguration">The type holding passive data about protocol initialization.</type
 45   /// <typeparam name="TProtocolConfiguration">The type holding passive data about protocol.</typeparam>
 46   /// <typeparam name="TPoolingConfiguration">The type holding passive data about pooling behaviour.</typeparam>
 47   public abstract class StatelessProtocolConnectionFactory<TConnection, TPrivateConnection, TConnectionFunctionality, T
 48      where TConnection : class
 49      where TPrivateConnection : class, TConnection
 50      where TConnectionFunctionality : class, PooledConnectionFunctionality
 51      where TConnectionCreationParameters : NetworkConnectionCreationInfo<TCreationData, TConnectionConfiguration, TInit
 52      where TCreationData : NetworkConnectionCreationInfoData<TConnectionConfiguration, TInitializationConfiguration, TP
 53      where TConnectionConfiguration : NetworkConnectionConfiguration
 54      where TInitializationConfiguration : NetworkInitializationConfiguration<TProtocolConfiguration, TPoolingConfigurat
 55      where TPoolingConfiguration : NetworkPoolingConfiguration
 56   {
 57
 58      private readonly Boolean _dedicatedStringPoolNeedsToBeConcurrent;
 59
 60      /// <summary>
 61      /// Creates a new instance of <see cref="StatelessProtocolConnectionFactory{TConnection, TPrivateConnection, TConn
 62      /// </summary>
 63      /// <param name="creationInfo">The connection creation parameters.</param>
 64      /// <param name="encodingInfo">The <see cref="IEncodingInfo"/> to use when (de)serializing strings.</param>
 65      /// <param name="isSSLPossible">The callback to check if remote end supports SSL.</param>
 66      /// <param name="noSSLStreamProvider">The callback to create an exception when there was not possible to create SS
 67      /// <param name="remoteNoSSLSupport">The callback to create an exception when the remote does not support SSL. May
 68      /// <param name="sslStreamProviderNoStream">The callback to create an exception when the SSL stream creation callb
 69      /// <param name="sslStreamProviderNoAuthenticationCallback">The callback to create an exception when the SSL strea
 70      /// <param name="sslStreamOtherError">The callback to create an exception when other error occurs during SSL strea
 71      /// <param name="dedicatedStringPoolNeedsToBeConcurrent">A boolean indicating whether per-connection dedicated str
 72      /// <exception cref="ArgumentNullException">If <paramref name="creationInfo"/> or <paramref name="encodingInfo"/> 
 73      public StatelessProtocolConnectionFactory(
 74         TConnectionCreationParameters creationInfo,
 75         IEncodingInfo encodingInfo,
 76         IsSSLPossibleDelegate<TConnectionCreationParameters> isSSLPossible,
 77         Func<Exception> noSSLStreamProvider,
 78         Func<Exception> remoteNoSSLSupport,
 79         Func<Exception> sslStreamProviderNoStream,
 80         Func<Exception> sslStreamProviderNoAuthenticationCallback,
 81         Func<Exception, Exception> sslStreamOtherError,
 82         Boolean dedicatedStringPoolNeedsToBeConcurrent
 283         ) : base( creationInfo )
 84      {
 285         this.Encoding = ArgumentValidator.ValidateNotNull( nameof( encodingInfo ), encodingInfo );
 286         ArgumentValidator.ValidateNotNull( nameof( creationInfo ), creationInfo );
 287         this._dedicatedStringPoolNeedsToBeConcurrent = dedicatedStringPoolNeedsToBeConcurrent;
 88
 289         var encoding = encodingInfo.Encoding;
 90#if NETSTANDARD1_0
 91         if ( !( creationInfo.CreationData?.Initialization?.ConnectionPool?.ConnectionsOwnStringPool ?? default ) )
 92         {
 93            this.GlobalStringPool = BinaryStringPoolFactory.NewConcurrentBinaryStringPool( encoding );
 94         }
 95#else
 296         (this.NetworkStreamConfiguration, this.RemoteAddress, this.GlobalStringPool) = creationInfo.CreateStatefulNetwo
 497            ( socket, stream, token ) => this.GetStringPoolForNewConnection(),
 298            encoding,
 499            state => isSSLPossible?.Invoke( this.CreationParameters, this.Encoding, state, this.IsDedicatedStringPool( s
 2100            noSSLStreamProvider,
 2101            remoteNoSSLSupport,
 2102            sslStreamProviderNoStream,
 2103            sslStreamProviderNoAuthenticationCallback,
 2104            sslStreamOtherError
 2105            );
 4106         this.NetworkStreamConfiguration.TransformStreamAfterCreation = stream => new DuplexBufferedAsyncStream( stream 
 107#endif
 2108      }
 109
 110      /// <summary>
 111      /// Gets the <see cref="IEncodingInfo"/> used to (de)serialize strings.
 112      /// </summary>
 113      /// <value>The <see cref="IEncodingInfo"/> used to (de)serialize strings.</value>
 4114      protected IEncodingInfo Encoding { get; }
 115
 116      /// <summary>
 117      /// Gets the <see cref="BinaryStringPool"/> shared between all connections created by this factory. May be <c>null
 118      /// </summary>
 119      /// <value>the <see cref="BinaryStringPool"/> shared between all connections created by this factory.</value>
 6120      protected BinaryStringPool GlobalStringPool { get; }
 121
 122      /// <summary>
 123      /// Helper method to check whether the <see cref="BinaryStringPool"/> is not <c>null</c> and is dedicated to the c
 124      /// </summary>
 125      /// <param name="stringPool">The <see cref="BinaryStringPool"/> to check.</param>
 126      /// <returns><c>true</c> if given <paramref name="stringPool"/> is not <c>null</c> and is dedicated to the connect
 127      protected Boolean IsDedicatedStringPool( BinaryStringPool stringPool )
 128      {
 4129         return stringPool != null && !ReferenceEquals( this.GlobalStringPool, stringPool );
 130      }
 131
 132#if !NETSTANDARD1_0
 133
 134      /// <summary>
 135      /// Gets the lazily asynchronous <see cref="IPAddress"/> of the remote endpoint. May be <c>null</c> (if e.g. conne
 136      /// </summary>
 137      /// <value>The lazily asynchronous <see cref="IPAddress"/> of the remote endpoint.</value>
 0138      protected ReadOnlyResettableAsyncLazy<IPAddress> RemoteAddress { get; }
 139
 140      /// <summary>
 141      /// Gets the <see cref="NetworkStreamFactoryConfiguration{TState}"/> that is used by the factory when creating new
 142      /// </summary>
 143      /// <value>The <see cref="NetworkStreamFactoryConfiguration{TState}"/> that is used by the factory when creating n
 144      /// <remarks>
 145      /// This configuration is stateful, as the factory needs to hold on possibly connection-specific <see cref="Binary
 146      /// </remarks>
 4147      protected NetworkStreamFactoryConfiguration<BinaryStringPool> NetworkStreamConfiguration { get; }
 148
 149#endif
 150
 151      /// <summary>
 152      /// Implements <see cref="ResourceFactoryInformation.ResetFactoryState"/> and resets this string pool, and remote 
 153      /// </summary>
 154      public override void ResetFactoryState()
 155      {
 0156         this.GlobalStringPool?.ClearPool();
 157#if !NETSTANDARD1_0
 0158         this.RemoteAddress?.Reset();
 159#endif
 0160      }
 161
 162      private BinaryStringPool GetStringPoolForNewConnection()
 163      {
 2164         return this.GlobalStringPool ?? ( this._dedicatedStringPoolNeedsToBeConcurrent ? BinaryStringPoolFactory.NewNot
 165      }
 166
 167      /// <summary>
 168      /// Implements <see cref="DefaultConnectionFactory{TConnection, TPrivateConnection, TConnectionFunctionality, TCon
 169      /// </summary>
 170      /// <param name="token">The cancellation token to use during initialization process.</param>
 171      /// <returns>Potentially asynchronously returns an instance of <typeparamref name="TConnectionFunctionality"/>.</r
 172      protected override async ValueTask<TConnectionFunctionality> CreateConnectionFunctionality( CancellationToken toke
 173      {
 174
 2175         var parameters = this.CreationParameters;
 2176         var streamFactory = parameters.StreamFactory;
 177
 178#if NETSTANDARD1_0
 179         Object
 180#else
 181         System.Net.Sockets.Socket
 182#endif
 183             socket;
 184         Stream stream;
 185         BinaryStringPool stringPool;
 2186         if ( streamFactory == null )
 187         {
 188#if NETSTANDARD1_0
 189            throw new ArgumentNullException( nameof( streamFactory ) );
 190#else
 2191            (socket, stream, stringPool) = await NetworkStreamFactory<BinaryStringPool>.AcquireNetworkStreamFromConfigur
 2192                  this.NetworkStreamConfiguration,
 2193                  token );
 194#endif
 2195         }
 196         else
 197         {
 0198            (socket, stream, stringPool) = (null, await streamFactory(), this.GetStringPoolForNewConnection());
 199         }
 200
 2201         return await this.CreateFunctionality(
 2202            stringPool,
 2203            stream,
 2204            socket,
 2205            token
 2206            );
 2207      }
 208
 209      /// <summary>
 210      /// Derived classes should implement this in order to create <typeparamref name="TConnectionFunctionality"/> from 
 211      /// </summary>
 212      /// <param name="stringPool">The possibly dedicated string pool to use for the connection being created.</param>
 213      /// <param name="acquiredStream">The <see cref="Stream"/> that has been acquired and is ready to use.</param>
 214      /// <param name="socketOrNull">A socket that has been acquired, or <c>null</c> if <see cref="NetworkConnectionCrea
 215      /// <param name="token">The <see cref="CancellationToken"/> to use.</param>
 216      /// <returns>Potentially asynchronously creates an instance of <typeparamref name="TConnectionFunctionality"/>.</r
 217      protected abstract ValueTask<TConnectionFunctionality> CreateFunctionality(
 218         BinaryStringPool stringPool,
 219         Stream acquiredStream,
 220         Object socketOrNull,
 221         CancellationToken token
 222         );
 223   }
 224
 225   /// <summary>
 226   /// This class extends and implements all missing functionality from <see cref="StatelessProtocolConnectionFactory{TC
 227   /// </summary>
 228   /// <typeparam name="TConnection">The public type of <see cref="Connection{TStatement, TStatementInformation, TStatem
 229   /// <typeparam name="TPrivateConnection">The actual type of <see cref="Connection{TStatement, TStatementInformation, 
 230   /// <typeparam name="TConnectionFunctionality">The actual type of <see cref="PooledConnectionFunctionality{TStatement
 231   /// <typeparam name="TConnectionCreationParameters">The type of parameter containing enough information to create an 
 232   /// <typeparam name="TCreationData">The type holding passive data about the remote endpoint and protocol configuratio
 233   /// <typeparam name="TConnectionConfiguration">The type holding passive data about the socket connection.</typeparam>
 234   /// <typeparam name="TInitializationConfiguration">The type holding passive data about protocol initialization.</type
 235   /// <typeparam name="TProtocolConfiguration">The type holding passive data about protocol.</typeparam>
 236   /// <typeparam name="TPoolingConfiguration">The type holding passive data about pooling behaviour.</typeparam>
 237   public sealed class DelegatingStatelessProtocolConnectionFactory<TConnection, TPrivateConnection, TConnectionFunction
 238     where TConnection : class
 239     where TPrivateConnection : class, TConnection
 240     where TConnectionFunctionality : class, PooledConnectionFunctionality
 241     where TConnectionCreationParameters : NetworkConnectionCreationInfo<TCreationData, TConnectionConfiguration, TIniti
 242     where TCreationData : NetworkConnectionCreationInfoData<TConnectionConfiguration, TInitializationConfiguration, TPr
 243     where TConnectionConfiguration : NetworkConnectionConfiguration
 244     where TInitializationConfiguration : NetworkInitializationConfiguration<TProtocolConfiguration, TPoolingConfigurati
 245     where TPoolingConfiguration : NetworkPoolingConfiguration
 246   {
 247      private readonly CreatePrivateConnectionDelegate<TConnectionFunctionality, TPrivateConnection> _createConnection;
 248      private readonly CreateConnectionAcquireInfo<TConnectionFunctionality, TPrivateConnection> _createConnectionAcquir
 249      private readonly CreateConnectionFunctionality<TConnectionFunctionality, TConnectionCreationParameters> _createFun
 250      private readonly ExtractStreamOnConnectionAcquirementErrorDelegate<TConnectionFunctionality, TPrivateConnection> _
 251
 252      /// <summary>
 253      /// Creates a new instance of <see cref="DelegatingStatelessProtocolConnectionFactory{TConnection, TPrivateConnect
 254      /// </summary>
 255      /// <param name="creationInfo">The connection creation parameters.</param>
 256      /// <param name="encodingInfo">The <see cref="IEncodingInfo"/> to use when (de)serializing strings.</param>
 257      /// <param name="isSSLPossible">The callback to check if remote end supports SSL.</param>
 258      /// <param name="noSSLStreamProvider">The callback to create an exception when there was not possible to create SS
 259      /// <param name="remoteNoSSLSupport">The callback to create an exception when the remote does not support SSL. May
 260      /// <param name="sslStreamProviderNoStream">The callback to create an exception when the SSL stream creation callb
 261      /// <param name="sslStreamProviderNoAuthenticationCallback">The callback to create an exception when the SSL strea
 262      /// <param name="sslStreamOtherError">The callback to create an exception when other error occurs during SSL strea
 263      /// <param name="dedicatedStringPoolNeedsToBeConcurrent">A boolean indicating whether per-connection dedicated str
 264      /// <param name="createFunctionality">The callback for <see cref="StatelessProtocolConnectionFactory{TConnection, 
 265      /// <param name="createConnection">The callback for <see cref="DefaultConnectionFactory{TConnection, TPrivateConne
 266      /// <param name="createConnectionAcquireInfo">The callback for <see cref="DefaultConnectionFactory{TConnection, TP
 267      /// <param name="extractStreamOnConnectionAcquirementError">The callback for <see cref="ConnectionFactoryStream{TC
 268      /// <exception cref="ArgumentNullException">If any of the <paramref name="creationInfo"/>, <paramref name="encodin
 269      public DelegatingStatelessProtocolConnectionFactory(
 270         TConnectionCreationParameters creationInfo,
 271         IEncodingInfo encodingInfo,
 272         IsSSLPossibleDelegate<TConnectionCreationParameters> isSSLPossible,
 273         Func<Exception> noSSLStreamProvider,
 274         Func<Exception> remoteNoSSLSupport,
 275         Func<Exception> sslStreamProviderNoStream,
 276         Func<Exception> sslStreamProviderNoAuthenticationCallback,
 277         Func<Exception, Exception> sslStreamOtherError,
 278         Boolean dedicatedStringPoolNeedsToBeConcurrent,
 279         CreateConnectionFunctionality<TConnectionFunctionality, TConnectionCreationParameters> createFunctionality,
 280         CreatePrivateConnectionDelegate<TConnectionFunctionality, TPrivateConnection> createConnection,
 281         CreateConnectionAcquireInfo<TConnectionFunctionality, TPrivateConnection> createConnectionAcquireInfo,
 282         ExtractStreamOnConnectionAcquirementErrorDelegate<TConnectionFunctionality, TPrivateConnection> extractStreamOn
 283         ) : base( creationInfo, encodingInfo, isSSLPossible, noSSLStreamProvider, remoteNoSSLSupport, sslStreamProvider
 284      {
 285         this._createConnection = ArgumentValidator.ValidateNotNull( nameof( createConnection ), createConnection );
 286         this._createConnectionAcquireInfo = ArgumentValidator.ValidateNotNull( nameof( createConnectionAcquireInfo ), c
 287         this._createFunctionality = ArgumentValidator.ValidateNotNull( nameof( createFunctionality ), createFunctionali
 288         this._extractStreamOnConnectionAcquirementError = ArgumentValidator.ValidateNotNull( nameof( extractStreamOnCon
 289      }
 290
 291      /// <inheritdoc />
 292      protected override ValueTask<TPrivateConnection> CreateConnection( TConnectionFunctionality functionality )
 293         => this._createConnection( functionality );
 294
 295      /// <inheritdoc />
 296      protected override AsyncResourceAcquireInfo<TPrivateConnection> CreateConnectionAcquireInfo( TConnectionFunctional
 297         => this._createConnectionAcquireInfo( functionality, connection );
 298
 299      /// <inheritdoc />
 300      protected override ValueTask<TConnectionFunctionality> CreateFunctionality( BinaryStringPool stringPool, Stream ac
 301         => this._createFunctionality( this.CreationParameters, this.Encoding, stringPool, this.IsDedicatedStringPool( s
 302
 303      /// <inheritdoc />
 304      protected override IDisposable ExtractStreamOnConnectionAcquirementError( TConnectionFunctionality functionality, 
 305         => this._extractStreamOnConnectionAcquirementError( functionality, connection, token, error );
 306   }
 307
 308   /// <summary>
 309   /// This delegate is used to create close-to-protocol connection functionality object.
 310   /// </summary>
 311   /// <typeparam name="TConnectionFunctionality">The type of close-to-protocol connection functionality.</typeparam>
 312   /// <typeparam name="TConnectionCreationParameters">The type of connection cration parameters.</typeparam>
 313   /// <param name="creationParameters">The connection creation parameters.</param>
 314   /// <param name="encoding">The <see cref="IEncodingInfo"/> used to (de)serialize strings.</param>
 315   /// <param name="stringPool">The <see cref="BinaryStringPool"/> to use when deserializing strings.</param>
 316   /// <param name="stringPoolIsDedicated"><c>true</c> if the <paramref name="stringPool"/> is dedicated to this connect
 317   /// <param name="stream">The <see cref="Stream"/> to the remote endpoint.</param>
 318   /// <param name="socketOrNull">The socket to the remote endpoint, or <c>null</c>, if stream was created via <see cref
 319   /// <param name="token">The <see cref="CancellationToken"/> to use.</param>
 320   /// <returns>Potentially asynchronously creates the close-to-protocol connection functionality object.</returns>
 321   public delegate ValueTask<TConnectionFunctionality> CreateConnectionFunctionality<TConnectionFunctionality, TConnecti
 322      TConnectionCreationParameters creationParameters,
 323      IEncodingInfo encoding,
 324      BinaryStringPool stringPool,
 325      Boolean stringPoolIsDedicated,
 326      Stream stream,
 327      Object socketOrNull,
 328      CancellationToken token
 329      );
 330
 331   /// <summary>
 332   /// This delegate is used to check whether remote end supports SSL, when there is no intermediate state used during c
 333   /// </summary>
 334   /// <typeparam name="TConnectionCreationParameters">The type of connection creation parameters.</typeparam>
 335   /// <param name="creationParameters">The connection creation parameters.</param>
 336   /// <param name="encoding">The <see cref="IEncodingInfo"/> used to (de)serialize strings.</param>
 337   /// <param name="stringPool">The <see cref="BinaryStringPool"/> to use when deserializing strings.</param>
 338   /// <param name="stringPoolIsDedicated"><c>true</c> if the <paramref name="stringPool"/> is dedicated to this connect
 339   /// <returns>Asynchronously checks whether remote endpoint supports using SSL.</returns>
 340   /// <seealso cref="TaskUtils.False"/>
 341   /// <seealso cref="TaskUtils.True"/>
 342   public delegate Task<Boolean> IsSSLPossibleDelegate<TConnectionCreationParameters>(
 343      TConnectionCreationParameters creationParameters,
 344      IEncodingInfo encoding,
 345      BinaryStringPool stringPool,
 346      Boolean stringPoolIsDedicated
 347      );
 348
 349
 350}