| | | 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.Threading.Tasks; |
| | | 6 | | using CoreWCF.Configuration; |
| | | 7 | | using Microsoft.AspNetCore.Builder; |
| | | 8 | | using Microsoft.AspNetCore.Http; |
| | | 9 | | using Microsoft.Extensions.DependencyInjection; |
| | | 10 | | using Microsoft.Extensions.Logging; |
| | | 11 | | |
| | | 12 | | namespace CoreWCF.Channels |
| | | 13 | | { |
| | | 14 | | public partial class ServiceModelHttpMiddleware |
| | | 15 | | { |
| | | 16 | | private const string RestorePathsDelegateItemName = nameof(ServiceModelHttpMiddleware) + "_RestorePathsDelegate" |
| | | 17 | | private readonly IApplicationBuilder _app; |
| | | 18 | | private readonly IServiceBuilder _serviceBuilder; |
| | | 19 | | private readonly IDispatcherBuilder _dispatcherBuilder; |
| | | 20 | | private readonly RequestDelegate _next; |
| | | 21 | | private readonly ILogger<ServiceModelHttpMiddleware> _logger; |
| | | 22 | | private RequestDelegate _branch; |
| | | 23 | | private bool _branchBuilt; |
| | | 24 | | |
| | 382 | 25 | | public ServiceModelHttpMiddleware(RequestDelegate next, IApplicationBuilder app, IServiceBuilder serviceBuilder, |
| | | 26 | | { |
| | 382 | 27 | | _app = app; |
| | 382 | 28 | | _serviceBuilder = serviceBuilder; |
| | 382 | 29 | | _dispatcherBuilder = dispatcherBuilder; |
| | 382 | 30 | | _next = next; |
| | 382 | 31 | | _logger = logger; |
| | 382 | 32 | | _branch = BuildBranchAndInvoke; |
| | 382 | 33 | | serviceBuilder.Opened += ServiceBuilderOpenedCallback; |
| | 382 | 34 | | } |
| | | 35 | | |
| | | 36 | | public async Task InvokeAsync(HttpContext context) |
| | | 37 | | { |
| | | 38 | | // Update the path |
| | 688 | 39 | | var path = context.Request.Path; |
| | 688 | 40 | | var pathBase = context.Request.PathBase; |
| | 688 | 41 | | context.Request.Path = pathBase.Add(path); |
| | 688 | 42 | | context.Request.PathBase = ""; |
| | 688 | 43 | | Action restorePaths = () => |
| | 688 | 44 | | { |
| | 1 | 45 | | context.Request.PathBase = pathBase; |
| | 1 | 46 | | context.Request.Path = path; |
| | 689 | 47 | | }; |
| | 688 | 48 | | context.Items[RestorePathsDelegateItemName] = restorePaths; |
| | 688 | 49 | | await _branch(context); |
| | 686 | 50 | | } |
| | | 51 | | |
| | | 52 | | private Task BuildBranchAndInvoke(HttpContext request) |
| | | 53 | | { |
| | 0 | 54 | | EnsureBranchBuilt(); |
| | 0 | 55 | | return _branch(request); |
| | | 56 | | } |
| | | 57 | | |
| | | 58 | | private void EnsureBranchBuilt() |
| | | 59 | | { |
| | 382 | 60 | | lock (this) |
| | | 61 | | { |
| | 382 | 62 | | if (!_branchBuilt) |
| | | 63 | | { |
| | 382 | 64 | | _branch = BuildBranch(); |
| | 372 | 65 | | _branchBuilt = true; |
| | | 66 | | } |
| | 372 | 67 | | } |
| | 372 | 68 | | } |
| | | 69 | | |
| | | 70 | | private void ServiceBuilderOpenedCallback(object sender, EventArgs e) |
| | | 71 | | { |
| | 382 | 72 | | EnsureBranchBuilt(); |
| | 372 | 73 | | } |
| | | 74 | | |
| | | 75 | | private RequestDelegate BuildBranch() |
| | | 76 | | { |
| | 382 | 77 | | _logger.LogDebug("Building branch map"); |
| | 382 | 78 | | IApplicationBuilder branchApp = _app.New(); |
| | | 79 | | |
| | 1538 | 80 | | foreach (Type serviceType in _serviceBuilder.Services) |
| | | 81 | | { |
| | 392 | 82 | | System.Collections.Generic.List<IServiceDispatcher> dispatchers = _dispatcherBuilder.BuildDispatchers(se |
| | 1610 | 83 | | foreach (IServiceDispatcher dispatcher in dispatchers) |
| | | 84 | | { |
| | 423 | 85 | | if (dispatcher.BaseAddress == null) |
| | | 86 | | { |
| | | 87 | | // TODO: Should we throw? Ignore? |
| | | 88 | | continue; |
| | | 89 | | } |
| | | 90 | | |
| | 423 | 91 | | if (!(dispatcher.Binding is CustomBinding binding)) |
| | | 92 | | { |
| | 351 | 93 | | binding = new CustomBinding(dispatcher.Binding); |
| | | 94 | | } |
| | 423 | 95 | | if (binding.Elements.Find<HttpTransportBindingElement>() == null) |
| | | 96 | | { |
| | 0 | 97 | | _logger.LogDebug("Binding for address {baseAddress} is not an HTTP[S] binding so skipping", disp |
| | 0 | 98 | | continue; // Not an HTTP(S) dispatcher |
| | | 99 | | } |
| | | 100 | | |
| | 423 | 101 | | var parameters = new BindingParameterCollection |
| | 423 | 102 | | { |
| | 423 | 103 | | _app |
| | 423 | 104 | | }; |
| | 423 | 105 | | IServiceDispatcher serviceDispatcher = null; |
| | 423 | 106 | | System.Collections.Generic.IList<Type> supportedChannels = dispatcher.SupportedChannelTypes; |
| | 4230 | 107 | | for (int i = 0; i < supportedChannels.Count; i++) |
| | | 108 | | { |
| | 1692 | 109 | | Type channelType = supportedChannels[i]; |
| | 1692 | 110 | | if (channelType == typeof(IInputChannel)) |
| | | 111 | | { |
| | 2 | 112 | | if (binding.CanBuildServiceDispatcher<IInputChannel>(parameters)) |
| | | 113 | | { |
| | 0 | 114 | | serviceDispatcher = binding.BuildServiceDispatcher<IInputChannel>(parameters, dispatcher |
| | 0 | 115 | | break; |
| | | 116 | | } |
| | | 117 | | } |
| | 1692 | 118 | | if (channelType == typeof(IReplyChannel)) |
| | | 119 | | { |
| | 421 | 120 | | if (binding.CanBuildServiceDispatcher<IReplyChannel>(parameters)) |
| | | 121 | | { |
| | 413 | 122 | | serviceDispatcher = binding.BuildServiceDispatcher<IReplyChannel>(parameters, dispatcher |
| | | 123 | | } |
| | | 124 | | } |
| | 1692 | 125 | | if (channelType == typeof(IDuplexChannel)) |
| | | 126 | | { |
| | 422 | 127 | | if (binding.CanBuildServiceDispatcher<IDuplexChannel>(parameters)) |
| | | 128 | | { |
| | 0 | 129 | | serviceDispatcher = binding.BuildServiceDispatcher<IDuplexChannel>(parameters, dispatche |
| | | 130 | | } |
| | | 131 | | } |
| | 1692 | 132 | | if (channelType == typeof(IInputSessionChannel)) |
| | | 133 | | { |
| | 2 | 134 | | if (binding.CanBuildServiceDispatcher<IInputSessionChannel>(parameters)) |
| | | 135 | | { |
| | 0 | 136 | | serviceDispatcher = binding.BuildServiceDispatcher<IInputSessionChannel>(parameters, dis |
| | | 137 | | } |
| | | 138 | | } |
| | 1692 | 139 | | if (channelType == typeof(IReplySessionChannel)) |
| | | 140 | | { |
| | 422 | 141 | | if (binding.CanBuildServiceDispatcher<IReplySessionChannel>(parameters)) |
| | | 142 | | { |
| | 0 | 143 | | serviceDispatcher = binding.BuildServiceDispatcher<IReplySessionChannel>(parameters, dis |
| | | 144 | | } |
| | | 145 | | } |
| | 1692 | 146 | | if (channelType == typeof(IDuplexSessionChannel)) |
| | | 147 | | { |
| | 423 | 148 | | if (binding.CanBuildServiceDispatcher<IDuplexSessionChannel>(parameters)) |
| | | 149 | | { |
| | 22 | 150 | | serviceDispatcher = binding.BuildServiceDispatcher<IDuplexSessionChannel>(parameters, di |
| | | 151 | | } |
| | | 152 | | } |
| | | 153 | | } |
| | | 154 | | |
| | 423 | 155 | | if (serviceDispatcher is null) |
| | | 156 | | { |
| | 0 | 157 | | _logger.LogError("Unable to map CoreWCF branch app for path {path}", dispatcher.BaseAddress.Abso |
| | 0 | 158 | | continue; |
| | | 159 | | } |
| | | 160 | | |
| | 423 | 161 | | _logger.LogInformation("Mapping CoreWCF branch app for path {path}", dispatcher.BaseAddress.Absolute |
| | | 162 | | |
| | | 163 | | bool ExecHandler(HttpContext context) |
| | | 164 | | { |
| | 737 | 165 | | return (dispatcher.BaseAddress.AbsolutePath == "/" |
| | 737 | 166 | | || context.Request.Path.StartsWithSegments(dispatcher.BaseAddress.AbsolutePath, out _, ou |
| | 737 | 167 | | dispatcher.Binding.Scheme == context.Request.Scheme; |
| | | 168 | | } |
| | | 169 | | |
| | 423 | 170 | | branchApp.MapWhen(ExecHandler, wcfApp => |
| | 423 | 171 | | { |
| | 423 | 172 | | IServiceScopeFactory servicesScopeFactory = wcfApp.ApplicationServices.GetRequiredService<IServi |
| | 423 | 173 | | var requestHandler = new RequestDelegateHandler(serviceDispatcher, servicesScopeFactory); |
| | 423 | 174 | | if (requestHandler.IsAuthenticationRequired) |
| | 423 | 175 | | { |
| | 109 | 176 | | wcfApp.UseAuthentication(); |
| | 423 | 177 | | } |
| | 423 | 178 | | if (requestHandler.WebSocketOptions != null) |
| | 423 | 179 | | { |
| | 22 | 180 | | wcfApp.UseWebSockets(requestHandler.WebSocketOptions); |
| | 423 | 181 | | } |
| | 423 | 182 | | wcfApp.Run(requestHandler.HandleRequest); |
| | 846 | 183 | | }); |
| | | 184 | | } |
| | | 185 | | } |
| | | 186 | | |
| | 744 | 187 | | branchApp.Use(_ => { return reqContext => |
| | 744 | 188 | | { |
| | 1 | 189 | | if (reqContext.Items.TryGetValue(RestorePathsDelegateItemName, out object restorePathsDelegateAsObject)) |
| | 744 | 190 | | { |
| | 1 | 191 | | (restorePathsDelegateAsObject as Action)?.Invoke(); |
| | 744 | 192 | | } |
| | 744 | 193 | | else |
| | 744 | 194 | | { |
| | 0 | 195 | | _logger.LogWarning("RequestContext missing delegate with key {key}", RestorePathsDelegateItemName); |
| | 744 | 196 | | } |
| | 744 | 197 | | |
| | 1 | 198 | | return _next(reqContext); |
| | 744 | 199 | | }; }); |
| | 372 | 200 | | return branchApp.Build(); |
| | | 201 | | } |
| | | 202 | | } |
| | | 203 | | } |