1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
4 namespace System.ServiceModel.Channels
6 using System.Collections.Generic;
7 using System.Collections.ObjectModel;
8 using System.Diagnostics;
9 using System.Globalization;
10 using System.IdentityModel.Policy;
11 using System.IdentityModel.Selectors;
12 using System.IdentityModel.Tokens;
15 using System.Net.Http;
16 using System.Net.WebSockets;
18 using System.Runtime.CompilerServices;
19 using System.Runtime.Diagnostics;
20 using System.Security.Authentication.ExtendedProtection;
21 using System.Security.Cryptography.X509Certificates;
22 using System.Security.Principal;
23 using System.ServiceModel;
24 using System.ServiceModel.Activation;
25 using System.ServiceModel.Description;
26 using System.ServiceModel.Diagnostics;
27 using System.ServiceModel.Diagnostics.Application;
28 using System.ServiceModel.Dispatcher;
29 using System.ServiceModel.Security;
30 using System.ServiceModel.Security.Tokens;
31 using System.Threading;
32 using System.Threading.Tasks;
35 abstract class HttpChannelListener : TransportChannelListener,
36 IHttpTransportFactorySettings
38 AuthenticationSchemes authenticationScheme;
39 bool extractGroupsForWindowsAccounts;
40 EndpointIdentity identity;
41 bool keepAliveEnabled;
43 readonly int maxPendingAccepts;
46 readonly TimeSpan requestInitializationTimeout;
47 TransferMode transferMode;
48 bool unsafeConnectionNtlmAuthentication;
49 ISecurityCapabilities securityCapabilities;
51 SecurityCredentialsManager credentialProvider;
52 SecurityTokenAuthenticator userNameTokenAuthenticator;
53 SecurityTokenAuthenticator windowsTokenAuthenticator;
54 ExtendedProtectionPolicy extendedProtectionPolicy;
55 bool usingDefaultSpnList;
56 HttpAnonymousUriPrefixMatcher anonymousUriPrefixMatcher;
58 HttpMessageSettings httpMessageSettings;
59 WebSocketTransportSettings webSocketSettings;
61 static UriPrefixTable<ITransportManagerRegistration> transportManagerTable =
62 new UriPrefixTable<ITransportManagerRegistration>(true);
64 public HttpChannelListener(HttpTransportBindingElement bindingElement, BindingContext context)
65 : base(bindingElement, context, HttpTransportDefaults.GetDefaultMessageEncoderFactory(),
66 bindingElement.HostNameComparisonMode)
68 if (bindingElement.TransferMode == TransferMode.Buffered)
70 if (bindingElement.MaxReceivedMessageSize > int.MaxValue)
72 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
73 new ArgumentOutOfRangeException("bindingElement.MaxReceivedMessageSize",
74 SR.GetString(SR.MaxReceivedMessageSizeMustBeInIntegerRange)));
77 if (bindingElement.MaxBufferSize != bindingElement.MaxReceivedMessageSize)
79 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement",
80 SR.GetString(SR.MaxBufferSizeMustMatchMaxReceivedMessageSize));
85 if (bindingElement.MaxBufferSize > bindingElement.MaxReceivedMessageSize)
87 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement",
88 SR.GetString(SR.MaxBufferSizeMustNotExceedMaxReceivedMessageSize));
92 if (bindingElement.AuthenticationScheme.IsSet(AuthenticationSchemes.Basic) &&
93 bindingElement.AuthenticationScheme.IsNotSet(AuthenticationSchemes.Digest | AuthenticationSchemes.Ntlm | AuthenticationSchemes.Negotiate) &&
94 bindingElement.ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.Always)
96 //Basic auth + PolicyEnforcement.Always doesn't make sense because basic auth can't support CBT.
97 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ExtendedProtectionPolicyBasicAuthNotSupported)));
100 this.authenticationScheme = bindingElement.AuthenticationScheme;
101 this.keepAliveEnabled = bindingElement.KeepAliveEnabled;
102 this.InheritBaseAddressSettings = bindingElement.InheritBaseAddressSettings;
103 this.maxBufferSize = bindingElement.MaxBufferSize;
104 this.maxPendingAccepts = HttpTransportDefaults.GetEffectiveMaxPendingAccepts(bindingElement.MaxPendingAccepts);
105 this.method = bindingElement.Method;
106 this.realm = bindingElement.Realm;
107 this.requestInitializationTimeout = bindingElement.RequestInitializationTimeout;
108 this.transferMode = bindingElement.TransferMode;
109 this.unsafeConnectionNtlmAuthentication = bindingElement.UnsafeConnectionNtlmAuthentication;
110 this.credentialProvider = context.BindingParameters.Find<SecurityCredentialsManager>();
111 this.securityCapabilities = bindingElement.GetProperty<ISecurityCapabilities>(context);
112 this.extendedProtectionPolicy = GetPolicyWithDefaultSpnCollection(bindingElement.ExtendedProtectionPolicy, this.authenticationScheme, this.HostNameComparisonModeInternal, base.Uri, out this.usingDefaultSpnList);
114 this.webSocketSettings = WebSocketHelper.GetRuntimeWebSocketSettings(bindingElement.WebSocketSettings);
116 if (bindingElement.AnonymousUriPrefixMatcher != null)
118 this.anonymousUriPrefixMatcher = new HttpAnonymousUriPrefixMatcher(bindingElement.AnonymousUriPrefixMatcher);
121 this.httpMessageSettings = context.BindingParameters.Find<HttpMessageSettings>() ?? new HttpMessageSettings();
123 if (this.httpMessageSettings.HttpMessagesSupported && this.MessageVersion != MessageVersion.None)
125 throw FxTrace.Exception.AsError(
126 new NotSupportedException(SR.GetString(
127 SR.MessageVersionNoneRequiredForHttpMessageSupport,
128 typeof(HttpRequestMessage).Name,
129 typeof(HttpResponseMessage).Name,
130 typeof(HttpMessageSettings).Name,
131 typeof(MessageVersion).Name,
132 typeof(MessageEncodingBindingElement).Name,
133 this.MessageVersion.ToString(),
134 MessageVersion.None.ToString())));
138 public TimeSpan RequestInitializationTimeout
140 get { return this.requestInitializationTimeout; }
143 public WebSocketTransportSettings WebSocketSettings
145 get { return this.webSocketSettings; }
148 public HttpMessageSettings HttpMessageSettings
150 get { return this.httpMessageSettings; }
153 public ExtendedProtectionPolicy ExtendedProtectionPolicy
157 return this.extendedProtectionPolicy;
161 public virtual bool IsChannelBindingSupportEnabled
169 public abstract bool UseWebSocketTransport { get; }
171 internal HttpAnonymousUriPrefixMatcher AnonymousUriPrefixMatcher
175 return this.anonymousUriPrefixMatcher;
179 protected SecurityTokenAuthenticator UserNameTokenAuthenticator
181 get { return this.userNameTokenAuthenticator; }
184 internal override void ApplyHostedContext(string virtualPath, bool isMetadataListener)
186 base.ApplyHostedContext(virtualPath, isMetadataListener);
187 AspNetEnvironment.Current.ValidateHttpSettings(virtualPath, isMetadataListener, this.usingDefaultSpnList, ref this.authenticationScheme, ref this.extendedProtectionPolicy, ref this.realm);
190 public AuthenticationSchemes AuthenticationScheme
194 return this.authenticationScheme;
198 public bool KeepAliveEnabled
202 return this.keepAliveEnabled;
206 public bool ExtractGroupsForWindowsAccounts
210 return this.extractGroupsForWindowsAccounts;
214 public HostNameComparisonMode HostNameComparisonMode
218 return this.HostNameComparisonModeInternal;
222 //Returns true if one of the non-anonymous authentication schemes is set on this.AuthenticationScheme
223 protected bool IsAuthenticationSupported
227 return this.authenticationScheme != AuthenticationSchemes.Anonymous;
231 bool IsAuthenticationRequired
235 return this.AuthenticationScheme.IsNotSet(AuthenticationSchemes.Anonymous);
239 public int MaxBufferSize
243 return this.maxBufferSize;
247 public int MaxPendingAccepts
249 get { return this.maxPendingAccepts; }
252 public virtual string Method
260 public TransferMode TransferMode
270 get { return this.realm; }
273 int IHttpTransportFactorySettings.MaxBufferSize
275 get { return MaxBufferSize; }
278 TransferMode IHttpTransportFactorySettings.TransferMode
280 get { return TransferMode; }
283 public override string Scheme
285 get { return Uri.UriSchemeHttp; }
288 internal static UriPrefixTable<ITransportManagerRegistration> StaticTransportManagerTable
292 return transportManagerTable;
296 public bool UnsafeConnectionNtlmAuthentication
300 return this.unsafeConnectionNtlmAuthentication;
304 internal override UriPrefixTable<ITransportManagerRegistration> TransportManagerTable
308 return transportManagerTable;
312 internal override ITransportManagerRegistration CreateTransportManagerRegistration(Uri listenUri)
314 return new SharedHttpTransportManager(listenUri, this);
317 string GetAuthType(HttpListenerContext listenerContext)
319 string authType = null;
320 IPrincipal principal = listenerContext.User;
321 if ((principal != null) && (principal.Identity != null))
323 authType = principal.Identity.AuthenticationType;
328 protected string GetAuthType(IHttpAuthenticationContext authenticationContext)
330 string authType = null;
331 if (authenticationContext.LogonUserIdentity != null)
333 authType = authenticationContext.LogonUserIdentity.AuthenticationType;
338 bool IsAuthSchemeValid(string authType)
340 return AuthenticationSchemesHelper.DoesAuthTypeMatch(this.authenticationScheme, authType);
343 internal override int GetMaxBufferSize()
345 return MaxBufferSize;
348 public override T GetProperty<T>()
350 if (typeof(T) == typeof(EndpointIdentity))
352 return (T)(object)(this.identity);
354 else if (typeof(T) == typeof(ILogonTokenCacheManager))
356 object cacheManager = (object)GetIdentityModelProperty<T>();
357 if (cacheManager != null)
359 return (T)cacheManager;
362 else if (typeof(T) == typeof(ISecurityCapabilities))
364 return (T)(object)this.securityCapabilities;
366 else if (typeof(T) == typeof(ExtendedProtectionPolicy))
368 return (T)(object)this.extendedProtectionPolicy;
371 return base.GetProperty<T>();
374 [MethodImpl(MethodImplOptions.NoInlining)]
375 T GetIdentityModelProperty<T>()
377 if (typeof(T) == typeof(EndpointIdentity))
379 if (this.identity == null)
381 if (this.authenticationScheme.IsSet(AuthenticationSchemes.Negotiate) ||
382 this.authenticationScheme.IsSet(AuthenticationSchemes.Ntlm))
384 this.identity = SecurityUtils.CreateWindowsIdentity();
388 return (T)(object)this.identity;
390 else if (typeof(T) == typeof(ILogonTokenCacheManager)
391 && (this.userNameTokenAuthenticator != null))
393 ILogonTokenCacheManager retVal = this.userNameTokenAuthenticator as ILogonTokenCacheManager;
397 return (T)(object)retVal;
404 internal abstract IAsyncResult BeginHttpContextReceived(
405 HttpRequestContext context,
406 Action acceptorCallback,
407 AsyncCallback callback,
410 internal abstract bool EndHttpContextReceived(IAsyncResult result);
412 [MethodImpl(MethodImplOptions.NoInlining)]
413 void InitializeSecurityTokenAuthenticator()
415 Fx.Assert(this.IsAuthenticationSupported, "SecurityTokenAuthenticator should only be initialized when authentication is supported.");
416 ServiceCredentials serviceCredentials = this.credentialProvider as ServiceCredentials;
418 if (serviceCredentials != null)
420 if (this.AuthenticationScheme == AuthenticationSchemes.Basic)
422 // when Basic authentiction is enabled - but Digest and Windows are disabled use the UsernameAuthenticationSetting
423 this.extractGroupsForWindowsAccounts = serviceCredentials.UserNameAuthentication.IncludeWindowsGroups;
427 if (this.AuthenticationScheme.IsSet(AuthenticationSchemes.Basic) &&
428 serviceCredentials.UserNameAuthentication.IncludeWindowsGroups != serviceCredentials.WindowsAuthentication.IncludeWindowsGroups)
430 // Ensure there are no inconsistencies when Basic and (Digest and/or Ntlm and/or Negotiate) are both enabled
431 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SecurityTokenProviderIncludeWindowsGroupsInconsistent,
432 (AuthenticationSchemes)authenticationScheme - AuthenticationSchemes.Basic,
433 serviceCredentials.UserNameAuthentication.IncludeWindowsGroups,
434 serviceCredentials.WindowsAuthentication.IncludeWindowsGroups)));
437 this.extractGroupsForWindowsAccounts = serviceCredentials.WindowsAuthentication.IncludeWindowsGroups;
440 // we will only support custom and windows validation modes, if anything else is specified, we'll fall back to windows user name.
441 if (serviceCredentials.UserNameAuthentication.UserNamePasswordValidationMode == UserNamePasswordValidationMode.Custom)
443 this.userNameTokenAuthenticator = new CustomUserNameSecurityTokenAuthenticator(serviceCredentials.UserNameAuthentication.GetUserNamePasswordValidator());
447 if (serviceCredentials.UserNameAuthentication.CacheLogonTokens)
449 this.userNameTokenAuthenticator = new WindowsUserNameCachingSecurityTokenAuthenticator(this.extractGroupsForWindowsAccounts,
450 serviceCredentials.UserNameAuthentication.MaxCachedLogonTokens, serviceCredentials.UserNameAuthentication.CachedLogonTokenLifetime);
454 this.userNameTokenAuthenticator = new WindowsUserNameSecurityTokenAuthenticator(this.extractGroupsForWindowsAccounts);
460 this.extractGroupsForWindowsAccounts = TransportDefaults.ExtractGroupsForWindowsAccounts;
461 this.userNameTokenAuthenticator = new WindowsUserNameSecurityTokenAuthenticator(this.extractGroupsForWindowsAccounts);
464 this.windowsTokenAuthenticator = new WindowsSecurityTokenAuthenticator(this.extractGroupsForWindowsAccounts);
467 protected override void OnOpened()
471 if (this.IsAuthenticationSupported)
473 InitializeSecurityTokenAuthenticator();
474 this.identity = GetIdentityModelProperty<EndpointIdentity>();
478 [MethodImpl(MethodImplOptions.NoInlining)]
479 protected void CloseUserNameTokenAuthenticator(TimeSpan timeout)
481 SecurityUtils.CloseTokenAuthenticatorIfRequired(this.userNameTokenAuthenticator, timeout);
484 [MethodImpl(MethodImplOptions.NoInlining)]
485 protected void AbortUserNameTokenAuthenticator()
487 SecurityUtils.AbortTokenAuthenticatorIfRequired(this.userNameTokenAuthenticator);
490 bool ShouldProcessAuthentication(IHttpAuthenticationContext authenticationContext)
492 Fx.Assert(authenticationContext != null, "IsAuthenticated should only be called if authenticationContext != null");
493 Fx.Assert(authenticationContext.LogonUserIdentity != null, "IsAuthenticated should only be called if authenticationContext.LogonUserIdentity != null");
494 return this.IsAuthenticationRequired || (this.IsAuthenticationSupported && authenticationContext.LogonUserIdentity.IsAuthenticated);
497 bool ShouldProcessAuthentication(HttpListenerContext listenerContext)
499 Fx.Assert(listenerContext != null, "IsAuthenticated should only be called if listenerContext != null");
500 Fx.Assert(listenerContext.Request != null, "IsAuthenticated should only be called if listenerContext.Request != null");
501 return this.IsAuthenticationRequired || (this.IsAuthenticationSupported && listenerContext.Request.IsAuthenticated);
504 public virtual SecurityMessageProperty ProcessAuthentication(IHttpAuthenticationContext authenticationContext)
506 if (this.ShouldProcessAuthentication(authenticationContext))
508 SecurityMessageProperty retValue;
511 retValue = this.ProcessAuthentication(authenticationContext.LogonUserIdentity, GetAuthType(authenticationContext));
513 #pragma warning suppress 56500 // covered by FXCop
514 catch (Exception exception)
516 if (Fx.IsFatal(exception))
519 // Audit Authentication failure
520 if (AuditLevel.Failure == (this.AuditBehavior.MessageAuthenticationAuditLevel & AuditLevel.Failure))
521 WriteAuditEvent(AuditLevel.Failure, (authenticationContext.LogonUserIdentity != null) ? authenticationContext.LogonUserIdentity.Name : String.Empty, exception);
526 // Audit Authentication success
527 if (AuditLevel.Success == (this.AuditBehavior.MessageAuthenticationAuditLevel & AuditLevel.Success))
528 WriteAuditEvent(AuditLevel.Success, (authenticationContext.LogonUserIdentity != null) ? authenticationContext.LogonUserIdentity.Name : String.Empty, null);
538 public virtual SecurityMessageProperty ProcessAuthentication(HttpListenerContext listenerContext)
540 if (this.ShouldProcessAuthentication(listenerContext))
542 return this.ProcessRequiredAuthentication(listenerContext);
550 SecurityMessageProperty ProcessRequiredAuthentication(HttpListenerContext listenerContext)
552 SecurityMessageProperty retValue;
553 HttpListenerBasicIdentity identity = null;
554 WindowsIdentity wid = null;
557 Fx.Assert(listenerContext.User != null, "HttpListener delivered authenticated request without an IPrincipal.");
558 wid = listenerContext.User.Identity as WindowsIdentity;
560 if (this.AuthenticationScheme.IsSet(AuthenticationSchemes.Basic)
563 identity = listenerContext.User.Identity as HttpListenerBasicIdentity;
564 Fx.Assert(identity != null, "HttpListener delivered Basic authenticated request with a non-Basic IIdentity.");
565 retValue = this.ProcessAuthentication(identity);
569 Fx.Assert(wid != null, "HttpListener delivered non-Basic authenticated request with a non-Windows IIdentity.");
570 retValue = this.ProcessAuthentication(wid, GetAuthType(listenerContext));
573 #pragma warning suppress 56500 // covered by FXCop
574 catch (Exception exception)
576 if (!Fx.IsFatal(exception))
578 // Audit Authentication failure
579 if (AuditLevel.Failure == (this.AuditBehavior.MessageAuthenticationAuditLevel & AuditLevel.Failure))
581 WriteAuditEvent(AuditLevel.Failure, (identity != null) ? identity.Name : ((wid != null) ? wid.Name : String.Empty), exception);
587 // Audit Authentication success
588 if (AuditLevel.Success == (this.AuditBehavior.MessageAuthenticationAuditLevel & AuditLevel.Success))
590 WriteAuditEvent(AuditLevel.Success, (identity != null) ? identity.Name : ((wid != null) ? wid.Name : String.Empty), null);
596 protected override bool TryGetTransportManagerRegistration(HostNameComparisonMode hostNameComparisonMode,
597 out ITransportManagerRegistration registration)
599 if (this.TransportManagerTable.TryLookupUri(this.Uri, hostNameComparisonMode, out registration))
601 HttpTransportManager httpTransportManager = registration as HttpTransportManager;
602 if (httpTransportManager != null && httpTransportManager.IsHosted)
606 // Due to HTTP.SYS behavior, we don't reuse registrations from a higher point in the URI hierarchy.
607 if (registration.ListenUri.Segments.Length >= this.BaseUri.Segments.Length)
615 protected void WriteAuditEvent(AuditLevel auditLevel, string primaryIdentity, Exception exception)
619 if (auditLevel == AuditLevel.Success)
621 SecurityAuditHelper.WriteTransportAuthenticationSuccessEvent(this.AuditBehavior.AuditLogLocation,
622 this.AuditBehavior.SuppressAuditFailure, null, this.Uri, primaryIdentity);
626 SecurityAuditHelper.WriteTransportAuthenticationFailureEvent(this.AuditBehavior.AuditLogLocation,
627 this.AuditBehavior.SuppressAuditFailure, null, this.Uri, primaryIdentity, exception);
630 #pragma warning suppress 56500
631 catch (Exception auditException)
633 if (Fx.IsFatal(auditException) || auditLevel == AuditLevel.Success)
636 DiagnosticUtility.TraceHandledException(auditException, TraceEventType.Error);
640 SecurityMessageProperty ProcessAuthentication(HttpListenerBasicIdentity identity)
642 SecurityToken securityToken = new UserNameSecurityToken(identity.Name, identity.Password);
643 ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = this.userNameTokenAuthenticator.ValidateToken(securityToken);
644 SecurityMessageProperty security = new SecurityMessageProperty();
645 security.TransportToken = new SecurityTokenSpecification(securityToken, authorizationPolicies);
646 security.ServiceSecurityContext = new ServiceSecurityContext(authorizationPolicies);
650 SecurityMessageProperty ProcessAuthentication(WindowsIdentity identity, string authenticationType)
652 SecurityUtils.ValidateAnonymityConstraint(identity, false);
653 SecurityToken securityToken = new WindowsSecurityToken(identity, SecurityUniqueId.Create().Value, authenticationType);
654 ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = this.windowsTokenAuthenticator.ValidateToken(securityToken);
655 SecurityMessageProperty security = new SecurityMessageProperty();
656 security.TransportToken = new SecurityTokenSpecification(securityToken, authorizationPolicies);
657 security.ServiceSecurityContext = new ServiceSecurityContext(authorizationPolicies);
661 HttpStatusCode ValidateAuthentication(string authType)
663 if (this.IsAuthSchemeValid(authType))
665 return HttpStatusCode.OK;
669 // Audit Authentication failure
670 if (AuditLevel.Failure == (this.AuditBehavior.MessageAuthenticationAuditLevel & AuditLevel.Failure))
672 string message = SR.GetString(SR.HttpAuthenticationFailed, this.AuthenticationScheme, HttpStatusCode.Unauthorized);
673 Exception exception = DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(message));
674 WriteAuditEvent(AuditLevel.Failure, String.Empty, exception);
677 return HttpStatusCode.Unauthorized;
681 public virtual HttpStatusCode ValidateAuthentication(IHttpAuthenticationContext authenticationContext)
683 HttpStatusCode result = HttpStatusCode.OK;
685 if (this.IsAuthenticationSupported)
687 string authType = GetAuthType(authenticationContext);
688 result = ValidateAuthentication(authType);
691 if (result == HttpStatusCode.OK &&
692 authenticationContext.LogonUserIdentity != null &&
693 authenticationContext.LogonUserIdentity.IsAuthenticated &&
694 this.ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.Always &&
695 !authenticationContext.IISSupportsExtendedProtection)
697 Exception exception = DiagnosticUtility.ExceptionUtility.ThrowHelperError(
698 new PlatformNotSupportedException(SR.GetString(SR.ExtendedProtectionNotSupported)));
699 WriteAuditEvent(AuditLevel.Failure, String.Empty, exception);
701 result = HttpStatusCode.Unauthorized;
707 public virtual HttpStatusCode ValidateAuthentication(HttpListenerContext listenerContext)
709 HttpStatusCode result = HttpStatusCode.OK;
711 if (this.IsAuthenticationSupported)
713 string authType = GetAuthType(listenerContext);
714 result = ValidateAuthentication(authType);
720 static ExtendedProtectionPolicy GetPolicyWithDefaultSpnCollection(ExtendedProtectionPolicy policy, AuthenticationSchemes authenticationScheme, HostNameComparisonMode hostNameComparisonMode, Uri listenUri, out bool usingDefaultSpnList)
722 if (policy.PolicyEnforcement != PolicyEnforcement.Never &&
723 policy.CustomServiceNames == null && //null indicates "use default"
724 policy.CustomChannelBinding == null && //not needed if a channel binding is provided.
725 authenticationScheme != AuthenticationSchemes.Anonymous && //SPN list only needed with authentication (mixed mode uses own default list)
726 string.Equals(listenUri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase))//SPN list not used for HTTPS (CBT is used instead).
728 usingDefaultSpnList = true;
729 return new ExtendedProtectionPolicy(policy.PolicyEnforcement, policy.ProtectionScenario, GetDefaultSpnList(hostNameComparisonMode, listenUri));
732 usingDefaultSpnList = false;
736 static ServiceNameCollection GetDefaultSpnList(HostNameComparisonMode hostNameComparisonMode, Uri listenUri)
738 //In 3.5 SP1, we started sending the HOST/xyz format, so we have to accept it for compat reasons.
739 //with this change, we will be changing our client so that it lets System.Net pick the SPN by default
740 //which will usually mean they use the HTTP/xyz format, which is more likely to interop with
741 //other web service stacks that support windows auth...
742 const string hostSpnFormat = "HOST/{0}";
743 const string httpSpnFormat = "HTTP/{0}";
744 const string localhost = "localhost";
746 Dictionary<string, string> serviceNames = new Dictionary<string, string>();
748 string hostName = null;
749 string dnsSafeHostName = listenUri.DnsSafeHost;
751 switch (hostNameComparisonMode)
753 case HostNameComparisonMode.Exact:
754 UriHostNameType hostNameType = listenUri.HostNameType;
755 if (hostNameType == UriHostNameType.IPv4 || hostNameType == UriHostNameType.IPv6)
757 hostName = Dns.GetHostEntry(string.Empty).HostName;
758 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, hostSpnFormat, hostName));
759 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, httpSpnFormat, hostName));
763 if (listenUri.DnsSafeHost.Contains("."))
765 //since we are listening explicitly on the FQDN, we should add only the FQDN SPN
766 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, hostSpnFormat, dnsSafeHostName));
767 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, httpSpnFormat, dnsSafeHostName));
771 hostName = Dns.GetHostEntry(string.Empty).HostName;
772 //add the short name (from the URI) and the FQDN (from Dns)
773 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, hostSpnFormat, dnsSafeHostName));
774 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, httpSpnFormat, dnsSafeHostName));
775 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, hostSpnFormat, hostName));
776 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, httpSpnFormat, hostName));
780 case HostNameComparisonMode.StrongWildcard:
781 case HostNameComparisonMode.WeakWildcard:
782 hostName = Dns.GetHostEntry(string.Empty).HostName;
783 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, hostSpnFormat, hostName));
784 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, httpSpnFormat, hostName));
787 Fx.Assert("Unhandled HostNameComparisonMode: " + hostNameComparisonMode);
791 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, hostSpnFormat, localhost));
792 AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, httpSpnFormat, localhost));
794 return new ServiceNameCollection(serviceNames.Values);
797 static void AddSpn(Dictionary<string, string> list, string value)
799 string key = value.ToLowerInvariant();
801 if (!list.ContainsKey(key))
803 list.Add(key, value);
807 public abstract bool CreateWebSocketChannelAndEnqueue(HttpRequestContext httpRequestContext, HttpPipeline httpPipeline, HttpResponseMessage httpResponseMessage, string subProtocol, Action dequeuedCallback);
809 public abstract byte[] TakeWebSocketInternalBuffer();
810 public abstract void ReturnWebSocketInternalBuffer(byte[] buffer);
812 internal interface IHttpAuthenticationContext
814 WindowsIdentity LogonUserIdentity { get; }
815 X509Certificate2 GetClientCertificate(out bool isValidCertificate);
816 bool IISSupportsExtendedProtection { get; }
817 TraceRecord CreateTraceRecord();
821 class HttpChannelListener<TChannel> : HttpChannelListener,
822 IChannelListener<TChannel> where TChannel : class, IChannel
824 InputQueueChannelAcceptor<TChannel> acceptor;
825 bool useWebSocketTransport;
826 CommunicationObjectManager<ServerWebSocketTransportDuplexSessionChannel> webSocketLifetimeManager;
827 TransportIntegrationHandler transportIntegrationHandler;
828 ConnectionBufferPool bufferPool;
829 string currentWebSocketVersion;
831 public HttpChannelListener(HttpTransportBindingElement bindingElement, BindingContext context)
832 : base(bindingElement, context)
834 this.useWebSocketTransport = bindingElement.WebSocketSettings.TransportUsage == WebSocketTransportUsage.Always
835 || (bindingElement.WebSocketSettings.TransportUsage == WebSocketTransportUsage.WhenDuplex && typeof(TChannel) != typeof(IReplyChannel));
836 if (this.useWebSocketTransport)
838 if (AspNetEnvironment.Enabled)
840 AspNetEnvironment env = AspNetEnvironment.Current;
842 // When IIS hosted, WebSockets can be used if the pipeline mode is integrated and the WebSocketModule is loaded.
843 // Otherwise, the client requests will not be upgraded to web sockets (see the code in HostedHttpTransportManager.HttpContextReceived(..)).
844 // We do the checks below (and fail the service activation), to avoid starting a WebSockets listener that won't get called.
845 if (!env.UsingIntegratedPipeline)
847 throw FxTrace.Exception.AsError(new NotSupportedException(SR.GetString(SR.WebSocketsNotSupportedInClassicPipeline)));
849 else if (!env.IsWebSocketModuleLoaded)
851 throw FxTrace.Exception.AsError(new NotSupportedException(SR.GetString(SR.WebSocketModuleNotLoaded)));
854 else if (!WebSocketHelper.OSSupportsWebSockets())
856 throw FxTrace.Exception.AsError(new PlatformNotSupportedException(SR.GetString(SR.WebSocketsServerSideNotSupported)));
859 this.currentWebSocketVersion = WebSocketHelper.GetCurrentVersion();
860 this.acceptor = new InputQueueChannelAcceptor<TChannel>(this);
861 int webSocketBufferSize = WebSocketHelper.ComputeServerBufferSize(bindingElement.MaxReceivedMessageSize);
862 this.bufferPool = new ConnectionBufferPool(webSocketBufferSize);
863 this.webSocketLifetimeManager = new CommunicationObjectManager<ServerWebSocketTransportDuplexSessionChannel>(this.ThisLock);
867 this.acceptor = (InputQueueChannelAcceptor<TChannel>)(object)(new TransportReplyChannelAcceptor(this));
870 this.CreatePipeline(bindingElement.MessageHandlerFactory);
873 public override bool UseWebSocketTransport
877 return this.useWebSocketTransport;
881 public InputQueueChannelAcceptor<TChannel> Acceptor
883 get { return this.acceptor; }
886 public override string Method
890 if (this.UseWebSocketTransport)
892 return WebSocketTransportSettings.WebSocketMethod;
898 public TChannel AcceptChannel()
900 return this.AcceptChannel(this.DefaultReceiveTimeout);
903 public IAsyncResult BeginAcceptChannel(AsyncCallback callback, object state)
905 return this.BeginAcceptChannel(this.DefaultReceiveTimeout, callback, state);
908 public TChannel AcceptChannel(TimeSpan timeout)
910 base.ThrowIfNotOpened();
911 return this.Acceptor.AcceptChannel(timeout);
914 public IAsyncResult BeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state)
916 base.ThrowIfNotOpened();
917 return this.Acceptor.BeginAcceptChannel(timeout, callback, state);
920 public TChannel EndAcceptChannel(IAsyncResult result)
923 return this.Acceptor.EndAcceptChannel(result);
926 public override bool CreateWebSocketChannelAndEnqueue(HttpRequestContext httpRequestContext, HttpPipeline pipeline, HttpResponseMessage httpResponseMessage, string subProtocol, Action dequeuedCallback)
928 Fx.Assert(this.WebSocketSettings.MaxPendingConnections > 0, "MaxPendingConnections should be positive.");
929 if (this.Acceptor.PendingCount >= this.WebSocketSettings.MaxPendingConnections)
931 if (TD.MaxPendingConnectionsExceededIsEnabled())
933 TD.MaxPendingConnectionsExceeded(SR.GetString(SR.WebSocketMaxPendingConnectionsReached, this.WebSocketSettings.MaxPendingConnections, WebSocketHelper.MaxPendingConnectionsString, WebSocketHelper.WebSocketTransportSettingsString));
936 if (DiagnosticUtility.ShouldTraceWarning)
938 TraceUtility.TraceEvent(TraceEventType.Warning,
939 TraceCode.MaxPendingConnectionsReached, SR.GetString(SR.WebSocketMaxPendingConnectionsReached, this.WebSocketSettings.MaxPendingConnections, WebSocketHelper.MaxPendingConnectionsString, WebSocketHelper.WebSocketTransportSettingsString),
940 new StringTraceRecord(WebSocketHelper.MaxPendingConnectionsString, this.WebSocketSettings.MaxPendingConnections.ToString(System.Globalization.CultureInfo.InvariantCulture)),
948 ServerWebSocketTransportDuplexSessionChannel channel = new ServerWebSocketTransportDuplexSessionChannel(this,
949 new EndpointAddress(this.Uri), this.Uri, this.bufferPool, httpRequestContext, pipeline, httpResponseMessage, subProtocol);
950 httpRequestContext.WebSocketChannel = channel;
952 // webSocketLifetimeManager hooks into the channel.Closed event as well and will take care of cleaning itself up OnClosed.
953 // We want to be called before any user-specified close handlers are called.
954 this.webSocketLifetimeManager.Add(channel);
955 this.Acceptor.EnqueueAndDispatch((TChannel)(object)channel, dequeuedCallback, true);
959 public override byte[] TakeWebSocketInternalBuffer()
961 Fx.Assert(this.bufferPool != null, "bufferPool should not be null.");
962 return this.bufferPool.Take();
965 public override void ReturnWebSocketInternalBuffer(byte[] buffer)
967 Fx.Assert(this.bufferPool != null, "bufferPool should not be null.");
968 this.bufferPool.Return(buffer);
971 protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
973 return new ChainedOpenAsyncResult(timeout, callback, state, base.OnBeginOpen, base.OnEndOpen, this.Acceptor);
976 protected override void OnOpen(TimeSpan timeout)
978 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
979 base.OnOpen(timeoutHelper.RemainingTime());
980 this.Acceptor.Open(timeoutHelper.RemainingTime());
983 protected override void OnEndOpen(IAsyncResult result)
985 ChainedOpenAsyncResult.End(result);
988 protected override void OnClose(TimeSpan timeout)
990 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
991 this.Acceptor.Close(timeoutHelper.RemainingTime());
992 if (this.IsAuthenticationSupported)
994 CloseUserNameTokenAuthenticator(timeoutHelper.RemainingTime());
996 if (this.useWebSocketTransport)
998 this.webSocketLifetimeManager.Close(timeoutHelper.RemainingTime());
1000 base.OnClose(timeoutHelper.RemainingTime());
1003 protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
1005 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1006 ICommunicationObject[] communicationObjects;
1007 ICommunicationObject communicationObject = this.UserNameTokenAuthenticator as ICommunicationObject;
1008 if (communicationObject == null)
1010 if (this.IsAuthenticationSupported)
1012 CloseUserNameTokenAuthenticator(timeoutHelper.RemainingTime());
1014 communicationObjects = new ICommunicationObject[] { this.Acceptor };
1018 communicationObjects = new ICommunicationObject[] { this.Acceptor, communicationObject };
1021 if (this.useWebSocketTransport)
1023 return new LifetimeWrappedCloseAsyncResult<ServerWebSocketTransportDuplexSessionChannel>(
1024 timeoutHelper.RemainingTime(),
1027 this.webSocketLifetimeManager,
1030 communicationObjects);
1034 return new ChainedCloseAsyncResult(timeoutHelper.RemainingTime(), callback, state, base.OnBeginClose, base.OnEndClose, communicationObjects);
1038 protected override void OnEndClose(IAsyncResult result)
1040 if (this.useWebSocketTransport)
1042 LifetimeWrappedCloseAsyncResult<ServerWebSocketTransportDuplexSessionChannel>.End(result);
1046 ChainedCloseAsyncResult.End(result);
1050 protected override void OnClosed()
1053 if (this.bufferPool != null)
1055 this.bufferPool.Close();
1058 if (this.transportIntegrationHandler != null)
1060 this.transportIntegrationHandler.Dispose();
1064 protected override void OnAbort()
1066 if (this.IsAuthenticationSupported)
1068 AbortUserNameTokenAuthenticator();
1071 this.Acceptor.Abort();
1073 if (this.useWebSocketTransport)
1075 this.webSocketLifetimeManager.Abort();
1081 protected override bool OnWaitForChannel(TimeSpan timeout)
1083 return Acceptor.WaitForChannel(timeout);
1086 protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state)
1088 return Acceptor.BeginWaitForChannel(timeout, callback, state);
1091 protected override bool OnEndWaitForChannel(IAsyncResult result)
1093 return Acceptor.EndWaitForChannel(result);
1096 internal override IAsyncResult BeginHttpContextReceived(HttpRequestContext context,
1097 Action acceptorCallback,
1098 AsyncCallback callback,
1101 return new HttpContextReceivedAsyncResult<TChannel>(
1109 internal override bool EndHttpContextReceived(IAsyncResult result)
1111 return HttpContextReceivedAsyncResult<TChannel>.End(result);
1114 void CreatePipeline(HttpMessageHandlerFactory httpMessageHandlerFactory)
1116 HttpMessageHandler innerPipeline;
1117 if (this.UseWebSocketTransport)
1119 innerPipeline = new DefaultWebSocketConnectionHandler(this.WebSocketSettings.SubProtocol, this.currentWebSocketVersion, this.MessageVersion, this.MessageEncoderFactory, this.TransferMode);
1120 if (httpMessageHandlerFactory != null)
1122 innerPipeline = httpMessageHandlerFactory.Create(innerPipeline);
1127 if (httpMessageHandlerFactory == null)
1132 innerPipeline = httpMessageHandlerFactory.Create(new ChannelModelIntegrationHandler());
1135 if (innerPipeline == null)
1137 throw FxTrace.Exception.AsError(
1138 new InvalidOperationException(SR.GetString(SR.HttpMessageHandlerChannelFactoryNullPipeline,
1139 httpMessageHandlerFactory.GetType().Name, typeof(HttpRequestContext).Name)));
1142 this.transportIntegrationHandler = new TransportIntegrationHandler(innerPipeline);
1145 static void HandleProcessInboundException(Exception ex, HttpRequestContext context)
1152 if (ex is ProtocolException)
1154 ProtocolException protocolException = (ProtocolException)ex;
1155 HttpStatusCode statusCode = HttpStatusCode.BadRequest;
1156 string statusDescription = string.Empty;
1157 if (protocolException.Data.Contains(HttpChannelUtilities.HttpStatusCodeExceptionKey))
1159 statusCode = (HttpStatusCode)protocolException.Data[HttpChannelUtilities.HttpStatusCodeExceptionKey];
1160 protocolException.Data.Remove(HttpChannelUtilities.HttpStatusCodeExceptionKey);
1162 if (protocolException.Data.Contains(HttpChannelUtilities.HttpStatusDescriptionExceptionKey))
1164 statusDescription = (string)protocolException.Data[HttpChannelUtilities.HttpStatusDescriptionExceptionKey];
1165 protocolException.Data.Remove(HttpChannelUtilities.HttpStatusDescriptionExceptionKey);
1167 context.SendResponseAndClose(statusCode, statusDescription);
1174 context.SendResponseAndClose(HttpStatusCode.BadRequest);
1176 catch (Exception closeException)
1178 if (Fx.IsFatal(closeException))
1183 DiagnosticUtility.TraceHandledException(closeException, TraceEventType.Error);
1188 static bool ContextReceiveExceptionHandled(Exception e)
1195 if (e is CommunicationException)
1197 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1199 else if (e is XmlException)
1201 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1203 else if (e is IOException)
1205 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1207 else if (e is TimeoutException)
1209 if (TD.ReceiveTimeoutIsEnabled())
1211 TD.ReceiveTimeout(e.Message);
1213 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1215 else if (e is OperationCanceledException)
1217 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1219 else if (!ExceptionHandler.HandleTransportExceptionHelper(e))
1227 class HttpContextReceivedAsyncResult<TListenerChannel> : TraceAsyncResult where TListenerChannel : class, IChannel
1229 static AsyncCallback onProcessInboundRequest = Fx.ThunkCallback(OnProcessInboundRequest);
1231 HttpRequestContext context;
1232 Action acceptorCallback;
1233 HttpChannelListener<TListenerChannel> listener;
1235 public HttpContextReceivedAsyncResult(
1236 HttpRequestContext requestContext,
1237 Action acceptorCallback,
1238 HttpChannelListener<TListenerChannel> listener,
1239 AsyncCallback callback,
1241 : base(callback, state)
1243 this.context = requestContext;
1244 this.acceptorCallback = acceptorCallback;
1245 this.listener = listener;
1247 if (this.ProcessHttpContextAsync() == AsyncCompletionResult.Completed)
1249 base.Complete(true);
1253 public static bool End(IAsyncResult result)
1255 return AsyncResult.End<HttpContextReceivedAsyncResult<TListenerChannel>>(result).enqueued;
1258 static void OnProcessInboundRequest(IAsyncResult result)
1260 if (result.CompletedSynchronously)
1265 HttpContextReceivedAsyncResult<TListenerChannel> thisPtr = (HttpContextReceivedAsyncResult<TListenerChannel>)result.AsyncState;
1266 Exception completionException = null;
1270 thisPtr.HandleProcessInboundRequest(result);
1272 catch (Exception ex)
1279 completionException = ex;
1282 thisPtr.Complete(false, completionException);
1285 AsyncCompletionResult ProcessHttpContextAsync()
1290 this.context.InitializeHttpPipeline(this.listener.transportIntegrationHandler);
1291 if (!this.Authenticate())
1293 return AsyncCompletionResult.Completed;
1296 if (listener.UseWebSocketTransport && !context.IsWebSocketRequest)
1298 this.context.SendResponseAndClose(HttpStatusCode.BadRequest, SR.GetString(SR.WebSocketEndpointOnlySupportWebSocketError));
1299 return AsyncCompletionResult.Completed;
1302 if (!listener.UseWebSocketTransport && context.IsWebSocketRequest)
1304 this.context.SendResponseAndClose(HttpStatusCode.BadRequest, SR.GetString(SR.WebSocketEndpointDoesNotSupportWebSocketError));
1305 return AsyncCompletionResult.Completed;
1310 IAsyncResult result = context.BeginProcessInboundRequest(listener.Acceptor as ReplyChannelAcceptor,
1311 this.acceptorCallback,
1312 onProcessInboundRequest,
1314 if (result.CompletedSynchronously)
1316 this.EndInboundProcessAndEnqueue(result);
1317 return AsyncCompletionResult.Completed;
1320 catch (Exception ex)
1322 HandleProcessInboundException(ex, this.context);
1326 catch (Exception ex)
1328 // containment -- we abort the context in all error cases, no additional containment action needed
1330 if (!ContextReceiveExceptionHandled(ex))
1343 return abort ? AsyncCompletionResult.Completed : AsyncCompletionResult.Queued;
1348 if (!this.context.ProcessAuthentication())
1350 if (TD.HttpAuthFailedIsEnabled())
1352 TD.HttpAuthFailed(context.EventTraceActivity);
1355 if (DiagnosticUtility.ShouldTraceInformation)
1357 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.HttpAuthFailed, SR.GetString(SR.TraceCodeHttpAuthFailed), this);
1366 void HandleProcessInboundRequest(IAsyncResult result)
1373 this.EndInboundProcessAndEnqueue(result);
1376 catch (Exception ex)
1378 HandleProcessInboundException(ex, this.context);
1382 catch (Exception ex)
1384 // containment -- we abort the context in all error cases, no additional containment action needed
1385 if (!ContextReceiveExceptionHandled(ex))
1399 void EndInboundProcessAndEnqueue(IAsyncResult result)
1401 Fx.Assert(result != null, "Trying to complete without issuing a BeginProcessInboundRequest.");
1402 context.EndProcessInboundRequest(result);
1404 //We have finally managed to enqueue the message.
1405 this.enqueued = true;
1409 class LifetimeWrappedCloseAsyncResult<TCommunicationObject> : AsyncResult where TCommunicationObject : CommunicationObject
1411 static AsyncCompletion handleLifetimeManagerClose = new AsyncCompletion(HandleLifetimeManagerClose);
1412 static AsyncCompletion handleChannelClose = new AsyncCompletion(HandleChannelClose);
1414 TimeoutHelper timeoutHelper;
1416 ICommunicationObject[] communicationObjects;
1417 CommunicationObjectManager<TCommunicationObject> communicationObjectManager;
1418 ChainedBeginHandler begin1;
1419 ChainedEndHandler end1;
1421 public LifetimeWrappedCloseAsyncResult(TimeSpan timeout, AsyncCallback callback, object state, CommunicationObjectManager<TCommunicationObject> communicationObjectManager, ChainedBeginHandler begin1, ChainedEndHandler end1, ICommunicationObject[] communicationObjects)
1422 : base(callback, state)
1424 this.timeoutHelper = new TimeoutHelper(timeout);
1425 this.begin1 = begin1;
1427 this.communicationObjects = communicationObjects;
1428 this.communicationObjectManager = communicationObjectManager;
1430 IAsyncResult result = communicationObjectManager.BeginClose(
1431 this.timeoutHelper.RemainingTime(),
1432 PrepareAsyncCompletion(handleLifetimeManagerClose),
1435 bool completeSelf = SyncContinue(result);
1439 this.Complete(true);
1443 public static void End(IAsyncResult result)
1445 AsyncResult.End<LifetimeWrappedCloseAsyncResult<TCommunicationObject>>(result);
1448 static bool HandleLifetimeManagerClose(IAsyncResult result)
1450 LifetimeWrappedCloseAsyncResult<TCommunicationObject> thisPtr = (LifetimeWrappedCloseAsyncResult<TCommunicationObject>)result.AsyncState;
1451 thisPtr.communicationObjectManager.EndClose(result);
1453 // begin second step of the close...
1454 ChainedCloseAsyncResult closeResult = new ChainedCloseAsyncResult(
1455 thisPtr.timeoutHelper.RemainingTime(),
1456 thisPtr.PrepareAsyncCompletion(handleChannelClose),
1460 thisPtr.communicationObjects);
1462 return thisPtr.SyncContinue(closeResult);
1465 static bool HandleChannelClose(IAsyncResult result)
1467 ChainedCloseAsyncResult.End(result);
1474 /// Handler wrapping the bottom (towards network) of the <see cref="HttpMessageHandler"/> and integrates
1475 /// back into the <see cref="IReplyChannel"/>.
1477 class TransportIntegrationHandler : DelegatingHandler
1480 /// Initializes a new instance of the <see cref="TransportIntegrationHandler"/> class.
1482 /// <param name="innerChannel">The inner <see cref="HttpMessageHandler"/> on which we send the <see cref="HttpRequestMessage"/>.</param>
1483 public TransportIntegrationHandler(HttpMessageHandler innerChannel)
1484 : base(innerChannel)
1489 /// Submits an <see cref="HttpRequestMessage"/> on the inner channel asynchronously.
1491 /// <param name="request"><see cref="HttpRequestMessage"/> to submit</param>
1492 /// <param name="cancellationToken">Token used to cancel operation.</param>
1493 /// <returns>A <see cref="Task<T>"/> representing the operation.</returns>
1494 public Task<HttpResponseMessage> ProcessPipelineAsync(HttpRequestMessage request, CancellationToken cancellationToken)
1496 return base.SendAsync(request, cancellationToken).ContinueWith(task =>
1498 HttpResponseMessage httpResponse;
1501 if (Fx.IsFatal(task.Exception))
1503 throw task.Exception;
1506 // We must inspect task.Exception -- otherwise it is automatically rethrown.
1507 FxTrace.Exception.AsError<FaultException>(task.Exception);
1508 httpResponse = TraceFaultAndGetResponseMessasge(request);
1510 else if (task.IsCanceled)
1512 HttpPipeline pipeline = HttpPipeline.GetHttpPipeline(request);
1513 if (TD.HttpPipelineTimeoutExceptionIsEnabled())
1515 TD.HttpPipelineTimeoutException(pipeline != null ? pipeline.EventTraceActivity : null);
1518 FxTrace.Exception.AsError(new TimeoutException(SR.GetString(SR.HttpPipelineOperationCanceledError)));
1520 httpResponse = null;
1524 httpResponse = task.Result;
1525 if (httpResponse == null)
1527 FxTrace.Exception.AsError(new NotSupportedException(SR.GetString(SR.HttpPipelineNotSupportNullResponseMessage, typeof(DelegatingHandler).Name, typeof(HttpResponseMessage).Name)));
1528 httpResponse = TraceFaultAndGetResponseMessasge(request);
1532 return httpResponse;
1534 TaskContinuationOptions.ExecuteSynchronously);
1537 static HttpResponseMessage TraceFaultAndGetResponseMessasge(HttpRequestMessage request)
1539 HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
1540 response.RequestMessage = request;
1542 if (TD.HttpPipelineFaultedIsEnabled())
1544 HttpPipeline pipeline = HttpPipeline.GetHttpPipeline(request);
1545 TD.HttpPipelineFaulted(pipeline != null ? pipeline.EventTraceActivity : null);
1553 /// Handler wrapping the top (towards Channel Model) of the <see cref="HttpMessageHandler"/> and integrates
1554 /// bask into the <see cref="IReplyChannel"/>.
1556 class ChannelModelIntegrationHandler : HttpMessageHandler
1559 /// Submits an <see cref="HttpRequestMessage"/> on the inner channel asynchronously.
1561 /// <param name="request"><see cref="HttpRequestMessage"/> to submit</param>
1562 /// <param name="cancellationToken">Token used to cancel operation.</param>
1563 /// <returns>A <see cref="Task<T>"/> representing the operation.</returns>
1564 protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
1566 if (request == null)
1568 throw FxTrace.Exception.ArgumentNull("request");
1571 if (cancellationToken == null)
1573 throw FxTrace.Exception.ArgumentNull("cancellationToken");
1576 cancellationToken.ThrowIfCancellationRequested();
1578 HttpChannelUtilities.EnsureHttpRequestMessageContentNotNull(request);
1579 //// We ran up through the pipeline and are now ready to hook back into the WCF channel model
1580 HttpPipeline httpPipeline = HttpPipeline.GetHttpPipeline(request);
1582 return httpPipeline.Dispatch(request);