Moving BSTR conv to native code in SecureStringToBSTR.
[mono.git] / mcs / class / referencesource / System.ServiceModel / System / ServiceModel / Channels / HttpChannelListener.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 namespace System.ServiceModel.Channels
5 {
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;
13     using System.IO;
14     using System.Net;
15     using System.Net.Http;
16     using System.Net.WebSockets;
17     using System.Runtime;
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;
33     using System.Xml;
34
35     abstract class HttpChannelListener : TransportChannelListener,
36         IHttpTransportFactorySettings
37     {
38         AuthenticationSchemes authenticationScheme;
39         bool extractGroupsForWindowsAccounts;
40         EndpointIdentity identity;
41         bool keepAliveEnabled;
42         int maxBufferSize;
43         readonly int maxPendingAccepts;
44         string method;
45         string realm;
46         readonly TimeSpan requestInitializationTimeout;
47         TransferMode transferMode;
48         bool unsafeConnectionNtlmAuthentication;
49         ISecurityCapabilities securityCapabilities;
50
51         SecurityCredentialsManager credentialProvider;
52         SecurityTokenAuthenticator userNameTokenAuthenticator;
53         SecurityTokenAuthenticator windowsTokenAuthenticator;
54         ExtendedProtectionPolicy extendedProtectionPolicy;
55         bool usingDefaultSpnList;
56         HttpAnonymousUriPrefixMatcher anonymousUriPrefixMatcher;
57
58         HttpMessageSettings httpMessageSettings;
59         WebSocketTransportSettings webSocketSettings;
60
61         static UriPrefixTable<ITransportManagerRegistration> transportManagerTable =
62             new UriPrefixTable<ITransportManagerRegistration>(true);
63
64         public HttpChannelListener(HttpTransportBindingElement bindingElement, BindingContext context)
65             : base(bindingElement, context, HttpTransportDefaults.GetDefaultMessageEncoderFactory(),
66                 bindingElement.HostNameComparisonMode)
67         {
68             if (bindingElement.TransferMode == TransferMode.Buffered)
69             {
70                 if (bindingElement.MaxReceivedMessageSize > int.MaxValue)
71                 {
72                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
73                         new ArgumentOutOfRangeException("bindingElement.MaxReceivedMessageSize",
74                         SR.GetString(SR.MaxReceivedMessageSizeMustBeInIntegerRange)));
75                 }
76
77                 if (bindingElement.MaxBufferSize != bindingElement.MaxReceivedMessageSize)
78                 {
79                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement",
80                         SR.GetString(SR.MaxBufferSizeMustMatchMaxReceivedMessageSize));
81                 }
82             }
83             else
84             {
85                 if (bindingElement.MaxBufferSize > bindingElement.MaxReceivedMessageSize)
86                 {
87                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement",
88                         SR.GetString(SR.MaxBufferSizeMustNotExceedMaxReceivedMessageSize));
89                 }
90             }
91
92             if (bindingElement.AuthenticationScheme.IsSet(AuthenticationSchemes.Basic) &&
93                 bindingElement.AuthenticationScheme.IsNotSet(AuthenticationSchemes.Digest | AuthenticationSchemes.Ntlm | AuthenticationSchemes.Negotiate) &&
94                 bindingElement.ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.Always)
95             {
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)));
98             }
99
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);
113
114             this.webSocketSettings = WebSocketHelper.GetRuntimeWebSocketSettings(bindingElement.WebSocketSettings);
115
116             if (bindingElement.AnonymousUriPrefixMatcher != null)
117             {
118                 this.anonymousUriPrefixMatcher = new HttpAnonymousUriPrefixMatcher(bindingElement.AnonymousUriPrefixMatcher);
119             }
120
121             this.httpMessageSettings = context.BindingParameters.Find<HttpMessageSettings>() ?? new HttpMessageSettings();
122
123             if (this.httpMessageSettings.HttpMessagesSupported && this.MessageVersion != MessageVersion.None)
124             {
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())));
135             }
136         }
137
138         public TimeSpan RequestInitializationTimeout
139         {
140             get { return this.requestInitializationTimeout; }
141         }
142
143         public WebSocketTransportSettings WebSocketSettings
144         {
145             get { return this.webSocketSettings; }
146         }
147
148         public HttpMessageSettings HttpMessageSettings
149         {
150             get { return this.httpMessageSettings; }
151         }
152         
153         public ExtendedProtectionPolicy ExtendedProtectionPolicy
154         {
155             get
156             {
157                 return this.extendedProtectionPolicy;
158             }
159         }
160
161         public virtual bool IsChannelBindingSupportEnabled
162         {
163             get
164             {
165                 return false;
166             }
167         }
168
169         public abstract bool UseWebSocketTransport { get; }
170
171         internal HttpAnonymousUriPrefixMatcher AnonymousUriPrefixMatcher
172         {
173             get
174             {
175                 return this.anonymousUriPrefixMatcher;
176             }
177         }
178
179         protected SecurityTokenAuthenticator UserNameTokenAuthenticator
180         {
181             get { return this.userNameTokenAuthenticator; }
182         }
183
184         internal override void ApplyHostedContext(string virtualPath, bool isMetadataListener)
185         {
186             base.ApplyHostedContext(virtualPath, isMetadataListener);
187             AspNetEnvironment.Current.ValidateHttpSettings(virtualPath, isMetadataListener, this.usingDefaultSpnList, ref this.authenticationScheme, ref this.extendedProtectionPolicy, ref this.realm);
188         }
189
190         public AuthenticationSchemes AuthenticationScheme
191         {
192             get
193             {
194                 return this.authenticationScheme;
195             }
196         }
197
198         public bool KeepAliveEnabled
199         {
200             get
201             {
202                 return this.keepAliveEnabled;
203             }
204         }
205
206         public bool ExtractGroupsForWindowsAccounts
207         {
208             get
209             {
210                 return this.extractGroupsForWindowsAccounts;
211             }
212         }
213
214         public HostNameComparisonMode HostNameComparisonMode
215         {
216             get
217             {
218                 return this.HostNameComparisonModeInternal;
219             }
220         }
221
222         //Returns true if one of the non-anonymous authentication schemes is set on this.AuthenticationScheme
223         protected bool IsAuthenticationSupported
224         {
225             get
226             {
227                 return this.authenticationScheme != AuthenticationSchemes.Anonymous;
228             }
229         }
230
231         bool IsAuthenticationRequired
232         {
233             get
234             {
235                 return this.AuthenticationScheme.IsNotSet(AuthenticationSchemes.Anonymous);
236             }
237         }
238
239         public int MaxBufferSize
240         {
241             get
242             {
243                 return this.maxBufferSize;
244             }
245         }
246
247         public int MaxPendingAccepts
248         {
249             get { return this.maxPendingAccepts; }
250         }
251
252         public virtual string Method
253         {
254             get
255             {
256                 return this.method;
257             }
258         }
259
260         public TransferMode TransferMode
261         {
262             get
263             {
264                 return transferMode;
265             }
266         }
267
268         public string Realm
269         {
270             get { return this.realm; }
271         }
272
273         int IHttpTransportFactorySettings.MaxBufferSize
274         {
275             get { return MaxBufferSize; }
276         }
277
278         TransferMode IHttpTransportFactorySettings.TransferMode
279         {
280             get { return TransferMode; }
281         }
282
283         public override string Scheme
284         {
285             get { return Uri.UriSchemeHttp; }
286         }
287
288         internal static UriPrefixTable<ITransportManagerRegistration> StaticTransportManagerTable
289         {
290             get
291             {
292                 return transportManagerTable;
293             }
294         }
295
296         public bool UnsafeConnectionNtlmAuthentication
297         {
298             get
299             {
300                 return this.unsafeConnectionNtlmAuthentication;
301             }
302         }
303
304         internal override UriPrefixTable<ITransportManagerRegistration> TransportManagerTable
305         {
306             get
307             {
308                 return transportManagerTable;
309             }
310         }
311
312         internal override ITransportManagerRegistration CreateTransportManagerRegistration(Uri listenUri)
313         {
314             return new SharedHttpTransportManager(listenUri, this);
315         }
316
317         string GetAuthType(HttpListenerContext listenerContext)
318         {
319             string authType = null;
320             IPrincipal principal = listenerContext.User;
321             if ((principal != null) && (principal.Identity != null))
322             {
323                 authType = principal.Identity.AuthenticationType;
324             }
325             return authType;
326         }
327
328         protected string GetAuthType(IHttpAuthenticationContext authenticationContext)
329         {
330             string authType = null;
331             if (authenticationContext.LogonUserIdentity != null)
332             {
333                 authType = authenticationContext.LogonUserIdentity.AuthenticationType;
334             }
335             return authType;
336         }
337
338         bool IsAuthSchemeValid(string authType)
339         {
340             return AuthenticationSchemesHelper.DoesAuthTypeMatch(this.authenticationScheme, authType);
341         }
342
343         internal override int GetMaxBufferSize()
344         {
345             return MaxBufferSize;
346         }
347
348         public override T GetProperty<T>()
349         {
350             if (typeof(T) == typeof(EndpointIdentity))
351             {
352                 return (T)(object)(this.identity);
353             }
354             else if (typeof(T) == typeof(ILogonTokenCacheManager))
355             {
356                 object cacheManager = (object)GetIdentityModelProperty<T>();
357                 if (cacheManager != null)
358                 {
359                     return (T)cacheManager;
360                 }
361             }
362             else if (typeof(T) == typeof(ISecurityCapabilities))
363             {
364                 return (T)(object)this.securityCapabilities;
365             }
366             else if (typeof(T) == typeof(ExtendedProtectionPolicy))
367             {
368                 return (T)(object)this.extendedProtectionPolicy;
369             }
370
371             return base.GetProperty<T>();
372         }
373
374         [MethodImpl(MethodImplOptions.NoInlining)]
375         T GetIdentityModelProperty<T>()
376         {
377             if (typeof(T) == typeof(EndpointIdentity))
378             {
379                 if (this.identity == null)
380                 {
381                     if (this.authenticationScheme.IsSet(AuthenticationSchemes.Negotiate) ||
382                         this.authenticationScheme.IsSet(AuthenticationSchemes.Ntlm))
383                     {
384                         this.identity = SecurityUtils.CreateWindowsIdentity();
385                     }
386                 }
387
388                 return (T)(object)this.identity;
389             }
390             else if (typeof(T) == typeof(ILogonTokenCacheManager)
391                 && (this.userNameTokenAuthenticator != null))
392             {
393                 ILogonTokenCacheManager retVal = this.userNameTokenAuthenticator as ILogonTokenCacheManager;
394
395                 if (retVal != null)
396                 {
397                     return (T)(object)retVal;
398                 }
399             }
400
401             return default(T);
402         }
403
404         internal abstract IAsyncResult BeginHttpContextReceived(
405                                                 HttpRequestContext context,
406                                                 Action acceptorCallback,
407                                                 AsyncCallback callback,
408                                                 object state);
409
410         internal abstract bool EndHttpContextReceived(IAsyncResult result);
411
412         [MethodImpl(MethodImplOptions.NoInlining)]
413         void InitializeSecurityTokenAuthenticator()
414         {
415             Fx.Assert(this.IsAuthenticationSupported, "SecurityTokenAuthenticator should only be initialized when authentication is supported.");
416             ServiceCredentials serviceCredentials = this.credentialProvider as ServiceCredentials;
417
418             if (serviceCredentials != null)
419             {
420                 if (this.AuthenticationScheme == AuthenticationSchemes.Basic)
421                 {
422                     // when Basic authentiction is enabled - but Digest and Windows are disabled use the UsernameAuthenticationSetting
423                     this.extractGroupsForWindowsAccounts = serviceCredentials.UserNameAuthentication.IncludeWindowsGroups;
424                 }
425                 else
426                 {
427                     if (this.AuthenticationScheme.IsSet(AuthenticationSchemes.Basic) &&
428                         serviceCredentials.UserNameAuthentication.IncludeWindowsGroups != serviceCredentials.WindowsAuthentication.IncludeWindowsGroups)
429                     {
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)));
435                     }
436
437                     this.extractGroupsForWindowsAccounts = serviceCredentials.WindowsAuthentication.IncludeWindowsGroups;
438                 }
439
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)
442                 {
443                     this.userNameTokenAuthenticator = new CustomUserNameSecurityTokenAuthenticator(serviceCredentials.UserNameAuthentication.GetUserNamePasswordValidator());
444                 }
445                 else
446                 {
447                     if (serviceCredentials.UserNameAuthentication.CacheLogonTokens)
448                     {
449                         this.userNameTokenAuthenticator = new WindowsUserNameCachingSecurityTokenAuthenticator(this.extractGroupsForWindowsAccounts,
450                             serviceCredentials.UserNameAuthentication.MaxCachedLogonTokens, serviceCredentials.UserNameAuthentication.CachedLogonTokenLifetime);
451                     }
452                     else
453                     {
454                         this.userNameTokenAuthenticator = new WindowsUserNameSecurityTokenAuthenticator(this.extractGroupsForWindowsAccounts);
455                     }
456                 }
457             }
458             else
459             {
460                 this.extractGroupsForWindowsAccounts = TransportDefaults.ExtractGroupsForWindowsAccounts;
461                 this.userNameTokenAuthenticator = new WindowsUserNameSecurityTokenAuthenticator(this.extractGroupsForWindowsAccounts);
462             }
463
464             this.windowsTokenAuthenticator = new WindowsSecurityTokenAuthenticator(this.extractGroupsForWindowsAccounts);
465         }
466
467         protected override void OnOpened()
468         {
469             base.OnOpened();
470
471             if (this.IsAuthenticationSupported)
472             {
473                 InitializeSecurityTokenAuthenticator();
474                 this.identity = GetIdentityModelProperty<EndpointIdentity>();
475             }
476         }
477
478         [MethodImpl(MethodImplOptions.NoInlining)]
479         protected void CloseUserNameTokenAuthenticator(TimeSpan timeout)
480         {
481             SecurityUtils.CloseTokenAuthenticatorIfRequired(this.userNameTokenAuthenticator, timeout);
482         }
483
484         [MethodImpl(MethodImplOptions.NoInlining)]
485         protected void AbortUserNameTokenAuthenticator()
486         {
487             SecurityUtils.AbortTokenAuthenticatorIfRequired(this.userNameTokenAuthenticator);
488         }
489
490         bool ShouldProcessAuthentication(IHttpAuthenticationContext authenticationContext)
491         {
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);
495         }
496
497         bool ShouldProcessAuthentication(HttpListenerContext listenerContext)
498         {
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);
502         }
503
504         public virtual SecurityMessageProperty ProcessAuthentication(IHttpAuthenticationContext authenticationContext)
505         {
506             if (this.ShouldProcessAuthentication(authenticationContext))
507             {
508                 SecurityMessageProperty retValue;
509                 try
510                 {
511                     retValue = this.ProcessAuthentication(authenticationContext.LogonUserIdentity, GetAuthType(authenticationContext));
512                 }
513 #pragma warning suppress 56500 // covered by FXCop
514                 catch (Exception exception)
515                 {
516                     if (Fx.IsFatal(exception))
517                         throw;
518
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);
522
523                     throw;
524                 }
525
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);
529
530                 return retValue;
531             }
532             else
533             {
534                 return null;
535             }
536         }
537
538         public virtual SecurityMessageProperty ProcessAuthentication(HttpListenerContext listenerContext)
539         {
540             if (this.ShouldProcessAuthentication(listenerContext))
541             {
542                 return this.ProcessRequiredAuthentication(listenerContext);
543             }
544             else
545             {
546                 return null;
547             }
548         }
549
550         SecurityMessageProperty ProcessRequiredAuthentication(HttpListenerContext listenerContext)
551         {
552             SecurityMessageProperty retValue;
553             HttpListenerBasicIdentity identity = null;
554             WindowsIdentity wid = null;
555             try
556             {
557                 Fx.Assert(listenerContext.User != null, "HttpListener delivered authenticated request without an IPrincipal.");
558                 wid = listenerContext.User.Identity as WindowsIdentity;
559
560                 if (this.AuthenticationScheme.IsSet(AuthenticationSchemes.Basic)
561                     && wid == null)
562                 {
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);
566                 }
567                 else
568                 {
569                     Fx.Assert(wid != null, "HttpListener delivered non-Basic authenticated request with a non-Windows IIdentity.");
570                     retValue = this.ProcessAuthentication(wid, GetAuthType(listenerContext));
571                 }
572             }
573 #pragma warning suppress 56500 // covered by FXCop
574             catch (Exception exception)
575             {
576                 if (!Fx.IsFatal(exception))
577                 {
578                     // Audit Authentication failure
579                     if (AuditLevel.Failure == (this.AuditBehavior.MessageAuthenticationAuditLevel & AuditLevel.Failure))
580                     {
581                         WriteAuditEvent(AuditLevel.Failure, (identity != null) ? identity.Name : ((wid != null) ? wid.Name : String.Empty), exception);
582                     }
583                 }
584                 throw;
585             }
586
587             // Audit Authentication success
588             if (AuditLevel.Success == (this.AuditBehavior.MessageAuthenticationAuditLevel & AuditLevel.Success))
589             {
590                 WriteAuditEvent(AuditLevel.Success, (identity != null) ? identity.Name : ((wid != null) ? wid.Name : String.Empty), null);
591             }
592
593             return retValue;
594         }
595
596         protected override bool TryGetTransportManagerRegistration(HostNameComparisonMode hostNameComparisonMode,
597             out ITransportManagerRegistration registration)
598         {
599             if (this.TransportManagerTable.TryLookupUri(this.Uri, hostNameComparisonMode, out registration))
600             {
601                 HttpTransportManager httpTransportManager = registration as HttpTransportManager;
602                 if (httpTransportManager != null && httpTransportManager.IsHosted)
603                 {
604                     return true;
605                 }
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)
608                 {
609                     return true;
610                 }
611             }
612             return false;
613         }
614
615         protected void WriteAuditEvent(AuditLevel auditLevel, string primaryIdentity, Exception exception)
616         {
617             try
618             {
619                 if (auditLevel == AuditLevel.Success)
620                 {
621                     SecurityAuditHelper.WriteTransportAuthenticationSuccessEvent(this.AuditBehavior.AuditLogLocation,
622                         this.AuditBehavior.SuppressAuditFailure, null, this.Uri, primaryIdentity);
623                 }
624                 else
625                 {
626                     SecurityAuditHelper.WriteTransportAuthenticationFailureEvent(this.AuditBehavior.AuditLogLocation,
627                         this.AuditBehavior.SuppressAuditFailure, null, this.Uri, primaryIdentity, exception);
628                 }
629             }
630 #pragma warning suppress 56500
631             catch (Exception auditException)
632             {
633                 if (Fx.IsFatal(auditException) || auditLevel == AuditLevel.Success)
634                     throw;
635
636                 DiagnosticUtility.TraceHandledException(auditException, TraceEventType.Error);
637             }
638         }
639
640         SecurityMessageProperty ProcessAuthentication(HttpListenerBasicIdentity identity)
641         {
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);
647             return security;
648         }
649
650         SecurityMessageProperty ProcessAuthentication(WindowsIdentity identity, string authenticationType)
651         {
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);
658             return security;
659         }
660
661         HttpStatusCode ValidateAuthentication(string authType)
662         {
663             if (this.IsAuthSchemeValid(authType))
664             {
665                 return HttpStatusCode.OK;
666             }
667             else
668             {
669                 // Audit Authentication failure
670                 if (AuditLevel.Failure == (this.AuditBehavior.MessageAuthenticationAuditLevel & AuditLevel.Failure))
671                 {
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);
675                 }
676
677                 return HttpStatusCode.Unauthorized;
678             }
679         }
680
681         public virtual HttpStatusCode ValidateAuthentication(IHttpAuthenticationContext authenticationContext)
682         {
683             HttpStatusCode result = HttpStatusCode.OK;
684
685             if (this.IsAuthenticationSupported)
686             {
687                 string authType = GetAuthType(authenticationContext);
688                 result = ValidateAuthentication(authType);
689             }
690
691             if (result == HttpStatusCode.OK &&
692                 authenticationContext.LogonUserIdentity != null &&
693                 authenticationContext.LogonUserIdentity.IsAuthenticated &&
694                 this.ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.Always &&
695                 !authenticationContext.IISSupportsExtendedProtection)
696             {
697                 Exception exception = DiagnosticUtility.ExceptionUtility.ThrowHelperError(
698                     new PlatformNotSupportedException(SR.GetString(SR.ExtendedProtectionNotSupported)));
699                 WriteAuditEvent(AuditLevel.Failure, String.Empty, exception);
700
701                 result = HttpStatusCode.Unauthorized;
702             }
703
704             return result;
705         }
706
707         public virtual HttpStatusCode ValidateAuthentication(HttpListenerContext listenerContext)
708         {
709             HttpStatusCode result = HttpStatusCode.OK;
710
711             if (this.IsAuthenticationSupported)
712             {
713                 string authType = GetAuthType(listenerContext);
714                 result = ValidateAuthentication(authType);
715             }
716
717             return result;
718         }
719
720         static ExtendedProtectionPolicy GetPolicyWithDefaultSpnCollection(ExtendedProtectionPolicy policy, AuthenticationSchemes authenticationScheme, HostNameComparisonMode hostNameComparisonMode, Uri listenUri, out bool usingDefaultSpnList)
721         {
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).
727             {
728                 usingDefaultSpnList = true;
729                 return new ExtendedProtectionPolicy(policy.PolicyEnforcement, policy.ProtectionScenario, GetDefaultSpnList(hostNameComparisonMode, listenUri));
730             }
731
732             usingDefaultSpnList = false;
733             return policy;
734         }
735
736         static ServiceNameCollection GetDefaultSpnList(HostNameComparisonMode hostNameComparisonMode, Uri listenUri)
737         {
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";
745
746             Dictionary<string, string> serviceNames = new Dictionary<string, string>();
747
748             string hostName = null;
749             string dnsSafeHostName = listenUri.DnsSafeHost;
750
751             switch (hostNameComparisonMode)
752             {
753                 case HostNameComparisonMode.Exact:
754                     UriHostNameType hostNameType = listenUri.HostNameType;
755                     if (hostNameType == UriHostNameType.IPv4 || hostNameType == UriHostNameType.IPv6)
756                     {
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));
760                     }
761                     else
762                     {
763                         if (listenUri.DnsSafeHost.Contains("."))
764                         {
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));
768                         }
769                         else
770                         {
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));
777                         }
778                     }
779                     break;
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));
785                     break;
786                 default:
787                     Fx.Assert("Unhandled HostNameComparisonMode: " + hostNameComparisonMode);
788                     break;
789             }
790
791             AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, hostSpnFormat, localhost));
792             AddSpn(serviceNames, string.Format(CultureInfo.InvariantCulture, httpSpnFormat, localhost));
793
794             return new ServiceNameCollection(serviceNames.Values);
795         }
796
797         static void AddSpn(Dictionary<string, string> list, string value)
798         {
799             string key = value.ToLowerInvariant();
800
801             if (!list.ContainsKey(key))
802             {
803                 list.Add(key, value);
804             }
805         }
806
807         public abstract bool CreateWebSocketChannelAndEnqueue(HttpRequestContext httpRequestContext, HttpPipeline httpPipeline, HttpResponseMessage httpResponseMessage, string subProtocol, Action dequeuedCallback);
808
809         public abstract byte[] TakeWebSocketInternalBuffer();
810         public abstract void ReturnWebSocketInternalBuffer(byte[] buffer);
811
812         internal interface IHttpAuthenticationContext
813         {
814             WindowsIdentity LogonUserIdentity { get; }
815             X509Certificate2 GetClientCertificate(out bool isValidCertificate);
816             bool IISSupportsExtendedProtection { get; }
817             TraceRecord CreateTraceRecord();
818         }
819     }
820
821     class HttpChannelListener<TChannel> : HttpChannelListener,
822         IChannelListener<TChannel> where TChannel : class, IChannel
823     {
824         InputQueueChannelAcceptor<TChannel> acceptor;
825         bool useWebSocketTransport;
826         CommunicationObjectManager<ServerWebSocketTransportDuplexSessionChannel> webSocketLifetimeManager;
827         TransportIntegrationHandler transportIntegrationHandler;
828         ConnectionBufferPool bufferPool;
829         string currentWebSocketVersion;
830
831         public HttpChannelListener(HttpTransportBindingElement bindingElement, BindingContext context)
832             : base(bindingElement, context)
833         {
834             this.useWebSocketTransport = bindingElement.WebSocketSettings.TransportUsage == WebSocketTransportUsage.Always
835                 || (bindingElement.WebSocketSettings.TransportUsage == WebSocketTransportUsage.WhenDuplex && typeof(TChannel) != typeof(IReplyChannel));
836             if (this.useWebSocketTransport)
837             {
838                 if (AspNetEnvironment.Enabled)
839                 {
840                     AspNetEnvironment env = AspNetEnvironment.Current;
841
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)
846                     {
847                         throw FxTrace.Exception.AsError(new NotSupportedException(SR.GetString(SR.WebSocketsNotSupportedInClassicPipeline)));
848                     }
849                     else if (!env.IsWebSocketModuleLoaded)
850                     {
851                         throw FxTrace.Exception.AsError(new NotSupportedException(SR.GetString(SR.WebSocketModuleNotLoaded)));
852                     }
853                 }
854                 else if (!WebSocketHelper.OSSupportsWebSockets())
855                 {
856                     throw FxTrace.Exception.AsError(new PlatformNotSupportedException(SR.GetString(SR.WebSocketsServerSideNotSupported)));
857                 }
858
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);
864             }
865             else
866             {
867                 this.acceptor = (InputQueueChannelAcceptor<TChannel>)(object)(new TransportReplyChannelAcceptor(this));
868             }
869
870             this.CreatePipeline(bindingElement.MessageHandlerFactory);
871         }
872
873         public override bool UseWebSocketTransport
874         {
875             get
876             {
877                 return this.useWebSocketTransport;
878             }
879         }
880
881         public InputQueueChannelAcceptor<TChannel> Acceptor
882         {
883             get { return this.acceptor; }
884         }
885
886         public override string Method
887         {
888             get
889             {
890                 if (this.UseWebSocketTransport)
891                 {
892                     return WebSocketTransportSettings.WebSocketMethod;
893                 }
894                 return base.Method;
895             }
896         }
897
898         public TChannel AcceptChannel()
899         {
900             return this.AcceptChannel(this.DefaultReceiveTimeout);
901         }
902
903         public IAsyncResult BeginAcceptChannel(AsyncCallback callback, object state)
904         {
905             return this.BeginAcceptChannel(this.DefaultReceiveTimeout, callback, state);
906         }
907
908         public TChannel AcceptChannel(TimeSpan timeout)
909         {
910             base.ThrowIfNotOpened();
911             return this.Acceptor.AcceptChannel(timeout);
912         }
913
914         public IAsyncResult BeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state)
915         {
916             base.ThrowIfNotOpened();
917             return this.Acceptor.BeginAcceptChannel(timeout, callback, state);
918         }
919
920         public TChannel EndAcceptChannel(IAsyncResult result)
921         {
922             base.ThrowPending();
923             return this.Acceptor.EndAcceptChannel(result);
924         }
925
926         public override bool CreateWebSocketChannelAndEnqueue(HttpRequestContext httpRequestContext, HttpPipeline pipeline, HttpResponseMessage httpResponseMessage, string subProtocol, Action dequeuedCallback)
927         {
928             Fx.Assert(this.WebSocketSettings.MaxPendingConnections > 0, "MaxPendingConnections should be positive.");
929             if (this.Acceptor.PendingCount >= this.WebSocketSettings.MaxPendingConnections)
930             {
931                 if (TD.MaxPendingConnectionsExceededIsEnabled())
932                 {
933                     TD.MaxPendingConnectionsExceeded(SR.GetString(SR.WebSocketMaxPendingConnectionsReached, this.WebSocketSettings.MaxPendingConnections, WebSocketHelper.MaxPendingConnectionsString, WebSocketHelper.WebSocketTransportSettingsString));
934                 }
935
936                 if (DiagnosticUtility.ShouldTraceWarning)
937                 {
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)),
941                         this,
942                         null);
943                 }
944
945                 return false;
946             }
947
948             ServerWebSocketTransportDuplexSessionChannel channel = new ServerWebSocketTransportDuplexSessionChannel(this,
949                                     new EndpointAddress(this.Uri), this.Uri, this.bufferPool, httpRequestContext, pipeline, httpResponseMessage, subProtocol);
950             httpRequestContext.WebSocketChannel = channel;
951
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);
956             return true;
957         }
958
959         public override byte[] TakeWebSocketInternalBuffer()
960         {
961             Fx.Assert(this.bufferPool != null, "bufferPool should not be null.");
962             return this.bufferPool.Take();
963         }
964
965         public override void ReturnWebSocketInternalBuffer(byte[] buffer)
966         {
967             Fx.Assert(this.bufferPool != null, "bufferPool should not be null.");
968             this.bufferPool.Return(buffer);
969         }
970
971         protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
972         {
973             return new ChainedOpenAsyncResult(timeout, callback, state, base.OnBeginOpen, base.OnEndOpen, this.Acceptor);
974         }
975
976         protected override void OnOpen(TimeSpan timeout)
977         {
978             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
979             base.OnOpen(timeoutHelper.RemainingTime());
980             this.Acceptor.Open(timeoutHelper.RemainingTime());
981         }
982
983         protected override void OnEndOpen(IAsyncResult result)
984         {
985             ChainedOpenAsyncResult.End(result);
986         }
987
988         protected override void OnClose(TimeSpan timeout)
989         {
990             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
991             this.Acceptor.Close(timeoutHelper.RemainingTime());
992             if (this.IsAuthenticationSupported)
993             {
994                 CloseUserNameTokenAuthenticator(timeoutHelper.RemainingTime());
995             }
996             if (this.useWebSocketTransport)
997             {
998                 this.webSocketLifetimeManager.Close(timeoutHelper.RemainingTime());
999             }
1000             base.OnClose(timeoutHelper.RemainingTime());
1001         }
1002
1003         protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
1004         {
1005             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1006             ICommunicationObject[] communicationObjects;
1007             ICommunicationObject communicationObject = this.UserNameTokenAuthenticator as ICommunicationObject;
1008             if (communicationObject == null)
1009             {
1010                 if (this.IsAuthenticationSupported)
1011                 {
1012                     CloseUserNameTokenAuthenticator(timeoutHelper.RemainingTime());
1013                 }
1014                 communicationObjects = new ICommunicationObject[] { this.Acceptor };
1015             }
1016             else
1017             {
1018                 communicationObjects = new ICommunicationObject[] { this.Acceptor, communicationObject };
1019             }
1020
1021             if (this.useWebSocketTransport)
1022             {
1023                 return new LifetimeWrappedCloseAsyncResult<ServerWebSocketTransportDuplexSessionChannel>(
1024                     timeoutHelper.RemainingTime(),
1025                     callback,
1026                     state,
1027                     this.webSocketLifetimeManager,
1028                     base.OnBeginClose,
1029                     base.OnEndClose,
1030                     communicationObjects);
1031             }
1032             else
1033             {
1034                 return new ChainedCloseAsyncResult(timeoutHelper.RemainingTime(), callback, state, base.OnBeginClose, base.OnEndClose, communicationObjects);
1035             }
1036         }
1037
1038         protected override void OnEndClose(IAsyncResult result)
1039         {
1040             if (this.useWebSocketTransport)
1041             {
1042                 LifetimeWrappedCloseAsyncResult<ServerWebSocketTransportDuplexSessionChannel>.End(result);
1043             }
1044             else
1045             {
1046                 ChainedCloseAsyncResult.End(result);
1047             }
1048         }
1049
1050         protected override void OnClosed()
1051         {
1052             base.OnClosed();
1053             if (this.bufferPool != null)
1054             {
1055                 this.bufferPool.Close();
1056             }
1057
1058             if (this.transportIntegrationHandler != null)
1059             {
1060                 this.transportIntegrationHandler.Dispose();
1061             }
1062         }
1063
1064         protected override void OnAbort()
1065         {
1066             if (this.IsAuthenticationSupported)
1067             {
1068                 AbortUserNameTokenAuthenticator();
1069             }
1070
1071             this.Acceptor.Abort();
1072
1073             if (this.useWebSocketTransport)
1074             {
1075                 this.webSocketLifetimeManager.Abort();
1076             }
1077
1078             base.OnAbort();
1079         }
1080
1081         protected override bool OnWaitForChannel(TimeSpan timeout)
1082         {
1083             return Acceptor.WaitForChannel(timeout);
1084         }
1085
1086         protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state)
1087         {
1088             return Acceptor.BeginWaitForChannel(timeout, callback, state);
1089         }
1090
1091         protected override bool OnEndWaitForChannel(IAsyncResult result)
1092         {
1093             return Acceptor.EndWaitForChannel(result);
1094         }
1095
1096         internal override IAsyncResult BeginHttpContextReceived(HttpRequestContext context,
1097                                                         Action acceptorCallback,
1098                                                         AsyncCallback callback,
1099                                                         object state)
1100         {
1101             return new HttpContextReceivedAsyncResult<TChannel>(
1102                 context,
1103                 acceptorCallback,
1104                 this,
1105                 callback,
1106                 state);
1107         }
1108
1109         internal override bool EndHttpContextReceived(IAsyncResult result)
1110         {
1111             return HttpContextReceivedAsyncResult<TChannel>.End(result);
1112         }
1113
1114         void CreatePipeline(HttpMessageHandlerFactory httpMessageHandlerFactory)
1115         {
1116             HttpMessageHandler innerPipeline;
1117             if (this.UseWebSocketTransport)
1118             {
1119                 innerPipeline = new DefaultWebSocketConnectionHandler(this.WebSocketSettings.SubProtocol, this.currentWebSocketVersion, this.MessageVersion, this.MessageEncoderFactory, this.TransferMode);
1120                 if (httpMessageHandlerFactory != null)
1121                 {
1122                     innerPipeline = httpMessageHandlerFactory.Create(innerPipeline);
1123                 }
1124             }
1125             else
1126             {
1127                 if (httpMessageHandlerFactory == null)
1128                 {
1129                     return;
1130                 }
1131
1132                 innerPipeline = httpMessageHandlerFactory.Create(new ChannelModelIntegrationHandler());
1133             }
1134
1135             if (innerPipeline == null)
1136             {
1137                 throw FxTrace.Exception.AsError(
1138                     new InvalidOperationException(SR.GetString(SR.HttpMessageHandlerChannelFactoryNullPipeline,
1139                         httpMessageHandlerFactory.GetType().Name, typeof(HttpRequestContext).Name)));
1140             }
1141
1142             this.transportIntegrationHandler = new TransportIntegrationHandler(innerPipeline);
1143         }
1144
1145         static void HandleProcessInboundException(Exception ex, HttpRequestContext context)
1146         {
1147             if (Fx.IsFatal(ex))
1148             {
1149                 return;
1150             }
1151
1152             if (ex is ProtocolException)
1153             {
1154                 ProtocolException protocolException = (ProtocolException)ex;
1155                 HttpStatusCode statusCode = HttpStatusCode.BadRequest;
1156                 string statusDescription = string.Empty;
1157                 if (protocolException.Data.Contains(HttpChannelUtilities.HttpStatusCodeExceptionKey))
1158                 {
1159                     statusCode = (HttpStatusCode)protocolException.Data[HttpChannelUtilities.HttpStatusCodeExceptionKey];
1160                     protocolException.Data.Remove(HttpChannelUtilities.HttpStatusCodeExceptionKey);
1161                 }
1162                 if (protocolException.Data.Contains(HttpChannelUtilities.HttpStatusDescriptionExceptionKey))
1163                 {
1164                     statusDescription = (string)protocolException.Data[HttpChannelUtilities.HttpStatusDescriptionExceptionKey];
1165                     protocolException.Data.Remove(HttpChannelUtilities.HttpStatusDescriptionExceptionKey);
1166                 }
1167                 context.SendResponseAndClose(statusCode, statusDescription);
1168
1169             }
1170             else
1171             {
1172                 try
1173                 {
1174                     context.SendResponseAndClose(HttpStatusCode.BadRequest);
1175                 }
1176                 catch (Exception closeException)
1177                 {
1178                     if (Fx.IsFatal(closeException))
1179                     {
1180                         throw;
1181                     }
1182
1183                     DiagnosticUtility.TraceHandledException(closeException, TraceEventType.Error);
1184                 }
1185             }
1186         }
1187
1188         static bool ContextReceiveExceptionHandled(Exception e)
1189         {
1190             if (Fx.IsFatal(e))
1191             {
1192                 return false;
1193             }
1194
1195             if (e is CommunicationException)
1196             {
1197                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1198             }
1199             else if (e is XmlException)
1200             {
1201                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1202             }
1203             else if (e is IOException)
1204             {
1205                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1206             }
1207             else if (e is TimeoutException)
1208             {
1209                 if (TD.ReceiveTimeoutIsEnabled())
1210                 {
1211                     TD.ReceiveTimeout(e.Message);
1212                 }
1213                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1214             }
1215             else if (e is OperationCanceledException)
1216             {
1217                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1218             }
1219             else if (!ExceptionHandler.HandleTransportExceptionHelper(e))
1220             {
1221                 return false;
1222             }
1223
1224             return true;
1225         }
1226
1227         class HttpContextReceivedAsyncResult<TListenerChannel> : TraceAsyncResult where TListenerChannel : class, IChannel
1228         {
1229             static AsyncCallback onProcessInboundRequest = Fx.ThunkCallback(OnProcessInboundRequest);
1230             bool enqueued;
1231             HttpRequestContext context;
1232             Action acceptorCallback;
1233             HttpChannelListener<TListenerChannel> listener;
1234
1235             public HttpContextReceivedAsyncResult(
1236                                                 HttpRequestContext requestContext,
1237                                                 Action acceptorCallback,
1238                                                 HttpChannelListener<TListenerChannel> listener,
1239                                                 AsyncCallback callback,
1240                                                 object state)
1241                 : base(callback, state)
1242             {
1243                 this.context = requestContext;
1244                 this.acceptorCallback = acceptorCallback;
1245                 this.listener = listener;
1246
1247                 if (this.ProcessHttpContextAsync() == AsyncCompletionResult.Completed)
1248                 {
1249                     base.Complete(true);
1250                 }
1251             }
1252
1253             public static bool End(IAsyncResult result)
1254             {
1255                 return AsyncResult.End<HttpContextReceivedAsyncResult<TListenerChannel>>(result).enqueued;
1256             }
1257
1258             static void OnProcessInboundRequest(IAsyncResult result)
1259             {
1260                 if (result.CompletedSynchronously)
1261                 {
1262                     return;
1263                 }
1264
1265                 HttpContextReceivedAsyncResult<TListenerChannel> thisPtr = (HttpContextReceivedAsyncResult<TListenerChannel>)result.AsyncState;
1266                 Exception completionException = null;
1267
1268                 try
1269                 {
1270                     thisPtr.HandleProcessInboundRequest(result);
1271                 }
1272                 catch (Exception ex)
1273                 {
1274                     if (Fx.IsFatal(ex))
1275                     {
1276                         throw;
1277                     }
1278
1279                     completionException = ex;
1280                 }
1281
1282                 thisPtr.Complete(false, completionException);
1283             }
1284
1285             AsyncCompletionResult ProcessHttpContextAsync()
1286             {
1287                 bool abort = false;
1288                 try
1289                 {
1290                     this.context.InitializeHttpPipeline(this.listener.transportIntegrationHandler);
1291                     if (!this.Authenticate())
1292                     {
1293                         return AsyncCompletionResult.Completed;
1294                     }
1295
1296                     if (listener.UseWebSocketTransport && !context.IsWebSocketRequest)
1297                     {
1298                         this.context.SendResponseAndClose(HttpStatusCode.BadRequest, SR.GetString(SR.WebSocketEndpointOnlySupportWebSocketError));
1299                         return AsyncCompletionResult.Completed;
1300                     }
1301
1302                     if (!listener.UseWebSocketTransport && context.IsWebSocketRequest)
1303                     {
1304                         this.context.SendResponseAndClose(HttpStatusCode.BadRequest, SR.GetString(SR.WebSocketEndpointDoesNotSupportWebSocketError));
1305                         return AsyncCompletionResult.Completed;
1306                     }
1307
1308                     try
1309                     {
1310                         IAsyncResult result = context.BeginProcessInboundRequest(listener.Acceptor as ReplyChannelAcceptor,
1311                                                                                             this.acceptorCallback,
1312                                                                                             onProcessInboundRequest,
1313                                                                                             this);
1314                         if (result.CompletedSynchronously)
1315                         {
1316                             this.EndInboundProcessAndEnqueue(result);
1317                             return AsyncCompletionResult.Completed;
1318                         }
1319                     }
1320                     catch (Exception ex)
1321                     {
1322                         HandleProcessInboundException(ex, this.context);
1323                         throw;
1324                     }
1325                 }
1326                 catch (Exception ex)
1327                 {
1328                     // containment -- we abort the context in all error cases, no additional containment action needed                
1329                     abort = true;
1330                     if (!ContextReceiveExceptionHandled(ex))
1331                     {
1332                         throw;
1333                     }
1334                 }
1335                 finally
1336                 {
1337                     if (abort)
1338                     {
1339                         context.Abort();
1340                     }
1341                 }
1342
1343                 return abort ? AsyncCompletionResult.Completed : AsyncCompletionResult.Queued;
1344             }
1345
1346             bool Authenticate()
1347             {
1348                 if (!this.context.ProcessAuthentication())
1349                 {
1350                     if (TD.HttpAuthFailedIsEnabled())
1351                     {
1352                         TD.HttpAuthFailed(context.EventTraceActivity);
1353                     }
1354
1355                     if (DiagnosticUtility.ShouldTraceInformation)
1356                     {
1357                         TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.HttpAuthFailed, SR.GetString(SR.TraceCodeHttpAuthFailed), this);
1358                     }
1359
1360                     return false;
1361                 }
1362
1363                 return true;
1364             }
1365
1366             void HandleProcessInboundRequest(IAsyncResult result)
1367             {
1368                 bool abort = true;
1369                 try
1370                 {
1371                     try
1372                     {
1373                         this.EndInboundProcessAndEnqueue(result);
1374                         abort = false;
1375                     }
1376                     catch (Exception ex)
1377                     {
1378                         HandleProcessInboundException(ex, this.context);
1379                         throw;
1380                     }
1381                 }
1382                 catch (Exception ex)
1383                 {
1384                     // containment -- we abort the context in all error cases, no additional containment action needed                                    
1385                     if (!ContextReceiveExceptionHandled(ex))
1386                     {
1387                         throw;
1388                     }
1389                 }
1390                 finally
1391                 {
1392                     if (abort)
1393                     {
1394                         context.Abort();
1395                     }
1396                 }
1397             }
1398
1399             void EndInboundProcessAndEnqueue(IAsyncResult result)
1400             {
1401                 Fx.Assert(result != null, "Trying to complete without issuing a BeginProcessInboundRequest.");
1402                 context.EndProcessInboundRequest(result);
1403
1404                 //We have finally managed to enqueue the message.
1405                 this.enqueued = true;
1406             }
1407         }
1408
1409         class LifetimeWrappedCloseAsyncResult<TCommunicationObject> : AsyncResult where TCommunicationObject : CommunicationObject
1410         {
1411             static AsyncCompletion handleLifetimeManagerClose = new AsyncCompletion(HandleLifetimeManagerClose);
1412             static AsyncCompletion handleChannelClose = new AsyncCompletion(HandleChannelClose);
1413
1414             TimeoutHelper timeoutHelper;
1415
1416             ICommunicationObject[] communicationObjects;
1417             CommunicationObjectManager<TCommunicationObject> communicationObjectManager;
1418             ChainedBeginHandler begin1;
1419             ChainedEndHandler end1;
1420
1421             public LifetimeWrappedCloseAsyncResult(TimeSpan timeout, AsyncCallback callback, object state, CommunicationObjectManager<TCommunicationObject> communicationObjectManager, ChainedBeginHandler begin1, ChainedEndHandler end1, ICommunicationObject[] communicationObjects)
1422                 : base(callback, state)
1423             {
1424                 this.timeoutHelper = new TimeoutHelper(timeout);
1425                 this.begin1 = begin1;
1426                 this.end1 = end1;
1427                 this.communicationObjects = communicationObjects;
1428                 this.communicationObjectManager = communicationObjectManager;
1429
1430                 IAsyncResult result = communicationObjectManager.BeginClose(
1431                     this.timeoutHelper.RemainingTime(),
1432                     PrepareAsyncCompletion(handleLifetimeManagerClose),
1433                     this);
1434
1435                 bool completeSelf = SyncContinue(result);
1436
1437                 if (completeSelf)
1438                 {
1439                     this.Complete(true);
1440                 }
1441             }
1442
1443             public static void End(IAsyncResult result)
1444             {
1445                 AsyncResult.End<LifetimeWrappedCloseAsyncResult<TCommunicationObject>>(result);
1446             }
1447
1448             static bool HandleLifetimeManagerClose(IAsyncResult result)
1449             {
1450                 LifetimeWrappedCloseAsyncResult<TCommunicationObject> thisPtr = (LifetimeWrappedCloseAsyncResult<TCommunicationObject>)result.AsyncState;
1451                 thisPtr.communicationObjectManager.EndClose(result);
1452
1453                 // begin second step of the close... 
1454                 ChainedCloseAsyncResult closeResult = new ChainedCloseAsyncResult(
1455                     thisPtr.timeoutHelper.RemainingTime(),
1456                     thisPtr.PrepareAsyncCompletion(handleChannelClose),
1457                     thisPtr,
1458                     thisPtr.begin1,
1459                     thisPtr.end1,
1460                     thisPtr.communicationObjects);
1461
1462                 return thisPtr.SyncContinue(closeResult);
1463             }
1464
1465             static bool HandleChannelClose(IAsyncResult result)
1466             {
1467                 ChainedCloseAsyncResult.End(result);
1468                 return true;
1469             }
1470         }
1471     }
1472
1473     /// <summary>
1474     /// Handler wrapping the bottom (towards network) of the <see cref="HttpMessageHandler"/> and integrates
1475     /// back into the <see cref="IReplyChannel"/>.
1476     /// </summary>
1477     class TransportIntegrationHandler : DelegatingHandler
1478     {
1479         /// <summary>
1480         /// Initializes a new instance of the <see cref="TransportIntegrationHandler"/> class.
1481         /// </summary>
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)
1485         {
1486         }
1487
1488         /// <summary>
1489         /// Submits an <see cref="HttpRequestMessage"/> on the inner channel asynchronously.
1490         /// </summary>
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&lt;T&gt;"/> representing the operation.</returns>
1494         public Task<HttpResponseMessage> ProcessPipelineAsync(HttpRequestMessage request, CancellationToken cancellationToken)
1495         {
1496             return base.SendAsync(request, cancellationToken).ContinueWith(task =>
1497                 {
1498                     HttpResponseMessage httpResponse;
1499                     if (task.IsFaulted) 
1500                     {
1501                         if (Fx.IsFatal(task.Exception))
1502                         {
1503                             throw task.Exception;
1504                         }
1505
1506                         // We must inspect task.Exception -- otherwise it is automatically rethrown.
1507                         FxTrace.Exception.AsError<FaultException>(task.Exception);
1508                         httpResponse = TraceFaultAndGetResponseMessasge(request);
1509                     }
1510                     else if (task.IsCanceled)
1511                     {
1512                         HttpPipeline pipeline = HttpPipeline.GetHttpPipeline(request);
1513                         if (TD.HttpPipelineTimeoutExceptionIsEnabled())
1514                         {
1515                             TD.HttpPipelineTimeoutException(pipeline != null ? pipeline.EventTraceActivity : null);
1516                         }
1517
1518                         FxTrace.Exception.AsError(new TimeoutException(SR.GetString(SR.HttpPipelineOperationCanceledError)));
1519                         pipeline.Cancel();
1520                         httpResponse = null;
1521                     }
1522                     else
1523                     {
1524                         httpResponse = task.Result;
1525                         if (httpResponse == null)
1526                         {
1527                             FxTrace.Exception.AsError(new NotSupportedException(SR.GetString(SR.HttpPipelineNotSupportNullResponseMessage, typeof(DelegatingHandler).Name, typeof(HttpResponseMessage).Name)));
1528                             httpResponse = TraceFaultAndGetResponseMessasge(request);
1529                         }
1530                     }
1531
1532                     return httpResponse;
1533                 },
1534                 TaskContinuationOptions.ExecuteSynchronously);
1535         }
1536
1537         static HttpResponseMessage TraceFaultAndGetResponseMessasge(HttpRequestMessage request)
1538         {
1539             HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
1540             response.RequestMessage = request;
1541
1542             if (TD.HttpPipelineFaultedIsEnabled())
1543             {
1544                 HttpPipeline pipeline = HttpPipeline.GetHttpPipeline(request);
1545                 TD.HttpPipelineFaulted(pipeline != null ? pipeline.EventTraceActivity : null);
1546             }
1547
1548             return response;
1549         }
1550     }
1551
1552     /// <summary>
1553     /// Handler wrapping the top (towards Channel Model) of the <see cref="HttpMessageHandler"/> and integrates
1554     /// bask into the <see cref="IReplyChannel"/>.
1555     /// </summary>
1556     class ChannelModelIntegrationHandler : HttpMessageHandler
1557     {
1558         /// <summary>
1559         /// Submits an <see cref="HttpRequestMessage"/> on the inner channel asynchronously.
1560         /// </summary>
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&lt;T&gt;"/> representing the operation.</returns>
1564         protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
1565         {
1566             if (request == null)
1567             {
1568                 throw FxTrace.Exception.ArgumentNull("request");
1569             }
1570
1571             if (cancellationToken == null)
1572             {
1573                 throw FxTrace.Exception.ArgumentNull("cancellationToken");
1574             }
1575
1576             cancellationToken.ThrowIfCancellationRequested();
1577
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);
1581
1582             return httpPipeline.Dispatch(request);
1583         }
1584     }
1585 }