//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.ServiceModel.Channels { using System.Collections.Generic; using System.Diagnostics; using System.Runtime; using System.Runtime.Remoting; using System.Security; using System.ServiceModel; using System.ServiceModel.Description; using System.ServiceModel.Diagnostics; using System.ServiceModel.Dispatcher; abstract class ServiceChannelFactory : ChannelFactoryBase { string bindingName; List channelsList; ClientRuntime clientRuntime; RequestReplyCorrelator requestReplyCorrelator = new RequestReplyCorrelator(); IDefaultCommunicationTimeouts timeouts; MessageVersion messageVersion; public ServiceChannelFactory(ClientRuntime clientRuntime, Binding binding) : base() { if (clientRuntime == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("clientRuntime"); } this.bindingName = binding.Name; this.channelsList = new List(); this.clientRuntime = clientRuntime; this.timeouts = new DefaultCommunicationTimeouts(binding); this.messageVersion = binding.MessageVersion; } public ClientRuntime ClientRuntime { get { this.ThrowIfDisposed(); return this.clientRuntime; } } internal RequestReplyCorrelator RequestReplyCorrelator { get { ThrowIfDisposed(); return this.requestReplyCorrelator; } } protected override TimeSpan DefaultCloseTimeout { get { return this.timeouts.CloseTimeout; } } protected override TimeSpan DefaultReceiveTimeout { get { return this.timeouts.ReceiveTimeout; } } protected override TimeSpan DefaultOpenTimeout { get { return this.timeouts.OpenTimeout; } } protected override TimeSpan DefaultSendTimeout { get { return this.timeouts.SendTimeout; } } public MessageVersion MessageVersion { get { return this.messageVersion; } } // special overload for security only public static ServiceChannelFactory BuildChannelFactory(ChannelBuilder channelBuilder, ClientRuntime clientRuntime) { if (channelBuilder.CanBuildChannelFactory()) { return new ServiceChannelFactoryOverDuplex(channelBuilder.BuildChannelFactory(), clientRuntime, channelBuilder.Binding); } else if (channelBuilder.CanBuildChannelFactory()) { return new ServiceChannelFactoryOverDuplexSession(channelBuilder.BuildChannelFactory(), clientRuntime, channelBuilder.Binding, false); } else { return new ServiceChannelFactoryOverRequestSession(channelBuilder.BuildChannelFactory(), clientRuntime, channelBuilder.Binding, false); } } public static ServiceChannelFactory BuildChannelFactory(ServiceEndpoint serviceEndpoint) { return BuildChannelFactory(serviceEndpoint, false); } public static ServiceChannelFactory BuildChannelFactory(ServiceEndpoint serviceEndpoint, bool useActiveAutoClose) { if (serviceEndpoint == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serviceEndpoint"); } serviceEndpoint.EnsureInvariants(); serviceEndpoint.ValidateForClient(); ChannelRequirements requirements; ContractDescription contractDescription = serviceEndpoint.Contract; ChannelRequirements.ComputeContractRequirements(contractDescription, out requirements); BindingParameterCollection parameters; ClientRuntime clientRuntime = DispatcherBuilder.BuildProxyBehavior(serviceEndpoint, out parameters); Binding binding = serviceEndpoint.Binding; Type[] requiredChannels = ChannelRequirements.ComputeRequiredChannels(ref requirements); CustomBinding customBinding = new CustomBinding(binding); BindingContext context = new BindingContext(customBinding, parameters); InternalDuplexBindingElement internalDuplexBindingElement = null; InternalDuplexBindingElement.AddDuplexFactorySupport(context, ref internalDuplexBindingElement); customBinding = new CustomBinding(context.RemainingBindingElements); customBinding.CopyTimeouts(serviceEndpoint.Binding); foreach (Type type in requiredChannels) { if (type == typeof(IOutputChannel) && customBinding.CanBuildChannelFactory(parameters)) { return new ServiceChannelFactoryOverOutput(customBinding.BuildChannelFactory(parameters), clientRuntime, binding); } if (type == typeof(IRequestChannel) && customBinding.CanBuildChannelFactory(parameters)) { return new ServiceChannelFactoryOverRequest(customBinding.BuildChannelFactory(parameters), clientRuntime, binding); } if (type == typeof(IDuplexChannel) && customBinding.CanBuildChannelFactory(parameters)) { if (requirements.usesReply && binding.CreateBindingElements().Find().ManualAddressing) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( SR.GetString(SR.CantCreateChannelWithManualAddressing))); } return new ServiceChannelFactoryOverDuplex(customBinding.BuildChannelFactory(parameters), clientRuntime, binding); } if (type == typeof(IOutputSessionChannel) && customBinding.CanBuildChannelFactory(parameters)) { return new ServiceChannelFactoryOverOutputSession(customBinding.BuildChannelFactory(parameters), clientRuntime, binding, false); } if (type == typeof(IRequestSessionChannel) && customBinding.CanBuildChannelFactory(parameters)) { return new ServiceChannelFactoryOverRequestSession(customBinding.BuildChannelFactory(parameters), clientRuntime, binding, false); } if (type == typeof(IDuplexSessionChannel) && customBinding.CanBuildChannelFactory(parameters)) { if (requirements.usesReply && binding.CreateBindingElements().Find().ManualAddressing) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( SR.GetString(SR.CantCreateChannelWithManualAddressing))); } return new ServiceChannelFactoryOverDuplexSession(customBinding.BuildChannelFactory(parameters), clientRuntime, binding, useActiveAutoClose); } } foreach (Type type in requiredChannels) { // For SessionMode.Allowed or SessionMode.NotAllowed we will accept session-ful variants as well if (type == typeof(IOutputChannel) && customBinding.CanBuildChannelFactory(parameters)) { return new ServiceChannelFactoryOverOutputSession(customBinding.BuildChannelFactory(parameters), clientRuntime, binding, true); } if (type == typeof(IRequestChannel) && customBinding.CanBuildChannelFactory(parameters)) { return new ServiceChannelFactoryOverRequestSession(customBinding.BuildChannelFactory(parameters), clientRuntime, binding, true); } // and for SessionMode.Required, it is possible that the InstanceContextProvider is handling the session management, so // accept datagram variants if that is the case if (type == typeof(IRequestSessionChannel) && customBinding.CanBuildChannelFactory(parameters) && customBinding.GetProperty(parameters) != null) { return new ServiceChannelFactoryOverRequest(customBinding.BuildChannelFactory(parameters), clientRuntime, binding); } } // we put a lot of work into creating a good error message, as this is a common case Dictionary supportedChannels = new Dictionary(); if (customBinding.CanBuildChannelFactory(parameters)) { supportedChannels.Add(typeof(IOutputChannel), 0); } if (customBinding.CanBuildChannelFactory(parameters)) { supportedChannels.Add(typeof(IRequestChannel), 0); } if (customBinding.CanBuildChannelFactory(parameters)) { supportedChannels.Add(typeof(IDuplexChannel), 0); } if (customBinding.CanBuildChannelFactory(parameters)) { supportedChannels.Add(typeof(IOutputSessionChannel), 0); } if (customBinding.CanBuildChannelFactory(parameters)) { supportedChannels.Add(typeof(IRequestSessionChannel), 0); } if (customBinding.CanBuildChannelFactory(parameters)) { supportedChannels.Add(typeof(IDuplexSessionChannel), 0); } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ChannelRequirements.CantCreateChannelException( supportedChannels.Keys, requiredChannels, binding.Name)); } protected override void OnAbort() { IChannel channel = null; lock (ThisLock) { channel = (channelsList.Count > 0) ? channelsList[channelsList.Count - 1] : null; } while (channel != null) { channel.Abort(); lock (ThisLock) { channelsList.Remove(channel); channel = (channelsList.Count > 0) ? channelsList[channelsList.Count - 1] : null; } } } protected override void OnClose(TimeSpan timeout) { TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); while (true) { int count; IChannel channel; lock (ThisLock) { count = channelsList.Count; if (count == 0) return; channel = channelsList[0]; } channel.Close(timeoutHelper.RemainingTime()); } } protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state) { List objectList; lock (ThisLock) { objectList = new List(); for (int index = 0; index < channelsList.Count; index++) objectList.Add(channelsList[index]); } return new CloseCollectionAsyncResult(timeout, callback, state, objectList); } protected override void OnEndClose(IAsyncResult result) { CloseCollectionAsyncResult.End(result); } protected override void OnOpened() { base.OnOpened(); this.clientRuntime.LockDownProperties(); } public void ChannelCreated(IChannel channel) { if (DiagnosticUtility.ShouldTraceVerbose) { TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.ChannelCreated, SR.GetString(SR.TraceCodeChannelCreated, TraceUtility.CreateSourceString(channel)), this); } lock (ThisLock) { ThrowIfDisposed(); channelsList.Add(channel); } } public void ChannelDisposed(IChannel channel) { if (DiagnosticUtility.ShouldTraceVerbose) { TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.ChannelDisposed, SR.GetString(SR.TraceCodeChannelDisposed, TraceUtility.CreateSourceString(channel)), this); } lock (ThisLock) { channelsList.Remove(channel); } } public virtual ServiceChannel CreateServiceChannel(EndpointAddress address, Uri via) { IChannelBinder binder = this.CreateInnerChannelBinder(address, via); ServiceChannel serviceChannel = new ServiceChannel(this, binder); if (binder is DuplexChannelBinder) { DuplexChannelBinder duplexChannelBinder = binder as DuplexChannelBinder; duplexChannelBinder.ChannelHandler = new ChannelHandler(this.messageVersion, binder, serviceChannel); duplexChannelBinder.DefaultCloseTimeout = this.DefaultCloseTimeout; duplexChannelBinder.DefaultSendTimeout = this.DefaultSendTimeout; duplexChannelBinder.IdentityVerifier = this.clientRuntime.IdentityVerifier; } return serviceChannel; } public TChannel CreateChannel(EndpointAddress address) { return this.CreateChannel(address, null); } public TChannel CreateChannel(EndpointAddress address, Uri via) { if (!this.CanCreateChannel()) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( SR.GetString(SR.CouldnTCreateChannelForChannelType2, this.bindingName, typeof(TChannel).Name))); } return (TChannel)this.CreateChannel(typeof(TChannel), address, via); } public abstract bool CanCreateChannel(); public object CreateChannel(Type channelType, EndpointAddress address) { return this.CreateChannel(channelType, address, null); } public object CreateChannel(Type channelType, EndpointAddress address, Uri via) { if (via == null) { via = this.ClientRuntime.Via; if (via == null) { via = address.Uri; } } ServiceChannel serviceChannel = this.CreateServiceChannel(address, via); serviceChannel.Proxy = CreateProxy(channelType, channelType, MessageDirection.Input, serviceChannel); serviceChannel.ClientRuntime.GetRuntime().InitializeChannel((IClientChannel)serviceChannel.Proxy); OperationContext current = OperationContext.Current; if ((current != null) && (current.InstanceContext != null)) { current.InstanceContext.WmiChannels.Add((IChannel)serviceChannel.Proxy); serviceChannel.WmiInstanceContext = current.InstanceContext; } return serviceChannel.Proxy; } [Fx.Tag.SecurityNote(Critical = "Constructs a ServiceChannelProxy, which is Critical.", Safe = "Returns the TP, but does not return the RealProxy -- caller can't get from TP to RP without an elevation.")] [SecuritySafeCritical] internal static object CreateProxy(Type interfaceType, Type proxiedType, MessageDirection direction, ServiceChannel serviceChannel) { if (!proxiedType.IsInterface) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("SFxChannelFactoryTypeMustBeInterface"))); } ServiceChannelProxy proxy = new ServiceChannelProxy(interfaceType, proxiedType, direction, serviceChannel); return proxy.GetTransparentProxy(); } [Fx.Tag.SecurityNote(Critical = "Calls LinkDemand method RemotingServices.GetRealProxy and access critical class ServiceChannelProxy.", Safe = "Gets the ServiceChannel (which is not critical) and discards the RealProxy.")] [SecuritySafeCritical] internal static ServiceChannel GetServiceChannel(object transparentProxy) { IChannelBaseProxy cb = transparentProxy as IChannelBaseProxy; if (cb != null) return cb.GetServiceChannel(); ServiceChannelProxy proxy = RemotingServices.GetRealProxy(transparentProxy) as ServiceChannelProxy; if (proxy != null) return proxy.GetServiceChannel(); else return null; } protected abstract IChannelBinder CreateInnerChannelBinder(EndpointAddress address, Uri via); abstract class TypedServiceChannelFactory : ServiceChannelFactory where TChannel : class, IChannel { IChannelFactory innerChannelFactory; protected TypedServiceChannelFactory(IChannelFactory innerChannelFactory, ClientRuntime clientRuntime, Binding binding) : base(clientRuntime, binding) { this.innerChannelFactory = innerChannelFactory; } protected IChannelFactory InnerChannelFactory { get { return this.innerChannelFactory; } } protected override void OnAbort() { base.OnAbort(); this.innerChannelFactory.Abort(); } protected override void OnOpen(TimeSpan timeout) { this.innerChannelFactory.Open(timeout); } protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) { return this.innerChannelFactory.BeginOpen(timeout, callback, state); } protected override void OnEndOpen(IAsyncResult result) { this.innerChannelFactory.EndOpen(result); } protected override void OnClose(TimeSpan timeout) { TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); base.OnClose(timeoutHelper.RemainingTime()); this.innerChannelFactory.Close(timeoutHelper.RemainingTime()); } protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state) { return new ChainedAsyncResult(timeout, callback, state, base.OnBeginClose, base.OnEndClose, this.innerChannelFactory.BeginClose, this.innerChannelFactory.EndClose); } protected override void OnEndClose(IAsyncResult result) { ChainedAsyncResult.End(result); } public override T GetProperty() { if (typeof(T) == typeof(TypedServiceChannelFactory)) { return (T)(object)this; } T baseProperty = base.GetProperty(); if (baseProperty != null) { return baseProperty; } return this.innerChannelFactory.GetProperty(); } } class ServiceChannelFactoryOverOutput : TypedServiceChannelFactory { public ServiceChannelFactoryOverOutput(IChannelFactory innerChannelFactory, ClientRuntime clientRuntime, Binding binding) : base(innerChannelFactory, clientRuntime, binding) { } protected override IChannelBinder CreateInnerChannelBinder(EndpointAddress to, Uri via) { return new OutputChannelBinder(this.InnerChannelFactory.CreateChannel(to, via)); } public override bool CanCreateChannel() { return (typeof(TChannel) == typeof(IOutputChannel) || typeof(TChannel) == typeof(IRequestChannel)); } } class ServiceChannelFactoryOverDuplex : TypedServiceChannelFactory { public ServiceChannelFactoryOverDuplex(IChannelFactory innerChannelFactory, ClientRuntime clientRuntime, Binding binding) : base(innerChannelFactory, clientRuntime, binding) { } protected override IChannelBinder CreateInnerChannelBinder(EndpointAddress to, Uri via) { return new DuplexChannelBinder(this.InnerChannelFactory.CreateChannel(to, via), this.RequestReplyCorrelator); } public override bool CanCreateChannel() { return (typeof(TChannel) == typeof(IOutputChannel) || typeof(TChannel) == typeof(IRequestChannel) || typeof(TChannel) == typeof(IDuplexChannel)); } } class ServiceChannelFactoryOverRequest : TypedServiceChannelFactory { public ServiceChannelFactoryOverRequest(IChannelFactory innerChannelFactory, ClientRuntime clientRuntime, Binding binding) : base(innerChannelFactory, clientRuntime, binding) { } protected override IChannelBinder CreateInnerChannelBinder(EndpointAddress to, Uri via) { return new RequestChannelBinder(this.InnerChannelFactory.CreateChannel(to, via)); } public override bool CanCreateChannel() { return (typeof(TChannel) == typeof(IOutputChannel) || typeof(TChannel) == typeof(IRequestChannel)); } } class ServiceChannelFactoryOverOutputSession : TypedServiceChannelFactory { bool datagramAdapter; public ServiceChannelFactoryOverOutputSession(IChannelFactory innerChannelFactory, ClientRuntime clientRuntime, Binding binding, bool datagramAdapter) : base(innerChannelFactory, clientRuntime, binding) { this.datagramAdapter = datagramAdapter; } protected override IChannelBinder CreateInnerChannelBinder(EndpointAddress to, Uri via) { IOutputChannel channel; if (this.datagramAdapter) { channel = DatagramAdapter.GetOutputChannel( delegate() { return this.InnerChannelFactory.CreateChannel(to, via); }, timeouts); } else { channel = this.InnerChannelFactory.CreateChannel(to, via); } return new OutputChannelBinder(channel); } public override bool CanCreateChannel() { return (typeof(TChannel) == typeof(IOutputChannel) || typeof(TChannel) == typeof(IOutputSessionChannel) || typeof(TChannel) == typeof(IRequestChannel) || typeof(TChannel) == typeof(IRequestSessionChannel)); } } class ServiceChannelFactoryOverDuplexSession : TypedServiceChannelFactory { bool useActiveAutoClose; public ServiceChannelFactoryOverDuplexSession(IChannelFactory innerChannelFactory, ClientRuntime clientRuntime, Binding binding, bool useActiveAutoClose) : base(innerChannelFactory, clientRuntime, binding) { this.useActiveAutoClose = useActiveAutoClose; } protected override IChannelBinder CreateInnerChannelBinder(EndpointAddress to, Uri via) { return new DuplexChannelBinder(this.InnerChannelFactory.CreateChannel(to, via), this.RequestReplyCorrelator, useActiveAutoClose); } public override bool CanCreateChannel() { return (typeof(TChannel) == typeof(IOutputChannel) || typeof(TChannel) == typeof(IRequestChannel) || typeof(TChannel) == typeof(IDuplexChannel) || typeof(TChannel) == typeof(IOutputSessionChannel) || typeof(TChannel) == typeof(IRequestSessionChannel) || typeof(TChannel) == typeof(IDuplexSessionChannel)); } } class ServiceChannelFactoryOverRequestSession : TypedServiceChannelFactory { bool datagramAdapter = false; public ServiceChannelFactoryOverRequestSession(IChannelFactory innerChannelFactory, ClientRuntime clientRuntime, Binding binding, bool datagramAdapter) : base(innerChannelFactory, clientRuntime, binding) { this.datagramAdapter = datagramAdapter; } protected override IChannelBinder CreateInnerChannelBinder(EndpointAddress to, Uri via) { IRequestChannel channel; if (this.datagramAdapter) { channel = DatagramAdapter.GetRequestChannel( delegate() { return this.InnerChannelFactory.CreateChannel(to, via); }, this.timeouts); } else { channel = this.InnerChannelFactory.CreateChannel(to, via); } return new RequestChannelBinder(channel); } public override bool CanCreateChannel() { return (typeof(TChannel) == typeof(IOutputChannel) || typeof(TChannel) == typeof(IOutputSessionChannel) || typeof(TChannel) == typeof(IRequestChannel) || typeof(TChannel) == typeof(IRequestSessionChannel)); } } class DefaultCommunicationTimeouts : IDefaultCommunicationTimeouts { TimeSpan closeTimeout; TimeSpan openTimeout; TimeSpan receiveTimeout; TimeSpan sendTimeout; public DefaultCommunicationTimeouts(IDefaultCommunicationTimeouts timeouts) { this.closeTimeout = timeouts.CloseTimeout; this.openTimeout = timeouts.OpenTimeout; this.receiveTimeout = timeouts.ReceiveTimeout; this.sendTimeout = timeouts.SendTimeout; } public TimeSpan CloseTimeout { get { return this.closeTimeout; } } public TimeSpan OpenTimeout { get { return this.openTimeout; } } public TimeSpan ReceiveTimeout { get { return this.receiveTimeout; } } public TimeSpan SendTimeout { get { return this.sendTimeout; } } } } }