< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.ServiceModel.Channels.RabbitMqOutputChannel
Assembly: CoreWCF.RabbitMQ.Client
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.RabbitMQ.Client/src/CoreWCF/ServiceModel/Channels/RabbitMqOutputChannel.cs
Line coverage
66%
Covered lines: 72
Uncovered lines: 36
Coverable lines: 108
Total lines: 286
Line coverage: 66.6%
Branch coverage
50%
Covered branches: 13
Total branches: 26
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
GetProperty()30%101030.76%
OnOpen(...)100%11100%
OnBeginOpen(...)100%110%
OnEndOpen(...)100%110%
OnAbort()100%110%
OnClose(...)100%110%
OnBeginClose(...)100%110%
OnEndClose(...)100%110%
Send(...)100%110%
Send(...)100%11100%
BeginSend(...)100%110%
BeginSend(...)100%110%
EndSend(...)100%110%
EncodeMessage(...)100%11100%
OnOpenAsync()100%22100%
GetPropertyValueFromReflection(...)50%44100%
SendAsync(...)100%110%
SendAsync(...)75%4470.58%
OnCloseAsync()100%110%
GetSecurityTokenAsync()50%22100%
GetSecurityTokenProvider()50%4488.23%
GetConnectionSettings(...)100%11100%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.RabbitMQ.Client/src/CoreWCF/ServiceModel/Channels/RabbitMqOutputChannel.cs

#LineLine coverage
 1using System;
 2using System.IdentityModel.Selectors;
 3using System.IdentityModel.Tokens;
 4using System.ServiceModel;
 5using System.ServiceModel.Channels;
 6using System.ServiceModel.Security.Tokens;
 7using System.Threading.Tasks;
 8using CoreWCF.Runtime;
 9using RabbitMQ.Client;
 10using RabbitMQ.Client.Exceptions;
 11
 12namespace CoreWCF.ServiceModel.Channels
 13{
 14    public class RabbitMqOutputChannel : ChannelBase, IOutputChannel
 15    {
 16        private readonly RabbitMqChannelFactory _parent;
 17        private readonly RabbitMqTransportBindingElement _transport;
 18        private readonly SecurityTokenManager _securityTokenManager;
 19        private readonly EndpointAddress _baseAddress;
 20        private readonly Uri _via;
 21        private readonly MessageEncoder _encoder;
 22        private RabbitMqConnectionSettings _connectionSettings;
 23        private IModel _rabbitMqClient;
 24
 25        internal RabbitMqOutputChannel(
 26            RabbitMqChannelFactory factory,
 27            EndpointAddress endpointAddress,
 28            Uri via,
 29            RabbitMqTransportBindingElement transport,
 30            SecurityTokenManager securityTokenManager,
 31            MessageEncoder encoder)
 332            : base(factory)
 33        {
 334            _parent = factory;
 335            _transport = transport;
 336            _securityTokenManager = securityTokenManager;
 337            _encoder = encoder;
 38
 339            _via = via;
 340            _baseAddress = endpointAddress;
 341        }
 42
 043        EndpointAddress IOutputChannel.RemoteAddress => _baseAddress;
 44
 045        Uri IOutputChannel.Via => _via;
 46
 347        protected ChannelParameterCollection ChannelParameters { get; private set; }
 48
 49        public override T GetProperty<T>()
 50        {
 351            if (typeof(T) == typeof(IOutputChannel))
 52            {
 053                return (T)(object)this;
 54            }
 55
 356            if (typeof(T) == typeof(ChannelParameterCollection))
 57            {
 358                if (State == CommunicationState.Created)
 59                {
 060                    lock (ThisLock)
 61                    {
 062                        if (ChannelParameters == null)
 63                        {
 064                            ChannelParameters = new ChannelParameterCollection();
 65                        }
 066                    }
 67                }
 368                return (T)(object)ChannelParameters;
 69            }
 70
 071            T messageEncoderProperty = _encoder.GetProperty<T>();
 072            if (messageEncoderProperty != null)
 73            {
 074                return messageEncoderProperty;
 75            }
 76
 077            return base.GetProperty<T>();
 78        }
 79
 80        /// <summary>
 81        /// Open the channel for use. We do not have any blocking work to perform so this is a no-op
 82        /// </summary>
 83        protected override void OnOpen(TimeSpan timeout)
 84        {
 385            OnOpenAsync(timeout).GetAwaiter().GetResult();
 386        }
 87
 88        protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
 89        {
 090            return OnOpenAsync(timeout).ToApm(callback, state);
 91        }
 92
 93        protected override void OnEndOpen(IAsyncResult result)
 94        {
 095            result.ToApmEnd();
 096        }
 97
 98        protected override void OnAbort()
 099        { }
 100
 101        protected override void OnClose(TimeSpan timeout)
 102        {
 0103            OnCloseAsync().GetAwaiter().GetResult();
 0104        }
 105
 106        protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
 107        {
 0108            return OnCloseAsync().ToApm(callback, state);
 109        }
 110
 111        protected override void OnEndClose(IAsyncResult result)
 112        {
 0113            result.ToApmEnd();
 0114        }
 115
 116        public void Send(Message message)
 117        {
 0118            SendAsync(message).GetAwaiter().GetResult();
 0119        }
 120
 121        public void Send(Message message, TimeSpan timeout)
 122        {
 3123            SendAsync(message, timeout).GetAwaiter().GetResult();
 3124        }
 125
 126        public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state)
 127        {
 0128            return SendAsync(message).ToApm(callback, state);
 129        }
 130
 131        public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)
 132        {
 0133            return SendAsync(message, timeout).ToApm(callback, state);
 134        }
 135
 136        public void EndSend(IAsyncResult result)
 137        {
 0138            result.ToApmEnd();
 0139        }
 140
 141        /// <summary>
 142        /// Address the Message and serialize it into a byte array.
 143        /// </summary>
 144        private ArraySegment<byte> EncodeMessage(Message message)
 145        {
 146            try
 147            {
 3148                _baseAddress.ApplyTo(message);
 3149                return _encoder.WriteMessage(
 3150                    message,
 3151                    (int)_parent.Transport.MaxReceivedMessageSize,
 3152                    _parent.BufferManager);
 153            }
 154            finally
 155            {
 156                // We have consumed the message by serializing it, so clean up
 3157                message.Close();
 3158            }
 3159        }
 160
 161        private async Task OnOpenAsync(TimeSpan timeout)
 162        {
 3163            string userName = ConnectionFactory.DefaultUser;
 3164            string password = ConnectionFactory.DefaultPass;
 165
 3166            var token = await GetSecurityTokenAsync(timeout);
 3167            if (token != null)
 168            {
 169                // When UserNameSecurityToken is made public, cast token to UserNameSecurityToken
 170                // and read UserName and Password properties directly instead of using reflection.
 3171                userName = GetPropertyValueFromReflection(token, "UserName");
 3172                password = GetPropertyValueFromReflection(token, "Password");
 173            }
 174
 3175            _connectionSettings = GetConnectionSettings(userName, password);
 3176            var factory = _connectionSettings.GetConnectionFactory();
 3177            var connection = factory.CreateConnection();
 3178            _rabbitMqClient = connection.CreateModel();
 3179            _rabbitMqClient.ConfirmSelect();
 3180        }
 181
 182        private string GetPropertyValueFromReflection(SecurityToken token, string propertyName)
 183        {
 6184            var tokenType = token.GetType();
 6185            var propertyValue = tokenType.GetProperty(propertyName)?.GetValue(token, null);
 6186            return propertyValue as string ?? string.Empty;
 187        }
 188
 189        /// <summary>
 190        /// Publish a message to RabbitMQ
 191        /// </summary>
 192        /// <exception cref="TimeoutException"></exception>
 193        private Task SendAsync(Message message)
 194        {
 0195            return SendAsync(message, System.Threading.Timeout.InfiniteTimeSpan);
 196        }
 197
 198        /// <summary>
 199        /// Published a Message to RabbitMQ and waits for a Publisher confirm.
 200        /// If timeout is less than or equal to zero, RabbitMQ will client will not
 201        /// wait for a publish confirmation from the RabbitMQ broker.
 202        /// Note: Waiting for the publish confirmation could impact performance.
 203        /// </summary>
 204        /// <exception cref="TimeoutException"></exception>
 205        private Task SendAsync(Message message, TimeSpan timeout)
 206        {
 3207            var messageBuffer = EncodeMessage(message);
 208
 209            try
 210            {
 3211                if (!_rabbitMqClient.IsOpen)
 212                {
 0213                    _rabbitMqClient.Abort();
 0214                    Abort();
 0215                    throw new CommunicationException(SR.RabbitMqClientNotOpen);
 216                }
 217
 3218                _rabbitMqClient.BasicPublish(
 3219                    exchange: _connectionSettings.Exchange,
 3220                    routingKey: _connectionSettings.RoutingKey,
 3221                    body: messageBuffer);
 3222                if (timeout > TimeSpan.Zero)
 223                {
 3224                    _rabbitMqClient.WaitForConfirmsOrDie(timeout);
 225                }
 3226            }
 0227            catch (OperationInterruptedException e)
 228            {
 0229                throw new TimeoutException(SR.Format(SR.RabbitMqWaitTimeExceeded, timeout.Milliseconds));
 230            }
 231            finally
 232            {
 233                // Make sure buffers are always returned to the BufferManager
 3234                _parent.BufferManager.ReturnBuffer(messageBuffer.Array);
 3235            }
 236
 3237            return Task.CompletedTask;
 238        }
 239
 240        private Task OnCloseAsync()
 241        {
 0242            _rabbitMqClient.Close();
 0243            return Task.CompletedTask;
 244        }
 245
 246        private async Task<SecurityToken> GetSecurityTokenAsync(TimeSpan timeout)
 247        {
 3248            var securityTokenProvider = GetSecurityTokenProvider();
 3249            var token = await securityTokenProvider?.GetTokenAsync(timeout);
 3250            return token;
 3251        }
 252
 253        private SecurityTokenProvider GetSecurityTokenProvider()
 254        {
 3255            if (_securityTokenManager == null)
 256            {
 0257                throw new Exception(SR.SecurityTokenManagerIsNull);
 258            }
 259
 3260            var usernameRequirement = new InitiatorServiceModelSecurityTokenRequirement
 3261            {
 3262                // Replace hardcoded value for TokenType when changes to make enum SecurityTokenTypes public are publish
 3263                // https://github.com/dotnet/wcf/blob/main/src/System.ServiceModel.Primitives/src/System/IdentityModel/T
 3264                TokenType = "http://schemas.microsoft.com/ws/2006/05/identitymodel/tokens/UserName",
 3265                RequireCryptographicToken = false,
 3266                TargetAddress = _baseAddress,
 3267                Via = _via,
 3268                TransportScheme = _transport.Scheme
 3269            };
 270
 3271            var channelParameters = GetProperty<ChannelParameterCollection>();
 3272            if (channelParameters != null)
 273            {
 0274                usernameRequirement.Properties[ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty]
 275            }
 3276            var securityTokenProvider = _securityTokenManager.CreateSecurityTokenProvider(usernameRequirement);
 277
 3278            return securityTokenProvider;
 279        }
 280
 281        private RabbitMqConnectionSettings GetConnectionSettings(string userName, string password)
 282        {
 3283            return RabbitMqConnectionSettings.FromUri(_via, userName, password, _transport.SslOption, _transport.Virtual
 284        }
 285    }
 286}