< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Configuration.NetTcpFramingOptions
Assembly: CoreWCF.NetTcp
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/ServiceModelWebHostBuilderExtensions.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 1
Coverable lines: 1
Total lines: 250
Line coverage: 0%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetTcp/src/CoreWCF/Configuration/ServiceModelWebHostBuilderExtensions.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.Net;
 7using System.Net.Sockets;
 8using System.Web.Services.Description;
 9using CoreWCF.Channels;
 10using CoreWCF.Channels.Framing;
 11using Microsoft.AspNetCore.Connections;
 12using Microsoft.AspNetCore.Hosting;
 13using Microsoft.AspNetCore.Hosting.Server;
 14using Microsoft.AspNetCore.Server.Kestrel.Core;
 15using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
 16using Microsoft.Extensions.DependencyInjection;
 17using Microsoft.Extensions.DependencyInjection.Extensions;
 18using Microsoft.Extensions.Hosting;
 19using Microsoft.Extensions.Logging;
 20using Microsoft.Extensions.Options;
 21
 22namespace CoreWCF.Configuration
 23{
 24    public static class ServiceModelWebHostBuilderExtensions
 25    {
 26        public static IWebHostBuilder UseNetTcp(this IWebHostBuilder webHostBuilder)
 27        {
 28            // Using default port for net.tcp
 29            return webHostBuilder.UseNetTcp(808);
 30        }
 31
 32        public static IWebHostBuilder UseNetTcp(this IWebHostBuilder webHostBuilder, int port)
 33        {
 34            return webHostBuilder.UseNetTcp(IPAddress.Any, port);
 35        }
 36
 37        public static IWebHostBuilder UseNetTcp(this IWebHostBuilder webHostBuilder, IPAddress ipAddress)
 38        {
 39            return webHostBuilder.UseNetTcp(ipAddress, 808);
 40        }
 41
 42        public static IWebHostBuilder UseNetTcp(this IWebHostBuilder webHostBuilder, IPAddress ipAddress, int port)
 43        {
 44            webHostBuilder.AddNetTcpRequiredServices()
 45                          .ConfigureNetTcp(options =>
 46                          {
 47                              var uriBuilder = new UriBuilder("net.tcp", ipAddress.ToString(), port);
 48                              options.Listen(uriBuilder.Uri);
 49                          });
 50            return webHostBuilder;
 51        }
 52
 53        private static IWebHostBuilder AddNetTcpRequiredServices(this IWebHostBuilder webHostBuilder)
 54        {
 55            webHostBuilder.ConfigureServices(services =>
 56            {
 57                services.AddNetFramingServices();
 58                services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, NetTcpHostedService>());
 59                services.TryAddSingleton<NetTcpFramingOptionsSetup>();
 60                services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<KestrelServerOptions>, NetTcpFra
 61                services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<NetTcpOptions>, NetTcpServerOpti
 62                services.TryAddSingleton<SocketTransportFactory>();
 63            });
 64            return webHostBuilder;
 65        }
 66
 67        public static IWebHostBuilder UseNetTcp(this IWebHostBuilder webHostBuilder, Action<NetTcpOptions> options)
 68        {
 69            webHostBuilder.AddNetTcpRequiredServices()
 70                          .ConfigureNetTcp(options);
 71            return webHostBuilder;
 72        }
 73
 74        public static IWebHostBuilder ConfigureNetTcp(this IWebHostBuilder webHostBuilder, Action<NetTcpOptions> options
 75        {
 76            return webHostBuilder.ConfigureServices(services =>
 77            {
 78                services.Configure(options);
 79            });
 80        }
 81
 82        private static IServiceCollection AddNetTcpServices(this IServiceCollection services, IPEndPoint endPoint)
 83        {
 84            services.Configure<NetTcpFramingOptions>(o =>
 85            {
 86                o.EndPoints.Add(endPoint);
 87            });
 88
 89            return services;
 90        }
 91    }
 92
 93    // Exposes options for how to bind. This could include port sharing in the future
 94    internal class NetTcpFramingOptions
 95    {
 096        public List<IPEndPoint> EndPoints { get; } = new List<IPEndPoint>();
 97    }
 98
 99    internal class NetTcpFramingOptionsSetup : IConfigureOptions<KestrelServerOptions>
 100    {
 101        private readonly ILogger<NetTcpFramingOptions> _logger;
 102        private readonly ILogger<FramingConnection> _framingConnectionLogger;
 103        private readonly IServiceProvider _serviceProvider;
 104        private readonly NetTcpOptions _options;
 105        private readonly IServiceBuilder _serviceBuilder;
 106
 107        public NetTcpFramingOptionsSetup(IOptions<NetTcpOptions> options, IServiceBuilder serviceBuilder, ILogger<NetTcp
 108        {
 109            _options = options.Value ?? new NetTcpOptions();
 110            _serviceBuilder = serviceBuilder;
 111            _logger = logger;
 112            _framingConnectionLogger = framingConnectionLogger;
 113            _serviceProvider = serviceProvider;
 114        }
 115
 116        public List<ListenOptions> ListenOptions { get; } = new List<ListenOptions>();
 117
 118        public bool ConfigureCalled { get; set; }
 119
 120        public void Configure(KestrelServerOptions options)
 121        {
 122            ConfigureCalled = true;
 123            options.ApplicationServices = _serviceProvider;
 124            foreach(var tcpListenOptions in _options.CodeBackedListenOptions)
 125            {
 126                var baseAddress = tcpListenOptions.BaseAddress;
 127                IPAddress listenAddress = null;
 128                if (baseAddress.HostNameType == UriHostNameType.IPv4 || baseAddress.HostNameType == UriHostNameType.IPv6
 129                {
 130                    if (IPAddress.TryParse(baseAddress.DnsSafeHost, out listenAddress))
 131                    {
 132                        if (listenAddress.AddressFamily == AddressFamily.InterNetwork && !Socket.OSSupportsIPv4)
 133                        {
 134                            _logger.LogError("NetTcp listen uri specified the IPv4 hostname \"{HostName}\" and the OS do
 135                            continue;
 136                        }
 137                        if (listenAddress.AddressFamily == AddressFamily.InterNetworkV6 && !Socket.OSSupportsIPv6)
 138                        {
 139                            _logger.LogError("NetTcp listen uri specified the IPv6 hostname \"{HostName}\" and the OS do
 140                            continue;
 141                        }
 142                    }
 143                }
 144                else if (baseAddress.HostNameType == UriHostNameType.Dns)
 145                {
 146                    if (Socket.OSSupportsIPv6)
 147                    {
 148                        listenAddress = IPAddress.IPv6Any;
 149                    }
 150                    else
 151                    {
 152                        listenAddress = IPAddress.Any;
 153                    }
 154                }
 155                else
 156                {
 157                    _logger.LogError("NetTcp listen uri specified an unusable hostname \"{HostName}\"", baseAddress.DnsS
 158                    continue;
 159                }
 160
 161                IPEndPoint endpoint = new IPEndPoint(listenAddress, baseAddress.Port);
 162                options.Listen(endpoint, builder =>
 163                {
 164                    AddTcpListenOptionsToConnectionContext(builder, tcpListenOptions);
 165                    builder.Use(ConvertExceptionsAndAddLogging);
 166                    builder.UseConnectionHandler<NetMessageFramingConnectionHandler>();
 167                    // Save the ListenOptions to be able to get final port number for adding BaseAddresses later
 168                    ListenOptions.Add(builder);
 169                    // Save the Kestrel ListenOptions in the TcpListenOptions to enable getting the final listening IPEn
 170                    // This helps to get the port number being used if specified as 0, after the server has started.
 171                    tcpListenOptions.ListenOptions = builder;
 172                });
 173            }
 174            //foreach (IPEndPoint endpoint in _options.EndPoints)
 175            //{
 176            //    options.Listen(endpoint, builder =>
 177            //    {
 178            //        builder.Use(ConvertExceptionsAndAddLogging);
 179            //        builder.UseConnectionHandler<NetMessageFramingConnectionHandler>();
 180            //        // Save the ListenOptions to be able to get final port number for adding BaseAddresses later
 181            //        ListenOptions.Add(builder);
 182            //    });
 183            //}
 184
 185            _serviceBuilder.Opening += OnServiceBuilderOpening;
 186        }
 187
 188        private void AddTcpListenOptionsToConnectionContext(ListenOptions builder, TcpListenOptions tcpListenOptions)
 189        {
 190            builder.Use(next =>
 191            {
 192                tcpListenOptions.Use(innerNext =>
 193                {
 194                    return (ConnectionContext context) =>
 195                    {
 196                        context.Features.Set<NetFramingListenOptions>(tcpListenOptions);
 197                        return innerNext(context);
 198                    };
 199                });
 200                tcpListenOptions.Use(_ =>
 201                {
 202                    return (ConnectionContext context) =>
 203                    {
 204                        return next(context);
 205                    };
 206                });
 207                var tcpListenOptionsMiddleware = ((IConnectionBuilder)tcpListenOptions).Build();
 208                return tcpListenOptionsMiddleware;
 209            });
 210        }
 211
 212        private ConnectionDelegate ConvertExceptionsAndAddLogging(ConnectionDelegate next)
 213        {
 214            return (ConnectionContext context) =>
 215            {
 216                var logger = new ConnectionIdWrappingLogger(_framingConnectionLogger, context.ConnectionId);
 217                context.Features.Set<ILogger>(logger);
 218
 219                //TODO: Add a public api mechanism to enable connection logging in RELEASE build
 220#if DEBUG
 221                context.Transport = new NetTcpExceptionConvertingDuplexPipe(new LoggingDuplexPipe(context.Transport, log
 222#else
 223                context.Transport = new NetTcpExceptionConvertingDuplexPipe(context.Transport);
 224#endif
 225                return next(context);
 226            };
 227        }
 228
 229        private void OnServiceBuilderOpening(object sender, EventArgs e)
 230        {
 231            UpdateServiceBuilderBaseAddresses();
 232        }
 233
 234        internal void UpdateServiceBuilderBaseAddresses()
 235        {
 236            foreach (ListenOptions listenOptions in ListenOptions)
 237            {
 238                IPEndPoint endpoint = listenOptions.IPEndPoint;
 239                string address = endpoint.Address.ToString();
 240                if (endpoint.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
 241                    address = $"[{address}]";
 242                var baseAddress = new Uri($"net.tcp://{address}:{endpoint.Port}/");
 243                _logger.LogDebug("Adding base address {baseAddress} to ServiceBuilderOptions", baseAddress);
 244                _serviceBuilder.BaseAddresses.Add(baseAddress);
 245            }
 246
 247            ListenOptions.Clear();
 248        }
 249    }
 250}

Methods/Properties

EndPoints()