| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- //-----------------------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //-----------------------------------------------------------------------------
- namespace System.ServiceModel.Description
- {
- using System.Collections;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.Runtime;
- using System.Runtime.Diagnostics;
- using System.ServiceModel.Channels;
- using System.ServiceModel.Diagnostics;
- using System.ServiceModel.Dispatcher;
- using System.Xml;
- public class ServiceMetadataBehavior : IServiceBehavior
- {
- public const string MexContractName = "IMetadataExchange";
- internal const string MexContractNamespace = "http://schemas.microsoft.com/2006/04/mex";
- static readonly Uri emptyUri = new Uri(String.Empty, UriKind.Relative);
- bool httpGetEnabled = false;
- bool httpsGetEnabled = false;
- Uri httpGetUrl;
- Uri httpsGetUrl;
- Binding httpGetBinding;
- Binding httpsGetBinding;
- Uri externalMetadataLocation = null;
- MetadataExporter metadataExporter = null;
- static ContractDescription mexContract = null;
- static object thisLock = new object();
- public bool HttpGetEnabled
- {
- get { return this.httpGetEnabled; }
- set { this.httpGetEnabled = value; }
- }
- [TypeConverter(typeof(UriTypeConverter))]
- public Uri HttpGetUrl
- {
- get { return this.httpGetUrl; }
- set
- {
- if (value != null && value.IsAbsoluteUri && value.Scheme != Uri.UriSchemeHttp)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxServiceMetadataBehaviorUrlMustBeHttpOrRelative,
- "HttpGetUrl", Uri.UriSchemeHttp, value.ToString(), value.Scheme));
- }
- this.httpGetUrl = value;
- }
- }
- public bool HttpsGetEnabled
- {
- get { return this.httpsGetEnabled; }
- set { this.httpsGetEnabled = value; }
- }
- [TypeConverter(typeof(UriTypeConverter))]
- public Uri HttpsGetUrl
- {
- get { return this.httpsGetUrl; }
- set
- {
- if (value != null && value.IsAbsoluteUri && value.Scheme != Uri.UriSchemeHttps)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxServiceMetadataBehaviorUrlMustBeHttpOrRelative,
- "HttpsGetUrl", Uri.UriSchemeHttps, value.ToString(), value.Scheme));
- }
- this.httpsGetUrl = value;
- }
- }
- public Binding HttpGetBinding
- {
- get { return this.httpGetBinding; }
- set
- {
- if (value != null)
- {
- if (!value.Scheme.Equals(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxBindingSchemeDoesNotMatch,
- value.Scheme, value.GetType().ToString(), Uri.UriSchemeHttp));
- }
- CustomBinding customBinding = new CustomBinding(value);
- TextMessageEncodingBindingElement textMessageEncodingBindingElement = customBinding.Elements.Find<TextMessageEncodingBindingElement>();
- if (textMessageEncodingBindingElement != null && !textMessageEncodingBindingElement.MessageVersion.IsMatch(MessageVersion.None))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxIncorrectMessageVersion,
- textMessageEncodingBindingElement.MessageVersion.ToString(), MessageVersion.None.ToString()));
- }
- HttpTransportBindingElement httpTransportBindingElement = customBinding.Elements.Find<HttpTransportBindingElement>();
- if (httpTransportBindingElement != null)
- {
- httpTransportBindingElement.Method = "GET";
- }
- this.httpGetBinding = customBinding;
- }
- }
- }
- public Binding HttpsGetBinding
- {
- get { return this.httpsGetBinding; }
- set
- {
- if (value != null)
- {
- if (!value.Scheme.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxBindingSchemeDoesNotMatch,
- value.Scheme, value.GetType().ToString(), Uri.UriSchemeHttps));
- }
- CustomBinding customBinding = new CustomBinding(value);
- TextMessageEncodingBindingElement textMessageEncodingBindingElement = customBinding.Elements.Find<TextMessageEncodingBindingElement>();
- if (textMessageEncodingBindingElement != null && !textMessageEncodingBindingElement.MessageVersion.IsMatch(MessageVersion.None))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxIncorrectMessageVersion,
- textMessageEncodingBindingElement.MessageVersion.ToString(), MessageVersion.None.ToString()));
- }
- HttpsTransportBindingElement httpsTransportBindingElement = customBinding.Elements.Find<HttpsTransportBindingElement>();
- if (httpsTransportBindingElement != null)
- {
- httpsTransportBindingElement.Method = "GET";
- }
- this.httpsGetBinding = customBinding;
- }
- }
- }
- [TypeConverter(typeof(UriTypeConverter))]
- public Uri ExternalMetadataLocation
- {
- get { return this.externalMetadataLocation; }
- set
- {
- if (value != null && value.IsAbsoluteUri && !(value.Scheme == Uri.UriSchemeHttp || value.Scheme == Uri.UriSchemeHttps))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("ExternalMetadataLocation", SR.GetString(SR.SFxBadMetadataLocationUri, value.OriginalString, value.Scheme));
- }
- this.externalMetadataLocation = value;
- }
- }
- public MetadataExporter MetadataExporter
- {
- get
- {
- if (this.metadataExporter == null)
- this.metadataExporter = new WsdlExporter();
- return this.metadataExporter;
- }
- set
- {
- this.metadataExporter = value;
- }
- }
- static internal ContractDescription MexContract
- {
- get
- {
- EnsureMexContractDescription();
- return ServiceMetadataBehavior.mexContract;
- }
- }
- void IServiceBehavior.Validate(ServiceDescription description, ServiceHostBase serviceHostBase)
- {
- }
- void IServiceBehavior.AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters)
- {
- }
- void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
- {
- if (description == null)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("description");
- if (serviceHostBase == null)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serviceHostBase");
- ApplyBehavior(description, serviceHostBase);
- }
- void ApplyBehavior(ServiceDescription description, ServiceHostBase host)
- {
- ServiceMetadataExtension mex = ServiceMetadataExtension.EnsureServiceMetadataExtension(description, host);
- SetExtensionProperties(description, host, mex);
- CustomizeMetadataEndpoints(description, host, mex);
- CreateHttpGetEndpoints(description, host, mex);
- }
- private void CreateHttpGetEndpoints(ServiceDescription description, ServiceHostBase host, ServiceMetadataExtension mex)
- {
- bool httpDispatcherEnabled = false;
- bool httpsDispatcherEnabled = false;
- if (this.httpGetEnabled)
- {
- httpDispatcherEnabled = EnsureGetDispatcher(host, mex, this.httpGetUrl, Uri.UriSchemeHttp);
- }
- if (this.httpsGetEnabled)
- {
- httpsDispatcherEnabled = EnsureGetDispatcher(host, mex, this.httpsGetUrl, Uri.UriSchemeHttps);
- }
- if (!httpDispatcherEnabled && !httpsDispatcherEnabled)
- {
- if (this.httpGetEnabled)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceMetadataBehaviorNoHttpBaseAddress)));
- }
- if (this.httpsGetEnabled)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceMetadataBehaviorNoHttpsBaseAddress)));
- }
- }
- }
- static bool EnsureGetDispatcher(ServiceHostBase host, ServiceMetadataExtension mex, Uri url, string scheme)
- {
- Uri address = host.GetVia(scheme, url == null ? new Uri(string.Empty, UriKind.Relative) : url);
- if (address != null)
- {
- ChannelDispatcher channelDispatcher = mex.EnsureGetDispatcher(address, false /* isServiceDebugBehavior */);
- ((ServiceMetadataExtension.HttpGetImpl)channelDispatcher.Endpoints[0].DispatchRuntime.SingletonInstanceContext.UserObject).GetWsdlEnabled = true;
- return true;
- }
- return false;
- }
- void SetExtensionProperties(ServiceDescription description, ServiceHostBase host, ServiceMetadataExtension mex)
- {
- mex.ExternalMetadataLocation = this.ExternalMetadataLocation;
- mex.Initializer = new MetadataExtensionInitializer(this, description, host);
- mex.HttpGetEnabled = this.httpGetEnabled;
- mex.HttpsGetEnabled = this.httpsGetEnabled;
- mex.HttpGetUrl = host.GetVia(Uri.UriSchemeHttp, this.httpGetUrl == null ? new Uri(string.Empty, UriKind.Relative) : this.httpGetUrl);
- mex.HttpsGetUrl = host.GetVia(Uri.UriSchemeHttps, this.httpsGetUrl == null ? new Uri(string.Empty, UriKind.Relative) : this.httpsGetUrl);
- mex.HttpGetBinding = this.httpGetBinding;
- mex.HttpsGetBinding = this.httpsGetBinding;
- UseRequestHeadersForMetadataAddressBehavior dynamicUpdateBehavior = description.Behaviors.Find<UseRequestHeadersForMetadataAddressBehavior>();
- if (dynamicUpdateBehavior != null)
- {
- mex.UpdateAddressDynamically = true;
- mex.UpdatePortsByScheme = new Dictionary<string, int>(dynamicUpdateBehavior.DefaultPortsByScheme);
- }
- foreach (ChannelDispatcherBase dispatcherBase in host.ChannelDispatchers)
- {
- ChannelDispatcher dispatcher = dispatcherBase as ChannelDispatcher;
- if (dispatcher != null && IsMetadataTransferDispatcher(description, dispatcher))
- {
- mex.MexEnabled = true;
- mex.MexUrl = dispatcher.Listener.Uri;
- if (dynamicUpdateBehavior != null)
- {
- foreach (EndpointDispatcher endpointDispatcher in dispatcher.Endpoints)
- {
- if (!endpointDispatcher.AddressFilterSetExplicit)
- {
- endpointDispatcher.AddressFilter = new MatchAllMessageFilter();
- }
- }
- }
- break;
- }
- }
- }
- private static void CustomizeMetadataEndpoints(ServiceDescription description, ServiceHostBase host, ServiceMetadataExtension mex)
- {
- for (int i = 0; i < host.ChannelDispatchers.Count; i++)
- {
- ChannelDispatcher channelDispatcher = host.ChannelDispatchers[i] as ChannelDispatcher;
- if (channelDispatcher != null && ServiceMetadataBehavior.IsMetadataTransferDispatcher(description, channelDispatcher))
- {
- if (channelDispatcher.Endpoints.Count != 1)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new InvalidOperationException(SR.GetString(SR.SFxServiceMetadataBehaviorInstancingError, channelDispatcher.Listener.Uri, channelDispatcher.CreateContractListString())));
- }
- DispatchRuntime dispatcher = channelDispatcher.Endpoints[0].DispatchRuntime;
- // set instancing
- dispatcher.InstanceContextProvider =
- InstanceContextProviderBase.GetProviderForMode(InstanceContextMode.Single, dispatcher);
- bool isListeningOnHttps = channelDispatcher.Listener.Uri.Scheme == Uri.UriSchemeHttps;
- Uri listenUri = channelDispatcher.Listener.Uri;
- ServiceMetadataExtension.WSMexImpl impl = new ServiceMetadataExtension.WSMexImpl(mex, isListeningOnHttps, listenUri);
- dispatcher.SingletonInstanceContext = new InstanceContext(host, impl, false);
- }
- }
- }
- static EndpointDispatcher GetListenerByID(SynchronizedCollection<ChannelDispatcherBase> channelDispatchers, string id)
- {
- for (int i = 0; i < channelDispatchers.Count; ++i)
- {
- ChannelDispatcher channelDispatcher = channelDispatchers[i] as ChannelDispatcher;
- if (channelDispatcher != null)
- {
- for (int j = 0; j < channelDispatcher.Endpoints.Count; ++j)
- {
- EndpointDispatcher endpointDispatcher = channelDispatcher.Endpoints[j];
- if (endpointDispatcher.Id == id)
- return endpointDispatcher;
- }
- }
- }
- return null;
- }
- internal static bool IsMetadataDispatcher(ServiceDescription description, ChannelDispatcher channelDispatcher)
- {
- foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
- {
- if (IsMetadataTransferDispatcher(description, channelDispatcher)
- || IsHttpGetMetadataDispatcher(description, channelDispatcher))
- return true;
- }
- return false;
- }
- static bool IsMetadataTransferDispatcher(ServiceDescription description, ChannelDispatcher channelDispatcher)
- {
- if (BehaviorMissingObjectNullOrServiceImplements(description, channelDispatcher))
- return false;
- foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
- {
- if (endpointDispatcher.ContractName == ServiceMetadataBehavior.MexContractName
- && endpointDispatcher.ContractNamespace == ServiceMetadataBehavior.MexContractNamespace)
- return true;
- }
- return false;
- }
- private static bool BehaviorMissingObjectNullOrServiceImplements(ServiceDescription description, object obj)
- {
- if (obj == null)
- return true;
- if (description.Behaviors != null && description.Behaviors.Find<ServiceMetadataBehavior>() == null)
- return true;
- if (description.ServiceType != null && description.ServiceType.GetInterface(typeof(IMetadataExchange).Name) != null)
- return true;
- return false;
- }
- internal static bool IsHttpGetMetadataDispatcher(ServiceDescription description, ChannelDispatcher channelDispatcher)
- {
- if (description.Behaviors.Find<ServiceMetadataBehavior>() == null)
- return false;
- foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
- {
- if (endpointDispatcher.ContractName == ServiceMetadataExtension.HttpGetImpl.ContractName
- && endpointDispatcher.ContractNamespace == ServiceMetadataExtension.HttpGetImpl.ContractNamespace)
- return true;
- }
- return false;
- }
- internal static bool IsMetadataEndpoint(ServiceDescription description, ServiceEndpoint endpoint)
- {
- if (BehaviorMissingObjectNullOrServiceImplements(description, endpoint))
- return false;
- return IsMetadataEndpoint(endpoint);
- }
- static bool IsMetadataEndpoint(ServiceEndpoint endpoint)
- {
- return (endpoint.Contract.Name == ServiceMetadataBehavior.MexContractName
- && endpoint.Contract.Namespace == ServiceMetadataBehavior.MexContractNamespace);
- }
- internal static bool IsMetadataImplementedType(ServiceDescription description, Type type)
- {
- if (BehaviorMissingObjectNullOrServiceImplements(description, type))
- return false;
- return type == typeof(IMetadataExchange);
- }
- internal static bool IsMetadataImplementedType(Type type)
- {
- return type == typeof(IMetadataExchange);
- }
- internal void AddImplementedContracts(ServiceHostBase.ServiceAndBehaviorsContractResolver resolver)
- {
- if (!resolver.BehaviorContracts.ContainsKey(MexContractName))
- {
- resolver.BehaviorContracts.Add(MexContractName, ServiceMetadataBehavior.MexContract);
- }
- }
- static void EnsureMexContractDescription()
- {
- if (ServiceMetadataBehavior.mexContract == null)
- {
- lock (thisLock)
- {
- if (ServiceMetadataBehavior.mexContract == null)
- {
- ServiceMetadataBehavior.mexContract = CreateMexContract();
- }
- }
- }
- }
- static ContractDescription CreateMexContract()
- {
- ContractDescription mexContract = ContractDescription.GetContract(typeof(IMetadataExchange));
- foreach (OperationDescription operation in mexContract.Operations)
- {
- operation.Behaviors.Find<OperationBehaviorAttribute>().Impersonation = ImpersonationOption.Allowed;
- }
- mexContract.Behaviors.Add(new ServiceMetadataContractBehavior(true));
- return mexContract;
- }
- internal class MetadataExtensionInitializer
- {
- ServiceMetadataBehavior behavior;
- ServiceDescription description;
- ServiceHostBase host;
- Exception metadataGenerationException = null;
- internal MetadataExtensionInitializer(ServiceMetadataBehavior behavior, ServiceDescription description, ServiceHostBase host)
- {
- this.behavior = behavior;
- this.description = description;
- this.host = host;
- }
- internal MetadataSet GenerateMetadata()
- {
- if (this.behavior.ExternalMetadataLocation == null || this.behavior.ExternalMetadataLocation.ToString() == string.Empty)
- {
- if (this.metadataGenerationException != null)
- throw this.metadataGenerationException;
- try
- {
- MetadataExporter exporter = this.behavior.MetadataExporter;
- XmlQualifiedName serviceName = new XmlQualifiedName(this.description.Name, this.description.Namespace);
- Collection<ServiceEndpoint> exportedEndpoints = new Collection<ServiceEndpoint>();
- foreach (ServiceEndpoint endpoint in this.description.Endpoints)
- {
- ServiceMetadataContractBehavior contractBehavior = endpoint.Contract.Behaviors.Find<ServiceMetadataContractBehavior>();
- // if contract behavior exists, generate metadata when the behavior allows metadata generation
- // if contract behavior doesn't exist, generate metadata only for non system endpoints
- if ((contractBehavior != null && !contractBehavior.MetadataGenerationDisabled) ||
- (contractBehavior == null && !endpoint.IsSystemEndpoint))
- {
- EndpointAddress address = null;
- EndpointDispatcher endpointDispatcher = GetListenerByID(this.host.ChannelDispatchers, endpoint.Id);
- if (endpointDispatcher != null)
- {
- address = endpointDispatcher.EndpointAddress;
- }
- ServiceEndpoint exportedEndpoint = new ServiceEndpoint(endpoint.Contract);
- exportedEndpoint.Binding = endpoint.Binding;
- exportedEndpoint.Name = endpoint.Name;
- exportedEndpoint.Address = address;
- foreach (IEndpointBehavior behavior in endpoint.Behaviors)
- {
- exportedEndpoint.Behaviors.Add(behavior);
- }
- exportedEndpoints.Add(exportedEndpoint);
- }
- }
- WsdlExporter wsdlExporter = exporter as WsdlExporter;
- if (wsdlExporter != null)
- {
- // Pass the BindingParameterCollection into the ExportEndpoints method so that the binding parameters can be using to export WSDL correctly.
- // The binding parameters are used in BuildChannelListener, during which they can modify the configuration of the channel in ways that might have to
- // be communicated in the WSDL. For example, in the case of Multi-Auth, the AuthenticationSchemesBindingParameter is used during BuildChannelListener
- // to set the AuthenticationSchemes supported by the virtual directory on the HttpTransportBindingElement. These authentication schemes also need
- // to be in the WSDL, so that clients know what authentication schemes are supported by the service. (see CSDMain #180381)
- Fx.Assert(this.host != null, "ServiceHostBase field on MetadataExtensionInitializer should never be null.");
- wsdlExporter.ExportEndpoints(exportedEndpoints, serviceName, this.host.GetBindingParameters(exportedEndpoints));
- }
- else
- {
- foreach (ServiceEndpoint endpoint in exportedEndpoints)
- {
- exporter.ExportEndpoint(endpoint);
- }
- }
- if (exporter.Errors.Count > 0 && DiagnosticUtility.ShouldTraceWarning)
- {
- TraceWsdlExportErrors(exporter);
- }
- return exporter.GetGeneratedMetadata();
- }
- catch (Exception e)
- {
- this.metadataGenerationException = e;
- throw;
- }
- }
- return null;
- }
- static void TraceWsdlExportErrors(MetadataExporter exporter)
- {
- foreach (MetadataConversionError error in exporter.Errors)
- {
- if (DiagnosticUtility.ShouldTraceWarning)
- {
- Hashtable h = new Hashtable(2)
- {
- { "IsWarning", error.IsWarning },
- { "Message", error.Message }
- };
- TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.WsmexNonCriticalWsdlExportError,
- SR.GetString(SR.TraceCodeWsmexNonCriticalWsdlExportError), new DictionaryTraceRecord(h), null, null);
- }
- }
- }
- }
- }
- }
|