2181 lines
90 KiB
C#
2181 lines
90 KiB
C#
|
//------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//------------------------------------------------------------
|
||
|
namespace System.ServiceModel.Channels
|
||
|
{
|
||
|
using System.Collections.ObjectModel;
|
||
|
using System.Diagnostics;
|
||
|
using System.IdentityModel.Policy;
|
||
|
using System.IdentityModel.Selectors;
|
||
|
using System.IdentityModel.Tokens;
|
||
|
using System.IO;
|
||
|
using System.Net;
|
||
|
using System.Net.Cache;
|
||
|
using System.Net.Security;
|
||
|
using System.Runtime;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Runtime.Diagnostics;
|
||
|
using System.Security;
|
||
|
using System.Security.Authentication.ExtendedProtection;
|
||
|
using System.Security.Cryptography;
|
||
|
using System.Security.Permissions;
|
||
|
using System.Security.Principal;
|
||
|
using System.ServiceModel;
|
||
|
using System.ServiceModel.Description;
|
||
|
using System.ServiceModel.Diagnostics;
|
||
|
using System.ServiceModel.Diagnostics.Application;
|
||
|
using System.ServiceModel.Security;
|
||
|
using System.ServiceModel.Security.Tokens;
|
||
|
using System.Text;
|
||
|
using System.Threading;
|
||
|
|
||
|
class HttpChannelFactory<TChannel>
|
||
|
: TransportChannelFactory<TChannel>,
|
||
|
IHttpTransportFactorySettings
|
||
|
{
|
||
|
static bool httpWebRequestWebPermissionDenied = false;
|
||
|
static RequestCachePolicy requestCachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
|
||
|
static long connectionGroupNamePrefix = 0;
|
||
|
|
||
|
readonly ClientWebSocketFactory clientWebSocketFactory;
|
||
|
|
||
|
bool allowCookies;
|
||
|
AuthenticationSchemes authenticationScheme;
|
||
|
HttpCookieContainerManager httpCookieContainerManager;
|
||
|
|
||
|
// Double-checked locking pattern requires volatile for read/write synchronization
|
||
|
volatile MruCache<Uri, Uri> credentialCacheUriPrefixCache;
|
||
|
bool decompressionEnabled;
|
||
|
|
||
|
// Double-checked locking pattern requires volatile for read/write synchronization
|
||
|
[Fx.Tag.SecurityNote(Critical = "This cache stores strings that contain domain/user name/password. Must not be settable from PT code.")]
|
||
|
[SecurityCritical]
|
||
|
volatile MruCache<string, string> credentialHashCache;
|
||
|
|
||
|
[Fx.Tag.SecurityNote(Critical = "This hash algorithm takes strings that contain domain/user name/password. Must not be settable from PT code.")]
|
||
|
[SecurityCritical]
|
||
|
HashAlgorithm hashAlgorithm;
|
||
|
bool keepAliveEnabled;
|
||
|
int maxBufferSize;
|
||
|
IWebProxy proxy;
|
||
|
WebProxyFactory proxyFactory;
|
||
|
SecurityCredentialsManager channelCredentials;
|
||
|
SecurityTokenManager securityTokenManager;
|
||
|
TransferMode transferMode;
|
||
|
ISecurityCapabilities securityCapabilities;
|
||
|
WebSocketTransportSettings webSocketSettings;
|
||
|
ConnectionBufferPool bufferPool;
|
||
|
Lazy<string> webSocketSoapContentType;
|
||
|
string uniqueConnectionGroupNamePrefix;
|
||
|
|
||
|
internal HttpChannelFactory(HttpTransportBindingElement bindingElement, BindingContext context)
|
||
|
: base(bindingElement, context, HttpTransportDefaults.GetDefaultMessageEncoderFactory())
|
||
|
{
|
||
|
// validate setting interactions
|
||
|
if (bindingElement.TransferMode == TransferMode.Buffered)
|
||
|
{
|
||
|
if (bindingElement.MaxReceivedMessageSize > int.MaxValue)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("bindingElement.MaxReceivedMessageSize",
|
||
|
SR.GetString(SR.MaxReceivedMessageSizeMustBeInIntegerRange)));
|
||
|
}
|
||
|
|
||
|
if (bindingElement.MaxBufferSize != bindingElement.MaxReceivedMessageSize)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement",
|
||
|
SR.GetString(SR.MaxBufferSizeMustMatchMaxReceivedMessageSize));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (bindingElement.MaxBufferSize > bindingElement.MaxReceivedMessageSize)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement",
|
||
|
SR.GetString(SR.MaxBufferSizeMustNotExceedMaxReceivedMessageSize));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (TransferModeHelper.IsRequestStreamed(bindingElement.TransferMode) &&
|
||
|
bindingElement.AuthenticationScheme != AuthenticationSchemes.Anonymous)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("bindingElement", SR.GetString(
|
||
|
SR.HttpAuthDoesNotSupportRequestStreaming));
|
||
|
}
|
||
|
|
||
|
this.allowCookies = bindingElement.AllowCookies;
|
||
|
#pragma warning disable 618
|
||
|
if (!this.allowCookies)
|
||
|
{
|
||
|
Collection<HttpCookieContainerBindingElement> httpCookieContainerBindingElements = context.BindingParameters.FindAll<HttpCookieContainerBindingElement>();
|
||
|
if (httpCookieContainerBindingElements.Count > 1)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MultipleCCbesInParameters, typeof(HttpCookieContainerBindingElement))));
|
||
|
}
|
||
|
if (httpCookieContainerBindingElements.Count == 1)
|
||
|
{
|
||
|
this.allowCookies = true;
|
||
|
context.BindingParameters.Remove<HttpCookieContainerBindingElement>();
|
||
|
}
|
||
|
}
|
||
|
#pragma warning restore 618
|
||
|
|
||
|
if (this.allowCookies)
|
||
|
{
|
||
|
this.httpCookieContainerManager = new HttpCookieContainerManager();
|
||
|
}
|
||
|
|
||
|
if (!bindingElement.AuthenticationScheme.IsSingleton())
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.GetString(SR.HttpRequiresSingleAuthScheme,
|
||
|
bindingElement.AuthenticationScheme));
|
||
|
}
|
||
|
|
||
|
this.authenticationScheme = bindingElement.AuthenticationScheme;
|
||
|
this.decompressionEnabled = bindingElement.DecompressionEnabled;
|
||
|
this.keepAliveEnabled = bindingElement.KeepAliveEnabled;
|
||
|
this.maxBufferSize = bindingElement.MaxBufferSize;
|
||
|
this.transferMode = bindingElement.TransferMode;
|
||
|
|
||
|
if (bindingElement.Proxy != null)
|
||
|
{
|
||
|
this.proxy = bindingElement.Proxy;
|
||
|
}
|
||
|
else if (bindingElement.ProxyAddress != null)
|
||
|
{
|
||
|
if (bindingElement.UseDefaultWebProxy)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.UseDefaultWebProxyCantBeUsedWithExplicitProxyAddress)));
|
||
|
}
|
||
|
|
||
|
if (bindingElement.ProxyAuthenticationScheme == AuthenticationSchemes.Anonymous)
|
||
|
{
|
||
|
this.proxy = new WebProxy(bindingElement.ProxyAddress, bindingElement.BypassProxyOnLocal);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.proxy = null;
|
||
|
this.proxyFactory =
|
||
|
new WebProxyFactory(bindingElement.ProxyAddress, bindingElement.BypassProxyOnLocal,
|
||
|
bindingElement.ProxyAuthenticationScheme);
|
||
|
}
|
||
|
}
|
||
|
else if (!bindingElement.UseDefaultWebProxy)
|
||
|
{
|
||
|
this.proxy = new WebProxy();
|
||
|
}
|
||
|
|
||
|
this.channelCredentials = context.BindingParameters.Find<SecurityCredentialsManager>();
|
||
|
this.securityCapabilities = bindingElement.GetProperty<ISecurityCapabilities>(context);
|
||
|
this.webSocketSettings = WebSocketHelper.GetRuntimeWebSocketSettings(bindingElement.WebSocketSettings);
|
||
|
|
||
|
int webSocketBufferSize = WebSocketHelper.ComputeClientBufferSize(this.MaxReceivedMessageSize);
|
||
|
this.bufferPool = new ConnectionBufferPool(webSocketBufferSize);
|
||
|
|
||
|
Collection<ClientWebSocketFactory> clientWebSocketFactories = context.BindingParameters.FindAll<ClientWebSocketFactory>();
|
||
|
if (clientWebSocketFactories.Count > 1)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(
|
||
|
"context",
|
||
|
SR.GetString(SR.MultipleClientWebSocketFactoriesSpecified, typeof(BindingContext).Name, typeof(ClientWebSocketFactory).Name));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.clientWebSocketFactory = clientWebSocketFactories.Count == 0 ? null : clientWebSocketFactories[0];
|
||
|
}
|
||
|
|
||
|
this.webSocketSoapContentType = new Lazy<string>(() => { return this.MessageEncoderFactory.CreateSessionEncoder().ContentType; }, LazyThreadSafetyMode.ExecutionAndPublication);
|
||
|
|
||
|
if (ServiceModelAppSettings.HttpTransportPerFactoryConnectionPool)
|
||
|
{
|
||
|
this.uniqueConnectionGroupNamePrefix = Interlocked.Increment(ref connectionGroupNamePrefix).ToString();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.uniqueConnectionGroupNamePrefix = string.Empty;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool AllowCookies
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.allowCookies;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public AuthenticationSchemes AuthenticationScheme
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.authenticationScheme;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool DecompressionEnabled
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.decompressionEnabled;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public virtual bool IsChannelBindingSupportEnabled
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool KeepAliveEnabled
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.keepAliveEnabled;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public SecurityTokenManager SecurityTokenManager
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.securityTokenManager;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int MaxBufferSize
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return maxBufferSize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public IWebProxy Proxy
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.proxy;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public TransferMode TransferMode
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return transferMode;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override string Scheme
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return Uri.UriSchemeHttp;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public WebSocketTransportSettings WebSocketSettings
|
||
|
{
|
||
|
get { return this.webSocketSettings; }
|
||
|
}
|
||
|
|
||
|
internal string WebSocketSoapContentType
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.webSocketSoapContentType.Value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected ConnectionBufferPool WebSocketBufferPool
|
||
|
{
|
||
|
get { return this.bufferPool; }
|
||
|
}
|
||
|
|
||
|
// must be called under lock (this.credentialHashCache)
|
||
|
HashAlgorithm HashAlgorithm
|
||
|
{
|
||
|
[SecurityCritical]
|
||
|
get
|
||
|
{
|
||
|
if (this.hashAlgorithm == null)
|
||
|
{
|
||
|
this.hashAlgorithm = CryptoHelper.CreateHashAlgorithm(SecurityAlgorithms.Sha1Digest);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.hashAlgorithm.Initialize();
|
||
|
}
|
||
|
|
||
|
return this.hashAlgorithm;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int IHttpTransportFactorySettings.MaxBufferSize
|
||
|
{
|
||
|
get { return MaxBufferSize; }
|
||
|
}
|
||
|
|
||
|
TransferMode IHttpTransportFactorySettings.TransferMode
|
||
|
{
|
||
|
get { return TransferMode; }
|
||
|
}
|
||
|
|
||
|
protected ClientWebSocketFactory ClientWebSocketFactory
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.clientWebSocketFactory;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override T GetProperty<T>()
|
||
|
{
|
||
|
if (typeof(T) == typeof(ISecurityCapabilities))
|
||
|
{
|
||
|
return (T)(object)this.securityCapabilities;
|
||
|
}
|
||
|
if (typeof(T) == typeof(IHttpCookieContainerManager))
|
||
|
{
|
||
|
return (T)(object)this.GetHttpCookieContainerManager();
|
||
|
}
|
||
|
|
||
|
return base.GetProperty<T>();
|
||
|
}
|
||
|
|
||
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||
|
private HttpCookieContainerManager GetHttpCookieContainerManager()
|
||
|
{
|
||
|
return this.httpCookieContainerManager;
|
||
|
}
|
||
|
|
||
|
internal virtual SecurityMessageProperty CreateReplySecurityProperty(HttpWebRequest request,
|
||
|
HttpWebResponse response)
|
||
|
{
|
||
|
// Don't pull in System.Authorization if we don't need to!
|
||
|
if (!response.IsMutuallyAuthenticated)
|
||
|
{
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return CreateMutuallyAuthenticatedReplySecurityProperty(response);
|
||
|
}
|
||
|
|
||
|
internal Exception CreateToMustEqualViaException(Uri to, Uri via)
|
||
|
{
|
||
|
return new ArgumentException(SR.GetString(SR.HttpToMustEqualVia, to, via));
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||
|
SecurityMessageProperty CreateMutuallyAuthenticatedReplySecurityProperty(HttpWebResponse response)
|
||
|
{
|
||
|
string spn = AuthenticationManager.CustomTargetNameDictionary[response.ResponseUri.AbsoluteUri];
|
||
|
if (spn == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.HttpSpnNotFound,
|
||
|
response.ResponseUri)));
|
||
|
}
|
||
|
ReadOnlyCollection<IAuthorizationPolicy> spnPolicies = SecurityUtils.CreatePrincipalNameAuthorizationPolicies(spn);
|
||
|
SecurityMessageProperty remoteSecurity = new SecurityMessageProperty();
|
||
|
remoteSecurity.TransportToken = new SecurityTokenSpecification(null, spnPolicies);
|
||
|
remoteSecurity.ServiceSecurityContext = new ServiceSecurityContext(spnPolicies);
|
||
|
return remoteSecurity;
|
||
|
}
|
||
|
|
||
|
internal override int GetMaxBufferSize()
|
||
|
{
|
||
|
return MaxBufferSize;
|
||
|
}
|
||
|
|
||
|
SecurityTokenProviderContainer CreateAndOpenTokenProvider(TimeSpan timeout, AuthenticationSchemes authenticationScheme,
|
||
|
EndpointAddress target, Uri via, ChannelParameterCollection channelParameters)
|
||
|
{
|
||
|
SecurityTokenProvider tokenProvider = null;
|
||
|
switch (authenticationScheme)
|
||
|
{
|
||
|
case AuthenticationSchemes.Anonymous:
|
||
|
break;
|
||
|
case AuthenticationSchemes.Basic:
|
||
|
tokenProvider = TransportSecurityHelpers.GetUserNameTokenProvider(this.SecurityTokenManager, target, via, this.Scheme, authenticationScheme, channelParameters);
|
||
|
break;
|
||
|
case AuthenticationSchemes.Negotiate:
|
||
|
case AuthenticationSchemes.Ntlm:
|
||
|
tokenProvider = TransportSecurityHelpers.GetSspiTokenProvider(this.SecurityTokenManager, target, via, this.Scheme, authenticationScheme, channelParameters);
|
||
|
break;
|
||
|
case AuthenticationSchemes.Digest:
|
||
|
tokenProvider = TransportSecurityHelpers.GetDigestTokenProvider(this.SecurityTokenManager, target, via, this.Scheme, authenticationScheme, channelParameters);
|
||
|
break;
|
||
|
default:
|
||
|
// The setter for this property should prevent this.
|
||
|
throw Fx.AssertAndThrow("CreateAndOpenTokenProvider: Invalid authentication scheme");
|
||
|
}
|
||
|
SecurityTokenProviderContainer result;
|
||
|
if (tokenProvider != null)
|
||
|
{
|
||
|
result = new SecurityTokenProviderContainer(tokenProvider);
|
||
|
result.Open(timeout);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
result = null;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
protected virtual void ValidateCreateChannelParameters(EndpointAddress remoteAddress, Uri via)
|
||
|
{
|
||
|
base.ValidateScheme(via);
|
||
|
|
||
|
if (this.MessageVersion.Addressing == AddressingVersion.None && remoteAddress.Uri != via)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateToMustEqualViaException(remoteAddress.Uri, via));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override TChannel OnCreateChannel(EndpointAddress remoteAddress, Uri via)
|
||
|
{
|
||
|
EndpointAddress httpRemoteAddress = remoteAddress != null && WebSocketHelper.IsWebSocketUri(remoteAddress.Uri) ?
|
||
|
new EndpointAddress(WebSocketHelper.NormalizeWsSchemeWithHttpScheme(remoteAddress.Uri), remoteAddress) :
|
||
|
remoteAddress;
|
||
|
|
||
|
Uri httpVia = WebSocketHelper.IsWebSocketUri(via) ? WebSocketHelper.NormalizeWsSchemeWithHttpScheme(via) : via;
|
||
|
return this.OnCreateChannelCore(httpRemoteAddress, httpVia);
|
||
|
}
|
||
|
|
||
|
protected virtual TChannel OnCreateChannelCore(EndpointAddress remoteAddress, Uri via)
|
||
|
{
|
||
|
ValidateCreateChannelParameters(remoteAddress, via);
|
||
|
this.ValidateWebSocketTransportUsage();
|
||
|
|
||
|
if (typeof(TChannel) == typeof(IRequestChannel))
|
||
|
{
|
||
|
return (TChannel)(object)new HttpRequestChannel((HttpChannelFactory<IRequestChannel>)(object)this, remoteAddress, via, ManualAddressing);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return (TChannel)(object)new ClientWebSocketTransportDuplexSessionChannel((HttpChannelFactory<IDuplexSessionChannel>)(object)this, this.clientWebSocketFactory, remoteAddress, via, this.WebSocketBufferPool);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected void ValidateWebSocketTransportUsage()
|
||
|
{
|
||
|
Type channelType = typeof(TChannel);
|
||
|
if (channelType == typeof(IRequestChannel) && this.WebSocketSettings.TransportUsage == WebSocketTransportUsage.Always)
|
||
|
{
|
||
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetString(
|
||
|
SR.WebSocketCannotCreateRequestClientChannelWithCertainWebSocketTransportUsage,
|
||
|
typeof(TChannel),
|
||
|
WebSocketTransportSettings.TransportUsageMethodName,
|
||
|
typeof(WebSocketTransportSettings).Name,
|
||
|
this.WebSocketSettings.TransportUsage)));
|
||
|
|
||
|
}
|
||
|
else if (channelType == typeof(IDuplexSessionChannel))
|
||
|
{
|
||
|
if (this.WebSocketSettings.TransportUsage == WebSocketTransportUsage.Never)
|
||
|
{
|
||
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetString(
|
||
|
SR.WebSocketCannotCreateRequestClientChannelWithCertainWebSocketTransportUsage,
|
||
|
typeof(TChannel),
|
||
|
WebSocketTransportSettings.TransportUsageMethodName,
|
||
|
typeof(WebSocketTransportSettings).Name,
|
||
|
this.WebSocketSettings.TransportUsage)));
|
||
|
}
|
||
|
else if (!WebSocketHelper.OSSupportsWebSockets() && this.ClientWebSocketFactory == null)
|
||
|
{
|
||
|
throw FxTrace.Exception.AsError(new PlatformNotSupportedException(SR.GetString(SR.WebSocketsClientSideNotSupported, typeof(ClientWebSocketFactory).FullName)));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||
|
void InitializeSecurityTokenManager()
|
||
|
{
|
||
|
if (this.channelCredentials == null)
|
||
|
{
|
||
|
this.channelCredentials = ClientCredentials.CreateDefaultCredentials();
|
||
|
}
|
||
|
this.securityTokenManager = this.channelCredentials.CreateSecurityTokenManager();
|
||
|
}
|
||
|
|
||
|
protected virtual bool IsSecurityTokenManagerRequired()
|
||
|
{
|
||
|
if (this.AuthenticationScheme != AuthenticationSchemes.Anonymous)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
if (this.proxyFactory != null && this.proxyFactory.AuthenticationScheme != AuthenticationSchemes.Anonymous)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
|
||
|
{
|
||
|
this.OnOpen(timeout);
|
||
|
return new CompletedAsyncResult(callback, state);
|
||
|
}
|
||
|
|
||
|
protected override void OnEndOpen(IAsyncResult result)
|
||
|
{
|
||
|
CompletedAsyncResult.End(result);
|
||
|
}
|
||
|
|
||
|
protected override void OnOpen(TimeSpan timeout)
|
||
|
{
|
||
|
if (IsSecurityTokenManagerRequired())
|
||
|
{
|
||
|
this.InitializeSecurityTokenManager();
|
||
|
}
|
||
|
|
||
|
if (this.AllowCookies &&
|
||
|
!this.httpCookieContainerManager.IsInitialized) // We don't want to overwrite the CookieContainer if someone has set it already.
|
||
|
{
|
||
|
this.httpCookieContainerManager.CookieContainer = new CookieContainer();
|
||
|
}
|
||
|
|
||
|
// we need to make sure System.Net will buffer faults (sent as 500 requests) up to our allowed size
|
||
|
// Their value is in Kbytes and ours is in bytes. We round up so that the KB value is large enough to
|
||
|
// encompass our MaxReceivedMessageSize. See MB#20860 and related for details
|
||
|
|
||
|
if (!httpWebRequestWebPermissionDenied && HttpWebRequest.DefaultMaximumErrorResponseLength != -1)
|
||
|
{
|
||
|
int MaxReceivedMessageSizeKbytes;
|
||
|
if (MaxBufferSize >= (int.MaxValue - 1024)) // make sure NCL doesn't overflow
|
||
|
{
|
||
|
MaxReceivedMessageSizeKbytes = -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
MaxReceivedMessageSizeKbytes = (int)(MaxBufferSize / 1024);
|
||
|
if (MaxReceivedMessageSizeKbytes * 1024 < MaxBufferSize)
|
||
|
{
|
||
|
MaxReceivedMessageSizeKbytes++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (MaxReceivedMessageSizeKbytes == -1
|
||
|
|| MaxReceivedMessageSizeKbytes > HttpWebRequest.DefaultMaximumErrorResponseLength)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
HttpWebRequest.DefaultMaximumErrorResponseLength = MaxReceivedMessageSizeKbytes;
|
||
|
}
|
||
|
catch (SecurityException exception)
|
||
|
{
|
||
|
// CSDMain\33725 - setting DefaultMaximumErrorResponseLength should not fail HttpChannelFactory.OnOpen
|
||
|
// if the user does not have the permission to do so.
|
||
|
httpWebRequestWebPermissionDenied = true;
|
||
|
|
||
|
DiagnosticUtility.TraceHandledException(exception, TraceEventType.Warning);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override void OnClosed()
|
||
|
{
|
||
|
base.OnClosed();
|
||
|
if (this.bufferPool != null)
|
||
|
{
|
||
|
this.bufferPool.Close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static internal void TraceResponseReceived(HttpWebResponse response, Message message, object receiver)
|
||
|
{
|
||
|
if (DiagnosticUtility.ShouldTraceVerbose)
|
||
|
{
|
||
|
if (response != null && response.ResponseUri != null)
|
||
|
{
|
||
|
TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.HttpResponseReceived, SR.GetString(SR.TraceCodeHttpResponseReceived), new StringTraceRecord("ResponseUri", response.ResponseUri.ToString()), receiver, null, message);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.HttpResponseReceived, SR.GetString(SR.TraceCodeHttpResponseReceived), receiver, message);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Fx.Tag.SecurityNote(Critical = "Uses unsafe critical method AppendWindowsAuthenticationInfo to access the credential domain/user name/password.")]
|
||
|
[SecurityCritical]
|
||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||
|
string AppendWindowsAuthenticationInfo(string inputString, NetworkCredential credential,
|
||
|
AuthenticationLevel authenticationLevel, TokenImpersonationLevel impersonationLevel)
|
||
|
{
|
||
|
return SecurityUtils.AppendWindowsAuthenticationInfo(inputString, credential, authenticationLevel, impersonationLevel);
|
||
|
}
|
||
|
|
||
|
protected virtual string OnGetConnectionGroupPrefix(HttpWebRequest httpWebRequest, SecurityTokenContainer clientCertificateToken)
|
||
|
{
|
||
|
return string.Empty;
|
||
|
}
|
||
|
|
||
|
internal static bool IsWindowsAuth(AuthenticationSchemes authScheme)
|
||
|
{
|
||
|
Fx.Assert(authScheme.IsSingleton(), "authenticationScheme used in an Http(s)ChannelFactory must be a singleton value.");
|
||
|
|
||
|
return authScheme == AuthenticationSchemes.Negotiate ||
|
||
|
authScheme == AuthenticationSchemes.Ntlm;
|
||
|
}
|
||
|
|
||
|
[Fx.Tag.SecurityNote(Critical = "Uses unsafe critical method AppendWindowsAuthenticationInfo to access the credential domain/user name/password.",
|
||
|
Safe = "Uses the domain/user name/password to store and compute a hash. The store is SecurityCritical. The hash leaks but" +
|
||
|
"the hash cannot be reversed to the domain/user name/password.")]
|
||
|
[SecuritySafeCritical]
|
||
|
string GetConnectionGroupName(HttpWebRequest httpWebRequest, NetworkCredential credential, AuthenticationLevel authenticationLevel,
|
||
|
TokenImpersonationLevel impersonationLevel, SecurityTokenContainer clientCertificateToken)
|
||
|
{
|
||
|
if (this.credentialHashCache == null)
|
||
|
{
|
||
|
lock (ThisLock)
|
||
|
{
|
||
|
if (this.credentialHashCache == null)
|
||
|
{
|
||
|
this.credentialHashCache = new MruCache<string, string>(5);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The following line is a work-around for VSWhidbey 558605. In particular, we need to isolate our
|
||
|
// connection groups based on whether we are streaming the request.
|
||
|
string inputString = TransferModeHelper.IsRequestStreamed(this.TransferMode) ? "streamed" : string.Empty;
|
||
|
|
||
|
if (IsWindowsAuth(this.AuthenticationScheme))
|
||
|
{
|
||
|
// for NTLM & Negotiate, System.Net doesn't pool connections by default. This is because
|
||
|
// IIS doesn't re-authenticate NTLM connections (made for a perf reason), and HttpWebRequest
|
||
|
// shared connections among multiple callers.
|
||
|
// This causes Indigo a performance problem in turn. We mitigate this by (1) enabling
|
||
|
// connection sharing for NTLM connections on our pool, and (2) scoping the pool we use
|
||
|
// to be based on the NetworkCredential that is being used to authenticate the connection.
|
||
|
// Therefore we're only sharing connections among the same Credential.
|
||
|
|
||
|
// Setting this will fail in partial trust, and that's ok since this is an optimization.
|
||
|
if (!httpWebRequestWebPermissionDenied)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
httpWebRequest.UnsafeAuthenticatedConnectionSharing = true;
|
||
|
}
|
||
|
catch (SecurityException e)
|
||
|
{
|
||
|
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
|
||
|
httpWebRequestWebPermissionDenied = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inputString = AppendWindowsAuthenticationInfo(inputString, credential, authenticationLevel, impersonationLevel);
|
||
|
}
|
||
|
|
||
|
string prefix = this.OnGetConnectionGroupPrefix(httpWebRequest, clientCertificateToken);
|
||
|
inputString = string.Concat(this.uniqueConnectionGroupNamePrefix, prefix, inputString);
|
||
|
|
||
|
string credentialHash = null;
|
||
|
|
||
|
// we have to lock around each call to TryGetValue since the MruCache modifies the
|
||
|
// contents of it's mruList in a single-threaded manner underneath TryGetValue
|
||
|
|
||
|
if (!string.IsNullOrEmpty(inputString))
|
||
|
{
|
||
|
lock (this.credentialHashCache)
|
||
|
{
|
||
|
if (!this.credentialHashCache.TryGetValue(inputString, out credentialHash))
|
||
|
{
|
||
|
byte[] inputBytes = new UTF8Encoding().GetBytes(inputString);
|
||
|
byte[] digestBytes = this.HashAlgorithm.ComputeHash(inputBytes);
|
||
|
credentialHash = Convert.ToBase64String(digestBytes);
|
||
|
this.credentialHashCache.Add(inputString, credentialHash);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return credentialHash;
|
||
|
}
|
||
|
|
||
|
Uri GetCredentialCacheUriPrefix(Uri via)
|
||
|
{
|
||
|
Uri result;
|
||
|
|
||
|
if (this.credentialCacheUriPrefixCache == null)
|
||
|
{
|
||
|
lock (ThisLock)
|
||
|
{
|
||
|
if (this.credentialCacheUriPrefixCache == null)
|
||
|
{
|
||
|
this.credentialCacheUriPrefixCache = new MruCache<Uri, Uri>(10);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lock (this.credentialCacheUriPrefixCache)
|
||
|
{
|
||
|
if (!this.credentialCacheUriPrefixCache.TryGetValue(via, out result))
|
||
|
{
|
||
|
result = new UriBuilder(via.Scheme, via.Host, via.Port).Uri;
|
||
|
this.credentialCacheUriPrefixCache.Add(via, result);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// core code for creating an HttpWebRequest
|
||
|
HttpWebRequest GetWebRequest(EndpointAddress to, Uri via, NetworkCredential credential,
|
||
|
TokenImpersonationLevel impersonationLevel, AuthenticationLevel authenticationLevel,
|
||
|
SecurityTokenProviderContainer proxyTokenProvider, SecurityTokenContainer clientCertificateToken, TimeSpan timeout, bool isWebSocketRequest)
|
||
|
{
|
||
|
Uri httpWebRequestUri = isWebSocketRequest ? WebSocketHelper.GetWebSocketUri(via) : via;
|
||
|
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(httpWebRequestUri);
|
||
|
Fx.Assert(httpWebRequest.Method.Equals("GET", StringComparison.OrdinalIgnoreCase), "the default HTTP method of HttpWebRequest should be 'Get'.");
|
||
|
|
||
|
if (!isWebSocketRequest)
|
||
|
{
|
||
|
httpWebRequest.Method = "POST";
|
||
|
|
||
|
if (TransferModeHelper.IsRequestStreamed(TransferMode))
|
||
|
{
|
||
|
httpWebRequest.SendChunked = true;
|
||
|
httpWebRequest.AllowWriteStreamBuffering = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
httpWebRequest.AllowWriteStreamBuffering = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
httpWebRequest.CachePolicy = requestCachePolicy;
|
||
|
httpWebRequest.KeepAlive = this.keepAliveEnabled;
|
||
|
|
||
|
if (this.decompressionEnabled)
|
||
|
{
|
||
|
httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
httpWebRequest.AutomaticDecompression = DecompressionMethods.None;
|
||
|
}
|
||
|
|
||
|
if (credential != null)
|
||
|
{
|
||
|
CredentialCache credentials = new CredentialCache();
|
||
|
credentials.Add(this.GetCredentialCacheUriPrefix(via),
|
||
|
AuthenticationSchemesHelper.ToString(this.authenticationScheme), credential);
|
||
|
httpWebRequest.Credentials = credentials;
|
||
|
}
|
||
|
httpWebRequest.AuthenticationLevel = authenticationLevel;
|
||
|
httpWebRequest.ImpersonationLevel = impersonationLevel;
|
||
|
|
||
|
string connectionGroupName = GetConnectionGroupName(httpWebRequest, credential, authenticationLevel, impersonationLevel, clientCertificateToken);
|
||
|
|
||
|
X509CertificateEndpointIdentity remoteCertificateIdentity = to.Identity as X509CertificateEndpointIdentity;
|
||
|
if (remoteCertificateIdentity != null)
|
||
|
{
|
||
|
connectionGroupName = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}[{1}]", connectionGroupName, remoteCertificateIdentity.Certificates[0].Thumbprint);
|
||
|
}
|
||
|
|
||
|
if (!string.IsNullOrEmpty(connectionGroupName))
|
||
|
{
|
||
|
httpWebRequest.ConnectionGroupName = connectionGroupName;
|
||
|
}
|
||
|
|
||
|
if (AuthenticationScheme == AuthenticationSchemes.Basic)
|
||
|
{
|
||
|
httpWebRequest.PreAuthenticate = true;
|
||
|
}
|
||
|
|
||
|
if (this.proxy != null)
|
||
|
{
|
||
|
httpWebRequest.Proxy = this.proxy;
|
||
|
}
|
||
|
else if (this.proxyFactory != null)
|
||
|
{
|
||
|
httpWebRequest.Proxy = this.proxyFactory.CreateWebProxy(httpWebRequest, proxyTokenProvider, timeout);
|
||
|
}
|
||
|
|
||
|
if (this.AllowCookies)
|
||
|
{
|
||
|
httpWebRequest.CookieContainer = this.httpCookieContainerManager.CookieContainer;
|
||
|
}
|
||
|
|
||
|
// we do this at the end so that we access the correct ServicePoint
|
||
|
httpWebRequest.ServicePoint.UseNagleAlgorithm = false;
|
||
|
|
||
|
return httpWebRequest;
|
||
|
}
|
||
|
|
||
|
void ApplyManualAddressing(ref EndpointAddress to, ref Uri via, Message message)
|
||
|
{
|
||
|
if (ManualAddressing)
|
||
|
{
|
||
|
Uri toHeader = message.Headers.To;
|
||
|
if (toHeader == null)
|
||
|
{
|
||
|
throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ManualAddressingRequiresAddressedMessages)), message);
|
||
|
}
|
||
|
|
||
|
to = new EndpointAddress(toHeader);
|
||
|
|
||
|
if (this.MessageVersion.Addressing == AddressingVersion.None)
|
||
|
{
|
||
|
via = toHeader;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// now apply query string property
|
||
|
object property;
|
||
|
if (message.Properties.TryGetValue(HttpRequestMessageProperty.Name, out property))
|
||
|
{
|
||
|
HttpRequestMessageProperty requestProperty = (HttpRequestMessageProperty)property;
|
||
|
if (!string.IsNullOrEmpty(requestProperty.QueryString))
|
||
|
{
|
||
|
UriBuilder uriBuilder = new UriBuilder(via);
|
||
|
|
||
|
if (requestProperty.QueryString.StartsWith("?", StringComparison.Ordinal))
|
||
|
{
|
||
|
uriBuilder.Query = requestProperty.QueryString.Substring(1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
uriBuilder.Query = requestProperty.QueryString;
|
||
|
}
|
||
|
|
||
|
via = uriBuilder.Uri;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||
|
void CreateAndOpenTokenProvidersCore(EndpointAddress to, Uri via, ChannelParameterCollection channelParameters, TimeSpan timeout, out SecurityTokenProviderContainer tokenProvider, out SecurityTokenProviderContainer proxyTokenProvider)
|
||
|
{
|
||
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
||
|
tokenProvider = CreateAndOpenTokenProvider(timeoutHelper.RemainingTime(), this.AuthenticationScheme, to, via, channelParameters);
|
||
|
if (this.proxyFactory != null)
|
||
|
{
|
||
|
proxyTokenProvider = CreateAndOpenTokenProvider(timeoutHelper.RemainingTime(), this.proxyFactory.AuthenticationScheme, to, via, channelParameters);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
proxyTokenProvider = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void CreateAndOpenTokenProviders(EndpointAddress to, Uri via, ChannelParameterCollection channelParameters, TimeSpan timeout, out SecurityTokenProviderContainer tokenProvider, out SecurityTokenProviderContainer proxyTokenProvider)
|
||
|
{
|
||
|
if (!IsSecurityTokenManagerRequired())
|
||
|
{
|
||
|
tokenProvider = null;
|
||
|
proxyTokenProvider = null;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CreateAndOpenTokenProvidersCore(to, via, channelParameters, timeout, out tokenProvider, out proxyTokenProvider);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal HttpWebRequest GetWebRequest(EndpointAddress to, Uri via, SecurityTokenProviderContainer tokenProvider,
|
||
|
SecurityTokenProviderContainer proxyTokenProvider, SecurityTokenContainer clientCertificateToken, TimeSpan timeout, bool isWebSocketRequest)
|
||
|
{
|
||
|
TokenImpersonationLevel impersonationLevel;
|
||
|
AuthenticationLevel authenticationLevel;
|
||
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
||
|
NetworkCredential credential = HttpChannelUtilities.GetCredential(this.authenticationScheme,
|
||
|
tokenProvider, timeoutHelper.RemainingTime(), out impersonationLevel, out authenticationLevel);
|
||
|
|
||
|
return GetWebRequest(to, via, credential, impersonationLevel, authenticationLevel, proxyTokenProvider, clientCertificateToken, timeoutHelper.RemainingTime(), isWebSocketRequest);
|
||
|
}
|
||
|
|
||
|
internal static bool MapIdentity(EndpointAddress target, AuthenticationSchemes authenticationScheme)
|
||
|
{
|
||
|
if ((target.Identity == null) || (target.Identity is X509CertificateEndpointIdentity))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return IsWindowsAuth(authenticationScheme);
|
||
|
}
|
||
|
|
||
|
bool MapIdentity(EndpointAddress target)
|
||
|
{
|
||
|
return MapIdentity(target, this.AuthenticationScheme);
|
||
|
}
|
||
|
|
||
|
protected class HttpRequestChannel : RequestChannel
|
||
|
{
|
||
|
// Double-checked locking pattern requires volatile for read/write synchronization
|
||
|
volatile bool cleanupIdentity;
|
||
|
HttpChannelFactory<IRequestChannel> factory;
|
||
|
SecurityTokenProviderContainer tokenProvider;
|
||
|
SecurityTokenProviderContainer proxyTokenProvider;
|
||
|
ServiceModelActivity activity = null;
|
||
|
ChannelParameterCollection channelParameters;
|
||
|
|
||
|
public HttpRequestChannel(HttpChannelFactory<IRequestChannel> factory, EndpointAddress to, Uri via, bool manualAddressing)
|
||
|
: base(factory, to, via, manualAddressing)
|
||
|
{
|
||
|
this.factory = factory;
|
||
|
}
|
||
|
|
||
|
public HttpChannelFactory<IRequestChannel> Factory
|
||
|
{
|
||
|
get { return this.factory; }
|
||
|
}
|
||
|
|
||
|
internal ServiceModelActivity Activity
|
||
|
{
|
||
|
get { return this.activity; }
|
||
|
}
|
||
|
|
||
|
protected ChannelParameterCollection ChannelParameters
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.channelParameters;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override T GetProperty<T>()
|
||
|
{
|
||
|
if (typeof(T) == typeof(ChannelParameterCollection))
|
||
|
{
|
||
|
if (this.State == CommunicationState.Created)
|
||
|
{
|
||
|
lock (ThisLock)
|
||
|
{
|
||
|
if (this.channelParameters == null)
|
||
|
{
|
||
|
this.channelParameters = new ChannelParameterCollection();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return (T)(object)this.channelParameters;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return base.GetProperty<T>();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PrepareOpen()
|
||
|
{
|
||
|
if (Factory.MapIdentity(RemoteAddress))
|
||
|
{
|
||
|
lock (ThisLock)
|
||
|
{
|
||
|
cleanupIdentity = HttpTransportSecurityHelpers.AddIdentityMapping(Via, RemoteAddress);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CreateAndOpenTokenProviders(TimeSpan timeout)
|
||
|
{
|
||
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
||
|
if (!ManualAddressing)
|
||
|
{
|
||
|
Factory.CreateAndOpenTokenProviders(this.RemoteAddress, this.Via, this.channelParameters, timeoutHelper.RemainingTime(), out this.tokenProvider, out this.proxyTokenProvider);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CloseTokenProviders(TimeSpan timeout)
|
||
|
{
|
||
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
||
|
if (this.tokenProvider != null)
|
||
|
{
|
||
|
tokenProvider.Close(timeoutHelper.RemainingTime());
|
||
|
}
|
||
|
if (this.proxyTokenProvider != null)
|
||
|
{
|
||
|
proxyTokenProvider.Close(timeoutHelper.RemainingTime());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void AbortTokenProviders()
|
||
|
{
|
||
|
if (this.tokenProvider != null)
|
||
|
{
|
||
|
tokenProvider.Abort();
|
||
|
}
|
||
|
if (this.proxyTokenProvider != null)
|
||
|
{
|
||
|
proxyTokenProvider.Abort();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
|
||
|
{
|
||
|
PrepareOpen();
|
||
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
||
|
CreateAndOpenTokenProviders(timeoutHelper.RemainingTime());
|
||
|
return new CompletedAsyncResult(callback, state);
|
||
|
}
|
||
|
|
||
|
protected override void OnOpen(TimeSpan timeout)
|
||
|
{
|
||
|
PrepareOpen();
|
||
|
CreateAndOpenTokenProviders(timeout);
|
||
|
}
|
||
|
|
||
|
protected override void OnEndOpen(IAsyncResult result)
|
||
|
{
|
||
|
CompletedAsyncResult.End(result);
|
||
|
}
|
||
|
|
||
|
void PrepareClose(bool aborting)
|
||
|
{
|
||
|
if (cleanupIdentity)
|
||
|
{
|
||
|
lock (ThisLock)
|
||
|
{
|
||
|
if (cleanupIdentity)
|
||
|
{
|
||
|
cleanupIdentity = false;
|
||
|
HttpTransportSecurityHelpers.RemoveIdentityMapping(Via, RemoteAddress, !aborting);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override void OnAbort()
|
||
|
{
|
||
|
PrepareClose(true);
|
||
|
AbortTokenProviders();
|
||
|
base.OnAbort();
|
||
|
}
|
||
|
|
||
|
protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
|
||
|
{
|
||
|
IAsyncResult retval = null;
|
||
|
using (ServiceModelActivity.BoundOperation(this.activity))
|
||
|
{
|
||
|
PrepareClose(false);
|
||
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
||
|
CloseTokenProviders(timeoutHelper.RemainingTime());
|
||
|
retval = base.BeginWaitForPendingRequests(timeoutHelper.RemainingTime(), callback, state);
|
||
|
}
|
||
|
ServiceModelActivity.Stop(this.activity);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
protected override void OnEndClose(IAsyncResult result)
|
||
|
{
|
||
|
using (ServiceModelActivity.BoundOperation(this.activity))
|
||
|
{
|
||
|
base.EndWaitForPendingRequests(result);
|
||
|
}
|
||
|
ServiceModelActivity.Stop(this.activity);
|
||
|
}
|
||
|
|
||
|
protected override void OnClose(TimeSpan timeout)
|
||
|
{
|
||
|
using (ServiceModelActivity.BoundOperation(this.activity))
|
||
|
{
|
||
|
PrepareClose(false);
|
||
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
||
|
CloseTokenProviders(timeoutHelper.RemainingTime());
|
||
|
base.WaitForPendingRequests(timeoutHelper.RemainingTime());
|
||
|
}
|
||
|
ServiceModelActivity.Stop(this.activity);
|
||
|
}
|
||
|
|
||
|
protected override IAsyncRequest CreateAsyncRequest(Message message, AsyncCallback callback, object state)
|
||
|
{
|
||
|
if (DiagnosticUtility.ShouldUseActivity && this.activity == null)
|
||
|
{
|
||
|
this.activity = ServiceModelActivity.CreateActivity();
|
||
|
if (null != FxTrace.Trace)
|
||
|
{
|
||
|
FxTrace.Trace.TraceTransfer(this.activity.Id);
|
||
|
}
|
||
|
ServiceModelActivity.Start(this.activity, SR.GetString(SR.ActivityReceiveBytes, this.RemoteAddress.Uri.ToString()), ActivityType.ReceiveBytes);
|
||
|
}
|
||
|
|
||
|
return new HttpChannelAsyncRequest(this, callback, state);
|
||
|
}
|
||
|
|
||
|
protected override IRequest CreateRequest(Message message)
|
||
|
{
|
||
|
return new HttpChannelRequest(this, Factory);
|
||
|
}
|
||
|
|
||
|
public virtual HttpWebRequest GetWebRequest(EndpointAddress to, Uri via, ref TimeoutHelper timeoutHelper)
|
||
|
{
|
||
|
return GetWebRequest(to, via, null, ref timeoutHelper);
|
||
|
}
|
||
|
|
||
|
protected HttpWebRequest GetWebRequest(EndpointAddress to, Uri via, SecurityTokenContainer clientCertificateToken, ref TimeoutHelper timeoutHelper)
|
||
|
{
|
||
|
SecurityTokenProviderContainer webRequestTokenProvider;
|
||
|
SecurityTokenProviderContainer webRequestProxyTokenProvider;
|
||
|
if (this.ManualAddressing)
|
||
|
{
|
||
|
this.Factory.CreateAndOpenTokenProviders(to, via, this.channelParameters, timeoutHelper.RemainingTime(),
|
||
|
out webRequestTokenProvider, out webRequestProxyTokenProvider);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
webRequestTokenProvider = this.tokenProvider;
|
||
|
webRequestProxyTokenProvider = this.proxyTokenProvider;
|
||
|
}
|
||
|
try
|
||
|
{
|
||
|
return this.Factory.GetWebRequest(to, via, webRequestTokenProvider, webRequestProxyTokenProvider, clientCertificateToken, timeoutHelper.RemainingTime(), false);
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
if (this.ManualAddressing)
|
||
|
{
|
||
|
if (webRequestTokenProvider != null)
|
||
|
{
|
||
|
webRequestTokenProvider.Abort();
|
||
|
}
|
||
|
if (webRequestProxyTokenProvider != null)
|
||
|
{
|
||
|
webRequestProxyTokenProvider.Abort();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected IAsyncResult BeginGetWebRequest(
|
||
|
EndpointAddress to, Uri via, SecurityTokenContainer clientCertificateToken, ref TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
|
||
|
{
|
||
|
return new GetWebRequestAsyncResult(this, to, via, clientCertificateToken, ref timeoutHelper, callback, state);
|
||
|
}
|
||
|
|
||
|
public virtual IAsyncResult BeginGetWebRequest(
|
||
|
EndpointAddress to, Uri via, ref TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
|
||
|
{
|
||
|
return BeginGetWebRequest(to, via, null, ref timeoutHelper, callback, state);
|
||
|
}
|
||
|
|
||
|
public virtual HttpWebRequest EndGetWebRequest(IAsyncResult result)
|
||
|
{
|
||
|
return GetWebRequestAsyncResult.End(result);
|
||
|
}
|
||
|
|
||
|
public virtual bool WillGetWebRequestCompleteSynchronously()
|
||
|
{
|
||
|
return ((this.tokenProvider == null) && !Factory.ManualAddressing);
|
||
|
}
|
||
|
|
||
|
internal virtual void OnWebRequestCompleted(HttpWebRequest request)
|
||
|
{
|
||
|
// empty
|
||
|
}
|
||
|
|
||
|
class HttpChannelRequest : IRequest
|
||
|
{
|
||
|
HttpRequestChannel channel;
|
||
|
HttpChannelFactory<IRequestChannel> factory;
|
||
|
EndpointAddress to;
|
||
|
Uri via;
|
||
|
HttpWebRequest webRequest;
|
||
|
HttpAbortReason abortReason;
|
||
|
ChannelBinding channelBinding;
|
||
|
int webRequestCompleted;
|
||
|
EventTraceActivity eventTraceActivity;
|
||
|
const string ConnectionGroupPrefixMessagePropertyName = "HttpTransportConnectionGroupNamePrefix";
|
||
|
|
||
|
public HttpChannelRequest(HttpRequestChannel channel, HttpChannelFactory<IRequestChannel> factory)
|
||
|
{
|
||
|
this.channel = channel;
|
||
|
this.to = channel.RemoteAddress;
|
||
|
this.via = channel.Via;
|
||
|
this.factory = factory;
|
||
|
}
|
||
|
|
||
|
private string GetConnectionGroupPrefix(Message message)
|
||
|
{
|
||
|
object property;
|
||
|
if (message.Properties.TryGetValue(ConnectionGroupPrefixMessagePropertyName, out property))
|
||
|
{
|
||
|
string prefix = property as string;
|
||
|
if (prefix != null)
|
||
|
{
|
||
|
return prefix;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return string.Empty;
|
||
|
}
|
||
|
|
||
|
public void SendRequest(Message message, TimeSpan timeout)
|
||
|
{
|
||
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
||
|
factory.ApplyManualAddressing(ref this.to, ref this.via, message);
|
||
|
this.webRequest = channel.GetWebRequest(this.to, this.via, ref timeoutHelper);
|
||
|
this.webRequest.ConnectionGroupName = GetConnectionGroupPrefix(message) + this.webRequest.ConnectionGroupName;
|
||
|
|
||
|
Message request = message;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
if (channel.State != CommunicationState.Opened)
|
||
|
{
|
||
|
// if we were aborted while getting our request or doing correlation,
|
||
|
// we need to abort the web request and bail
|
||
|
Cleanup();
|
||
|
channel.ThrowIfDisposedOrNotOpen();
|
||
|
}
|
||
|
|
||
|
HttpChannelUtilities.SetRequestTimeout(this.webRequest, timeoutHelper.RemainingTime());
|
||
|
HttpOutput httpOutput = HttpOutput.CreateHttpOutput(this.webRequest, this.factory, request, this.factory.IsChannelBindingSupportEnabled);
|
||
|
|
||
|
bool success = false;
|
||
|
try
|
||
|
{
|
||
|
|
||
|
httpOutput.Send(timeoutHelper.RemainingTime());
|
||
|
|
||
|
this.channelBinding = httpOutput.TakeChannelBinding();
|
||
|
httpOutput.Close();
|
||
|
success = true;
|
||
|
|
||
|
if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
|
||
|
{
|
||
|
this.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
|
||
|
if (TD.MessageSentByTransportIsEnabled())
|
||
|
{
|
||
|
TD.MessageSentByTransport(eventTraceActivity, this.to.Uri.AbsoluteUri);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
if (!success)
|
||
|
{
|
||
|
httpOutput.Abort(HttpAbortReason.Aborted);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
if (!object.ReferenceEquals(request, message))
|
||
|
{
|
||
|
request.Close();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Cleanup()
|
||
|
{
|
||
|
if (this.webRequest != null)
|
||
|
{
|
||
|
HttpChannelUtilities.AbortRequest(this.webRequest);
|
||
|
this.TryCompleteWebRequest(this.webRequest);
|
||
|
}
|
||
|
|
||
|
ChannelBindingUtility.Dispose(ref this.channelBinding);
|
||
|
}
|
||
|
|
||
|
public void Abort(RequestChannel channel)
|
||
|
{
|
||
|
Cleanup();
|
||
|
abortReason = HttpAbortReason.Aborted;
|
||
|
}
|
||
|
|
||
|
public void Fault(RequestChannel channel)
|
||
|
{
|
||
|
Cleanup();
|
||
|
}
|
||
|
|
||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.ReliabilityBasic, "Reliability104",
|
||
|
Justification = "This is an old method from previous release.")]
|
||
|
public Message WaitForReply(TimeSpan timeout)
|
||
|
{
|
||
|
if (TD.HttpResponseReceiveStartIsEnabled())
|
||
|
{
|
||
|
TD.HttpResponseReceiveStart(this.eventTraceActivity);
|
||
|
}
|
||
|
|
||
|
HttpWebResponse response = null;
|
||
|
WebException responseException = null;
|
||
|
try
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
response = (HttpWebResponse)webRequest.GetResponse();
|
||
|
}
|
||
|
catch (NullReferenceException nullReferenceException)
|
||
|
{
|
||
|
// workaround for Whidbey bug #558605 - only happens in streamed case.
|
||
|
if (TransferModeHelper.IsRequestStreamed(this.factory.transferMode))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
HttpChannelUtilities.CreateNullReferenceResponseException(nullReferenceException));
|
||
|
}
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
if (TD.MessageReceivedByTransportIsEnabled())
|
||
|
{
|
||
|
TD.MessageReceivedByTransport(this.eventTraceActivity ?? EventTraceActivity.Empty,
|
||
|
response.ResponseUri != null ? response.ResponseUri.AbsoluteUri : string.Empty,
|
||
|
EventTraceActivity.GetActivityIdFromThread());
|
||
|
}
|
||
|
|
||
|
if (DiagnosticUtility.ShouldTraceVerbose)
|
||
|
{
|
||
|
HttpChannelFactory<TChannel>.TraceResponseReceived(response, null, this);
|
||
|
}
|
||
|
}
|
||
|
catch (WebException webException)
|
||
|
{
|
||
|
responseException = webException;
|
||
|
response = HttpChannelUtilities.ProcessGetResponseWebException(webException, this.webRequest,
|
||
|
abortReason);
|
||
|
}
|
||
|
|
||
|
HttpInput httpInput = HttpChannelUtilities.ValidateRequestReplyResponse(this.webRequest, response,
|
||
|
this.factory, responseException, this.channelBinding);
|
||
|
this.channelBinding = null;
|
||
|
|
||
|
Message replyMessage = null;
|
||
|
if (httpInput != null)
|
||
|
{
|
||
|
Exception exception = null;
|
||
|
replyMessage = httpInput.ParseIncomingMessage(out exception);
|
||
|
Fx.Assert(exception == null, "ParseIncomingMessage should not set an exception after parsing a response message.");
|
||
|
|
||
|
if (replyMessage != null)
|
||
|
{
|
||
|
HttpChannelUtilities.AddReplySecurityProperty(this.factory, this.webRequest, response,
|
||
|
replyMessage);
|
||
|
|
||
|
if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled && (eventTraceActivity != null))
|
||
|
{
|
||
|
EventTraceActivityHelper.TryAttachActivity(replyMessage, eventTraceActivity);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.TryCompleteWebRequest(this.webRequest);
|
||
|
return replyMessage;
|
||
|
}
|
||
|
|
||
|
public void OnReleaseRequest()
|
||
|
{
|
||
|
this.TryCompleteWebRequest(this.webRequest);
|
||
|
}
|
||
|
|
||
|
void TryCompleteWebRequest(HttpWebRequest request)
|
||
|
{
|
||
|
if (request == null)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (Interlocked.CompareExchange(ref this.webRequestCompleted, 1, 0) == 0)
|
||
|
{
|
||
|
this.channel.OnWebRequestCompleted(request);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class HttpChannelAsyncRequest : TraceAsyncResult, IAsyncRequest
|
||
|
{
|
||
|
static AsyncCallback onProcessIncomingMessage = Fx.ThunkCallback(new AsyncCallback(OnParseIncomingMessage));
|
||
|
static AsyncCallback onGetResponse = Fx.ThunkCallback(new AsyncCallback(OnGetResponse));
|
||
|
static AsyncCallback onGetWebRequestCompleted;
|
||
|
static AsyncCallback onSend = Fx.ThunkCallback(new AsyncCallback(OnSend));
|
||
|
static Action<object> onSendTimeout;
|
||
|
ChannelBinding channelBinding;
|
||
|
|
||
|
HttpChannelFactory<IRequestChannel> factory;
|
||
|
HttpRequestChannel channel;
|
||
|
HttpOutput httpOutput;
|
||
|
HttpInput httpInput;
|
||
|
Message message;
|
||
|
Message requestMessage;
|
||
|
Message replyMessage;
|
||
|
HttpWebResponse response;
|
||
|
HttpWebRequest request;
|
||
|
object sendLock = new object();
|
||
|
IOThreadTimer sendTimer;
|
||
|
TimeoutHelper timeoutHelper;
|
||
|
EndpointAddress to;
|
||
|
Uri via;
|
||
|
HttpAbortReason abortReason;
|
||
|
int webRequestCompleted;
|
||
|
EventTraceActivity eventTraceActivity;
|
||
|
|
||
|
public HttpChannelAsyncRequest(HttpRequestChannel channel, AsyncCallback callback, object state)
|
||
|
: base(callback, state)
|
||
|
{
|
||
|
this.channel = channel;
|
||
|
this.to = channel.RemoteAddress;
|
||
|
this.via = channel.Via;
|
||
|
this.factory = channel.Factory;
|
||
|
}
|
||
|
|
||
|
IOThreadTimer SendTimer
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (this.sendTimer == null)
|
||
|
{
|
||
|
if (onSendTimeout == null)
|
||
|
{
|
||
|
onSendTimeout = new Action<object>(OnSendTimeout);
|
||
|
}
|
||
|
|
||
|
this.sendTimer = new IOThreadTimer(onSendTimeout, this, false);
|
||
|
}
|
||
|
|
||
|
return this.sendTimer;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static void End(IAsyncResult result)
|
||
|
{
|
||
|
AsyncResult.End<HttpChannelAsyncRequest>(result);
|
||
|
}
|
||
|
|
||
|
public void BeginSendRequest(Message message, TimeSpan timeout)
|
||
|
{
|
||
|
this.message = this.requestMessage = message;
|
||
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
||
|
|
||
|
if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
|
||
|
{
|
||
|
this.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
|
||
|
}
|
||
|
|
||
|
factory.ApplyManualAddressing(ref this.to, ref this.via, this.requestMessage);
|
||
|
if (this.channel.WillGetWebRequestCompleteSynchronously())
|
||
|
{
|
||
|
SetWebRequest(channel.GetWebRequest(this.to, this.via, ref this.timeoutHelper));
|
||
|
if (this.SendWebRequest())
|
||
|
{
|
||
|
base.Complete(true);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (onGetWebRequestCompleted == null)
|
||
|
{
|
||
|
onGetWebRequestCompleted = Fx.ThunkCallback(
|
||
|
new AsyncCallback(OnGetWebRequestCompletedCallback));
|
||
|
}
|
||
|
|
||
|
IAsyncResult result = channel.BeginGetWebRequest(
|
||
|
to, via, ref this.timeoutHelper, onGetWebRequestCompleted, this);
|
||
|
|
||
|
if (result.CompletedSynchronously)
|
||
|
{
|
||
|
if (TD.MessageSentByTransportIsEnabled())
|
||
|
{
|
||
|
TD.MessageSentByTransport(this.eventTraceActivity, this.to.Uri.AbsoluteUri);
|
||
|
}
|
||
|
if (this.OnGetWebRequestCompleted(result))
|
||
|
{
|
||
|
base.Complete(true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void OnGetWebRequestCompletedCallback(IAsyncResult result)
|
||
|
{
|
||
|
if (result.CompletedSynchronously)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
HttpChannelAsyncRequest thisPtr = (HttpChannelAsyncRequest)result.AsyncState;
|
||
|
Exception completionException = null;
|
||
|
bool completeSelf;
|
||
|
try
|
||
|
{
|
||
|
completeSelf = thisPtr.OnGetWebRequestCompleted(result);
|
||
|
}
|
||
|
#pragma warning suppress 56500 // [....], transferring exception to another thread
|
||
|
catch (Exception e)
|
||
|
{
|
||
|
if (Fx.IsFatal(e))
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
completeSelf = true;
|
||
|
completionException = e;
|
||
|
}
|
||
|
if (completeSelf)
|
||
|
{
|
||
|
thisPtr.Complete(false, completionException);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void AbortSend()
|
||
|
{
|
||
|
CancelSendTimer();
|
||
|
if (this.request != null)
|
||
|
{
|
||
|
this.TryCompleteWebRequest(this.request);
|
||
|
this.abortReason = HttpAbortReason.TimedOut;
|
||
|
httpOutput.Abort(this.abortReason);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CancelSendTimer()
|
||
|
{
|
||
|
lock (sendLock)
|
||
|
{
|
||
|
if (this.sendTimer != null)
|
||
|
{
|
||
|
this.sendTimer.Cancel();
|
||
|
this.sendTimer = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool OnGetWebRequestCompleted(IAsyncResult result)
|
||
|
{
|
||
|
SetWebRequest(this.channel.EndGetWebRequest(result));
|
||
|
return this.SendWebRequest();
|
||
|
}
|
||
|
|
||
|
bool SendWebRequest()
|
||
|
{
|
||
|
this.httpOutput = HttpOutput.CreateHttpOutput(this.request, this.factory, this.requestMessage, this.factory.IsChannelBindingSupportEnabled);
|
||
|
|
||
|
bool success = false;
|
||
|
try
|
||
|
{
|
||
|
bool result = false;
|
||
|
SetSendTimeout(timeoutHelper.RemainingTime());
|
||
|
IAsyncResult asyncResult = httpOutput.BeginSend(timeoutHelper.RemainingTime(), onSend, this);
|
||
|
success = true;
|
||
|
|
||
|
if (asyncResult.CompletedSynchronously)
|
||
|
{
|
||
|
result = CompleteSend(asyncResult);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
if (!success)
|
||
|
{
|
||
|
this.httpOutput.Abort(HttpAbortReason.Aborted);
|
||
|
|
||
|
if (!object.ReferenceEquals(this.message, this.requestMessage))
|
||
|
{
|
||
|
this.requestMessage.Close();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool CompleteSend(IAsyncResult result)
|
||
|
{
|
||
|
bool success = false;
|
||
|
try
|
||
|
{
|
||
|
httpOutput.EndSend(result);
|
||
|
this.channelBinding = httpOutput.TakeChannelBinding();
|
||
|
httpOutput.Close();
|
||
|
success = true;
|
||
|
if (TD.MessageSentByTransportIsEnabled())
|
||
|
{
|
||
|
TD.MessageSentByTransport(this.eventTraceActivity, this.to.Uri.AbsoluteUri);
|
||
|
}
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
if (!success)
|
||
|
{
|
||
|
httpOutput.Abort(HttpAbortReason.Aborted);
|
||
|
}
|
||
|
|
||
|
if (!object.ReferenceEquals(this.message, this.requestMessage))
|
||
|
{
|
||
|
this.requestMessage.Close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
try
|
||
|
{
|
||
|
IAsyncResult getResponseResult;
|
||
|
try
|
||
|
{
|
||
|
getResponseResult = request.BeginGetResponse(onGetResponse, this);
|
||
|
}
|
||
|
catch (NullReferenceException nullReferenceException)
|
||
|
{
|
||
|
// workaround for Whidbey bug #558605 - only happens in streamed case.
|
||
|
if (TransferModeHelper.IsRequestStreamed(this.factory.transferMode))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
HttpChannelUtilities.CreateNullReferenceResponseException(nullReferenceException));
|
||
|
}
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (getResponseResult.CompletedSynchronously)
|
||
|
{
|
||
|
return CompleteGetResponse(getResponseResult);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
catch (IOException ioException)
|
||
|
{
|
||
|
throw TraceUtility.ThrowHelperError(new CommunicationException(ioException.Message,
|
||
|
ioException), this.requestMessage);
|
||
|
}
|
||
|
catch (WebException webException)
|
||
|
{
|
||
|
throw TraceUtility.ThrowHelperError(new CommunicationException(webException.Message,
|
||
|
webException), this.requestMessage);
|
||
|
}
|
||
|
catch (ObjectDisposedException objectDisposedException)
|
||
|
{
|
||
|
if (abortReason == HttpAbortReason.Aborted)
|
||
|
{
|
||
|
throw TraceUtility.ThrowHelperError(new CommunicationObjectAbortedException(SR.GetString(SR.HttpRequestAborted, to.Uri),
|
||
|
objectDisposedException), this.requestMessage);
|
||
|
}
|
||
|
|
||
|
throw TraceUtility.ThrowHelperError(new TimeoutException(SR.GetString(SR.HttpRequestTimedOut,
|
||
|
to.Uri, this.timeoutHelper.OriginalTimeout), objectDisposedException), this.requestMessage);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.ReliabilityBasic, "Reliability104",
|
||
|
Justification = "This is an old method from previous release.")]
|
||
|
bool CompleteGetResponse(IAsyncResult result)
|
||
|
{
|
||
|
using (ServiceModelActivity.BoundOperation(this.channel.Activity))
|
||
|
{
|
||
|
HttpWebResponse response = null;
|
||
|
WebException responseException = null;
|
||
|
try
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
CancelSendTimer();
|
||
|
response = (HttpWebResponse)request.EndGetResponse(result);
|
||
|
}
|
||
|
catch (NullReferenceException nullReferenceException)
|
||
|
{
|
||
|
// workaround for Whidbey bug #558605 - only happens in streamed case.
|
||
|
if (TransferModeHelper.IsRequestStreamed(this.factory.transferMode))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
HttpChannelUtilities.CreateNullReferenceResponseException(nullReferenceException));
|
||
|
}
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
if (TD.MessageReceivedByTransportIsEnabled())
|
||
|
{
|
||
|
TD.MessageReceivedByTransport(
|
||
|
this.eventTraceActivity ?? EventTraceActivity.Empty,
|
||
|
this.to.Uri.AbsoluteUri,
|
||
|
EventTraceActivity.GetActivityIdFromThread());
|
||
|
}
|
||
|
|
||
|
if (DiagnosticUtility.ShouldTraceVerbose)
|
||
|
{
|
||
|
HttpChannelFactory<TChannel>.TraceResponseReceived(response, this.message, this);
|
||
|
}
|
||
|
}
|
||
|
catch (WebException webException)
|
||
|
{
|
||
|
responseException = webException;
|
||
|
response = HttpChannelUtilities.ProcessGetResponseWebException(webException, request,
|
||
|
abortReason);
|
||
|
}
|
||
|
|
||
|
return ProcessResponse(response, responseException);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Cleanup()
|
||
|
{
|
||
|
if (this.request != null)
|
||
|
{
|
||
|
HttpChannelUtilities.AbortRequest(this.request);
|
||
|
this.TryCompleteWebRequest(this.request);
|
||
|
}
|
||
|
|
||
|
ChannelBindingUtility.Dispose(ref this.channelBinding);
|
||
|
}
|
||
|
|
||
|
void SetSendTimeout(TimeSpan timeout)
|
||
|
{
|
||
|
// We also set the timeout on the HttpWebRequest so that we can subsequently use it in the
|
||
|
// exception message in the event of a timeout.
|
||
|
HttpChannelUtilities.SetRequestTimeout(this.request, timeout);
|
||
|
|
||
|
if (timeout == TimeSpan.MaxValue)
|
||
|
{
|
||
|
CancelSendTimer();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SendTimer.Set(timeout);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Abort(RequestChannel channel)
|
||
|
{
|
||
|
Cleanup();
|
||
|
abortReason = HttpAbortReason.Aborted;
|
||
|
}
|
||
|
|
||
|
public void Fault(RequestChannel channel)
|
||
|
{
|
||
|
Cleanup();
|
||
|
}
|
||
|
|
||
|
void SetWebRequest(HttpWebRequest webRequest)
|
||
|
{
|
||
|
this.request = webRequest;
|
||
|
|
||
|
if (channel.State != CommunicationState.Opened)
|
||
|
{
|
||
|
// if we were aborted while getting our request, we need to abort the web request and bail
|
||
|
Cleanup();
|
||
|
channel.ThrowIfDisposedOrNotOpen();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Message End()
|
||
|
{
|
||
|
HttpChannelAsyncRequest.End(this);
|
||
|
return replyMessage;
|
||
|
}
|
||
|
|
||
|
bool ProcessResponse(HttpWebResponse response, WebException responseException)
|
||
|
{
|
||
|
this.httpInput = HttpChannelUtilities.ValidateRequestReplyResponse(this.request, response,
|
||
|
this.factory, responseException, this.channelBinding);
|
||
|
this.channelBinding = null;
|
||
|
|
||
|
if (httpInput != null)
|
||
|
{
|
||
|
this.response = response;
|
||
|
IAsyncResult result =
|
||
|
httpInput.BeginParseIncomingMessage(onProcessIncomingMessage, this);
|
||
|
if (!result.CompletedSynchronously)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
CompleteParseIncomingMessage(result);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.replyMessage = null;
|
||
|
}
|
||
|
|
||
|
this.TryCompleteWebRequest(this.request);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CompleteParseIncomingMessage(IAsyncResult result)
|
||
|
{
|
||
|
Exception exception = null;
|
||
|
this.replyMessage = this.httpInput.EndParseIncomingMessage(result, out exception);
|
||
|
Fx.Assert(exception == null, "ParseIncomingMessage should not set an exception after parsing a response message.");
|
||
|
|
||
|
if (this.replyMessage != null)
|
||
|
{
|
||
|
HttpChannelUtilities.AddReplySecurityProperty(this.factory, this.request, this.response,
|
||
|
this.replyMessage);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void OnParseIncomingMessage(IAsyncResult result)
|
||
|
{
|
||
|
if (result.CompletedSynchronously)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
HttpChannelAsyncRequest thisPtr = (HttpChannelAsyncRequest)result.AsyncState;
|
||
|
|
||
|
Exception completionException = null;
|
||
|
try
|
||
|
{
|
||
|
thisPtr.CompleteParseIncomingMessage(result);
|
||
|
}
|
||
|
#pragma warning suppress 56500 // [....], transferring exception to another thread
|
||
|
catch (Exception e)
|
||
|
{
|
||
|
if (Fx.IsFatal(e))
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
completionException = e;
|
||
|
}
|
||
|
thisPtr.Complete(false, completionException);
|
||
|
}
|
||
|
|
||
|
static void OnSend(IAsyncResult result)
|
||
|
{
|
||
|
if (result.CompletedSynchronously)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
HttpChannelAsyncRequest thisPtr = (HttpChannelAsyncRequest)result.AsyncState;
|
||
|
|
||
|
Exception completionException = null;
|
||
|
bool completeSelf;
|
||
|
try
|
||
|
{
|
||
|
completeSelf = thisPtr.CompleteSend(result);
|
||
|
}
|
||
|
#pragma warning suppress 56500 // [....], transferring exception to another thread
|
||
|
catch (Exception e)
|
||
|
{
|
||
|
if (Fx.IsFatal(e))
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
completeSelf = true;
|
||
|
completionException = e;
|
||
|
}
|
||
|
if (completeSelf)
|
||
|
{
|
||
|
thisPtr.Complete(false, completionException);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void OnSendTimeout(object state)
|
||
|
{
|
||
|
HttpChannelAsyncRequest thisPtr = (HttpChannelAsyncRequest)state;
|
||
|
thisPtr.AbortSend();
|
||
|
}
|
||
|
|
||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.ReliabilityBasic, "Reliability104",
|
||
|
Justification = "This is an old method from previous release.")]
|
||
|
static void OnGetResponse(IAsyncResult result)
|
||
|
{
|
||
|
if (result.CompletedSynchronously)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
HttpChannelAsyncRequest thisPtr = (HttpChannelAsyncRequest)result.AsyncState;
|
||
|
|
||
|
Exception completionException = null;
|
||
|
bool completeSelf;
|
||
|
try
|
||
|
{
|
||
|
completeSelf = thisPtr.CompleteGetResponse(result);
|
||
|
}
|
||
|
catch (WebException webException)
|
||
|
{
|
||
|
completeSelf = true;
|
||
|
completionException = new CommunicationException(webException.Message, webException);
|
||
|
}
|
||
|
#pragma warning suppress 56500 // [....], transferring exception to another thread
|
||
|
catch (Exception e)
|
||
|
{
|
||
|
if (Fx.IsFatal(e))
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
completeSelf = true;
|
||
|
completionException = e;
|
||
|
}
|
||
|
if (completeSelf)
|
||
|
{
|
||
|
thisPtr.Complete(false, completionException);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void OnReleaseRequest()
|
||
|
{
|
||
|
this.TryCompleteWebRequest(this.request);
|
||
|
}
|
||
|
|
||
|
void TryCompleteWebRequest(HttpWebRequest request)
|
||
|
{
|
||
|
if (request == null)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (Interlocked.CompareExchange(ref this.webRequestCompleted, 1, 0) == 0)
|
||
|
{
|
||
|
this.channel.OnWebRequestCompleted(request);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class GetWebRequestAsyncResult : AsyncResult
|
||
|
{
|
||
|
static AsyncCallback onGetSspiCredential;
|
||
|
static AsyncCallback onGetUserNameCredential;
|
||
|
|
||
|
SecurityTokenContainer clientCertificateToken;
|
||
|
HttpChannelFactory<IRequestChannel> factory;
|
||
|
SecurityTokenProviderContainer proxyTokenProvider;
|
||
|
HttpWebRequest request;
|
||
|
EndpointAddress to;
|
||
|
TimeoutHelper timeoutHelper;
|
||
|
SecurityTokenProviderContainer tokenProvider;
|
||
|
Uri via;
|
||
|
|
||
|
public GetWebRequestAsyncResult(HttpRequestChannel channel,
|
||
|
EndpointAddress to, Uri via, SecurityTokenContainer clientCertificateToken, ref TimeoutHelper timeoutHelper,
|
||
|
AsyncCallback callback, object state)
|
||
|
: base(callback, state)
|
||
|
{
|
||
|
this.to = to;
|
||
|
this.via = via;
|
||
|
this.clientCertificateToken = clientCertificateToken;
|
||
|
this.timeoutHelper = timeoutHelper;
|
||
|
this.factory = channel.Factory;
|
||
|
this.tokenProvider = channel.tokenProvider;
|
||
|
this.proxyTokenProvider = channel.proxyTokenProvider;
|
||
|
if (factory.ManualAddressing)
|
||
|
{
|
||
|
this.factory.CreateAndOpenTokenProviders(to, via, channel.channelParameters, timeoutHelper.RemainingTime(),
|
||
|
out this.tokenProvider, out this.proxyTokenProvider);
|
||
|
}
|
||
|
|
||
|
bool completeSelf = false;
|
||
|
IAsyncResult result = null;
|
||
|
if (factory.AuthenticationScheme == AuthenticationSchemes.Anonymous)
|
||
|
{
|
||
|
SetupWebRequest(AuthenticationLevel.None, TokenImpersonationLevel.None, null);
|
||
|
completeSelf = true;
|
||
|
}
|
||
|
else if (factory.AuthenticationScheme == AuthenticationSchemes.Basic)
|
||
|
{
|
||
|
if (onGetUserNameCredential == null)
|
||
|
{
|
||
|
onGetUserNameCredential = Fx.ThunkCallback(new AsyncCallback(OnGetUserNameCredential));
|
||
|
}
|
||
|
|
||
|
result = TransportSecurityHelpers.BeginGetUserNameCredential(
|
||
|
tokenProvider, timeoutHelper.RemainingTime(), onGetUserNameCredential, this);
|
||
|
|
||
|
if (result.CompletedSynchronously)
|
||
|
{
|
||
|
CompleteGetUserNameCredential(result);
|
||
|
completeSelf = true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (onGetSspiCredential == null)
|
||
|
{
|
||
|
onGetSspiCredential = Fx.ThunkCallback(new AsyncCallback(OnGetSspiCredential));
|
||
|
}
|
||
|
|
||
|
result = TransportSecurityHelpers.BeginGetSspiCredential(
|
||
|
tokenProvider, timeoutHelper.RemainingTime(), onGetSspiCredential, this);
|
||
|
|
||
|
if (result.CompletedSynchronously)
|
||
|
{
|
||
|
CompleteGetSspiCredential(result);
|
||
|
completeSelf = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (completeSelf)
|
||
|
{
|
||
|
CloseTokenProvidersIfRequired();
|
||
|
base.Complete(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static HttpWebRequest End(IAsyncResult result)
|
||
|
{
|
||
|
GetWebRequestAsyncResult thisPtr = AsyncResult.End<GetWebRequestAsyncResult>(result);
|
||
|
return thisPtr.request;
|
||
|
}
|
||
|
|
||
|
void CompleteGetUserNameCredential(IAsyncResult result)
|
||
|
{
|
||
|
NetworkCredential credential =
|
||
|
TransportSecurityHelpers.EndGetUserNameCredential(result);
|
||
|
SetupWebRequest(AuthenticationLevel.None, TokenImpersonationLevel.None, credential);
|
||
|
}
|
||
|
|
||
|
void CompleteGetSspiCredential(IAsyncResult result)
|
||
|
{
|
||
|
AuthenticationLevel authenticationLevel;
|
||
|
TokenImpersonationLevel impersonationLevel;
|
||
|
NetworkCredential credential =
|
||
|
TransportSecurityHelpers.EndGetSspiCredential(result, out impersonationLevel, out authenticationLevel);
|
||
|
|
||
|
if (factory.AuthenticationScheme == AuthenticationSchemes.Digest)
|
||
|
{
|
||
|
HttpChannelUtilities.ValidateDigestCredential(ref credential, impersonationLevel);
|
||
|
}
|
||
|
else if (factory.AuthenticationScheme == AuthenticationSchemes.Ntlm)
|
||
|
{
|
||
|
if (authenticationLevel == AuthenticationLevel.MutualAuthRequired)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
|
||
|
SR.GetString(SR.CredentialDisallowsNtlm)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SetupWebRequest(authenticationLevel, impersonationLevel, credential);
|
||
|
}
|
||
|
|
||
|
void SetupWebRequest(AuthenticationLevel authenticationLevel, TokenImpersonationLevel impersonationLevel, NetworkCredential credential)
|
||
|
{
|
||
|
this.request = factory.GetWebRequest(to, via, credential, impersonationLevel,
|
||
|
authenticationLevel, this.proxyTokenProvider, this.clientCertificateToken, timeoutHelper.RemainingTime(), false);
|
||
|
}
|
||
|
|
||
|
void CloseTokenProvidersIfRequired()
|
||
|
{
|
||
|
if (this.factory.ManualAddressing)
|
||
|
{
|
||
|
if (this.tokenProvider != null)
|
||
|
{
|
||
|
tokenProvider.Abort();
|
||
|
}
|
||
|
if (this.proxyTokenProvider != null)
|
||
|
{
|
||
|
proxyTokenProvider.Abort();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void OnGetSspiCredential(IAsyncResult result)
|
||
|
{
|
||
|
if (result.CompletedSynchronously)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
GetWebRequestAsyncResult thisPtr = (GetWebRequestAsyncResult)result.AsyncState;
|
||
|
|
||
|
Exception completionException = null;
|
||
|
try
|
||
|
{
|
||
|
thisPtr.CompleteGetSspiCredential(result);
|
||
|
thisPtr.CloseTokenProvidersIfRequired();
|
||
|
}
|
||
|
#pragma warning suppress 56500 // [....], transferring exception to another thread
|
||
|
catch (Exception e)
|
||
|
{
|
||
|
if (Fx.IsFatal(e))
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
completionException = e;
|
||
|
}
|
||
|
thisPtr.Complete(false, completionException);
|
||
|
}
|
||
|
|
||
|
static void OnGetUserNameCredential(IAsyncResult result)
|
||
|
{
|
||
|
if (result.CompletedSynchronously)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
GetWebRequestAsyncResult thisPtr = (GetWebRequestAsyncResult)result.AsyncState;
|
||
|
|
||
|
Exception completionException = null;
|
||
|
try
|
||
|
{
|
||
|
thisPtr.CompleteGetUserNameCredential(result);
|
||
|
thisPtr.CloseTokenProvidersIfRequired();
|
||
|
}
|
||
|
#pragma warning suppress 56500 // [....], transferring exception to another thread
|
||
|
catch (Exception e)
|
||
|
{
|
||
|
if (Fx.IsFatal(e))
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
completionException = e;
|
||
|
}
|
||
|
thisPtr.Complete(false, completionException);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class WebProxyFactory
|
||
|
{
|
||
|
Uri address;
|
||
|
bool bypassOnLocal;
|
||
|
AuthenticationSchemes authenticationScheme;
|
||
|
|
||
|
public WebProxyFactory(Uri address, bool bypassOnLocal, AuthenticationSchemes authenticationScheme)
|
||
|
{
|
||
|
this.address = address;
|
||
|
this.bypassOnLocal = bypassOnLocal;
|
||
|
|
||
|
if (!authenticationScheme.IsSingleton())
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.GetString(SR.HttpRequiresSingleAuthScheme,
|
||
|
authenticationScheme));
|
||
|
}
|
||
|
|
||
|
this.authenticationScheme = authenticationScheme;
|
||
|
}
|
||
|
|
||
|
internal AuthenticationSchemes AuthenticationScheme
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return authenticationScheme;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public IWebProxy CreateWebProxy(HttpWebRequest request, SecurityTokenProviderContainer tokenProvider, TimeSpan timeout)
|
||
|
{
|
||
|
WebProxy result = new WebProxy(this.address, this.bypassOnLocal);
|
||
|
|
||
|
if (this.authenticationScheme != AuthenticationSchemes.Anonymous)
|
||
|
{
|
||
|
TokenImpersonationLevel impersonationLevel;
|
||
|
AuthenticationLevel authenticationLevel;
|
||
|
NetworkCredential credential = HttpChannelUtilities.GetCredential(this.authenticationScheme,
|
||
|
tokenProvider, timeout, out impersonationLevel, out authenticationLevel);
|
||
|
|
||
|
// The impersonation level for target auth is also used for proxy auth (by System.Net). Therefore,
|
||
|
// fail if the level stipulated for proxy auth is more restrictive than that for target auth.
|
||
|
if (!TokenImpersonationLevelHelper.IsGreaterOrEqual(impersonationLevel, request.ImpersonationLevel))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
|
||
|
SR.ProxyImpersonationLevelMismatch, impersonationLevel, request.ImpersonationLevel)));
|
||
|
}
|
||
|
|
||
|
// The authentication level for target auth is also used for proxy auth (by System.Net).
|
||
|
// Therefore, fail if proxy auth requires mutual authentication but target auth does not.
|
||
|
if ((authenticationLevel == AuthenticationLevel.MutualAuthRequired) &&
|
||
|
(request.AuthenticationLevel != AuthenticationLevel.MutualAuthRequired))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
|
||
|
SR.ProxyAuthenticationLevelMismatch, authenticationLevel, request.AuthenticationLevel)));
|
||
|
}
|
||
|
|
||
|
CredentialCache credentials = new CredentialCache();
|
||
|
credentials.Add(this.address, AuthenticationSchemesHelper.ToString(this.authenticationScheme),
|
||
|
credential);
|
||
|
result.Credentials = credentials;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|