< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Channels.DuplexSessionChannelDemuxer
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelDemuxer.cs
Line coverage
39%
Covered lines: 19
Uncovered lines: 29
Coverable lines: 48
Total lines: 608
Line coverage: 39.5%
Branch coverage
42%
Covered branches: 6
Total branches: 14
Branch coverage: 42.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
CreateServiceChannelDispatcherAsync(...)100%11100%
AbortItem(...)100%110%
EndpointNotFoundAsync()0%440%
GetMessage(...)100%110%
.ctor(...)100%11100%
DispatchAsync(...)100%110%
DispatchAsync()60%101068.75%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Channels/ChannelDemuxer.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.Diagnostics;
 7using System.Threading.Tasks;
 8using CoreWCF.Configuration;
 9using CoreWCF.Dispatcher;
 10using CoreWCF.Runtime;
 11using static CoreWCF.Security.SecuritySessionServerSettings;
 12
 13namespace CoreWCF.Channels
 14{
 15    internal class ChannelDemuxer
 16    {
 17        public static readonly TimeSpan UseDefaultReceiveTimeout = TimeSpan.MinValue;
 18        //private TypedChannelDemuxer _inputDemuxer;
 19        private TypedChannelDemuxer _replyDemuxer;
 20        private Dictionary<Type, TypedChannelDemuxer> _typeDemuxers;
 21        private object _thisLock = new object(); // Used to protect acces to _typedDemuxers and _replyDemuxer
 22
 23        private TimeSpan _peekTimeout;
 24
 25        public ChannelDemuxer()
 26        {
 27            _peekTimeout = UseDefaultReceiveTimeout; //use the default receive timeout (original behavior)
 28            MaxPendingSessions = 10;
 29            _typeDemuxers = new Dictionary<Type, TypedChannelDemuxer>();
 30        }
 31
 32        public TimeSpan PeekTimeout
 33        {
 34            get
 35            {
 36                return _peekTimeout;
 37            }
 38            set
 39            {
 40                _peekTimeout = value;
 41            }
 42        }
 43
 44        public int MaxPendingSessions { get; set; }
 45
 46        internal IServiceDispatcher CreateServiceDispatcher<TChannel>(IServiceDispatcher innerDispatcher, ChannelDemuxer
 47        {
 48            return GetTypedServiceDispatcher<TChannel>(bindingParameters).AddDispatcher(innerDispatcher, filter);
 49        }
 50
 51        internal IServiceDispatcher CreateServiceDispatcher<TChannel>(IServiceDispatcher innerDispatcher, BindingParamet
 52        {
 53            return GetTypedServiceDispatcher<TChannel>(bindingParameters).AddDispatcher(innerDispatcher, new ChannelDemu
 54        }
 55
 56        internal void RemoveServiceDispatcher<TChannel>(MessageFilter filter, BindingParameterCollection bindingParamete
 57        {
 58            // Don't create if it doesn't already exist as the filter can't be held by a non-existent demuxer
 59            TryGetTypedServiceDispatcher(typeof(TChannel), bindingParameters)?.RemoveDispatcher(filter);
 60        }
 61
 62        internal TypedChannelDemuxer GetTypedServiceDispatcher<TChannel>(BindingParameterCollection bindingParameters)
 63        {
 64            return GetTypedServiceDispatcher(typeof(TChannel), bindingParameters);
 65        }
 66
 67        internal TypedChannelDemuxer TryGetTypedServiceDispatcher(Type channelType, BindingParameterCollection bindingPa
 68        {
 69            TypedChannelDemuxer typeDemuxer = null;
 70
 71            //if (typeof(TChannel) == typeof(IInputChannel))
 72            //{
 73            //    if (this.inputDemuxer == null)
 74            //    {
 75            //        if (context.CanBuildInnerChannelListener<IReplyChannel>())
 76            //            this.inputDemuxer = this.replyDemuxer = new ReplyChannelDemuxer(context);
 77            //        else
 78            //            this.inputDemuxer = new InputChannelDemuxer(context);
 79            //        createdDemuxer = true;
 80            //    }
 81            //    typeDemuxer = this.inputDemuxer;
 82            //}
 83            //else
 84            Type parentType = GetParentType(channelType);
 85            if (parentType == typeof(IReplyChannel))
 86            {
 87                typeDemuxer = _replyDemuxer;
 88            }
 89            else
 90            {
 91                lock (_thisLock)
 92                {
 93                    if (!_typeDemuxers.TryGetValue(parentType, out typeDemuxer))
 94                    {
 95                        typeDemuxer = null; // When not found, technically typeDemuxer will be undefined
 96                    }
 97                }
 98            }
 99
 100            //if (!createdDemuxer)
 101            //{
 102            //    context.RemainingBindingElements.Clear();
 103            //}
 104
 105            return typeDemuxer;
 106        }
 107
 108        internal TypedChannelDemuxer GetTypedServiceDispatcher(Type channelType, BindingParameterCollection bindingParam
 109        {
 110            TypedChannelDemuxer typeDemuxer = null;
 111
 112            //if (typeof(TChannel) == typeof(IInputChannel))
 113            //{
 114            //    if (this.inputDemuxer == null)
 115            //    {
 116            //        if (context.CanBuildInnerChannelListener<IReplyChannel>())
 117            //            this.inputDemuxer = this.replyDemuxer = new ReplyChannelDemuxer(context);
 118            //        else
 119            //            this.inputDemuxer = new InputChannelDemuxer(context);
 120            //        createdDemuxer = true;
 121            //    }
 122            //    typeDemuxer = this.inputDemuxer;
 123            //}
 124            //else
 125            Type parentType = GetParentType(channelType);
 126            if (parentType == typeof(IReplyChannel))
 127            {
 128                if (_replyDemuxer == null)
 129                {
 130                    lock (_thisLock)
 131                    {
 132                        if (_replyDemuxer == null)
 133                        {
 134                            _replyDemuxer = new ReplyChannelDemuxer(bindingParameters);
 135                        }
 136                    }
 137                }
 138
 139                typeDemuxer = _replyDemuxer;
 140            }
 141            else
 142            {
 143                lock (_thisLock)
 144                {
 145                    if (!_typeDemuxers.TryGetValue(parentType, out typeDemuxer))
 146                    {
 147                        typeDemuxer = CreateTypedDemuxer(channelType, bindingParameters);
 148                        _typeDemuxers.Add(channelType, typeDemuxer);
 149                    }
 150                }
 151            }
 152
 153            //if (!createdDemuxer)
 154            //{
 155            //    context.RemainingBindingElements.Clear();
 156            //}
 157
 158            return typeDemuxer;
 159        }
 160
 161        private TypedChannelDemuxer CreateTypedDemuxer(Type channelType, BindingParameterCollection bindingParameters)
 162        {
 163            /* if (channelType == typeof(IDuplexChannel))
 164                 return (TypedChannelDemuxer)(object)new DuplexChannelDemuxer(context);
 165             if (channelType == typeof(IInputSessionChannel))
 166                 return (TypedChannelDemuxer)(object)new InputSessionChannelDemuxer(context, this.peekTimeout, this.maxP
 167             if (channelType == typeof(IReplySessionChannel))
 168                 return (TypedChannelDemuxer)(object)new ReplySessionChannelDemuxer(context, this.peekTimeout, this.maxP
 169            if (channelType == typeof(IDuplexSessionChannel))
 170                return (TypedChannelDemuxer)(object)new DuplexSessionChannelDemuxer(bindingParameters);//, this.peekTime
 171            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
 172        }
 173
 174        private Type GetParentType(Type originalType)
 175        {
 176            if (typeof(IDuplexSessionChannel).IsAssignableFrom(originalType))
 177                return typeof(IDuplexSessionChannel);
 178            if (typeof(IReplyChannel).IsAssignableFrom(originalType))
 179                return typeof(IReplyChannel);
 180            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
 181        }
 182    }
 183
 184    internal abstract class TypedChannelDemuxer : IServiceDispatcher
 185    {
 186        public abstract Uri BaseAddress { get; }
 187        public abstract Binding Binding { get; }
 188        public abstract IList<Type> SupportedChannelTypes { get; }
 189
 190        public ServiceHostBase Host => throw new NotImplementedException();
 191
 192        public abstract Task<IServiceChannelDispatcher> CreateServiceChannelDispatcherAsync(IChannel channel);
 193
 194        public abstract IServiceDispatcher AddDispatcher(IServiceDispatcher innerDispatcher, ChannelDemuxerFilter filter
 195
 196        public abstract void RemoveDispatcher(MessageFilter filter);
 197        internal static void AbortMessage(RequestContext request)
 198        {
 199            // RequestContext.RequestMessage can throw an AddressMismatch exception.
 200            try
 201            {
 202                AbortMessage(request.RequestMessage);
 203            }
 204            catch (Exception e)
 205            {
 206                if (Fx.IsFatal(e))
 207                {
 208                    throw;
 209                }
 210
 211                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
 212            }
 213        }
 214
 215        internal static void AbortMessage(Message message)
 216        {
 217            try
 218            {
 219                message.Close();
 220            }
 221            catch (CommunicationException e)
 222            {
 223                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
 224            }
 225            catch (TimeoutException e)
 226            {
 227                //if (TD.CloseTimeoutIsEnabled())
 228                //{
 229                //    TD.CloseTimeout(e.Message);
 230                //}
 231
 232                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
 233            }
 234        }
 235    }
 236
 237    //
 238    // Session demuxers
 239    //
 240
 241    internal abstract class SessionChannelDemuxer<TInnerChannel, TInnerItem> : TypedChannelDemuxer
 242        where TInnerChannel : class, IChannel
 243        where TInnerItem : class, IDisposable
 244    {
 245        private readonly MessageFilterTable<IServiceDispatcher> _filterTable;
 246        public SessionChannelDemuxer(BindingParameterCollection bindingParameters)//, TimeSpan peekTimeout, int maxPendi
 247        {
 248            _filterTable = new MessageFilterTable<IServiceDispatcher>();
 249            DemuxFailureHandler = bindingParameters.Find<IChannelDemuxFailureHandler>();
 250        }
 251
 252        protected object ThisLock
 253        {
 254            get { return this; }
 255        }
 256
 257        protected IChannelDemuxFailureHandler DemuxFailureHandler { get; }
 258
 259        public override IServiceDispatcher AddDispatcher(IServiceDispatcher innerDispatcher, ChannelDemuxerFilter filter
 260        {
 261            lock (ThisLock)
 262            {
 263                _filterTable.Add(filter.Filter, innerDispatcher, filter.Priority);
 264            }
 265
 266            return this;
 267        }
 268
 269        public override void RemoveDispatcher(MessageFilter filter)
 270        {
 271            lock (ThisLock)
 272            {
 273                _filterTable.Remove(filter);
 274            }
 275        }
 276
 277        protected abstract void AbortItem(TInnerItem item);
 278        protected abstract Task EndpointNotFoundAsync(TInnerChannel channel, TInnerItem item);
 279        protected abstract Message GetMessage(TInnerItem item);
 280
 281        protected IServiceDispatcher MatchDispatcher(Message message)
 282        {
 283            IServiceDispatcher matchingDispatcher = null;
 284            lock (ThisLock)
 285            {
 286                if (_filterTable.GetMatchingValue(message, out matchingDispatcher))
 287                {
 288                    return matchingDispatcher;
 289                }
 290            }
 291
 292            return null;
 293        }
 294    }
 295
 296    internal class DuplexSessionChannelDemuxer : SessionChannelDemuxer<IDuplexSessionChannel, Message>
 297    {
 298        public DuplexSessionChannelDemuxer(BindingParameterCollection bindingParameters)//, TimeSpan peekTimeout, int ma
 3299            : base(bindingParameters)//, peekTimeout, maxPendingSessions)
 300        {
 3301        }
 302
 0303        public override Uri BaseAddress => throw new NotImplementedException();
 304
 0305        public override Binding Binding => throw new NotImplementedException();
 306
 0307        public override IList<Type> SupportedChannelTypes => throw new NotImplementedException();
 308
 309        public override Task<IServiceChannelDispatcher> CreateServiceChannelDispatcherAsync(IChannel channel)
 310        {
 5311            return Task.FromResult<IServiceChannelDispatcher>(new DuplexSessionChannelDispatcher(this, (IDuplexSessionCh
 312        }
 313
 314        protected override void AbortItem(Message message)
 315        {
 0316            AbortMessage(message);
 0317        }
 318
 319        protected override async Task EndpointNotFoundAsync(IDuplexSessionChannel channel, Message message)
 320        {
 0321            bool abortItem = true;
 322            try
 323            {
 0324                if (DemuxFailureHandler != null)
 325                {
 0326                    var duplexSessionRequestContext = new DuplexSessionRequestContext(channel, message);
 0327                    await DemuxFailureHandler.HandleDemuxFailureAsync(message, duplexSessionRequestContext);
 0328                    abortItem = false;
 329                }
 330
 0331            }
 332            catch (CommunicationException e)
 333            {
 0334                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
 0335            }
 336            catch (TimeoutException e)
 337            {
 0338                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
 0339            }
 340            catch (ObjectDisposedException e)
 341            {
 0342                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
 0343            }
 344            catch (Exception e)
 345            {
 0346                if (Fx.IsFatal(e)) throw;
 347                throw;
 348            }
 349            finally
 350            {
 0351                if (abortItem)
 352                {
 0353                    AbortMessage(message);
 0354                    channel.Abort();
 355                }
 356            }
 0357        }
 358
 359        protected override Message GetMessage(Message message)
 360        {
 0361            return message;
 362        }
 363
 364        internal class DuplexSessionChannelDispatcher : IServiceChannelDispatcher
 365        {
 366            private readonly DuplexSessionChannelDemuxer _demuxer;
 367            private readonly IDuplexSessionChannel _channel;
 368            private IServiceChannelDispatcher _serviceChannelDispatcher;
 369
 5370            public DuplexSessionChannelDispatcher(DuplexSessionChannelDemuxer replyChannelDemuxer, IDuplexSessionChannel
 371            {
 5372                _demuxer = replyChannelDemuxer;
 5373                _channel = channel;
 5374                channel.OpenAsync();
 5375            }
 376
 377            public Task DispatchAsync(RequestContext context)
 378            {
 0379                return Task.FromException(new NotImplementedException());
 380            }
 381
 382            public async Task DispatchAsync(Message message)
 383            {
 9384                if (message == null) //0 bytes
 385                {
 386                    //We have already closed all channels, return. (Couldn't use DoneReceivingInCurrentState())
 2387                    if (_channel.State == CommunicationState.Closed
 2388                        || _channel.State == CommunicationState.Closing
 2389                        || _channel.State == CommunicationState.Closed)
 390                    {
 0391                        return;
 392                    }
 393                    else
 394                    {
 2395                        await _serviceChannelDispatcher.DispatchAsync(message);
 2396                        return;
 397                    }
 398                }
 7399                IServiceDispatcher serviceDispatcher = _demuxer.MatchDispatcher(message);
 7400                if (serviceDispatcher == null)
 401                {
 0402                    ErrorBehavior.ThrowAndCatch(
 0403                        new EndpointNotFoundException(SR.Format(SR.UnableToDemuxChannel, message.Headers.Action)), messa
 0404                    await _demuxer.EndpointNotFoundAsync((IDuplexSessionChannel) _channel, message);
 0405                    return;
 406                }
 7407                _serviceChannelDispatcher = await serviceDispatcher.CreateServiceChannelDispatcherAsync(_channel);
 7408                await _serviceChannelDispatcher.DispatchAsync(message);
 9409            }
 410        }
 411
 412    }
 413
 414    //
 415    // Datagram demuxers
 416    //
 417
 418    internal abstract class DatagramChannelDemuxer<TInnerChannel, TInnerItem> : TypedChannelDemuxer
 419        where TInnerChannel : class, IChannel
 420        where TInnerItem : class, IDisposable
 421    {
 422        private readonly MessageFilterTable<IServiceDispatcher> _filterTable;
 423
 424        // since the OnOuterListenerOpen method will be called for every outer listener and we will open
 425        // the inner listener only once, we need to ensure that all the outer listeners wait till the
 426        // inner listener is opened.
 427        public DatagramChannelDemuxer(BindingParameterCollection bindingParameters)
 428        {
 429            _filterTable = new MessageFilterTable<IServiceDispatcher>();
 430            DemuxFailureHandler = bindingParameters.Find<IChannelDemuxFailureHandler>();
 431        }
 432
 433        protected TInnerChannel InnerChannel { get; }
 434
 435        protected IServiceDispatcher InnerDispatcher { get; }
 436
 437        protected object ThisLock
 438        {
 439            get { return this; }
 440        }
 441
 442        protected IChannelDemuxFailureHandler DemuxFailureHandler { get; }
 443
 444        public override IServiceDispatcher AddDispatcher(IServiceDispatcher innerDispatcher, ChannelDemuxerFilter filter
 445        {
 446            lock (ThisLock)
 447            {
 448                _filterTable.Add(filter.Filter, innerDispatcher, filter.Priority);
 449            }
 450
 451            return this;
 452        }
 453
 454        public override void RemoveDispatcher(MessageFilter filter)
 455        {
 456            lock (ThisLock)
 457            {
 458                _filterTable.Remove(filter);
 459            }
 460        }
 461
 462        protected abstract void AbortItem(TInnerItem item);
 463        protected abstract Task EndpointNotFoundAsync(TInnerItem item);
 464        protected abstract Message GetMessage(TInnerItem item);
 465
 466        protected IServiceDispatcher MatchDispatcher(Message message)
 467        {
 468            IServiceDispatcher matchingDispatcher = null;
 469            lock (ThisLock)
 470            {
 471                if (_filterTable.GetMatchingValue(message, out matchingDispatcher))
 472                {
 473                    return matchingDispatcher;
 474                }
 475            }
 476
 477            return null;
 478        }
 479    }
 480
 481    internal class ReplyChannelDemuxer : DatagramChannelDemuxer<IReplyChannel, RequestContext>
 482    {
 483        private static readonly IList<Type> s_supportedChannelTypes = new List<Type> { typeof(IReplyChannel) };
 484
 485        public override Uri BaseAddress => throw new NotImplementedException();
 486
 487        public override Binding Binding => throw new NotImplementedException();
 488
 489        public override IList<Type> SupportedChannelTypes => s_supportedChannelTypes;
 490
 491        public ReplyChannelDemuxer(BindingParameterCollection bindingParameters) : base(bindingParameters)
 492        {
 493        }
 494
 495        public override Task<IServiceChannelDispatcher> CreateServiceChannelDispatcherAsync(IChannel channel)
 496        {
 497            return Task.FromResult<IServiceChannelDispatcher>(new ReplyChannelDispatcher(this, channel));
 498        }
 499
 500        protected override void AbortItem(RequestContext request)
 501        {
 502            AbortMessage(request);
 503            request.Abort();
 504        }
 505
 506        protected override async Task EndpointNotFoundAsync(RequestContext request)
 507        {
 508            bool abortItem = true;
 509            try
 510            {
 511                if (DemuxFailureHandler != null)
 512                {
 513                    try
 514                    {
 515                       await DemuxFailureHandler.HandleDemuxFailureAsync(request.RequestMessage, request);
 516                       abortItem = false;
 517                    }
 518                    catch (CommunicationException e)
 519                    {
 520                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
 521                    }
 522                    catch (TimeoutException e)
 523                    {
 524                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
 525                    }
 526                    catch (ObjectDisposedException e)
 527                    {
 528                        DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
 529                    }
 530                    catch (Exception e)
 531                    {
 532                        if (Fx.IsFatal(e)) throw;
 533                        throw e;
 534                    }
 535                }
 536            }
 537            finally
 538            {
 539                if (abortItem)
 540                {
 541                    AbortItem(request);
 542                }
 543            }
 544        }
 545
 546        protected override Message GetMessage(RequestContext request)
 547        {
 548            return request.RequestMessage;
 549        }
 550
 551        internal class ReplyChannelDispatcher : IServiceChannelDispatcher
 552        {
 553            private readonly ReplyChannelDemuxer _demuxer;
 554            private readonly IChannel _channel;
 555
 556            public ReplyChannelDispatcher(ReplyChannelDemuxer replyChannelDemuxer, IChannel channel)
 557            {
 558                _demuxer = replyChannelDemuxer;
 559                _channel = channel;
 560            }
 561
 562            public async Task DispatchAsync(RequestContext context)
 563            {
 564                // TODO: Find way to avoid instantiating a new ServiceChannelDispatcher each time
 565                IServiceDispatcher serviceDispatcher = _demuxer.MatchDispatcher(context.RequestMessage);
 566                if (serviceDispatcher == null)
 567                {
 568                    ErrorBehavior.ThrowAndCatch(
 569                        new EndpointNotFoundException(SR.Format(SR.UnableToDemuxChannel, context.RequestMessage.Headers.
 570                    await _demuxer.EndpointNotFoundAsync(context);
 571                    return;
 572                }
 573                // TODO: if serviceDispatcher == null, use the EndpointNotFound code path
 574                IServiceChannelDispatcher serviceChannelDispatcher = await serviceDispatcher.CreateServiceChannelDispatc
 575                await serviceChannelDispatcher.DispatchAsync(context);
 576            }
 577
 578            public Task DispatchAsync(Message message)
 579            {
 580               return Task.FromException(new NotImplementedException());
 581            }
 582        }
 583    }
 584
 585    internal interface IChannelDemuxerFilter
 586    {
 587        ChannelDemuxerFilter Filter { get; }
 588    }
 589
 590    internal class ChannelDemuxerFilter
 591    {
 592        public ChannelDemuxerFilter(MessageFilter filter, int priority)
 593        {
 594            Filter = filter;
 595            Priority = priority;
 596        }
 597
 598        public MessageFilter Filter { get; }
 599
 600        public int Priority { get; }
 601    }
 602
 603    internal interface IChannelDemuxFailureHandler
 604    {
 605        Task HandleDemuxFailureAsync(Message message);
 606        Task HandleDemuxFailureAsync(Message message, RequestContext faultContext);
 607    }
 608}