//------------------------------------------------------------ // 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 : TransportChannelFactory, 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 upgradeBindingElements = context.BindingParameters.FindAll(); 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(); this.securityCapabilities = upgradeBindingElements[0].GetProperty(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() { if (typeof(T) == typeof(ISecurityCapabilities)) { return (T)(object)this.securityCapabilities; } T result = base.GetProperty(); if (result == null && this.upgrade != null) { result = this.upgrade.GetProperty(); } 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(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 parent; ConnectionPool connectionPool; StreamUpgradeProvider upgradeProvider; TimeoutHelper timeoutHelper; static AsyncCallback onCloseComplete = Fx.ThunkCallback(new AsyncCallback(OnCloseComplete)); static Action onReleaseConnectionPoolScheduled; public CloseAsyncResult(ConnectionOrientedTransportChannelFactory 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(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(result); } } } }