| | | 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 | | |
| | | 4 | | using System; |
| | | 5 | | using System.Collections.Generic; |
| | | 6 | | using System.Linq; |
| | | 7 | | using System.Threading.Tasks; |
| | | 8 | | using CoreWCF.Configuration; |
| | | 9 | | using CoreWCF.Dispatcher; |
| | | 10 | | using CoreWCF.Security; |
| | | 11 | | using Microsoft.AspNetCore.Connections; |
| | | 12 | | using Microsoft.AspNetCore.Server.Kestrel.Core; |
| | | 13 | | using Microsoft.Extensions.DependencyInjection; |
| | | 14 | | using Microsoft.Extensions.Logging; |
| | | 15 | | |
| | | 16 | | namespace CoreWCF.Channels.Framing |
| | | 17 | | { |
| | | 18 | | public class NetMessageFramingConnectionHandler : ConnectionHandler |
| | | 19 | | { |
| | | 20 | | private readonly IDispatcherBuilder _dispatcherBuilder; |
| | | 21 | | private readonly HandshakeDelegate _handshake; |
| | | 22 | | private readonly ILogger _framingLogger; |
| | | 23 | | private readonly IServiceProvider _services; |
| | | 24 | | |
| | | 25 | | [Obsolete("Added by mistake, does nothing", true)] |
| | 83 | 26 | | public List<ListenOptions> ListenOptions { get; } = new List<ListenOptions>(); |
| | | 27 | | |
| | 83 | 28 | | public NetMessageFramingConnectionHandler(IServiceBuilder serviceBuilder, IDispatcherBuilder dispatcherBuilder, |
| | | 29 | | { |
| | 83 | 30 | | _dispatcherBuilder = dispatcherBuilder; |
| | 83 | 31 | | _handshake = BuildHandshake(handshakeBuilder); |
| | 83 | 32 | | _framingLogger = framingLogger; |
| | 83 | 33 | | _services = handshakeBuilder.HandshakeServices; |
| | 83 | 34 | | if (serviceBuilder.State == CommunicationState.Created) |
| | | 35 | | { |
| | 83 | 36 | | serviceBuilder.Opened += OnServiceBuilderOpened; |
| | | 37 | | } |
| | 83 | 38 | | } |
| | | 39 | | |
| | | 40 | | private void OnServiceBuilderOpened(object sender, EventArgs e) |
| | | 41 | | { |
| | | 42 | | // Trigger building all of the services to improve first request time and to catch any service config issues |
| | 83 | 43 | | _services.GetRequiredService<UriPrefixTable<HandshakeDelegate>>(); |
| | 83 | 44 | | } |
| | | 45 | | |
| | | 46 | | private HandshakeDelegate BuildHandshake(IFramingConnectionHandshakeBuilder handshakeBuilder) |
| | | 47 | | { |
| | 83 | 48 | | handshakeBuilder.UseMiddleware<FramingModeHandshakeMiddleware>(); |
| | 198 | 49 | | handshakeBuilder.Map(connection => connection.FramingMode == FramingMode.Duplex, |
| | 83 | 50 | | configuration => |
| | 83 | 51 | | { |
| | 83 | 52 | | configuration.UseMiddleware<DuplexFramingMiddleware>(); |
| | 231 | 53 | | configuration.Use(next => connection => PerformServiceHandshake(configuration, connection, next)); |
| | 83 | 54 | | configuration.UseMiddleware<ServerFramingDuplexSessionMiddleware>(); |
| | 83 | 55 | | configuration.UseMiddleware<ServerSessionConnectionReaderMiddleware>(); |
| | 166 | 56 | | }); |
| | 133 | 57 | | handshakeBuilder.Map(connection => connection.FramingMode == FramingMode.Singleton, |
| | 83 | 58 | | configuration => |
| | 83 | 59 | | { |
| | 83 | 60 | | configuration.UseMiddleware<SingletonFramingMiddleware>(); |
| | 216 | 61 | | configuration.Use(next => connection => PerformServiceHandshake(configuration, connection, next)); |
| | 83 | 62 | | configuration.UseMiddleware<ServerFramingSingletonMiddleware>(); |
| | 83 | 63 | | configuration.UseMiddleware<ServerSingletonConnectionReaderMiddleware>(); |
| | 166 | 64 | | }); |
| | 83 | 65 | | return handshakeBuilder.Build(); |
| | | 66 | | } |
| | | 67 | | |
| | | 68 | | internal static UriPrefixTable<HandshakeDelegate> BuildAddressTable(IServiceProvider services) |
| | | 69 | | { |
| | 83 | 70 | | ILogger<NetMessageFramingConnectionHandler> logger = services.GetRequiredService<ILogger<NetMessageFramingCo |
| | 83 | 71 | | IServiceBuilder serviceBuilder = services.GetRequiredService<IServiceBuilder>(); |
| | 83 | 72 | | IDispatcherBuilder dispatcherBuilder = services.GetRequiredService<IDispatcherBuilder>(); |
| | 83 | 73 | | var addressTable = new UriPrefixTable<HandshakeDelegate>(); |
| | 336 | 74 | | foreach (Type serviceType in serviceBuilder.Services) |
| | | 75 | | { |
| | 85 | 76 | | List<IServiceDispatcher> dispatchers = dispatcherBuilder.BuildDispatchers(serviceType); |
| | 386 | 77 | | foreach (IServiceDispatcher dispatcher in dispatchers) |
| | | 78 | | { |
| | 108 | 79 | | if (dispatcher.BaseAddress == null) |
| | | 80 | | { |
| | | 81 | | // TODO: Should we throw? Ignore? |
| | | 82 | | continue; |
| | | 83 | | } |
| | | 84 | | |
| | | 85 | | // TODO: Limit to specifically TcpTransportBindingElement if net.tcp etc |
| | 108 | 86 | | BindingElementCollection be = dispatcher.Binding.CreateBindingElements(); |
| | 108 | 87 | | ConnectionOrientedTransportBindingElement cotbe = be.Find<ConnectionOrientedTransportBindingElement> |
| | 108 | 88 | | if (cotbe == null) |
| | | 89 | | { |
| | | 90 | | // TODO: Should we throw? Ignore? |
| | | 91 | | continue; |
| | | 92 | | } |
| | | 93 | | |
| | 108 | 94 | | IServiceDispatcher _serviceDispatcher = null; |
| | 108 | 95 | | var _customBinding = dispatcher.Binding as CustomBinding ?? new CustomBinding(dispatcher.Binding); |
| | 108 | 96 | | if (_customBinding.Elements.Find<ConnectionOrientedTransportBindingElement>() != null) |
| | | 97 | | { |
| | 108 | 98 | | var parameters = new BindingParameterCollection(); |
| | 108 | 99 | | if (_customBinding.CanBuildServiceDispatcher<IDuplexSessionChannel>(parameters)) |
| | | 100 | | { |
| | 108 | 101 | | _serviceDispatcher = _customBinding.BuildServiceDispatcher<IDuplexSessionChannel>(parameters |
| | | 102 | | } |
| | | 103 | | } |
| | 108 | 104 | | _serviceDispatcher ??= dispatcher; |
| | 108 | 105 | | HandshakeDelegate handshake = BuildHandshakeDelegateForDispatcher(_serviceDispatcher); |
| | | 106 | | |
| | 108 | 107 | | logger.LogDebug("Registering URI {baseAddress} with NetMessageFramingConnectionHandler", dispatcher. |
| | 108 | 108 | | addressTable.RegisterUri(dispatcher.BaseAddress, cotbe.HostNameComparisonMode, handshake); |
| | | 109 | | } |
| | | 110 | | } |
| | | 111 | | |
| | 83 | 112 | | return addressTable; |
| | | 113 | | } |
| | | 114 | | |
| | | 115 | | private static HandshakeDelegate BuildHandshakeDelegateForDispatcher(IServiceDispatcher dispatcher) |
| | | 116 | | { |
| | 108 | 117 | | BindingElementCollection be = dispatcher.Binding.CreateBindingElements(); |
| | 108 | 118 | | MessageEncodingBindingElement mebe = be.Find<MessageEncodingBindingElement>(); |
| | 108 | 119 | | MessageEncoderFactory mefact = mebe.CreateMessageEncoderFactory(); |
| | 108 | 120 | | ConnectionOrientedTransportBindingElement tbe = be.Find<ConnectionOrientedTransportBindingElement>(); |
| | 108 | 121 | | long maxReceivedMessageSize = tbe.MaxReceivedMessageSize; |
| | 108 | 122 | | int maxBufferSize = tbe.MaxBufferSize; |
| | 108 | 123 | | var bufferManager = BufferManager.CreateBufferManager(tbe.MaxBufferPoolSize, maxBufferSize); |
| | 108 | 124 | | int connectionBufferSize = tbe.ConnectionBufferSize; |
| | 108 | 125 | | TransferMode transferMode = tbe.TransferMode; |
| | 346 | 126 | | var upgradeBindingElements = (from element in be where element is StreamUpgradeBindingElement select element |
| | 108 | 127 | | StreamUpgradeProvider streamUpgradeProvider = null; |
| | 108 | 128 | | ISecurityCapabilities securityCapabilities = null; |
| | 108 | 129 | | if (upgradeBindingElements.Count > 1) |
| | | 130 | | { |
| | 0 | 131 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.MultipleStrea |
| | | 132 | | } |
| | | 133 | | // TODO: Limit NamedPipes to prevent it using SslStreamSecurityUpgradeProvider |
| | 108 | 134 | | else if ((upgradeBindingElements.Count == 1) && tbe.SupportsUpgrade(upgradeBindingElements[0])) |
| | | 135 | | { |
| | 19 | 136 | | SecurityCredentialsManager credentialsManager = dispatcher.Host.Description.Behaviors.Find<SecurityCrede |
| | 19 | 137 | | var bindingContext = new BindingContext(new CustomBinding(dispatcher.Binding), new BindingParameterColle |
| | | 138 | | |
| | 19 | 139 | | if (credentialsManager != null) |
| | 5 | 140 | | bindingContext.BindingParameters.Add(credentialsManager); |
| | | 141 | | |
| | 19 | 142 | | streamUpgradeProvider = upgradeBindingElements[0].BuildServerStreamUpgradeProvider(bindingContext); |
| | 19 | 143 | | if (streamUpgradeProvider != null) |
| | | 144 | | { |
| | 18 | 145 | | streamUpgradeProvider.OpenAsync().GetAwaiter().GetResult(); |
| | 18 | 146 | | securityCapabilities = upgradeBindingElements[0].GetProperty<ISecurityCapabilities>(bindingContext); |
| | 18 | 147 | | var identity = (streamUpgradeProvider as StreamSecurityUpgradeProvider)?.Identity; |
| | 18 | 148 | | if (identity != null) |
| | | 149 | | { |
| | 17 | 150 | | TryApplyIdentityToChannelDispatcher(dispatcher, identity); |
| | | 151 | | } |
| | | 152 | | } |
| | | 153 | | } |
| | 108 | 154 | | return (connection) => |
| | 108 | 155 | | { |
| | 114 | 156 | | connection.MessageEncoderFactory = mefact; |
| | 114 | 157 | | connection.StreamUpgradeAcceptor = streamUpgradeProvider?.CreateUpgradeAcceptor(); |
| | 114 | 158 | | if(connection.StreamUpgradeAcceptor != null) |
| | 108 | 159 | | { |
| | 8 | 160 | | connection.StreamUpgradeAcceptor.Features.Set<FramingConnection>(connection); |
| | 108 | 161 | | } |
| | 114 | 162 | | connection.SecurityCapabilities = securityCapabilities; |
| | 114 | 163 | | connection.ServiceDispatcher = dispatcher; |
| | 114 | 164 | | connection.BufferManager = bufferManager; |
| | 114 | 165 | | connection.MaxReceivedMessageSize = maxReceivedMessageSize; |
| | 114 | 166 | | connection.MaxBufferSize = maxBufferSize; |
| | 114 | 167 | | connection.ConnectionBufferSize = connectionBufferSize; |
| | 114 | 168 | | connection.TransferMode = transferMode; |
| | 114 | 169 | | return Task.CompletedTask; |
| | 108 | 170 | | }; |
| | | 171 | | } |
| | | 172 | | |
| | | 173 | | private static void TryApplyIdentityToChannelDispatcher(IServiceDispatcher dispatcher, EndpointIdentity identity |
| | | 174 | | { |
| | | 175 | | // The transport-derived identity applies only to the endpoints hosted on the |
| | | 176 | | // ChannelDispatcher for this service dispatcher's listen address. Applying it to |
| | | 177 | | // every ChannelDispatcher on the host would set a distinct identity instance on |
| | | 178 | | // endpoints that already had one, tripping EndpointDispatcher's set-once guard when |
| | | 179 | | // multiple net.tcp endpoints are hosted under transport security (see issue #1742). |
| | 88 | 180 | | foreach (ChannelDispatcher channelDispatcher in dispatcher.Host.ChannelDispatchers) |
| | | 181 | | { |
| | 27 | 182 | | if (channelDispatcher.ListenUri == dispatcher.BaseAddress) |
| | | 183 | | { |
| | 68 | 184 | | foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) |
| | | 185 | | { |
| | 17 | 186 | | endpointDispatcher.Identity = identity; |
| | | 187 | | } |
| | | 188 | | } |
| | | 189 | | } |
| | 17 | 190 | | } |
| | | 191 | | |
| | | 192 | | private static async Task PerformServiceHandshake(IFramingConnectionHandshakeBuilder configuration, FramingConne |
| | | 193 | | { |
| | 115 | 194 | | UriPrefixTable<HandshakeDelegate> addressTable = configuration.HandshakeServices.GetRequiredService<UriPrefi |
| | 115 | 195 | | HandshakeDelegate serviceHandshake = GetServiceHandshakeDelegate(addressTable, connection.Via); |
| | 115 | 196 | | if (serviceHandshake != null) |
| | | 197 | | { |
| | 114 | 198 | | await serviceHandshake(connection); |
| | 114 | 199 | | await next(connection); |
| | | 200 | | } |
| | | 201 | | else |
| | | 202 | | { |
| | 1 | 203 | | await connection.SendFaultAsync(FramingEncodingString.EndpointNotFoundFault, TransportDefaults.MaxDrainS |
| | | 204 | | } |
| | 114 | 205 | | } |
| | | 206 | | |
| | | 207 | | private static HandshakeDelegate GetServiceHandshakeDelegate(UriPrefixTable<HandshakeDelegate> addressTable, Uri |
| | | 208 | | { |
| | 115 | 209 | | if (addressTable.TryLookupUri(via, HostNameComparisonMode.StrongWildcard, out HandshakeDelegate handshake)) |
| | | 210 | | { |
| | 114 | 211 | | return handshake; |
| | | 212 | | } |
| | | 213 | | |
| | 1 | 214 | | if (addressTable.TryLookupUri(via, HostNameComparisonMode.Exact, out handshake)) |
| | | 215 | | { |
| | 0 | 216 | | return handshake; |
| | | 217 | | } |
| | | 218 | | |
| | 1 | 219 | | addressTable.TryLookupUri(via, HostNameComparisonMode.WeakWildcard, out handshake); |
| | 1 | 220 | | return handshake; |
| | | 221 | | } |
| | | 222 | | |
| | | 223 | | public override Task OnConnectedAsync(ConnectionContext context) |
| | | 224 | | { |
| | 82 | 225 | | var connection = new FramingConnection(context); |
| | 82 | 226 | | return _handshake(connection); |
| | | 227 | | } |
| | | 228 | | } |
| | | 229 | | } |