< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Dispatcher.DuplexChannelBinder
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DuplexChannelBinder.cs
Line coverage
62%
Covered lines: 107
Uncovered lines: 65
Coverable lines: 172
Total lines: 512
Line coverage: 62.2%
Branch coverage
46%
Covered branches: 35
Total branches: 76
Branch coverage: 46%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor()100%11100%
Init(...)100%11100%
Init(...)50%2287.5%
OnFaulted(...)100%11100%
Abort()100%11100%
CloseAfterFault(...)100%110%
AbortRequests()42.85%141456.25%
GetReceiveTimeoutException(...)0%440%
HandleRequestAsReply(...)100%2275%
HandleRequestAsReplyCore(...)50%2280%
CreateRequestContext(...)100%11100%
SendAsync(...)100%110%
RequestAsync()100%11100%
RequestStarting(...)100%44100%
RequestCompleting(...)16.66%6650%
AddToTimedOutRequestList(...)100%110%
RemoveFromTimedOutRequestList(...)0%220%
DeleteTimedoutRequestsFromCorrelator()37.5%161633.33%
EnsureIncomingIdentity(...)100%110%
ThrowIfInvalidReplyIdentity(...)50%6633.33%
SetNextDispatcher(...)100%11100%
DispatchAsync(...)100%110%
DispatchAsync(...)100%44100%
.ctor(...)100%11100%
OnAbort()100%110%
OnCloseAsync(...)100%11100%
OnReplyAsync(...)100%22100%
.ctor(...)100%11100%
Abort()100%110%
WaitForReplyAsync()50%2285.71%
GotReply(...)100%11100%
CloseWaitHandle()100%22100%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Dispatcher/DuplexChannelBinder.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Runtime.CompilerServices;
 7using System.Threading;
 8using System.Threading.Tasks;
 9using System.Xml;
 10using CoreWCF.Channels;
 11using CoreWCF.Configuration;
 12using CoreWCF.Runtime;
 13using CoreWCF.Security;
 14
 15namespace CoreWCF.Dispatcher
 16{
 17    internal class DuplexChannelBinder : IChannelBinder
 18    {
 19        private IDuplexChannel _channel;
 20        private IRequestReplyCorrelator _correlator;
 21        private IdentityVerifier _identityVerifier;
 22        private int _pending;
 23        private List<IDuplexRequest> _requests;
 24        private List<ICorrelatorKey> _timedOutRequests;
 25        private ChannelHandler _channelHandler;
 26        private bool _requestAborted;
 27        private bool _initialized = false;
 28        private IDefaultCommunicationTimeouts _timeouts;
 29        private IServiceChannelDispatcher _next;
 30
 15031        public DuplexChannelBinder() { }
 32
 33        internal void Init(IDuplexSessionChannel channel, IRequestReplyCorrelator correlator, Uri listenUri)
 34        {
 7535            Init((IDuplexChannel)channel, correlator, listenUri);
 7536            HasSession = true;
 7537        }
 38
 39        internal void Init(IDuplexChannel channel, IRequestReplyCorrelator correlator, Uri listenUri)
 40        {
 7541            if (_initialized)
 42            {
 43                Fx.Assert(_channel == channel, "Wrong channel when calling Init");
 44                Fx.Assert(_correlator == correlator, "Wrong channel when calling Init");
 45                Fx.Assert(ListenUri == listenUri, "Wrong listenUri when calling Init");
 046                return;
 47            }
 48
 49            Fx.Assert(channel != null, "caller must verify");
 50            Fx.Assert(correlator != null, "caller must verify");
 51
 7552            _channel = channel;
 7553            ListenUri = listenUri;
 7554            _correlator = correlator;
 7555            _channel.Faulted += new EventHandler(OnFaulted);
 7556            _initialized = true;
 7557        }
 58
 8959        public TimeSpan DefaultSendTimeout => _timeouts.SendTimeout;
 8960        public TimeSpan DefaultCloseTimeout => _timeouts.CloseTimeout;
 61
 62        public IChannel Channel
 63        {
 60464            get { return _channel; }
 65        }
 66
 67        internal ChannelHandler ChannelHandler
 68        {
 69            get
 70            {
 071                if (!(_channelHandler != null))
 72                {
 73                    Fx.Assert("DuplexChannelBinder.ChannelHandler: (channelHandler != null)");
 74                }
 075                return _channelHandler;
 76            }
 77            set
 78            {
 079                if (!(_channelHandler == null))
 80                {
 81                    Fx.Assert("DuplexChannelBinder.ChannelHandler: (channelHandler == null)");
 82                }
 083                _channelHandler = value;
 084            }
 85        }
 86
 36887        public bool HasSession { get; private set; }
 88
 89        internal IdentityVerifier IdentityVerifier
 90        {
 91            get
 92            {
 093                if (_identityVerifier == null)
 94                {
 095                    _identityVerifier = IdentityVerifier.CreateDefault();
 96                }
 97
 098                return _identityVerifier;
 99            }
 100            set
 101            {
 0102                _identityVerifier = value ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(val
 0103            }
 104        }
 105
 75106        public Uri ListenUri { get; private set; }
 107
 108        public EndpointAddress LocalAddress
 109        {
 0110            get { return _channel.LocalAddress; }
 111        }
 112
 113        public EndpointAddress RemoteAddress
 114        {
 0115            get { return _channel.RemoteAddress; }
 116        }
 117
 118        private List<IDuplexRequest> Requests
 119        {
 120            get
 121            {
 1122                lock (ThisLock)
 123                {
 1124                    if (_requests == null)
 125                    {
 1126                        _requests = new List<IDuplexRequest>();
 127                    }
 128
 1129                    return _requests;
 130                }
 1131            }
 132        }
 133
 134        private List<ICorrelatorKey> TimedOutRequests
 135        {
 136            get
 137            {
 0138                lock (ThisLock)
 139                {
 0140                    if (_timedOutRequests == null)
 141                    {
 0142                        _timedOutRequests = new List<ICorrelatorKey>();
 143                    }
 0144                    return _timedOutRequests;
 145                }
 0146            }
 147        }
 148
 149        private object ThisLock
 150        {
 74151            get { return this; }
 152        }
 153
 154        private void OnFaulted(object sender, EventArgs e)
 155        {
 156            //Some unhandled exception happened on the channel.
 157            //So close all pending requests so the callbacks (in case of async)
 158            //on the requests are called.
 1159            AbortRequests();
 1160        }
 161
 162        public void Abort()
 163        {
 1164            _channel.Abort();
 1165            AbortRequests();
 1166        }
 167
 168        public void CloseAfterFault(TimeSpan timeout)
 169        {
 0170            var helper = new TimeoutHelper(timeout);
 0171            _channel.CloseAsync(helper.GetCancellationToken());
 0172            AbortRequests();
 0173        }
 174
 175        private void AbortRequests()
 176        {
 71177            IDuplexRequest[] array = null;
 71178            lock (ThisLock)
 179            {
 71180                if (_requests != null)
 181                {
 0182                    array = _requests.ToArray();
 183
 0184                    foreach (IDuplexRequest request in array)
 185                    {
 0186                        request.Abort();
 187                    }
 188                }
 71189                _requests = null;
 71190                _requestAborted = true;
 71191            }
 192
 193            // Remove requests from the correlator since the channel might be either faulting or aborting,
 194            // We are not going to get a reply for these requests. If they are not removed from the correlator, this wil
 195            // This operation does not have to be under the lock
 71196            if (array != null && array.Length > 0)
 197            {
 0198                if (_correlator is RequestReplyCorrelator requestReplyCorrelator)
 199                {
 0200                    foreach (IDuplexRequest request in array)
 201                    {
 0202                        if (request is ICorrelatorKey keyedRequest)
 203                        {
 0204                            requestReplyCorrelator.RemoveRequest(keyedRequest);
 205                        }
 206                    }
 207                }
 208            }
 209
 210            //if there are any timed out requests, delete it from the correlator table
 71211            DeleteTimedoutRequestsFromCorrelator();
 71212        }
 213
 214        private TimeoutException GetReceiveTimeoutException(TimeSpan timeout)
 215        {
 0216            EndpointAddress address = _channel.RemoteAddress ?? _channel.LocalAddress;
 0217            if (address != null)
 218            {
 0219                return new TimeoutException(SR.Format(SR.SFxRequestTimedOut2, address, timeout));
 220            }
 221            else
 222            {
 0223                return new TimeoutException(SR.Format(SR.SFxRequestTimedOut1, timeout));
 224            }
 225        }
 226
 227        internal bool HandleRequestAsReply(Message message)
 228        {
 89229            UniqueId relatesTo = null;
 230            try
 231            {
 89232                relatesTo = message.Headers.RelatesTo;
 89233            }
 0234            catch (MessageHeaderException)
 235            {
 236                // ignore it
 0237            }
 89238            if (relatesTo == null)
 239            {
 88240                return false;
 241            }
 242            else
 243            {
 1244                return HandleRequestAsReplyCore(message);
 245            }
 246        }
 247
 248        private bool HandleRequestAsReplyCore(Message message)
 249        {
 1250            IDuplexRequest request = _correlator.Find<IDuplexRequest>(message, true);
 1251            if (request != null)
 252            {
 1253                request.GotReply(message);
 1254                return true;
 255            }
 256
 0257            return false;
 258        }
 259
 260        public RequestContext CreateRequestContext(Message message)
 261        {
 89262            return new DuplexRequestContext(_channel, message, this);
 263        }
 264
 265        public Task SendAsync(Message message, CancellationToken token)
 266        {
 0267            return _channel.SendAsync(message, token);
 268        }
 269
 270        public async Task<Message> RequestAsync(Message message, CancellationToken token)
 271        {
 1272            RequestReplyCorrelator.PrepareRequest(message);
 1273            AsyncDuplexRequest duplexRequest = new AsyncDuplexRequest(this);
 274
 1275            lock (ThisLock)
 276            {
 1277                RequestStarting(message, duplexRequest);
 1278            }
 279
 1280            await _channel.SendAsync(message, token);
 1281            return await duplexRequest.WaitForReplyAsync(token);
 1282        }
 283
 284        // ASSUMPTION: caller holds lock (this.mutex)
 285        private void RequestStarting(Message message, IDuplexRequest request)
 286        {
 1287            if (request != null)
 288            {
 1289                Requests.Add(request);
 1290                if (!_requestAborted)
 291                {
 1292                    _correlator.Add<IDuplexRequest>(message, request);
 293                }
 294            }
 295
 1296            _pending++;
 1297        }
 298
 299        // ASSUMPTION: (mmaruch) caller holds lock (this.mutex)
 300        private void RequestCompleting(IDuplexRequest request)
 301        {
 1302            _pending--;
 1303            if (_pending == 0)
 304            {
 1305                _requests = null;
 306            }
 0307            else if ((request != null) && (_requests != null))
 308            {
 0309                _requests.Remove(request);
 310            }
 0311        }
 312
 313        // ASSUMPTION: caller holds ThisLock
 314        private void AddToTimedOutRequestList(ICorrelatorKey request)
 315        {
 316            Fx.Assert(request != null, "request cannot be null");
 0317            TimedOutRequests.Add(request);
 0318        }
 319
 320        // ASSUMPTION: caller holds  ThisLock
 321        private void RemoveFromTimedOutRequestList(ICorrelatorKey request)
 322        {
 323            Fx.Assert(request != null, "request cannot be null");
 0324            if (_timedOutRequests != null)
 325            {
 0326                _timedOutRequests.Remove(request);
 327            }
 0328        }
 329
 330        private void DeleteTimedoutRequestsFromCorrelator()
 331        {
 71332            ICorrelatorKey[] array = null;
 71333            if (_timedOutRequests != null && _timedOutRequests.Count > 0)
 334            {
 0335                lock (ThisLock)
 336                {
 0337                    if (_timedOutRequests != null && _timedOutRequests.Count > 0)
 338                    {
 0339                        array = _timedOutRequests.ToArray();
 0340                        _timedOutRequests = null;
 341                    }
 0342                }
 343            }
 344
 345            // Remove requests from the correlator since the channel might be either faulting, aborting or closing
 346            // We are not going to get a reply for these timed out requests. If they are not removed from the correlator
 347            // This operation does not have to be under the lock
 71348            if (array != null && array.Length > 0)
 349            {
 0350                if (_correlator is RequestReplyCorrelator requestReplyCorrelator)
 351                {
 0352                    foreach (ICorrelatorKey request in array)
 353                    {
 0354                        requestReplyCorrelator.RemoveRequest(request);
 355                    }
 356                }
 357            }
 71358        }
 359
 360        [MethodImpl(MethodImplOptions.NoInlining)]
 361        private void EnsureIncomingIdentity(SecurityMessageProperty property, EndpointAddress address, Message reply)
 362        {
 0363            IdentityVerifier.EnsureIncomingIdentity(address, property.ServiceSecurityContext.AuthorizationContext);
 0364        }
 365
 366        private void ThrowIfInvalidReplyIdentity(Message reply)
 367        {
 1368            if (!HasSession)
 369            {
 0370                SecurityMessageProperty property = reply.Properties.Security;
 0371                EndpointAddress address = _channel.RemoteAddress;
 372
 0373                if ((property != null) && (address != null))
 374                {
 0375                    EnsureIncomingIdentity(property, address, reply);
 376                }
 377            }
 1378        }
 379
 380        public void SetNextDispatcher(IServiceChannelDispatcher dispatcher)
 381        {
 382            Fx.Assert(dispatcher is IDefaultCommunicationTimeouts, "Next Dispatcher must implement IDefaultCommunication
 75383            _timeouts = dispatcher as IDefaultCommunicationTimeouts;
 75384            _next = dispatcher;
 75385        }
 386
 387        public Task DispatchAsync(RequestContext context)
 388        {
 0389            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
 390        }
 391
 392        public Task DispatchAsync(Message message)
 393        {
 394            Fx.Assert(_next != null, "SetNextDispatcher wasn't called");
 395            Fx.Assert(_channel.State != CommunicationState.Closed, "Expected dispatcher state to be Opened or Faulted, i
 157396            if (_channel.State == CommunicationState.Faulted || message == null)
 397            {
 69398                AbortRequests();
 69399                return _next.DispatchAsync((RequestContext)null);
 400            }
 401
 88402            return _next.DispatchAsync(CreateRequestContext(message));
 403        }
 404
 405        private class DuplexRequestContext : RequestContextBase
 406        {
 407            private readonly DuplexChannelBinder _binder;
 408            private readonly IDuplexChannel _channel;
 409
 410            internal DuplexRequestContext(IDuplexChannel channel, Message request, DuplexChannelBinder binder)
 89411                : base(request, binder.DefaultCloseTimeout, binder.DefaultSendTimeout)
 412            {
 89413                _channel = channel;
 89414                _binder = binder;
 89415            }
 416
 417            protected override void OnAbort()
 418            {
 0419            }
 420
 421            protected override Task OnCloseAsync(CancellationToken token)
 422            {
 88423                return Task.CompletedTask;
 424            }
 425
 426            protected override Task OnReplyAsync(Message message, CancellationToken token)
 427            {
 88428                if (message != null)
 429                {
 87430                    return _channel.SendAsync(message, token);
 431                }
 432
 1433                return Task.CompletedTask;
 434            }
 435        }
 436
 437        private interface IDuplexRequest
 438        {
 439            void Abort();
 440            void GotReply(Message reply);
 441        }
 442
 443        private class AsyncDuplexRequest : IDuplexRequest, ICorrelatorKey
 444        {
 445            private Message _reply;
 446            private readonly DuplexChannelBinder _parent;
 1447            private readonly AsyncManualResetEvent _wait = new AsyncManualResetEvent();
 448            private int _waitCount = 0;
 449            private RequestReplyCorrelator.Key _requestCorrelatorKey;
 450
 1451            internal AsyncDuplexRequest(DuplexChannelBinder parent)
 452            {
 1453                _parent = parent;
 1454            }
 455
 456            RequestReplyCorrelator.Key ICorrelatorKey.RequestCorrelatorKey
 457            {
 458                get
 459                {
 0460                    return _requestCorrelatorKey;
 461                }
 462                set
 463                {
 464                    Fx.Assert(_requestCorrelatorKey == null, "RequestCorrelatorKey is already set for this request");
 1465                    _requestCorrelatorKey = value;
 1466                }
 467            }
 468
 469            public void Abort()
 470            {
 0471                _wait.Set();
 0472            }
 473
 474            internal async Task<Message> WaitForReplyAsync(CancellationToken token)
 475            {
 476                try
 477                {
 1478                    if (!await _wait.WaitAsync(token))
 479                    {
 0480                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(_parent.GetReceiveTimeoutException(Tim
 481                    }
 1482                }
 483                finally
 484                {
 1485                    CloseWaitHandle();
 486                }
 487
 1488                _parent.ThrowIfInvalidReplyIdentity(_reply);
 1489                return _reply;
 1490            }
 491
 492            public void GotReply(Message reply)
 493            {
 1494                lock (_parent.ThisLock)
 495                {
 1496                    _parent.RequestCompleting(this);
 1497                }
 1498                _reply = reply;
 1499                _wait.Set();
 1500                CloseWaitHandle();
 1501            }
 502
 503            private void CloseWaitHandle()
 504            {
 2505                if (Interlocked.Increment(ref _waitCount) == 2)
 506                {
 1507                    _wait.Dispose();
 508                }
 2509            }
 510        }
 511    }
 512}

Methods/Properties

.ctor()
Init(CoreWCF.Channels.IDuplexSessionChannel,CoreWCF.Channels.IRequestReplyCorrelator,System.Uri)
Init(CoreWCF.Channels.IDuplexChannel,CoreWCF.Channels.IRequestReplyCorrelator,System.Uri)
DefaultSendTimeout()
DefaultCloseTimeout()
Channel()
ChannelHandler()
ChannelHandler(CoreWCF.Dispatcher.ChannelHandler)
HasSession()
IdentityVerifier()
IdentityVerifier(CoreWCF.Security.IdentityVerifier)
ListenUri()
LocalAddress()
RemoteAddress()
Requests()
TimedOutRequests()
ThisLock()
OnFaulted(System.Object,System.EventArgs)
Abort()
CloseAfterFault(System.TimeSpan)
AbortRequests()
GetReceiveTimeoutException(System.TimeSpan)
HandleRequestAsReply(CoreWCF.Channels.Message)
HandleRequestAsReplyCore(CoreWCF.Channels.Message)
CreateRequestContext(CoreWCF.Channels.Message)
SendAsync(CoreWCF.Channels.Message,System.Threading.CancellationToken)
RequestAsync()
RequestStarting(CoreWCF.Channels.Message,CoreWCF.Dispatcher.DuplexChannelBinder/IDuplexRequest)
RequestCompleting(CoreWCF.Dispatcher.DuplexChannelBinder/IDuplexRequest)
AddToTimedOutRequestList(CoreWCF.Channels.ICorrelatorKey)
RemoveFromTimedOutRequestList(CoreWCF.Channels.ICorrelatorKey)
DeleteTimedoutRequestsFromCorrelator()
EnsureIncomingIdentity(CoreWCF.Security.SecurityMessageProperty,CoreWCF.EndpointAddress,CoreWCF.Channels.Message)
ThrowIfInvalidReplyIdentity(CoreWCF.Channels.Message)
SetNextDispatcher(CoreWCF.Configuration.IServiceChannelDispatcher)
DispatchAsync(CoreWCF.Channels.RequestContext)
DispatchAsync(CoreWCF.Channels.Message)
.ctor(CoreWCF.Channels.IDuplexChannel,CoreWCF.Channels.Message,CoreWCF.Dispatcher.DuplexChannelBinder)
OnAbort()
OnCloseAsync(System.Threading.CancellationToken)
OnReplyAsync(CoreWCF.Channels.Message,System.Threading.CancellationToken)
.ctor(CoreWCF.Dispatcher.DuplexChannelBinder)
WCF.Channels.ICorrelatorKey.get_RequestCorrelatorKey()
WCF.Channels.ICorrelatorKey.set_RequestCorrelatorKey(CoreWCF.Channels.RequestReplyCorrelator/Key)
Abort()
WaitForReplyAsync()
GotReply(CoreWCF.Channels.Message)
CloseWaitHandle()