Merge pull request #3213 from henricm/fix-for-win-securestring-to-bstr
[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
843                     if (!env.UsingIntegratedPipeline)
844                     {
845                         throw FxTrace.Exception.AsError(new NotSupportedException(SR.GetString(SR.WebSocketsNotSupportedInClassicPipeline)));
846                     }
847                 }
848                 else if (!WebSocketHelper.OSSupportsWebSockets())
849                 {
850                     throw FxTrace.Exception.AsError(new PlatformNotSupportedException(SR.GetString(SR.WebSocketsServerSideNotSupported)));
851                 }
852
853                 this.currentWebSocketVersion = WebSocketHelper.GetCurrentVersion();
854                 this.acceptor = new InputQueueChannelAcceptor<TChannel>(this);
855                 int webSocketBufferSize = WebSocketHelper.ComputeServerBufferSize(bindingElement.MaxReceivedMessageSize);
856                 this.bufferPool = new ConnectionBufferPool(webSocketBufferSize);
857                 this.webSocketLifetimeManager = new CommunicationObjectManager<ServerWebSocketTransportDuplexSessionChannel>(this.ThisLock);
858             }
859             else
860             {
861                 this.acceptor = (InputQueueChannelAcceptor<TChannel>)(object)(new TransportReplyChannelAcceptor(this));
862             }
863
864             this.CreatePipeline(bindingElement.MessageHandlerFactory);
865         }
866
867         public override bool UseWebSocketTransport
868         {
869             get
870             {
871                 return this.useWebSocketTransport;
872             }
873         }
874
875         public InputQueueChannelAcceptor<TChannel> Acceptor
876         {
877             get { return this.acceptor; }
878         }
879
880         public override string Method
881         {
882             get
883             {
884                 if (this.UseWebSocketTransport)
885                 {
886                     return WebSocketTransportSettings.WebSocketMethod;
887                 }
888                 return base.Method;
889             }
890         }
891
892         public TChannel AcceptChannel()
893         {
894             return this.AcceptChannel(this.DefaultReceiveTimeout);
895         }
896
897         public IAsyncResult BeginAcceptChannel(AsyncCallback callback, object state)
898         {
899             return this.BeginAcceptChannel(this.DefaultReceiveTimeout, callback, state);
900         }
901
902         public TChannel AcceptChannel(TimeSpan timeout)
903         {
904             base.ThrowIfNotOpened();
905             return this.Acceptor.AcceptChannel(timeout);
906         }
907
908         public IAsyncResult BeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state)
909         {
910             base.ThrowIfNotOpened();
911             return this.Acceptor.BeginAcceptChannel(timeout, callback, state);
912         }
913
914         public TChannel EndAcceptChannel(IAsyncResult result)
915         {
916             base.ThrowPending();
917             return this.Acceptor.EndAcceptChannel(result);
918         }
919
920         public override bool CreateWebSocketChannelAndEnqueue(HttpRequestContext httpRequestContext, HttpPipeline pipeline, HttpResponseMessage httpResponseMessage, string subProtocol, Action dequeuedCallback)
921         {
922             Fx.Assert(this.WebSocketSettings.MaxPendingConnections > 0, "MaxPendingConnections should be positive.");
923             if (this.Acceptor.PendingCount >= this.WebSocketSettings.MaxPendingConnections)
924             {
925                 if (TD.MaxPendingConnectionsExceededIsEnabled())
926                 {
927                     TD.MaxPendingConnectionsExceeded(SR.GetString(SR.WebSocketMaxPendingConnectionsReached, this.WebSocketSettings.MaxPendingConnections, WebSocketHelper.MaxPendingConnectionsString, WebSocketHelper.WebSocketTransportSettingsString));
928                 }
929
930                 if (DiagnosticUtility.ShouldTraceWarning)
931                 {
932                     TraceUtility.TraceEvent(TraceEventType.Warning,
933                         TraceCode.MaxPendingConnectionsReached, SR.GetString(SR.WebSocketMaxPendingConnectionsReached, this.WebSocketSettings.MaxPendingConnections, WebSocketHelper.MaxPendingConnectionsString, WebSocketHelper.WebSocketTransportSettingsString),
934                         new StringTraceRecord(WebSocketHelper.MaxPendingConnectionsString, this.WebSocketSettings.MaxPendingConnections.ToString(System.Globalization.CultureInfo.InvariantCulture)),
935                         this,
936                         null);
937                 }
938
939                 return false;
940             }
941
942             ServerWebSocketTransportDuplexSessionChannel channel = new ServerWebSocketTransportDuplexSessionChannel(this,
943                                     new EndpointAddress(this.Uri), this.Uri, this.bufferPool, httpRequestContext, pipeline, httpResponseMessage, subProtocol);
944             httpRequestContext.WebSocketChannel = channel;
945
946             // webSocketLifetimeManager hooks into the channel.Closed event as well and will take care of cleaning itself up OnClosed. 
947             // We want to be called before any user-specified close handlers are called. 
948             this.webSocketLifetimeManager.Add(channel);
949             this.Acceptor.EnqueueAndDispatch((TChannel)(object)channel, dequeuedCallback, true);
950             return true;
951         }
952
953         public override byte[] TakeWebSocketInternalBuffer()
954         {
955             Fx.Assert(this.bufferPool != null, "bufferPool should not be null.");
956             return this.bufferPool.Take();
957         }
958
959         public override void ReturnWebSocketInternalBuffer(byte[] buffer)
960         {
961             Fx.Assert(this.bufferPool != null, "bufferPool should not be null.");
962             this.bufferPool.Return(buffer);
963         }
964
965         protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
966         {
967             return new ChainedOpenAsyncResult(timeout, callback, state, base.OnBeginOpen, base.OnEndOpen, this.Acceptor);
968         }
969
970         protected override void OnOpen(TimeSpan timeout)
971         {
972             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
973             base.OnOpen(timeoutHelper.RemainingTime());
974             this.Acceptor.Open(timeoutHelper.RemainingTime());
975         }
976
977         protected override void OnEndOpen(IAsyncResult result)
978         {
979             ChainedOpenAsyncResult.End(result);
980         }
981
982         protected override void OnClose(TimeSpan timeout)
983         {
984             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
985             this.Acceptor.Close(timeoutHelper.RemainingTime());
986             if (this.IsAuthenticationSupported)
987             {
988                 CloseUserNameTokenAuthenticator(timeoutHelper.RemainingTime());
989             }
990             if (this.useWebSocketTransport)
991             {
992                 this.webSocketLifetimeManager.Close(timeoutHelper.RemainingTime());
993             }
994             base.OnClose(timeoutHelper.RemainingTime());
995         }
996
997         protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
998         {
999             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1000             ICommunicationObject[] communicationObjects;
1001             ICommunicationObject communicationObject = this.UserNameTokenAuthenticator as ICommunicationObject;
1002             if (communicationObject == null)
1003             {
1004                 if (this.IsAuthenticationSupported)
1005                 {
1006                     CloseUserNameTokenAuthenticator(timeoutHelper.RemainingTime());
1007                 }
1008                 communicationObjects = new ICommunicationObject[] { this.Acceptor };
1009             }
1010             else
1011             {
1012                 communicationObjects = new ICommunicationObject[] { this.Acceptor, communicationObject };
1013             }
1014
1015             if (this.useWebSocketTransport)
1016             {
1017                 return new LifetimeWrappedCloseAsyncResult<ServerWebSocketTransportDuplexSessionChannel>(
1018                     timeoutHelper.RemainingTime(),
1019                     callback,
1020                     state,
1021                     this.webSocketLifetimeManager,
1022                     base.OnBeginClose,
1023                     base.OnEndClose,
1024                     communicationObjects);
1025             }
1026             else
1027             {
1028                 return new ChainedCloseAsyncResult(timeoutHelper.RemainingTime(), callback, state, base.OnBeginClose, base.OnEndClose, communicationObjects);
1029             }
1030         }
1031
1032         protected override void OnEndClose(IAsyncResult result)
1033         {
1034             if (this.useWebSocketTransport)
1035             {
1036                 LifetimeWrappedCloseAsyncResult<ServerWebSocketTransportDuplexSessionChannel>.End(result);
1037             }
1038             else
1039             {
1040                 ChainedCloseAsyncResult.End(result);
1041             }
1042         }
1043
1044         protected override void OnClosed()
1045         {
1046             base.OnClosed();
1047             if (this.bufferPool != null)
1048             {
1049                 this.bufferPool.Close();
1050             }
1051
1052             if (this.transportIntegrationHandler != null)
1053             {
1054                 this.transportIntegrationHandler.Dispose();
1055             }
1056         }
1057
1058         protected override void OnAbort()
1059         {
1060             if (this.IsAuthenticationSupported)
1061             {
1062                 AbortUserNameTokenAuthenticator();
1063             }
1064
1065             this.Acceptor.Abort();
1066
1067             if (this.useWebSocketTransport)
1068             {
1069                 this.webSocketLifetimeManager.Abort();
1070             }
1071
1072             base.OnAbort();
1073         }
1074
1075         protected override bool OnWaitForChannel(TimeSpan timeout)
1076         {
1077             return Acceptor.WaitForChannel(timeout);
1078         }
1079
1080         protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state)
1081         {
1082             return Acceptor.BeginWaitForChannel(timeout, callback, state);
1083         }
1084
1085         protected override bool OnEndWaitForChannel(IAsyncResult result)
1086         {
1087             return Acceptor.EndWaitForChannel(result);
1088         }
1089
1090         internal override IAsyncResult BeginHttpContextReceived(HttpRequestContext context,
1091                                                         Action acceptorCallback,
1092                                                         AsyncCallback callback,
1093                                                         object state)
1094         {
1095             return new HttpContextReceivedAsyncResult<TChannel>(
1096                 context,
1097                 acceptorCallback,
1098                 this,
1099                 callback,
1100                 state);
1101         }
1102
1103         internal override bool EndHttpContextReceived(IAsyncResult result)
1104         {
1105             return HttpContextReceivedAsyncResult<TChannel>.End(result);
1106         }
1107
1108         void CreatePipeline(HttpMessageHandlerFactory httpMessageHandlerFactory)
1109         {
1110             HttpMessageHandler innerPipeline;
1111             if (this.UseWebSocketTransport)
1112             {
1113                 innerPipeline = new DefaultWebSocketConnectionHandler(this.WebSocketSettings.SubProtocol, this.currentWebSocketVersion, this.MessageVersion, this.MessageEncoderFactory, this.TransferMode);
1114                 if (httpMessageHandlerFactory != null)
1115                 {
1116                     innerPipeline = httpMessageHandlerFactory.Create(innerPipeline);
1117                 }
1118             }
1119             else
1120             {
1121                 if (httpMessageHandlerFactory == null)
1122                 {
1123                     return;
1124                 }
1125
1126                 innerPipeline = httpMessageHandlerFactory.Create(new ChannelModelIntegrationHandler());
1127             }
1128
1129             if (innerPipeline == null)
1130             {
1131                 throw FxTrace.Exception.AsError(
1132                     new InvalidOperationException(SR.GetString(SR.HttpMessageHandlerChannelFactoryNullPipeline,
1133                         httpMessageHandlerFactory.GetType().Name, typeof(HttpRequestContext).Name)));
1134             }
1135
1136             this.transportIntegrationHandler = new TransportIntegrationHandler(innerPipeline);
1137         }
1138
1139         static void HandleProcessInboundException(Exception ex, HttpRequestContext context)
1140         {
1141             if (Fx.IsFatal(ex))
1142             {
1143                 return;
1144             }
1145
1146             if (ex is ProtocolException)
1147             {
1148                 ProtocolException protocolException = (ProtocolException)ex;
1149                 HttpStatusCode statusCode = HttpStatusCode.BadRequest;
1150                 string statusDescription = string.Empty;
1151                 if (protocolException.Data.Contains(HttpChannelUtilities.HttpStatusCodeExceptionKey))
1152                 {
1153                     statusCode = (HttpStatusCode)protocolException.Data[HttpChannelUtilities.HttpStatusCodeExceptionKey];
1154                     protocolException.Data.Remove(HttpChannelUtilities.HttpStatusCodeExceptionKey);
1155                 }
1156                 if (protocolException.Data.Contains(HttpChannelUtilities.HttpStatusDescriptionExceptionKey))
1157                 {
1158                     statusDescription = (string)protocolException.Data[HttpChannelUtilities.HttpStatusDescriptionExceptionKey];
1159                     protocolException.Data.Remove(HttpChannelUtilities.HttpStatusDescriptionExceptionKey);
1160                 }
1161                 context.SendResponseAndClose(statusCode, statusDescription);
1162
1163             }
1164             else
1165             {
1166                 try
1167                 {
1168                     context.SendResponseAndClose(HttpStatusCode.BadRequest);
1169                 }
1170                 catch (Exception closeException)
1171                 {
1172                     if (Fx.IsFatal(closeException))
1173                     {
1174                         throw;
1175                     }
1176
1177                     DiagnosticUtility.TraceHandledException(closeException, TraceEventType.Error);
1178                 }
1179             }
1180         }
1181
1182         static bool ContextReceiveExceptionHandled(Exception e)
1183         {
1184             if (Fx.IsFatal(e))
1185             {
1186                 return false;
1187             }
1188
1189             if (e is CommunicationException)
1190             {
1191                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1192             }
1193             else if (e is XmlException)
1194             {
1195                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1196             }
1197             else if (e is IOException)
1198             {
1199                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1200             }
1201             else if (e is TimeoutException)
1202             {
1203                 if (TD.ReceiveTimeoutIsEnabled())
1204                 {
1205                     TD.ReceiveTimeout(e.Message);
1206                 }
1207                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1208             }
1209             else if (e is OperationCanceledException)
1210             {
1211                 DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
1212             }
1213             else if (!ExceptionHandler.HandleTransportExceptionHelper(e))
1214             {
1215                 return false;
1216             }
1217
1218             return true;
1219         }
1220
1221         class HttpContextReceivedAsyncResult<TListenerChannel> : TraceAsyncResult where TListenerChannel : class, IChannel
1222         {
1223             static AsyncCallback onProcessInboundRequest = Fx.ThunkCallback(OnProcessInboundRequest);
1224             bool enqueued;
1225             HttpRequestContext context;
1226             Action acceptorCallback;
1227             HttpChannelListener<TListenerChannel> listener;
1228
1229             public HttpContextReceivedAsyncResult(
1230                                                 HttpRequestContext requestContext,
1231                                                 Action acceptorCallback,
1232                                                 HttpChannelListener<TListenerChannel> listener,
1233                                                 AsyncCallback callback,
1234                                                 object state)
1235                 : base(callback, state)
1236             {
1237                 this.context = requestContext;
1238                 this.acceptorCallback = acceptorCallback;
1239                 this.listener = listener;
1240
1241                 if (this.ProcessHttpContextAsync() == AsyncCompletionResult.Completed)
1242                 {
1243                     base.Complete(true);
1244                 }
1245             }
1246
1247             public static bool End(IAsyncResult result)
1248             {
1249                 return AsyncResult.End<HttpContextReceivedAsyncResult<TListenerChannel>>(result).enqueued;
1250             }
1251
1252             static void OnProcessInboundRequest(IAsyncResult result)
1253             {
1254                 if (result.CompletedSynchronously)
1255                 {
1256                     return;
1257                 }
1258
1259                 HttpContextReceivedAsyncResult<TListenerChannel> thisPtr = (HttpContextReceivedAsyncResult<TListenerChannel>)result.AsyncState;
1260                 Exception completionException = null;
1261
1262                 try
1263                 {
1264                     thisPtr.HandleProcessInboundRequest(result);
1265                 }
1266                 catch (Exception ex)
1267                 {
1268                     if (Fx.IsFatal(ex))
1269                     {
1270                         throw;
1271                     }
1272
1273                     completionException = ex;
1274                 }
1275
1276                 thisPtr.Complete(false, completionException);
1277             }
1278
1279             AsyncCompletionResult ProcessHttpContextAsync()
1280             {
1281                 bool abort = false;
1282                 try
1283                 {
1284                     this.context.InitializeHttpPipeline(this.listener.transportIntegrationHandler);
1285                     if (!this.Authenticate())
1286                     {
1287                         return AsyncCompletionResult.Completed;
1288                     }
1289
1290                     if (listener.UseWebSocketTransport && !context.IsWebSocketRequest)
1291                     {
1292                         this.context.SendResponseAndClose(HttpStatusCode.BadRequest, SR.GetString(SR.WebSocketEndpointOnlySupportWebSocketError));
1293                         return AsyncCompletionResult.Completed;
1294                     }
1295
1296                     if (!listener.UseWebSocketTransport && context.IsWebSocketRequest)
1297                     {
1298                         this.context.SendResponseAndClose(HttpStatusCode.BadRequest, SR.GetString(SR.WebSocketEndpointDoesNotSupportWebSocketError));
1299                         return AsyncCompletionResult.Completed;
1300                     }
1301
1302                     try
1303                     {
1304                         IAsyncResult result = context.BeginProcessInboundRequest(listener.Acceptor as ReplyChannelAcceptor,
1305                                                                                             this.acceptorCallback,
1306                                                                                             onProcessInboundRequest,
1307                                                                                             this);
1308                         if (result.CompletedSynchronously)
1309                         {
1310                             this.EndInboundProcessAndEnqueue(result);
1311                             return AsyncCompletionResult.Completed;
1312                         }
1313                     }
1314                     catch (Exception ex)
1315                     {
1316                         HandleProcessInboundException(ex, this.context);
1317                         throw;
1318                     }
1319                 }
1320                 catch (Exception ex)
1321                 {
1322                     // containment -- we abort the context in all error cases, no additional containment action needed                
1323                     abort = true;
1324                     if (!ContextReceiveExceptionHandled(ex))
1325                     {
1326                         throw;
1327                     }
1328                 }
1329                 finally
1330                 {
1331                     if (abort)
1332                     {
1333                         context.Abort();
1334                     }
1335                 }
1336
1337                 return abort ? AsyncCompletionResult.Completed : AsyncCompletionResult.Queued;
1338             }
1339
1340             bool Authenticate()
1341             {
1342                 if (!this.context.ProcessAuthentication())
1343                 {
1344                     if (TD.HttpAuthFailedIsEnabled())
1345                     {
1346                         TD.HttpAuthFailed(context.EventTraceActivity);
1347                     }
1348
1349                     if (DiagnosticUtility.ShouldTraceInformation)
1350                     {
1351                         TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.HttpAuthFailed, SR.GetString(SR.TraceCodeHttpAuthFailed), this);
1352                     }
1353
1354                     return false;
1355                 }
1356
1357                 return true;
1358             }
1359
1360             void HandleProcessInboundRequest(IAsyncResult result)
1361             {
1362                 bool abort = true;
1363                 try
1364                 {
1365                     try
1366                     {
1367                         this.EndInboundProcessAndEnqueue(result);
1368                         abort = false;
1369                     }
1370                     catch (Exception ex)
1371                     {
1372                         HandleProcessInboundException(ex, this.context);
1373                         throw;
1374                     }
1375                 }
1376                 catch (Exception ex)
1377                 {
1378                     // containment -- we abort the context in all error cases, no additional containment action needed                                    
1379                     if (!ContextReceiveExceptionHandled(ex))
1380                     {
1381                         throw;
1382                     }
1383                 }
1384                 finally
1385                 {
1386                     if (abort)
1387                     {
1388                         context.Abort();
1389                     }
1390                 }
1391             }
1392
1393             void EndInboundProcessAndEnqueue(IAsyncResult result)
1394             {
1395                 Fx.Assert(result != null, "Trying to complete without issuing a BeginProcessInboundRequest.");
1396                 context.EndProcessInboundRequest(result);
1397
1398                 //We have finally managed to enqueue the message.
1399                 this.enqueued = true;
1400             }
1401         }
1402
1403         class LifetimeWrappedCloseAsyncResult<TCommunicationObject> : AsyncResult where TCommunicationObject : CommunicationObject
1404         {
1405             static AsyncCompletion handleLifetimeManagerClose = new AsyncCompletion(HandleLifetimeManagerClose);
1406             static AsyncCompletion handleChannelClose = new AsyncCompletion(HandleChannelClose);
1407
1408             TimeoutHelper timeoutHelper;
1409
1410             ICommunicationObject[] communicationObjects;
1411             CommunicationObjectManager<TCommunicationObject> communicationObjectManager;
1412             ChainedBeginHandler begin1;
1413             ChainedEndHandler end1;
1414
1415             public LifetimeWrappedCloseAsyncResult(TimeSpan timeout, AsyncCallback callback, object state, CommunicationObjectManager<TCommunicationObject> communicationObjectManager, ChainedBeginHandler begin1, ChainedEndHandler end1, ICommunicationObject[] communicationObjects)
1416                 : base(callback, state)
1417             {
1418                 this.timeoutHelper = new TimeoutHelper(timeout);
1419                 this.begin1 = begin1;
1420                 this.end1 = end1;
1421                 this.communicationObjects = communicationObjects;
1422                 this.communicationObjectManager = communicationObjectManager;
1423
1424                 IAsyncResult result = communicationObjectManager.BeginClose(
1425                     this.timeoutHelper.RemainingTime(),
1426                     PrepareAsyncCompletion(handleLifetimeManagerClose),
1427                     this);
1428
1429                 bool completeSelf = SyncContinue(result);
1430
1431                 if (completeSelf)
1432                 {
1433                     this.Complete(true);
1434                 }
1435             }
1436
1437             public static void End(IAsyncResult result)
1438             {
1439                 AsyncResult.End<LifetimeWrappedCloseAsyncResult<TCommunicationObject>>(result);
1440             }
1441
1442             static bool HandleLifetimeManagerClose(IAsyncResult result)
1443             {
1444                 LifetimeWrappedCloseAsyncResult<TCommunicationObject> thisPtr = (LifetimeWrappedCloseAsyncResult<TCommunicationObject>)result.AsyncState;
1445                 thisPtr.communicationObjectManager.EndClose(result);
1446
1447                 // begin second step of the close... 
1448                 ChainedCloseAsyncResult closeResult = new ChainedCloseAsyncResult(
1449                     thisPtr.timeoutHelper.RemainingTime(),
1450                     thisPtr.PrepareAsyncCompletion(handleChannelClose),
1451                     thisPtr,
1452                     thisPtr.begin1,
1453                     thisPtr.end1,
1454                     thisPtr.communicationObjects);
1455
1456                 return thisPtr.SyncContinue(closeResult);
1457             }
1458
1459             static bool HandleChannelClose(IAsyncResult result)
1460             {
1461                 ChainedCloseAsyncResult.End(result);
1462                 return true;
1463             }
1464         }
1465     }
1466
1467     /// <summary>
1468     /// Handler wrapping the bottom (towards network) of the <see cref="HttpMessageHandler"/> and integrates
1469     /// back into the <see cref="IReplyChannel"/>.
1470     /// </summary>
1471     class TransportIntegrationHandler : DelegatingHandler
1472     {
1473         /// <summary>
1474         /// Initializes a new instance of the <see cref="TransportIntegrationHandler"/> class.
1475         /// </summary>
1476         /// <param name="innerChannel">The inner <see cref="HttpMessageHandler"/> on which we send the <see cref="HttpRequestMessage"/>.</param>
1477         public TransportIntegrationHandler(HttpMessageHandler innerChannel)
1478             : base(innerChannel)
1479         {
1480         }
1481
1482         /// <summary>
1483         /// Submits an <see cref="HttpRequestMessage"/> on the inner channel asynchronously.
1484         /// </summary>
1485         /// <param name="request"><see cref="HttpRequestMessage"/> to submit</param>
1486         /// <param name="cancellationToken">Token used to cancel operation.</param>
1487         /// <returns>A <see cref="Task&lt;T&gt;"/> representing the operation.</returns>
1488         public Task<HttpResponseMessage> ProcessPipelineAsync(HttpRequestMessage request, CancellationToken cancellationToken)
1489         {
1490             return base.SendAsync(request, cancellationToken).ContinueWith(task =>
1491                 {
1492                     HttpResponseMessage httpResponse;
1493                     if (task.IsFaulted) 
1494                     {
1495                         if (Fx.IsFatal(task.Exception))
1496                         {
1497                             throw task.Exception;
1498                         }
1499
1500                         // We must inspect task.Exception -- otherwise it is automatically rethrown.
1501                         FxTrace.Exception.AsError<FaultException>(task.Exception);
1502                         httpResponse = TraceFaultAndGetResponseMessasge(request);
1503                     }
1504                     else if (task.IsCanceled)
1505                     {
1506                         HttpPipeline pipeline = HttpPipeline.GetHttpPipeline(request);
1507                         if (TD.HttpPipelineTimeoutExceptionIsEnabled())
1508                         {
1509                             TD.HttpPipelineTimeoutException(pipeline != null ? pipeline.EventTraceActivity : null);
1510                         }
1511
1512                         FxTrace.Exception.AsError(new TimeoutException(SR.GetString(SR.HttpPipelineOperationCanceledError)));
1513                         pipeline.Cancel();
1514                         httpResponse = null;
1515                     }
1516                     else
1517                     {
1518                         httpResponse = task.Result;
1519                         if (httpResponse == null)
1520                         {
1521                             FxTrace.Exception.AsError(new NotSupportedException(SR.GetString(SR.HttpPipelineNotSupportNullResponseMessage, typeof(DelegatingHandler).Name, typeof(HttpResponseMessage).Name)));
1522                             httpResponse = TraceFaultAndGetResponseMessasge(request);
1523                         }
1524                     }
1525
1526                     return httpResponse;
1527                 },
1528                 TaskContinuationOptions.ExecuteSynchronously);
1529         }
1530
1531         static HttpResponseMessage TraceFaultAndGetResponseMessasge(HttpRequestMessage request)
1532         {
1533             HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
1534             response.RequestMessage = request;
1535
1536             if (TD.HttpPipelineFaultedIsEnabled())
1537             {
1538                 HttpPipeline pipeline = HttpPipeline.GetHttpPipeline(request);
1539                 TD.HttpPipelineFaulted(pipeline != null ? pipeline.EventTraceActivity : null);
1540             }
1541
1542             return response;
1543         }
1544     }
1545
1546     /// <summary>
1547     /// Handler wrapping the top (towards Channel Model) of the <see cref="HttpMessageHandler"/> and integrates
1548     /// bask into the <see cref="IReplyChannel"/>.
1549     /// </summary>
1550     class ChannelModelIntegrationHandler : HttpMessageHandler
1551     {
1552         /// <summary>
1553         /// Submits an <see cref="HttpRequestMessage"/> on the inner channel asynchronously.
1554         /// </summary>
1555         /// <param name="request"><see cref="HttpRequestMessage"/> to submit</param>
1556         /// <param name="cancellationToken">Token used to cancel operation.</param>
1557         /// <returns>A <see cref="Task&lt;T&gt;"/> representing the operation.</returns>
1558         protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
1559         {
1560             if (request == null)
1561             {
1562                 throw FxTrace.Exception.ArgumentNull("request");
1563             }
1564
1565             if (cancellationToken == null)
1566             {
1567                 throw FxTrace.Exception.ArgumentNull("cancellationToken");
1568             }
1569
1570             cancellationToken.ThrowIfCancellationRequested();
1571
1572             HttpChannelUtilities.EnsureHttpRequestMessageContentNotNull(request);
1573             //// We ran up through the pipeline and are now ready to hook back into the WCF channel model
1574             HttpPipeline httpPipeline = HttpPipeline.GetHttpPipeline(request);
1575
1576             return httpPipeline.Dispatch(request);
1577         }
1578     }
1579 }