1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
4 namespace System
.ServiceModel
.Channels
6 using System
.Collections
.ObjectModel
;
7 using System
.Diagnostics
;
8 using System
.IdentityModel
.Policy
;
9 using System
.IdentityModel
.Selectors
;
10 using System
.IdentityModel
.Tokens
;
13 using System
.Net
.Cache
;
14 using System
.Net
.Security
;
16 using System
.Runtime
.CompilerServices
;
17 using System
.Runtime
.Diagnostics
;
18 using System
.Security
;
19 using System
.Security
.Authentication
.ExtendedProtection
;
20 using System
.Security
.Cryptography
;
21 using System
.Security
.Permissions
;
22 using System
.Security
.Principal
;
23 using System
.ServiceModel
;
24 using System
.ServiceModel
.Description
;
25 using System
.ServiceModel
.Diagnostics
;
26 using System
.ServiceModel
.Diagnostics
.Application
;
27 using System
.ServiceModel
.Security
;
28 using System
.ServiceModel
.Security
.Tokens
;
30 using System
.Threading
;
32 class HttpChannelFactory
<TChannel
>
33 : TransportChannelFactory
<TChannel
>,
34 IHttpTransportFactorySettings
36 static bool httpWebRequestWebPermissionDenied
= false;
37 static RequestCachePolicy requestCachePolicy
= new RequestCachePolicy(RequestCacheLevel
.BypassCache
);
38 static long connectionGroupNamePrefix
= 0;
40 readonly ClientWebSocketFactory clientWebSocketFactory
;
43 AuthenticationSchemes authenticationScheme
;
44 HttpCookieContainerManager httpCookieContainerManager
;
46 // Double-checked locking pattern requires volatile for read/write synchronization
47 volatile MruCache
<Uri
, Uri
> credentialCacheUriPrefixCache
;
48 bool decompressionEnabled
;
50 // Double-checked locking pattern requires volatile for read/write synchronization
51 [Fx
.Tag
.SecurityNote(Critical
= "This cache stores strings that contain domain/user name/password. Must not be settable from PT code.")]
53 volatile MruCache
<string, string> credentialHashCache
;
55 [Fx
.Tag
.SecurityNote(Critical
= "This hash algorithm takes strings that contain domain/user name/password. Must not be settable from PT code.")]
57 HashAlgorithm hashAlgorithm
;
58 bool keepAliveEnabled
;
61 WebProxyFactory proxyFactory
;
62 SecurityCredentialsManager channelCredentials
;
63 SecurityTokenManager securityTokenManager
;
64 TransferMode transferMode
;
65 ISecurityCapabilities securityCapabilities
;
66 WebSocketTransportSettings webSocketSettings
;
67 ConnectionBufferPool bufferPool
;
68 Lazy
<string> webSocketSoapContentType
;
69 string uniqueConnectionGroupNamePrefix
;
71 internal HttpChannelFactory(HttpTransportBindingElement bindingElement
, BindingContext context
)
72 : base(bindingElement
, context
, HttpTransportDefaults
.GetDefaultMessageEncoderFactory())
74 // validate setting interactions
75 if (bindingElement
.TransferMode
== TransferMode
.Buffered
)
77 if (bindingElement
.MaxReceivedMessageSize
> int.MaxValue
)
79 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(
80 new ArgumentOutOfRangeException("bindingElement.MaxReceivedMessageSize",
81 SR
.GetString(SR
.MaxReceivedMessageSizeMustBeInIntegerRange
)));
84 if (bindingElement
.MaxBufferSize
!= bindingElement
.MaxReceivedMessageSize
)
86 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperArgument("bindingElement",
87 SR
.GetString(SR
.MaxBufferSizeMustMatchMaxReceivedMessageSize
));
92 if (bindingElement
.MaxBufferSize
> bindingElement
.MaxReceivedMessageSize
)
94 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperArgument("bindingElement",
95 SR
.GetString(SR
.MaxBufferSizeMustNotExceedMaxReceivedMessageSize
));
99 if (TransferModeHelper
.IsRequestStreamed(bindingElement
.TransferMode
) &&
100 bindingElement
.AuthenticationScheme
!= AuthenticationSchemes
.Anonymous
)
102 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperArgument("bindingElement", SR
.GetString(
103 SR
.HttpAuthDoesNotSupportRequestStreaming
));
106 this.allowCookies
= bindingElement
.AllowCookies
;
107 #pragma warning disable 618
108 if (!this.allowCookies
)
110 Collection
<HttpCookieContainerBindingElement
> httpCookieContainerBindingElements
= context
.BindingParameters
.FindAll
<HttpCookieContainerBindingElement
>();
111 if (httpCookieContainerBindingElements
.Count
> 1)
113 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new InvalidOperationException(SR
.GetString(SR
.MultipleCCbesInParameters
, typeof(HttpCookieContainerBindingElement
))));
115 if (httpCookieContainerBindingElements
.Count
== 1)
117 this.allowCookies
= true;
118 context
.BindingParameters
.Remove
<HttpCookieContainerBindingElement
>();
121 #pragma warning restore 618
123 if (this.allowCookies
)
125 this.httpCookieContainerManager
= new HttpCookieContainerManager();
128 if (!bindingElement
.AuthenticationScheme
.IsSingleton())
130 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperArgument("value", SR
.GetString(SR
.HttpRequiresSingleAuthScheme
,
131 bindingElement
.AuthenticationScheme
));
134 this.authenticationScheme
= bindingElement
.AuthenticationScheme
;
135 this.decompressionEnabled
= bindingElement
.DecompressionEnabled
;
136 this.keepAliveEnabled
= bindingElement
.KeepAliveEnabled
;
137 this.maxBufferSize
= bindingElement
.MaxBufferSize
;
138 this.transferMode
= bindingElement
.TransferMode
;
140 if (bindingElement
.Proxy
!= null)
142 this.proxy
= bindingElement
.Proxy
;
144 else if (bindingElement
.ProxyAddress
!= null)
146 if (bindingElement
.UseDefaultWebProxy
)
148 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new InvalidOperationException(SR
.GetString(SR
.UseDefaultWebProxyCantBeUsedWithExplicitProxyAddress
)));
151 if (bindingElement
.ProxyAuthenticationScheme
== AuthenticationSchemes
.Anonymous
)
153 this.proxy
= new WebProxy(bindingElement
.ProxyAddress
, bindingElement
.BypassProxyOnLocal
);
159 new WebProxyFactory(bindingElement
.ProxyAddress
, bindingElement
.BypassProxyOnLocal
,
160 bindingElement
.ProxyAuthenticationScheme
);
163 else if (!bindingElement
.UseDefaultWebProxy
)
165 this.proxy
= new WebProxy();
168 this.channelCredentials
= context
.BindingParameters
.Find
<SecurityCredentialsManager
>();
169 this.securityCapabilities
= bindingElement
.GetProperty
<ISecurityCapabilities
>(context
);
170 this.webSocketSettings
= WebSocketHelper
.GetRuntimeWebSocketSettings(bindingElement
.WebSocketSettings
);
172 int webSocketBufferSize
= WebSocketHelper
.ComputeClientBufferSize(this.MaxReceivedMessageSize
);
173 this.bufferPool
= new ConnectionBufferPool(webSocketBufferSize
);
175 Collection
<ClientWebSocketFactory
> clientWebSocketFactories
= context
.BindingParameters
.FindAll
<ClientWebSocketFactory
>();
176 if (clientWebSocketFactories
.Count
> 1)
178 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperArgument(
180 SR
.GetString(SR
.MultipleClientWebSocketFactoriesSpecified
, typeof(BindingContext
).Name
, typeof(ClientWebSocketFactory
).Name
));
184 this.clientWebSocketFactory
= clientWebSocketFactories
.Count
== 0 ? null : clientWebSocketFactories
[0];
187 this.webSocketSoapContentType
= new Lazy
<string>(() => { return this.MessageEncoderFactory.CreateSessionEncoder().ContentType; }
, LazyThreadSafetyMode
.ExecutionAndPublication
);
189 if (ServiceModelAppSettings
.HttpTransportPerFactoryConnectionPool
)
191 this.uniqueConnectionGroupNamePrefix
= Interlocked
.Increment(ref connectionGroupNamePrefix
).ToString();
195 this.uniqueConnectionGroupNamePrefix
= string.Empty
;
199 public bool AllowCookies
203 return this.allowCookies
;
207 public AuthenticationSchemes AuthenticationScheme
211 return this.authenticationScheme
;
215 public bool DecompressionEnabled
219 return this.decompressionEnabled
;
223 public virtual bool IsChannelBindingSupportEnabled
231 public bool KeepAliveEnabled
235 return this.keepAliveEnabled
;
239 public SecurityTokenManager SecurityTokenManager
243 return this.securityTokenManager
;
247 public int MaxBufferSize
251 return maxBufferSize
;
255 public IWebProxy Proxy
263 public TransferMode TransferMode
271 public override string Scheme
275 return Uri
.UriSchemeHttp
;
279 public WebSocketTransportSettings WebSocketSettings
281 get { return this.webSocketSettings; }
284 internal string WebSocketSoapContentType
288 return this.webSocketSoapContentType
.Value
;
292 protected ConnectionBufferPool WebSocketBufferPool
294 get { return this.bufferPool; }
297 // must be called under lock (this.credentialHashCache)
298 HashAlgorithm HashAlgorithm
303 if (this.hashAlgorithm
== null)
305 this.hashAlgorithm
= CryptoHelper
.CreateHashAlgorithm(SecurityAlgorithms
.Sha1Digest
);
309 this.hashAlgorithm
.Initialize();
312 return this.hashAlgorithm
;
316 int IHttpTransportFactorySettings
.MaxBufferSize
318 get { return MaxBufferSize; }
321 TransferMode IHttpTransportFactorySettings
.TransferMode
323 get { return TransferMode; }
326 protected ClientWebSocketFactory ClientWebSocketFactory
330 return this.clientWebSocketFactory
;
334 public override T GetProperty
<T
>()
336 if (typeof(T
) == typeof(ISecurityCapabilities
))
338 return (T
)(object)this.securityCapabilities
;
340 if (typeof(T
) == typeof(IHttpCookieContainerManager
))
342 return (T
)(object)this.GetHttpCookieContainerManager();
345 return base.GetProperty
<T
>();
348 [PermissionSet(SecurityAction
.Demand
, Unrestricted
= true), SecuritySafeCritical
]
349 [MethodImpl(MethodImplOptions
.NoInlining
)]
350 private HttpCookieContainerManager
GetHttpCookieContainerManager()
352 return this.httpCookieContainerManager
;
355 internal virtual SecurityMessageProperty
CreateReplySecurityProperty(HttpWebRequest request
,
356 HttpWebResponse response
)
358 // Don't pull in System.Authorization if we don't need to!
359 if (!response
.IsMutuallyAuthenticated
)
364 return CreateMutuallyAuthenticatedReplySecurityProperty(response
);
367 internal Exception
CreateToMustEqualViaException(Uri to
, Uri via
)
369 return new ArgumentException(SR
.GetString(SR
.HttpToMustEqualVia
, to
, via
));
372 [MethodImpl(MethodImplOptions
.NoInlining
)]
373 SecurityMessageProperty
CreateMutuallyAuthenticatedReplySecurityProperty(HttpWebResponse response
)
375 string spn
= AuthenticationManager
.CustomTargetNameDictionary
[response
.ResponseUri
.AbsoluteUri
];
378 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new MessageSecurityException(SR
.GetString(SR
.HttpSpnNotFound
,
379 response
.ResponseUri
)));
381 ReadOnlyCollection
<IAuthorizationPolicy
> spnPolicies
= SecurityUtils
.CreatePrincipalNameAuthorizationPolicies(spn
);
382 SecurityMessageProperty remoteSecurity
= new SecurityMessageProperty();
383 remoteSecurity
.TransportToken
= new SecurityTokenSpecification(null, spnPolicies
);
384 remoteSecurity
.ServiceSecurityContext
= new ServiceSecurityContext(spnPolicies
);
385 return remoteSecurity
;
388 internal override int GetMaxBufferSize()
390 return MaxBufferSize
;
393 SecurityTokenProviderContainer
CreateAndOpenTokenProvider(TimeSpan timeout
, AuthenticationSchemes authenticationScheme
,
394 EndpointAddress target
, Uri via
, ChannelParameterCollection channelParameters
)
396 SecurityTokenProvider tokenProvider
= null;
397 switch (authenticationScheme
)
399 case AuthenticationSchemes
.Anonymous
:
401 case AuthenticationSchemes
.Basic
:
402 tokenProvider
= TransportSecurityHelpers
.GetUserNameTokenProvider(this.SecurityTokenManager
, target
, via
, this.Scheme
, authenticationScheme
, channelParameters
);
404 case AuthenticationSchemes
.Negotiate
:
405 case AuthenticationSchemes
.Ntlm
:
406 tokenProvider
= TransportSecurityHelpers
.GetSspiTokenProvider(this.SecurityTokenManager
, target
, via
, this.Scheme
, authenticationScheme
, channelParameters
);
408 case AuthenticationSchemes
.Digest
:
409 tokenProvider
= TransportSecurityHelpers
.GetDigestTokenProvider(this.SecurityTokenManager
, target
, via
, this.Scheme
, authenticationScheme
, channelParameters
);
412 // The setter for this property should prevent this.
413 throw Fx
.AssertAndThrow("CreateAndOpenTokenProvider: Invalid authentication scheme");
415 SecurityTokenProviderContainer result
;
416 if (tokenProvider
!= null)
418 result
= new SecurityTokenProviderContainer(tokenProvider
);
419 result
.Open(timeout
);
428 protected virtual void ValidateCreateChannelParameters(EndpointAddress remoteAddress
, Uri via
)
430 base.ValidateScheme(via
);
432 if (this.MessageVersion
.Addressing
== AddressingVersion
.None
&& remoteAddress
.Uri
!= via
)
434 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(CreateToMustEqualViaException(remoteAddress
.Uri
, via
));
438 protected override TChannel
OnCreateChannel(EndpointAddress remoteAddress
, Uri via
)
440 EndpointAddress httpRemoteAddress
= remoteAddress
!= null && WebSocketHelper
.IsWebSocketUri(remoteAddress
.Uri
) ?
441 new EndpointAddress(WebSocketHelper
.NormalizeWsSchemeWithHttpScheme(remoteAddress
.Uri
), remoteAddress
) :
444 Uri httpVia
= WebSocketHelper
.IsWebSocketUri(via
) ? WebSocketHelper
.NormalizeWsSchemeWithHttpScheme(via
) : via
;
445 return this.OnCreateChannelCore(httpRemoteAddress
, httpVia
);
448 protected virtual TChannel
OnCreateChannelCore(EndpointAddress remoteAddress
, Uri via
)
450 ValidateCreateChannelParameters(remoteAddress
, via
);
451 this.ValidateWebSocketTransportUsage();
453 if (typeof(TChannel
) == typeof(IRequestChannel
))
455 return (TChannel
)(object)new HttpRequestChannel((HttpChannelFactory
<IRequestChannel
>)(object)this, remoteAddress
, via
, ManualAddressing
);
459 return (TChannel
)(object)new ClientWebSocketTransportDuplexSessionChannel((HttpChannelFactory
<IDuplexSessionChannel
>)(object)this, this.clientWebSocketFactory
, remoteAddress
, via
, this.WebSocketBufferPool
);
463 protected void ValidateWebSocketTransportUsage()
465 Type channelType
= typeof(TChannel
);
466 if (channelType
== typeof(IRequestChannel
) && this.WebSocketSettings
.TransportUsage
== WebSocketTransportUsage
.Always
)
468 throw FxTrace
.Exception
.AsError(new InvalidOperationException(SR
.GetString(
469 SR
.WebSocketCannotCreateRequestClientChannelWithCertainWebSocketTransportUsage
,
471 WebSocketTransportSettings
.TransportUsageMethodName
,
472 typeof(WebSocketTransportSettings
).Name
,
473 this.WebSocketSettings
.TransportUsage
)));
476 else if (channelType
== typeof(IDuplexSessionChannel
))
478 if (this.WebSocketSettings
.TransportUsage
== WebSocketTransportUsage
.Never
)
480 throw FxTrace
.Exception
.AsError(new InvalidOperationException(SR
.GetString(
481 SR
.WebSocketCannotCreateRequestClientChannelWithCertainWebSocketTransportUsage
,
483 WebSocketTransportSettings
.TransportUsageMethodName
,
484 typeof(WebSocketTransportSettings
).Name
,
485 this.WebSocketSettings
.TransportUsage
)));
487 else if (!WebSocketHelper
.OSSupportsWebSockets() && this.ClientWebSocketFactory
== null)
489 throw FxTrace
.Exception
.AsError(new PlatformNotSupportedException(SR
.GetString(SR
.WebSocketsClientSideNotSupported
, typeof(ClientWebSocketFactory
).FullName
)));
494 [MethodImpl(MethodImplOptions
.NoInlining
)]
495 void InitializeSecurityTokenManager()
497 if (this.channelCredentials
== null)
499 this.channelCredentials
= ClientCredentials
.CreateDefaultCredentials();
501 this.securityTokenManager
= this.channelCredentials
.CreateSecurityTokenManager();
504 protected virtual bool IsSecurityTokenManagerRequired()
506 if (this.AuthenticationScheme
!= AuthenticationSchemes
.Anonymous
)
510 if (this.proxyFactory
!= null && this.proxyFactory
.AuthenticationScheme
!= AuthenticationSchemes
.Anonymous
)
520 protected override IAsyncResult
OnBeginOpen(TimeSpan timeout
, AsyncCallback callback
, object state
)
522 this.OnOpen(timeout
);
523 return new CompletedAsyncResult(callback
, state
);
526 protected override void OnEndOpen(IAsyncResult result
)
528 CompletedAsyncResult
.End(result
);
531 protected override void OnOpen(TimeSpan timeout
)
533 if (IsSecurityTokenManagerRequired())
535 this.InitializeSecurityTokenManager();
538 if (this.AllowCookies
&&
539 !this.httpCookieContainerManager
.IsInitialized
) // We don't want to overwrite the CookieContainer if someone has set it already.
541 this.httpCookieContainerManager
.CookieContainer
= new CookieContainer();
544 // we need to make sure System.Net will buffer faults (sent as 500 requests) up to our allowed size
545 // Their value is in Kbytes and ours is in bytes. We round up so that the KB value is large enough to
546 // encompass our MaxReceivedMessageSize. See MB#20860 and related for details
548 if (!httpWebRequestWebPermissionDenied
&& HttpWebRequest
.DefaultMaximumErrorResponseLength
!= -1)
550 int MaxReceivedMessageSizeKbytes
;
551 if (MaxBufferSize
>= (int.MaxValue
- 1024)) // make sure NCL doesn't overflow
553 MaxReceivedMessageSizeKbytes
= -1;
557 MaxReceivedMessageSizeKbytes
= (int)(MaxBufferSize
/ 1024);
558 if (MaxReceivedMessageSizeKbytes
* 1024 < MaxBufferSize
)
560 MaxReceivedMessageSizeKbytes
++;
564 if (MaxReceivedMessageSizeKbytes
== -1
565 || MaxReceivedMessageSizeKbytes
> HttpWebRequest
.DefaultMaximumErrorResponseLength
)
569 HttpWebRequest
.DefaultMaximumErrorResponseLength
= MaxReceivedMessageSizeKbytes
;
571 catch (SecurityException exception
)
573 // CSDMain\33725 - setting DefaultMaximumErrorResponseLength should not fail HttpChannelFactory.OnOpen
574 // if the user does not have the permission to do so.
575 httpWebRequestWebPermissionDenied
= true;
577 DiagnosticUtility
.TraceHandledException(exception
, TraceEventType
.Warning
);
583 protected override void OnClosed()
586 if (this.bufferPool
!= null)
588 this.bufferPool
.Close();
592 static internal void TraceResponseReceived(HttpWebResponse response
, Message message
, object receiver
)
594 if (DiagnosticUtility
.ShouldTraceVerbose
)
596 if (response
!= null && response
.ResponseUri
!= null)
598 TraceUtility
.TraceEvent(TraceEventType
.Verbose
, TraceCode
.HttpResponseReceived
, SR
.GetString(SR
.TraceCodeHttpResponseReceived
), new StringTraceRecord("ResponseUri", response
.ResponseUri
.ToString()), receiver
, null, message
);
602 TraceUtility
.TraceEvent(TraceEventType
.Verbose
, TraceCode
.HttpResponseReceived
, SR
.GetString(SR
.TraceCodeHttpResponseReceived
), receiver
, message
);
607 [Fx
.Tag
.SecurityNote(Critical
= "Uses unsafe critical method AppendWindowsAuthenticationInfo to access the credential domain/user name/password.")]
609 [MethodImpl(MethodImplOptions
.NoInlining
)]
610 string AppendWindowsAuthenticationInfo(string inputString
, NetworkCredential credential
,
611 AuthenticationLevel authenticationLevel
, TokenImpersonationLevel impersonationLevel
)
613 return SecurityUtils
.AppendWindowsAuthenticationInfo(inputString
, credential
, authenticationLevel
, impersonationLevel
);
616 protected virtual string OnGetConnectionGroupPrefix(HttpWebRequest httpWebRequest
, SecurityTokenContainer clientCertificateToken
)
621 internal static bool IsWindowsAuth(AuthenticationSchemes authScheme
)
623 Fx
.Assert(authScheme
.IsSingleton(), "authenticationScheme used in an Http(s)ChannelFactory must be a singleton value.");
625 return authScheme
== AuthenticationSchemes
.Negotiate
||
626 authScheme
== AuthenticationSchemes
.Ntlm
;
629 [Fx
.Tag
.SecurityNote(Critical
= "Uses unsafe critical method AppendWindowsAuthenticationInfo to access the credential domain/user name/password.",
630 Safe
= "Uses the domain/user name/password to store and compute a hash. The store is SecurityCritical. The hash leaks but" +
631 "the hash cannot be reversed to the domain/user name/password.")]
632 [SecuritySafeCritical
]
633 string GetConnectionGroupName(HttpWebRequest httpWebRequest
, NetworkCredential credential
, AuthenticationLevel authenticationLevel
,
634 TokenImpersonationLevel impersonationLevel
, SecurityTokenContainer clientCertificateToken
)
636 if (this.credentialHashCache
== null)
640 if (this.credentialHashCache
== null)
642 this.credentialHashCache
= new MruCache
<string, string>(5);
647 // The following line is a work-around for VSWhidbey 558605. In particular, we need to isolate our
648 // connection groups based on whether we are streaming the request.
649 string inputString
= TransferModeHelper
.IsRequestStreamed(this.TransferMode
) ? "streamed" : string.Empty
;
651 if (IsWindowsAuth(this.AuthenticationScheme
))
653 // for NTLM & Negotiate, System.Net doesn't pool connections by default. This is because
654 // IIS doesn't re-authenticate NTLM connections (made for a perf reason), and HttpWebRequest
655 // shared connections among multiple callers.
656 // This causes Indigo a performance problem in turn. We mitigate this by (1) enabling
657 // connection sharing for NTLM connections on our pool, and (2) scoping the pool we use
658 // to be based on the NetworkCredential that is being used to authenticate the connection.
659 // Therefore we're only sharing connections among the same Credential.
661 // Setting this will fail in partial trust, and that's ok since this is an optimization.
662 if (!httpWebRequestWebPermissionDenied
)
666 httpWebRequest
.UnsafeAuthenticatedConnectionSharing
= true;
668 catch (SecurityException e
)
670 DiagnosticUtility
.TraceHandledException(e
, TraceEventType
.Information
);
671 httpWebRequestWebPermissionDenied
= true;
675 inputString
= AppendWindowsAuthenticationInfo(inputString
, credential
, authenticationLevel
, impersonationLevel
);
678 string prefix
= this.OnGetConnectionGroupPrefix(httpWebRequest
, clientCertificateToken
);
679 inputString
= string.Concat(this.uniqueConnectionGroupNamePrefix
, prefix
, inputString
);
681 string credentialHash
= null;
683 // we have to lock around each call to TryGetValue since the MruCache modifies the
684 // contents of it's mruList in a single-threaded manner underneath TryGetValue
686 if (!string.IsNullOrEmpty(inputString
))
688 lock (this.credentialHashCache
)
690 if (!this.credentialHashCache
.TryGetValue(inputString
, out credentialHash
))
692 byte[] inputBytes
= new UTF8Encoding().GetBytes(inputString
);
693 byte[] digestBytes
= this.HashAlgorithm
.ComputeHash(inputBytes
);
694 credentialHash
= Convert
.ToBase64String(digestBytes
);
695 this.credentialHashCache
.Add(inputString
, credentialHash
);
700 return credentialHash
;
703 Uri
GetCredentialCacheUriPrefix(Uri via
)
707 if (this.credentialCacheUriPrefixCache
== null)
711 if (this.credentialCacheUriPrefixCache
== null)
713 this.credentialCacheUriPrefixCache
= new MruCache
<Uri
, Uri
>(10);
718 lock (this.credentialCacheUriPrefixCache
)
720 if (!this.credentialCacheUriPrefixCache
.TryGetValue(via
, out result
))
722 result
= new UriBuilder(via
.Scheme
, via
.Host
, via
.Port
).Uri
;
723 this.credentialCacheUriPrefixCache
.Add(via
, result
);
730 // core code for creating an HttpWebRequest
731 HttpWebRequest
GetWebRequest(EndpointAddress to
, Uri via
, NetworkCredential credential
,
732 TokenImpersonationLevel impersonationLevel
, AuthenticationLevel authenticationLevel
,
733 SecurityTokenProviderContainer proxyTokenProvider
, SecurityTokenContainer clientCertificateToken
, TimeSpan timeout
, bool isWebSocketRequest
)
735 Uri httpWebRequestUri
= isWebSocketRequest
? WebSocketHelper
.GetWebSocketUri(via
) : via
;
736 HttpWebRequest httpWebRequest
= (HttpWebRequest
)WebRequest
.Create(httpWebRequestUri
);
737 Fx
.Assert(httpWebRequest
.Method
.Equals("GET", StringComparison
.OrdinalIgnoreCase
), "the default HTTP method of HttpWebRequest should be 'Get'.");
739 if (!isWebSocketRequest
)
741 httpWebRequest
.Method
= "POST";
743 if (TransferModeHelper
.IsRequestStreamed(TransferMode
))
745 httpWebRequest
.SendChunked
= true;
746 httpWebRequest
.AllowWriteStreamBuffering
= false;
750 httpWebRequest
.AllowWriteStreamBuffering
= true;
754 httpWebRequest
.CachePolicy
= requestCachePolicy
;
755 httpWebRequest
.KeepAlive
= this.keepAliveEnabled
;
757 if (this.decompressionEnabled
)
759 httpWebRequest
.AutomaticDecompression
= DecompressionMethods
.GZip
| DecompressionMethods
.Deflate
;
763 httpWebRequest
.AutomaticDecompression
= DecompressionMethods
.None
;
766 if (credential
!= null)
768 CredentialCache credentials
= new CredentialCache();
769 credentials
.Add(this.GetCredentialCacheUriPrefix(via
),
770 AuthenticationSchemesHelper
.ToString(this.authenticationScheme
), credential
);
771 httpWebRequest
.Credentials
= credentials
;
773 httpWebRequest
.AuthenticationLevel
= authenticationLevel
;
774 httpWebRequest
.ImpersonationLevel
= impersonationLevel
;
776 string connectionGroupName
= GetConnectionGroupName(httpWebRequest
, credential
, authenticationLevel
, impersonationLevel
, clientCertificateToken
);
778 X509CertificateEndpointIdentity remoteCertificateIdentity
= to
.Identity
as X509CertificateEndpointIdentity
;
779 if (remoteCertificateIdentity
!= null)
781 connectionGroupName
= string.Format(System
.Globalization
.CultureInfo
.InvariantCulture
, "{0}[{1}]", connectionGroupName
, remoteCertificateIdentity
.Certificates
[0].Thumbprint
);
784 if (!string.IsNullOrEmpty(connectionGroupName
))
786 httpWebRequest
.ConnectionGroupName
= connectionGroupName
;
789 if (AuthenticationScheme
== AuthenticationSchemes
.Basic
)
791 httpWebRequest
.PreAuthenticate
= true;
794 if (this.proxy
!= null)
796 httpWebRequest
.Proxy
= this.proxy
;
798 else if (this.proxyFactory
!= null)
800 httpWebRequest
.Proxy
= this.proxyFactory
.CreateWebProxy(httpWebRequest
, proxyTokenProvider
, timeout
);
803 if (this.AllowCookies
)
805 httpWebRequest
.CookieContainer
= this.httpCookieContainerManager
.CookieContainer
;
808 // we do this at the end so that we access the correct ServicePoint
809 httpWebRequest
.ServicePoint
.UseNagleAlgorithm
= false;
811 return httpWebRequest
;
814 void ApplyManualAddressing(ref EndpointAddress to
, ref Uri via
, Message message
)
816 if (ManualAddressing
)
818 Uri toHeader
= message
.Headers
.To
;
819 if (toHeader
== null)
821 throw TraceUtility
.ThrowHelperError(new InvalidOperationException(SR
.GetString(SR
.ManualAddressingRequiresAddressedMessages
)), message
);
824 to
= new EndpointAddress(toHeader
);
826 if (this.MessageVersion
.Addressing
== AddressingVersion
.None
)
832 // now apply query string property
834 if (message
.Properties
.TryGetValue(HttpRequestMessageProperty
.Name
, out property
))
836 HttpRequestMessageProperty requestProperty
= (HttpRequestMessageProperty
)property
;
837 if (!string.IsNullOrEmpty(requestProperty
.QueryString
))
839 UriBuilder uriBuilder
= new UriBuilder(via
);
841 if (requestProperty
.QueryString
.StartsWith("?", StringComparison
.Ordinal
))
843 uriBuilder
.Query
= requestProperty
.QueryString
.Substring(1);
847 uriBuilder
.Query
= requestProperty
.QueryString
;
850 via
= uriBuilder
.Uri
;
855 [MethodImpl(MethodImplOptions
.NoInlining
)]
856 void CreateAndOpenTokenProvidersCore(EndpointAddress to
, Uri via
, ChannelParameterCollection channelParameters
, TimeSpan timeout
, out SecurityTokenProviderContainer tokenProvider
, out SecurityTokenProviderContainer proxyTokenProvider
)
858 TimeoutHelper timeoutHelper
= new TimeoutHelper(timeout
);
859 tokenProvider
= CreateAndOpenTokenProvider(timeoutHelper
.RemainingTime(), this.AuthenticationScheme
, to
, via
, channelParameters
);
860 if (this.proxyFactory
!= null)
862 proxyTokenProvider
= CreateAndOpenTokenProvider(timeoutHelper
.RemainingTime(), this.proxyFactory
.AuthenticationScheme
, to
, via
, channelParameters
);
866 proxyTokenProvider
= null;
870 internal void CreateAndOpenTokenProviders(EndpointAddress to
, Uri via
, ChannelParameterCollection channelParameters
, TimeSpan timeout
, out SecurityTokenProviderContainer tokenProvider
, out SecurityTokenProviderContainer proxyTokenProvider
)
872 if (!IsSecurityTokenManagerRequired())
874 tokenProvider
= null;
875 proxyTokenProvider
= null;
879 CreateAndOpenTokenProvidersCore(to
, via
, channelParameters
, timeout
, out tokenProvider
, out proxyTokenProvider
);
883 internal HttpWebRequest
GetWebRequest(EndpointAddress to
, Uri via
, SecurityTokenProviderContainer tokenProvider
,
884 SecurityTokenProviderContainer proxyTokenProvider
, SecurityTokenContainer clientCertificateToken
, TimeSpan timeout
, bool isWebSocketRequest
)
886 TokenImpersonationLevel impersonationLevel
;
887 AuthenticationLevel authenticationLevel
;
888 TimeoutHelper timeoutHelper
= new TimeoutHelper(timeout
);
889 NetworkCredential credential
= HttpChannelUtilities
.GetCredential(this.authenticationScheme
,
890 tokenProvider
, timeoutHelper
.RemainingTime(), out impersonationLevel
, out authenticationLevel
);
892 return GetWebRequest(to
, via
, credential
, impersonationLevel
, authenticationLevel
, proxyTokenProvider
, clientCertificateToken
, timeoutHelper
.RemainingTime(), isWebSocketRequest
);
895 internal static bool MapIdentity(EndpointAddress target
, AuthenticationSchemes authenticationScheme
)
897 if ((target
.Identity
== null) || (target
.Identity
is X509CertificateEndpointIdentity
))
902 return IsWindowsAuth(authenticationScheme
);
905 bool MapIdentity(EndpointAddress target
)
907 return MapIdentity(target
, this.AuthenticationScheme
);
910 protected class HttpRequestChannel
: RequestChannel
912 // Double-checked locking pattern requires volatile for read/write synchronization
913 volatile bool cleanupIdentity
;
914 HttpChannelFactory
<IRequestChannel
> factory
;
915 SecurityTokenProviderContainer tokenProvider
;
916 SecurityTokenProviderContainer proxyTokenProvider
;
917 ServiceModelActivity activity
= null;
918 ChannelParameterCollection channelParameters
;
920 public HttpRequestChannel(HttpChannelFactory
<IRequestChannel
> factory
, EndpointAddress to
, Uri via
, bool manualAddressing
)
921 : base(factory
, to
, via
, manualAddressing
)
923 this.factory
= factory
;
926 public HttpChannelFactory
<IRequestChannel
> Factory
928 get { return this.factory; }
931 internal ServiceModelActivity Activity
933 get { return this.activity; }
936 protected ChannelParameterCollection ChannelParameters
940 return this.channelParameters
;
944 public override T GetProperty
<T
>()
946 if (typeof(T
) == typeof(ChannelParameterCollection
))
948 if (this.State
== CommunicationState
.Created
)
952 if (this.channelParameters
== null)
954 this.channelParameters
= new ChannelParameterCollection();
958 return (T
)(object)this.channelParameters
;
962 return base.GetProperty
<T
>();
968 if (Factory
.MapIdentity(RemoteAddress
))
972 cleanupIdentity
= HttpTransportSecurityHelpers
.AddIdentityMapping(Via
, RemoteAddress
);
977 void CreateAndOpenTokenProviders(TimeSpan timeout
)
979 TimeoutHelper timeoutHelper
= new TimeoutHelper(timeout
);
980 if (!ManualAddressing
)
982 Factory
.CreateAndOpenTokenProviders(this.RemoteAddress
, this.Via
, this.channelParameters
, timeoutHelper
.RemainingTime(), out this.tokenProvider
, out this.proxyTokenProvider
);
986 void CloseTokenProviders(TimeSpan timeout
)
988 TimeoutHelper timeoutHelper
= new TimeoutHelper(timeout
);
989 if (this.tokenProvider
!= null)
991 tokenProvider
.Close(timeoutHelper
.RemainingTime());
993 if (this.proxyTokenProvider
!= null)
995 proxyTokenProvider
.Close(timeoutHelper
.RemainingTime());
999 void AbortTokenProviders()
1001 if (this.tokenProvider
!= null)
1003 tokenProvider
.Abort();
1005 if (this.proxyTokenProvider
!= null)
1007 proxyTokenProvider
.Abort();
1011 protected override IAsyncResult
OnBeginOpen(TimeSpan timeout
, AsyncCallback callback
, object state
)
1014 TimeoutHelper timeoutHelper
= new TimeoutHelper(timeout
);
1015 CreateAndOpenTokenProviders(timeoutHelper
.RemainingTime());
1016 return new CompletedAsyncResult(callback
, state
);
1019 protected override void OnOpen(TimeSpan timeout
)
1022 CreateAndOpenTokenProviders(timeout
);
1025 protected override void OnEndOpen(IAsyncResult result
)
1027 CompletedAsyncResult
.End(result
);
1030 void PrepareClose(bool aborting
)
1032 if (cleanupIdentity
)
1036 if (cleanupIdentity
)
1038 cleanupIdentity
= false;
1039 HttpTransportSecurityHelpers
.RemoveIdentityMapping(Via
, RemoteAddress
, !aborting
);
1045 protected override void OnAbort()
1048 AbortTokenProviders();
1052 protected override IAsyncResult
OnBeginClose(TimeSpan timeout
, AsyncCallback callback
, object state
)
1054 IAsyncResult retval
= null;
1055 using (ServiceModelActivity
.BoundOperation(this.activity
))
1057 PrepareClose(false);
1058 TimeoutHelper timeoutHelper
= new TimeoutHelper(timeout
);
1059 CloseTokenProviders(timeoutHelper
.RemainingTime());
1060 retval
= base.BeginWaitForPendingRequests(timeoutHelper
.RemainingTime(), callback
, state
);
1062 ServiceModelActivity
.Stop(this.activity
);
1066 protected override void OnEndClose(IAsyncResult result
)
1068 using (ServiceModelActivity
.BoundOperation(this.activity
))
1070 base.EndWaitForPendingRequests(result
);
1072 ServiceModelActivity
.Stop(this.activity
);
1075 protected override void OnClose(TimeSpan timeout
)
1077 using (ServiceModelActivity
.BoundOperation(this.activity
))
1079 PrepareClose(false);
1080 TimeoutHelper timeoutHelper
= new TimeoutHelper(timeout
);
1081 CloseTokenProviders(timeoutHelper
.RemainingTime());
1082 base.WaitForPendingRequests(timeoutHelper
.RemainingTime());
1084 ServiceModelActivity
.Stop(this.activity
);
1087 protected override IAsyncRequest
CreateAsyncRequest(Message message
, AsyncCallback callback
, object state
)
1089 if (DiagnosticUtility
.ShouldUseActivity
&& this.activity
== null)
1091 this.activity
= ServiceModelActivity
.CreateActivity();
1092 if (null != FxTrace
.Trace
)
1094 FxTrace
.Trace
.TraceTransfer(this.activity
.Id
);
1096 ServiceModelActivity
.Start(this.activity
, SR
.GetString(SR
.ActivityReceiveBytes
, this.RemoteAddress
.Uri
.ToString()), ActivityType
.ReceiveBytes
);
1099 return new HttpChannelAsyncRequest(this, callback
, state
);
1102 protected override IRequest
CreateRequest(Message message
)
1104 return new HttpChannelRequest(this, Factory
);
1107 public virtual HttpWebRequest
GetWebRequest(EndpointAddress to
, Uri via
, ref TimeoutHelper timeoutHelper
)
1109 return GetWebRequest(to
, via
, null, ref timeoutHelper
);
1112 protected HttpWebRequest
GetWebRequest(EndpointAddress to
, Uri via
, SecurityTokenContainer clientCertificateToken
, ref TimeoutHelper timeoutHelper
)
1114 SecurityTokenProviderContainer webRequestTokenProvider
;
1115 SecurityTokenProviderContainer webRequestProxyTokenProvider
;
1116 if (this.ManualAddressing
)
1118 this.Factory
.CreateAndOpenTokenProviders(to
, via
, this.channelParameters
, timeoutHelper
.RemainingTime(),
1119 out webRequestTokenProvider
, out webRequestProxyTokenProvider
);
1123 webRequestTokenProvider
= this.tokenProvider
;
1124 webRequestProxyTokenProvider
= this.proxyTokenProvider
;
1128 return this.Factory
.GetWebRequest(to
, via
, webRequestTokenProvider
, webRequestProxyTokenProvider
, clientCertificateToken
, timeoutHelper
.RemainingTime(), false);
1132 if (this.ManualAddressing
)
1134 if (webRequestTokenProvider
!= null)
1136 webRequestTokenProvider
.Abort();
1138 if (webRequestProxyTokenProvider
!= null)
1140 webRequestProxyTokenProvider
.Abort();
1146 protected IAsyncResult
BeginGetWebRequest(
1147 EndpointAddress to
, Uri via
, SecurityTokenContainer clientCertificateToken
, ref TimeoutHelper timeoutHelper
, AsyncCallback callback
, object state
)
1149 return new GetWebRequestAsyncResult(this, to
, via
, clientCertificateToken
, ref timeoutHelper
, callback
, state
);
1152 public virtual IAsyncResult
BeginGetWebRequest(
1153 EndpointAddress to
, Uri via
, ref TimeoutHelper timeoutHelper
, AsyncCallback callback
, object state
)
1155 return BeginGetWebRequest(to
, via
, null, ref timeoutHelper
, callback
, state
);
1158 public virtual HttpWebRequest
EndGetWebRequest(IAsyncResult result
)
1160 return GetWebRequestAsyncResult
.End(result
);
1163 public virtual bool WillGetWebRequestCompleteSynchronously()
1165 return ((this.tokenProvider
== null) && !Factory
.ManualAddressing
);
1168 internal virtual void OnWebRequestCompleted(HttpWebRequest request
)
1173 class HttpChannelRequest
: IRequest
1175 HttpRequestChannel channel
;
1176 HttpChannelFactory
<IRequestChannel
> factory
;
1179 HttpWebRequest webRequest
;
1180 HttpAbortReason abortReason
;
1181 ChannelBinding channelBinding
;
1182 int webRequestCompleted
;
1183 EventTraceActivity eventTraceActivity
;
1184 const string ConnectionGroupPrefixMessagePropertyName
= "HttpTransportConnectionGroupNamePrefix";
1186 public HttpChannelRequest(HttpRequestChannel channel
, HttpChannelFactory
<IRequestChannel
> factory
)
1188 this.channel
= channel
;
1189 this.to
= channel
.RemoteAddress
;
1190 this.via
= channel
.Via
;
1191 this.factory
= factory
;
1194 private string GetConnectionGroupPrefix(Message message
)
1197 if (message
.Properties
.TryGetValue(ConnectionGroupPrefixMessagePropertyName
, out property
))
1199 string prefix
= property
as string;
1206 return string.Empty
;
1209 public void SendRequest(Message message
, TimeSpan timeout
)
1211 TimeoutHelper timeoutHelper
= new TimeoutHelper(timeout
);
1212 factory
.ApplyManualAddressing(ref this.to
, ref this.via
, message
);
1213 this.webRequest
= channel
.GetWebRequest(this.to
, this.via
, ref timeoutHelper
);
1214 this.webRequest
.ConnectionGroupName
= GetConnectionGroupPrefix(message
) + this.webRequest
.ConnectionGroupName
;
1216 Message request
= message
;
1220 if (channel
.State
!= CommunicationState
.Opened
)
1222 // if we were aborted while getting our request or doing correlation,
1223 // we need to abort the web request and bail
1225 channel
.ThrowIfDisposedOrNotOpen();
1228 HttpChannelUtilities
.SetRequestTimeout(this.webRequest
, timeoutHelper
.RemainingTime());
1229 HttpOutput httpOutput
= HttpOutput
.CreateHttpOutput(this.webRequest
, this.factory
, request
, this.factory
.IsChannelBindingSupportEnabled
);
1231 bool success
= false;
1235 httpOutput
.Send(timeoutHelper
.RemainingTime());
1237 this.channelBinding
= httpOutput
.TakeChannelBinding();
1241 if (FxTrace
.Trace
.IsEnd2EndActivityTracingEnabled
)
1243 this.eventTraceActivity
= EventTraceActivityHelper
.TryExtractActivity(message
);
1244 if (TD
.MessageSentByTransportIsEnabled())
1246 TD
.MessageSentByTransport(eventTraceActivity
, this.to
.Uri
.AbsoluteUri
);
1254 httpOutput
.Abort(HttpAbortReason
.Aborted
);
1260 if (!object.ReferenceEquals(request
, message
))
1269 if (this.webRequest
!= null)
1271 HttpChannelUtilities
.AbortRequest(this.webRequest
);
1272 this.TryCompleteWebRequest(this.webRequest
);
1275 ChannelBindingUtility
.Dispose(ref this.channelBinding
);
1278 public void Abort(RequestChannel channel
)
1281 abortReason
= HttpAbortReason
.Aborted
;
1284 public void Fault(RequestChannel channel
)
1289 [System
.Diagnostics
.CodeAnalysis
.SuppressMessage(FxCop
.Category
.ReliabilityBasic
, "Reliability104",
1290 Justification
= "This is an old method from previous release.")]
1291 public Message
WaitForReply(TimeSpan timeout
)
1293 if (TD
.HttpResponseReceiveStartIsEnabled())
1295 TD
.HttpResponseReceiveStart(this.eventTraceActivity
);
1298 HttpWebResponse response
= null;
1299 WebException responseException
= null;
1304 response
= (HttpWebResponse
)webRequest
.GetResponse();
1306 catch (NullReferenceException nullReferenceException
)
1308 // workaround for Whidbey bug #558605 - only happens in streamed case.
1309 if (TransferModeHelper
.IsRequestStreamed(this.factory
.transferMode
))
1311 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(
1312 HttpChannelUtilities
.CreateNullReferenceResponseException(nullReferenceException
));
1317 if (TD
.MessageReceivedByTransportIsEnabled())
1319 TD
.MessageReceivedByTransport(this.eventTraceActivity
?? EventTraceActivity
.Empty
,
1320 response
.ResponseUri
!= null ? response
.ResponseUri
.AbsoluteUri
: string.Empty
,
1321 EventTraceActivity
.GetActivityIdFromThread());
1324 if (DiagnosticUtility
.ShouldTraceVerbose
)
1326 HttpChannelFactory
<TChannel
>.TraceResponseReceived(response
, null, this);
1329 catch (WebException webException
)
1331 responseException
= webException
;
1332 response
= HttpChannelUtilities
.ProcessGetResponseWebException(webException
, this.webRequest
,
1336 HttpInput httpInput
= HttpChannelUtilities
.ValidateRequestReplyResponse(this.webRequest
, response
,
1337 this.factory
, responseException
, this.channelBinding
);
1338 this.channelBinding
= null;
1340 Message replyMessage
= null;
1341 if (httpInput
!= null)
1343 Exception exception
= null;
1344 replyMessage
= httpInput
.ParseIncomingMessage(out exception
);
1345 Fx
.Assert(exception
== null, "ParseIncomingMessage should not set an exception after parsing a response message.");
1347 if (replyMessage
!= null)
1349 HttpChannelUtilities
.AddReplySecurityProperty(this.factory
, this.webRequest
, response
,
1352 if (FxTrace
.Trace
.IsEnd2EndActivityTracingEnabled
&& (eventTraceActivity
!= null))
1354 EventTraceActivityHelper
.TryAttachActivity(replyMessage
, eventTraceActivity
);
1359 this.TryCompleteWebRequest(this.webRequest
);
1360 return replyMessage
;
1363 public void OnReleaseRequest()
1365 this.TryCompleteWebRequest(this.webRequest
);
1368 void TryCompleteWebRequest(HttpWebRequest request
)
1370 if (request
== null)
1375 if (Interlocked
.CompareExchange(ref this.webRequestCompleted
, 1, 0) == 0)
1377 this.channel
.OnWebRequestCompleted(request
);
1382 class HttpChannelAsyncRequest
: TraceAsyncResult
, IAsyncRequest
1384 static AsyncCallback onProcessIncomingMessage
= Fx
.ThunkCallback(new AsyncCallback(OnParseIncomingMessage
));
1385 static AsyncCallback onGetResponse
= Fx
.ThunkCallback(new AsyncCallback(OnGetResponse
));
1386 static AsyncCallback onGetWebRequestCompleted
;
1387 static AsyncCallback onSend
= Fx
.ThunkCallback(new AsyncCallback(OnSend
));
1388 static Action
<object> onSendTimeout
;
1389 ChannelBinding channelBinding
;
1391 HttpChannelFactory
<IRequestChannel
> factory
;
1392 HttpRequestChannel channel
;
1393 HttpOutput httpOutput
;
1394 HttpInput httpInput
;
1396 Message requestMessage
;
1397 Message replyMessage
;
1398 HttpWebResponse response
;
1399 HttpWebRequest request
;
1400 object sendLock
= new object();
1401 IOThreadTimer sendTimer
;
1402 TimeoutHelper timeoutHelper
;
1405 HttpAbortReason abortReason
;
1406 int webRequestCompleted
;
1407 EventTraceActivity eventTraceActivity
;
1409 public HttpChannelAsyncRequest(HttpRequestChannel channel
, AsyncCallback callback
, object state
)
1410 : base(callback
, state
)
1412 this.channel
= channel
;
1413 this.to
= channel
.RemoteAddress
;
1414 this.via
= channel
.Via
;
1415 this.factory
= channel
.Factory
;
1418 IOThreadTimer SendTimer
1422 if (this.sendTimer
== null)
1424 if (onSendTimeout
== null)
1426 onSendTimeout
= new Action
<object>(OnSendTimeout
);
1429 this.sendTimer
= new IOThreadTimer(onSendTimeout
, this, false);
1432 return this.sendTimer
;
1436 public static void End(IAsyncResult result
)
1438 AsyncResult
.End
<HttpChannelAsyncRequest
>(result
);
1441 public void BeginSendRequest(Message message
, TimeSpan timeout
)
1443 this.message
= this.requestMessage
= message
;
1444 this.timeoutHelper
= new TimeoutHelper(timeout
);
1446 if (FxTrace
.Trace
.IsEnd2EndActivityTracingEnabled
)
1448 this.eventTraceActivity
= EventTraceActivityHelper
.TryExtractActivity(message
);
1451 factory
.ApplyManualAddressing(ref this.to
, ref this.via
, this.requestMessage
);
1452 if (this.channel
.WillGetWebRequestCompleteSynchronously())
1454 SetWebRequest(channel
.GetWebRequest(this.to
, this.via
, ref this.timeoutHelper
));
1455 if (this.SendWebRequest())
1457 base.Complete(true);
1462 if (onGetWebRequestCompleted
== null)
1464 onGetWebRequestCompleted
= Fx
.ThunkCallback(
1465 new AsyncCallback(OnGetWebRequestCompletedCallback
));
1468 IAsyncResult result
= channel
.BeginGetWebRequest(
1469 to
, via
, ref this.timeoutHelper
, onGetWebRequestCompleted
, this);
1471 if (result
.CompletedSynchronously
)
1473 if (TD
.MessageSentByTransportIsEnabled())
1475 TD
.MessageSentByTransport(this.eventTraceActivity
, this.to
.Uri
.AbsoluteUri
);
1477 if (this.OnGetWebRequestCompleted(result
))
1479 base.Complete(true);
1485 static void OnGetWebRequestCompletedCallback(IAsyncResult result
)
1487 if (result
.CompletedSynchronously
)
1492 HttpChannelAsyncRequest thisPtr
= (HttpChannelAsyncRequest
)result
.AsyncState
;
1493 Exception completionException
= null;
1497 completeSelf
= thisPtr
.OnGetWebRequestCompleted(result
);
1499 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
1506 completeSelf
= true;
1507 completionException
= e
;
1511 thisPtr
.Complete(false, completionException
);
1518 if (this.request
!= null)
1520 this.TryCompleteWebRequest(this.request
);
1521 this.abortReason
= HttpAbortReason
.TimedOut
;
1522 httpOutput
.Abort(this.abortReason
);
1526 void CancelSendTimer()
1530 if (this.sendTimer
!= null)
1532 this.sendTimer
.Cancel();
1533 this.sendTimer
= null;
1538 bool OnGetWebRequestCompleted(IAsyncResult result
)
1540 SetWebRequest(this.channel
.EndGetWebRequest(result
));
1541 return this.SendWebRequest();
1544 bool SendWebRequest()
1546 this.httpOutput
= HttpOutput
.CreateHttpOutput(this.request
, this.factory
, this.requestMessage
, this.factory
.IsChannelBindingSupportEnabled
);
1548 bool success
= false;
1551 bool result
= false;
1552 SetSendTimeout(timeoutHelper
.RemainingTime());
1553 IAsyncResult asyncResult
= httpOutput
.BeginSend(timeoutHelper
.RemainingTime(), onSend
, this);
1556 if (asyncResult
.CompletedSynchronously
)
1558 result
= CompleteSend(asyncResult
);
1567 this.httpOutput
.Abort(HttpAbortReason
.Aborted
);
1569 if (!object.ReferenceEquals(this.message
, this.requestMessage
))
1571 this.requestMessage
.Close();
1577 bool CompleteSend(IAsyncResult result
)
1579 bool success
= false;
1582 httpOutput
.EndSend(result
);
1583 this.channelBinding
= httpOutput
.TakeChannelBinding();
1586 if (TD
.MessageSentByTransportIsEnabled())
1588 TD
.MessageSentByTransport(this.eventTraceActivity
, this.to
.Uri
.AbsoluteUri
);
1595 httpOutput
.Abort(HttpAbortReason
.Aborted
);
1598 if (!object.ReferenceEquals(this.message
, this.requestMessage
))
1600 this.requestMessage
.Close();
1606 IAsyncResult getResponseResult
;
1609 getResponseResult
= request
.BeginGetResponse(onGetResponse
, this);
1611 catch (NullReferenceException nullReferenceException
)
1613 // workaround for Whidbey bug #558605 - only happens in streamed case.
1614 if (TransferModeHelper
.IsRequestStreamed(this.factory
.transferMode
))
1616 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(
1617 HttpChannelUtilities
.CreateNullReferenceResponseException(nullReferenceException
));
1623 if (getResponseResult
.CompletedSynchronously
)
1625 return CompleteGetResponse(getResponseResult
);
1630 catch (IOException ioException
)
1632 throw TraceUtility
.ThrowHelperError(new CommunicationException(ioException
.Message
,
1633 ioException
), this.requestMessage
);
1635 catch (WebException webException
)
1637 throw TraceUtility
.ThrowHelperError(new CommunicationException(webException
.Message
,
1638 webException
), this.requestMessage
);
1640 catch (ObjectDisposedException objectDisposedException
)
1642 if (abortReason
== HttpAbortReason
.Aborted
)
1644 throw TraceUtility
.ThrowHelperError(new CommunicationObjectAbortedException(SR
.GetString(SR
.HttpRequestAborted
, to
.Uri
),
1645 objectDisposedException
), this.requestMessage
);
1648 throw TraceUtility
.ThrowHelperError(new TimeoutException(SR
.GetString(SR
.HttpRequestTimedOut
,
1649 to
.Uri
, this.timeoutHelper
.OriginalTimeout
), objectDisposedException
), this.requestMessage
);
1653 [System
.Diagnostics
.CodeAnalysis
.SuppressMessage(FxCop
.Category
.ReliabilityBasic
, "Reliability104",
1654 Justification
= "This is an old method from previous release.")]
1655 bool CompleteGetResponse(IAsyncResult result
)
1657 using (ServiceModelActivity
.BoundOperation(this.channel
.Activity
))
1659 HttpWebResponse response
= null;
1660 WebException responseException
= null;
1666 response
= (HttpWebResponse
)request
.EndGetResponse(result
);
1668 catch (NullReferenceException nullReferenceException
)
1670 // workaround for Whidbey bug #558605 - only happens in streamed case.
1671 if (TransferModeHelper
.IsRequestStreamed(this.factory
.transferMode
))
1673 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(
1674 HttpChannelUtilities
.CreateNullReferenceResponseException(nullReferenceException
));
1679 if (TD
.MessageReceivedByTransportIsEnabled())
1681 TD
.MessageReceivedByTransport(
1682 this.eventTraceActivity
?? EventTraceActivity
.Empty
,
1683 this.to
.Uri
.AbsoluteUri
,
1684 EventTraceActivity
.GetActivityIdFromThread());
1687 if (DiagnosticUtility
.ShouldTraceVerbose
)
1689 HttpChannelFactory
<TChannel
>.TraceResponseReceived(response
, this.message
, this);
1692 catch (WebException webException
)
1694 responseException
= webException
;
1695 response
= HttpChannelUtilities
.ProcessGetResponseWebException(webException
, request
,
1699 return ProcessResponse(response
, responseException
);
1705 if (this.request
!= null)
1707 HttpChannelUtilities
.AbortRequest(this.request
);
1708 this.TryCompleteWebRequest(this.request
);
1711 ChannelBindingUtility
.Dispose(ref this.channelBinding
);
1714 void SetSendTimeout(TimeSpan timeout
)
1716 // We also set the timeout on the HttpWebRequest so that we can subsequently use it in the
1717 // exception message in the event of a timeout.
1718 HttpChannelUtilities
.SetRequestTimeout(this.request
, timeout
);
1720 if (timeout
== TimeSpan
.MaxValue
)
1726 SendTimer
.Set(timeout
);
1730 public void Abort(RequestChannel channel
)
1733 abortReason
= HttpAbortReason
.Aborted
;
1736 public void Fault(RequestChannel channel
)
1741 void SetWebRequest(HttpWebRequest webRequest
)
1743 this.request
= webRequest
;
1745 if (channel
.State
!= CommunicationState
.Opened
)
1747 // if we were aborted while getting our request, we need to abort the web request and bail
1749 channel
.ThrowIfDisposedOrNotOpen();
1753 public Message
End()
1755 HttpChannelAsyncRequest
.End(this);
1756 return replyMessage
;
1759 bool ProcessResponse(HttpWebResponse response
, WebException responseException
)
1761 this.httpInput
= HttpChannelUtilities
.ValidateRequestReplyResponse(this.request
, response
,
1762 this.factory
, responseException
, this.channelBinding
);
1763 this.channelBinding
= null;
1765 if (httpInput
!= null)
1767 this.response
= response
;
1768 IAsyncResult result
=
1769 httpInput
.BeginParseIncomingMessage(onProcessIncomingMessage
, this);
1770 if (!result
.CompletedSynchronously
)
1775 CompleteParseIncomingMessage(result
);
1779 this.replyMessage
= null;
1782 this.TryCompleteWebRequest(this.request
);
1786 void CompleteParseIncomingMessage(IAsyncResult result
)
1788 Exception exception
= null;
1789 this.replyMessage
= this.httpInput
.EndParseIncomingMessage(result
, out exception
);
1790 Fx
.Assert(exception
== null, "ParseIncomingMessage should not set an exception after parsing a response message.");
1792 if (this.replyMessage
!= null)
1794 HttpChannelUtilities
.AddReplySecurityProperty(this.factory
, this.request
, this.response
,
1799 static void OnParseIncomingMessage(IAsyncResult result
)
1801 if (result
.CompletedSynchronously
)
1806 HttpChannelAsyncRequest thisPtr
= (HttpChannelAsyncRequest
)result
.AsyncState
;
1808 Exception completionException
= null;
1811 thisPtr
.CompleteParseIncomingMessage(result
);
1813 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
1820 completionException
= e
;
1822 thisPtr
.Complete(false, completionException
);
1825 static void OnSend(IAsyncResult result
)
1827 if (result
.CompletedSynchronously
)
1832 HttpChannelAsyncRequest thisPtr
= (HttpChannelAsyncRequest
)result
.AsyncState
;
1834 Exception completionException
= null;
1838 completeSelf
= thisPtr
.CompleteSend(result
);
1840 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
1848 completeSelf
= true;
1849 completionException
= e
;
1853 thisPtr
.Complete(false, completionException
);
1857 static void OnSendTimeout(object state
)
1859 HttpChannelAsyncRequest thisPtr
= (HttpChannelAsyncRequest
)state
;
1860 thisPtr
.AbortSend();
1863 [System
.Diagnostics
.CodeAnalysis
.SuppressMessage(FxCop
.Category
.ReliabilityBasic
, "Reliability104",
1864 Justification
= "This is an old method from previous release.")]
1865 static void OnGetResponse(IAsyncResult result
)
1867 if (result
.CompletedSynchronously
)
1872 HttpChannelAsyncRequest thisPtr
= (HttpChannelAsyncRequest
)result
.AsyncState
;
1874 Exception completionException
= null;
1878 completeSelf
= thisPtr
.CompleteGetResponse(result
);
1880 catch (WebException webException
)
1882 completeSelf
= true;
1883 completionException
= new CommunicationException(webException
.Message
, webException
);
1885 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
1892 completeSelf
= true;
1893 completionException
= e
;
1897 thisPtr
.Complete(false, completionException
);
1901 public void OnReleaseRequest()
1903 this.TryCompleteWebRequest(this.request
);
1906 void TryCompleteWebRequest(HttpWebRequest request
)
1908 if (request
== null)
1913 if (Interlocked
.CompareExchange(ref this.webRequestCompleted
, 1, 0) == 0)
1915 this.channel
.OnWebRequestCompleted(request
);
1920 class GetWebRequestAsyncResult
: AsyncResult
1922 static AsyncCallback onGetSspiCredential
;
1923 static AsyncCallback onGetUserNameCredential
;
1925 SecurityTokenContainer clientCertificateToken
;
1926 HttpChannelFactory
<IRequestChannel
> factory
;
1927 SecurityTokenProviderContainer proxyTokenProvider
;
1928 HttpWebRequest request
;
1930 TimeoutHelper timeoutHelper
;
1931 SecurityTokenProviderContainer tokenProvider
;
1934 public GetWebRequestAsyncResult(HttpRequestChannel channel
,
1935 EndpointAddress to
, Uri via
, SecurityTokenContainer clientCertificateToken
, ref TimeoutHelper timeoutHelper
,
1936 AsyncCallback callback
, object state
)
1937 : base(callback
, state
)
1941 this.clientCertificateToken
= clientCertificateToken
;
1942 this.timeoutHelper
= timeoutHelper
;
1943 this.factory
= channel
.Factory
;
1944 this.tokenProvider
= channel
.tokenProvider
;
1945 this.proxyTokenProvider
= channel
.proxyTokenProvider
;
1946 if (factory
.ManualAddressing
)
1948 this.factory
.CreateAndOpenTokenProviders(to
, via
, channel
.channelParameters
, timeoutHelper
.RemainingTime(),
1949 out this.tokenProvider
, out this.proxyTokenProvider
);
1952 bool completeSelf
= false;
1953 IAsyncResult result
= null;
1954 if (factory
.AuthenticationScheme
== AuthenticationSchemes
.Anonymous
)
1956 SetupWebRequest(AuthenticationLevel
.None
, TokenImpersonationLevel
.None
, null);
1957 completeSelf
= true;
1959 else if (factory
.AuthenticationScheme
== AuthenticationSchemes
.Basic
)
1961 if (onGetUserNameCredential
== null)
1963 onGetUserNameCredential
= Fx
.ThunkCallback(new AsyncCallback(OnGetUserNameCredential
));
1966 result
= TransportSecurityHelpers
.BeginGetUserNameCredential(
1967 tokenProvider
, timeoutHelper
.RemainingTime(), onGetUserNameCredential
, this);
1969 if (result
.CompletedSynchronously
)
1971 CompleteGetUserNameCredential(result
);
1972 completeSelf
= true;
1977 if (onGetSspiCredential
== null)
1979 onGetSspiCredential
= Fx
.ThunkCallback(new AsyncCallback(OnGetSspiCredential
));
1982 result
= TransportSecurityHelpers
.BeginGetSspiCredential(
1983 tokenProvider
, timeoutHelper
.RemainingTime(), onGetSspiCredential
, this);
1985 if (result
.CompletedSynchronously
)
1987 CompleteGetSspiCredential(result
);
1988 completeSelf
= true;
1994 CloseTokenProvidersIfRequired();
1995 base.Complete(true);
1999 public static HttpWebRequest
End(IAsyncResult result
)
2001 GetWebRequestAsyncResult thisPtr
= AsyncResult
.End
<GetWebRequestAsyncResult
>(result
);
2002 return thisPtr
.request
;
2005 void CompleteGetUserNameCredential(IAsyncResult result
)
2007 NetworkCredential credential
=
2008 TransportSecurityHelpers
.EndGetUserNameCredential(result
);
2009 SetupWebRequest(AuthenticationLevel
.None
, TokenImpersonationLevel
.None
, credential
);
2012 void CompleteGetSspiCredential(IAsyncResult result
)
2014 AuthenticationLevel authenticationLevel
;
2015 TokenImpersonationLevel impersonationLevel
;
2016 NetworkCredential credential
=
2017 TransportSecurityHelpers
.EndGetSspiCredential(result
, out impersonationLevel
, out authenticationLevel
);
2019 if (factory
.AuthenticationScheme
== AuthenticationSchemes
.Digest
)
2021 HttpChannelUtilities
.ValidateDigestCredential(ref credential
, impersonationLevel
);
2023 else if (factory
.AuthenticationScheme
== AuthenticationSchemes
.Ntlm
)
2025 if (authenticationLevel
== AuthenticationLevel
.MutualAuthRequired
)
2027 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new InvalidOperationException(
2028 SR
.GetString(SR
.CredentialDisallowsNtlm
)));
2032 SetupWebRequest(authenticationLevel
, impersonationLevel
, credential
);
2035 void SetupWebRequest(AuthenticationLevel authenticationLevel
, TokenImpersonationLevel impersonationLevel
, NetworkCredential credential
)
2037 this.request
= factory
.GetWebRequest(to
, via
, credential
, impersonationLevel
,
2038 authenticationLevel
, this.proxyTokenProvider
, this.clientCertificateToken
, timeoutHelper
.RemainingTime(), false);
2041 void CloseTokenProvidersIfRequired()
2043 if (this.factory
.ManualAddressing
)
2045 if (this.tokenProvider
!= null)
2047 tokenProvider
.Abort();
2049 if (this.proxyTokenProvider
!= null)
2051 proxyTokenProvider
.Abort();
2056 static void OnGetSspiCredential(IAsyncResult result
)
2058 if (result
.CompletedSynchronously
)
2063 GetWebRequestAsyncResult thisPtr
= (GetWebRequestAsyncResult
)result
.AsyncState
;
2065 Exception completionException
= null;
2068 thisPtr
.CompleteGetSspiCredential(result
);
2069 thisPtr
.CloseTokenProvidersIfRequired();
2071 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2079 completionException
= e
;
2081 thisPtr
.Complete(false, completionException
);
2084 static void OnGetUserNameCredential(IAsyncResult result
)
2086 if (result
.CompletedSynchronously
)
2091 GetWebRequestAsyncResult thisPtr
= (GetWebRequestAsyncResult
)result
.AsyncState
;
2093 Exception completionException
= null;
2096 thisPtr
.CompleteGetUserNameCredential(result
);
2097 thisPtr
.CloseTokenProvidersIfRequired();
2099 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
2107 completionException
= e
;
2109 thisPtr
.Complete(false, completionException
);
2114 class WebProxyFactory
2118 AuthenticationSchemes authenticationScheme
;
2120 public WebProxyFactory(Uri address
, bool bypassOnLocal
, AuthenticationSchemes authenticationScheme
)
2122 this.address
= address
;
2123 this.bypassOnLocal
= bypassOnLocal
;
2125 if (!authenticationScheme
.IsSingleton())
2127 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperArgument("value", SR
.GetString(SR
.HttpRequiresSingleAuthScheme
,
2128 authenticationScheme
));
2131 this.authenticationScheme
= authenticationScheme
;
2134 internal AuthenticationSchemes AuthenticationScheme
2138 return authenticationScheme
;
2142 public IWebProxy
CreateWebProxy(HttpWebRequest request
, SecurityTokenProviderContainer tokenProvider
, TimeSpan timeout
)
2144 WebProxy result
= new WebProxy(this.address
, this.bypassOnLocal
);
2146 if (this.authenticationScheme
!= AuthenticationSchemes
.Anonymous
)
2148 TokenImpersonationLevel impersonationLevel
;
2149 AuthenticationLevel authenticationLevel
;
2150 NetworkCredential credential
= HttpChannelUtilities
.GetCredential(this.authenticationScheme
,
2151 tokenProvider
, timeout
, out impersonationLevel
, out authenticationLevel
);
2153 // The impersonation level for target auth is also used for proxy auth (by System.Net). Therefore,
2154 // fail if the level stipulated for proxy auth is more restrictive than that for target auth.
2155 if (!TokenImpersonationLevelHelper
.IsGreaterOrEqual(impersonationLevel
, request
.ImpersonationLevel
))
2157 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new InvalidOperationException(SR
.GetString(
2158 SR
.ProxyImpersonationLevelMismatch
, impersonationLevel
, request
.ImpersonationLevel
)));
2161 // The authentication level for target auth is also used for proxy auth (by System.Net).
2162 // Therefore, fail if proxy auth requires mutual authentication but target auth does not.
2163 if ((authenticationLevel
== AuthenticationLevel
.MutualAuthRequired
) &&
2164 (request
.AuthenticationLevel
!= AuthenticationLevel
.MutualAuthRequired
))
2166 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperError(new InvalidOperationException(SR
.GetString(
2167 SR
.ProxyAuthenticationLevelMismatch
, authenticationLevel
, request
.AuthenticationLevel
)));
2170 CredentialCache credentials
= new CredentialCache();
2171 credentials
.Add(this.address
, AuthenticationSchemesHelper
.ToString(this.authenticationScheme
),
2173 result
.Credentials
= credentials
;