//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.ServiceModel { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Net; using System.Runtime; using System.Security; using System.ServiceModel.Administration; using System.ServiceModel.Channels; using System.ServiceModel.Configuration; using System.ServiceModel.Description; using System.ServiceModel.Diagnostics; using System.ServiceModel.Dispatcher; using System.Text; using System.Runtime.Diagnostics; using System.Threading; using System.ServiceModel.Activation; using System.ServiceModel.Diagnostics.Application; using System.Reflection; using System.Linq.Expressions; public abstract class ServiceHostBase : CommunicationObject, IExtensibleObject, IDisposable { internal static readonly Uri EmptyUri = new Uri(string.Empty, UriKind.RelativeOrAbsolute); bool initializeDescriptionHasFinished; UriSchemeKeyedCollection baseAddresses; ChannelDispatcherCollection channelDispatchers; TimeSpan closeTimeout = ServiceDefaults.ServiceHostCloseTimeout; ServiceDescription description; ExtensionCollection extensions; ReadOnlyCollection externalBaseAddresses; IDictionary implementedContracts; IInstanceContextManager instances; TimeSpan openTimeout = ServiceDefaults.OpenTimeout; ServicePerformanceCountersBase servicePerformanceCounters; DefaultPerformanceCounters defaultPerformanceCounters; ServiceThrottle serviceThrottle; ServiceCredentials readOnlyCredentials; ServiceAuthorizationBehavior readOnlyAuthorization; ServiceAuthenticationBehavior readOnlyAuthentication; Dictionary> endpointsByListenUriInfo; int busyCount; EventTraceActivity eventTraceActivity; internal event EventHandler BusyCountIncremented; public event EventHandler UnknownMessageReceived; protected ServiceHostBase() { TraceUtility.SetEtwProviderId(); this.baseAddresses = new UriSchemeKeyedCollection(this.ThisLock); this.channelDispatchers = new ChannelDispatcherCollection(this, this.ThisLock); this.extensions = new ExtensionCollection(this, this.ThisLock); this.instances = new InstanceContextManager(this.ThisLock); this.serviceThrottle = new ServiceThrottle(this); this.TraceOpenAndClose = true; this.Faulted += new EventHandler(OnServiceHostFaulted); } internal EventTraceActivity EventTraceActivity { get { if (this.eventTraceActivity == null) { this.eventTraceActivity = new EventTraceActivity(); } return eventTraceActivity; } } public ServiceAuthorizationBehavior Authorization { get { if (this.Description == null) { return null; } else if (this.State == CommunicationState.Created || this.State == CommunicationState.Opening) { return EnsureAuthorization(this.Description); } else { return this.readOnlyAuthorization; } } } public ServiceAuthenticationBehavior Authentication { get { if (this.Description == null) { return null; } else if (this.State == CommunicationState.Created || this.State == CommunicationState.Opening) { return EnsureAuthentication(this.Description); } else { return this.readOnlyAuthentication; } } } public ReadOnlyCollection BaseAddresses { get { externalBaseAddresses = new ReadOnlyCollection(new List(this.baseAddresses)); return externalBaseAddresses; } } public ChannelDispatcherCollection ChannelDispatchers { get { return this.channelDispatchers; } } public TimeSpan CloseTimeout { get { return this.closeTimeout; } set { if (value < TimeSpan.Zero) { string message = SR.GetString(SR.SFxTimeoutOutOfRange0); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", message)); } if (TimeoutHelper.IsTooLarge(value)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.SFxTimeoutOutOfRangeTooBig))); } lock (this.ThisLock) { this.ThrowIfClosedOrOpened(); this.closeTimeout = value; } } } internal ServicePerformanceCountersBase Counters { get { return this.servicePerformanceCounters; } set { this.servicePerformanceCounters = value; this.serviceThrottle.SetServicePerformanceCounters(this.servicePerformanceCounters); } } internal DefaultPerformanceCounters DefaultCounters { get { return this.defaultPerformanceCounters; } set { this.defaultPerformanceCounters = value; } } public ServiceCredentials Credentials { get { if (this.Description == null) { return null; } else if (this.State == CommunicationState.Created || this.State == CommunicationState.Opening) { return EnsureCredentials(this.Description); } else { return this.readOnlyCredentials; } } } protected override TimeSpan DefaultCloseTimeout { get { return this.CloseTimeout; } } protected override TimeSpan DefaultOpenTimeout { get { return this.OpenTimeout; } } public ServiceDescription Description { get { return this.description; } } public IExtensionCollection Extensions { get { return this.extensions; } } protected internal IDictionary ImplementedContracts { get { return this.implementedContracts; } } internal UriSchemeKeyedCollection InternalBaseAddresses { get { return this.baseAddresses; } } public int ManualFlowControlLimit { get { return this.ServiceThrottle.ManualFlowControlLimit; } set { this.ServiceThrottle.ManualFlowControlLimit = value; } } public TimeSpan OpenTimeout { get { return this.openTimeout; } set { if (value < TimeSpan.Zero) { string message = SR.GetString(SR.SFxTimeoutOutOfRange0); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", message)); } if (TimeoutHelper.IsTooLarge(value)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SR.GetString(SR.SFxTimeoutOutOfRangeTooBig))); } lock (this.ThisLock) { this.ThrowIfClosedOrOpened(); this.openTimeout = value; } } } internal ServiceThrottle ServiceThrottle { get { return this.serviceThrottle; } } internal virtual object DisposableInstance { get { return null; } } internal Dictionary> EndpointsByListenUriInfo { get { if (this.endpointsByListenUriInfo == null) { this.endpointsByListenUriInfo = this.GetEndpointsByListenUriInfo(); } return this.endpointsByListenUriInfo; } } Dictionary> GetEndpointsByListenUriInfo() { Dictionary> endpointDictionary = new Dictionary>(); foreach (ServiceEndpoint endpoint in this.Description.Endpoints) { DispatcherBuilder.ListenUriInfo listenUriInfo = DispatcherBuilder.GetListenUriInfoForEndpoint(this, endpoint); if (!endpointDictionary.ContainsKey(listenUriInfo)) { endpointDictionary.Add(listenUriInfo, new Collection()); } endpointDictionary[listenUriInfo].Add(endpoint); } return endpointDictionary; } protected void AddBaseAddress(Uri baseAddress) { if (this.initializeDescriptionHasFinished) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( SR.GetString(SR.SFxCannotCallAddBaseAddress))); } this.baseAddresses.Add(baseAddress); } public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address) { return this.AddServiceEndpoint(implementedContract, binding, address, (Uri)null); } public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri) { if (address == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("address")); } ServiceEndpoint endpoint = this.AddServiceEndpoint(implementedContract, binding, new Uri(address, UriKind.RelativeOrAbsolute)); if (listenUri != null) { endpoint.UnresolvedListenUri = listenUri; listenUri = MakeAbsoluteUri(listenUri, binding); endpoint.ListenUri = listenUri; } return endpoint; } public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address) { return this.AddServiceEndpoint(implementedContract, binding, address, (Uri)null); } public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri) { if (address == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("address")); } if (binding == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("binding")); } if (implementedContract == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("implementedContract")); } if (this.State != CommunicationState.Created && this.State != CommunicationState.Opening) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotAddEndpointAfterOpen))); } if (this.Description == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotAddEndpointWithoutDescription))); } Uri via = this.MakeAbsoluteUri(address, binding); ConfigLoader configLoader = new ConfigLoader(GetContractResolver(this.implementedContracts)); ContractDescription contract = configLoader.LookupContract(implementedContract, this.Description.Name); ServiceEndpoint serviceEndpoint = new ServiceEndpoint(contract, binding, new EndpointAddress(via)); this.Description.Endpoints.Add(serviceEndpoint); serviceEndpoint.UnresolvedAddress = address; if (listenUri != null) { serviceEndpoint.UnresolvedListenUri = listenUri; listenUri = MakeAbsoluteUri(listenUri, binding); serviceEndpoint.ListenUri = listenUri; } return serviceEndpoint; } public virtual void AddServiceEndpoint(ServiceEndpoint endpoint) { if (endpoint == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint"); } if (this.State != CommunicationState.Created && this.State != CommunicationState.Opening) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotAddEndpointAfterOpen))); } if (this.Description == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotAddEndpointWithoutDescription))); } if (endpoint.Address == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxEndpointAddressNotSpecified)); } if (endpoint.Contract == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxEndpointContractNotSpecified)); } if (endpoint.Binding == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxEndpointBindingNotSpecified)); } if (!endpoint.IsSystemEndpoint || endpoint.Contract.ContractType == typeof(IMetadataExchange)) { ConfigLoader loader = new ConfigLoader(GetContractResolver(this.implementedContracts)); loader.LookupContract(endpoint.Contract.ConfigurationName, this.Description.Name); } this.Description.Endpoints.Add(endpoint); } public void SetEndpointAddress(ServiceEndpoint endpoint, string relativeAddress) { if (endpoint == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint"); } if (relativeAddress == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("relativeAddress"); } if (endpoint.Binding == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SFxEndpointBindingNotSpecified)); } Uri absoluteUri = MakeAbsoluteUri(new Uri(relativeAddress, UriKind.Relative), endpoint.Binding); endpoint.Address = new EndpointAddress(absoluteUri); } internal Uri MakeAbsoluteUri(Uri relativeOrAbsoluteUri, Binding binding) { return MakeAbsoluteUri(relativeOrAbsoluteUri, binding, this.InternalBaseAddresses); } internal static Uri MakeAbsoluteUri(Uri relativeOrAbsoluteUri, Binding binding, UriSchemeKeyedCollection baseAddresses) { Uri result = relativeOrAbsoluteUri; if (!result.IsAbsoluteUri) { if (binding.Scheme == string.Empty) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxCustomBindingWithoutTransport))); } result = GetVia(binding.Scheme, result, baseAddresses); if (result == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxEndpointNoMatchingScheme, binding.Scheme, binding.Name, GetBaseAddressSchemes(baseAddresses)))); } } return result; } protected virtual void ApplyConfiguration() { if (this.Description == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotApplyConfigurationWithoutDescription))); } ConfigLoader configLoader = new ConfigLoader(GetContractResolver(implementedContracts)); // Call the overload of LoadConfigurationSectionInternal which looks up the serviceElement from ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None) LoadConfigurationSectionInternal(configLoader, this.Description, this.Description.ConfigurationName); EnsureAuthenticationAuthorizationDebug(this.Description); } internal void EnsureAuthenticationAuthorizationDebug(ServiceDescription description) { EnsureAuthentication(description); EnsureAuthorization(description); EnsureDebug(description); } public virtual ReadOnlyCollection AddDefaultEndpoints() { List defaultEndpoints = new List(); foreach (Uri baseAddress in this.InternalBaseAddresses) { ProtocolMappingItem protocolMappingItem = ConfigLoader.LookupProtocolMapping(baseAddress.Scheme); if (protocolMappingItem != null) { Binding defaultBinding = ConfigLoader.LookupBinding(protocolMappingItem.Binding, protocolMappingItem.BindingConfiguration); if (defaultBinding != null) { AddDefaultEndpoints(defaultBinding, defaultEndpoints); } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Exception(SR.GetString(SR.BindingProtocolMappingNotDefined, baseAddress.Scheme))); } } } if (DiagnosticUtility.ShouldTraceInformation && defaultEndpoints.Count > 0) { Dictionary dictionary = new Dictionary(); dictionary["ServiceConfigurationName"] = this.description.ConfigurationName; TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.DefaultEndpointsAdded, SR.GetString(SR.TraceCodeDefaultEndpointsAdded), new DictionaryTraceRecord(dictionary)); } return new ReadOnlyCollection(defaultEndpoints); } internal virtual void AddDefaultEndpoints(Binding defaultBinding, List defaultEndpoints) { } internal virtual void BindInstance(InstanceContext instance) { this.instances.Add(instance); if (null != this.servicePerformanceCounters) { lock (this.ThisLock) { if (null != this.servicePerformanceCounters) { this.servicePerformanceCounters.ServiceInstanceCreated(); } } } } void IDisposable.Dispose() { Close(); } protected abstract ServiceDescription CreateDescription(out IDictionary implementedContracts); protected virtual void InitializeRuntime() { if (this.Description == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotInitializeRuntimeWithoutDescription))); } if (this.Description.Endpoints.Count == 0) { this.AddDefaultEndpoints(); } this.EnsureAuthenticationSchemes(); DispatcherBuilder dispatcherBuilder = new DispatcherBuilder(); dispatcherBuilder.InitializeServiceHost(description, this); SecurityValidationBehavior.Instance.AfterBuildTimeValidation(description); } internal virtual void AfterInitializeRuntime(TimeSpan timeout) { } internal virtual IAsyncResult BeginAfterInitializeRuntime(TimeSpan timeout, AsyncCallback callback, object state) { return new CompletedAsyncResult(callback, state); } internal virtual void EndAfterInitializeRuntime(IAsyncResult result) { CompletedAsyncResult.End(result); } ServiceAuthorizationBehavior EnsureAuthorization(ServiceDescription description) { Fx.Assert(this.State == CommunicationState.Created || this.State == CommunicationState.Opening, ""); ServiceAuthorizationBehavior a = description.Behaviors.Find(); if (a == null) { a = new ServiceAuthorizationBehavior(); description.Behaviors.Add(a); } return a; } ServiceAuthenticationBehavior EnsureAuthentication(ServiceDescription description) { Fx.Assert(this.State == CommunicationState.Created || this.State == CommunicationState.Opening, ""); ServiceAuthenticationBehavior a = description.Behaviors.Find(); if (a == null) { a = new ServiceAuthenticationBehavior(); description.Behaviors.Add(a); } return a; } ServiceDebugBehavior EnsureDebug(ServiceDescription description) { Fx.Assert(this.State == CommunicationState.Created || this.State == CommunicationState.Opening, ""); ServiceDebugBehavior m = description.Behaviors.Find(); if (m == null) { m = new ServiceDebugBehavior(); description.Behaviors.Add(m); } return m; } ServiceCredentials EnsureCredentials(ServiceDescription description) { Fx.Assert(this.State == CommunicationState.Created || this.State == CommunicationState.Opening, ""); ServiceCredentials c = description.Behaviors.Find(); if (c == null) { c = new ServiceCredentials(); description.Behaviors.Add(c); } return c; } internal void FaultInternal() { this.Fault(); } internal string GetBaseAddressSchemes() { return GetBaseAddressSchemes(baseAddresses); } internal static String GetBaseAddressSchemes(UriSchemeKeyedCollection uriSchemeKeyedCollection) { StringBuilder buffer = new StringBuilder(); bool firstScheme = true; foreach (Uri address in uriSchemeKeyedCollection) { if (firstScheme) { buffer.Append(address.Scheme); firstScheme = false; } else { buffer.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator).Append(address.Scheme); } } return buffer.ToString(); } internal BindingParameterCollection GetBindingParameters() { return DispatcherBuilder.GetBindingParameters(this, new Collection()); } internal BindingParameterCollection GetBindingParameters(ServiceEndpoint inputEndpoint) { Collection endpoints; if (inputEndpoint == null) { endpoints = new Collection(); } else if (!this.EndpointsByListenUriInfo.TryGetValue(DispatcherBuilder.GetListenUriInfoForEndpoint(this, inputEndpoint), out endpoints) || !endpoints.Contains(inputEndpoint)) { endpoints = new Collection(); endpoints.Add(inputEndpoint); } return DispatcherBuilder.GetBindingParameters(this, endpoints); } internal BindingParameterCollection GetBindingParameters(Collection endpoints) { return DispatcherBuilder.GetBindingParameters(this, endpoints); } internal ReadOnlyCollection GetInstanceContexts() { return Array.AsReadOnly(this.instances.ToArray()); } internal virtual IContractResolver GetContractResolver(IDictionary implementedContracts) { ServiceAndBehaviorsContractResolver resolver = new ServiceAndBehaviorsContractResolver(new ImplementedContractsContractResolver(implementedContracts)); resolver.AddBehaviorContractsToResolver(this.description == null ? null : this.description.Behaviors); return resolver; } internal static Uri GetUri(Uri baseUri, Uri relativeUri) { return GetUri(baseUri, relativeUri.OriginalString); } internal static Uri GetUri(Uri baseUri, string path) { if (path.StartsWith("/", StringComparison.Ordinal) || path.StartsWith("\\", StringComparison.Ordinal)) { int i = 1; for (; i < path.Length; ++i) { if (path[i] != '/' && path[i] != '\\') { break; } } path = path.Substring(i); } // VSWhidbey#541152: new Uri(Uri, string.Empty) is broken if (path.Length == 0) return baseUri; if (!baseUri.AbsoluteUri.EndsWith("/", StringComparison.Ordinal)) { baseUri = new Uri(baseUri.AbsoluteUri + "/"); } return new Uri(baseUri, path); } internal Uri GetVia(string scheme, Uri address) { return ServiceHost.GetVia(scheme, address, InternalBaseAddresses); } internal static Uri GetVia(string scheme, Uri address, UriSchemeKeyedCollection baseAddresses) { Uri via = address; if (!via.IsAbsoluteUri) { if (!baseAddresses.Contains(scheme)) { return null; } via = GetUri(baseAddresses[scheme], address); } return via; } public int IncrementManualFlowControlLimit(int incrementBy) { return this.ServiceThrottle.IncrementManualFlowControlLimit(incrementBy); } protected void InitializeDescription(UriSchemeKeyedCollection baseAddresses) { foreach (Uri baseAddress in baseAddresses) { this.baseAddresses.Add(baseAddress); } IDictionary implementedContracts = null; ServiceDescription description = CreateDescription(out implementedContracts); this.description = description; this.implementedContracts = implementedContracts; ApplyConfiguration(); this.initializeDescriptionHasFinished = true; } protected void LoadConfigurationSection(ServiceElement serviceSection) { if (serviceSection == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serviceSection"); } if (this.Description == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotLoadConfigurationSectionWithoutDescription))); } ConfigLoader configLoader = new ConfigLoader(GetContractResolver(this.ImplementedContracts)); LoadConfigurationSectionInternal(configLoader, this.Description, serviceSection); } internal void LoadConfigurationSectionHelper(Uri baseAddress) { this.AddBaseAddress(baseAddress); } [Fx.Tag.SecurityNote(Critical = "Calls LookupService which is critical.", Safe = "Doesn't leak ServiceElement out of SecurityCritical code.")] [SecuritySafeCritical] void LoadConfigurationSectionInternal(ConfigLoader configLoader, ServiceDescription description, string configurationName) { ServiceElement serviceSection = configLoader.LookupService(configurationName); LoadConfigurationSectionInternal(configLoader, description, serviceSection); } [Fx.Tag.SecurityNote(Critical = "Handles a ServiceElement, which should not be leaked out of SecurityCritical code.", Safe = "Doesn't leak ServiceElement out of SecurityCritical code.")] [SecuritySafeCritical] void LoadConfigurationSectionInternal(ConfigLoader configLoader, ServiceDescription description, ServiceElement serviceSection) { // caller must validate arguments before calling configLoader.LoadServiceDescription(this, description, serviceSection, this.LoadConfigurationSectionHelper); } protected override void OnAbort() { this.instances.Abort(); foreach (ChannelDispatcherBase dispatcher in this.ChannelDispatchers) { if (dispatcher.Listener != null) { dispatcher.Listener.Abort(); } dispatcher.Abort(); } ThreadTrace.StopTracing(); } internal void OnAddChannelDispatcher(ChannelDispatcherBase channelDispatcher) { lock (this.ThisLock) { this.ThrowIfClosedOrOpened(); channelDispatcher.AttachInternal(this); channelDispatcher.Faulted += new EventHandler(OnChannelDispatcherFaulted); } } protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state) { return new CloseAsyncResult(timeout, callback, state, this); } void OnBeginOpen() { this.TraceServiceHostOpenStart(); this.TraceBaseAddresses(); MessageLogger.EnsureInitialized(); //force config validation instead of waiting for the first message exchange InitializeRuntime(); } protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) { this.OnBeginOpen(); return new OpenAsyncResult(this, timeout, callback, state); } IAsyncResult BeginOpenChannelDispatchers(TimeSpan timeout, AsyncCallback callback, object state) { return new OpenCollectionAsyncResult(timeout, callback, state, this.SnapshotChannelDispatchers()); } protected override void OnClose(TimeSpan timeout) { try { TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); if (ManagementExtension.IsEnabled && null != this.Description) { ManagementExtension.OnServiceClosing(this); } for (int i = 0; i < this.ChannelDispatchers.Count; i++) { ChannelDispatcherBase dispatcher = this.ChannelDispatchers[i]; if (dispatcher.Listener != null) { dispatcher.Listener.Close(timeoutHelper.RemainingTime()); } } for (int i = 0; i < this.ChannelDispatchers.Count; i++) { ChannelDispatcherBase dispatcher = this.ChannelDispatchers[i]; dispatcher.CloseInput(timeoutHelper.RemainingTime()); } // Wait for existing work to complete this.instances.CloseInput(timeoutHelper.RemainingTime()); // Close instances (closes contexts/channels) this.instances.Close(timeoutHelper.RemainingTime()); // Close dispatchers for (int i = 0; i < this.ChannelDispatchers.Count; i++) { ChannelDispatcherBase dispatcher = this.ChannelDispatchers[i]; dispatcher.Close(timeoutHelper.RemainingTime()); } this.ReleasePerformanceCounters(); this.TraceBaseAddresses(); ThreadTrace.StopTracing(); } catch (TimeoutException e) { if (TD.CloseTimeoutIsEnabled()) { TD.CloseTimeout(SR.GetString(SR.TraceCodeServiceHostTimeoutOnClose)); } if (DiagnosticUtility.ShouldTraceWarning) { TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.ServiceHostTimeoutOnClose, SR.GetString(SR.TraceCodeServiceHostTimeoutOnClose), this, e); } this.Abort(); } } protected override void OnClosed() { try { for (int i = 0; i < this.ChannelDispatchers.Count; i++) { ChannelDispatcher dispatcher = this.ChannelDispatchers[i] as ChannelDispatcher; if (dispatcher != null) { dispatcher.ReleasePerformanceCounters(); } } } finally { base.OnClosed(); } } void TraceBaseAddresses() { if (DiagnosticUtility.ShouldTraceInformation && this.baseAddresses != null && this.baseAddresses.Count > 0) { TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.ServiceHostBaseAddresses, SR.GetString(SR.TraceCodeServiceHostBaseAddresses), new CollectionTraceRecord("BaseAddresses", "Address", this.baseAddresses), this, null); } } void TraceServiceHostOpenStart() { if (TD.ServiceHostOpenStartIsEnabled()) { TD.ServiceHostOpenStart(this.EventTraceActivity); } } protected override void OnEndClose(IAsyncResult result) { try { CloseAsyncResult.End(result); this.TraceBaseAddresses(); ThreadTrace.StopTracing(); } catch (TimeoutException e) { if (TD.CloseTimeoutIsEnabled()) { TD.CloseTimeout(SR.GetString(SR.TraceCodeServiceHostTimeoutOnClose)); } if (DiagnosticUtility.ShouldTraceWarning) { TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.ServiceHostTimeoutOnClose, SR.GetString(SR.TraceCodeServiceHostTimeoutOnClose), this, e); } this.Abort(); } } protected override void OnEndOpen(IAsyncResult result) { OpenAsyncResult.End(result); } void EndOpenChannelDispatchers(IAsyncResult result) { OpenCollectionAsyncResult.End(result); } void EnsureAuthenticationSchemes() { if (this.Authentication == null) { return; } //Exit immediately when not hosted in IIS or if VirtualPathExtension is not set. VirtualPathExtension is used as a flag to indicate whether a ServiceHost // is webhosted (WsDualHttpBinding-ChannelFactory is using HttpListener instead of IIS even when running in IIS) if (!AspNetEnvironment.Enabled || this.Extensions.Find() == null) { return; } foreach (ServiceEndpoint serviceEndpoint in this.Description.Endpoints) { if (serviceEndpoint.Binding != null && serviceEndpoint.ListenUri != null && ("http".Equals(serviceEndpoint.ListenUri.Scheme, StringComparison.OrdinalIgnoreCase) || "https".Equals(serviceEndpoint.ListenUri.Scheme, StringComparison.OrdinalIgnoreCase)) && this.baseAddresses.Contains(serviceEndpoint.ListenUri.Scheme)) { HttpTransportBindingElement httpTransportBindingElement = serviceEndpoint.Binding.CreateBindingElements().Find(); if (httpTransportBindingElement != null) { AuthenticationSchemes hostSupportedAuthenticationSchemes = AspNetEnvironment.Current.GetAuthenticationSchemes(this.baseAddresses[serviceEndpoint.ListenUri.Scheme]); if (hostSupportedAuthenticationSchemes != AuthenticationSchemes.None) { //If no authentication schemes are explicitly defined for the ServiceHost... if (this.Authentication.AuthenticationSchemes == AuthenticationSchemes.None) { //Inherit authentication schemes from IIS this.Authentication.AuthenticationSchemes = hostSupportedAuthenticationSchemes; } else { // Build intersection between authenticationSchemes on the ServiceHost and in IIS this.Authentication.AuthenticationSchemes &= hostSupportedAuthenticationSchemes; } } } break; } } } protected override void OnOpen(TimeSpan timeout) { TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); this.OnBeginOpen(); AfterInitializeRuntime(timeoutHelper.RemainingTime()); for (int i = 0; i < this.ChannelDispatchers.Count; i++) { ChannelDispatcherBase dispatcher = this.ChannelDispatchers[i]; dispatcher.Open(timeoutHelper.RemainingTime()); } } protected override void OnOpened() { if (this.Description != null) { ServiceCredentials c = description.Behaviors.Find(); if (c != null) { ServiceCredentials credentialsCopy = c.Clone(); credentialsCopy.MakeReadOnly(); this.readOnlyCredentials = credentialsCopy; } ServiceAuthorizationBehavior authorization = description.Behaviors.Find(); if (authorization != null) { ServiceAuthorizationBehavior authorizationCopy = authorization.Clone(); authorizationCopy.MakeReadOnly(); this.readOnlyAuthorization = authorizationCopy; } ServiceAuthenticationBehavior authentication = description.Behaviors.Find(); if (authentication != null) { ServiceAuthenticationBehavior authenticationCopy = authentication.Clone(); authentication.MakeReadOnly(); this.readOnlyAuthentication = authenticationCopy; } if (ManagementExtension.IsEnabled) { ManagementExtension.OnServiceOpened(this); } } base.OnOpened(); if (TD.ServiceHostOpenStopIsEnabled()) { TD.ServiceHostOpenStop(this.EventTraceActivity); } } internal void OnRemoveChannelDispatcher(ChannelDispatcherBase channelDispatcher) { lock (this.ThisLock) { this.ThrowIfClosedOrOpened(); channelDispatcher.DetachInternal(this); } } void OnChannelDispatcherFaulted(object sender, EventArgs e) { this.Fault(); } void OnServiceHostFaulted(object sender, EventArgs args) { if (TD.ServiceHostFaultedIsEnabled()) { TD.ServiceHostFaulted(this.EventTraceActivity, this); } if (DiagnosticUtility.ShouldTraceWarning) { TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.ServiceHostFaulted, SR.GetString(SR.TraceCodeServiceHostFaulted), this); } foreach (ICommunicationObject channelDispatcher in this.SnapshotChannelDispatchers()) { if (channelDispatcher.State == CommunicationState.Opened) { channelDispatcher.Abort(); } } } internal void RaiseUnknownMessageReceived(Message message) { try { EventHandler handler = UnknownMessageReceived; if (handler != null) { handler(this, new UnknownMessageReceivedEventArgs(message)); } } catch (Exception e) { if (Fx.IsFatal(e)) throw; throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e); } } protected void ReleasePerformanceCounters() { if (this.servicePerformanceCounters != null) { lock (this.ThisLock) { if (this.servicePerformanceCounters != null) { this.servicePerformanceCounters.Dispose(); this.servicePerformanceCounters = null; } } } if (this.defaultPerformanceCounters != null) { lock (this.ThisLock) { if (this.defaultPerformanceCounters != null) { this.defaultPerformanceCounters.Dispose(); this.defaultPerformanceCounters = null; } } } } ICommunicationObject[] SnapshotChannelDispatchers() { lock (this.ThisLock) { ICommunicationObject[] array = new ICommunicationObject[this.ChannelDispatchers.Count]; for (int i = 0; i < array.Length; i++) { array[i] = this.ChannelDispatchers[i]; } return array; } } internal virtual void UnbindInstance(InstanceContext instance) { this.instances.Remove(instance); if (null != this.servicePerformanceCounters) { lock (this.ThisLock) { if (null != this.servicePerformanceCounters) { this.servicePerformanceCounters.ServiceInstanceRemoved(); } } } } internal void IncrementBusyCount() { if (AspNetEnvironment.Enabled) { AspNetEnvironment.Current.IncrementBusyCount(); Interlocked.Increment(ref this.busyCount); } EventHandler handler = this.BusyCountIncremented; if (handler != null) { try { handler(this, EventArgs.Empty); } catch (Exception exception) { if (Fx.IsFatal(exception)) throw; throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception); } } } internal void DecrementBusyCount() { if (AspNetEnvironment.Enabled) { Interlocked.Decrement(ref this.busyCount); AspNetEnvironment.Current.DecrementBusyCount(); } } internal int BusyCount { get { return this.busyCount; } } class OpenAsyncResult : AsyncResult { static AsyncCompletion handleEndAfterInitializeRuntime = new AsyncCompletion(HandleEndAfterInitializeRuntime); static AsyncCompletion handleEndOpenChannelDispatchers = new AsyncCompletion(HandleEndOpenChannelDispatchers); TimeoutHelper timeoutHelper; ServiceHostBase host; public OpenAsyncResult(ServiceHostBase host, TimeSpan timeout, AsyncCallback callback, object state) : base(callback, state) { this.timeoutHelper = new TimeoutHelper(timeout); this.host = host; if (ProcessAfterInitializeRuntime()) { Complete(true); } } bool ProcessAfterInitializeRuntime() { IAsyncResult result = this.host.BeginAfterInitializeRuntime( this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleEndAfterInitializeRuntime), this); return SyncContinue(result); } static bool HandleEndAfterInitializeRuntime(IAsyncResult result) { OpenAsyncResult thisPtr = (OpenAsyncResult)result.AsyncState; thisPtr.host.EndAfterInitializeRuntime(result); return thisPtr.ProcessOpenChannelDispatchers(); } bool ProcessOpenChannelDispatchers() { IAsyncResult result = this.host.BeginOpenChannelDispatchers( this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleEndOpenChannelDispatchers), this); return SyncContinue(result); } static bool HandleEndOpenChannelDispatchers(IAsyncResult result) { OpenAsyncResult thisPtr = (OpenAsyncResult)result.AsyncState; thisPtr.host.EndOpenChannelDispatchers(result); return true; } public static void End(IAsyncResult result) { AsyncResult.End(result); } } class CloseAsyncResult : AsyncResult { ServiceHostBase serviceHost; TimeoutHelper timeoutHelper; public CloseAsyncResult(TimeSpan timeout, AsyncCallback callback, object state, ServiceHostBase serviceHost) : base(callback, state) { this.timeoutHelper = new TimeoutHelper(timeout); this.serviceHost = serviceHost; if (ManagementExtension.IsEnabled && null != serviceHost.Description) { ManagementExtension.OnServiceClosing(serviceHost); } this.CloseListeners(true); } void CloseListeners(bool completedSynchronously) { List listeners = new List(); for (int i = 0; i < this.serviceHost.ChannelDispatchers.Count; i++) { if (this.serviceHost.ChannelDispatchers[i].Listener != null) { listeners.Add(this.serviceHost.ChannelDispatchers[i].Listener); } } AsyncCallback callback = Fx.ThunkCallback(this.CloseListenersCallback); TimeSpan timeout = this.timeoutHelper.RemainingTime(); Exception exception = null; IAsyncResult result = null; try { result = new CloseCollectionAsyncResult(timeout, callback, this, listeners); } catch (Exception e) { if (Fx.IsFatal(e) || completedSynchronously) { throw; } exception = e; } if (exception != null) { this.CallComplete(completedSynchronously, exception); } else if (result.CompletedSynchronously) { this.FinishCloseListeners(result, completedSynchronously); } } void CloseListenersCallback(IAsyncResult result) { if (!result.CompletedSynchronously) { ((CloseAsyncResult)result.AsyncState).FinishCloseListeners(result, false); } } void FinishCloseListeners(IAsyncResult result, bool completedSynchronously) { Exception exception = null; try { CloseCollectionAsyncResult.End(result); } catch (Exception e) { if (Fx.IsFatal(e) || completedSynchronously) { throw; } exception = e; } if (exception != null) { this.CallComplete(completedSynchronously, exception); } else { this.CloseInput(completedSynchronously); } } // Wait for existing work to complete void CloseInput(bool completedSynchronously) { AsyncCallback callback = Fx.ThunkCallback(this.CloseInputCallback); Exception exception = null; IAsyncResult result = null; try { for (int i = 0; i < this.serviceHost.ChannelDispatchers.Count; i++) { ChannelDispatcherBase dispatcher = this.serviceHost.ChannelDispatchers[i]; dispatcher.CloseInput(this.timeoutHelper.RemainingTime()); } result = this.serviceHost.instances.BeginCloseInput(this.timeoutHelper.RemainingTime(), callback, this); } catch (Exception e) { if (Fx.IsFatal(e) || completedSynchronously) { throw; } exception = e; } if (exception != null) { // Any exception during async processing causes this // async callback to report the error and then relies on // Abort to cleanup any unclosed channels or instance contexts. FxTrace.Exception.AsWarning(exception); this.CallComplete(completedSynchronously, exception); } else if (result.CompletedSynchronously) { this.FinishCloseInput(result, completedSynchronously); } } void CloseInputCallback(IAsyncResult result) { if (!result.CompletedSynchronously) { ((CloseAsyncResult)result.AsyncState).FinishCloseInput(result, false); } } void FinishCloseInput(IAsyncResult result, bool completedSynchronously) { Exception exception = null; try { serviceHost.instances.EndCloseInput(result); } catch (Exception e) { if (Fx.IsFatal(e) || completedSynchronously) { throw; } exception = e; } if (exception != null) { this.CallComplete(completedSynchronously, exception); } else { this.CloseInstances(completedSynchronously); } } // Close instances (closes contexts/channels) void CloseInstances(bool completedSynchronously) { AsyncCallback callback = Fx.ThunkCallback(this.CloseInstancesCallback); TimeSpan timeout = this.timeoutHelper.RemainingTime(); Exception exception = null; IAsyncResult result = null; try { result = this.serviceHost.instances.BeginClose(timeout, callback, this); } catch (Exception e) { if (Fx.IsFatal(e) || completedSynchronously) { throw; } exception = e; } if (exception != null) { this.CallComplete(completedSynchronously, exception); } else if (result.CompletedSynchronously) { this.FinishCloseInstances(result, completedSynchronously); } } void CloseInstancesCallback(IAsyncResult result) { if (!result.CompletedSynchronously) { ((CloseAsyncResult)result.AsyncState).FinishCloseInstances(result, false); } } void FinishCloseInstances(IAsyncResult result, bool completedSynchronously) { Exception exception = null; try { this.serviceHost.instances.EndClose(result); } catch (Exception e) { if (Fx.IsFatal(e) || completedSynchronously) { throw; } exception = e; } if (exception != null) { this.CallComplete(completedSynchronously, exception); } else { this.CloseChannelDispatchers(completedSynchronously); } } void CloseChannelDispatchers(bool completedSynchronously) { IList channelDispatchers = this.serviceHost.SnapshotChannelDispatchers(); AsyncCallback callback = Fx.ThunkCallback(this.CloseChannelDispatchersCallback); TimeSpan timeout = this.timeoutHelper.RemainingTime(); Exception exception = null; IAsyncResult result = null; try { result = new CloseCollectionAsyncResult(timeout, callback, this, channelDispatchers); } catch (Exception e) { if (Fx.IsFatal(e) || completedSynchronously) { throw; } exception = e; } if (exception != null) { this.CallComplete(completedSynchronously, exception); } else if (result.CompletedSynchronously) { this.FinishCloseChannelDispatchers(result, completedSynchronously); } } void CloseChannelDispatchersCallback(IAsyncResult result) { if (!result.CompletedSynchronously) { ((CloseAsyncResult)result.AsyncState).FinishCloseChannelDispatchers(result, false); } } void FinishCloseChannelDispatchers(IAsyncResult result, bool completedSynchronously) { Exception exception = null; try { CloseCollectionAsyncResult.End(result); } catch (Exception e) { if (Fx.IsFatal(e) || completedSynchronously) { throw; } exception = e; } this.CallComplete(completedSynchronously, exception); } void CallComplete(bool completedSynchronously, Exception exception) { this.Complete(completedSynchronously, exception); } public static void End(IAsyncResult result) { AsyncResult.End(result); } } class ImplementedContractsContractResolver : IContractResolver { IDictionary implementedContracts; public ImplementedContractsContractResolver(IDictionary implementedContracts) { this.implementedContracts = implementedContracts; } public ContractDescription ResolveContract(string contractName) { return this.implementedContracts != null && this.implementedContracts.ContainsKey(contractName) ? this.implementedContracts[contractName] : null; } } internal class ServiceAndBehaviorsContractResolver : IContractResolver { IContractResolver serviceResolver; Dictionary behaviorContracts; public Dictionary BehaviorContracts { get { return behaviorContracts; } } public ServiceAndBehaviorsContractResolver(IContractResolver serviceResolver) { this.serviceResolver = serviceResolver; behaviorContracts = new Dictionary(); } public ContractDescription ResolveContract(string contractName) { ContractDescription contract = serviceResolver.ResolveContract(contractName); if (contract == null) { contract = this.behaviorContracts.ContainsKey(contractName) ? this.behaviorContracts[contractName] : null; } return contract; } public void AddBehaviorContractsToResolver(KeyedByTypeCollection behaviors) { // It would be nice to make this loop over all Behaviors... someday. if (behaviors != null && behaviors.Contains(typeof(ServiceMetadataBehavior))) { behaviors.Find().AddImplementedContracts(this); } } } } public class ServiceHost : ServiceHostBase { object singletonInstance; Type serviceType; ReflectedContractCollection reflectedContracts; IDisposable disposableInstance; protected ServiceHost() { } public ServiceHost(Type serviceType, params Uri[] baseAddresses) { if (serviceType == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("serviceType")); } this.serviceType = serviceType; using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null) { if (DiagnosticUtility.ShouldUseActivity) { ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityConstructServiceHost, serviceType.FullName), ActivityType.Construct); } InitializeDescription(serviceType, new UriSchemeKeyedCollection(baseAddresses)); } } public ServiceHost(object singletonInstance, params Uri[] baseAddresses) { if (singletonInstance == null) { throw new ArgumentNullException("singletonInstance"); } this.singletonInstance = singletonInstance; this.serviceType = singletonInstance.GetType(); using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null) { if (DiagnosticUtility.ShouldUseActivity) { ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityConstructServiceHost, serviceType.FullName), ActivityType.Construct); } InitializeDescription(singletonInstance, new UriSchemeKeyedCollection(baseAddresses)); } } public object SingletonInstance { get { return this.singletonInstance; } } internal override object DisposableInstance { get { return this.disposableInstance; } } public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address) { return this.AddServiceEndpoint(implementedContract, binding, address, (Uri)null); } public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address, Uri listenUri) { if (address == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("address")); } ServiceEndpoint endpoint = this.AddServiceEndpoint(implementedContract, binding, new Uri(address, UriKind.RelativeOrAbsolute)); if (listenUri != null) { listenUri = MakeAbsoluteUri(listenUri, binding); endpoint.ListenUri = listenUri; } return endpoint; } public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address) { return this.AddServiceEndpoint(implementedContract, binding, address, (Uri)null); } void ValidateContractType(Type implementedContract, ReflectedAndBehaviorContractCollection reflectedAndBehaviorContracts) { if (!implementedContract.IsDefined(typeof(ServiceContractAttribute), false)) { #pragma warning suppress 56506 // implementedContract is never null at this point throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SfxServiceContractAttributeNotFound, implementedContract.FullName))); } if (!reflectedAndBehaviorContracts.Contains(implementedContract)) { if (implementedContract == typeof(IMetadataExchange)) #pragma warning suppress 56506 // ServiceType is never null at this point throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SfxReflectedContractKeyNotFoundIMetadataExchange, this.serviceType.FullName))); else #pragma warning suppress 56506 // implementedContract and ServiceType are never null at this point throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SfxReflectedContractKeyNotFound2, implementedContract.FullName, this.serviceType.FullName))); } } public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address, Uri listenUri) { if (implementedContract == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("implementedContract")); } if (this.reflectedContracts == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SfxReflectedContractsNotInitialized1, implementedContract.FullName))); } ReflectedAndBehaviorContractCollection reflectedAndBehaviorContracts = new ReflectedAndBehaviorContractCollection(this.reflectedContracts, this.Description.Behaviors); ValidateContractType(implementedContract, reflectedAndBehaviorContracts); ServiceEndpoint endpoint = AddServiceEndpoint(reflectedAndBehaviorContracts.GetConfigKey(implementedContract), binding, address); if (listenUri != null) { listenUri = MakeAbsoluteUri(listenUri, binding); endpoint.ListenUri = listenUri; } return endpoint; } internal override void AddDefaultEndpoints(Binding defaultBinding, List defaultEndpoints) { // don't generate endpoints for contracts that serve as the base type for other reflected contracts List mostSpecificContracts = new List(); for (int i = 0; i < this.reflectedContracts.Count; i++) { bool addContractEndpoint = true; ContractDescription contract = this.reflectedContracts[i]; Type contractType = contract.ContractType; if (contractType != null) { for (int j = 0; j < this.reflectedContracts.Count; j++) { ContractDescription otherContract = this.reflectedContracts[j]; Type otherContractType = otherContract.ContractType; if (i == j || otherContractType == null) { continue; } if (contractType.IsAssignableFrom(otherContractType)) { addContractEndpoint = false; break; } } } if (addContractEndpoint) { mostSpecificContracts.Add(contract); } } foreach (ContractDescription contract in mostSpecificContracts) { ServiceEndpoint endpoint = AddServiceEndpoint(contract.ConfigurationName, defaultBinding, string.Empty); ConfigLoader.LoadDefaultEndpointBehaviors(endpoint); defaultEndpoints.Add(endpoint); } } // Run static Configure method on service type if it exists, else load configuration from Web.config/App.config protected override void ApplyConfiguration() { // Load from static Configure method if it exists with the right signature Type serviceType = this.Description.ServiceType; if (serviceType != null) { MethodInfo configure = GetConfigureMethod(serviceType); if (configure != null) { // load config ConfigLoader configLoader = new ConfigLoader(GetContractResolver(this.ImplementedContracts)); LoadHostConfigurationInternal(configLoader, this.Description, this.Description.ConfigurationName); // Invoke configure method for service ServiceConfiguration configuration = new ServiceConfiguration(this); InvokeConfigure(configure, configuration); return; } } // else just load from Web.config/App.config base.ApplyConfiguration(); } // Find the Configure method with the required signature, closest to serviceType in the type hierarchy static MethodInfo GetConfigureMethod(Type serviceType) { // Use recursion instead of BindingFlags.FlattenHierarchy because we require return type to be void // base case: all Types are rooted in object eventually if (serviceType == typeof(object)) { return null; } // signature: "public static void Configure(ServiceConfiguration)" MethodInfo configure = serviceType.GetMethod("Configure", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(ServiceConfiguration) }, null); if (configure != null && configure.ReturnType == typeof(void)) { return configure; } else { return GetConfigureMethod(serviceType.BaseType); } } static void InvokeConfigure(MethodInfo configureMethod, ServiceConfiguration configuration) { Action call = Delegate.CreateDelegate(typeof(Action), configureMethod) as Action; call(configuration); } // called from ServiceConfiguration.LoadFromConfiguration() internal void LoadFromConfiguration() { if (this.Description == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotApplyConfigurationWithoutDescription))); } ConfigLoader configLoader = new ConfigLoader(GetContractResolver(this.ImplementedContracts)); // Call the overload of LoadConfigurationSectionInternal which looks up the serviceElement from ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None) LoadConfigurationSectionExceptHostInternal(configLoader, this.Description, this.Description.ConfigurationName); EnsureAuthenticationAuthorizationDebug(this.Description); } // called from ServiceConfiguration.LoadFromConfiguration(configuration) internal void LoadFromConfiguration(System.Configuration.Configuration configuration) { if (this.Description == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostBaseCannotApplyConfigurationWithoutDescription))); } ConfigLoader configLoader = new ConfigLoader(GetContractResolver(this.ImplementedContracts)); // Look up the serviceElement explicitly on configuration, then call the overload of LoadConfigurationSectionInternal that loads the rest of the config from the same configuration as serviceElement ServicesSection servicesSection = (ServicesSection)configuration.GetSection(ConfigurationStrings.ServicesSectionPath); ServiceElement serviceElement = configLoader.LookupService(this.Description.ConfigurationName, servicesSection); configLoader.LoadServiceDescription(this, this.Description, serviceElement, this.LoadConfigurationSectionHelper, skipHost: true); EnsureAuthenticationAuthorizationDebug(this.Description); } // Load only "host" section within "service" tag [Fx.Tag.SecurityNote(Critical = "Calls LookupService which is critical.", Safe = "Doesn't leak ServiceElement out of SecurityCritical code.")] [SecuritySafeCritical] void LoadHostConfigurationInternal(ConfigLoader configLoader, ServiceDescription description, string configurationName) { ServiceElement serviceSection = configLoader.LookupService(configurationName); if (serviceSection != null) { configLoader.LoadHostConfig(serviceSection, this, (addr => this.InternalBaseAddresses.Add(addr))); } } // Load service description for service from config, but skip "host" section within "service" tag [Fx.Tag.SecurityNote(Critical = "Calls LookupService which is critical.", Safe = "Doesn't leak ServiceElement out of SecurityCritical code.")] [SecuritySafeCritical] void LoadConfigurationSectionExceptHostInternal(ConfigLoader configLoader, ServiceDescription description, string configurationName) { ServiceElement serviceSection = configLoader.LookupService(configurationName); configLoader.LoadServiceDescription(this, description, serviceSection, this.LoadConfigurationSectionHelper, skipHost: true); } internal override string CloseActivityName { get { return SR.GetString(SR.ActivityCloseServiceHost, this.serviceType.FullName); } } internal override string OpenActivityName { get { return SR.GetString(SR.ActivityOpenServiceHost, this.serviceType.FullName); } } protected override ServiceDescription CreateDescription(out IDictionary implementedContracts) { if (this.serviceType == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxServiceHostCannotCreateDescriptionWithoutServiceType))); } ServiceDescription description; if (this.SingletonInstance != null) { description = ServiceDescription.GetService(this.SingletonInstance); } else { description = ServiceDescription.GetService(this.serviceType); } ServiceBehaviorAttribute serviceBehavior = description.Behaviors.Find(); object serviceInstanceUsedAsABehavior = serviceBehavior.GetWellKnownSingleton(); if (serviceInstanceUsedAsABehavior == null) { serviceInstanceUsedAsABehavior = serviceBehavior.GetHiddenSingleton(); this.disposableInstance = serviceInstanceUsedAsABehavior as IDisposable; } if ((typeof(IServiceBehavior).IsAssignableFrom(this.serviceType) || typeof(IContractBehavior).IsAssignableFrom(this.serviceType)) && serviceInstanceUsedAsABehavior == null) { serviceInstanceUsedAsABehavior = ServiceDescription.CreateImplementation(this.serviceType); this.disposableInstance = serviceInstanceUsedAsABehavior as IDisposable; } if (this.SingletonInstance == null) { if (serviceInstanceUsedAsABehavior is IServiceBehavior) { description.Behaviors.Add((IServiceBehavior)serviceInstanceUsedAsABehavior); } } ReflectedContractCollection reflectedContracts = new ReflectedContractCollection(); List interfaces = ServiceReflector.GetInterfaces(this.serviceType); for (int i = 0; i < interfaces.Count; i++) { Type contractType = interfaces[i]; if (!reflectedContracts.Contains(contractType)) { ContractDescription contract = null; if (serviceInstanceUsedAsABehavior != null) { contract = ContractDescription.GetContract(contractType, serviceInstanceUsedAsABehavior); } else { contract = ContractDescription.GetContract(contractType, this.serviceType); } reflectedContracts.Add(contract); Collection inheritedContracts = contract.GetInheritedContracts(); for (int j = 0; j < inheritedContracts.Count; j++) { ContractDescription inheritedContract = inheritedContracts[j]; if (!reflectedContracts.Contains(inheritedContract.ContractType)) { reflectedContracts.Add(inheritedContract); } } } } this.reflectedContracts = reflectedContracts; implementedContracts = reflectedContracts.ToImplementedContracts(); return description; } protected void InitializeDescription(object singletonInstance, UriSchemeKeyedCollection baseAddresses) { if (singletonInstance == null) { throw new ArgumentNullException("singletonInstance"); } this.singletonInstance = singletonInstance; InitializeDescription(singletonInstance.GetType(), baseAddresses); } protected void InitializeDescription(Type serviceType, UriSchemeKeyedCollection baseAddresses) { if (serviceType == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("serviceType")); } this.serviceType = serviceType; base.InitializeDescription(baseAddresses); } protected override void OnClosed() { base.OnClosed(); if (this.disposableInstance != null) { this.disposableInstance.Dispose(); } } class ReflectedContractCollection : KeyedCollection { public ReflectedContractCollection() : base(null, 4) { } protected override Type GetKeyForItem(ContractDescription item) { if (item == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item"); return item.ContractType; } public IDictionary ToImplementedContracts() { Dictionary implementedContracts = new Dictionary(); foreach (ContractDescription contract in this.Items) { implementedContracts.Add(GetConfigKey(contract), contract); } return implementedContracts; } internal static string GetConfigKey(ContractDescription contract) { return contract.ConfigurationName; } } class ReflectedAndBehaviorContractCollection { ReflectedContractCollection reflectedContracts; KeyedByTypeCollection behaviors; public ReflectedAndBehaviorContractCollection(ReflectedContractCollection reflectedContracts, KeyedByTypeCollection behaviors) { this.reflectedContracts = reflectedContracts; this.behaviors = behaviors; } internal bool Contains(Type implementedContract) { if (this.reflectedContracts.Contains(implementedContract)) { return true; } if (this.behaviors.Contains(typeof(ServiceMetadataBehavior)) && ServiceMetadataBehavior.IsMetadataImplementedType(implementedContract)) { return true; } return false; } internal string GetConfigKey(Type implementedContract) { if (this.reflectedContracts.Contains(implementedContract)) { return ReflectedContractCollection.GetConfigKey(reflectedContracts[implementedContract]); } if (this.behaviors.Contains(typeof(ServiceMetadataBehavior)) && ServiceMetadataBehavior.IsMetadataImplementedType(implementedContract)) { return ServiceMetadataBehavior.MexContractName; } Fx.Assert("Calls to GetConfigKey are preceeded by calls to Contains."); #pragma warning suppress 56506 // implementedContract is never null at this point throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SfxReflectedContractKeyNotFound2, implementedContract.FullName, string.Empty))); } } } }