495 lines
17 KiB
C#
495 lines
17 KiB
C#
|
//------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//------------------------------------------------------------
|
||
|
|
||
|
namespace System.ServiceModel.Channels
|
||
|
{
|
||
|
using System.Collections.ObjectModel;
|
||
|
using System.Runtime;
|
||
|
using System.ServiceModel;
|
||
|
using System.ServiceModel.Description;
|
||
|
using System.ServiceModel.Diagnostics;
|
||
|
using System.Threading;
|
||
|
|
||
|
abstract class ConnectionOrientedTransportChannelFactory<TChannel> : TransportChannelFactory<TChannel>, IConnectionOrientedTransportChannelFactorySettings
|
||
|
{
|
||
|
int connectionBufferSize;
|
||
|
IConnectionInitiator connectionInitiator;
|
||
|
ConnectionPool connectionPool;
|
||
|
string connectionPoolGroupName;
|
||
|
bool exposeConnectionProperty;
|
||
|
TimeSpan idleTimeout;
|
||
|
int maxBufferSize;
|
||
|
int maxOutboundConnectionsPerEndpoint;
|
||
|
TimeSpan maxOutputDelay;
|
||
|
TransferMode transferMode;
|
||
|
ISecurityCapabilities securityCapabilities;
|
||
|
StreamUpgradeProvider upgrade;
|
||
|
bool flowIdentity;
|
||
|
|
||
|
internal ConnectionOrientedTransportChannelFactory(
|
||
|
ConnectionOrientedTransportBindingElement bindingElement, BindingContext context,
|
||
|
string connectionPoolGroupName, TimeSpan idleTimeout, int maxOutboundConnectionsPerEndpoint, bool supportsImpersonationDuringAsyncOpen)
|
||
|
: base(bindingElement, context)
|
||
|
{
|
||
|
if (bindingElement.TransferMode == TransferMode.Buffered && bindingElement.MaxReceivedMessageSize > int.MaxValue)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new ArgumentOutOfRangeException("bindingElement.MaxReceivedMessageSize",
|
||
|
SR.GetString(SR.MaxReceivedMessageSizeMustBeInIntegerRange)));
|
||
|
}
|
||
|
|
||
|
this.connectionBufferSize = bindingElement.ConnectionBufferSize;
|
||
|
this.connectionPoolGroupName = connectionPoolGroupName;
|
||
|
this.exposeConnectionProperty = bindingElement.ExposeConnectionProperty;
|
||
|
this.idleTimeout = idleTimeout;
|
||
|
this.maxBufferSize = bindingElement.MaxBufferSize;
|
||
|
this.maxOutboundConnectionsPerEndpoint = maxOutboundConnectionsPerEndpoint;
|
||
|
this.maxOutputDelay = bindingElement.MaxOutputDelay;
|
||
|
this.transferMode = bindingElement.TransferMode;
|
||
|
|
||
|
Collection<StreamUpgradeBindingElement> upgradeBindingElements =
|
||
|
context.BindingParameters.FindAll<StreamUpgradeBindingElement>();
|
||
|
|
||
|
if (upgradeBindingElements.Count > 1)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MultipleStreamUpgradeProvidersInParameters)));
|
||
|
}
|
||
|
else if ((upgradeBindingElements.Count == 1) && this.SupportsUpgrade(upgradeBindingElements[0]))
|
||
|
{
|
||
|
this.upgrade = upgradeBindingElements[0].BuildClientStreamUpgradeProvider(context);
|
||
|
context.BindingParameters.Remove<StreamUpgradeBindingElement>();
|
||
|
this.securityCapabilities = upgradeBindingElements[0].GetProperty<ISecurityCapabilities>(context);
|
||
|
// flow the identity only if the channel factory supports impersonating during an async open AND
|
||
|
// there is the binding is configured with security
|
||
|
this.flowIdentity = supportsImpersonationDuringAsyncOpen;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int ConnectionBufferSize
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.connectionBufferSize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal IConnectionInitiator ConnectionInitiator
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (this.connectionInitiator == null)
|
||
|
{
|
||
|
lock (ThisLock)
|
||
|
{
|
||
|
if (this.connectionInitiator == null)
|
||
|
{
|
||
|
this.connectionInitiator = GetConnectionInitiator();
|
||
|
if (DiagnosticUtility.ShouldUseActivity)
|
||
|
{
|
||
|
this.connectionInitiator = new TracingConnectionInitiator(this.connectionInitiator,
|
||
|
ServiceModelActivity.Current != null && ServiceModelActivity.Current.ActivityType == ActivityType.OpenClient);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return this.connectionInitiator;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public string ConnectionPoolGroupName
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return connectionPoolGroupName;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public TimeSpan IdleTimeout
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.idleTimeout;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int MaxBufferSize
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return maxBufferSize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int MaxOutboundConnectionsPerEndpoint
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return maxOutboundConnectionsPerEndpoint;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public TimeSpan MaxOutputDelay
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return maxOutputDelay;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public StreamUpgradeProvider Upgrade
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
StreamUpgradeProvider localUpgrade = this.upgrade;
|
||
|
ThrowIfDisposed();
|
||
|
return localUpgrade;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public TransferMode TransferMode
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return transferMode;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int IConnectionOrientedTransportFactorySettings.MaxBufferSize
|
||
|
{
|
||
|
get { return MaxBufferSize; }
|
||
|
}
|
||
|
|
||
|
TransferMode IConnectionOrientedTransportFactorySettings.TransferMode
|
||
|
{
|
||
|
get { return TransferMode; }
|
||
|
}
|
||
|
|
||
|
StreamUpgradeProvider IConnectionOrientedTransportFactorySettings.Upgrade
|
||
|
{
|
||
|
get { return Upgrade; }
|
||
|
}
|
||
|
|
||
|
ServiceSecurityAuditBehavior IConnectionOrientedTransportFactorySettings.AuditBehavior
|
||
|
{
|
||
|
#pragma warning suppress 56503 // Internal method.
|
||
|
get { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SecurityAuditNotSupportedOnChannelFactory))); }
|
||
|
}
|
||
|
|
||
|
public override T GetProperty<T>()
|
||
|
{
|
||
|
if (typeof(T) == typeof(ISecurityCapabilities))
|
||
|
{
|
||
|
return (T)(object)this.securityCapabilities;
|
||
|
}
|
||
|
|
||
|
T result = base.GetProperty<T>();
|
||
|
if (result == null && this.upgrade != null)
|
||
|
{
|
||
|
result = this.upgrade.GetProperty<T>();
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal override int GetMaxBufferSize()
|
||
|
{
|
||
|
return this.MaxBufferSize;
|
||
|
}
|
||
|
|
||
|
internal abstract IConnectionInitiator GetConnectionInitiator();
|
||
|
|
||
|
internal abstract ConnectionPool GetConnectionPool();
|
||
|
|
||
|
internal abstract void ReleaseConnectionPool(ConnectionPool pool, TimeSpan timeout);
|
||
|
|
||
|
protected override TChannel OnCreateChannel(EndpointAddress address, Uri via)
|
||
|
{
|
||
|
base.ValidateScheme(via);
|
||
|
|
||
|
if (TransferMode == TransferMode.Buffered)
|
||
|
{
|
||
|
// typeof(TChannel) == typeof(IDuplexSessionChannel)
|
||
|
return (TChannel)(object)new ClientFramingDuplexSessionChannel(this, this, address, via,
|
||
|
ConnectionInitiator, connectionPool, exposeConnectionProperty, this.flowIdentity);
|
||
|
}
|
||
|
|
||
|
// typeof(TChannel) == typeof(IRequestChannel)
|
||
|
return (TChannel)(object)new StreamedFramingRequestChannel(this, this, address, via,
|
||
|
ConnectionInitiator, connectionPool);
|
||
|
}
|
||
|
|
||
|
bool GetUpgradeAndConnectionPool(out StreamUpgradeProvider upgradeCopy, out ConnectionPool poolCopy)
|
||
|
{
|
||
|
if (this.upgrade != null || this.connectionPool != null)
|
||
|
{
|
||
|
lock (ThisLock)
|
||
|
{
|
||
|
if (this.upgrade != null || this.connectionPool != null)
|
||
|
{
|
||
|
upgradeCopy = this.upgrade;
|
||
|
poolCopy = this.connectionPool;
|
||
|
this.upgrade = null;
|
||
|
this.connectionPool = null;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
upgradeCopy = null;
|
||
|
poolCopy = null;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
protected override void OnAbort()
|
||
|
{
|
||
|
StreamUpgradeProvider localUpgrade;
|
||
|
ConnectionPool localConnectionPool;
|
||
|
if (GetUpgradeAndConnectionPool(out localUpgrade, out localConnectionPool))
|
||
|
{
|
||
|
if (localConnectionPool != null)
|
||
|
{
|
||
|
ReleaseConnectionPool(localConnectionPool, TimeSpan.Zero);
|
||
|
}
|
||
|
|
||
|
if (localUpgrade != null)
|
||
|
{
|
||
|
localUpgrade.Abort();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
|
||
|
{
|
||
|
return new CloseAsyncResult(this, timeout, callback, state);
|
||
|
}
|
||
|
|
||
|
protected override void OnEndClose(IAsyncResult result)
|
||
|
{
|
||
|
CloseAsyncResult.End(result);
|
||
|
}
|
||
|
|
||
|
protected override void OnClose(TimeSpan timeout)
|
||
|
{
|
||
|
StreamUpgradeProvider localUpgrade;
|
||
|
ConnectionPool localConnectionPool;
|
||
|
|
||
|
if (GetUpgradeAndConnectionPool(out localUpgrade, out localConnectionPool))
|
||
|
{
|
||
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
||
|
|
||
|
if (localConnectionPool != null)
|
||
|
{
|
||
|
ReleaseConnectionPool(localConnectionPool, timeoutHelper.RemainingTime());
|
||
|
}
|
||
|
|
||
|
if (localUpgrade != null)
|
||
|
{
|
||
|
localUpgrade.Close(timeoutHelper.RemainingTime());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override void OnOpening()
|
||
|
{
|
||
|
base.OnOpening();
|
||
|
this.connectionPool = GetConnectionPool(); // returns an already opened pool
|
||
|
Fx.Assert(this.connectionPool != null, "ConnectionPool should always be found");
|
||
|
}
|
||
|
|
||
|
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
|
||
|
{
|
||
|
return new OpenAsyncResult(this.Upgrade, timeout, callback, state);
|
||
|
}
|
||
|
|
||
|
protected override void OnEndOpen(IAsyncResult result)
|
||
|
{
|
||
|
OpenAsyncResult.End(result);
|
||
|
}
|
||
|
|
||
|
class OpenAsyncResult : AsyncResult
|
||
|
{
|
||
|
ICommunicationObject communicationObject;
|
||
|
static AsyncCallback onOpenComplete = Fx.ThunkCallback(new AsyncCallback(OnOpenComplete));
|
||
|
|
||
|
public OpenAsyncResult(ICommunicationObject communicationObject, TimeSpan timeout, AsyncCallback callback, object state)
|
||
|
: base(callback, state)
|
||
|
{
|
||
|
this.communicationObject = communicationObject;
|
||
|
|
||
|
if (this.communicationObject == null)
|
||
|
{
|
||
|
this.Complete(true);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
IAsyncResult result = this.communicationObject.BeginOpen(timeout, onOpenComplete, this);
|
||
|
if (result.CompletedSynchronously)
|
||
|
{
|
||
|
this.communicationObject.EndOpen(result);
|
||
|
this.Complete(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void OnOpenComplete(IAsyncResult result)
|
||
|
{
|
||
|
if (result.CompletedSynchronously)
|
||
|
return;
|
||
|
|
||
|
OpenAsyncResult thisPtr = (OpenAsyncResult)result.AsyncState;
|
||
|
Exception exception = null;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
thisPtr.communicationObject.EndOpen(result);
|
||
|
}
|
||
|
catch (Exception e)
|
||
|
{
|
||
|
if (Fx.IsFatal(e))
|
||
|
throw;
|
||
|
|
||
|
exception = e;
|
||
|
}
|
||
|
|
||
|
thisPtr.Complete(false, exception);
|
||
|
}
|
||
|
|
||
|
public static void End(IAsyncResult result)
|
||
|
{
|
||
|
AsyncResult.End<OpenAsyncResult>(result);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override void OnOpen(TimeSpan timeout)
|
||
|
{
|
||
|
StreamUpgradeProvider localUpgrade = this.Upgrade;
|
||
|
if (localUpgrade != null)
|
||
|
{
|
||
|
localUpgrade.Open(timeout);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected virtual bool SupportsUpgrade(StreamUpgradeBindingElement upgradeBindingElement)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
class CloseAsyncResult : AsyncResult
|
||
|
{
|
||
|
ConnectionOrientedTransportChannelFactory<TChannel> parent;
|
||
|
ConnectionPool connectionPool;
|
||
|
StreamUpgradeProvider upgradeProvider;
|
||
|
TimeoutHelper timeoutHelper;
|
||
|
static AsyncCallback onCloseComplete = Fx.ThunkCallback(new AsyncCallback(OnCloseComplete));
|
||
|
static Action<object> onReleaseConnectionPoolScheduled;
|
||
|
|
||
|
public CloseAsyncResult(ConnectionOrientedTransportChannelFactory<TChannel> parent, TimeSpan timeout,
|
||
|
AsyncCallback callback, object state)
|
||
|
: base(callback, state)
|
||
|
{
|
||
|
this.parent = parent;
|
||
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
||
|
|
||
|
this.parent.GetUpgradeAndConnectionPool(out this.upgradeProvider, out this.connectionPool);
|
||
|
|
||
|
if (this.connectionPool == null)
|
||
|
{
|
||
|
if (this.HandleReleaseConnectionPoolComplete())
|
||
|
{
|
||
|
this.Complete(true);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (onReleaseConnectionPoolScheduled == null)
|
||
|
{
|
||
|
onReleaseConnectionPoolScheduled = new Action<object>(OnReleaseConnectionPoolScheduled);
|
||
|
}
|
||
|
ActionItem.Schedule(onReleaseConnectionPoolScheduled, this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool HandleReleaseConnectionPoolComplete()
|
||
|
{
|
||
|
if (this.upgradeProvider == null)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IAsyncResult result = this.upgradeProvider.BeginClose(this.timeoutHelper.RemainingTime(),
|
||
|
onCloseComplete, this);
|
||
|
|
||
|
if (result.CompletedSynchronously)
|
||
|
{
|
||
|
this.upgradeProvider.EndClose(result);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool OnReleaseConnectionPoolScheduled()
|
||
|
{
|
||
|
this.parent.ReleaseConnectionPool(this.connectionPool, this.timeoutHelper.RemainingTime());
|
||
|
return this.HandleReleaseConnectionPoolComplete();
|
||
|
}
|
||
|
|
||
|
static void OnReleaseConnectionPoolScheduled(object state)
|
||
|
{
|
||
|
CloseAsyncResult thisPtr = (CloseAsyncResult)state;
|
||
|
bool completeSelf;
|
||
|
Exception completionException = null;
|
||
|
try
|
||
|
{
|
||
|
completeSelf = thisPtr.OnReleaseConnectionPoolScheduled();
|
||
|
}
|
||
|
catch (Exception e)
|
||
|
{
|
||
|
if (Fx.IsFatal(e))
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
completeSelf = true;
|
||
|
completionException = e;
|
||
|
}
|
||
|
|
||
|
if (completeSelf)
|
||
|
{
|
||
|
thisPtr.Complete(false, completionException);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void OnCloseComplete(IAsyncResult result)
|
||
|
{
|
||
|
if (result.CompletedSynchronously)
|
||
|
return;
|
||
|
|
||
|
CloseAsyncResult thisPtr = (CloseAsyncResult)result.AsyncState;
|
||
|
Exception exception = null;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
thisPtr.upgradeProvider.EndClose(result);
|
||
|
}
|
||
|
catch (Exception e)
|
||
|
{
|
||
|
if (Fx.IsFatal(e))
|
||
|
throw;
|
||
|
|
||
|
exception = e;
|
||
|
}
|
||
|
|
||
|
thisPtr.Complete(false, exception);
|
||
|
}
|
||
|
|
||
|
public static void End(IAsyncResult result)
|
||
|
{
|
||
|
AsyncResult.End<CloseAsyncResult>(result);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|