Summary

Class:CBAM.Abstractions.Implementation.NetworkStream.DelegatingStatelessProtocolConnectionFactory`9
Assembly:CBAM.Abstractions.Implementation.NetworkStream
File(s):/repo-dir/contents/Source/Code/CBAM.Abstractions.Implementation.NetworkStream/ConnectionFactory.Stateless.cs
Covered lines:9
Uncovered lines:1
Coverable lines:10
Total lines:350
Line coverage:90%

Coverage History

Metrics

MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage
.ctor(...)101%0%
CreateConnection(...)101%0%
CreateConnectionAcquireInfo(...)101%0%
CreateFunctionality(...)101%0%
ExtractStreamOnConnectionAcquirementError(...)100%0%

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
 83         ) : base( creationInfo )
 84      {
 85         this.Encoding = ArgumentValidator.ValidateNotNull( nameof( encodingInfo ), encodingInfo );
 86         ArgumentValidator.ValidateNotNull( nameof( creationInfo ), creationInfo );
 87         this._dedicatedStringPoolNeedsToBeConcurrent = dedicatedStringPoolNeedsToBeConcurrent;
 88
 89         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
 96         (this.NetworkStreamConfiguration, this.RemoteAddress, this.GlobalStringPool) = creationInfo.CreateStatefulNetwo
 97            ( socket, stream, token ) => this.GetStringPoolForNewConnection(),
 98            encoding,
 99            state => isSSLPossible?.Invoke( this.CreationParameters, this.Encoding, state, this.IsDedicatedStringPool( s
 100            noSSLStreamProvider,
 101            remoteNoSSLSupport,
 102            sslStreamProviderNoStream,
 103            sslStreamProviderNoAuthenticationCallback,
 104            sslStreamOtherError
 105            );
 106         this.NetworkStreamConfiguration.TransformStreamAfterCreation = stream => new DuplexBufferedAsyncStream( stream 
 107#endif
 108      }
 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>
 114      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>
 120      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      {
 129         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>
 138      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>
 147      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      {
 156         this.GlobalStringPool?.ClearPool();
 157#if !NETSTANDARD1_0
 158         this.RemoteAddress?.Reset();
 159#endif
 160      }
 161
 162      private BinaryStringPool GetStringPoolForNewConnection()
 163      {
 164         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
 175         var parameters = this.CreationParameters;
 176         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;
 186         if ( streamFactory == null )
 187         {
 188#if NETSTANDARD1_0
 189            throw new ArgumentNullException( nameof( streamFactory ) );
 190#else
 191            (socket, stream, stringPool) = await NetworkStreamFactory<BinaryStringPool>.AcquireNetworkStreamFromConfigur
 192                  this.NetworkStreamConfiguration,
 193                  token );
 194#endif
 195         }
 196         else
 197         {
 198            (socket, stream, stringPool) = (null, await streamFactory(), this.GetStringPoolForNewConnection());
 199         }
 200
 201         return await this.CreateFunctionality(
 202            stringPool,
 203            stream,
 204            socket,
 205            token
 206            );
 207      }
 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
 2283         ) : base( creationInfo, encodingInfo, isSSLPossible, noSSLStreamProvider, remoteNoSSLSupport, sslStreamProvider
 284      {
 2285         this._createConnection = ArgumentValidator.ValidateNotNull( nameof( createConnection ), createConnection );
 2286         this._createConnectionAcquireInfo = ArgumentValidator.ValidateNotNull( nameof( createConnectionAcquireInfo ), c
 2287         this._createFunctionality = ArgumentValidator.ValidateNotNull( nameof( createFunctionality ), createFunctionali
 2288         this._extractStreamOnConnectionAcquirementError = ArgumentValidator.ValidateNotNull( nameof( extractStreamOnCon
 2289      }
 290
 291      /// <inheritdoc />
 292      protected override ValueTask<TPrivateConnection> CreateConnection( TConnectionFunctionality functionality )
 2293         => this._createConnection( functionality );
 294
 295      /// <inheritdoc />
 296      protected override AsyncResourceAcquireInfo<TPrivateConnection> CreateConnectionAcquireInfo( TConnectionFunctional
 2297         => this._createConnectionAcquireInfo( functionality, connection );
 298
 299      /// <inheritdoc />
 300      protected override ValueTask<TConnectionFunctionality> CreateFunctionality( BinaryStringPool stringPool, Stream ac
 2301         => this._createFunctionality( this.CreationParameters, this.Encoding, stringPool, this.IsDedicatedStringPool( s
 302
 303      /// <inheritdoc />
 304      protected override IDisposable ExtractStreamOnConnectionAcquirementError( TConnectionFunctionality functionality, 
 0305         => 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}