|
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Description
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Channels;
using System.ServiceModel.Diagnostics;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.MsmqIntegration;
using System.ServiceModel.Security;
using System.Xml;
class DispatcherBuilder
{
// get Contract info msmq integration needs, and put in it's BE
// also update Contract
static void AddMsmqIntegrationContractInformation(ServiceEndpoint endpoint)
{
MsmqIntegrationBinding binding = endpoint.Binding as MsmqIntegrationBinding;
if (null != binding)
{
Type[] types = ProcessDescriptionForMsmqIntegration(endpoint, binding.TargetSerializationTypes);
binding.TargetSerializationTypes = types;
}
else
{
CustomBinding customBinding = endpoint.Binding as CustomBinding;
if (null != customBinding)
{
MsmqIntegrationBindingElement element = customBinding.Elements.Find<MsmqIntegrationBindingElement>();
if (null != element)
{
Type[] types = ProcessDescriptionForMsmqIntegration(endpoint, element.TargetSerializationTypes);
element.TargetSerializationTypes = types;
}
}
}
}
static Type[] ProcessDescriptionForMsmqIntegration(ServiceEndpoint endpoint, Type[] existingSerializationTypes)
{
List<Type> targetSerializationTypes;
if (existingSerializationTypes == null)
{
targetSerializationTypes = new List<Type>();
}
else
{
targetSerializationTypes = new List<Type>(existingSerializationTypes);
}
foreach (OperationDescription operationDesc in endpoint.Contract.Operations)
{
foreach (Type type in operationDesc.KnownTypes)
{
// add contract known types to targetSerializationTypes
if (!targetSerializationTypes.Contains(type))
{
targetSerializationTypes.Add(type);
}
}
// Default document style doesn't work for Integration Transport
// because messages that SFx layer deals with are not wrapped
// We need to change style for each operation
foreach (MessageDescription messageDescription in operationDesc.Messages)
{
messageDescription.Body.WrapperName = messageDescription.Body.WrapperNamespace = null;
}
}
return targetSerializationTypes.ToArray();
}
internal static ClientRuntime BuildProxyBehavior(ServiceEndpoint serviceEndpoint, out BindingParameterCollection parameters)
{
parameters = new BindingParameterCollection();
SecurityContractInformationEndpointBehavior.ClientInstance.AddBindingParameters(serviceEndpoint, parameters);
AddBindingParameters(serviceEndpoint, parameters);
ContractDescription contractDescription = serviceEndpoint.Contract;
ClientRuntime clientRuntime = new ClientRuntime(contractDescription.Name, contractDescription.Namespace);
clientRuntime.ContractClientType = contractDescription.ContractType;
IdentityVerifier identityVerifier = serviceEndpoint.Binding.GetProperty<IdentityVerifier>(parameters);
if (identityVerifier != null)
{
clientRuntime.IdentityVerifier = identityVerifier;
}
for (int i = 0; i < contractDescription.Operations.Count; i++)
{
OperationDescription operation = contractDescription.Operations[i];
if (!operation.IsServerInitiated())
{
DispatcherBuilder.BuildProxyOperation(operation, clientRuntime);
}
else
{
DispatcherBuilder.BuildDispatchOperation(operation, clientRuntime.CallbackDispatchRuntime, null);
}
}
DispatcherBuilder.ApplyClientBehavior(serviceEndpoint, clientRuntime);
return clientRuntime;
}
class EndpointInfo
{
ServiceEndpoint endpoint;
EndpointDispatcher endpointDispatcher;
EndpointFilterProvider provider;
public EndpointInfo(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher, EndpointFilterProvider provider)
{
this.endpoint = endpoint;
this.endpointDispatcher = endpointDispatcher;
this.provider = provider;
}
public ServiceEndpoint Endpoint { get { return this.endpoint; } }
public EndpointFilterProvider FilterProvider { get { return this.provider; } }
public EndpointDispatcher EndpointDispatcher { get { return this.endpointDispatcher; } }
}
internal class ListenUriInfo
{
Uri listenUri;
ListenUriMode listenUriMode;
public ListenUriInfo(Uri listenUri, ListenUriMode listenUriMode)
{
this.listenUri = listenUri;
this.listenUriMode = listenUriMode;
}
public Uri ListenUri
{
get { return this.listenUri; }
}
public ListenUriMode ListenUriMode
{
get { return this.listenUriMode; }
}
// implement Equals and GetHashCode so that we can use this as a key in a dictionary
public override bool Equals(Object other)
{
return this.Equals(other as ListenUriInfo);
}
public bool Equals(ListenUriInfo other)
{
if (other == null)
{
return false;
}
if (object.ReferenceEquals(this, other))
{
return true;
}
return (this.listenUriMode == other.listenUriMode)
&& EndpointAddress.UriEquals(this.listenUri, other.listenUri, true /* ignoreCase */, true /* includeHost */);
}
public override int GetHashCode()
{
return EndpointAddress.UriGetHashCode(this.listenUri, true /* includeHost */);
}
}
class StuffPerListenUriInfo
{
public BindingParameterCollection Parameters = new BindingParameterCollection();
public Collection<ServiceEndpoint> Endpoints = new Collection<ServiceEndpoint>();
public ChannelDispatcher ChannelDispatcher = null;
}
void ValidateDescription(ServiceDescription description, ServiceHostBase serviceHost)
{
description.EnsureInvariants();
(PartialTrustValidationBehavior.Instance as IServiceBehavior).Validate(description, serviceHost);
#pragma warning disable 0618
(PeerValidationBehavior.Instance as IServiceBehavior).Validate(description, serviceHost);
#pragma warning restore 0618
(TransactionValidationBehavior.Instance as IServiceBehavior).Validate(description, serviceHost);
(System.ServiceModel.MsmqIntegration.MsmqIntegrationValidationBehavior.Instance as IServiceBehavior).Validate(description, serviceHost);
(SecurityValidationBehavior.Instance as IServiceBehavior).Validate(description, serviceHost);
(new UniqueContractNameValidationBehavior() as IServiceBehavior).Validate(description, serviceHost);
for (int i = 0; i < description.Behaviors.Count; i++)
{
IServiceBehavior iServiceBehavior = description.Behaviors[i];
iServiceBehavior.Validate(description, serviceHost);
}
for (int i = 0; i < description.Endpoints.Count; i++)
{
ServiceEndpoint endpoint = description.Endpoints[i];
ContractDescription contract = endpoint.Contract;
bool alreadyProcessedThisContract = false;
for (int j = 0; j < i; j++)
{
if (description.Endpoints[j].Contract == contract)
{
alreadyProcessedThisContract = true;
break;
}
}
endpoint.ValidateForService(!alreadyProcessedThisContract);
}
}
static void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection parameters)
{
foreach (IContractBehavior icb in endpoint.Contract.Behaviors)
{
icb.AddBindingParameters(endpoint.Contract, endpoint, parameters);
}
foreach (IEndpointBehavior ieb in endpoint.Behaviors)
{
ieb.AddBindingParameters(endpoint, parameters);
}
foreach (OperationDescription op in endpoint.Contract.Operations)
{
foreach (IOperationBehavior iob in op.Behaviors)
{
iob.AddBindingParameters(op, parameters);
}
}
}
Type BuildChannelListener(StuffPerListenUriInfo stuff,
ServiceHostBase serviceHost,
Uri listenUri,
ListenUriMode listenUriMode,
bool supportContextSession,
out IChannelListener result)
{
Binding originalBinding = stuff.Endpoints[0].Binding;
CustomBinding binding = new CustomBinding(originalBinding);
BindingParameterCollection parameters = stuff.Parameters;
Uri listenUriBaseAddress;
string listenUriRelativeAddress;
GetBaseAndRelativeAddresses(serviceHost, listenUri, binding.Scheme, out listenUriBaseAddress, out listenUriRelativeAddress);
InternalDuplexBindingElement internalDuplex = null;
InternalDuplexBindingElement.AddDuplexListenerSupport(binding, ref internalDuplex);
// All types are supported to start
bool reply = true;
bool replySession = true;
bool input = true;
bool inputSession = true;
bool duplex = true;
bool duplexSession = true;
string sessionContractName = null;
string datagramContractName = null;
// each endpoint adds constraints
for (int i = 0; i < stuff.Endpoints.Count; ++i)
{
ContractDescription contract = stuff.Endpoints[i].Contract;
if (contract.SessionMode == SessionMode.Required)
{
sessionContractName = contract.Name;
}
if (contract.SessionMode == SessionMode.NotAllowed)
{
datagramContractName = contract.Name;
}
System.Collections.IList endpointTypes = GetSupportedChannelTypes(contract);
if (!endpointTypes.Contains(typeof(IReplyChannel)))
{
reply = false;
}
if (!endpointTypes.Contains(typeof(IReplySessionChannel)))
{
replySession = false;
}
if (!endpointTypes.Contains(typeof(IInputChannel)))
{
input = false;
}
if (!endpointTypes.Contains(typeof(IInputSessionChannel)))
{
inputSession = false;
}
if (!endpointTypes.Contains(typeof(IDuplexChannel)))
{
duplex = false;
}
if (!endpointTypes.Contains(typeof(IDuplexSessionChannel)))
{
duplexSession = false;
}
}
if ((sessionContractName != null) && (datagramContractName != null))
{
string text = SR.GetString(SR.SFxCannotRequireBothSessionAndDatagram3, datagramContractName, sessionContractName, binding.Name);
Exception error = new InvalidOperationException(text);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
}
List<Type> supportedChannelTypes = new List<Type>();
if (input)
{
supportedChannelTypes.Add(typeof(IInputChannel));
}
if (inputSession)
{
supportedChannelTypes.Add(typeof(IInputSessionChannel));
}
if (reply)
{
supportedChannelTypes.Add(typeof(IReplyChannel));
}
if (replySession)
{
supportedChannelTypes.Add(typeof(IReplySessionChannel));
}
if (duplex)
{
supportedChannelTypes.Add(typeof(IDuplexChannel));
}
if (duplexSession)
{
supportedChannelTypes.Add(typeof(IDuplexSessionChannel));
}
// now we know what channel types we can use to support the contracts at this ListenUri
Type returnValue = DispatcherBuilder.MaybeCreateListener(true, supportedChannelTypes.ToArray(), binding, parameters,
listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, serviceHost.ServiceThrottle, out result,
supportContextSession && sessionContractName != null);
if (result == null)
{
// we put a lot of work into creating a good error message, as this is a common case
Dictionary<Type, byte> setOfChannelTypesSupportedByBinding = new Dictionary<Type, byte>();
if (binding.CanBuildChannelListener<IInputChannel>())
{
setOfChannelTypesSupportedByBinding.Add(typeof(IInputChannel), 0);
}
if (binding.CanBuildChannelListener<IReplyChannel>())
{
setOfChannelTypesSupportedByBinding.Add(typeof(IReplyChannel), 0);
}
if (binding.CanBuildChannelListener<IDuplexChannel>())
{
setOfChannelTypesSupportedByBinding.Add(typeof(IDuplexChannel), 0);
}
if (binding.CanBuildChannelListener<IInputSessionChannel>())
{
setOfChannelTypesSupportedByBinding.Add(typeof(IInputSessionChannel), 0);
}
if (binding.CanBuildChannelListener<IReplySessionChannel>())
{
setOfChannelTypesSupportedByBinding.Add(typeof(IReplySessionChannel), 0);
}
if (binding.CanBuildChannelListener<IDuplexSessionChannel>())
{
setOfChannelTypesSupportedByBinding.Add(typeof(IDuplexSessionChannel), 0);
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ChannelRequirements.CantCreateListenerException(
setOfChannelTypesSupportedByBinding.Keys, supportedChannelTypes, originalBinding.Name));
}
return returnValue;
}
static internal Type MaybeCreateListener(bool actuallyCreate, Type[] supportedChannels,
Binding binding, BindingParameterCollection parameters,
Uri listenUriBaseAddress, string listenUriRelativeAddress,
ListenUriMode listenUriMode, ServiceThrottle throttle,
out IChannelListener result)
{
return MaybeCreateListener(actuallyCreate, supportedChannels, binding, parameters, listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, throttle,
out result, false);
}
static Type MaybeCreateListener(bool actuallyCreate, Type[] supportedChannels,
Binding binding, BindingParameterCollection parameters,
Uri listenUriBaseAddress, string listenUriRelativeAddress,
ListenUriMode listenUriMode, ServiceThrottle throttle,
out IChannelListener result, bool supportContextSession)
{
// if actuallyCreate is true, then this behaves like CreateListener()
// else this behaves like CanCreateListener()
// result is channel type that was (would be) created, null if can't create
//
// Ugly API helps refactor common code in these two similar-but-different methods
result = null;
for (int i = 0; i < supportedChannels.Length; i++)
{
Type channelType = supportedChannels[i];
if (channelType == typeof(IInputChannel))
{
if (binding.CanBuildChannelListener<IInputChannel>(parameters))
{
if (actuallyCreate)
{
result = binding.BuildChannelListener<IInputChannel>(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters);
}
return typeof(IInputChannel);
}
}
if (channelType == typeof(IReplyChannel))
{
if (binding.CanBuildChannelListener<IReplyChannel>(parameters))
{
if (actuallyCreate)
{
result = binding.BuildChannelListener<IReplyChannel>(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters);
}
return typeof(IReplyChannel);
}
}
if (channelType == typeof(IDuplexChannel))
{
if (binding.CanBuildChannelListener<IDuplexChannel>(parameters))
{
if (actuallyCreate)
{
result = binding.BuildChannelListener<IDuplexChannel>(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters);
}
return typeof(IDuplexChannel);
}
}
if (channelType == typeof(IInputSessionChannel))
{
if (binding.CanBuildChannelListener<IInputSessionChannel>(parameters))
{
if (actuallyCreate)
{
result = binding.BuildChannelListener<IInputSessionChannel>(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters);
}
return typeof(IInputSessionChannel);
}
}
if (channelType == typeof(IReplySessionChannel))
{
if (binding.CanBuildChannelListener<IReplySessionChannel>(parameters))
{
if (actuallyCreate)
{
result = binding.BuildChannelListener<IReplySessionChannel>(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters);
}
return typeof(IReplySessionChannel);
}
}
if (channelType == typeof(IDuplexSessionChannel))
{
if (binding.CanBuildChannelListener<IDuplexSessionChannel>(parameters))
{
if (actuallyCreate)
{
result = binding.BuildChannelListener<IDuplexSessionChannel>(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters);
}
return typeof(IDuplexSessionChannel);
}
}
}
// If the binding does not support the type natively, try to adapt
for (int i = 0; i < supportedChannels.Length; i++)
{
Type channelType = supportedChannels[i];
// For SessionMode.Allowed or SessionMode.NotAllowed we will accept session-ful variants as well and adapt them
if (channelType == typeof(IInputChannel))
{
if (binding.CanBuildChannelListener<IInputSessionChannel>(parameters))
{
if (actuallyCreate)
{
IChannelListener<IInputSessionChannel> temp = binding.BuildChannelListener<IInputSessionChannel>(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters);
result = DatagramAdapter.GetInputListener(temp, throttle, binding);
}
return typeof(IInputSessionChannel);
}
}
if (channelType == typeof(IReplyChannel))
{
if (binding.CanBuildChannelListener<IReplySessionChannel>(parameters))
{
if (actuallyCreate)
{
IChannelListener<IReplySessionChannel> temp = binding.BuildChannelListener<IReplySessionChannel>(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters);
result = DatagramAdapter.GetReplyListener(temp, throttle, binding);
}
return typeof(IReplySessionChannel);
}
}
if (supportContextSession)
{
// 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 (channelType == typeof(IReplySessionChannel))
{
if (binding.CanBuildChannelListener<IReplyChannel>(parameters)
&& binding.GetProperty<IContextSessionProvider>(parameters) != null)
{
if (actuallyCreate)
{
result = binding.BuildChannelListener<IReplyChannel>(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, parameters);
}
return typeof(IReplyChannel);
}
}
}
}
return null;
}
void EnsureThereAreApplicationEndpoints(ServiceDescription description)
{
foreach (ServiceEndpoint endpoint in description.Endpoints)
{
if (!endpoint.InternalIsSystemEndpoint(description))
{
return;
}
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
SR.GetString(SR.ServiceHasZeroAppEndpoints, description.ConfigurationName)));
}
static Uri EnsureListenUri(ServiceHostBase serviceHost, ServiceEndpoint endpoint)
{
Uri listenUri = endpoint.ListenUri;
if (listenUri == null)
{
listenUri = serviceHost.GetVia(endpoint.Binding.Scheme, ServiceHost.EmptyUri);
}
if (listenUri == null)
{
AspNetEnvironment.Current.ProcessNotMatchedEndpointAddress(listenUri, endpoint.Binding.Name);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxEndpointNoMatchingScheme, endpoint.Binding.Scheme, endpoint.Binding.Name, serviceHost.GetBaseAddressSchemes())));
}
return listenUri;
}
void GetBaseAndRelativeAddresses(ServiceHostBase serviceHost, Uri listenUri, string scheme, out Uri listenUriBaseAddress, out string listenUriRelativeAddress)
{
// set the ListenUri (old EndpointListener EnsureListenUri() logic)
listenUriBaseAddress = listenUri;
listenUriRelativeAddress = String.Empty;
if (serviceHost.InternalBaseAddresses.Contains(scheme))
{
Uri baseAddress = serviceHost.InternalBaseAddresses[scheme];
if (!baseAddress.AbsoluteUri.EndsWith("/", StringComparison.Ordinal))
{
baseAddress = new Uri(baseAddress.AbsoluteUri + "/");
}
string baseAddressString = baseAddress.ToString();
string thisAddressString = listenUri.ToString();
if (thisAddressString.StartsWith(baseAddressString, StringComparison.OrdinalIgnoreCase))
{
listenUriBaseAddress = baseAddress;
listenUriRelativeAddress = thisAddressString.Substring(baseAddressString.Length);
}
}
}
void InitializeServicePerformanceCounters(ServiceHostBase serviceHost)
{
if (PerformanceCounters.PerformanceCountersEnabled)
{
ServicePerformanceCountersBase tempCounters = PerformanceCountersFactory.CreateServiceCounters(serviceHost);
if (tempCounters != null && tempCounters.Initialized)
{
serviceHost.Counters = tempCounters;
}
}
// Some perf. counters are enabled by default
else if (PerformanceCounters.MinimalPerformanceCountersEnabled)
{
DefaultPerformanceCounters tempCounters = new DefaultPerformanceCounters(serviceHost);
if (tempCounters.Initialized)
{
serviceHost.DefaultCounters = tempCounters;
}
}
}
//This method generates the BindingParameterCollection in the same way it is created during DispatcherBuilder.InitializeServiceHost
internal static BindingParameterCollection GetBindingParameters(ServiceHostBase serviceHost, Collection<ServiceEndpoint> endpoints)
{
BindingParameterCollection parameters = new BindingParameterCollection();
parameters.Add(new ThreadSafeMessageFilterTable<EndpointAddress>());
foreach (IServiceBehavior behavior in serviceHost.Description.Behaviors)
{
behavior.AddBindingParameters(serviceHost.Description, serviceHost, endpoints, parameters);
}
foreach (ServiceEndpoint endpoint in endpoints)
{
DispatcherBuilder.SecurityContractInformationEndpointBehavior.ServerInstance.AddBindingParameters(endpoint, parameters);
DispatcherBuilder.AddBindingParameters(endpoint, parameters);
}
return parameters;
}
internal static ListenUriInfo GetListenUriInfoForEndpoint(ServiceHostBase host, ServiceEndpoint endpoint)
{
Uri listenUri = EnsureListenUri(host, endpoint);
return new ListenUriInfo(listenUri, endpoint.ListenUriMode);
}
public void InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost)
{
if (serviceHost.ImplementedContracts != null && serviceHost.ImplementedContracts.Count > 0)
{
EnsureThereAreApplicationEndpoints(description);
}
ValidateDescription(description, serviceHost);
AspNetEnvironment.Current.AddHostingBehavior(serviceHost, description);
ServiceBehaviorAttribute instanceSettings = description.Behaviors.Find<ServiceBehaviorAttribute>();
InitializeServicePerformanceCounters(serviceHost);
Dictionary<ListenUriInfo, StuffPerListenUriInfo> stuffPerListenUriInfo
= new Dictionary<ListenUriInfo, StuffPerListenUriInfo>();
Dictionary<EndpointAddress, Collection<EndpointInfo>> endpointInfosPerEndpointAddress
= new Dictionary<EndpointAddress, Collection<EndpointInfo>>();
// Ensure ListenUri and group endpoints per ListenUri
for (int i = 0; i < description.Endpoints.Count; i++)
{
//Ensure ReceiveContextSettings before building channel
bool requiresReceiveContext = false; //at least one operation had ReceiveContextEnabledAttribute
ServiceEndpoint endpoint = description.Endpoints[i];
foreach (OperationDescription operation in endpoint.Contract.Operations)
{
if (operation.Behaviors.Find<ReceiveContextEnabledAttribute>() != null)
{
requiresReceiveContext = true;
break;
}
}
if (requiresReceiveContext)
{
IReceiveContextSettings receiveContextSettings = endpoint.Binding.GetProperty<IReceiveContextSettings>(new BindingParameterCollection());
if (receiveContextSettings == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new InvalidOperationException(
SR.GetString(SR.SFxReceiveContextSettingsPropertyMissing,
endpoint.Contract.Name,
typeof(ReceiveContextEnabledAttribute).Name,
endpoint.Address.Uri.AbsoluteUri,
typeof(IReceiveContextSettings).Name)));
}
//Enable ReceiveContext on the binding.
receiveContextSettings.Enabled = true;
}
ListenUriInfo listenUriInfo = GetListenUriInfoForEndpoint(serviceHost, endpoint);
if (!stuffPerListenUriInfo.ContainsKey(listenUriInfo))
{
stuffPerListenUriInfo.Add(listenUriInfo, new StuffPerListenUriInfo());
}
stuffPerListenUriInfo[listenUriInfo].Endpoints.Add(endpoint);
}
foreach (KeyValuePair<ListenUriInfo, StuffPerListenUriInfo> stuff in stuffPerListenUriInfo)
{
Uri listenUri = stuff.Key.ListenUri;
ListenUriMode listenUriMode = stuff.Key.ListenUriMode;
BindingParameterCollection parameters = stuff.Value.Parameters;
Binding binding = stuff.Value.Endpoints[0].Binding;
EndpointIdentity identity = stuff.Value.Endpoints[0].Address.Identity;
// same EndpointAddressTable instance must be shared between channelDispatcher and parameters
ThreadSafeMessageFilterTable<EndpointAddress> endpointAddressTable = new ThreadSafeMessageFilterTable<EndpointAddress>();
parameters.Add(endpointAddressTable);
bool supportContextSession = false;
// add service-level binding parameters
foreach (IServiceBehavior behavior in description.Behaviors)
{
if (behavior is IContextSessionProvider)
{
supportContextSession = true;
}
behavior.AddBindingParameters(description, serviceHost, stuff.Value.Endpoints, parameters);
}
for (int i = 0; i < stuff.Value.Endpoints.Count; i++)
{
ServiceEndpoint endpoint = stuff.Value.Endpoints[i];
string viaString = listenUri.AbsoluteUri;
// ensure all endpoints with this ListenUriInfo have same binding
if (endpoint.Binding != binding)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ABindingInstanceHasAlreadyBeenAssociatedTo1, viaString)));
}
// ensure all endpoints with this ListenUriInfo have same identity
if (!object.Equals(endpoint.Address.Identity, identity))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
SR.GetString(SR.SFxWhenMultipleEndpointsShareAListenUriTheyMustHaveSameIdentity, viaString)));
}
// add binding parameters (endpoint scope and below)
AddMsmqIntegrationContractInformation(endpoint);
SecurityContractInformationEndpointBehavior.ServerInstance.AddBindingParameters(endpoint, parameters);
AddBindingParameters(endpoint, parameters);
}
// build IChannelListener and ChannelDispatcher
IChannelListener listener;
Type channelType = this.BuildChannelListener(stuff.Value,
serviceHost,
listenUri,
listenUriMode,
supportContextSession,
out listener);
XmlQualifiedName bindingQname = new XmlQualifiedName(binding.Name, binding.Namespace);
ChannelDispatcher channelDispatcher = new ChannelDispatcher(listener, bindingQname.ToString(), binding);
channelDispatcher.SetEndpointAddressTable(endpointAddressTable);
stuff.Value.ChannelDispatcher = channelDispatcher;
bool canReceiveInTransaction = false; // at least one operation is TransactionScopeRequired
int transactedBatchSize = int.MaxValue;
for (int i = 0; i < stuff.Value.Endpoints.Count; i++)
{
ServiceEndpoint endpoint = stuff.Value.Endpoints[i];
string viaString = listenUri.AbsoluteUri;
EndpointFilterProvider provider = new EndpointFilterProvider();
EndpointDispatcher dispatcher = DispatcherBuilder.BuildDispatcher(serviceHost, description, endpoint, endpoint.Contract, provider);
for (int j = 0; j < endpoint.Contract.Operations.Count; j++)
{
OperationDescription operation = endpoint.Contract.Operations[j];
OperationBehaviorAttribute operationBehavior = operation.Behaviors.Find<OperationBehaviorAttribute>();
if (null != operationBehavior && operationBehavior.TransactionScopeRequired)
{
canReceiveInTransaction = true;
break;
}
}
if (!endpointInfosPerEndpointAddress.ContainsKey(endpoint.Address))
{
endpointInfosPerEndpointAddress.Add(endpoint.Address, new Collection<EndpointInfo>());
}
endpointInfosPerEndpointAddress[endpoint.Address].Add(new EndpointInfo(endpoint, dispatcher, provider));
channelDispatcher.Endpoints.Add(dispatcher);
TransactedBatchingBehavior batchBehavior = endpoint.Behaviors.Find<TransactedBatchingBehavior>();
if (batchBehavior == null)
{
transactedBatchSize = 0;
}
else
{
if (!canReceiveInTransaction)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MsmqBatchRequiresTransactionScope)));
transactedBatchSize = System.Math.Min(transactedBatchSize, batchBehavior.MaxBatchSize);
}
if (PerformanceCounters.PerformanceCountersEnabled || PerformanceCounters.MinimalPerformanceCountersEnabled)
{
PerformanceCounters.AddPerformanceCountersForEndpoint(serviceHost, endpoint.Contract, dispatcher);
}
} // end foreach "endpoint"
// Clear performance counter cache.
if ((PerformanceCounters.PerformanceCountersEnabled || PerformanceCounters.MinimalPerformanceCountersEnabled) && ServiceModelAppSettings.EnsureUniquePerformanceCounterInstanceNames)
{
System.Diagnostics.PerformanceCounter.CloseSharedResources();
}
if (canReceiveInTransaction)
{
BindingElementCollection bindingElements = binding.CreateBindingElements();
foreach (BindingElement bindingElement in bindingElements)
{
ITransactedBindingElement txElement = bindingElement as ITransactedBindingElement;
if (null != txElement && txElement.TransactedReceiveEnabled)
{
channelDispatcher.IsTransactedReceive = true;
channelDispatcher.MaxTransactedBatchSize = transactedBatchSize;
break;
}
}
}
//Set the mode of operation for ChannelDispatcher based on binding Settings.
IReceiveContextSettings receiveContextSettings = binding.GetProperty<IReceiveContextSettings>(new BindingParameterCollection());
if (receiveContextSettings != null)
{
channelDispatcher.ReceiveContextEnabled = receiveContextSettings.Enabled;
}
serviceHost.ChannelDispatchers.Add(channelDispatcher);
} // end foreach "ListenUri/ChannelDispatcher" group
// run service behaviors
for (int i = 0; i < description.Behaviors.Count; i++)
{
IServiceBehavior serviceBehavior = description.Behaviors[i];
serviceBehavior.ApplyDispatchBehavior(description, serviceHost);
}
foreach (KeyValuePair<ListenUriInfo, StuffPerListenUriInfo> stuff in stuffPerListenUriInfo)
{
for (int i = 0; i < stuff.Value.Endpoints.Count; i++)
{
ServiceEndpoint endpoint = stuff.Value.Endpoints[i];
// rediscover which dispatcher goes with this endpoint
Collection<EndpointInfo> infos = endpointInfosPerEndpointAddress[endpoint.Address];
EndpointInfo info = null;
foreach (EndpointInfo ei in infos)
{
if (ei.Endpoint == endpoint)
{
info = ei;
break;
}
}
EndpointDispatcher dispatcher = info.EndpointDispatcher;
// run contract behaviors
for (int k = 0; k < endpoint.Contract.Behaviors.Count; k++)
{
IContractBehavior behavior = endpoint.Contract.Behaviors[k];
behavior.ApplyDispatchBehavior(endpoint.Contract, endpoint, dispatcher.DispatchRuntime);
}
// run endpoint behaviors
BindingInformationEndpointBehavior.Instance.ApplyDispatchBehavior(endpoint, dispatcher);
TransactionContractInformationEndpointBehavior.Instance.ApplyDispatchBehavior(endpoint, dispatcher);
for (int j = 0; j < endpoint.Behaviors.Count; j++)
{
IEndpointBehavior eb = endpoint.Behaviors[j];
eb.ApplyDispatchBehavior(endpoint, dispatcher);
}
// run operation behaviors
DispatcherBuilder.BindOperations(endpoint.Contract, null, dispatcher.DispatchRuntime);
}
}
this.EnsureRequiredRuntimeProperties(endpointInfosPerEndpointAddress);
// Warn about obvious demux conflicts
foreach (Collection<EndpointInfo> endpointInfos in endpointInfosPerEndpointAddress.Values)
{
// all elements of endpointInfos share the same Address (and thus EndpointListener.AddressFilter)
if (endpointInfos.Count > 1)
{
for (int i = 0; i < endpointInfos.Count; i++)
{
for (int j = i + 1; j < endpointInfos.Count; j++)
{
// if not same ListenUri, won't conflict
// if not same ChannelType, may not conflict (some transports demux based on this)
// if they share a ChannelDispatcher, this means same ListenUri and same ChannelType
if (endpointInfos[i].EndpointDispatcher.ChannelDispatcher ==
endpointInfos[j].EndpointDispatcher.ChannelDispatcher)
{
EndpointFilterProvider iProvider = endpointInfos[i].FilterProvider;
EndpointFilterProvider jProvider = endpointInfos[j].FilterProvider;
// if not default EndpointFilterProvider, we won't try to throw, you're on your own
string commonAction;
if (iProvider != null && jProvider != null
&& HaveCommonInitiatingActions(iProvider, jProvider, out commonAction))
{
// you will definitely get a MultipleFiltersMatchedException at runtime,
// so let's go ahead and throw now
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new InvalidOperationException(
SR.GetString(SR.SFxDuplicateInitiatingActionAtSameVia,
endpointInfos[i].Endpoint.ListenUri, commonAction)));
}
}
}
}
}
}
}
void EnsureRequiredRuntimeProperties(Dictionary<EndpointAddress, Collection<EndpointInfo>> endpointInfosPerEndpointAddress)
{
foreach (Collection<EndpointInfo> endpointInfos in endpointInfosPerEndpointAddress.Values)
{
for (int i = 0; i < endpointInfos.Count; i++)
{
DispatchRuntime dispatch = endpointInfos[i].EndpointDispatcher.DispatchRuntime;
if (dispatch.InstanceContextProvider == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxRequiredRuntimePropertyMissing, "InstanceContextProvider")));
}
}
}
}
static EndpointDispatcher BuildDispatcher(ServiceHostBase service,
ServiceDescription serviceDescription,
ServiceEndpoint endpoint,
ContractDescription contractDescription,
EndpointFilterProvider provider)
{
if (service == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("service");
}
if (serviceDescription == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serviceDescription");
}
if (contractDescription == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contractDescription");
}
EndpointAddress address = endpoint.Address;
EndpointDispatcher dispatcher = new EndpointDispatcher(address, contractDescription.Name, contractDescription.Namespace, endpoint.Id, endpoint.InternalIsSystemEndpoint(serviceDescription));
DispatchRuntime dispatch = dispatcher.DispatchRuntime;
if (contractDescription.CallbackContractType != null)
{
dispatch.CallbackClientRuntime.CallbackClientType = contractDescription.CallbackContractType;
dispatch.CallbackClientRuntime.ContractClientType = contractDescription.ContractType;
}
for (int i = 0; i < contractDescription.Operations.Count; i++)
{
OperationDescription operation = contractDescription.Operations[i];
if (!operation.IsServerInitiated())
{
DispatcherBuilder.BuildDispatchOperation(operation, dispatch, provider);
}
else
{
DispatcherBuilder.BuildProxyOperation(operation, dispatch.CallbackClientRuntime);
}
}
//dispatcher.SetSupportedChannels(DispatcherBuilder.GetSupportedChannelTypes(contractDescription));
int filterPriority = 0;
dispatcher.ContractFilter = provider.CreateFilter(out filterPriority);
dispatcher.FilterPriority = filterPriority;
return dispatcher;
}
static void BuildProxyOperation(OperationDescription operation, ClientRuntime parent)
{
ClientOperation child;
if (operation.Messages.Count == 1)
{
child = new ClientOperation(parent, operation.Name, operation.Messages[0].Action);
}
else
{
child = new ClientOperation(parent, operation.Name, operation.Messages[0].Action,
operation.Messages[1].Action);
}
child.TaskMethod = operation.TaskMethod;
child.TaskTResult = operation.TaskTResult;
child.SyncMethod = operation.SyncMethod;
child.BeginMethod = operation.BeginMethod;
child.EndMethod = operation.EndMethod;
child.IsOneWay = operation.IsOneWay;
child.IsTerminating = operation.IsTerminating;
child.IsInitiating = operation.IsInitiating;
child.IsSessionOpenNotificationEnabled = operation.IsSessionOpenNotificationEnabled;
for (int i = 0; i < operation.Faults.Count; i++)
{
FaultDescription fault = operation.Faults[i];
child.FaultContractInfos.Add(new FaultContractInfo(fault.Action, fault.DetailType, fault.ElementName, fault.Namespace, operation.KnownTypes));
}
parent.Operations.Add(child);
}
static void BuildDispatchOperation(OperationDescription operation, DispatchRuntime parent, EndpointFilterProvider provider)
{
string requestAction = operation.Messages[0].Action;
DispatchOperation child = null;
if (operation.IsOneWay)
{
child = new DispatchOperation(parent, operation.Name, requestAction);
}
else
{
string replyAction = operation.Messages[1].Action;
child = new DispatchOperation(parent, operation.Name, requestAction, replyAction);
}
child.HasNoDisposableParameters = operation.HasNoDisposableParameters;
child.IsTerminating = operation.IsTerminating;
child.IsSessionOpenNotificationEnabled = operation.IsSessionOpenNotificationEnabled;
for (int i = 0; i < operation.Faults.Count; i++)
{
FaultDescription fault = operation.Faults[i];
child.FaultContractInfos.Add(new FaultContractInfo(fault.Action, fault.DetailType, fault.ElementName, fault.Namespace, operation.KnownTypes));
}
child.IsInsideTransactedReceiveScope = operation.IsInsideTransactedReceiveScope;
if (provider != null)
{
if (operation.IsInitiating)
{
provider.InitiatingActions.Add(requestAction);
}
}
if (requestAction != MessageHeaders.WildcardAction)
{
parent.Operations.Add(child);
}
else
{
if (parent.HasMatchAllOperation)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxMultipleContractStarOperations0)));
}
parent.UnhandledDispatchOperation = child;
}
}
static void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime clientRuntime)
{
// contract behaviors
ContractDescription contractDescription = serviceEndpoint.Contract;
for (int i = 0; i < contractDescription.Behaviors.Count; i++)
{
IContractBehavior behavior = contractDescription.Behaviors[i];
behavior.ApplyClientBehavior(contractDescription, serviceEndpoint, clientRuntime);
}
// endpoint behaviors
BindingInformationEndpointBehavior.Instance.ApplyClientBehavior(serviceEndpoint, clientRuntime);
TransactionContractInformationEndpointBehavior.Instance.ApplyClientBehavior(serviceEndpoint, clientRuntime);
for (int i = 0; i < serviceEndpoint.Behaviors.Count; i++)
{
IEndpointBehavior behavior = serviceEndpoint.Behaviors[i];
behavior.ApplyClientBehavior(serviceEndpoint, clientRuntime);
}
// operation behaviors
DispatcherBuilder.BindOperations(contractDescription, clientRuntime, null);
}
static void BindOperations(ContractDescription contract, ClientRuntime proxy, DispatchRuntime dispatch)
{
if (!(((proxy == null) != (dispatch == null))))
{
throw Fx.AssertAndThrowFatal("DispatcherBuilder.BindOperations: ((proxy == null) != (dispatch == null))");
}
MessageDirection local = (proxy == null) ? MessageDirection.Input : MessageDirection.Output;
for (int i = 0; i < contract.Operations.Count; i++)
{
OperationDescription operation = contract.Operations[i];
MessageDescription first = operation.Messages[0];
if (first.Direction != local)
{
if (proxy == null)
{
proxy = dispatch.CallbackClientRuntime;
}
ClientOperation proxyOperation = proxy.Operations[operation.Name];
Fx.Assert(proxyOperation != null, "");
for (int j = 0; j < operation.Behaviors.Count; j++)
{
IOperationBehavior behavior = operation.Behaviors[j];
behavior.ApplyClientBehavior(operation, proxyOperation);
}
}
else
{
if (dispatch == null)
{
dispatch = proxy.CallbackDispatchRuntime;
}
DispatchOperation dispatchOperation = null;
if (dispatch.Operations.Contains(operation.Name))
{
dispatchOperation = dispatch.Operations[operation.Name];
}
if (dispatchOperation == null && dispatch.UnhandledDispatchOperation != null && dispatch.UnhandledDispatchOperation.Name == operation.Name)
{
dispatchOperation = dispatch.UnhandledDispatchOperation;
}
if (dispatchOperation != null)
{
for (int j = 0; j < operation.Behaviors.Count; j++)
{
IOperationBehavior behavior = operation.Behaviors[j];
behavior.ApplyDispatchBehavior(operation, dispatchOperation);
}
}
}
}
}
internal static Type[] GetSupportedChannelTypes(ContractDescription contractDescription)
{
if (contractDescription == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("contractDescription"));
}
ChannelRequirements reqs;
ChannelRequirements.ComputeContractRequirements(contractDescription, out reqs);
Type[] supportedChannels = ChannelRequirements.ComputeRequiredChannels(ref reqs);
// supportedChannels is client-side, need to make server-side
for (int i = 0; i < supportedChannels.Length; i++)
{
if (supportedChannels[i] == typeof(IRequestChannel))
{
supportedChannels[i] = typeof(IReplyChannel);
}
else if (supportedChannels[i] == typeof(IRequestSessionChannel))
{
supportedChannels[i] = typeof(IReplySessionChannel);
}
else if (supportedChannels[i] == typeof(IOutputChannel))
{
supportedChannels[i] = typeof(IInputChannel);
}
else if (supportedChannels[i] == typeof(IOutputSessionChannel))
{
supportedChannels[i] = typeof(IInputSessionChannel);
}
else if (supportedChannels[i] == typeof(IDuplexChannel))
{
// no-op; duplex is its own dual
}
else if (supportedChannels[i] == typeof(IDuplexSessionChannel))
{
// no-op; duplex is its own dual
}
else
{
throw Fx.AssertAndThrowFatal("DispatcherBuilder.GetSupportedChannelTypes: Unexpected channel type");
}
}
return supportedChannels;
}
static bool HaveCommonInitiatingActions(EndpointFilterProvider x, EndpointFilterProvider y, out string commonAction)
{
commonAction = null;
foreach (string action in x.InitiatingActions)
{
if (y.InitiatingActions.Contains(action))
{
commonAction = action;
return true;
}
}
return false;
}
class BindingInformationEndpointBehavior : IEndpointBehavior
{
static BindingInformationEndpointBehavior instance;
public static BindingInformationEndpointBehavior Instance
{
get
{
if (instance == null)
{
instance = new BindingInformationEndpointBehavior();
}
return instance;
}
}
public void Validate(ServiceEndpoint serviceEndpoint) { }
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection parameters) { }
public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime behavior)
{
behavior.ManualAddressing = this.IsManualAddressing(serviceEndpoint.Binding);
behavior.EnableFaults = !this.IsMulticast(serviceEndpoint.Binding);
if (serviceEndpoint.Contract.IsDuplex())
{
behavior.CallbackDispatchRuntime.ChannelDispatcher.MessageVersion = serviceEndpoint.Binding.MessageVersion;
}
}
public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher)
{
IBindingRuntimePreferences runtimePreferences = serviceEndpoint.Binding as IBindingRuntimePreferences;
if (runtimePreferences != null)
{
// it is ok to go up to the ChannelDispatcher here, since
// all endpoints that share a ChannelDispatcher also share the same binding
endpointDispatcher.ChannelDispatcher.ReceiveSynchronously = runtimePreferences.ReceiveSynchronously;
}
endpointDispatcher.ChannelDispatcher.ManualAddressing = this.IsManualAddressing(serviceEndpoint.Binding);
endpointDispatcher.ChannelDispatcher.EnableFaults = !this.IsMulticast(serviceEndpoint.Binding);
endpointDispatcher.ChannelDispatcher.MessageVersion = serviceEndpoint.Binding.MessageVersion;
}
bool IsManualAddressing(Binding binding)
{
TransportBindingElement transport = binding.CreateBindingElements().Find<TransportBindingElement>();
if (transport == null)
{
string text = SR.GetString(SR.SFxBindingMustContainTransport2, binding.Name, binding.Namespace);
Exception error = new InvalidOperationException(text);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
}
return transport.ManualAddressing;
}
bool IsMulticast(Binding binding)
{
IBindingMulticastCapabilities multicast = binding.GetProperty<IBindingMulticastCapabilities>(new BindingParameterCollection());
return (multicast != null) && multicast.IsMulticast;
}
}
class TransactionContractInformationEndpointBehavior : IEndpointBehavior
{
static TransactionContractInformationEndpointBehavior instance;
public static TransactionContractInformationEndpointBehavior Instance
{
get
{
if (instance == null)
{
instance = new TransactionContractInformationEndpointBehavior();
}
return instance;
}
}
public void Validate(ServiceEndpoint serviceEndpoint) { }
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection parameters) { }
public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime behavior)
{
behavior.AddTransactionFlowProperties = UsesTransactionFlowProperties(serviceEndpoint.Binding.CreateBindingElements(),
serviceEndpoint.Contract);
}
public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.IgnoreTransactionMessageProperty = !UsesTransactionFlowProperties(
serviceEndpoint.Binding.CreateBindingElements(), serviceEndpoint.Contract);
}
static bool UsesTransactionFlowProperties(BindingElementCollection bindingElements, ContractDescription contract)
{
BindingElementCollection bindingElementCollection = new BindingElementCollection(bindingElements);
TransactionFlowBindingElement txBE = bindingElementCollection.Find<TransactionFlowBindingElement>();
if (txBE == null)
{
return false;
}
return txBE.IsFlowEnabled(contract);
}
}
class SecurityContractInformationEndpointBehavior : IEndpointBehavior
{
bool isForClient;
SecurityContractInformationEndpointBehavior(bool isForClient)
{
this.isForClient = isForClient;
}
static SecurityContractInformationEndpointBehavior serverInstance;
public static SecurityContractInformationEndpointBehavior ServerInstance
{
get
{
if (serverInstance == null)
{
serverInstance = new SecurityContractInformationEndpointBehavior(false);
}
return serverInstance;
}
}
static SecurityContractInformationEndpointBehavior clientInstance;
public static SecurityContractInformationEndpointBehavior ClientInstance
{
get
{
if (clientInstance == null)
{
clientInstance = new SecurityContractInformationEndpointBehavior(true);
}
return clientInstance;
}
}
public void Validate(ServiceEndpoint serviceEndpoint) { }
public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher) { }
public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime behavior) { }
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection parameters)
{
// get Contract info security needs, and put in BindingParameterCollection
ISecurityCapabilities isc = null;
BindingElementCollection elements = endpoint.Binding.CreateBindingElements();
for (int i = 0; i < elements.Count; ++i)
{
if (!(elements[i] is ITransportTokenAssertionProvider))
{
ISecurityCapabilities tmp = elements[i].GetIndividualProperty<ISecurityCapabilities>();
if (tmp != null)
{
isc = tmp;
break;
}
}
}
if (isc != null)
{
// ensure existence of binding parameter
ChannelProtectionRequirements requirements = parameters.Find<ChannelProtectionRequirements>();
if (requirements == null)
{
requirements = new ChannelProtectionRequirements();
parameters.Add(requirements);
}
MessageEncodingBindingElement encoding = elements.Find<MessageEncodingBindingElement>();
// use endpoint.Binding.Version
if (encoding != null && encoding.MessageVersion.Addressing == AddressingVersion.None)
{
// This binding does not support response actions, so...
requirements.Add(ChannelProtectionRequirements.CreateFromContractAndUnionResponseProtectionRequirements(endpoint.Contract, isc, isForClient));
}
else
{
requirements.Add(ChannelProtectionRequirements.CreateFromContract(endpoint.Contract, isc, isForClient));
}
}
}
}
}
}
|