< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.ServiceHostObjectModel<T>
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/ServiceHostObjectModel.cs
Line coverage
76%
Covered lines: 86
Uncovered lines: 27
Coverable lines: 113
Total lines: 317
Line coverage: 76.1%
Branch coverage
68%
Covered branches: 52
Total branches: 76
Branch coverage: 68.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
CreateDescription(...)95.83%242497.14%
.ctor()100%11100%
GetKeyForItem(...)50%2266.66%
ToImplementedContracts()100%22100%
GetConfigKey(...)100%11100%
.ctor(...)100%110%
Contains(...)0%660%
GetConfigKey(...)0%660%
ApplyConfiguration()100%1212100%
FixUri(...)54.16%242458.33%
OnAbort()100%11100%
OnCloseAsync(...)100%110%
OnOpenAsync(...)100%110%
WaitForServiceBuilderOpening(...)100%11100%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/ServiceHostObjectModel.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.Collections.ObjectModel;
 7using System.Diagnostics;
 8using System.Linq;
 9using System.Text;
 10using System.Threading;
 11using System.Threading.Tasks;
 12using CoreWCF.Collections.Generic;
 13using CoreWCF.Configuration;
 14using CoreWCF.Description;
 15using CoreWCF.Dispatcher;
 16using Microsoft.AspNetCore.Hosting.Server;
 17using Microsoft.AspNetCore.Hosting.Server.Features;
 18using Microsoft.Extensions.DependencyInjection;
 19using Microsoft.Extensions.Logging;
 20
 21namespace CoreWCF
 22{
 23    internal class ServiceHostObjectModel<TService> : ServiceHostBase where TService : class
 24    {
 25        private IDisposable _disposableInstance;
 26        private readonly IServiceProvider _serviceProvider;
 27#pragma warning disable IDE0052 // Remove unread private members - see issue #286
 28        private readonly ILogger<ServiceHostObjectModel<TService>> _logger;
 29#pragma warning restore IDE0052 // Remove unread private members
 30
 58331        public ServiceHostObjectModel(IServiceProvider serviceProvider, ServiceBuilder serviceBuilder, ILogger<ServiceHo
 32        {
 58333            _serviceProvider = serviceProvider;
 58334            _logger = logger;
 35
 58336            WaitForServiceBuilderOpening(serviceBuilder);
 37
 58338            InitializeDescription(new UriSchemeKeyedCollection(serviceBuilder.BaseAddresses.ToArray()));
 57639        }
 40
 7941        public TService SingletonInstance { get; private set; }
 42
 43        internal override object DisposableInstance
 44        {
 45            get
 46            {
 238947                return _disposableInstance;
 48            }
 49        }
 50
 185851        internal ReflectedContractCollection ReflectedContracts { get; private set; }
 52
 53        protected override ServiceDescription CreateDescription(out IDictionary<string, ContractDescription> implemented
 54        {
 58355            ServiceDescription description = _serviceProvider.GetService<ServiceDescription<TService>>();
 56
 58257            ServiceBehaviorAttribute serviceBehavior = description.Behaviors.Find<ServiceBehaviorAttribute>();
 58258            TService serviceInstanceUsedAsABehavior = (TService)serviceBehavior.GetWellKnownSingleton();
 58259            if (serviceInstanceUsedAsABehavior == null)
 60            {
 57561                serviceInstanceUsedAsABehavior = (TService)serviceBehavior.GetHiddenSingleton();
 57562                _disposableInstance = serviceInstanceUsedAsABehavior as IDisposable;
 63            }
 64
 65            // serviceInstanceUsedAsBehavior will be null when InstanceContextMode != Single
 66            // In this case, we need to check if the service type is a behavior and if it is, create an instance to appl
 58267            if ((typeof(IServiceBehavior).IsAssignableFrom(typeof(TService)) || typeof(IContractBehavior).IsAssignableFr
 58268                    && serviceInstanceUsedAsABehavior == null)
 69            {
 770                serviceInstanceUsedAsABehavior = _serviceProvider.GetService<TService>(); // First try DI to get an inst
 771                if (serviceInstanceUsedAsABehavior == null) // Not in DI so create the old WCF way using reflection
 72                {
 273                    serviceInstanceUsedAsABehavior = ServiceDescription.CreateImplementation<TService>();
 74                }
 75
 776                _disposableInstance = serviceInstanceUsedAsABehavior as IDisposable;
 77            }
 78
 58279            if (serviceBehavior.InstanceContextMode == InstanceContextMode.Single)
 80            {
 7981                serviceBehavior.ServicePovider = _serviceProvider;
 82
 83                // If using Single, then ServiceBehavior fetched/created the instance and need to set on SingletonInstan
 84                Debug.Assert(serviceInstanceUsedAsABehavior != null, "Service behavior should have created a singleton i
 7985                SingletonInstance = serviceInstanceUsedAsABehavior;
 86            }
 87            else
 88            {
 50389                serviceBehavior.InstanceProvider = new DependencyInjectionWithLegacyFallbackInstanceProvider(_servicePro
 90            }
 91
 58292            if (serviceInstanceUsedAsABehavior is IServiceBehavior behavior)
 93            {
 994                description.Behaviors.Add(behavior);
 95            }
 96
 58297            ReflectedContractCollection reflectedContracts = new ReflectedContractCollection();
 58298            List<Type> interfaces = ServiceReflector.GetInterfaces<TService>();
 247699            for (int i = 0; i < interfaces.Count; i++)
 100            {
 662101                Type contractType = interfaces[i];
 662102                if (!reflectedContracts.Contains(contractType))
 103                {
 104                    ContractDescription contract;
 594105                    if (serviceInstanceUsedAsABehavior != null)
 106                    {
 89107                        contract = ContractDescription.GetContract<TService>(contractType, serviceInstanceUsedAsABehavio
 108                    }
 109                    else
 110                    {
 505111                        contract = ContractDescription.GetContract<TService>(contractType);
 112                    }
 113
 590114                    reflectedContracts.Add(contract);
 590115                    Collection<ContractDescription> inheritedContracts = contract.GetInheritedContracts();
 1336116                    for (int j = 0; j < inheritedContracts.Count; j++)
 117                    {
 78118                        ContractDescription inheritedContract = inheritedContracts[j];
 78119                        if (!reflectedContracts.Contains(inheritedContract.ContractType))
 120                        {
 68121                            reflectedContracts.Add(inheritedContract);
 122                        }
 123                    }
 124                }
 125            }
 126
 576127            ReflectedContracts = reflectedContracts;
 128
 576129            implementedContracts = reflectedContracts.ToImplementedContracts();
 576130            return description;
 131        }
 132
 133        internal class ReflectedContractCollection : KeyedCollection<Type, ContractDescription>
 134        {
 135            public ReflectedContractCollection()
 582136                : base(null, 4)
 137            {
 582138            }
 139
 140            protected override Type GetKeyForItem(ContractDescription item)
 141            {
 2286142                if (item == null)
 143                {
 0144                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(item));
 145                }
 146
 2286147                return item.ContractType;
 148            }
 149
 150            public IDictionary<string, ContractDescription> ToImplementedContracts()
 151            {
 576152                Dictionary<string, ContractDescription> implementedContracts = new Dictionary<string, ContractDescriptio
 2468153                foreach (ContractDescription contract in Items)
 154                {
 658155                    implementedContracts.Add(GetConfigKey(contract), contract);
 156                }
 576157                return implementedContracts;
 158            }
 159
 160            internal static string GetConfigKey(ContractDescription contract)
 161            {
 658162                return contract.ConfigurationName;
 163            }
 164        }
 165
 166        private class ReflectedAndBehaviorContractCollection
 167        {
 168            private readonly ReflectedContractCollection _reflectedContracts;
 169            private readonly KeyedByTypeCollection<IServiceBehavior> _behaviors;
 0170            public ReflectedAndBehaviorContractCollection(ReflectedContractCollection reflectedContracts, KeyedByTypeCol
 171            {
 0172                _reflectedContracts = reflectedContracts;
 0173                _behaviors = behaviors;
 0174            }
 175
 176            internal bool Contains(Type implementedContract)
 177            {
 0178                if (_reflectedContracts.Contains(implementedContract))
 179                {
 0180                    return true;
 181                }
 182
 0183                if (_behaviors.Contains(typeof(ServiceMetadataBehavior)) && ServiceMetadataBehavior.IsMetadataImplemente
 184                {
 0185                    return true;
 186                }
 187
 0188                return false;
 189            }
 190
 191            internal string GetConfigKey(Type implementedContract)
 192            {
 0193                if (_reflectedContracts.Contains(implementedContract))
 194                {
 0195                    return ReflectedContractCollection.GetConfigKey(_reflectedContracts[implementedContract]);
 196                }
 197
 0198                if (_behaviors.Contains(typeof(ServiceMetadataBehavior)) && ServiceMetadataBehavior.IsMetadataImplemente
 199                {
 0200                    return ServiceMetadataBehavior.MexContractName;
 201                }
 202
 0203                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.Sfx
 204            }
 205        }
 206
 207        protected override void ApplyConfiguration()
 208        {
 576209            base.ApplyConfiguration();
 210
 576211            IServer server = _serviceProvider.GetService<IServer>();
 212            //For UDS there is no web server.
 213            //May be check if specific to UDS or not
 576214            if(server != null)
 215            {
 573216                IServerAddressesFeature addresses = server.Features.Get<IServerAddressesFeature>();
 2364217                foreach (string address in addresses.Addresses)
 218                {
 609219                    if (!address.StartsWith("http", StringComparison.OrdinalIgnoreCase))
 220                    {
 221                        // IIS hosting can populate Addresses with net.tcp/net.pipe/net.msmq addresses
 222                        // if the site has those bindings
 223                        continue;
 224                    }
 225
 558226                    var fixedUri = FixUri(address);
 227                    // ASP.NET Core assumes all listeners are http. Other transports such as NetTcp will already be popu
 228                    // in the base addresses so filter them out.
 558229                    bool skip = false;
 1275230                    foreach (var baseAddress in InternalBaseAddresses)
 231                    {
 123232                        if (baseAddress.Port == fixedUri.Port) // Already added with a different protocol
 233                        {
 87234                            skip = true;
 87235                            break;
 236                        }
 237                    }
 238
 558239                    if (!skip)
 240                    {
 471241                        AddBaseAddress(FixUri(address));
 242                    }
 243                }
 244            }
 245
 576246        }
 247
 248        private static Uri FixUri(string address)
 249        {
 1029250            if (address.StartsWith("http://+:", StringComparison.OrdinalIgnoreCase) ||
 1029251                address.StartsWith("http://+/", StringComparison.OrdinalIgnoreCase) ||
 1029252                address.StartsWith("http://*:", StringComparison.OrdinalIgnoreCase) ||
 1029253                address.StartsWith("http://*/", StringComparison.OrdinalIgnoreCase))
 254            {
 0255                address = "http://localhost" + address.Substring(8);
 256            }
 1029257            else if (address.StartsWith("https://+:", StringComparison.OrdinalIgnoreCase) ||
 1029258                     address.StartsWith("https://+/", StringComparison.OrdinalIgnoreCase) ||
 1029259                     address.StartsWith("https://*:", StringComparison.OrdinalIgnoreCase) ||
 1029260                     address.StartsWith("https://*/", StringComparison.OrdinalIgnoreCase))
 261            {
 0262                address = "https://localhost" + address.Substring(9);
 263            }
 1029264            else if (address.StartsWith("http://[::]", StringComparison.OrdinalIgnoreCase))
 265            {
 6266                address = "http://localhost" + address.Substring(11);
 267            }
 1023268            else if (address.StartsWith("https://[::]", StringComparison.OrdinalIgnoreCase))
 269            {
 0270                address = "https://localhost" + address.Substring(12);
 271            }
 1023272            else if (address.StartsWith("https://*.", StringComparison.OrdinalIgnoreCase) ||
 1023273                    address.StartsWith("http://*.", StringComparison.OrdinalIgnoreCase))
 274            {
 0275                int colonIndex = address.IndexOf(':');
 0276                string beforeAsterisk = address.Substring(0, colonIndex + 3);
 0277                string rest = address.Substring(colonIndex + 4);
 0278                StringBuilder sb = new StringBuilder(beforeAsterisk);
 0279                sb.Append(System.Net.Dns.GetHostName());
 0280                sb.Append(rest);
 0281                address = sb.ToString();
 282            }
 1029283            return new Uri(address);
 284        }
 285
 518286        protected override void OnAbort() { }
 287
 288        protected override Task OnCloseAsync(CancellationToken token)
 289        {
 0290            return Task.CompletedTask;
 291        }
 292
 293        protected override Task OnOpenAsync(CancellationToken token)
 294        {
 0295            return Task.CompletedTask;
 296        }
 297
 298        /// <summary>
 299        /// Adds any implicitly-bound addresses to the service builder base addresses, so that net.tcp://
 300        /// bindings will correctly work on them. This is only necessary when the port provided to `UseNetTcp` is 0.
 301        ///
 302        /// <remarks>
 303        /// Currently, this does not remove the bound address from the server addresses. The framework removes
 304        /// the `net.tcp` scheme and replaces with with `http`, so the host name itself is checked.
 305        ///
 306        /// Another option that might be cleaner, would be to remove the use of `serviceBuilder.BaseAddresses` or
 307        /// populate it at runtime once from ServerAddresses, and disallow adding values by hand.
 308        /// </remarks>
 309        /// </summary>
 310        /// <param name="serviceBuilder"></param>
 311        private void WaitForServiceBuilderOpening(ServiceBuilder serviceBuilder)
 312        {
 583313            serviceBuilder.WaitForOpening().GetAwaiter().GetResult();
 583314            serviceBuilder.ThrowIfDisposedOrNotOpen();
 583315        }
 316    }
 317}