ServiceHostingEnvironment.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ WCF / WCF / 3.5.30729.1 / untmp / Orcas / SP / ndp / cdf / src / WCF / ServiceModel / System / ServiceModel / ServiceHostingEnvironment.cs / 3 / ServiceHostingEnvironment.cs

                            //---------------------------------------------------------------------------- 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//---------------------------------------------------------------------------
namespace System.ServiceModel
{ 
    using System.Configuration;
    using System.Collections; 
    using System.Collections.Specialized; 
    using System.Collections.Generic;
    using System.Diagnostics; 
    using System.Globalization;
    using System.Reflection;
    using System.Net;
    using System.Runtime.CompilerServices; 
    using System.ServiceModel.Activation;
    using System.ServiceModel.Channels; 
    using System.ServiceModel.Diagnostics; 
    using System.ServiceModel.ComIntegration;
    using System.Threading; 
    using System.Web;
    using System.Web.Compilation;
    using System.Web.Configuration;
    using System.Web.Hosting; 
    using System.Runtime.Serialization;
    using System.Security.Principal; 
    using System.ServiceModel.Configuration; 
    using System.Runtime;
    using System.Runtime.InteropServices; 
    using System.ComponentModel;
    using System.Security.Permissions;
    using System.Security;
 
    public static class ServiceHostingEnvironment
    { 
        static object syncRoot = new object(); 
        static HostingManager hostingManager;
        static bool isHosted; 
        static bool isSimpleApplicationHost;
        static Int64 requestCount;
        static bool didAssemblyCheck;
        static bool isApplicationDomainHosted; 

        internal const string VerbPost = "POST"; 
        internal const string ISAPIApplicationIdPrefix = "/LM/W3SVC/"; 
        internal const string RelativeVirtualPathPrefix = "~";
        internal const string ServiceParserDelimiter = "|"; 

        const char FileExtensionSeparator = '.';
        const char UriSchemeSeparator = ':';
        const char PathSeparator = '/'; 

        ///  
        /// Critical - Calls into an unsafe UnsafeLogEvent method 
        /// TreatAsSafe - Event identities cannot be spoofed as they are constants determined inside the method
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            if (DiagnosticUtility.ShouldTraceError) 
            {
                Exception exception = e.ExceptionObject as Exception; 
                DiagnosticUtility.UnsafeEventLog.UnsafeLogEvent(TraceEventType.Error, EventLogCategory.WebHost, EventLogEventId.WebHostUnhandledException, true, 
                    DiagnosticTrace.CreateSourceString(sender),
                    exception == null ? string.Empty : exception.ToString()); 
            }
        }

        ///  
        /// Review - called by ProcessRequest outside of the restricted SecurityContext
        ///  
        [SecurityRequiresReview] 
        public static bool AspNetCompatibilityEnabled
        { 
            /// 
            /// Review - can be called outside of user context.
            /// 
            [SecurityRequiresReview] 
            get
            { 
                if (!IsHosted) 
                    return false;
 
                return IsAspNetCompatibilityEnabled();
            }
        }
 
        /// 
        /// Review - called by ServiceHostFactory.CreateServiceHost 
        ///  
        [SecurityRequiresReview]
        internal static Uri[] PrefixFilters 
        {
            [SecurityRequiresReview]
            get
            { 
                if (!IsHosted)
                    return null; 
 
                return GetBaseAddressPrefixFilters();
            } 
        }


 
        /// 
        /// Review - can be called outside of user context. 
        ///  
        [SecurityRequiresReview]
        [MethodImpl(MethodImplOptions.NoInlining)] 
        static bool IsAspNetCompatibilityEnabled()
        {
            return hostingManager.AspNetCompatibilityEnabled;
        } 

        ///  
        /// Review - can be called outside of user context. 
        /// 
        [SecurityRequiresReview] 
        [MethodImpl(MethodImplOptions.NoInlining)]
        static Uri[] GetBaseAddressPrefixFilters()
        {
            return hostingManager.BaseAddressPrefixFilters; 
        }
 
 
        public static void EnsureServiceAvailable(string virtualPath)
        { 
            if (string.IsNullOrEmpty(virtualPath))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("virtualPath"));

            if (virtualPath.IndexOf(UriSchemeSeparator) > 0) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.Hosting_AddressIsAbsoluteUri, virtualPath), "virtualPath"));
 
            EnsureInitialized(); 
            virtualPath = NormalizeVirtualPath(virtualPath);
            EnsureServiceAvailableFast(virtualPath); 
        }

        internal static void EnsureServiceAvailableFast(string relativeVirtualPath)
        { 
            try
            { 
                hostingManager.EnsureServiceAvailable(relativeVirtualPath); 
            }
            catch (ServiceActivationException exception) 
            {
                LogServiceActivationException(exception);

                throw; 
            }
        } 
 
        /// 
        /// Critical - Calls into an unsafe UnsafeLogEvent method 
        /// TreatAsSafe - Event identities cannot be spoofed as they are constants determined inside the method
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private static void LogServiceActivationException(ServiceActivationException exception) 
        {
            if (exception.InnerException is HttpException) 
            { 
                string messageAsString = SafeTryGetHtmlErrorMessage((HttpException)exception.InnerException);
                if (messageAsString == null || messageAsString.Length == 0) 
                    messageAsString = exception.Message;

                DiagnosticUtility.UnsafeEventLog.UnsafeLogEvent(TraceEventType.Error, EventLogCategory.WebHost, EventLogEventId.WebHostHttpError, true,
                    DiagnosticTrace.CreateSourceString(hostingManager), 
                    messageAsString,
                    exception == null ? string.Empty : exception.ToString()); 
            } 
            else
            { 
                DiagnosticUtility.UnsafeEventLog.UnsafeLogEvent(TraceEventType.Error, EventLogCategory.WebHost, EventLogEventId.WebHostFailedToProcessRequest, true,
                    DiagnosticTrace.CreateSourceString(hostingManager),
                    exception == null ? string.Empty : exception.ToString());
            } 
        }
 
        static bool canGetHtmlErrorMessage = true; 
        static string SafeTryGetHtmlErrorMessage(HttpException exception)
        { 
            if (exception != null && canGetHtmlErrorMessage)
            {
                try
                { 
                    return exception.GetHtmlErrorMessage();
                } 
                catch (SecurityException e) 
                {
                    canGetHtmlErrorMessage = false; 

                    // not re-throwing on purpose
                    if (DiagnosticUtility.ShouldTraceWarning)
                    { 
                        DiagnosticUtility.ExceptionUtility.TraceHandledException(e, TraceEventType.Warning);
                    } 
                } 
            }
            return null; 
        }

        /// 
        /// Review - can be called outside of user context. 
        /// 
        [SecurityRequiresReview] 
        internal static void IncrementRequestCount() 
        {
            Interlocked.Increment(ref requestCount); 
        }

        internal static void DecrementRequestCount()
        { 
            Interlocked.Decrement(ref requestCount);
            Debug.Assert(requestCount >= 0, "Request count should always be non-nagative."); 
            if (requestCount == 0) 
            {
                if (hostingManager != null) 
                {
                    hostingManager.NotifyAllRequestDone();
                }
            } 
        }
 
        internal static string CurrentVirtualPath 
        {
            get 
            {
                DiagnosticUtility.DebugAssert(IsHosted, "CurrentVirtualPath should not be called from non web-hosted environment.");
                return hostingManager.CurrentVirtualPath;
            } 
        }
 
        internal static void ProcessNotMatchedEndpointAddress(Uri uri, string endpointName) 
        {
            if (ServiceHostingEnvironment.AspNetCompatibilityEnabled && 
                !object.ReferenceEquals(uri.Scheme, Uri.UriSchemeHttp) &&
                !object.ReferenceEquals(uri.Scheme, Uri.UriSchemeHttps))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.Hosting_NonHTTPInCompatibilityMode, endpointName))); 
            }
        } 
 
        /// 
        /// Review - called by ProcessRequest outside of the restricted SecurityContext 
        /// 
        [SecurityRequiresReview]
        internal static bool GetExtensionSupported(string extension)
        { 
            DiagnosticUtility.DebugAssert(IsHosted, "GetExtensionSupported should not be called from non web-hosted environment.");
            return hostingManager.GetExtensionSupported(extension); 
        } 

        internal static bool IsRecycling 
        {
            get
            {
                DiagnosticUtility.DebugAssert(IsHosted, "IsRecycling should not be called from non web-hosted environment."); 
                return hostingManager.IsRecycling;
            } 
        } 

        static object ThisLock 
        {
            get
            {
                return syncRoot; 
            }
        } 
 
        internal static void DecrementBusyCount()
        { 
            if (ServiceHostingEnvironment.IsHosted)
                HostingEnvironmentWrapper.DecrementBusyCount();
        }
 
        internal static void IncrementBusyCount()
        { 
            if (ServiceHostingEnvironment.IsHosted) 
                HostingEnvironmentWrapper.IncrementBusyCount();
        } 

        /// 
        /// Review - called by ProcessRequest outside of the restricted SecurityContext
        ///  
        [SecurityRequiresReview]
        internal static void SafeEnsureInitialized() 
        { 
            if (hostingManager == null)
            { 
                PartialTrustHelpers.PartialTrustInvoke(new ContextCallback(OnEnsureInitialized), null);
            }
        }
 
        static void OnEnsureInitialized(object state)
        { 
            EnsureInitialized(); 
        }
 
        internal static void EnsureInitialized()
        {
            if (hostingManager != null)
                return; 

            lock (ThisLock) 
            { 
                if (hostingManager != null)
                    return; 

                if (!HostingEnvironmentWrapper.IsHosted)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.Hosting_ProcessNotExecutingUnderHostedContext, "ServiceHostingEnvironment.EnsureServiceAvailable")));
 
                HostingManager tempHostingManager = new HostingManager();
                // register the following code when we use the service environment class 
                // the first time 
                //
                HookADUnhandledExceptionEvent(); 

                Thread.MemoryBarrier();

                isSimpleApplicationHost = GetIsSimpleApplicationHost(); 

                hostingManager = tempHostingManager; 
                isHosted = true; 
            }
        } 

        /// 
        /// Critical - Satisfies a LinkDemand for SecurityPermission(ControlAppDomain) on HookADUnhandledExceptionEvent
        /// Safe - no control flow in for handler 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        static void HookADUnhandledExceptionEvent() 
        {
            AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; 

        }

        ///  
        /// Critical - Uses SecurityCritical property UnsafeApplicationID to get application id with an elevation
        /// Safe - processes result into a simple bool which is not protected 
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        static bool GetIsSimpleApplicationHost() 
        {
            // ASPNET won't provide API to check Cassini. But it's safe and performant to check only
            // the ApplicationID prefix (MessageBus Bug 24832).
            return (string.Compare(ISAPIApplicationIdPrefix, 0, 
                    HostingEnvironmentWrapper.UnsafeApplicationID, 0, ISAPIApplicationIdPrefix.Length, StringComparison.OrdinalIgnoreCase) != 0);
        } 
 
        internal static string NormalizeVirtualPath(string virtualPath)
        { 
            string processedVirtualPath = null;
            try
            {
                // Convert the virtual path to relative if not already is. 
                processedVirtualPath = VirtualPathUtility.ToAppRelative(virtualPath, HostingEnvironmentWrapper.ApplicationVirtualPath);
            } 
            catch (HttpException exception) 
            {
                // We want to throw an ArgumentException. 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(exception.Message, "virtualPath", exception));
            }

            if (string.IsNullOrEmpty(processedVirtualPath) || 
                !processedVirtualPath.StartsWith(RelativeVirtualPathPrefix, StringComparison.Ordinal))
            { 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.Hosting_AddressPointsOutsideTheVirtualDirectory, 
                    virtualPath, HostingEnvironmentWrapper.ApplicationVirtualPath)));
            } 

            // Find the position to start.
            int pos = processedVirtualPath.IndexOf(FileExtensionSeparator);
 
            while (pos > 0)
            { 
                // Search inside the processedVirtualPath to find the extension. 
                pos = processedVirtualPath.IndexOf(PathSeparator, pos + 1);
 
                string subVirtualPath = (pos == -1) ? processedVirtualPath : processedVirtualPath.Substring(0, pos);
                string extension = VirtualPathUtility.GetExtension(subVirtualPath);
                if ((!string.IsNullOrEmpty(extension)) &&
                     ServiceHostingEnvironment.GetExtensionSupported(extension)) 
                {
                    // Remove the pathinfo. 
                    return subVirtualPath; 
                }
            } 

            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new EndpointNotFoundException(SR.GetString(SR.Hosting_ServiceNotExist, virtualPath)));
        }
 
        internal static bool IsHosted
        { 
            ///  
            /// Review - can be called outside of user context.
            ///  
            [SecurityRequiresReview]
            get
            {
                return isHosted; 
            }
        } 
 
        internal static bool IsSimpleApplicationHost
        { 
            get
            {
                DiagnosticUtility.DebugAssert(IsHosted, "IsSimpleApplicationHost should not be called from non web-hosted environment.");
                return isSimpleApplicationHost; 
            }
        } 
 
        const string SystemWebComma = "System.Web,";
        internal static bool ApplicationDomainHosted 
        {
            get
            {
                if (didAssemblyCheck) 
                {
                    return isApplicationDomainHosted; 
                } 

                Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); 
                for (int i = 0; i < assemblies.Length; i++)
                {
                    if (string.Compare(assemblies[i].FullName, 0, SystemWebComma, 0, SystemWebComma.Length, StringComparison.OrdinalIgnoreCase) == 0)
                    { 
                        isApplicationDomainHosted = IsApplicationDomainHosted();
                        break; 
                    } 
                }
 
                didAssemblyCheck = true;
                return isApplicationDomainHosted;
            }
        } 

        ///  
        /// Critical - Assert a demand for AspNetHostingPermission 
        /// Safe - Only queries if we are hosted - no actual action is initiated, no critical
        ///         information is leaking. 
        /// 
        [MethodImpl(MethodImplOptions.NoInlining)]
        [SecurityCritical, SecurityTreatAsSafe]
        [AspNetHostingPermission(SecurityAction.Assert, Level = AspNetHostingPermissionLevel.Minimal)] 
        static bool IsApplicationDomainHosted()
        { 
            return HostingEnvironment.IsHosted; 
        }
 
        class HostingManager : IRegisteredObject
        {
            IDictionary directory;
            readonly ExtensionHelper extensions; 
            bool aspNetCompatibilityEnabled;
            bool isUnregistered; 
            bool isRecycling; 
            bool isStopStarted;
            static object syncRoot = new object(); 
            Uri[] baseAddressPrefixFilters;

            // One instance per appdomain, don't need to be disposed.
            ManualResetEvent allRequestDoneInStop = new ManualResetEvent(false); 

            ///  
            /// Critical - Admin-provided value that allows for machine resource allocation 
            /// 
            [SecurityCritical] 
            int minFreeMemoryPercentageToActivateService;

            [ThreadStatic]
            string currentVirtualPath; 

            internal HostingManager() 
            { 
                this.directory = new Dictionary(16, StringComparer.OrdinalIgnoreCase);
 
                try
                {
                    RegisterObject();
 
                    LoadConfigParameters();
 
                    this.extensions = new ExtensionHelper(); 
                }
                finally 
                {
                    // Make sure failure to construct is idempotent
                    if (this.extensions == null)
                    { 
                        UnregisterObject();
                    } 
                } 
            }
 
            /// 
            /// Critical - Uses SecurityCritical method UnsafeGetSection to get config with an elevation
            ///          - Sets minFreeMemoryPercentageToActivateService
            /// Safe - does not leak config objects 
            /// 
            [SecurityCritical, SecurityTreatAsSafe] 
            private void LoadConfigParameters() 
            {
                ServiceHostingEnvironmentSection section = ServiceHostingEnvironmentSection.UnsafeGetSection(); 
                this.aspNetCompatibilityEnabled = section.AspNetCompatibilityEnabled;
                this.minFreeMemoryPercentageToActivateService = section.MinFreeMemoryPercentageToActivateService;
                List prefixFilters = new List();
 
                foreach (BaseAddressPrefixFilterElement element in section.BaseAddressPrefixFilters)
                { 
                    prefixFilters.Add(element.Prefix); 
                }
                this.baseAddressPrefixFilters = prefixFilters.ToArray(); 
            }

            /// 
            /// Review - can be called outside of user context. 
            /// 
            [SecurityRequiresReview] 
            internal bool GetExtensionSupported(string extension) 
            {
                return extensions.GetExtensionSupported(extension); 
            }

            internal bool AspNetCompatibilityEnabled
            { 
                /// 
                /// Review - can be called outside of user context. 
                ///  
                [SecurityRequiresReview]
                get 
                {
                    return aspNetCompatibilityEnabled;
                }
            } 

            internal Uri[] BaseAddressPrefixFilters 
            { 
                /// 
                /// Review - can be called outside of user context. 
                /// 
                [SecurityRequiresReview]
                get
                { 
                    return baseAddressPrefixFilters;
                } 
            } 

 
            internal string CurrentVirtualPath { get { return currentVirtualPath; } }

            internal static object ThisLock
            { 
                get
                { 
                    return syncRoot; 
                }
            } 

            internal bool IsRecycling
            {
                get 
                {
                    return isRecycling; 
                } 
            }
 
            internal void EnsureServiceAvailable(string normalizedVirtualPath)
            {
                TryDebugPrint("HostingManager.EnsureServiceAvailable(" + normalizedVirtualPath + ")");
 
                ServiceActivationInfo activationInfo = null;
 
                // 1. Use global lock to find ServiceActivationInfo 
                lock (ThisLock)
                { 
                    if (directory.TryGetValue(normalizedVirtualPath, out activationInfo))
                    {
                        if (activationInfo.Service != null)
                            return; 
                    }
 
                    FailActivationIfRecyling(normalizedVirtualPath); 

                    if (activationInfo == null) 
                    {
                        // Check service file existence.
                        if (!HostingEnvironment.VirtualPathProvider.FileExists(normalizedVirtualPath))
                        { 
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                                new EndpointNotFoundException( 
                                    SR.GetString( 
                                        SR.Hosting_ServiceNotExist,
                                        VirtualPathUtility.ToAbsolute(normalizedVirtualPath, HostingEnvironmentWrapper.ApplicationVirtualPath)))); 
                        }

                        activationInfo = new ServiceActivationInfo(normalizedVirtualPath);
                        directory.Add(normalizedVirtualPath, activationInfo); 
                    }
                } 
 
                // 2. Use local lock to activate the service.
                ServiceHostBase newService = null; 
                lock (activationInfo)
                {
                    if (activationInfo.Service != null)
                    { 
                        // The service has been activated by another thread.
                        return; 
                    } 

                    FailActivationIfRecyling(normalizedVirtualPath); 
                    try
                    {
                        CheckMemoryGates();
 
                        newService = ActivateService(normalizedVirtualPath);
 
                        // We need to lock and check IsRecycling here because it could race with Abort method. 
                        lock (ThisLock)
                        { 
                            if (!IsRecycling)
                            {
                                activationInfo.Service = newService;
                            } 
                        }
 
                        if (DiagnosticUtility.ShouldTraceInformation) 
                        {
                            TraceUtility.TraceEvent( 
                                TraceEventType.Information, TraceCode.WebHostServiceActivated,
                                new StringTraceRecord("VirtualPath", VirtualPathUtility.ToAbsolute(normalizedVirtualPath, HostingEnvironmentWrapper.ApplicationVirtualPath)), this, (Exception)null);
                        }
                    } 
                    catch (HttpCompileException ex)
                    { 
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 
                            new ServiceActivationException(SR.GetString(SR.Hosting_ServiceCannotBeActivated, VirtualPathUtility.ToAbsolute(normalizedVirtualPath, HostingEnvironmentWrapper.ApplicationVirtualPath), ex.Message), ex));
                    } 
                    catch (ServiceActivationException)
                    {
                        throw;
                    } 
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception ex) 
                    { 
                        // If it is a fatal exception, don't wrap it.
                        if (DiagnosticUtility.IsFatal(ex)) 
                        {
                            throw;
                        }
 
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new ServiceActivationException(SR.GetString(SR.Hosting_ServiceCannotBeActivated, VirtualPathUtility.ToAbsolute(normalizedVirtualPath, HostingEnvironmentWrapper.ApplicationVirtualPath), ex.Message), ex)); 
                    } 
                    finally
                    { 
                        currentVirtualPath = null;
                    }
                }
 
                if (activationInfo.Service == null)
                { 
                    DiagnosticUtility.DebugAssert( 
                        IsRecycling && (newService != null),
                        "Must happen in recycling state, also new service must has been created."); 

                    newService.Abort();
                }
 
                FailActivationIfRecyling(normalizedVirtualPath);
            } 
 
            /// 
            /// Critical - Accesses minFreeMemoryPercentageToActivateService, calls Check 
            /// Safe - no input / output, safe operation if called with administrator-provided value
            /// 
            [SecurityCritical, SecurityTreatAsSafe]
            void CheckMemoryGates() 
            {
                ServiceMemoryGates.Check(this.minFreeMemoryPercentageToActivateService); 
            } 

            ServiceHostBase ActivateService(string normalizedVirtualPath) 
            {
                ServiceHostBase service = CreateService(normalizedVirtualPath);
                service.Closed += this.OnServiceClosed;
 
                FailActivationIfRecyling(normalizedVirtualPath);
 
                try 
                {
                    service.Open(); 
                }
                finally
                {
                    if (service.State != CommunicationState.Opened) 
                    {
                        // Abort the service to clear possible cached information. 
                        service.Abort(); 
                    }
                } 

                return service;
            }
 
            /// 
            /// Critical - Uses SecurityCritical method UnsafeImpersonate to establish the impersonation context 
            /// Safe - does not leak anything, does not let caller influence impersonation 
            /// 
            // Why this triple try blocks instead of using "using" statement: 
            // 1. "using" will do the impersonation prior to entering the try,
            //    which leaves an opertunity to Thread.Abort this thread and get it to exit the method still impersonated.
            // 2. put the assignment of unsafeImpersonate in a finally block
            //    in order to prevent Threat.Abort after impersonation but before the assignment. 
            // 3. the finally of a "using" doesn't run until exception filters higher up the stack have executed.
            //    they will do so in the impersonated context if an exception is thrown inside the try. 
            // In sumary, this should prevent the thread from existing this method well still impersonated. 
            [SecurityCritical, SecurityTreatAsSafe]
            string GetCompiledCustomString(string normalizedVirtualPath) 
            {
                try
                {
                    IDisposable unsafeImpersonate = null; 
                    try
                    { 
                        try { } 
                        finally
                        { 
                            unsafeImpersonate = HostingEnvironmentWrapper.UnsafeImpersonate();
                        }
                        return BuildManager.GetCompiledCustomString(normalizedVirtualPath);
                    } 
                    finally
                    { 
                        if (null != unsafeImpersonate) 
                        {
                            unsafeImpersonate.Dispose(); 
                        }
                    }
                }
                catch { 
                    throw;
                } 
            } 

            static Uri[] FilterBaseAddressList(Uri[] baseAddresses, Uri[] prefixFilters) 
            {
                // Precondition assumption:
                // filterAddresses only contains one Uri per scheme.
                // Enforced by throwing exception when duplicates found. 
                List results = new List();
                Dictionary schemeMappings = new Dictionary(); 
 
                foreach (Uri filterUri in prefixFilters)
                { 
                    if (!schemeMappings.ContainsKey(filterUri.Scheme))
                    {
                        schemeMappings.Add(filterUri.Scheme, filterUri);
                    } 
                    else
                    { 
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.BaseAddressDuplicateScheme, filterUri.Scheme))); 
                    }
                } 

                foreach (Uri baseUri in baseAddresses)
                {
                    string scheme = baseUri.Scheme; 
                    if (schemeMappings.ContainsKey(scheme))
                    { 
                        Uri filterUri = schemeMappings[scheme]; 

                        if ((baseUri.Port == filterUri.Port) && 
                           (string.Compare(baseUri.Host, filterUri.Host, StringComparison.OrdinalIgnoreCase) == 0))
                        {
                            results.Add(baseUri);
                        } 
                    }
                    else 
                    { 
                        results.Add(baseUri);
                    } 
                }
                return results.ToArray();
            }
 
            ServiceHostBase CreateService(string normalizedVirtualPath)
            { 
                // 1. Compile the service 
                // The expected format is:
                //      || 
                // The first two cannot be empty.
                string compiledString = GetCompiledCustomString(normalizedVirtualPath);

                if (string.IsNullOrEmpty(compiledString)) 
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.Hosting_CompilationResultEmpty, normalizedVirtualPath))); 
                } 

                TryDebugPrint("HostingManager.CreateService() BuildManager.GetCompiledCustomString() returned compiledString: " + compiledString); 
                string[] compiledStrings = compiledString.Split(ServiceParserDelimiter.ToCharArray());
                if (compiledStrings.Length < 3)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.Hosting_CompilationResultInvalid, normalizedVirtualPath))); 
                }
 
                // 2. Add the base addresses 
                Uri[] baseAddresses = HostedTransportConfigurationManager.GetBaseAddresses(compiledStrings[0]);
                Uri[] prefixFilters = ServiceHostingEnvironment.PrefixFilters; 

                if (prefixFilters != null && prefixFilters.Length > 0)
                {
                    baseAddresses = FilterBaseAddressList(baseAddresses, prefixFilters); 
                }
 
                // We generate the virtual path from compiled string so that it will has correct case. 
                normalizedVirtualPath = VirtualPathUtility.ToAppRelative(compiledStrings[0], HostingEnvironmentWrapper.ApplicationVirtualPath);
 
                // Get the current virtual path (full path except for the .svc file name).
                currentVirtualPath = compiledStrings[0].Substring(0, compiledStrings[0].LastIndexOf('/'));
                if (currentVirtualPath.Length == 0)
                { 
                    currentVirtualPath = "/";
                } 
 
                // 3. Create service
                ServiceHostBase service = null; 
                ServiceHostFactoryBase factory = null;
                if (string.IsNullOrEmpty(compiledStrings[1]))
                    factory = new ServiceHostFactory();
                else 
                {
                    Type compiledType = Type.GetType(compiledStrings[1]); 
                    if (!typeof(ServiceHostFactoryBase).IsAssignableFrom(compiledType)) 
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.Hosting_IServiceHostNotImplemented, compiledStrings[1])));
 
                    ConstructorInfo ctor = compiledType.GetConstructor(new Type[] { });
                    if (ctor == null)
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.Hosting_NoDefaultCtor, compiledStrings[1])));
 
                    factory = (ServiceHostFactoryBase)ctor.Invoke(new object[] { });
                } 
 
                // Push assembly context into ServiceHostFactory
                if (factory is ServiceHostFactory) 
                {
                    for (int index = 3; index < compiledStrings.Length; ++index)
                        ((ServiceHostFactory)factory).AddAssemblyReference(compiledStrings[index]);
                } 

 
 
                service = factory.CreateServiceHost(compiledStrings[2], baseAddresses);
                if (service == null) 
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.Hosting_ServiceHostBaseIsNull, compiledStrings[2])));
                }
 
                // 4. Create VirtualPathExtension for ServiceHostBase
                VirtualPathExtension virtualPathExtension = new VirtualPathExtension(normalizedVirtualPath); 
                service.Extensions.Add(virtualPathExtension); 

                return service; 
            }

            void FailActivationIfRecyling(string normalizedVirtualPath)
            { 
                if (IsRecycling)
                { 
                    InvalidOperationException exception = new InvalidOperationException(SR.GetString( 
                        SR.Hosting_EnvironmentShuttingDown, normalizedVirtualPath,
                        HostingEnvironmentWrapper.ApplicationVirtualPath)); 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ServiceActivationException(exception.Message, exception));
                }
            }
 
            public void Stop(bool immediate)
            { 
                if (!immediate) 
                {
                    // Try to wait for all requests to be done, then close all the ServiceHosts. 
                    IOThreadScheduler.ScheduleCallback(new WaitCallback(WaitAndCloseCallback), this);
                }
                else
                { 
                    // Will execute here only if HostingEnvironment.UnregisterObject hasn't been called.
                    Abort(); 
                } 
            }
 
            static bool canDebugPrint = true;

            [Conditional("DEBUG")]
            static void TryDebugPrint(string message) 
            {
                if (canDebugPrint) 
                { 
                    try
                    { 
                        Debug.Print(message);
                    }
                    catch (SecurityException e)
                    { 
                        canDebugPrint = false;
 
                        // not re-throwing on purpose 
                        if (DiagnosticUtility.ShouldTraceWarning)
                        { 
                            DiagnosticUtility.ExceptionUtility.TraceHandledException(e, TraceEventType.Warning);
                        }
                    }
                } 
            }
 
            void OnServiceClosed(object sender, EventArgs e) 
            {
                lock (ThisLock) 
                {
                    if (!isRecycling)
                    {
                        ServiceHostBase closedService = (ServiceHostBase)sender; 
                        string key = null;
                        foreach (string address in directory.Keys) 
                        { 
                            if (directory[address].Service == closedService)
                            { 
                                key = address;
                                break;
                            }
                        } 

                        if (key != null) 
                        { 
                            directory.Remove(key);
                        } 
                    }
                }
            }
 
            internal void NotifyAllRequestDone()
            { 
                if (isStopStarted) 
                {
                    allRequestDoneInStop.Set(); 
                }
            }

            void Abort() 
            {
                allRequestDoneInStop.Set(); 
 
                List> list = null;
                lock (ThisLock) 
                {
                    // We need to set isRecycling inside lock because we want to make sure no
                    // new request will be handed once we start to shut down.
                    isRecycling = true; 

                    if (UnregisterObject()) 
                    { 
                        return;
                    } 

                    // Make a copy of directory.
                    list = new List>(directory);
                } 

                // Enumerate all the ServiceHosts, abort them one by one. 
                for (int i = 0; i < list.Count; i++) 
                {
                    //If it is called with immediate=true, then we will abort all the services. 
                    if (list[i].Value.Service != null)
                    {
                        try
                        { 
                            list[i].Value.Service.Abort();
                        } 
                        catch (Exception exception) 
                        {
                            if (!(DiagnosticUtility.IsFatal(exception))) 
                            {
                                LogServiceCloseError(list[i].Key, exception);
                            }
 
                            throw;
                        } 
                    } 

                    RemoveCachedService(list[i].Key); 
                }
            }

            void WaitAndCloseCallback(object obj) 
            {
                isStopStarted = true; 
                if (ServiceHostingEnvironment.requestCount != 0) 
                {
                    allRequestDoneInStop.WaitOne(); 
                }

                List> list = null;
                lock (ThisLock) 
                {
                    if (UnregisterObject()) 
                    { 
                        return;
                    } 
                    // Make a copy of directory.
                    list = new List>(directory);
                }
 
                // Enumerate all the ServiceHosts, close them one by one.
                AsyncCallback callback = null; 
                for (int i = 0; i < list.Count; i++) 
                {
                    if (list[i].Value.Service != null) 
                    {
                        // We will try to close all the services asynchronously.
                        if (callback == null)
                        { 
                            callback = DiagnosticUtility.ThunkAsyncCallback(new AsyncCallback(OnCloseService));
                        } 
 
                        IAsyncResult result = null;
                        try 
                        {
                            // Set timeout to MaxValue and so that only ASP.NET setting is used.
                            result = list[i].Value.Service.BeginClose(TimeSpan.MaxValue, callback, list[i]);
                        } 
                        catch (Exception exception)
                        { 
                            // If BeginClose throw an exception, abort should already have been called. 
                            if (!(DiagnosticUtility.IsFatal(exception)))
                            { 
                                LogServiceCloseError(list[i].Key, exception);
                            }

                            if (!(exception is CommunicationException)) 
                            {
                                throw; 
                            } 

                            // We will remove the service and continue processing other services 
                            // if a CommunicationException happened.
                            RemoveCachedService(list[i].Key);
                        }
 
                        if (result != null && result.CompletedSynchronously)
                        { 
                            EndCloseService(result); 
                        }
                    } 
                    else
                    {
                        RemoveCachedService(list[i].Key);
                    } 
                }
            } 
 
            void OnCloseService(IAsyncResult result)
            { 
                if (!result.CompletedSynchronously)
                {
                    EndCloseService(result);
                } 
            }
 
            void EndCloseService(IAsyncResult result) 
            {
                KeyValuePair item = (KeyValuePair)result.AsyncState; 

                try
                {
                    item.Value.Service.EndClose(result); 
                }
                catch (Exception exception) 
                { 
                    //If EndClose throw an exception, abort should already have been called.
                    if (DiagnosticUtility.IsFatal(exception)) 
                    {
                        throw;
                    }
 
                    LogServiceCloseError(item.Key, exception);
                } 
 
                RemoveCachedService(item.Key);
            } 

            void RemoveCachedService(string path)
            {
                lock (ThisLock) 
                {
                    // At the time when we just removed all the service, we will unregister 
                    // from HostingEnvironement. 
                    directory.Remove(path);
 
                    UnregisterObject();
                }
            }
 
            void LogServiceCloseError(string virtualPath, Exception exception)
            { 
                if (DiagnosticUtility.ShouldTraceError) 
                {
                    TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.WebHostServiceCloseFailed, 
                        new StringTraceRecord("VirtualPath", VirtualPathUtility.ToAbsolute(virtualPath, HostingEnvironmentWrapper.ApplicationVirtualPath)),
                        this, exception);
                }
            } 

            ///  
            /// Critical - Uses HostingEnvironmentWrapper.UnsafeRegisterObject which is critical 
            /// Safe - doesn't allow the caller to control the variable -- only registers 'this'
            ///  
            [SecurityCritical, SecurityTreatAsSafe]
            void RegisterObject()
            {
                HostingEnvironmentWrapper.UnsafeRegisterObject(this); 
            }
 
            // Note : this method should only be called under lock of ThisLock. 
            /// 
            /// Critical - Uses HostingEnvironmentWrapper.UnsafeUnregisterObject which is critical 
            /// Safe - doesn't allow the caller to control the variable -- only unregisters 'this'
            /// 
            [SecurityCritical, SecurityTreatAsSafe]
            bool UnregisterObject() 
            {
                if (directory.Count == 0) 
                { 
                    if (!isUnregistered)
                    { 
                        isUnregistered = true;
                        HostingEnvironmentWrapper.UnsafeUnregisterObject(this);
                    }
                    return true; 
                }
 
                return false; 
            }
 
            class ExtensionHelper
            {
                readonly IDictionary buildProviders;
 
                /// 
                /// Critical - loads config through an elevation and stores results 
                /// Safe - stores results in BuildProviderInfo instances which restrict access to the BuildProvider config object 
                /// 
                [SecurityCritical, SecurityTreatAsSafe] 
                public ExtensionHelper()
                {
                    buildProviders = new Dictionary(8, StringComparer.OrdinalIgnoreCase);
                    CompilationSection compilationSection = (CompilationSection)ConfigurationHelpers.UnsafeGetSectionFromWebConfigurationManager("system.web/compilation"); 
                    foreach (System.Web.Configuration.BuildProvider buildProvider in compilationSection.BuildProviders)
                    { 
                        buildProviders.Add(buildProvider.Extension, new BuildProviderInfo(buildProvider)); 
                    }
                } 

                /// 
                /// Review - can be called outside of user context.
                ///  
                [SecurityRequiresReview]
                public bool GetExtensionSupported(string extension) 
                { 
                    BuildProviderInfo info;
                    if (!buildProviders.TryGetValue(extension, out info)) 
                        return false;

                    return info.IsSupported;
                } 
            }
        } 
 
        class ServiceActivationInfo
        { 
            string virtualPath;
            ServiceHostBase service;
            public ServiceActivationInfo(string virtualPath)
            { 
                this.virtualPath = virtualPath;
            } 
 
            public ServiceHostBase Service
            { 
                get
                {
                    return this.service;
                } 

                set 
                { 
                    this.service = value;
                } 
            }
        }

        class BuildProviderInfo 
        {
            ///  
            /// Critical - stores the result of an elevation 
            /// 
            [SecurityCritical] 
            System.Web.Configuration.BuildProvider buildProvider;

            bool initialized;
            bool isSupported; 
            object thisLock = new object();
 
            ///  
            /// Critical - stores the result of an elevation
            /// Safe - stores it in a Critical field 
            /// 
            [SecurityCritical, SecurityTreatAsSafe]
            public BuildProviderInfo(System.Web.Configuration.BuildProvider buildProvider)
            { 
                this.buildProvider = buildProvider;
            } 
 
            /// 
            /// Review - can be called outside of user context. 
            /// 
            [SecurityRequiresReview]
            void EnsureInitialized()
            { 
                if (initialized)
                    return; 
 
                lock (thisLock)
                { 
                    if (initialized)
                        return;

                    Type type = Type.GetType(BuildProviderType, false); 
                    if (type == null)
                    { 
                        Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); 
                        for (int i = 0; i < assemblies.Length; i++)
                        { 
                            type = assemblies[i].GetType(BuildProviderType, false);
                            if (type != null)
                                break;
                        } 
                    }
 
                    if (type != null) 
                    {
                        object[] attributes = System.ServiceModel.Description.ServiceReflector.GetCustomAttributes(type, typeof(ServiceActivationBuildProviderAttribute), true); 
                        if (attributes.Length > 0)
                        {
                            isSupported = true;
                        } 
                    }
 
                    ClearBuildProvider(); 
                    initialized = true;
                } 
            }

            string BuildProviderType
            { 
                /// 
                /// Critical - accesses the SecurityCritical buildProvider field 
                /// Safe - returns the Type property, which is allowed; doesn't leak the BuildProvider instance 
                /// 
                [SecurityCritical, SecurityTreatAsSafe] 
                get { return buildProvider.Type; }
            }

            ///  
            /// Review - can be called outside of user context.
            ///  
            [SecurityRequiresReview] 
            public bool IsSupported
            { 
                get
                {
                    EnsureInitialized();
                    return isSupported; 
                }
            } 
 
            /// 
            /// Critical - accesses the SecurityCritical buildProvider field 
            ///          - Can be called outside user context.
            /// Safe - just clears it, doesn't leak anything
            /// 
            [SecurityCritical, SecurityTreatAsSafe] 
            void ClearBuildProvider()
            { 
                this.buildProvider = null; 
            }
        } 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK