File: System\ServiceModel\ServiceHost.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// 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<ServiceHostBase>, 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<ServiceHostBase> extensions;
        ReadOnlyCollection<Uri> externalBaseAddresses;
        IDictionary<string, ContractDescription> implementedContracts;
        IInstanceContextManager instances;
        TimeSpan openTimeout = ServiceDefaults.OpenTimeout;
        ServicePerformanceCountersBase servicePerformanceCounters;
        DefaultPerformanceCounters defaultPerformanceCounters;
        ServiceThrottle serviceThrottle;
        ServiceCredentials readOnlyCredentials;
        ServiceAuthorizationBehavior readOnlyAuthorization;
        ServiceAuthenticationBehavior readOnlyAuthentication;
        Dictionary<DispatcherBuilder.ListenUriInfo, Collection<ServiceEndpoint>> endpointsByListenUriInfo;
        int busyCount;
        EventTraceActivity eventTraceActivity;
 
        internal event EventHandler BusyCountIncremented;
 
        public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived;
 
        protected ServiceHostBase()
        {
            TraceUtility.SetEtwProviderId();
            this.baseAddresses = new UriSchemeKeyedCollection(this.ThisLock);
            this.channelDispatchers = new ChannelDispatcherCollection(this, this.ThisLock);
            this.extensions = new ExtensionCollection<ServiceHostBase>(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<Uri> BaseAddresses
        {
            get
            {
                externalBaseAddresses = new ReadOnlyCollection<Uri>(new List<Uri>(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<ServiceHostBase> Extensions
        {
            get { return this.extensions; }
        }
 
        protected internal IDictionary<string, ContractDescription> 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<DispatcherBuilder.ListenUriInfo, Collection<ServiceEndpoint>> EndpointsByListenUriInfo
        {
            get
            {
                if (this.endpointsByListenUriInfo == null)
                {
                    this.endpointsByListenUriInfo = this.GetEndpointsByListenUriInfo();
                }
                return this.endpointsByListenUriInfo;
            }
        }
 
        Dictionary<DispatcherBuilder.ListenUriInfo, Collection<ServiceEndpoint>> GetEndpointsByListenUriInfo()
        {
            Dictionary<DispatcherBuilder.ListenUriInfo, Collection<ServiceEndpoint>> endpointDictionary = new Dictionary<DispatcherBuilder.ListenUriInfo, Collection<ServiceEndpoint>>();
            foreach (ServiceEndpoint endpoint in this.Description.Endpoints)
            {
                DispatcherBuilder.ListenUriInfo listenUriInfo = DispatcherBuilder.GetListenUriInfoForEndpoint(this, endpoint);
                if (!endpointDictionary.ContainsKey(listenUriInfo))
                {
                    endpointDictionary.Add(listenUriInfo, new Collection<ServiceEndpoint>());
                }
                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<ServiceEndpoint> AddDefaultEndpoints()
        {
            List<ServiceEndpoint> defaultEndpoints = new List<ServiceEndpoint>();
            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<string, string> dictionary = new Dictionary<string, string>();
                dictionary["ServiceConfigurationName"] = this.description.ConfigurationName;
                TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.DefaultEndpointsAdded, SR.GetString(SR.TraceCodeDefaultEndpointsAdded), new DictionaryTraceRecord(dictionary));
            }
            return new ReadOnlyCollection<ServiceEndpoint>(defaultEndpoints);
        }
 
        internal virtual void AddDefaultEndpoints(Binding defaultBinding, List<ServiceEndpoint> 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<string, ContractDescription> 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<ServiceAuthorizationBehavior>();
 
            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<ServiceAuthenticationBehavior>();
 
            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<ServiceDebugBehavior>();
 
            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<ServiceCredentials>();
 
            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<ServiceEndpoint>());
        }
 
        internal BindingParameterCollection GetBindingParameters(ServiceEndpoint inputEndpoint)
        {
            Collection<ServiceEndpoint> endpoints;
            if (inputEndpoint == null)
            {
                endpoints = new Collection<ServiceEndpoint>();
            }
            else if (!this.EndpointsByListenUriInfo.TryGetValue(DispatcherBuilder.GetListenUriInfoForEndpoint(this, inputEndpoint), out endpoints) || !endpoints.Contains(inputEndpoint))
            {
                endpoints = new Collection<ServiceEndpoint>();
                endpoints.Add(inputEndpoint);
            }
 
            return DispatcherBuilder.GetBindingParameters(this, endpoints);
        }
 
        internal BindingParameterCollection GetBindingParameters(Collection<ServiceEndpoint> endpoints)
        {
            return DispatcherBuilder.GetBindingParameters(this, endpoints);
        }
 
        internal ReadOnlyCollection<InstanceContext> GetInstanceContexts()
        {
            return Array.AsReadOnly<InstanceContext>(this.instances.ToArray());
        }
 
        internal virtual IContractResolver GetContractResolver(IDictionary<string, ContractDescription> 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<string, ContractDescription> 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<VirtualPathExtension>() == 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<HttpTransportBindingElement>();
 
                    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<ServiceCredentials>();
                if (c != null)
                {
                    ServiceCredentials credentialsCopy = c.Clone();
                    credentialsCopy.MakeReadOnly();
                    this.readOnlyCredentials = credentialsCopy;
                }
 
                ServiceAuthorizationBehavior authorization = description.Behaviors.Find<ServiceAuthorizationBehavior>();
                if (authorization != null)
                {
                    ServiceAuthorizationBehavior authorizationCopy = authorization.Clone();
                    authorizationCopy.MakeReadOnly();
                    this.readOnlyAuthorization = authorizationCopy;
                }
 
                ServiceAuthenticationBehavior authentication = description.Behaviors.Find<ServiceAuthenticationBehavior>();
                if (authentication != null)
                {
                    ServiceAuthenticationBehavior authenticationCopy = authentication.Clone();
                    authentication.MakeReadOnly();
                    this.readOnlyAuthentication = authenticationCopy;
                }
 
                if (ManagementExtension.IsEnabled)
                {
                    ManagementExtension.OnServiceOpened(this);
                }
 
                // log telemetry data for the current WCF service.
                TelemetryTraceLogging.LogSeriveKPIData(this.Description);
            }
            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<UnknownMessageReceivedEventArgs> 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<OpenAsyncResult>(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<ICommunicationObject> listeners = new List<ICommunicationObject>();
                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<ICommunicationObject> 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<CloseAsyncResult>(result);
            }
        }
 
        class ImplementedContractsContractResolver : IContractResolver
        {
            IDictionary<string, ContractDescription> implementedContracts;
 
            public ImplementedContractsContractResolver(IDictionary<string, ContractDescription> 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<string, ContractDescription> behaviorContracts;
 
            public Dictionary<string, ContractDescription> BehaviorContracts
            {
                get { return behaviorContracts; }
            }
 
            public ServiceAndBehaviorsContractResolver(IContractResolver serviceResolver)
            {
                this.serviceResolver = serviceResolver;
                behaviorContracts = new Dictionary<string, ContractDescription>();
            }
 
            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<IServiceBehavior> behaviors)
            {
                // It would be nice to make this loop over all Behaviors... someday.
                if (behaviors != null && behaviors.Contains(typeof(ServiceMetadataBehavior)))
                {
                    behaviors.Find<ServiceMetadataBehavior>().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<ServiceEndpoint> defaultEndpoints)
        {
            // don't generate endpoints for contracts that serve as the base type for other reflected contracts
            List<ContractDescription> mostSpecificContracts = new List<ContractDescription>();
            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 <host> 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<ServiceConfiguration> call = Delegate.CreateDelegate(typeof(Action<ServiceConfiguration>), configureMethod) as Action<ServiceConfiguration>;
            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<string, ContractDescription> 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<ServiceBehaviorAttribute>();
            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<Type> 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<ContractDescription> 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<Type, ContractDescription>
        {
            public ReflectedContractCollection()
                : base(null, 4)
            {
            }
 
            protected override Type GetKeyForItem(ContractDescription item)
            {
                if (item == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item");
 
                return item.ContractType;
            }
 
            public IDictionary<string, ContractDescription> ToImplementedContracts()
            {
                Dictionary<string, ContractDescription> implementedContracts = new Dictionary<string, ContractDescription>();
                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<IServiceBehavior> behaviors;
            public ReflectedAndBehaviorContractCollection(ReflectedContractCollection reflectedContracts, KeyedByTypeCollection<IServiceBehavior> 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)));
 
            }
        }
    }
}