OutOfProcStateClientManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / xsp / System / Web / State / OutOfProcStateClientManager.cs / 1 / OutOfProcStateClientManager.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Web.SessionState { 
    using System.Collections; 
    using System.Configuration;
    using System.Web.Configuration; 
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary; 
    using System.Text;
    using System.Threading; 
    using System.Web; 
    using System.Web.Caching;
    using System.Web.Util; 
    using System.Xml;
    using System.Collections.Specialized;
    using System.Configuration.Provider;
    using System.Globalization; 
    using System.Web.Management;
    using System.Web.Hosting; 
 
    internal sealed class OutOfProcSessionStateStore : SessionStateStoreProviderBase {
        internal static readonly IntPtr INVALID_SOCKET = UnsafeNativeMethods.INVALID_HANDLE_VALUE; 
        internal static readonly int    WHIDBEY_MAJOR_VERSION = 2;
        internal const int              STATE_NETWORK_TIMEOUT_DEFAULT = 10; // in sec

        static string       s_uribase; 
        static int          s_networkTimeout;
        #pragma warning disable 0649 
        static ReadWriteSpinLock            s_lock; 
        #pragma warning restore 0649
        static bool         s_oneTimeInited; 
        static StateServerPartitionInfo s_singlePartitionInfo;
        static PartitionManager s_partitionManager;
        static bool         s_usePartition;
        static EventHandler s_onAppDomainUnload; 

        // We keep these info because we don't want to hold on to the config object. 
        static string       s_configPartitionResolverType; 
        static string       s_configStateConnectionString;
        static string       s_configStateConnectionStringFileName; 
        static int          s_configStateConnectionStringLineNumber;

        // Per request info
        IPartitionResolver  _partitionResolver; 
        StateServerPartitionInfo    _partitionInfo;
 
 
        internal override void Initialize(string name, NameValueCollection config, IPartitionResolver partitionResolver) {
            _partitionResolver = partitionResolver; 
            Initialize(name, config);
        }

        public override void Initialize(string name, NameValueCollection config) { 
            if (String.IsNullOrEmpty(name))
                name = "State Server Session State Provider"; 
            base.Initialize(name, config); 

            if (!s_oneTimeInited) { 
                s_lock.AcquireWriterLock();
                try {
                    if (!s_oneTimeInited) {
                        OneTimeInit(); 
                    }
                } 
                finally { 
                    s_lock.ReleaseWriterLock();
                } 
            }

            if (!s_usePartition) {
                // For single partition, the connection info won't change from request to request 
                Debug.Assert(s_partitionManager == null);
                _partitionInfo = s_singlePartitionInfo; 
            } 
        }
 
        void OneTimeInit() {
            SessionStateSection config = RuntimeConfig.GetAppConfig().SessionState;

            s_configPartitionResolverType = config.PartitionResolverType; 
            s_configStateConnectionString = config.StateConnectionString;
            s_configStateConnectionStringFileName = config.ElementInformation.Properties["stateConnectionString"].Source; 
            s_configStateConnectionStringLineNumber = config.ElementInformation.Properties["stateConnectionString"].LineNumber; 

            if (_partitionResolver == null) { 
                String stateConnectionString = config.StateConnectionString;

                SessionStateModule.ReadConnectionString(config, ref stateConnectionString, "stateConnectionString");
 
                s_singlePartitionInfo = (StateServerPartitionInfo)CreatePartitionInfo(stateConnectionString);
            } 
            else { 
                s_usePartition = true;
                s_partitionManager = new PartitionManager(new CreatePartitionInfo(CreatePartitionInfo)); 
            }

            s_networkTimeout = (int)config.StateNetworkTimeout.TotalSeconds;
 
            string appId = HttpRuntime.AppDomainAppIdInternal;
            string idHash = MachineKeySection.HashAndBase64EncodeString(appId); 
 
            // Make sure that we have a absolute URI, some hosts(Cassini) don't provide this.
            if (appId.StartsWith("/", StringComparison.Ordinal)) { 
                s_uribase = appId + "(" + idHash + ")/";
            }
            else {
                s_uribase = "/" + appId + "(" + idHash + ")/"; 
            }
 
            // We only need to do this in one instance 
            s_onAppDomainUnload = new EventHandler(OnAppDomainUnload);
            Thread.GetDomain().DomainUnload += s_onAppDomainUnload; 

            s_oneTimeInited = true;
        }
 
        void OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) {
            Debug.Trace("OutOfProcSessionStateStore", "OnAppDomainUnload called"); 
 
            Thread.GetDomain().DomainUnload -= s_onAppDomainUnload;
 
            if (_partitionResolver == null) {
                if (s_singlePartitionInfo != null) {
                    s_singlePartitionInfo.Dispose();
                } 
            }
            else { 
                if (s_partitionManager != null) { 
                    s_partitionManager.Dispose();
                } 
            }
        }

        internal IPartitionInfo CreatePartitionInfo(string stateConnectionString) { 
            string  server;
            int     port; 
            int     hr; 

            try { 
                /*
                 * stateConnection string has the following format:
                 *
                 *     "tcpip=:" 
                 */
 
 
                string [] parts = stateConnectionString.Split(new char[] {'='});
                if (parts.Length != 2 || parts[0] != "tcpip") { 
                    throw new ArgumentException("stateConnectionString");
                }

                parts = parts[1].Split(new char[] {':'}); 
                if (parts.Length != 2) {
                    throw new ArgumentException("stateConnectionString"); 
                } 

                server = parts[0]; 
                port = (int) System.UInt16.Parse(parts[1], CultureInfo.InvariantCulture);

                // At v1, we won't accept server name that has non-ascii characters
                for (int i = 0; i < server.Length; ++i) { 
                    if (server[i] > 0x7F) {
                        throw new ArgumentException("stateConnectionString"); 
                    } 
                }
 
            }
            catch {
                if (s_usePartition) {
                    throw new HttpException( 
                           SR.GetString(SR.Error_parsing_state_server_partition_resolver_string, s_configPartitionResolverType));
                } 
                else { 
                    throw new ConfigurationErrorsException(
                            SR.GetString(SR.Invalid_value_for_sessionstate_stateConnectionString, s_configStateConnectionString), 
                            s_configStateConnectionStringFileName, s_configStateConnectionStringLineNumber);
                }
            }
 
            hr = UnsafeNativeMethods.SessionNDConnectToService(server);
            if (hr != 0) { 
                throw CreateConnectionException(server, port, hr); 
            }
 
            return new StateServerPartitionInfo(new ResourcePool(new TimeSpan(0, 0, 5), int.MaxValue),
                                            server,
                                            port);
 
        }
 
        internal static HttpException CreateConnectionException(string server, int port, int hr) { 
            if (s_usePartition) {
                return new HttpException( 
                        SR.GetString(SR.Cant_make_session_request_partition_resolver,
                                    s_configPartitionResolverType, server, port.ToString(CultureInfo.InvariantCulture)), hr);
            }
            else { 
                return new HttpException(
                    SR.GetString(SR.Cant_make_session_request), hr); 
            } 
        }
 

        public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback) {
            return false;
        } 

        public override void Dispose() { 
        } 

        public override void InitializeRequest(HttpContext context) { 
            if (s_usePartition) {
                // For multiple partition case, the connection info can change from request to request
                Debug.Assert(_partitionResolver != null);
                _partitionInfo = null; 
            }
        } 
 
        void MakeRequest(
                UnsafeNativeMethods.StateProtocolVerb   verb, 
                String                                  id,
                UnsafeNativeMethods.StateProtocolExclusive    exclusiveAccess,
                int                                     extraFlags,
                int                                     timeout, 
                int                                     lockCookie,
                byte[]                                  buf, 
                int                                     cb, 
                int                                     networkTimeout,
                out UnsafeNativeMethods.SessionNDMakeRequestResults results) { 

            int                         hr;
            string                      uri;
            OutOfProcConnection         conn = null; 
            HandleRef                   socketHandle;
            bool                        checkVersion = false; 
 
            Debug.Assert(timeout <= SessionStateModule.MAX_CACHE_BASED_TIMEOUT_MINUTES, "item.Timeout <= SessionStateModule.MAX_CACHE_BASED_TIMEOUT_MINUTES");
 
            SessionIDManager.CheckIdLength(id, true /* throwOnFail */);

            if (_partitionInfo == null) {
                Debug.Assert(s_partitionManager != null); 
                Debug.Assert(_partitionResolver != null);
 
                _partitionInfo = (StateServerPartitionInfo)s_partitionManager.GetPartition(_partitionResolver, id); 

                // If its still null, we give up 
                if (_partitionInfo == null) {
                    throw new HttpException(SR.GetString(SR.Bad_partition_resolver_connection_string, "PartitionManager"));
                }
            } 

            // Need to make sure we dispose the connection if anything goes wrong 
            try { 
                conn = (OutOfProcConnection)_partitionInfo.RetrieveResource();
                if (conn != null) { 
                    socketHandle = new HandleRef(this, conn._socketHandle.Handle);
                }
                else {
                    socketHandle = new HandleRef(this, INVALID_SOCKET); 
                }
 
                if (_partitionInfo.StateServerVersion == -1) { 
                    // We don't need locking here because it's okay to have two
                    // requests initializing s_stateServerVersion. 
                    checkVersion = true;
                }

                Debug.Trace("OutOfProcSessionStateStoreMakeRequest", 
                            "Calling MakeRequest, " +
                            "socket=" + (IntPtr)socketHandle.Handle + 
                            "verb=" + verb + 
                            " id=" + id +
                            " exclusiveAccess=" + exclusiveAccess + 
                            " timeout=" + timeout +
                            " buf=" + ((buf != null) ? "non-null" : "null") +
                            " cb=" + cb +
                            " checkVersion=" + checkVersion + 
                            " extraFlags=" + extraFlags);
 
                // Have to UrlEncode id because it may contain non-URL-safe characters 
                uri = HttpUtility.UrlEncode(s_uribase + id);
 
                hr = UnsafeNativeMethods.SessionNDMakeRequest(
                        socketHandle, _partitionInfo.Server, _partitionInfo.Port, networkTimeout, verb, uri,
                        exclusiveAccess, extraFlags, timeout, lockCookie,
                        buf, cb, checkVersion, out results); 

                Debug.Trace("OutOfProcSessionStateStoreMakeRequest", "MakeRequest returned: " + 
                            "hr=" + hr + 
                            " socket=" + (IntPtr)results.socket +
                            " httpstatus=" + results.httpStatus + 
                            " timeout=" + results.timeout +
                            " contentlength=" + results.contentLength +
                            " uri=" + (IntPtr)results.content +
                            " lockCookie=" + results.lockCookie + 
                            " lockDate=" + string.Format("{0:x}", results.lockDate) +
                            " lockAge=" + results.lockAge + 
                            " stateServerMajVer=" + results.stateServerMajVer + 
                            " actionFlags=" + results.actionFlags);
 
                if (conn != null) {
                    if (results.socket == INVALID_SOCKET) {
                        conn.Detach();
                        conn = null; 
                    }
                    else if (results.socket != socketHandle.Handle) { 
                        // The original socket is no good.  We've got a new one. 
                        // Pleae note that EnsureConnected has closed the bad
                        // one already. 
                        conn._socketHandle = new HandleRef(this, results.socket);
                    }
                }
                else if (results.socket != INVALID_SOCKET) { 
                    conn = new OutOfProcConnection(results.socket);
                } 
 
                if (conn != null) {
                    _partitionInfo.StoreResource(conn); 
                }
            }
            catch {
                // We just need to dispose the connection if anything bad happened 
                if (conn != null) {
                    conn.Dispose(); 
                } 

                throw; 
            }

            if (hr != 0) {
                HttpException e = CreateConnectionException(_partitionInfo.Server, _partitionInfo.Port, hr); 

                string phase = null; 
 
                switch (results.lastPhase) {
                case (int)UnsafeNativeMethods.SessionNDMakeRequestPhase.Initialization: 
                    phase = SR.GetString(SR.State_Server_detailed_error_phase0);
                    break;

                case (int)UnsafeNativeMethods.SessionNDMakeRequestPhase.Connecting: 
                    phase = SR.GetString(SR.State_Server_detailed_error_phase1);
                    break; 
 
                case (int)UnsafeNativeMethods.SessionNDMakeRequestPhase.SendingRequest:
                    phase = SR.GetString(SR.State_Server_detailed_error_phase2); 
                    break;

                case (int)UnsafeNativeMethods.SessionNDMakeRequestPhase.ReadingResponse:
                    phase = SR.GetString(SR.State_Server_detailed_error_phase3); 
                    break;
 
                default: 
                    Debug.Assert(false, "Unknown results.lastPhase: " + results.lastPhase);
                    break; 
                }

                WebBaseEvent.RaiseSystemEvent(SR.GetString(
                            SR.State_Server_detailed_error, 
                            phase,
                            "0x" + hr.ToString("X08", CultureInfo.InvariantCulture), 
                            cb.ToString(CultureInfo.InvariantCulture)), 
                            this, WebEventCodes.WebErrorOtherError, WebEventCodes.StateServerConnectionError, e);
 
                throw e;
            }

            if (results.httpStatus == 400) { 
                if (s_usePartition) {
                    throw new HttpException( 
                        SR.GetString(SR.Bad_state_server_request_partition_resolver, 
                                    s_configPartitionResolverType, _partitionInfo.Server, _partitionInfo.Port.ToString(CultureInfo.InvariantCulture)));
                } 
                else {
                    throw new HttpException(
                        SR.GetString(SR.Bad_state_server_request));
                } 
            }
 
            if (checkVersion) { 
                _partitionInfo.StateServerVersion = results.stateServerMajVer;
                if (_partitionInfo.StateServerVersion < WHIDBEY_MAJOR_VERSION) { 
                    // We won't work with versions lower than Whidbey
                    if (s_usePartition) {
                        throw new HttpException(
                            SR.GetString(SR.Need_v2_State_Server_partition_resolver, 
                                        s_configPartitionResolverType, _partitionInfo.Server, _partitionInfo.Port.ToString(CultureInfo.InvariantCulture)));
                    } 
                    else { 
                        throw new HttpException(
                            SR.GetString(SR.Need_v2_State_Server)); 
                    }
                }
            }
        } 

        internal SessionStateStoreData DoGet(HttpContext context, 
                                            String id, 
                                            UnsafeNativeMethods.StateProtocolExclusive exclusiveAccess,
                                            out bool locked, 
                                            out TimeSpan lockAge,
                                            out object lockId,
                                            out SessionStateActions actionFlags) {
            SessionStateStoreData   item = null; 
            UnmanagedMemoryStream   stream;
            int                     contentLength; 
            UnsafeNativeMethods.SessionNDMakeRequestResults results; 

            // Set default return values 
            locked = false;
            lockId = null;
            lockAge = TimeSpan.Zero;
            actionFlags = 0; 
            results.content = IntPtr.Zero;
 
            try { 
                MakeRequest(UnsafeNativeMethods.StateProtocolVerb.GET,
                            id, exclusiveAccess, 0, 0, 0, 
                            null, 0, s_networkTimeout, out results);

                switch (results.httpStatus) {
                    case 200: 
                        /* item found, deserialize it */
                        contentLength = results.contentLength; 
                        if (contentLength > 0) { 
                            unsafe {
                                stream = new UnmanagedMemoryStream((byte*)results.content, contentLength); 
                            }

                            item = SessionStateUtility.Deserialize(context, stream);
                            stream.Close(); 

                            lockId = results.lockCookie; 
                            actionFlags = (SessionStateActions) results.actionFlags; 
                        }
 
                        break;

                    case 423:
                        /* state locked, return lock information */ 

                        if (0 <= results.lockAge) { 
                            if (results.lockAge < Sec.ONE_YEAR) { 
                                lockAge = new TimeSpan(0, 0, results.lockAge);
                            } 
                            else {
                                lockAge = TimeSpan.Zero;
                            }
                        } 
                        else {
                            DateTime now = DateTime.Now; 
                            if (0 < results.lockDate && results.lockDate < now.Ticks) { 
                                lockAge = now - new DateTime(results.lockDate);
                            } 
                            else {
                                lockAge = TimeSpan.Zero;
                            }
                        } 

                        locked = true; 
                        lockId = results.lockCookie; 

                        Debug.Assert((results.actionFlags & (int)SessionStateActions.InitializeItem) == 0, 
                            "(results.actionFlags & (int)SessionStateActions.InitializeItem) == 0; uninitialized item cannot be locked");
                        break;
                }
            } 
            finally {
                if (results.content != IntPtr.Zero) { 
                    UnsafeNativeMethods.SessionNDFreeBody(new HandleRef(this, results.content)); 
                }
            } 

            return item;
        }
 
        public override SessionStateStoreData  GetItem(HttpContext context,
                                                            String id, 
                                                            out bool locked, 
                                                            out TimeSpan lockAge,
                                                            out object lockId, 
                                                            out SessionStateActions actionFlags) {
            Debug.Trace("OutOfProcSessionStateStore", "Calling Get, id=" + id);

            return DoGet(context, id, UnsafeNativeMethods.StateProtocolExclusive.NONE, 
                        out locked, out lockAge, out lockId, out actionFlags);
        } 
 

        public override SessionStateStoreData  GetItemExclusive(HttpContext context, 
                                                String id,
                                                out bool locked,
                                                out TimeSpan lockAge,
                                                out object lockId, 
                                                out SessionStateActions actionFlags) {
            Debug.Trace("OutOfProcSessionStateStore", "Calling GetExlusive, id=" + id); 
 
            return DoGet(context, id, UnsafeNativeMethods.StateProtocolExclusive.ACQUIRE,
                        out locked, out lockAge, out lockId, out actionFlags); 
        }

        public override void ReleaseItemExclusive(HttpContext context,
                                String id, 
                                object lockId) {
            Debug.Assert(lockId != null, "lockId != null"); 
 
            UnsafeNativeMethods.SessionNDMakeRequestResults results;
            int lockCookie = (int)lockId; 

            Debug.Trace("OutOfProcSessionStateStore", "Calling ReleaseExclusive, id=" + id);
            MakeRequest(UnsafeNativeMethods.StateProtocolVerb.GET, id,
                        UnsafeNativeMethods.StateProtocolExclusive.RELEASE, 0, 0, 
                        lockCookie, null, 0, s_networkTimeout, out results);
 
        } 

        public override void SetAndReleaseItemExclusive(HttpContext context, 
                                    String id,
                                    SessionStateStoreData item,
                                    object lockId,
                                    bool newItem) { 
            UnsafeNativeMethods.SessionNDMakeRequestResults results;
            byte[]          buf; 
            int             length; 
            int             lockCookie;
 
            Debug.Assert(item.Items != null, "item.Items != null");
            Debug.Assert(item.StaticObjects != null, "item.StaticObjects != null");

            Debug.Trace("OutOfProcSessionStateStore", "Calling Set, id=" + id + " sessionItems=" + item.Items + " timeout=" + item.Timeout); 

            try { 
                SessionStateUtility.SerializeStoreData(item, 0, out buf, out length); 
            }
            catch { 
                if (!newItem) {
                    ((SessionStateStoreProviderBase)this).ReleaseItemExclusive(context, id, lockId);
                }
                throw; 
            }
 
            // Save it to the store 

            if (lockId == null) { 
                lockCookie = 0;
            }
            else {
                lockCookie = (int)lockId; 
            }
 
            MakeRequest(UnsafeNativeMethods.StateProtocolVerb.PUT, id, 
                        UnsafeNativeMethods.StateProtocolExclusive.NONE, 0, item.Timeout, lockCookie,
                        buf, length, s_networkTimeout, out results); 
        }

        public override void RemoveItem(HttpContext context,
                                        String id, 
                                        object lockId,
                                        SessionStateStoreData item) { 
            Debug.Assert(lockId != null, "lockId != null"); 
            Debug.Trace("OutOfProcSessionStateStore", "Calling Remove, id=" + id);
 
            UnsafeNativeMethods.SessionNDMakeRequestResults results;
            int             lockCookie = (int)lockId;

            MakeRequest(UnsafeNativeMethods.StateProtocolVerb.DELETE, id, 
                        UnsafeNativeMethods.StateProtocolExclusive.NONE, 0, 0, lockCookie,
                        null, 0, s_networkTimeout, out results); 
 
        }
 
        public override void ResetItemTimeout(HttpContext context, String id) {
            UnsafeNativeMethods.SessionNDMakeRequestResults results;

            Debug.Trace("OutOfProcSessionStateStore", "Calling ResetTimeout, id=" + id); 
            MakeRequest(UnsafeNativeMethods.StateProtocolVerb.HEAD, id,
                        UnsafeNativeMethods.StateProtocolExclusive.NONE, 0, 0, 0, 
                        null, 0, s_networkTimeout, out results); 
        }
 
        public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
        {
            Debug.Assert(timeout <= SessionStateModule.MAX_CACHE_BASED_TIMEOUT_MINUTES, "item.Timeout <= SessionStateModule.MAX_CACHE_BASED_TIMEOUT_MINUTES");
 
            return SessionStateUtility.CreateLegitStoreData(context, null, null, timeout);
        } 
 
        public override void CreateUninitializedItem(HttpContext context, String id, int timeout) {
            UnsafeNativeMethods.SessionNDMakeRequestResults results; 
            byte[]          buf;
            int             length;

            Debug.Trace("OutOfProcSessionStateStore", "Calling CreateUninitializedItem, id=" + id + " timeout=" + timeout); 

            // Create an empty item 
            SessionStateUtility.SerializeStoreData(CreateNewStoreData(context, timeout), 0, out buf, out length); 

            // Save it to the store 
            MakeRequest(UnsafeNativeMethods.StateProtocolVerb.PUT, id,
                        UnsafeNativeMethods.StateProtocolExclusive.NONE,
                        (int)SessionStateItemFlags.Uninitialized, timeout, 0,
                        buf, length, s_networkTimeout, out results); 
        }
 
        // Called during EndRequest event 
        public override void EndRequest(HttpContext context) {
        } 

        class StateServerPartitionInfo : PartitionInfo {
            string  _server;
            int     _port; 
            int     _stateServerVersion;
 
            internal StateServerPartitionInfo(ResourcePool rpool, string server, int port) : base(rpool) { 
                _server = server;
                _port = port; 
                _stateServerVersion = -1;
                Debug.Trace("PartitionInfo", "Created a new info, server=" + server + ", port=" + port);
            }
 
            internal string Server {
                get { return _server; } 
            } 

            internal int Port { 
                get { return _port; }
            }

            internal int StateServerVersion { 
                get { return _stateServerVersion; }
                set { _stateServerVersion = value; } 
            } 

            protected override string TracingPartitionString { 
                get {
                    return Server + ":" + Port;
                }
            } 
        }
 
 
        class OutOfProcConnection : IDisposable {
            internal HandleRef _socketHandle; 

            internal OutOfProcConnection(IntPtr socket) {
                Debug.Assert(socket != OutOfProcSessionStateStore.INVALID_SOCKET,
                             "socket != OutOfProcSessionStateStore.INVALID_SOCKET"); 

                _socketHandle = new HandleRef(this, socket); 
                PerfCounters.IncrementCounter(AppPerfCounter.SESSION_STATE_SERVER_CONNECTIONS); 
            }
 
            ~OutOfProcConnection() {
                Dispose(false);
            }
 
            public void Dispose() {
                Debug.Trace("ResourcePool", "Disposing OutOfProcConnection"); 
 
                Dispose(true);
                System.GC.SuppressFinalize(this); 
            }

            private void Dispose(bool dummy) {
                if (_socketHandle.Handle != OutOfProcSessionStateStore.INVALID_SOCKET) { 
                    UnsafeNativeMethods.SessionNDCloseConnection(_socketHandle);
                    _socketHandle = new HandleRef(this, OutOfProcSessionStateStore.INVALID_SOCKET); 
                    PerfCounters.DecrementCounter(AppPerfCounter.SESSION_STATE_SERVER_CONNECTIONS); 
                }
            } 

            internal void Detach() {
                _socketHandle = new HandleRef(this, OutOfProcSessionStateStore.INVALID_SOCKET);
            } 
        }
    } 
} 

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

namespace System.Web.SessionState { 
    using System.Collections; 
    using System.Configuration;
    using System.Web.Configuration; 
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary; 
    using System.Text;
    using System.Threading; 
    using System.Web; 
    using System.Web.Caching;
    using System.Web.Util; 
    using System.Xml;
    using System.Collections.Specialized;
    using System.Configuration.Provider;
    using System.Globalization; 
    using System.Web.Management;
    using System.Web.Hosting; 
 
    internal sealed class OutOfProcSessionStateStore : SessionStateStoreProviderBase {
        internal static readonly IntPtr INVALID_SOCKET = UnsafeNativeMethods.INVALID_HANDLE_VALUE; 
        internal static readonly int    WHIDBEY_MAJOR_VERSION = 2;
        internal const int              STATE_NETWORK_TIMEOUT_DEFAULT = 10; // in sec

        static string       s_uribase; 
        static int          s_networkTimeout;
        #pragma warning disable 0649 
        static ReadWriteSpinLock            s_lock; 
        #pragma warning restore 0649
        static bool         s_oneTimeInited; 
        static StateServerPartitionInfo s_singlePartitionInfo;
        static PartitionManager s_partitionManager;
        static bool         s_usePartition;
        static EventHandler s_onAppDomainUnload; 

        // We keep these info because we don't want to hold on to the config object. 
        static string       s_configPartitionResolverType; 
        static string       s_configStateConnectionString;
        static string       s_configStateConnectionStringFileName; 
        static int          s_configStateConnectionStringLineNumber;

        // Per request info
        IPartitionResolver  _partitionResolver; 
        StateServerPartitionInfo    _partitionInfo;
 
 
        internal override void Initialize(string name, NameValueCollection config, IPartitionResolver partitionResolver) {
            _partitionResolver = partitionResolver; 
            Initialize(name, config);
        }

        public override void Initialize(string name, NameValueCollection config) { 
            if (String.IsNullOrEmpty(name))
                name = "State Server Session State Provider"; 
            base.Initialize(name, config); 

            if (!s_oneTimeInited) { 
                s_lock.AcquireWriterLock();
                try {
                    if (!s_oneTimeInited) {
                        OneTimeInit(); 
                    }
                } 
                finally { 
                    s_lock.ReleaseWriterLock();
                } 
            }

            if (!s_usePartition) {
                // For single partition, the connection info won't change from request to request 
                Debug.Assert(s_partitionManager == null);
                _partitionInfo = s_singlePartitionInfo; 
            } 
        }
 
        void OneTimeInit() {
            SessionStateSection config = RuntimeConfig.GetAppConfig().SessionState;

            s_configPartitionResolverType = config.PartitionResolverType; 
            s_configStateConnectionString = config.StateConnectionString;
            s_configStateConnectionStringFileName = config.ElementInformation.Properties["stateConnectionString"].Source; 
            s_configStateConnectionStringLineNumber = config.ElementInformation.Properties["stateConnectionString"].LineNumber; 

            if (_partitionResolver == null) { 
                String stateConnectionString = config.StateConnectionString;

                SessionStateModule.ReadConnectionString(config, ref stateConnectionString, "stateConnectionString");
 
                s_singlePartitionInfo = (StateServerPartitionInfo)CreatePartitionInfo(stateConnectionString);
            } 
            else { 
                s_usePartition = true;
                s_partitionManager = new PartitionManager(new CreatePartitionInfo(CreatePartitionInfo)); 
            }

            s_networkTimeout = (int)config.StateNetworkTimeout.TotalSeconds;
 
            string appId = HttpRuntime.AppDomainAppIdInternal;
            string idHash = MachineKeySection.HashAndBase64EncodeString(appId); 
 
            // Make sure that we have a absolute URI, some hosts(Cassini) don't provide this.
            if (appId.StartsWith("/", StringComparison.Ordinal)) { 
                s_uribase = appId + "(" + idHash + ")/";
            }
            else {
                s_uribase = "/" + appId + "(" + idHash + ")/"; 
            }
 
            // We only need to do this in one instance 
            s_onAppDomainUnload = new EventHandler(OnAppDomainUnload);
            Thread.GetDomain().DomainUnload += s_onAppDomainUnload; 

            s_oneTimeInited = true;
        }
 
        void OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) {
            Debug.Trace("OutOfProcSessionStateStore", "OnAppDomainUnload called"); 
 
            Thread.GetDomain().DomainUnload -= s_onAppDomainUnload;
 
            if (_partitionResolver == null) {
                if (s_singlePartitionInfo != null) {
                    s_singlePartitionInfo.Dispose();
                } 
            }
            else { 
                if (s_partitionManager != null) { 
                    s_partitionManager.Dispose();
                } 
            }
        }

        internal IPartitionInfo CreatePartitionInfo(string stateConnectionString) { 
            string  server;
            int     port; 
            int     hr; 

            try { 
                /*
                 * stateConnection string has the following format:
                 *
                 *     "tcpip=:" 
                 */
 
 
                string [] parts = stateConnectionString.Split(new char[] {'='});
                if (parts.Length != 2 || parts[0] != "tcpip") { 
                    throw new ArgumentException("stateConnectionString");
                }

                parts = parts[1].Split(new char[] {':'}); 
                if (parts.Length != 2) {
                    throw new ArgumentException("stateConnectionString"); 
                } 

                server = parts[0]; 
                port = (int) System.UInt16.Parse(parts[1], CultureInfo.InvariantCulture);

                // At v1, we won't accept server name that has non-ascii characters
                for (int i = 0; i < server.Length; ++i) { 
                    if (server[i] > 0x7F) {
                        throw new ArgumentException("stateConnectionString"); 
                    } 
                }
 
            }
            catch {
                if (s_usePartition) {
                    throw new HttpException( 
                           SR.GetString(SR.Error_parsing_state_server_partition_resolver_string, s_configPartitionResolverType));
                } 
                else { 
                    throw new ConfigurationErrorsException(
                            SR.GetString(SR.Invalid_value_for_sessionstate_stateConnectionString, s_configStateConnectionString), 
                            s_configStateConnectionStringFileName, s_configStateConnectionStringLineNumber);
                }
            }
 
            hr = UnsafeNativeMethods.SessionNDConnectToService(server);
            if (hr != 0) { 
                throw CreateConnectionException(server, port, hr); 
            }
 
            return new StateServerPartitionInfo(new ResourcePool(new TimeSpan(0, 0, 5), int.MaxValue),
                                            server,
                                            port);
 
        }
 
        internal static HttpException CreateConnectionException(string server, int port, int hr) { 
            if (s_usePartition) {
                return new HttpException( 
                        SR.GetString(SR.Cant_make_session_request_partition_resolver,
                                    s_configPartitionResolverType, server, port.ToString(CultureInfo.InvariantCulture)), hr);
            }
            else { 
                return new HttpException(
                    SR.GetString(SR.Cant_make_session_request), hr); 
            } 
        }
 

        public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback) {
            return false;
        } 

        public override void Dispose() { 
        } 

        public override void InitializeRequest(HttpContext context) { 
            if (s_usePartition) {
                // For multiple partition case, the connection info can change from request to request
                Debug.Assert(_partitionResolver != null);
                _partitionInfo = null; 
            }
        } 
 
        void MakeRequest(
                UnsafeNativeMethods.StateProtocolVerb   verb, 
                String                                  id,
                UnsafeNativeMethods.StateProtocolExclusive    exclusiveAccess,
                int                                     extraFlags,
                int                                     timeout, 
                int                                     lockCookie,
                byte[]                                  buf, 
                int                                     cb, 
                int                                     networkTimeout,
                out UnsafeNativeMethods.SessionNDMakeRequestResults results) { 

            int                         hr;
            string                      uri;
            OutOfProcConnection         conn = null; 
            HandleRef                   socketHandle;
            bool                        checkVersion = false; 
 
            Debug.Assert(timeout <= SessionStateModule.MAX_CACHE_BASED_TIMEOUT_MINUTES, "item.Timeout <= SessionStateModule.MAX_CACHE_BASED_TIMEOUT_MINUTES");
 
            SessionIDManager.CheckIdLength(id, true /* throwOnFail */);

            if (_partitionInfo == null) {
                Debug.Assert(s_partitionManager != null); 
                Debug.Assert(_partitionResolver != null);
 
                _partitionInfo = (StateServerPartitionInfo)s_partitionManager.GetPartition(_partitionResolver, id); 

                // If its still null, we give up 
                if (_partitionInfo == null) {
                    throw new HttpException(SR.GetString(SR.Bad_partition_resolver_connection_string, "PartitionManager"));
                }
            } 

            // Need to make sure we dispose the connection if anything goes wrong 
            try { 
                conn = (OutOfProcConnection)_partitionInfo.RetrieveResource();
                if (conn != null) { 
                    socketHandle = new HandleRef(this, conn._socketHandle.Handle);
                }
                else {
                    socketHandle = new HandleRef(this, INVALID_SOCKET); 
                }
 
                if (_partitionInfo.StateServerVersion == -1) { 
                    // We don't need locking here because it's okay to have two
                    // requests initializing s_stateServerVersion. 
                    checkVersion = true;
                }

                Debug.Trace("OutOfProcSessionStateStoreMakeRequest", 
                            "Calling MakeRequest, " +
                            "socket=" + (IntPtr)socketHandle.Handle + 
                            "verb=" + verb + 
                            " id=" + id +
                            " exclusiveAccess=" + exclusiveAccess + 
                            " timeout=" + timeout +
                            " buf=" + ((buf != null) ? "non-null" : "null") +
                            " cb=" + cb +
                            " checkVersion=" + checkVersion + 
                            " extraFlags=" + extraFlags);
 
                // Have to UrlEncode id because it may contain non-URL-safe characters 
                uri = HttpUtility.UrlEncode(s_uribase + id);
 
                hr = UnsafeNativeMethods.SessionNDMakeRequest(
                        socketHandle, _partitionInfo.Server, _partitionInfo.Port, networkTimeout, verb, uri,
                        exclusiveAccess, extraFlags, timeout, lockCookie,
                        buf, cb, checkVersion, out results); 

                Debug.Trace("OutOfProcSessionStateStoreMakeRequest", "MakeRequest returned: " + 
                            "hr=" + hr + 
                            " socket=" + (IntPtr)results.socket +
                            " httpstatus=" + results.httpStatus + 
                            " timeout=" + results.timeout +
                            " contentlength=" + results.contentLength +
                            " uri=" + (IntPtr)results.content +
                            " lockCookie=" + results.lockCookie + 
                            " lockDate=" + string.Format("{0:x}", results.lockDate) +
                            " lockAge=" + results.lockAge + 
                            " stateServerMajVer=" + results.stateServerMajVer + 
                            " actionFlags=" + results.actionFlags);
 
                if (conn != null) {
                    if (results.socket == INVALID_SOCKET) {
                        conn.Detach();
                        conn = null; 
                    }
                    else if (results.socket != socketHandle.Handle) { 
                        // The original socket is no good.  We've got a new one. 
                        // Pleae note that EnsureConnected has closed the bad
                        // one already. 
                        conn._socketHandle = new HandleRef(this, results.socket);
                    }
                }
                else if (results.socket != INVALID_SOCKET) { 
                    conn = new OutOfProcConnection(results.socket);
                } 
 
                if (conn != null) {
                    _partitionInfo.StoreResource(conn); 
                }
            }
            catch {
                // We just need to dispose the connection if anything bad happened 
                if (conn != null) {
                    conn.Dispose(); 
                } 

                throw; 
            }

            if (hr != 0) {
                HttpException e = CreateConnectionException(_partitionInfo.Server, _partitionInfo.Port, hr); 

                string phase = null; 
 
                switch (results.lastPhase) {
                case (int)UnsafeNativeMethods.SessionNDMakeRequestPhase.Initialization: 
                    phase = SR.GetString(SR.State_Server_detailed_error_phase0);
                    break;

                case (int)UnsafeNativeMethods.SessionNDMakeRequestPhase.Connecting: 
                    phase = SR.GetString(SR.State_Server_detailed_error_phase1);
                    break; 
 
                case (int)UnsafeNativeMethods.SessionNDMakeRequestPhase.SendingRequest:
                    phase = SR.GetString(SR.State_Server_detailed_error_phase2); 
                    break;

                case (int)UnsafeNativeMethods.SessionNDMakeRequestPhase.ReadingResponse:
                    phase = SR.GetString(SR.State_Server_detailed_error_phase3); 
                    break;
 
                default: 
                    Debug.Assert(false, "Unknown results.lastPhase: " + results.lastPhase);
                    break; 
                }

                WebBaseEvent.RaiseSystemEvent(SR.GetString(
                            SR.State_Server_detailed_error, 
                            phase,
                            "0x" + hr.ToString("X08", CultureInfo.InvariantCulture), 
                            cb.ToString(CultureInfo.InvariantCulture)), 
                            this, WebEventCodes.WebErrorOtherError, WebEventCodes.StateServerConnectionError, e);
 
                throw e;
            }

            if (results.httpStatus == 400) { 
                if (s_usePartition) {
                    throw new HttpException( 
                        SR.GetString(SR.Bad_state_server_request_partition_resolver, 
                                    s_configPartitionResolverType, _partitionInfo.Server, _partitionInfo.Port.ToString(CultureInfo.InvariantCulture)));
                } 
                else {
                    throw new HttpException(
                        SR.GetString(SR.Bad_state_server_request));
                } 
            }
 
            if (checkVersion) { 
                _partitionInfo.StateServerVersion = results.stateServerMajVer;
                if (_partitionInfo.StateServerVersion < WHIDBEY_MAJOR_VERSION) { 
                    // We won't work with versions lower than Whidbey
                    if (s_usePartition) {
                        throw new HttpException(
                            SR.GetString(SR.Need_v2_State_Server_partition_resolver, 
                                        s_configPartitionResolverType, _partitionInfo.Server, _partitionInfo.Port.ToString(CultureInfo.InvariantCulture)));
                    } 
                    else { 
                        throw new HttpException(
                            SR.GetString(SR.Need_v2_State_Server)); 
                    }
                }
            }
        } 

        internal SessionStateStoreData DoGet(HttpContext context, 
                                            String id, 
                                            UnsafeNativeMethods.StateProtocolExclusive exclusiveAccess,
                                            out bool locked, 
                                            out TimeSpan lockAge,
                                            out object lockId,
                                            out SessionStateActions actionFlags) {
            SessionStateStoreData   item = null; 
            UnmanagedMemoryStream   stream;
            int                     contentLength; 
            UnsafeNativeMethods.SessionNDMakeRequestResults results; 

            // Set default return values 
            locked = false;
            lockId = null;
            lockAge = TimeSpan.Zero;
            actionFlags = 0; 
            results.content = IntPtr.Zero;
 
            try { 
                MakeRequest(UnsafeNativeMethods.StateProtocolVerb.GET,
                            id, exclusiveAccess, 0, 0, 0, 
                            null, 0, s_networkTimeout, out results);

                switch (results.httpStatus) {
                    case 200: 
                        /* item found, deserialize it */
                        contentLength = results.contentLength; 
                        if (contentLength > 0) { 
                            unsafe {
                                stream = new UnmanagedMemoryStream((byte*)results.content, contentLength); 
                            }

                            item = SessionStateUtility.Deserialize(context, stream);
                            stream.Close(); 

                            lockId = results.lockCookie; 
                            actionFlags = (SessionStateActions) results.actionFlags; 
                        }
 
                        break;

                    case 423:
                        /* state locked, return lock information */ 

                        if (0 <= results.lockAge) { 
                            if (results.lockAge < Sec.ONE_YEAR) { 
                                lockAge = new TimeSpan(0, 0, results.lockAge);
                            } 
                            else {
                                lockAge = TimeSpan.Zero;
                            }
                        } 
                        else {
                            DateTime now = DateTime.Now; 
                            if (0 < results.lockDate && results.lockDate < now.Ticks) { 
                                lockAge = now - new DateTime(results.lockDate);
                            } 
                            else {
                                lockAge = TimeSpan.Zero;
                            }
                        } 

                        locked = true; 
                        lockId = results.lockCookie; 

                        Debug.Assert((results.actionFlags & (int)SessionStateActions.InitializeItem) == 0, 
                            "(results.actionFlags & (int)SessionStateActions.InitializeItem) == 0; uninitialized item cannot be locked");
                        break;
                }
            } 
            finally {
                if (results.content != IntPtr.Zero) { 
                    UnsafeNativeMethods.SessionNDFreeBody(new HandleRef(this, results.content)); 
                }
            } 

            return item;
        }
 
        public override SessionStateStoreData  GetItem(HttpContext context,
                                                            String id, 
                                                            out bool locked, 
                                                            out TimeSpan lockAge,
                                                            out object lockId, 
                                                            out SessionStateActions actionFlags) {
            Debug.Trace("OutOfProcSessionStateStore", "Calling Get, id=" + id);

            return DoGet(context, id, UnsafeNativeMethods.StateProtocolExclusive.NONE, 
                        out locked, out lockAge, out lockId, out actionFlags);
        } 
 

        public override SessionStateStoreData  GetItemExclusive(HttpContext context, 
                                                String id,
                                                out bool locked,
                                                out TimeSpan lockAge,
                                                out object lockId, 
                                                out SessionStateActions actionFlags) {
            Debug.Trace("OutOfProcSessionStateStore", "Calling GetExlusive, id=" + id); 
 
            return DoGet(context, id, UnsafeNativeMethods.StateProtocolExclusive.ACQUIRE,
                        out locked, out lockAge, out lockId, out actionFlags); 
        }

        public override void ReleaseItemExclusive(HttpContext context,
                                String id, 
                                object lockId) {
            Debug.Assert(lockId != null, "lockId != null"); 
 
            UnsafeNativeMethods.SessionNDMakeRequestResults results;
            int lockCookie = (int)lockId; 

            Debug.Trace("OutOfProcSessionStateStore", "Calling ReleaseExclusive, id=" + id);
            MakeRequest(UnsafeNativeMethods.StateProtocolVerb.GET, id,
                        UnsafeNativeMethods.StateProtocolExclusive.RELEASE, 0, 0, 
                        lockCookie, null, 0, s_networkTimeout, out results);
 
        } 

        public override void SetAndReleaseItemExclusive(HttpContext context, 
                                    String id,
                                    SessionStateStoreData item,
                                    object lockId,
                                    bool newItem) { 
            UnsafeNativeMethods.SessionNDMakeRequestResults results;
            byte[]          buf; 
            int             length; 
            int             lockCookie;
 
            Debug.Assert(item.Items != null, "item.Items != null");
            Debug.Assert(item.StaticObjects != null, "item.StaticObjects != null");

            Debug.Trace("OutOfProcSessionStateStore", "Calling Set, id=" + id + " sessionItems=" + item.Items + " timeout=" + item.Timeout); 

            try { 
                SessionStateUtility.SerializeStoreData(item, 0, out buf, out length); 
            }
            catch { 
                if (!newItem) {
                    ((SessionStateStoreProviderBase)this).ReleaseItemExclusive(context, id, lockId);
                }
                throw; 
            }
 
            // Save it to the store 

            if (lockId == null) { 
                lockCookie = 0;
            }
            else {
                lockCookie = (int)lockId; 
            }
 
            MakeRequest(UnsafeNativeMethods.StateProtocolVerb.PUT, id, 
                        UnsafeNativeMethods.StateProtocolExclusive.NONE, 0, item.Timeout, lockCookie,
                        buf, length, s_networkTimeout, out results); 
        }

        public override void RemoveItem(HttpContext context,
                                        String id, 
                                        object lockId,
                                        SessionStateStoreData item) { 
            Debug.Assert(lockId != null, "lockId != null"); 
            Debug.Trace("OutOfProcSessionStateStore", "Calling Remove, id=" + id);
 
            UnsafeNativeMethods.SessionNDMakeRequestResults results;
            int             lockCookie = (int)lockId;

            MakeRequest(UnsafeNativeMethods.StateProtocolVerb.DELETE, id, 
                        UnsafeNativeMethods.StateProtocolExclusive.NONE, 0, 0, lockCookie,
                        null, 0, s_networkTimeout, out results); 
 
        }
 
        public override void ResetItemTimeout(HttpContext context, String id) {
            UnsafeNativeMethods.SessionNDMakeRequestResults results;

            Debug.Trace("OutOfProcSessionStateStore", "Calling ResetTimeout, id=" + id); 
            MakeRequest(UnsafeNativeMethods.StateProtocolVerb.HEAD, id,
                        UnsafeNativeMethods.StateProtocolExclusive.NONE, 0, 0, 0, 
                        null, 0, s_networkTimeout, out results); 
        }
 
        public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
        {
            Debug.Assert(timeout <= SessionStateModule.MAX_CACHE_BASED_TIMEOUT_MINUTES, "item.Timeout <= SessionStateModule.MAX_CACHE_BASED_TIMEOUT_MINUTES");
 
            return SessionStateUtility.CreateLegitStoreData(context, null, null, timeout);
        } 
 
        public override void CreateUninitializedItem(HttpContext context, String id, int timeout) {
            UnsafeNativeMethods.SessionNDMakeRequestResults results; 
            byte[]          buf;
            int             length;

            Debug.Trace("OutOfProcSessionStateStore", "Calling CreateUninitializedItem, id=" + id + " timeout=" + timeout); 

            // Create an empty item 
            SessionStateUtility.SerializeStoreData(CreateNewStoreData(context, timeout), 0, out buf, out length); 

            // Save it to the store 
            MakeRequest(UnsafeNativeMethods.StateProtocolVerb.PUT, id,
                        UnsafeNativeMethods.StateProtocolExclusive.NONE,
                        (int)SessionStateItemFlags.Uninitialized, timeout, 0,
                        buf, length, s_networkTimeout, out results); 
        }
 
        // Called during EndRequest event 
        public override void EndRequest(HttpContext context) {
        } 

        class StateServerPartitionInfo : PartitionInfo {
            string  _server;
            int     _port; 
            int     _stateServerVersion;
 
            internal StateServerPartitionInfo(ResourcePool rpool, string server, int port) : base(rpool) { 
                _server = server;
                _port = port; 
                _stateServerVersion = -1;
                Debug.Trace("PartitionInfo", "Created a new info, server=" + server + ", port=" + port);
            }
 
            internal string Server {
                get { return _server; } 
            } 

            internal int Port { 
                get { return _port; }
            }

            internal int StateServerVersion { 
                get { return _stateServerVersion; }
                set { _stateServerVersion = value; } 
            } 

            protected override string TracingPartitionString { 
                get {
                    return Server + ":" + Port;
                }
            } 
        }
 
 
        class OutOfProcConnection : IDisposable {
            internal HandleRef _socketHandle; 

            internal OutOfProcConnection(IntPtr socket) {
                Debug.Assert(socket != OutOfProcSessionStateStore.INVALID_SOCKET,
                             "socket != OutOfProcSessionStateStore.INVALID_SOCKET"); 

                _socketHandle = new HandleRef(this, socket); 
                PerfCounters.IncrementCounter(AppPerfCounter.SESSION_STATE_SERVER_CONNECTIONS); 
            }
 
            ~OutOfProcConnection() {
                Dispose(false);
            }
 
            public void Dispose() {
                Debug.Trace("ResourcePool", "Disposing OutOfProcConnection"); 
 
                Dispose(true);
                System.GC.SuppressFinalize(this); 
            }

            private void Dispose(bool dummy) {
                if (_socketHandle.Handle != OutOfProcSessionStateStore.INVALID_SOCKET) { 
                    UnsafeNativeMethods.SessionNDCloseConnection(_socketHandle);
                    _socketHandle = new HandleRef(this, OutOfProcSessionStateStore.INVALID_SOCKET); 
                    PerfCounters.DecrementCounter(AppPerfCounter.SESSION_STATE_SERVER_CONNECTIONS); 
                }
            } 

            internal void Detach() {
                _socketHandle = new HandleRef(this, OutOfProcSessionStateStore.INVALID_SOCKET);
            } 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

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