| | | 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.ObjectModel; |
| | | 6 | | using System.Diagnostics; |
| | | 7 | | using System.IO; |
| | | 8 | | using System.Net; |
| | | 9 | | using System.Security.Authentication.ExtendedProtection; |
| | | 10 | | using System.Security.Claims; |
| | | 11 | | using System.Security.Principal; |
| | | 12 | | using System.Threading; |
| | | 13 | | using System.Threading.Tasks; |
| | | 14 | | using System.Xml; |
| | | 15 | | using CoreWCF.IdentityModel; |
| | | 16 | | using CoreWCF.IdentityModel.Policy; |
| | | 17 | | using CoreWCF.IdentityModel.Selectors; |
| | | 18 | | using CoreWCF.IdentityModel.Tokens; |
| | | 19 | | using CoreWCF.Runtime; |
| | | 20 | | using CoreWCF.Security; |
| | | 21 | | using Microsoft.AspNetCore.Authentication; |
| | | 22 | | using Microsoft.AspNetCore.Http; |
| | | 23 | | |
| | | 24 | | namespace CoreWCF.Channels |
| | | 25 | | { |
| | | 26 | | internal abstract class HttpRequestContext : RequestContextBase |
| | | 27 | | { |
| | | 28 | | private HttpOutput _httpOutput; |
| | | 29 | | private bool _errorGettingHttpInput; |
| | | 30 | | private SecurityMessageProperty _securityProperty; |
| | | 31 | | private readonly TaskCompletionSource<object> _replySentTcs; |
| | | 32 | | //EventTraceActivity eventTraceActivity; |
| | | 33 | | //ServerWebSocketTransportDuplexSessionChannel webSocketChannel; |
| | | 34 | | |
| | | 35 | | protected HttpRequestContext(IHttpTransportFactorySettings settings, Message requestMessage) |
| | 650 | 36 | | : base(requestMessage, settings.CloseTimeout, settings.SendTimeout) |
| | | 37 | | { |
| | 650 | 38 | | HttpTransportSettings = settings; |
| | 650 | 39 | | _replySentTcs = new TaskCompletionSource<object>(TaskContinuationOptions.RunContinuationsAsynchronously); |
| | 650 | 40 | | } |
| | | 41 | | |
| | | 42 | | public bool KeepAliveEnabled |
| | | 43 | | { |
| | | 44 | | get |
| | | 45 | | { |
| | 0 | 46 | | return HttpTransportSettings.KeepAliveEnabled; |
| | | 47 | | } |
| | | 48 | | } |
| | | 49 | | |
| | | 50 | | public abstract string HttpMethod { get; } |
| | | 51 | | |
| | | 52 | | //internal ServerWebSocketTransportDuplexSessionChannel WebSocketChannel |
| | | 53 | | //{ |
| | | 54 | | // get |
| | | 55 | | // { |
| | | 56 | | // return this.webSocketChannel; |
| | | 57 | | // } |
| | | 58 | | |
| | | 59 | | // set |
| | | 60 | | // { |
| | | 61 | | // Fx.Assert(this.webSocketChannel == null, "webSocketChannel should not be set twice."); |
| | | 62 | | // this.webSocketChannel = value; |
| | | 63 | | // } |
| | | 64 | | //} |
| | | 65 | | |
| | 4550 | 66 | | internal IHttpTransportFactorySettings HttpTransportSettings { get; } |
| | | 67 | | |
| | | 68 | | //internal EventTraceActivity EventTraceActivity |
| | | 69 | | //{ |
| | | 70 | | // get |
| | | 71 | | // { |
| | | 72 | | // return this.eventTraceActivity; |
| | | 73 | | // } |
| | | 74 | | //} |
| | | 75 | | |
| | | 76 | | // Note: This method will return null in the case where throwOnError is false, and a non-fatal error occurs. |
| | | 77 | | // Please exercise caution when passing in throwOnError = false. This should basically only be done in error |
| | | 78 | | // code paths, or code paths where there is very good reason that you would not want this method to throw. |
| | | 79 | | // When passing in throwOnError = false, please handle the case where this method returns null. |
| | | 80 | | public HttpInput GetHttpInput(bool throwOnError) |
| | | 81 | | { |
| | 650 | 82 | | HttpInput httpInput = null; |
| | 650 | 83 | | if (throwOnError || !_errorGettingHttpInput) |
| | | 84 | | { |
| | | 85 | | try |
| | | 86 | | { |
| | 650 | 87 | | httpInput = GetHttpInput(); |
| | 650 | 88 | | _errorGettingHttpInput = false; |
| | 650 | 89 | | } |
| | 0 | 90 | | catch (Exception e) |
| | | 91 | | { |
| | 0 | 92 | | _errorGettingHttpInput = true; |
| | 0 | 93 | | if (throwOnError || Fx.IsFatal(e)) |
| | | 94 | | { |
| | 0 | 95 | | throw; |
| | | 96 | | } |
| | | 97 | | |
| | 0 | 98 | | DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); |
| | 0 | 99 | | } |
| | | 100 | | } |
| | | 101 | | |
| | 650 | 102 | | return httpInput; |
| | | 103 | | } |
| | | 104 | | |
| | | 105 | | internal static HttpRequestContext CreateContext(IHttpTransportFactorySettings settings, HttpContext httpContext |
| | | 106 | | { |
| | 650 | 107 | | return new AspNetCoreHttpContext(settings, httpContext); |
| | | 108 | | } |
| | | 109 | | |
| | | 110 | | protected abstract Task<SecurityMessageProperty> OnProcessAuthenticationAsync(); |
| | | 111 | | |
| | | 112 | | public abstract HttpOutput GetHttpOutput(Message message); |
| | | 113 | | |
| | | 114 | | protected abstract HttpInput GetHttpInput(); |
| | | 115 | | |
| | | 116 | | public HttpOutput GetHttpOutputCore(Message message) |
| | | 117 | | { |
| | 650 | 118 | | if (_httpOutput != null) |
| | | 119 | | { |
| | 0 | 120 | | return _httpOutput; |
| | | 121 | | } |
| | | 122 | | |
| | 650 | 123 | | return GetHttpOutput(message); |
| | | 124 | | } |
| | | 125 | | |
| | | 126 | | protected override void OnAbort() |
| | | 127 | | { |
| | 0 | 128 | | if (_httpOutput != null) |
| | | 129 | | { |
| | 0 | 130 | | _httpOutput.Abort(HttpAbortReason.Aborted); |
| | | 131 | | } |
| | | 132 | | |
| | 0 | 133 | | Cleanup(); |
| | 0 | 134 | | _replySentTcs.TrySetResult(null); |
| | 0 | 135 | | } |
| | | 136 | | |
| | | 137 | | protected override async Task OnCloseAsync(CancellationToken token) |
| | | 138 | | { |
| | | 139 | | try |
| | | 140 | | { |
| | 648 | 141 | | if (_httpOutput != null) |
| | | 142 | | { |
| | 648 | 143 | | await _httpOutput.CloseAsync(); ; |
| | | 144 | | } |
| | 648 | 145 | | } |
| | | 146 | | finally |
| | | 147 | | { |
| | 648 | 148 | | Cleanup(); |
| | 648 | 149 | | _replySentTcs.TrySetResult(null); |
| | | 150 | | } |
| | 648 | 151 | | } |
| | | 152 | | |
| | | 153 | | protected virtual void Cleanup() |
| | | 154 | | { |
| | 648 | 155 | | } |
| | | 156 | | |
| | | 157 | | internal void SetMessage(Message message, Exception requestException) |
| | | 158 | | { |
| | 648 | 159 | | if ((message == null) && (requestException == null)) |
| | | 160 | | { |
| | 0 | 161 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( |
| | 0 | 162 | | new ProtocolException(SR.MessageXmlProtocolError, |
| | 0 | 163 | | new XmlException(SR.MessageIsEmpty))); |
| | | 164 | | } |
| | | 165 | | |
| | 648 | 166 | | TraceHttpMessageReceived(message); |
| | | 167 | | |
| | 648 | 168 | | if (requestException != null) |
| | | 169 | | { |
| | 1 | 170 | | SetRequestMessage(requestException); |
| | 1 | 171 | | message.Close(); |
| | | 172 | | } |
| | | 173 | | else |
| | | 174 | | { |
| | 647 | 175 | | message.Properties.Security = (_securityProperty != null) ? (SecurityMessageProperty)_securityProperty.C |
| | 647 | 176 | | SetRequestMessage(message); |
| | | 177 | | } |
| | 647 | 178 | | } |
| | | 179 | | |
| | | 180 | | private void TraceHttpMessageReceived(Message message) |
| | | 181 | | { |
| | 648 | 182 | | } |
| | | 183 | | |
| | | 184 | | protected abstract HttpStatusCode ValidateAuthentication(); |
| | | 185 | | |
| | | 186 | | private bool PrepareReply(ref Message message) |
| | | 187 | | { |
| | 650 | 188 | | bool closeOnReceivedEof = false; |
| | | 189 | | |
| | | 190 | | // null means we're done |
| | 650 | 191 | | if (message == null) |
| | | 192 | | { |
| | | 193 | | // A null message means either a one-way request or that the service operation returned null and |
| | | 194 | | // hence we can close the HttpOutput. By default we keep the HttpOutput open to allow the writing to the |
| | | 195 | | // even after the HttpInput EOF is received and the HttpOutput will be closed only on close of the HttpR |
| | 106 | 196 | | closeOnReceivedEof = true; |
| | 106 | 197 | | message = CreateAckMessage(HttpStatusCode.Accepted, string.Empty); |
| | | 198 | | } |
| | | 199 | | |
| | 650 | 200 | | if (!HttpTransportSettings.ManualAddressing) |
| | | 201 | | { |
| | 615 | 202 | | if (message.Version.Addressing == AddressingVersion.WSAddressingAugust2004) |
| | | 203 | | { |
| | 2 | 204 | | if (message.Headers.To == null || |
| | 2 | 205 | | (HttpTransportSettings.AnonymousUriPrefixMatcher as HttpAnonymousUriPrefixMatcher) == null || |
| | 2 | 206 | | !(HttpTransportSettings.AnonymousUriPrefixMatcher as HttpAnonymousUriPrefixMatcher).IsAnonymousU |
| | | 207 | | { |
| | 2 | 208 | | message.Headers.To = message.Version.Addressing.AnonymousUri; |
| | | 209 | | } |
| | | 210 | | } |
| | 613 | 211 | | else if (message.Version.Addressing == AddressingVersion.WSAddressing10 |
| | 613 | 212 | | || message.Version.Addressing == AddressingVersion.None) |
| | | 213 | | { |
| | 613 | 214 | | if (message.Headers.To != null && |
| | 613 | 215 | | (HttpTransportSettings.AnonymousUriPrefixMatcher as HttpAnonymousUriPrefixMatcher == null || |
| | 613 | 216 | | !(HttpTransportSettings.AnonymousUriPrefixMatcher as HttpAnonymousUriPrefixMatcher).IsAnonymousU |
| | | 217 | | { |
| | 0 | 218 | | message.Headers.To = null; |
| | | 219 | | } |
| | | 220 | | } |
| | | 221 | | else |
| | | 222 | | { |
| | 0 | 223 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( |
| | 0 | 224 | | new ProtocolException(SR.Format(SR.AddressingVersionNotSupported, message.Version.Addressing))); |
| | | 225 | | } |
| | | 226 | | } |
| | | 227 | | |
| | 650 | 228 | | message.Properties.AllowOutputBatching = false; |
| | 650 | 229 | | _httpOutput = GetHttpOutputCore(message); |
| | | 230 | | |
| | 650 | 231 | | return closeOnReceivedEof; |
| | | 232 | | } |
| | | 233 | | |
| | | 234 | | protected override async Task OnReplyAsync(Message message, CancellationToken token) |
| | | 235 | | { |
| | 650 | 236 | | Message responseMessage = message; |
| | | 237 | | |
| | | 238 | | try |
| | | 239 | | { |
| | 650 | 240 | | bool closeOutputAfterReply = PrepareReply(ref responseMessage); |
| | 650 | 241 | | await _httpOutput.SendAsync(token); |
| | | 242 | | |
| | 650 | 243 | | if (closeOutputAfterReply) |
| | | 244 | | { |
| | 106 | 245 | | await _httpOutput.CloseAsync(); |
| | | 246 | | } |
| | 650 | 247 | | } |
| | | 248 | | finally |
| | | 249 | | { |
| | 650 | 250 | | if (message != null && |
| | 650 | 251 | | !ReferenceEquals(message, responseMessage)) |
| | | 252 | | { |
| | 0 | 253 | | responseMessage.Close(); |
| | | 254 | | } |
| | | 255 | | } |
| | 650 | 256 | | } |
| | | 257 | | |
| | 618 | 258 | | public Task ReplySent => _replySentTcs.Task; |
| | | 259 | | |
| | | 260 | | public async Task<bool> ProcessAuthenticationAsync() |
| | | 261 | | { |
| | 650 | 262 | | HttpStatusCode statusCode = ValidateAuthentication(); |
| | | 263 | | |
| | 650 | 264 | | if (statusCode == HttpStatusCode.OK) |
| | | 265 | | { |
| | 650 | 266 | | bool authenticationSucceeded = false; |
| | 650 | 267 | | statusCode = HttpStatusCode.Forbidden; |
| | | 268 | | try |
| | | 269 | | { |
| | 650 | 270 | | _securityProperty = await OnProcessAuthenticationAsync(); |
| | 650 | 271 | | authenticationSucceeded = true; |
| | 650 | 272 | | return true; |
| | | 273 | | } |
| | 0 | 274 | | catch (Exception e) |
| | | 275 | | { |
| | 0 | 276 | | if (Fx.IsFatal(e)) |
| | | 277 | | { |
| | 0 | 278 | | throw; |
| | | 279 | | } |
| | | 280 | | |
| | 0 | 281 | | if (e.Data.Contains(HttpChannelUtilities.HttpStatusCodeKey)) |
| | | 282 | | { |
| | 0 | 283 | | if (e.Data[HttpChannelUtilities.HttpStatusCodeKey] is HttpStatusCode) |
| | | 284 | | { |
| | 0 | 285 | | statusCode = (HttpStatusCode)e.Data[HttpChannelUtilities.HttpStatusCodeKey]; |
| | | 286 | | } |
| | | 287 | | } |
| | | 288 | | |
| | 0 | 289 | | throw; |
| | | 290 | | } |
| | | 291 | | finally |
| | | 292 | | { |
| | 650 | 293 | | if (!authenticationSucceeded) |
| | | 294 | | { |
| | 0 | 295 | | await SendResponseAndCloseAsync(statusCode); |
| | | 296 | | } |
| | | 297 | | } |
| | | 298 | | } |
| | | 299 | | else |
| | | 300 | | { |
| | 0 | 301 | | await SendResponseAndCloseAsync(statusCode); |
| | 0 | 302 | | return false; |
| | | 303 | | } |
| | 650 | 304 | | } |
| | | 305 | | |
| | | 306 | | internal Task SendResponseAndCloseAsync(HttpStatusCode statusCode) |
| | | 307 | | { |
| | 31 | 308 | | return SendResponseAndCloseAsync(statusCode, string.Empty); |
| | | 309 | | } |
| | | 310 | | |
| | | 311 | | internal async Task SendResponseAndCloseAsync(HttpStatusCode statusCode, string statusDescription) |
| | | 312 | | { |
| | 32 | 313 | | if (ReplyInitiated) |
| | | 314 | | { |
| | 30 | 315 | | await CloseAsync(); |
| | 30 | 316 | | return; |
| | | 317 | | } |
| | | 318 | | |
| | 2 | 319 | | using (Message ackMessage = CreateAckMessage(statusCode, statusDescription)) |
| | | 320 | | { |
| | 2 | 321 | | await ReplyAsync(ackMessage); |
| | 2 | 322 | | } |
| | | 323 | | |
| | 2 | 324 | | await CloseAsync(); |
| | 32 | 325 | | } |
| | | 326 | | |
| | | 327 | | private Message CreateAckMessage(HttpStatusCode statusCode, string statusDescription) |
| | | 328 | | { |
| | 108 | 329 | | Message ackMessage = new NullMessage(); |
| | 108 | 330 | | HttpResponseMessageProperty httpResponseProperty = new HttpResponseMessageProperty |
| | 108 | 331 | | { |
| | 108 | 332 | | StatusCode = statusCode, |
| | 108 | 333 | | SuppressEntityBody = true |
| | 108 | 334 | | }; |
| | 108 | 335 | | if (statusDescription.Length > 0) |
| | | 336 | | { |
| | 1 | 337 | | httpResponseProperty.StatusDescription = statusDescription; |
| | | 338 | | } |
| | | 339 | | |
| | 108 | 340 | | ackMessage.Properties.Add(HttpResponseMessageProperty.Name, httpResponseProperty); |
| | | 341 | | |
| | 108 | 342 | | return ackMessage; |
| | | 343 | | } |
| | | 344 | | |
| | | 345 | | private class AspNetCoreHttpContext : HttpRequestContext |
| | | 346 | | { |
| | | 347 | | private const string Http11ProtocolString = "HTTP/1.1"; |
| | | 348 | | private readonly HttpContext _aspNetContext; |
| | | 349 | | // byte[] webSocketInternalBuffer; |
| | | 350 | | |
| | | 351 | | public AspNetCoreHttpContext(IHttpTransportFactorySettings settings, HttpContext aspNetContext) |
| | 650 | 352 | | : base(settings, null) |
| | | 353 | | { |
| | 650 | 354 | | _aspNetContext = aspNetContext; |
| | 650 | 355 | | } |
| | | 356 | | |
| | 650 | 357 | | public override string HttpMethod => _aspNetContext.Request.Method; |
| | | 358 | | |
| | | 359 | | protected override HttpInput GetHttpInput() |
| | | 360 | | { |
| | 650 | 361 | | return new AspNetCoreHttpInput(this); |
| | | 362 | | } |
| | | 363 | | |
| | | 364 | | public override HttpOutput GetHttpOutput(Message message) |
| | | 365 | | { |
| | 650 | 366 | | if (StringComparer.OrdinalIgnoreCase.Equals(Http11ProtocolString, _aspNetContext.Request.Protocol)) |
| | | 367 | | { |
| | 650 | 368 | | if (HttpTransportSettings.KeepAliveEnabled) |
| | | 369 | | { |
| | 650 | 370 | | _aspNetContext.Response.Headers["Connection"] = "keep-alive"; |
| | | 371 | | } |
| | | 372 | | else |
| | | 373 | | { |
| | 0 | 374 | | _aspNetContext.Response.Headers["Connection"] = "close"; |
| | | 375 | | } |
| | | 376 | | } |
| | | 377 | | |
| | 650 | 378 | | if (HttpTransportSettings.MessageEncoderFactory.Encoder is ICompressedMessageEncoder compressedMessageEn |
| | | 379 | | { |
| | 2 | 380 | | string acceptEncoding = _aspNetContext.Request.Headers[HttpChannelUtilities.AcceptEncodingHeader]; |
| | 2 | 381 | | compressedMessageEncoder.AddCompressedMessageProperties(message, acceptEncoding); |
| | | 382 | | } |
| | | 383 | | |
| | 650 | 384 | | return HttpOutput.CreateHttpOutput(_aspNetContext, HttpTransportSettings, message, HttpMethod); |
| | | 385 | | } |
| | | 386 | | |
| | | 387 | | protected override async Task<SecurityMessageProperty> OnProcessAuthenticationAsync() |
| | | 388 | | { |
| | 650 | 389 | | if (HttpTransportSettings.IsAuthenticationRequired) |
| | | 390 | | { |
| | 56 | 391 | | ServiceSecurityContext securityContext = await CreateSecurityContextAsync(_aspNetContext.User); |
| | 56 | 392 | | SecurityMessageProperty securityMessageProperty = new() |
| | 56 | 393 | | { |
| | 56 | 394 | | ServiceSecurityContext = securityContext |
| | 56 | 395 | | }; |
| | 56 | 396 | | return securityMessageProperty; |
| | | 397 | | } |
| | | 398 | | |
| | 594 | 399 | | return null; |
| | 650 | 400 | | } |
| | | 401 | | |
| | | 402 | | protected override HttpStatusCode ValidateAuthentication() |
| | | 403 | | { |
| | 650 | 404 | | if (HttpTransportSettings.IsAuthenticationRequired) |
| | | 405 | | { |
| | 56 | 406 | | return _aspNetContext.User.Identity.IsAuthenticated |
| | 56 | 407 | | ? HttpStatusCode.OK |
| | 56 | 408 | | : HttpStatusCode.Unauthorized; |
| | | 409 | | } |
| | | 410 | | |
| | 594 | 411 | | return HttpStatusCode.OK; |
| | | 412 | | //return Listener.ValidateAuthentication(listenerContext); |
| | | 413 | | } |
| | | 414 | | |
| | | 415 | | protected override void OnAbort() |
| | | 416 | | { |
| | 0 | 417 | | _aspNetContext.Abort(); |
| | 0 | 418 | | Cleanup(); |
| | 0 | 419 | | } |
| | | 420 | | |
| | | 421 | | protected override Task OnCloseAsync(CancellationToken token) |
| | | 422 | | { |
| | 648 | 423 | | return base.OnCloseAsync(token); |
| | | 424 | | //try |
| | | 425 | | //{ |
| | | 426 | | // // TODO: Work out how to close the HttpContext |
| | | 427 | | // // Most likely will be some mechanism to complete the Task returned by the RequestDelegate |
| | | 428 | | // aspNetContext.Response.Close(); |
| | | 429 | | //} |
| | | 430 | | //catch (HttpListenerException listenerException) |
| | | 431 | | //{ |
| | | 432 | | // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( |
| | | 433 | | // HttpChannelUtilities.CreateCommunicationException(listenerException)); |
| | | 434 | | //} |
| | | 435 | | } |
| | | 436 | | |
| | | 437 | | private async Task<ServiceSecurityContext> CreateSecurityContextAsync(IPrincipal principal) |
| | | 438 | | { |
| | 56 | 439 | | if (principal.Identity is WindowsIdentity wid) |
| | | 440 | | { |
| | 0 | 441 | | WindowsSecurityTokenAuthenticator tokenAuthenticator = new WindowsSecurityTokenAuthenticator(); |
| | 0 | 442 | | SecurityToken windowsToken = new WindowsSecurityToken(wid); |
| | 0 | 443 | | ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = await tokenAuthenticator.ValidateTo |
| | 0 | 444 | | return new ServiceSecurityContext(authorizationPolicies); |
| | | 445 | | } |
| | 56 | 446 | | else if (principal.Identity is GenericIdentity gid) |
| | | 447 | | { |
| | 0 | 448 | | WindowsSecurityTokenAuthenticator tokenAuthenticator = new WindowsSecurityTokenAuthenticator(); |
| | 0 | 449 | | SecurityToken genericToken = new GenericIdentitySecurityToken(gid, SecurityUniqueId.Create().Value); |
| | 0 | 450 | | ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = await tokenAuthenticator.ValidateTo |
| | 0 | 451 | | return new ServiceSecurityContext(authorizationPolicies); |
| | | 452 | | } |
| | 56 | 453 | | else if (principal.Identity is ClaimsIdentity) |
| | | 454 | | { |
| | 56 | 455 | | AuthorizationContext authorizationContext = AuthorizationContext.CreateDefaultAuthorizationContext(n |
| | 56 | 456 | | authorizationContext.Properties.Add(nameof(ClaimsPrincipal), principal); |
| | 56 | 457 | | return new ServiceSecurityContext(authorizationContext); |
| | | 458 | | } |
| | | 459 | | |
| | 0 | 460 | | return null; |
| | 56 | 461 | | } |
| | | 462 | | |
| | | 463 | | private class AspNetCoreHttpInput : HttpInput |
| | | 464 | | { |
| | | 465 | | private readonly AspNetCoreHttpContext _aspNetCoreHttpContext; |
| | | 466 | | private string _cachedContentType; // accessing the header in System.Net involves a native transition |
| | | 467 | | private byte[] _preReadBuffer; |
| | | 468 | | |
| | | 469 | | // TODO: ChannelBindingSupport |
| | | 470 | | public AspNetCoreHttpInput(AspNetCoreHttpContext aspNetCoreHttpContext) |
| | 650 | 471 | | : base(aspNetCoreHttpContext.HttpTransportSettings, true, false /* ChannelBindingSupportEnabled */) |
| | | 472 | | { |
| | 650 | 473 | | _aspNetCoreHttpContext = aspNetCoreHttpContext; |
| | 650 | 474 | | } |
| | | 475 | | |
| | | 476 | | protected override async Task CheckForContentAsync() |
| | | 477 | | { |
| | 650 | 478 | | if (!_aspNetCoreHttpContext._aspNetContext.Request.ContentLength.HasValue) |
| | | 479 | | { |
| | 44 | 480 | | _preReadBuffer = new byte[1]; |
| | | 481 | | // TODO: Look into useing PipeReader with look-ahead |
| | 44 | 482 | | if (await _aspNetCoreHttpContext._aspNetContext.Request.Body.ReadAsync(_preReadBuffer, 0, 1) == |
| | | 483 | | { |
| | 28 | 484 | | _preReadBuffer = null; |
| | | 485 | | } |
| | | 486 | | } |
| | 650 | 487 | | } |
| | | 488 | | |
| | | 489 | | // TODO: Switch to nullable |
| | 3082 | 490 | | public override long ContentLength => _aspNetCoreHttpContext._aspNetContext.Request.ContentLength ?? -1; |
| | | 491 | | |
| | | 492 | | protected override string ContentTypeCore |
| | | 493 | | { |
| | | 494 | | get |
| | | 495 | | { |
| | 2107 | 496 | | if (_cachedContentType == null) |
| | | 497 | | { |
| | 624 | 498 | | _cachedContentType = _aspNetCoreHttpContext._aspNetContext.Request.ContentType; |
| | | 499 | | } |
| | | 500 | | |
| | 2107 | 501 | | return _cachedContentType; |
| | | 502 | | } |
| | | 503 | | } |
| | | 504 | | |
| | 1299 | 505 | | protected override bool HasContent => _preReadBuffer != null || ContentLength > 0; |
| | | 506 | | |
| | 493 | 507 | | protected override string SoapActionHeader => _aspNetCoreHttpContext._aspNetContext.Request.Headers["SOA |
| | | 508 | | |
| | | 509 | | |
| | | 510 | | protected override ChannelBinding ChannelBinding |
| | | 511 | | { |
| | | 512 | | get |
| | | 513 | | { |
| | 0 | 514 | | throw new PlatformNotSupportedException("Shouldn't be able to request CBT"); // TODO: ChannelBin |
| | | 515 | | // return ChannelBindingUtility.GetToken(this.listenerHttpContext.listenerContext.Request.Transp |
| | | 516 | | } |
| | | 517 | | } |
| | | 518 | | |
| | | 519 | | protected override void AddProperties(Message message) |
| | | 520 | | { |
| | 648 | 521 | | HttpRequest request = _aspNetCoreHttpContext._aspNetContext.Request; |
| | 648 | 522 | | var requestProperty = new HttpRequestMessageProperty(_aspNetCoreHttpContext._aspNetContext); |
| | 648 | 523 | | message.Properties.Add(HttpRequestMessageProperty.Name, requestProperty); |
| | 648 | 524 | | String hostAddress = String.Concat(request.IsHttps ? "https://" : "http://", request.Host.HasValue ? |
| | | 525 | | // TODO: Test the Via code |
| | 648 | 526 | | message.Properties.Via = new Uri(string.Concat( |
| | 648 | 527 | | hostAddress, |
| | 648 | 528 | | request.PathBase.ToUriComponent(), |
| | 648 | 529 | | request.Path.ToUriComponent(), |
| | 648 | 530 | | request.QueryString.ToUriComponent())); |
| | | 531 | | |
| | 648 | 532 | | IPAddress remoteIPAddress = request.HttpContext.Connection.RemoteIpAddress; |
| | 648 | 533 | | int remotePort = request.HttpContext.Connection.RemotePort; |
| | 648 | 534 | | remotePort &= 0x0FFFF; // Ensure port is a valid 16-bit value. Work around for dotnet/aspnetcore#621 |
| | | 535 | | |
| | 648 | 536 | | if (remoteIPAddress != null) |
| | | 537 | | { |
| | 616 | 538 | | RemoteEndpointMessageProperty remoteEndpointProperty = new RemoteEndpointMessageProperty(new IPE |
| | 616 | 539 | | message.Properties.Add(RemoteEndpointMessageProperty.Name, remoteEndpointProperty); |
| | | 540 | | } |
| | 648 | 541 | | } |
| | | 542 | | |
| | | 543 | | protected override Stream GetInputStream() |
| | | 544 | | { |
| | 621 | 545 | | if (_preReadBuffer != null) |
| | | 546 | | { |
| | 16 | 547 | | return new AspNetCoreInputStream(_aspNetCoreHttpContext, _preReadBuffer); |
| | | 548 | | } |
| | | 549 | | else |
| | | 550 | | { |
| | 605 | 551 | | return new AspNetCoreInputStream(_aspNetCoreHttpContext); |
| | | 552 | | } |
| | | 553 | | } |
| | | 554 | | |
| | | 555 | | private class AspNetCoreInputStream : DetectEofStream |
| | | 556 | | { |
| | | 557 | | public AspNetCoreInputStream(AspNetCoreHttpContext aspNetCoreHttpContext) |
| | 605 | 558 | | : base(aspNetCoreHttpContext._aspNetContext.Request.Body) |
| | | 559 | | { |
| | 605 | 560 | | } |
| | | 561 | | |
| | | 562 | | public AspNetCoreInputStream(AspNetCoreHttpContext aspNetCoreHttpContext, byte[] preReadBuffer) |
| | 16 | 563 | | : base(new PreReadStream(aspNetCoreHttpContext._aspNetContext.Request.Body, preReadBuffer)) |
| | | 564 | | { |
| | 16 | 565 | | } |
| | | 566 | | |
| | | 567 | | public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, |
| | | 568 | | { |
| | | 569 | | try |
| | | 570 | | { |
| | 0 | 571 | | return base.BeginRead(buffer, offset, count, callback, state); |
| | | 572 | | } |
| | 0 | 573 | | catch (Exception exception) |
| | | 574 | | { |
| | 0 | 575 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( |
| | 0 | 576 | | HttpChannelUtilities.CreateCommunicationException(exception)); |
| | | 577 | | } |
| | 0 | 578 | | } |
| | | 579 | | |
| | | 580 | | public override int EndRead(IAsyncResult result) |
| | | 581 | | { |
| | | 582 | | try |
| | | 583 | | { |
| | 0 | 584 | | return base.EndRead(result); |
| | | 585 | | } |
| | 0 | 586 | | catch (Exception exception) |
| | | 587 | | { |
| | 0 | 588 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( |
| | 0 | 589 | | HttpChannelUtilities.CreateCommunicationException(exception)); |
| | | 590 | | } |
| | 0 | 591 | | } |
| | | 592 | | |
| | | 593 | | public override int Read(byte[] buffer, int offset, int count) |
| | | 594 | | { |
| | | 595 | | try |
| | | 596 | | { |
| | 11513 | 597 | | return base.Read(buffer, offset, count); |
| | | 598 | | } |
| | 0 | 599 | | catch (Exception exception) |
| | | 600 | | { |
| | 0 | 601 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( |
| | 0 | 602 | | HttpChannelUtilities.CreateCommunicationException(exception)); |
| | | 603 | | } |
| | 11513 | 604 | | } |
| | | 605 | | |
| | | 606 | | public override int ReadByte() |
| | | 607 | | { |
| | | 608 | | try |
| | | 609 | | { |
| | 11406 | 610 | | return base.ReadByte(); |
| | | 611 | | } |
| | 0 | 612 | | catch (Exception exception) |
| | | 613 | | { |
| | 0 | 614 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( |
| | 0 | 615 | | HttpChannelUtilities.CreateCommunicationException(exception)); |
| | | 616 | | } |
| | 11406 | 617 | | } |
| | | 618 | | } |
| | | 619 | | } |
| | | 620 | | } |
| | | 621 | | } |
| | | 622 | | } |