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 / SessionStateModule.cs / 3 / SessionStateModule.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* * SessionStateModule * * Copyright (c) 1998-2002, Microsoft Corporation * */ namespace System.Web.SessionState { using System; using System.Threading; using System.Collections; using System.Configuration; using System.IO; using System.Web.Caching; using System.Web.Util; using System.Web.Configuration; using System.Xml; using System.Security.Cryptography; using System.Data.SqlClient; using System.Globalization; using System.Security.Permissions; using System.Text; using System.Web.Hosting; using System.Web.Management; using Microsoft.Win32; public delegate void SessionStateItemExpireCallback( string id, SessionStateStoreData item); class SessionOnEndTargetWorkItem { SessionOnEndTarget _target; HttpSessionState _sessionState; internal SessionOnEndTargetWorkItem(SessionOnEndTarget target, HttpSessionState sessionState) { _target = target; _sessionState = sessionState; } internal void RaiseOnEndCallback() { _target.RaiseOnEnd(_sessionState); } } /* * Calls the OnSessionEnd event. We use an object other than the SessionStateModule * because the state of the module is unknown - it could have been disposed * when a session ends. */ class SessionOnEndTarget { internal int _sessionEndEventHandlerCount; internal SessionOnEndTarget() { } internal int SessionEndEventHandlerCount { get { return _sessionEndEventHandlerCount; } set { _sessionEndEventHandlerCount = value; } } internal void RaiseOnEnd(HttpSessionState sessionState) { Debug.Trace("SessionOnEnd", "Firing OnSessionEnd for " + sessionState.SessionID); if (_sessionEndEventHandlerCount > 0) { HttpApplicationFactory.EndSession(sessionState, this, EventArgs.Empty); } } internal void RaiseSessionOnEnd(String id, SessionStateStoreData item) { HttpSessionStateContainer sessionStateContainer = new HttpSessionStateContainer( id, item.Items, item.StaticObjects, item.Timeout, false, SessionStateModule.s_configCookieless, SessionStateModule.s_configMode, true); HttpSessionState sessionState = new HttpSessionState(sessionStateContainer); if (HttpRuntime.ShutdownInProgress) { // call directly when shutting down RaiseOnEnd(sessionState); } else { // post via thread pool SessionOnEndTargetWorkItem workItem = new SessionOnEndTargetWorkItem(this, sessionState); WorkItem.PostInternal(new WorkItemCallback(workItem.RaiseOnEndCallback)); } } } /* * The sesssion state module provides session state services * for an application. */ ////// [AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)] public sealed class SessionStateModule : IHttpModule { internal const string SQL_CONNECTION_STRING_DEFAULT = "data source=localhost;Integrated Security=SSPI"; internal const string STATE_CONNECTION_STRING_DEFAULT = "tcpip=loopback:42424"; internal const int TIMEOUT_DEFAULT = 20; internal const SessionStateMode MODE_DEFAULT = SessionStateMode.InProc; private static long LOCKED_ITEM_POLLING_INTERVAL = 500; // in milliseconds static readonly TimeSpan LOCKED_ITEM_POLLING_DELTA = new TimeSpan(250 * TimeSpan.TicksPerMillisecond); static readonly TimeSpan DEFAULT_DBG_EXECUTION_TIMEOUT = new TimeSpan(0, 0, System.Web.Compilation.PageCodeDomTreeGenerator.DebugScriptTimeout); // When we are using Cache to store session state (InProc and StateServer), // can't specify a timeout value larger than 1 year because CacheEntry ctor // will throw an exception. internal const int MAX_CACHE_BASED_TIMEOUT_MINUTES = 365 * 24 * 60; bool s_oneTimeInit; static int s_timeout; #pragma warning disable 0649 static ReadWriteSpinLock s_lock; #pragma warning restore 0649 static bool s_trustLevelInsufficient; static TimeSpan s_configExecutionTimeout; static bool s_configRegenerateExpiredSessionId; static bool s_useHostingIdentity; internal static HttpCookieMode s_configCookieless; internal static SessionStateMode s_configMode; // This is used as a perf optimization for IIS7 Integrated Mode. If session state is released // in ReleaseState, we can disable the EndRequest notification if the mode is InProc or StateServer // because neither InProcSessionStateStore.EndRequest nor OutOfProcSessionStateStore.EndRequest // are implemented. static bool s_canSkipEndRequestCall; private static bool s_PollIntervalRegLookedUp = false; private static object s_PollIntervalRegLock = new object(); // // Check if we can optmize for InProc case. // Optimization details: // // If we are in InProc mode, and cookieless=false, in certain scenarios we // can avoid reading the session ID from the cookies because that's an expensive operation. // To allow that, we use s_sessionEverSet to keep track of whether we've ever created // any session state. // // If no session has ever be created, we can optimize in the following two cases: // // Case 1: Page has disabled session state // In BeginAcquireState, we usually read the session ID, and reset the timeout value // of the session state. However, since no session has ever been created, we can // skip both reading the session id and resetting the timeout. // // Case 2: Page has enabled session state // In this case, we will delay reading (and creating it if not found) the session ID // until it's really needed. (e.g. from HttpSessionStateContainer.SessionID) // // Please note that we optimize only if the app is using SessionIDManager // as the session ID provider; otherwise, we do not have knowledge about // the provider in order to optimize safely. // // And we will delay reading the id only if we are using cookie to store the session ID. If we // use cookieless, in the delayed session ID creation scenario, cookieless requires a redirect, // and it'll be bad to do that in the middle of a page execution. // static bool s_allowInProcOptimization; static bool s_sessionEverSet; // // Another optimization is to delay the creation of a new session state store item // until it's needed. static bool s_allowDelayedStateStoreItemCreation; static HttpSessionStateContainer s_delayedSessionState = new HttpSessionStateContainer(); /* per application vars */ EventHandler _sessionStartEventHandler; Timer _timer; TimerCallback _timerCallback; volatile int _timerId; ISessionIDManager _idManager; bool _usingAspnetSessionIdManager; SessionStateStoreProviderBase _store; bool _supportSessionExpiry; IPartitionResolver _partitionResolver; bool _ignoreImpersonation; readonly SessionOnEndTarget _onEndTarget = new SessionOnEndTarget(); /* per request data goes in _rq* variables */ bool _acquireCalled; bool _releaseCalled; HttpSessionStateContainer _rqSessionState; String _rqId; bool _rqIdNew; ISessionStateItemCollection _rqSessionItems; HttpStaticObjectsCollection _rqStaticObjects; bool _rqIsNewSession; bool _rqSessionStateNotFound; bool _rqReadonly; HttpContext _rqContext; HttpAsyncResult _rqAr; SessionStateStoreData _rqItem; object _rqLockId; // The id of its SessionStateItem ownership // If the ownership change hands (e.g. this ownership // times out), the lockId of the item at the store // will change. int _rqInCallback; DateTime _rqLastPollCompleted; TimeSpan _rqExecutionTimeout; bool _rqAddedCookie; SessionStateActions _rqActionFlags; ImpersonationContext _rqIctx; internal int _rqChangeImpersonationRefCount; ImpersonationContext _rqTimerThreadImpersonationIctx; bool _rqSupportSessionIdReissue; bool _rqUninitializedItem; ///[To be supplied.] ////// [SecurityPermission(SecurityAction.Demand, Unrestricted=true)] public SessionStateModule() { } static bool CheckTrustLevel(SessionStateSection config) { switch (config.Mode) { case SessionStateMode.SQLServer: case SessionStateMode.StateServer: return HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium); default: case SessionStateMode.Off: case SessionStateMode.InProc: // In-proc session doesn't require any trust level (part of ASURT 124513) return true; } } // Create an instance of the custom store as specified in the config file SessionStateStoreProviderBase InitCustomStore(SessionStateSection config) { string providerName = config.CustomProvider; ProviderSettings ps; if (String.IsNullOrEmpty(providerName)) { throw new ConfigurationErrorsException( SR.GetString(SR.Invalid_session_custom_provider, providerName), config.ElementInformation.Properties["customProvider"].Source, config.ElementInformation.Properties["customProvider"].LineNumber); } ps = config.Providers[providerName]; if (ps == null) { throw new ConfigurationErrorsException( SR.GetString(SR.Missing_session_custom_provider, providerName), config.ElementInformation.Properties["customProvider"].Source, config.ElementInformation.Properties["customProvider"].LineNumber); } return (SessionStateStoreProviderBase)ProvidersHelper.InstantiateProvider(ps, typeof(SessionStateStoreProviderBase)); } IPartitionResolver InitPartitionResolver(SessionStateSection config) { string partitionResolverType = config.PartitionResolverType; Type resolverType; IPartitionResolver iResolver; if (String.IsNullOrEmpty(partitionResolverType)) { return null; } if (config.Mode != SessionStateMode.StateServer && config.Mode != SessionStateMode.SQLServer) { throw new ConfigurationErrorsException(SR.GetString(SR.Cant_use_partition_resolve), config.ElementInformation.Properties["partitionResolverType"].Source, config.ElementInformation.Properties["partitionResolverType"].LineNumber); } resolverType = ConfigUtil.GetType(partitionResolverType, "partitionResolverType", config); ConfigUtil.CheckAssignableType(typeof(IPartitionResolver), resolverType, config, "partitionResolverType"); iResolver = (IPartitionResolver)HttpRuntime.CreatePublicInstance(resolverType); iResolver.Initialize(); return iResolver; } ISessionIDManager InitSessionIDManager(SessionStateSection config) { string sessionIDManagerType = config.SessionIDManagerType; ISessionIDManager iManager; if (String.IsNullOrEmpty(sessionIDManagerType)) { iManager = new SessionIDManager(); _usingAspnetSessionIdManager = true; } else { Type managerType; managerType = ConfigUtil.GetType(sessionIDManagerType, "sessionIDManagerType", config); ConfigUtil.CheckAssignableType(typeof(ISessionIDManager), managerType, config, "sessionIDManagerType"); iManager = (ISessionIDManager)HttpRuntime.CreatePublicInstance(managerType); } iManager.Initialize(); return iManager; } void InitModuleFromConfig(HttpApplication app, SessionStateSection config) { if (config.Mode == SessionStateMode.Off) { return; } app.AddOnAcquireRequestStateAsync( new BeginEventHandler(this.BeginAcquireState), new EndEventHandler(this.EndAcquireState)); app.ReleaseRequestState += new EventHandler(this.OnReleaseState); app.EndRequest += new EventHandler(this.OnEndRequest); _partitionResolver = InitPartitionResolver(config); switch (config.Mode) { case SessionStateMode.InProc: if (HttpRuntime.UseIntegratedPipeline) { s_canSkipEndRequestCall = true; } _store = new InProcSessionStateStore(); _store.Initialize(null, null); break; #if !FEATURE_PAL // FEATURE_PAL does not enable out of proc session state case SessionStateMode.StateServer: if (HttpRuntime.UseIntegratedPipeline) { s_canSkipEndRequestCall = true; } _store = new OutOfProcSessionStateStore(); ((OutOfProcSessionStateStore)_store).Initialize(null, null, _partitionResolver); break; case SessionStateMode.SQLServer: _store = new SqlSessionStateStore(); ((SqlSessionStateStore)_store).Initialize(null, null, _partitionResolver); #if DBG ((SqlSessionStateStore)_store).SetModule(this); #endif break; #else // !FEATURE_PAL case SessionStateMode.StateServer: throw new NotImplementedException("ROTORTODO"); break; case SessionStateMode.SQLServer: throw new NotImplementedException("ROTORTODO"); break; #endif // !FEATURE_PAL case SessionStateMode.Custom: _store = InitCustomStore(config); break; default: break; } // We depend on SessionIDManager to manage session id _idManager = InitSessionIDManager(config); if ((config.Mode == SessionStateMode.InProc || config.Mode == SessionStateMode.StateServer) && _usingAspnetSessionIdManager) { // If we're using InProc mode or StateServer mode, and also using our own session id module, // we know we don't care about impersonation in our all session state store read/write // and session id read/write. _ignoreImpersonation = true; } } public void Init(HttpApplication app) { bool initModuleCalled = false; SessionStateSection config = RuntimeConfig.GetAppConfig().SessionState; if (!s_oneTimeInit) { s_lock.AcquireWriterLock(); try { if (!s_oneTimeInit) { InitModuleFromConfig(app, config); initModuleCalled = true; if (!CheckTrustLevel(config)) s_trustLevelInsufficient = true; s_timeout = (int)config.Timeout.TotalMinutes; s_useHostingIdentity = config.UseHostingIdentity; // See if we can try InProc optimization. See inline doc of s_allowInProcOptimization // for details. if (config.Mode == SessionStateMode.InProc && _usingAspnetSessionIdManager) { s_allowInProcOptimization = true; } if (config.Mode != SessionStateMode.Custom && config.Mode != SessionStateMode.Off && !config.RegenerateExpiredSessionId) { s_allowDelayedStateStoreItemCreation = true; } s_configExecutionTimeout = RuntimeConfig.GetConfig().HttpRuntime.ExecutionTimeout; s_configRegenerateExpiredSessionId = config.RegenerateExpiredSessionId; s_configCookieless = config.Cookieless; s_configMode = config.Mode; // The last thing to set in this if-block. s_oneTimeInit = true; Debug.Trace("SessionStateModuleInit", "Configuration: _mode=" + config.Mode + ";Timeout=" + config.Timeout + ";CookieMode=" + config.Cookieless + ";SqlConnectionString=" + config.SqlConnectionString + ";StateConnectionString=" + config.StateConnectionString + ";s_allowInProcOptimization=" + s_allowInProcOptimization + ";s_allowDelayedStateStoreItemCreation=" + s_allowDelayedStateStoreItemCreation); } } finally { s_lock.ReleaseWriterLock(); } } if (!initModuleCalled) { InitModuleFromConfig(app, config); } if (s_trustLevelInsufficient) { throw new HttpException(SR.GetString(SR.Session_state_need_higher_trust)); } } ////// Initializes a new instance of the ////// class. /// /// public void Dispose() { if (_timer != null) { ((IDisposable)_timer).Dispose(); } if (_store != null) { _store.Dispose(); } } void ResetPerRequestFields() { Debug.Assert(_rqIctx == null, "_rqIctx == null"); Debug.Assert(_rqChangeImpersonationRefCount == 0, "_rqChangeImpersonationRefCount == 0"); _rqSessionState = null; _rqId = null; _rqSessionItems = null; _rqStaticObjects = null; _rqIsNewSession = false; _rqSessionStateNotFound = true; _rqReadonly = false; _rqItem = null; _rqContext = null; _rqAr = null; _rqLockId = null; _rqInCallback = 0; _rqLastPollCompleted = DateTime.MinValue; _rqExecutionTimeout = TimeSpan.Zero; _rqAddedCookie = false; _rqIdNew = false; _rqActionFlags = 0; _rqIctx = null; _rqChangeImpersonationRefCount = 0; _rqTimerThreadImpersonationIctx = null; _rqSupportSessionIdReissue = false; _rqUninitializedItem = false; } /* * Add a OnStart event handler. * * @param sessionEventHandler */ ///[To be supplied.] ////// public event EventHandler Start { add { _sessionStartEventHandler += value; } remove { _sessionStartEventHandler -= value; } } void RaiseOnStart(EventArgs e) { if (_sessionStartEventHandler == null) return; Debug.Trace("SessionStateModuleRaiseOnStart", "Session_Start called for session id:" + _rqId); // Session_OnStart for ASPCOMPAT pages has to be raised from an STA thread // if (HttpRuntime.ApartmentThreading || _rqContext.InAspCompatMode) { #if !FEATURE_PAL // FEATURE_PAL does not enable COM AspCompatApplicationStep.RaiseAspCompatEvent( _rqContext, _rqContext.ApplicationInstance, null, _sessionStartEventHandler, this, e); #else // !FEATURE_PAL throw new NotImplementedException ("ROTORTODO"); #endif // !FEATURE_PAL } else { if (HttpContext.Current == null) { // This can happen if it's called by a timer thread HttpContextWrapper.SwitchContext(_rqContext); } _sessionStartEventHandler(this, e); } } /* * Fire the OnStart event. * * @param e */ void OnStart(EventArgs e) { RaiseOnStart(e); } /* * Add a OnEnd event handler. * * @param sessionEventHandler */ ///[To be supplied.] ////// public event EventHandler End { add { lock(_onEndTarget) { if (_store != null && _onEndTarget.SessionEndEventHandlerCount == 0) { _supportSessionExpiry = _store.SetItemExpireCallback( new SessionStateItemExpireCallback(_onEndTarget.RaiseSessionOnEnd)); } ++_onEndTarget.SessionEndEventHandlerCount; } } remove { lock(_onEndTarget) { --_onEndTarget.SessionEndEventHandlerCount; // if (_store != null && _onEndTarget.SessionEndEventHandlerCount == 0) { _store.SetItemExpireCallback(null); _supportSessionExpiry = false; } } } } /* * Acquire session state */ IAsyncResult BeginAcquireState(Object source, EventArgs e, AsyncCallback cb, Object extraData) { bool requiresState; bool isCompleted = true; bool skipReadingId = false; Debug.Trace("SessionStateModuleOnAcquireState", "Beginning SessionStateModule::OnAcquireState"); _acquireCalled = true; _releaseCalled = false; ResetPerRequestFields(); _rqContext = ((HttpApplication)source).Context; _rqAr = new HttpAsyncResult(cb, extraData); ChangeImpersonation(_rqContext, false); try { if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_BEGIN, _rqContext.WorkerRequest); /* Notify the store we are beginning to get process request */ _store.InitializeRequest(_rqContext); /* determine if the request requires state at all */ requiresState = _rqContext.RequiresSessionState; // SessionIDManager may need to do a redirect if cookieless setting is AutoDetect if (_idManager.InitializeRequest(_rqContext, false, out _rqSupportSessionIdReissue)) { _rqAr.Complete(true, null, null); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest); return _rqAr; } // See if we can skip reading the session id. See inline doc of s_allowInProcOptimization // for details. if (s_allowInProcOptimization && !s_sessionEverSet && (!requiresState || // Case 1 !((SessionIDManager)_idManager).UseCookieless(_rqContext)) ) { // Case 2 skipReadingId = true; #if DBG if (!requiresState) { // Case 1 Debug.Trace("SessionStateModuleOnAcquireState", "Skip reading id because page has disabled session state"); } else { // Case 2 Debug.Trace("SessionStateModuleOnAcquireState", "Delay reading id because we're using InProc optimization, and we are not using cookieless"); } #endif } else { /* Get sessionid */ _rqId = _idManager.GetSessionID(_rqContext); Debug.Trace("SessionStateModuleOnAcquireState", "Current request id=" + _rqId); } if (!requiresState) { if (_rqId == null) { Debug.Trace("SessionStateModuleOnAcquireState", "Handler does not require state, " + "session id skipped or no id found, " + "skipReadingId=" + skipReadingId + "\nReturning from SessionStateModule::OnAcquireState"); } else { Debug.Trace("SessionStateModuleOnAcquireState", "Handler does not require state, " + "resetting timeout for SessionId=" + _rqId + "\nReturning from SessionStateModule::OnAcquireState"); // Still need to update the sliding timeout to keep session alive. // There is a plan to skip this for perf reason. But it was postponed to // after Whidbey. _store.ResetItemTimeout(_rqContext, _rqId); } _rqAr.Complete(true, null, null); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest); return _rqAr; } _rqExecutionTimeout = _rqContext.Timeout; // If the page is marked as DEBUG, HttpContext.Timeout will return a very large value (~1 year) // In this case, we want to use the executionTimeout value specified in the config to avoid // PollLockedSession to run forever. if (_rqExecutionTimeout == DEFAULT_DBG_EXECUTION_TIMEOUT) { _rqExecutionTimeout = s_configExecutionTimeout; } /* determine if we need just read-only access */ _rqReadonly = _rqContext.ReadOnlySessionState; if (_rqId != null) { /* get the session state corresponding to this session id */ isCompleted = GetSessionStateItem(); } else if (!skipReadingId) { /* if there's no id yet, create it */ bool redirected = CreateSessionId(); _rqIdNew = true; if (redirected) { if (s_configRegenerateExpiredSessionId) { // See inline comments in CreateUninitializedSessionState() CreateUninitializedSessionState(); } _rqAr.Complete(true, null, null); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest); return _rqAr; } } if (isCompleted) { CompleteAcquireState(); _rqAr.Complete(true, null, null); } return _rqAr; } finally { RestoreImpersonation(); } } internal bool CreateSessionId() { // CreateSessionId should be called only if: Debug.Assert(_rqId == null || // Session id isn't found in the request, OR (_rqSessionStateNotFound && // The session state isn't found, AND s_configRegenerateExpiredSessionId && // We are regenerating expired session id, AND _rqSupportSessionIdReissue && // This request supports session id re-issue, AND !_rqIdNew), // The above three condition should imply the session id // isn't just created, but is sent by the request. "CreateSessionId should be called only if we're generating new id, or re-generating expired one"); Debug.Assert(_rqChangeImpersonationRefCount > 0, "Must call ChangeImpersonation first"); bool redirected; _rqId = _idManager.CreateSessionID(_rqContext); _idManager.SaveSessionID(_rqContext, _rqId, out redirected, out _rqAddedCookie); return redirected; } // Called when AcquireState is done. This function will add the returned // SessionStateStore item to the request context. void CompleteAcquireState() { Debug.Trace("SessionStateModuleOnAcquireState", "Item retrieved=" + (_rqItem != null).ToString(CultureInfo.InvariantCulture)); bool delayInitStateStoreItem = false; Debug.Assert(!(s_allowDelayedStateStoreItemCreation && s_configRegenerateExpiredSessionId), "!(s_allowDelayedStateStoreItemCreation && s_configRegenerateExpiredSessionId)"); try { if (_rqItem != null) { _rqSessionStateNotFound = false; if ((_rqActionFlags & SessionStateActions.InitializeItem) != 0) { Debug.Trace("SessionStateModuleOnAcquireState", "Initialize an uninit item"); _rqIsNewSession = true; } else { _rqIsNewSession = false; } } else { _rqIsNewSession = true; _rqSessionStateNotFound = true; if (s_allowDelayedStateStoreItemCreation) { Debug.Trace("SessionStateModuleOnAcquireState", "Delay creating new session state"); delayInitStateStoreItem = true; } // We couldn't find the session state. if (!_rqIdNew && // If the request has a session id, that means the session state has expired s_configRegenerateExpiredSessionId && // And we're asked to regenerate expired session _rqSupportSessionIdReissue) { // And this request support session id reissue // We will generate a new session id for this expired session state bool redirected = CreateSessionId(); Debug.Trace("SessionStateModuleOnAcquireState", "Complete re-creating new id; redirected=" + redirected); if (redirected) { Debug.Trace("SessionStateModuleOnAcquireState", "Will redirect because we've reissued a new id and it's cookieless"); CreateUninitializedSessionState(); return; } } } if (delayInitStateStoreItem) { SessionStateUtility.AddDelayedHttpSessionStateToContext(_rqContext, this); _rqSessionState = s_delayedSessionState; } else { InitStateStoreItem(true); } if (_rqIsNewSession) { Debug.Trace("SessionStateModuleOnAcquireState", "Calling OnStart"); OnStart(EventArgs.Empty); } } finally { if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest); } #if DBG if (_rqIsNewSession) { if (_rqId == null) { Debug.Assert(s_allowInProcOptimization, "s_allowInProcOptimization"); Debug.Trace("SessionStateModuleOnAcquireState", "New session: session id reading is delayed"+ "\nReturning from SessionStateModule::OnAcquireState"); } else { Debug.Trace("SessionStateModuleOnAcquireState", "New session: SessionId= " + _rqId + "\nReturning from SessionStateModule::OnAcquireState"); } } else { Debug.Trace("SessionStateModuleOnAcquireState", "Retrieved old session, SessionId= " + _rqId + "\nReturning from SessionStateModule::OnAcquireState"); } #endif } void CreateUninitializedSessionState() { Debug.Assert(_rqChangeImpersonationRefCount > 0, "Must call ChangeImpersonation first"); // When we generate a new session id in cookieless case, and if "reissueExpiredSession" is // true, we need to generate a new temporary empty session and save it // under the new session id, otherwise when the next request (i.e. when the browser is // redirected back to the web server) comes in, we will think it's accessing an expired session. _store.CreateUninitializedItem(_rqContext, _rqId, s_timeout); } internal void InitStateStoreItem(bool addToContext) { Debug.Assert(_rqId != null || s_allowInProcOptimization, "_rqId != null || s_allowInProcOptimization"); ChangeImpersonation(_rqContext, false); try { if (_rqItem == null) { Debug.Trace("InitStateStoreItem", "Creating new session state"); _rqItem = _store.CreateNewStoreData(_rqContext, s_timeout); } _rqSessionItems = _rqItem.Items; if (_rqSessionItems == null) { throw new HttpException(SR.GetString(SR.Null_value_for_SessionStateItemCollection)); } // No check for null because we allow our custom provider to return a null StaticObjects. _rqStaticObjects = _rqItem.StaticObjects; _rqSessionItems.Dirty = false; _rqSessionState = new HttpSessionStateContainer( this, _rqId, // could be null if we're using InProc optimization _rqSessionItems, _rqStaticObjects, _rqItem.Timeout, _rqIsNewSession, s_configCookieless, s_configMode, _rqReadonly); if (addToContext) { SessionStateUtility.AddHttpSessionStateToContext(_rqContext, _rqSessionState); } } finally { RestoreImpersonation(); } } // Used for InProc session id optimization internal string DelayedGetSessionId() { Debug.Assert(s_allowInProcOptimization, "Shouldn't be called if we don't allow InProc optimization"); Debug.Assert(_rqId == null, "Shouldn't be called if we already have the id"); Debug.Assert(!((SessionIDManager)_idManager).UseCookieless(_rqContext), "We can delay session id only if we are not using cookieless"); Debug.Trace("DelayedOperation", "Delayed getting session id"); bool redirected; ChangeImpersonation(_rqContext, false); try { _rqId = _idManager.GetSessionID(_rqContext); if (_rqId == null) { Debug.Trace("DelayedOperation", "Delayed creating session id"); redirected = CreateSessionId(); Debug.Assert(!redirected, "DelayedGetSessionId shouldn't redirect us here."); } } finally { RestoreImpersonation(); } return _rqId; } bool GetSessionStateItem() { bool isCompleted = true; bool locked; TimeSpan lockAge; Debug.Assert(_rqId != null, "_rqId != null"); Debug.Assert(_rqChangeImpersonationRefCount > 0, "Must call ChangeImpersonation first"); if (_rqReadonly) { _rqItem = _store.GetItem(_rqContext, _rqId, out locked, out lockAge, out _rqLockId, out _rqActionFlags); } else { _rqItem = _store.GetItemExclusive(_rqContext, _rqId, out locked, out lockAge, out _rqLockId, out _rqActionFlags); // DevDiv Bugs 146875: WebForm and WebService Session Access Concurrency Issue // If we have an expired session, we need to insert the state in the store here to // ensure serialized access in case more than one entity requests it simultaneously. // If the state has already been created before, CreateUninitializedSessionState is a no-op. if (_rqItem == null && locked == false && _rqId != null) { if (!(s_configCookieless == HttpCookieMode.UseUri && s_configRegenerateExpiredSessionId == true)) { _rqUninitializedItem = true; CreateUninitializedSessionState(); _rqItem = _store.GetItemExclusive(_rqContext, _rqId, out locked, out lockAge, out _rqLockId, out _rqActionFlags); } } } // We didn't get it because it's locked.... if (_rqItem == null && locked) { // if (lockAge >= _rqExecutionTimeout) { /* Release the lock on the item, which is held by another thread*/ Debug.Trace("SessionStateModuleOnAcquireState", "Lock timed out, lockAge=" + lockAge + ", id=" + _rqId); _store.ReleaseItemExclusive(_rqContext, _rqId, _rqLockId); } Debug.Trace("SessionStateModuleOnAcquireState", "Item is locked, will poll, id=" + _rqId); isCompleted = false; PollLockedSession(); } return isCompleted; } void PollLockedSession() { if (_timerCallback == null) { _timerCallback = new TimerCallback(this.PollLockedSessionCallback); } if (_timer == null) { _timerId++; #if DBG if (!Debug.IsTagPresent("Timer") || Debug.IsTagEnabled("Timer")) #endif { if (!s_PollIntervalRegLookedUp) LookUpRegForPollInterval(); _timer = new Timer(_timerCallback, _timerId, LOCKED_ITEM_POLLING_INTERVAL, LOCKED_ITEM_POLLING_INTERVAL); } } } [RegistryPermission(SecurityAction.Assert, Unrestricted = true)] private static void LookUpRegForPollInterval() { lock (s_PollIntervalRegLock) { if (s_PollIntervalRegLookedUp) return; try { object o = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET", "SessionStateLockedItemPollInterval", 0); if (o != null && (o is int || o is uint) && ((int)o) > 0) LOCKED_ITEM_POLLING_INTERVAL = (int) o; s_PollIntervalRegLookedUp = true; } catch { // ignore exceptions } } } void ResetPollTimer() { _timerId++; if (_timer != null) { ((IDisposable)_timer).Dispose(); _timer = null; } } void ChangeImpersonation(HttpContext context, bool timerThread) { #if !FEATURE_PAL // FEATURE_PAL doesn't enable impersonation _rqChangeImpersonationRefCount++; if (_ignoreImpersonation) { return; } // If SQL store isn't using integrated security, and we're using our own session id module, // we know we don't care about impersonation in our all session state store read/write // and session id read/write. if (s_configMode == SessionStateMode.SQLServer && ((SqlSessionStateStore)_store).KnowForSureNotUsingIntegratedSecurity && _usingAspnetSessionIdManager) { return; } // Please note that there are two types of calls coming in. One is from a request thread, // where timerThread==false; the other is from PollLockedSessionCallback, where // timerThread==true. if (s_useHostingIdentity) { // If we're told to use Application Identity, in each case we should impersonate, // if not called yet. if (_rqIctx == null) { _rqIctx = new ApplicationImpersonationContext(); } } else { if (timerThread) { // For the timer thread, we should explicity impersonate back to what the HttpContext was // orginally impersonating. _rqTimerThreadImpersonationIctx = new ClientImpersonationContext(context, false); } else { // For a request thread, if we're told to not use hosting id, there's no need // to do anything special. Debug.Assert(_rqIctx == null, "_rqIctx == null"); return; } } #endif // !FEATURE_PAL } void RestoreImpersonation() { Debug.Assert(_rqChangeImpersonationRefCount != 0, "_rqChangeImpersonationRefCount != 0"); _rqChangeImpersonationRefCount--; if (_rqChangeImpersonationRefCount == 0) { Debug.Assert(!(_rqIctx != null && _rqTimerThreadImpersonationIctx != null), "Should not have mixed mode of impersonation"); if (_rqIctx != null) { _rqIctx.Undo(); _rqIctx = null; } if (_rqTimerThreadImpersonationIctx != null) { Debug.Assert(_rqContext != null, "_rqContext != null"); _rqTimerThreadImpersonationIctx.Undo(); _rqTimerThreadImpersonationIctx = null; } } } void PollLockedSessionCallback(object state) { Debug.Assert(_rqId != null, "_rqId != null"); Debug.Trace("SessionStateModuleOnAcquireState", "Polling callback called from timer, id=" + _rqId); bool isCompleted = false; Exception error = null; /* check whether we are currently in a callback */ if (Interlocked.CompareExchange(ref _rqInCallback, 1, 0) != 0) return; try { /* * check whether this callback is for the current request, * and whether sufficient time has passed since the last poll * to try again. */ int timerId = (int) state; if ( (timerId == _timerId) && (DateTime.UtcNow - _rqLastPollCompleted >= LOCKED_ITEM_POLLING_DELTA)) { ChangeImpersonation(_rqContext, true); try { isCompleted = GetSessionStateItem(); _rqLastPollCompleted = DateTime.UtcNow; if (isCompleted) { Debug.Assert(_timer != null, "_timer != null"); ResetPollTimer(); CompleteAcquireState(); } } finally { RestoreImpersonation(); } } } catch (Exception e) { ResetPollTimer(); error = e; } finally { Interlocked.Exchange(ref _rqInCallback, 0); } if (isCompleted || error != null) { _rqAr.Complete(false, null, error); } } void EndAcquireState(IAsyncResult ar) { ((HttpAsyncResult)ar).End(); } // Called by OnReleaseState to get the session id. string ReleaseStateGetSessionID() { if (_rqId == null) { Debug.Assert(s_allowInProcOptimization, "s_allowInProcOptimization"); DelayedGetSessionId(); } Debug.Assert(_rqId != null, "_rqId != null"); return _rqId; } /* * Release session state */ ///[To be supplied.] ////// void OnReleaseState(Object source, EventArgs eventArgs) { HttpApplication app; HttpContext context; bool setItemCalled = false; Debug.Trace("SessionStateOnReleaseState", "Beginning SessionStateModule::OnReleaseState"); Debug.Assert(!(_rqAddedCookie && !_rqIsNewSession), "If session id was added to the cookie, it must be a new session."); // !!! // Please note that due to InProc session id optimization, this function should not // use _rqId directly because it can still be null. Instead, use DelayedGetSessionId(). _releaseCalled = true; app = (HttpApplication)source; context = app.Context; ChangeImpersonation(context, false); try { if (_rqSessionState != null) { bool delayedSessionState = (_rqSessionState == s_delayedSessionState); Debug.Trace("SessionStateOnReleaseState", "Remove session state from context"); SessionStateUtility.RemoveHttpSessionStateFromContext(_rqContext, delayedSessionState); /* * Don't store untouched new sessions. */ if ( // The store doesn't have the session state. // ( Please note we aren't checking _rqIsNewSession because _rqIsNewSession // is lalso true if the item is converted from temp to perm in a GetItemXXX() call.) _rqSessionStateNotFound // OnStart is not defined && _sessionStartEventHandler == null // Nothing has been stored in session state && (delayedSessionState || !_rqSessionItems.Dirty) && (delayedSessionState || _rqStaticObjects == null || _rqStaticObjects.NeverAccessed) ) { Debug.Trace("SessionStateOnReleaseState", "Not storing unused new session."); } else if (_rqSessionState.IsAbandoned) { Debug.Trace("SessionStateOnReleaseState", "Removing session due to abandonment, SessionId=" + _rqId); if (_rqSessionStateNotFound) { // The store provider doesn't have it, and so we don't need to remove it from the store. // However, if the store provider supports session expiry, and we have a Session_End in global.asax, // we need to explicitly call Session_End. if (_supportSessionExpiry) { if (delayedSessionState) { Debug.Assert(s_allowDelayedStateStoreItemCreation, "s_allowDelayedStateStoreItemCreation"); Debug.Assert(_rqItem == null, "_rqItem == null"); InitStateStoreItem(false /*addToContext*/); } _onEndTarget.RaiseSessionOnEnd(ReleaseStateGetSessionID(), _rqItem); } } else { Debug.Assert(_rqItem != null, "_rqItem cannot null if it's not a new session"); // Remove it from the store because the session is abandoned. _store.RemoveItem(_rqContext, ReleaseStateGetSessionID(), _rqLockId, _rqItem); } } else if (!_rqReadonly) { // We need to save it since it isn't read-only // We save it only if there is no error, and if something has changed (unless it's a new session) if ( context.Error == null // no error && ( _rqSessionStateNotFound || _rqSessionItems.Dirty // SessionItems has changed. || (_rqStaticObjects != null && !_rqStaticObjects.NeverAccessed) // Static objects have been accessed || _rqItem.Timeout != _rqSessionState.Timeout // Timeout value has changed ) ) { if (delayedSessionState) { Debug.Assert(_rqIsNewSession, "Saving a session and delayedSessionState is true: _rqIsNewSession must be true"); Debug.Assert(s_allowDelayedStateStoreItemCreation, "Saving a session and delayedSessionState is true: s_allowDelayedStateStoreItemCreation"); Debug.Assert(_rqItem == null, "Saving a session and delayedSessionState is true: _rqItem == null"); InitStateStoreItem(false /*addToContext*/); } #if DBG if (_rqSessionItems.Dirty) { Debug.Trace("SessionStateOnReleaseState", "Setting new session due to dirty SessionItems, SessionId=" + _rqId); } else if (_rqStaticObjects != null && !_rqStaticObjects.NeverAccessed) { Debug.Trace("SessionStateOnReleaseState", "Setting new session due to accessed Static Objects, SessionId=" + _rqId); } else if (_rqSessionStateNotFound) { Debug.Trace("SessionStateOnReleaseState", "Setting new session because it's not found, SessionId=" + _rqId); } else { Debug.Trace("SessionStateOnReleaseState", "Setting new session due to options change, SessionId=" + _rqId + "\n\t_rq.timeout=" + _rqItem.Timeout.ToString(CultureInfo.InvariantCulture) + ", _rqSessionState.timeout=" + _rqSessionState.Timeout.ToString(CultureInfo.InvariantCulture)); } #endif if (_rqItem.Timeout != _rqSessionState.Timeout) { _rqItem.Timeout = _rqSessionState.Timeout; } s_sessionEverSet = true; setItemCalled = true; _store.SetAndReleaseItemExclusive(_rqContext, ReleaseStateGetSessionID(), _rqItem, _rqLockId, _rqSessionStateNotFound); } else if (_rqUninitializedItem) { _store.RemoveItem(_rqContext, ReleaseStateGetSessionID(), _rqLockId, _rqItem); } else { // Can't save it because of various reason. Just release our exclusive lock on it. Debug.Trace("SessionStateOnReleaseState", "Release exclusive lock on session, SessionId=" + _rqId); if (!_rqSessionStateNotFound) { Debug.Assert(_rqItem != null, "_rqItem cannot null if it's not a new session"); _store.ReleaseItemExclusive(_rqContext, ReleaseStateGetSessionID(), _rqLockId); } } } #if DBG else { Debug.Trace("SessionStateOnReleaseState", "Session is read-only, ignoring SessionId=" + _rqId); } #endif Debug.Trace("SessionStateOnReleaseState", "Returning from SessionStateModule::OnReleaseState"); } if (_rqAddedCookie && !setItemCalled && context.Response.IsBuffered()) { _idManager.RemoveSessionID(_rqContext); } } finally { RestoreImpersonation(); } // WOS 1679798: PERF: Session State Module should disable EndRequest on successful cleanup bool implementsIRequiresSessionState = context.RequiresSessionState; if (HttpRuntime.UseIntegratedPipeline && (context.NotificationContext.CurrentNotification == RequestNotification.ReleaseRequestState) && (s_canSkipEndRequestCall || !implementsIRequiresSessionState)) { context.DisableNotifications(RequestNotification.EndRequest, 0 /*postNotifications*/); _acquireCalled = false; _releaseCalled = false; ResetPerRequestFields(); } } /* * End of request processing. Possibly does release if skipped due to errors */ ///[To be supplied.] ////// void OnEndRequest(Object source, EventArgs eventArgs) { HttpApplication app; HttpContext context; String id; Debug.Trace("SessionStateOnEndRequest", "Beginning SessionStateModule::OnEndRequest"); app = (HttpApplication)source; context = app.Context; /* determine if the request requires state at all */ if (!context.RequiresSessionState) { return; } ChangeImpersonation(context, false); try { if (!_releaseCalled) { if (_acquireCalled) { /* * need to do release here if the request short-circuited due to an error */ OnReleaseState(source, eventArgs); } else { /* * 'advise' -- update session timeout */ if (_rqContext == null) { _rqContext = context; } // We haven't called BeginAcquireState. So we have to call these InitializeRequest // methods here. bool dummy; _store.InitializeRequest(_rqContext); _idManager.InitializeRequest(_rqContext, true, out dummy); id = _idManager.GetSessionID(context); if (id != null) { Debug.Trace("SessionStateOnEndRequest", "Resetting timeout for SessionId=" + id); _store.ResetItemTimeout(context, id); } #if DBG else { Debug.Trace("SessionStateOnEndRequest", "No session id found."); } #endif } } /* Notify the store we are finishing a request */ _store.EndRequest(_rqContext); } finally { _acquireCalled = false; _releaseCalled = false; RestoreImpersonation(); ResetPerRequestFields(); } Debug.Trace("SessionStateOnEndRequest", "Returning from SessionStateModule::OnEndRequest"); } internal static void ReadConnectionString(SessionStateSection config, ref string cntString, string propName) { ConfigsHelper.GetRegistryStringAttribute(ref cntString, config, propName); HandlerBase.CheckAndReadConnectionString(ref cntString, true); } internal bool SessionIDManagerUseCookieless { get { // See VSWhidbey 399907 if (!_usingAspnetSessionIdManager) { return s_configCookieless == HttpCookieMode.UseUri; } else { return ((SessionIDManager)_idManager).UseCookieless(_rqContext); } } } // DevDiv Bugs 151914: Release session state before executing child request internal void EnsureReleaseState(HttpApplication app) { if (HttpRuntime.UseIntegratedPipeline && _acquireCalled && !_releaseCalled) { try { OnReleaseState(app, null); } catch { } } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ //[To be supplied.] ///// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* * SessionStateModule * * Copyright (c) 1998-2002, Microsoft Corporation * */ namespace System.Web.SessionState { using System; using System.Threading; using System.Collections; using System.Configuration; using System.IO; using System.Web.Caching; using System.Web.Util; using System.Web.Configuration; using System.Xml; using System.Security.Cryptography; using System.Data.SqlClient; using System.Globalization; using System.Security.Permissions; using System.Text; using System.Web.Hosting; using System.Web.Management; using Microsoft.Win32; public delegate void SessionStateItemExpireCallback( string id, SessionStateStoreData item); class SessionOnEndTargetWorkItem { SessionOnEndTarget _target; HttpSessionState _sessionState; internal SessionOnEndTargetWorkItem(SessionOnEndTarget target, HttpSessionState sessionState) { _target = target; _sessionState = sessionState; } internal void RaiseOnEndCallback() { _target.RaiseOnEnd(_sessionState); } } /* * Calls the OnSessionEnd event. We use an object other than the SessionStateModule * because the state of the module is unknown - it could have been disposed * when a session ends. */ class SessionOnEndTarget { internal int _sessionEndEventHandlerCount; internal SessionOnEndTarget() { } internal int SessionEndEventHandlerCount { get { return _sessionEndEventHandlerCount; } set { _sessionEndEventHandlerCount = value; } } internal void RaiseOnEnd(HttpSessionState sessionState) { Debug.Trace("SessionOnEnd", "Firing OnSessionEnd for " + sessionState.SessionID); if (_sessionEndEventHandlerCount > 0) { HttpApplicationFactory.EndSession(sessionState, this, EventArgs.Empty); } } internal void RaiseSessionOnEnd(String id, SessionStateStoreData item) { HttpSessionStateContainer sessionStateContainer = new HttpSessionStateContainer( id, item.Items, item.StaticObjects, item.Timeout, false, SessionStateModule.s_configCookieless, SessionStateModule.s_configMode, true); HttpSessionState sessionState = new HttpSessionState(sessionStateContainer); if (HttpRuntime.ShutdownInProgress) { // call directly when shutting down RaiseOnEnd(sessionState); } else { // post via thread pool SessionOnEndTargetWorkItem workItem = new SessionOnEndTargetWorkItem(this, sessionState); WorkItem.PostInternal(new WorkItemCallback(workItem.RaiseOnEndCallback)); } } } /* * The sesssion state module provides session state services * for an application. */ ////// [AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)] public sealed class SessionStateModule : IHttpModule { internal const string SQL_CONNECTION_STRING_DEFAULT = "data source=localhost;Integrated Security=SSPI"; internal const string STATE_CONNECTION_STRING_DEFAULT = "tcpip=loopback:42424"; internal const int TIMEOUT_DEFAULT = 20; internal const SessionStateMode MODE_DEFAULT = SessionStateMode.InProc; private static long LOCKED_ITEM_POLLING_INTERVAL = 500; // in milliseconds static readonly TimeSpan LOCKED_ITEM_POLLING_DELTA = new TimeSpan(250 * TimeSpan.TicksPerMillisecond); static readonly TimeSpan DEFAULT_DBG_EXECUTION_TIMEOUT = new TimeSpan(0, 0, System.Web.Compilation.PageCodeDomTreeGenerator.DebugScriptTimeout); // When we are using Cache to store session state (InProc and StateServer), // can't specify a timeout value larger than 1 year because CacheEntry ctor // will throw an exception. internal const int MAX_CACHE_BASED_TIMEOUT_MINUTES = 365 * 24 * 60; bool s_oneTimeInit; static int s_timeout; #pragma warning disable 0649 static ReadWriteSpinLock s_lock; #pragma warning restore 0649 static bool s_trustLevelInsufficient; static TimeSpan s_configExecutionTimeout; static bool s_configRegenerateExpiredSessionId; static bool s_useHostingIdentity; internal static HttpCookieMode s_configCookieless; internal static SessionStateMode s_configMode; // This is used as a perf optimization for IIS7 Integrated Mode. If session state is released // in ReleaseState, we can disable the EndRequest notification if the mode is InProc or StateServer // because neither InProcSessionStateStore.EndRequest nor OutOfProcSessionStateStore.EndRequest // are implemented. static bool s_canSkipEndRequestCall; private static bool s_PollIntervalRegLookedUp = false; private static object s_PollIntervalRegLock = new object(); // // Check if we can optmize for InProc case. // Optimization details: // // If we are in InProc mode, and cookieless=false, in certain scenarios we // can avoid reading the session ID from the cookies because that's an expensive operation. // To allow that, we use s_sessionEverSet to keep track of whether we've ever created // any session state. // // If no session has ever be created, we can optimize in the following two cases: // // Case 1: Page has disabled session state // In BeginAcquireState, we usually read the session ID, and reset the timeout value // of the session state. However, since no session has ever been created, we can // skip both reading the session id and resetting the timeout. // // Case 2: Page has enabled session state // In this case, we will delay reading (and creating it if not found) the session ID // until it's really needed. (e.g. from HttpSessionStateContainer.SessionID) // // Please note that we optimize only if the app is using SessionIDManager // as the session ID provider; otherwise, we do not have knowledge about // the provider in order to optimize safely. // // And we will delay reading the id only if we are using cookie to store the session ID. If we // use cookieless, in the delayed session ID creation scenario, cookieless requires a redirect, // and it'll be bad to do that in the middle of a page execution. // static bool s_allowInProcOptimization; static bool s_sessionEverSet; // // Another optimization is to delay the creation of a new session state store item // until it's needed. static bool s_allowDelayedStateStoreItemCreation; static HttpSessionStateContainer s_delayedSessionState = new HttpSessionStateContainer(); /* per application vars */ EventHandler _sessionStartEventHandler; Timer _timer; TimerCallback _timerCallback; volatile int _timerId; ISessionIDManager _idManager; bool _usingAspnetSessionIdManager; SessionStateStoreProviderBase _store; bool _supportSessionExpiry; IPartitionResolver _partitionResolver; bool _ignoreImpersonation; readonly SessionOnEndTarget _onEndTarget = new SessionOnEndTarget(); /* per request data goes in _rq* variables */ bool _acquireCalled; bool _releaseCalled; HttpSessionStateContainer _rqSessionState; String _rqId; bool _rqIdNew; ISessionStateItemCollection _rqSessionItems; HttpStaticObjectsCollection _rqStaticObjects; bool _rqIsNewSession; bool _rqSessionStateNotFound; bool _rqReadonly; HttpContext _rqContext; HttpAsyncResult _rqAr; SessionStateStoreData _rqItem; object _rqLockId; // The id of its SessionStateItem ownership // If the ownership change hands (e.g. this ownership // times out), the lockId of the item at the store // will change. int _rqInCallback; DateTime _rqLastPollCompleted; TimeSpan _rqExecutionTimeout; bool _rqAddedCookie; SessionStateActions _rqActionFlags; ImpersonationContext _rqIctx; internal int _rqChangeImpersonationRefCount; ImpersonationContext _rqTimerThreadImpersonationIctx; bool _rqSupportSessionIdReissue; bool _rqUninitializedItem; ///[To be supplied.] ////// [SecurityPermission(SecurityAction.Demand, Unrestricted=true)] public SessionStateModule() { } static bool CheckTrustLevel(SessionStateSection config) { switch (config.Mode) { case SessionStateMode.SQLServer: case SessionStateMode.StateServer: return HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium); default: case SessionStateMode.Off: case SessionStateMode.InProc: // In-proc session doesn't require any trust level (part of ASURT 124513) return true; } } // Create an instance of the custom store as specified in the config file SessionStateStoreProviderBase InitCustomStore(SessionStateSection config) { string providerName = config.CustomProvider; ProviderSettings ps; if (String.IsNullOrEmpty(providerName)) { throw new ConfigurationErrorsException( SR.GetString(SR.Invalid_session_custom_provider, providerName), config.ElementInformation.Properties["customProvider"].Source, config.ElementInformation.Properties["customProvider"].LineNumber); } ps = config.Providers[providerName]; if (ps == null) { throw new ConfigurationErrorsException( SR.GetString(SR.Missing_session_custom_provider, providerName), config.ElementInformation.Properties["customProvider"].Source, config.ElementInformation.Properties["customProvider"].LineNumber); } return (SessionStateStoreProviderBase)ProvidersHelper.InstantiateProvider(ps, typeof(SessionStateStoreProviderBase)); } IPartitionResolver InitPartitionResolver(SessionStateSection config) { string partitionResolverType = config.PartitionResolverType; Type resolverType; IPartitionResolver iResolver; if (String.IsNullOrEmpty(partitionResolverType)) { return null; } if (config.Mode != SessionStateMode.StateServer && config.Mode != SessionStateMode.SQLServer) { throw new ConfigurationErrorsException(SR.GetString(SR.Cant_use_partition_resolve), config.ElementInformation.Properties["partitionResolverType"].Source, config.ElementInformation.Properties["partitionResolverType"].LineNumber); } resolverType = ConfigUtil.GetType(partitionResolverType, "partitionResolverType", config); ConfigUtil.CheckAssignableType(typeof(IPartitionResolver), resolverType, config, "partitionResolverType"); iResolver = (IPartitionResolver)HttpRuntime.CreatePublicInstance(resolverType); iResolver.Initialize(); return iResolver; } ISessionIDManager InitSessionIDManager(SessionStateSection config) { string sessionIDManagerType = config.SessionIDManagerType; ISessionIDManager iManager; if (String.IsNullOrEmpty(sessionIDManagerType)) { iManager = new SessionIDManager(); _usingAspnetSessionIdManager = true; } else { Type managerType; managerType = ConfigUtil.GetType(sessionIDManagerType, "sessionIDManagerType", config); ConfigUtil.CheckAssignableType(typeof(ISessionIDManager), managerType, config, "sessionIDManagerType"); iManager = (ISessionIDManager)HttpRuntime.CreatePublicInstance(managerType); } iManager.Initialize(); return iManager; } void InitModuleFromConfig(HttpApplication app, SessionStateSection config) { if (config.Mode == SessionStateMode.Off) { return; } app.AddOnAcquireRequestStateAsync( new BeginEventHandler(this.BeginAcquireState), new EndEventHandler(this.EndAcquireState)); app.ReleaseRequestState += new EventHandler(this.OnReleaseState); app.EndRequest += new EventHandler(this.OnEndRequest); _partitionResolver = InitPartitionResolver(config); switch (config.Mode) { case SessionStateMode.InProc: if (HttpRuntime.UseIntegratedPipeline) { s_canSkipEndRequestCall = true; } _store = new InProcSessionStateStore(); _store.Initialize(null, null); break; #if !FEATURE_PAL // FEATURE_PAL does not enable out of proc session state case SessionStateMode.StateServer: if (HttpRuntime.UseIntegratedPipeline) { s_canSkipEndRequestCall = true; } _store = new OutOfProcSessionStateStore(); ((OutOfProcSessionStateStore)_store).Initialize(null, null, _partitionResolver); break; case SessionStateMode.SQLServer: _store = new SqlSessionStateStore(); ((SqlSessionStateStore)_store).Initialize(null, null, _partitionResolver); #if DBG ((SqlSessionStateStore)_store).SetModule(this); #endif break; #else // !FEATURE_PAL case SessionStateMode.StateServer: throw new NotImplementedException("ROTORTODO"); break; case SessionStateMode.SQLServer: throw new NotImplementedException("ROTORTODO"); break; #endif // !FEATURE_PAL case SessionStateMode.Custom: _store = InitCustomStore(config); break; default: break; } // We depend on SessionIDManager to manage session id _idManager = InitSessionIDManager(config); if ((config.Mode == SessionStateMode.InProc || config.Mode == SessionStateMode.StateServer) && _usingAspnetSessionIdManager) { // If we're using InProc mode or StateServer mode, and also using our own session id module, // we know we don't care about impersonation in our all session state store read/write // and session id read/write. _ignoreImpersonation = true; } } public void Init(HttpApplication app) { bool initModuleCalled = false; SessionStateSection config = RuntimeConfig.GetAppConfig().SessionState; if (!s_oneTimeInit) { s_lock.AcquireWriterLock(); try { if (!s_oneTimeInit) { InitModuleFromConfig(app, config); initModuleCalled = true; if (!CheckTrustLevel(config)) s_trustLevelInsufficient = true; s_timeout = (int)config.Timeout.TotalMinutes; s_useHostingIdentity = config.UseHostingIdentity; // See if we can try InProc optimization. See inline doc of s_allowInProcOptimization // for details. if (config.Mode == SessionStateMode.InProc && _usingAspnetSessionIdManager) { s_allowInProcOptimization = true; } if (config.Mode != SessionStateMode.Custom && config.Mode != SessionStateMode.Off && !config.RegenerateExpiredSessionId) { s_allowDelayedStateStoreItemCreation = true; } s_configExecutionTimeout = RuntimeConfig.GetConfig().HttpRuntime.ExecutionTimeout; s_configRegenerateExpiredSessionId = config.RegenerateExpiredSessionId; s_configCookieless = config.Cookieless; s_configMode = config.Mode; // The last thing to set in this if-block. s_oneTimeInit = true; Debug.Trace("SessionStateModuleInit", "Configuration: _mode=" + config.Mode + ";Timeout=" + config.Timeout + ";CookieMode=" + config.Cookieless + ";SqlConnectionString=" + config.SqlConnectionString + ";StateConnectionString=" + config.StateConnectionString + ";s_allowInProcOptimization=" + s_allowInProcOptimization + ";s_allowDelayedStateStoreItemCreation=" + s_allowDelayedStateStoreItemCreation); } } finally { s_lock.ReleaseWriterLock(); } } if (!initModuleCalled) { InitModuleFromConfig(app, config); } if (s_trustLevelInsufficient) { throw new HttpException(SR.GetString(SR.Session_state_need_higher_trust)); } } ////// Initializes a new instance of the ////// class. /// /// public void Dispose() { if (_timer != null) { ((IDisposable)_timer).Dispose(); } if (_store != null) { _store.Dispose(); } } void ResetPerRequestFields() { Debug.Assert(_rqIctx == null, "_rqIctx == null"); Debug.Assert(_rqChangeImpersonationRefCount == 0, "_rqChangeImpersonationRefCount == 0"); _rqSessionState = null; _rqId = null; _rqSessionItems = null; _rqStaticObjects = null; _rqIsNewSession = false; _rqSessionStateNotFound = true; _rqReadonly = false; _rqItem = null; _rqContext = null; _rqAr = null; _rqLockId = null; _rqInCallback = 0; _rqLastPollCompleted = DateTime.MinValue; _rqExecutionTimeout = TimeSpan.Zero; _rqAddedCookie = false; _rqIdNew = false; _rqActionFlags = 0; _rqIctx = null; _rqChangeImpersonationRefCount = 0; _rqTimerThreadImpersonationIctx = null; _rqSupportSessionIdReissue = false; _rqUninitializedItem = false; } /* * Add a OnStart event handler. * * @param sessionEventHandler */ ///[To be supplied.] ////// public event EventHandler Start { add { _sessionStartEventHandler += value; } remove { _sessionStartEventHandler -= value; } } void RaiseOnStart(EventArgs e) { if (_sessionStartEventHandler == null) return; Debug.Trace("SessionStateModuleRaiseOnStart", "Session_Start called for session id:" + _rqId); // Session_OnStart for ASPCOMPAT pages has to be raised from an STA thread // if (HttpRuntime.ApartmentThreading || _rqContext.InAspCompatMode) { #if !FEATURE_PAL // FEATURE_PAL does not enable COM AspCompatApplicationStep.RaiseAspCompatEvent( _rqContext, _rqContext.ApplicationInstance, null, _sessionStartEventHandler, this, e); #else // !FEATURE_PAL throw new NotImplementedException ("ROTORTODO"); #endif // !FEATURE_PAL } else { if (HttpContext.Current == null) { // This can happen if it's called by a timer thread HttpContextWrapper.SwitchContext(_rqContext); } _sessionStartEventHandler(this, e); } } /* * Fire the OnStart event. * * @param e */ void OnStart(EventArgs e) { RaiseOnStart(e); } /* * Add a OnEnd event handler. * * @param sessionEventHandler */ ///[To be supplied.] ////// public event EventHandler End { add { lock(_onEndTarget) { if (_store != null && _onEndTarget.SessionEndEventHandlerCount == 0) { _supportSessionExpiry = _store.SetItemExpireCallback( new SessionStateItemExpireCallback(_onEndTarget.RaiseSessionOnEnd)); } ++_onEndTarget.SessionEndEventHandlerCount; } } remove { lock(_onEndTarget) { --_onEndTarget.SessionEndEventHandlerCount; // if (_store != null && _onEndTarget.SessionEndEventHandlerCount == 0) { _store.SetItemExpireCallback(null); _supportSessionExpiry = false; } } } } /* * Acquire session state */ IAsyncResult BeginAcquireState(Object source, EventArgs e, AsyncCallback cb, Object extraData) { bool requiresState; bool isCompleted = true; bool skipReadingId = false; Debug.Trace("SessionStateModuleOnAcquireState", "Beginning SessionStateModule::OnAcquireState"); _acquireCalled = true; _releaseCalled = false; ResetPerRequestFields(); _rqContext = ((HttpApplication)source).Context; _rqAr = new HttpAsyncResult(cb, extraData); ChangeImpersonation(_rqContext, false); try { if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_BEGIN, _rqContext.WorkerRequest); /* Notify the store we are beginning to get process request */ _store.InitializeRequest(_rqContext); /* determine if the request requires state at all */ requiresState = _rqContext.RequiresSessionState; // SessionIDManager may need to do a redirect if cookieless setting is AutoDetect if (_idManager.InitializeRequest(_rqContext, false, out _rqSupportSessionIdReissue)) { _rqAr.Complete(true, null, null); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest); return _rqAr; } // See if we can skip reading the session id. See inline doc of s_allowInProcOptimization // for details. if (s_allowInProcOptimization && !s_sessionEverSet && (!requiresState || // Case 1 !((SessionIDManager)_idManager).UseCookieless(_rqContext)) ) { // Case 2 skipReadingId = true; #if DBG if (!requiresState) { // Case 1 Debug.Trace("SessionStateModuleOnAcquireState", "Skip reading id because page has disabled session state"); } else { // Case 2 Debug.Trace("SessionStateModuleOnAcquireState", "Delay reading id because we're using InProc optimization, and we are not using cookieless"); } #endif } else { /* Get sessionid */ _rqId = _idManager.GetSessionID(_rqContext); Debug.Trace("SessionStateModuleOnAcquireState", "Current request id=" + _rqId); } if (!requiresState) { if (_rqId == null) { Debug.Trace("SessionStateModuleOnAcquireState", "Handler does not require state, " + "session id skipped or no id found, " + "skipReadingId=" + skipReadingId + "\nReturning from SessionStateModule::OnAcquireState"); } else { Debug.Trace("SessionStateModuleOnAcquireState", "Handler does not require state, " + "resetting timeout for SessionId=" + _rqId + "\nReturning from SessionStateModule::OnAcquireState"); // Still need to update the sliding timeout to keep session alive. // There is a plan to skip this for perf reason. But it was postponed to // after Whidbey. _store.ResetItemTimeout(_rqContext, _rqId); } _rqAr.Complete(true, null, null); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest); return _rqAr; } _rqExecutionTimeout = _rqContext.Timeout; // If the page is marked as DEBUG, HttpContext.Timeout will return a very large value (~1 year) // In this case, we want to use the executionTimeout value specified in the config to avoid // PollLockedSession to run forever. if (_rqExecutionTimeout == DEFAULT_DBG_EXECUTION_TIMEOUT) { _rqExecutionTimeout = s_configExecutionTimeout; } /* determine if we need just read-only access */ _rqReadonly = _rqContext.ReadOnlySessionState; if (_rqId != null) { /* get the session state corresponding to this session id */ isCompleted = GetSessionStateItem(); } else if (!skipReadingId) { /* if there's no id yet, create it */ bool redirected = CreateSessionId(); _rqIdNew = true; if (redirected) { if (s_configRegenerateExpiredSessionId) { // See inline comments in CreateUninitializedSessionState() CreateUninitializedSessionState(); } _rqAr.Complete(true, null, null); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest); return _rqAr; } } if (isCompleted) { CompleteAcquireState(); _rqAr.Complete(true, null, null); } return _rqAr; } finally { RestoreImpersonation(); } } internal bool CreateSessionId() { // CreateSessionId should be called only if: Debug.Assert(_rqId == null || // Session id isn't found in the request, OR (_rqSessionStateNotFound && // The session state isn't found, AND s_configRegenerateExpiredSessionId && // We are regenerating expired session id, AND _rqSupportSessionIdReissue && // This request supports session id re-issue, AND !_rqIdNew), // The above three condition should imply the session id // isn't just created, but is sent by the request. "CreateSessionId should be called only if we're generating new id, or re-generating expired one"); Debug.Assert(_rqChangeImpersonationRefCount > 0, "Must call ChangeImpersonation first"); bool redirected; _rqId = _idManager.CreateSessionID(_rqContext); _idManager.SaveSessionID(_rqContext, _rqId, out redirected, out _rqAddedCookie); return redirected; } // Called when AcquireState is done. This function will add the returned // SessionStateStore item to the request context. void CompleteAcquireState() { Debug.Trace("SessionStateModuleOnAcquireState", "Item retrieved=" + (_rqItem != null).ToString(CultureInfo.InvariantCulture)); bool delayInitStateStoreItem = false; Debug.Assert(!(s_allowDelayedStateStoreItemCreation && s_configRegenerateExpiredSessionId), "!(s_allowDelayedStateStoreItemCreation && s_configRegenerateExpiredSessionId)"); try { if (_rqItem != null) { _rqSessionStateNotFound = false; if ((_rqActionFlags & SessionStateActions.InitializeItem) != 0) { Debug.Trace("SessionStateModuleOnAcquireState", "Initialize an uninit item"); _rqIsNewSession = true; } else { _rqIsNewSession = false; } } else { _rqIsNewSession = true; _rqSessionStateNotFound = true; if (s_allowDelayedStateStoreItemCreation) { Debug.Trace("SessionStateModuleOnAcquireState", "Delay creating new session state"); delayInitStateStoreItem = true; } // We couldn't find the session state. if (!_rqIdNew && // If the request has a session id, that means the session state has expired s_configRegenerateExpiredSessionId && // And we're asked to regenerate expired session _rqSupportSessionIdReissue) { // And this request support session id reissue // We will generate a new session id for this expired session state bool redirected = CreateSessionId(); Debug.Trace("SessionStateModuleOnAcquireState", "Complete re-creating new id; redirected=" + redirected); if (redirected) { Debug.Trace("SessionStateModuleOnAcquireState", "Will redirect because we've reissued a new id and it's cookieless"); CreateUninitializedSessionState(); return; } } } if (delayInitStateStoreItem) { SessionStateUtility.AddDelayedHttpSessionStateToContext(_rqContext, this); _rqSessionState = s_delayedSessionState; } else { InitStateStoreItem(true); } if (_rqIsNewSession) { Debug.Trace("SessionStateModuleOnAcquireState", "Calling OnStart"); OnStart(EventArgs.Empty); } } finally { if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest); } #if DBG if (_rqIsNewSession) { if (_rqId == null) { Debug.Assert(s_allowInProcOptimization, "s_allowInProcOptimization"); Debug.Trace("SessionStateModuleOnAcquireState", "New session: session id reading is delayed"+ "\nReturning from SessionStateModule::OnAcquireState"); } else { Debug.Trace("SessionStateModuleOnAcquireState", "New session: SessionId= " + _rqId + "\nReturning from SessionStateModule::OnAcquireState"); } } else { Debug.Trace("SessionStateModuleOnAcquireState", "Retrieved old session, SessionId= " + _rqId + "\nReturning from SessionStateModule::OnAcquireState"); } #endif } void CreateUninitializedSessionState() { Debug.Assert(_rqChangeImpersonationRefCount > 0, "Must call ChangeImpersonation first"); // When we generate a new session id in cookieless case, and if "reissueExpiredSession" is // true, we need to generate a new temporary empty session and save it // under the new session id, otherwise when the next request (i.e. when the browser is // redirected back to the web server) comes in, we will think it's accessing an expired session. _store.CreateUninitializedItem(_rqContext, _rqId, s_timeout); } internal void InitStateStoreItem(bool addToContext) { Debug.Assert(_rqId != null || s_allowInProcOptimization, "_rqId != null || s_allowInProcOptimization"); ChangeImpersonation(_rqContext, false); try { if (_rqItem == null) { Debug.Trace("InitStateStoreItem", "Creating new session state"); _rqItem = _store.CreateNewStoreData(_rqContext, s_timeout); } _rqSessionItems = _rqItem.Items; if (_rqSessionItems == null) { throw new HttpException(SR.GetString(SR.Null_value_for_SessionStateItemCollection)); } // No check for null because we allow our custom provider to return a null StaticObjects. _rqStaticObjects = _rqItem.StaticObjects; _rqSessionItems.Dirty = false; _rqSessionState = new HttpSessionStateContainer( this, _rqId, // could be null if we're using InProc optimization _rqSessionItems, _rqStaticObjects, _rqItem.Timeout, _rqIsNewSession, s_configCookieless, s_configMode, _rqReadonly); if (addToContext) { SessionStateUtility.AddHttpSessionStateToContext(_rqContext, _rqSessionState); } } finally { RestoreImpersonation(); } } // Used for InProc session id optimization internal string DelayedGetSessionId() { Debug.Assert(s_allowInProcOptimization, "Shouldn't be called if we don't allow InProc optimization"); Debug.Assert(_rqId == null, "Shouldn't be called if we already have the id"); Debug.Assert(!((SessionIDManager)_idManager).UseCookieless(_rqContext), "We can delay session id only if we are not using cookieless"); Debug.Trace("DelayedOperation", "Delayed getting session id"); bool redirected; ChangeImpersonation(_rqContext, false); try { _rqId = _idManager.GetSessionID(_rqContext); if (_rqId == null) { Debug.Trace("DelayedOperation", "Delayed creating session id"); redirected = CreateSessionId(); Debug.Assert(!redirected, "DelayedGetSessionId shouldn't redirect us here."); } } finally { RestoreImpersonation(); } return _rqId; } bool GetSessionStateItem() { bool isCompleted = true; bool locked; TimeSpan lockAge; Debug.Assert(_rqId != null, "_rqId != null"); Debug.Assert(_rqChangeImpersonationRefCount > 0, "Must call ChangeImpersonation first"); if (_rqReadonly) { _rqItem = _store.GetItem(_rqContext, _rqId, out locked, out lockAge, out _rqLockId, out _rqActionFlags); } else { _rqItem = _store.GetItemExclusive(_rqContext, _rqId, out locked, out lockAge, out _rqLockId, out _rqActionFlags); // DevDiv Bugs 146875: WebForm and WebService Session Access Concurrency Issue // If we have an expired session, we need to insert the state in the store here to // ensure serialized access in case more than one entity requests it simultaneously. // If the state has already been created before, CreateUninitializedSessionState is a no-op. if (_rqItem == null && locked == false && _rqId != null) { if (!(s_configCookieless == HttpCookieMode.UseUri && s_configRegenerateExpiredSessionId == true)) { _rqUninitializedItem = true; CreateUninitializedSessionState(); _rqItem = _store.GetItemExclusive(_rqContext, _rqId, out locked, out lockAge, out _rqLockId, out _rqActionFlags); } } } // We didn't get it because it's locked.... if (_rqItem == null && locked) { // if (lockAge >= _rqExecutionTimeout) { /* Release the lock on the item, which is held by another thread*/ Debug.Trace("SessionStateModuleOnAcquireState", "Lock timed out, lockAge=" + lockAge + ", id=" + _rqId); _store.ReleaseItemExclusive(_rqContext, _rqId, _rqLockId); } Debug.Trace("SessionStateModuleOnAcquireState", "Item is locked, will poll, id=" + _rqId); isCompleted = false; PollLockedSession(); } return isCompleted; } void PollLockedSession() { if (_timerCallback == null) { _timerCallback = new TimerCallback(this.PollLockedSessionCallback); } if (_timer == null) { _timerId++; #if DBG if (!Debug.IsTagPresent("Timer") || Debug.IsTagEnabled("Timer")) #endif { if (!s_PollIntervalRegLookedUp) LookUpRegForPollInterval(); _timer = new Timer(_timerCallback, _timerId, LOCKED_ITEM_POLLING_INTERVAL, LOCKED_ITEM_POLLING_INTERVAL); } } } [RegistryPermission(SecurityAction.Assert, Unrestricted = true)] private static void LookUpRegForPollInterval() { lock (s_PollIntervalRegLock) { if (s_PollIntervalRegLookedUp) return; try { object o = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET", "SessionStateLockedItemPollInterval", 0); if (o != null && (o is int || o is uint) && ((int)o) > 0) LOCKED_ITEM_POLLING_INTERVAL = (int) o; s_PollIntervalRegLookedUp = true; } catch { // ignore exceptions } } } void ResetPollTimer() { _timerId++; if (_timer != null) { ((IDisposable)_timer).Dispose(); _timer = null; } } void ChangeImpersonation(HttpContext context, bool timerThread) { #if !FEATURE_PAL // FEATURE_PAL doesn't enable impersonation _rqChangeImpersonationRefCount++; if (_ignoreImpersonation) { return; } // If SQL store isn't using integrated security, and we're using our own session id module, // we know we don't care about impersonation in our all session state store read/write // and session id read/write. if (s_configMode == SessionStateMode.SQLServer && ((SqlSessionStateStore)_store).KnowForSureNotUsingIntegratedSecurity && _usingAspnetSessionIdManager) { return; } // Please note that there are two types of calls coming in. One is from a request thread, // where timerThread==false; the other is from PollLockedSessionCallback, where // timerThread==true. if (s_useHostingIdentity) { // If we're told to use Application Identity, in each case we should impersonate, // if not called yet. if (_rqIctx == null) { _rqIctx = new ApplicationImpersonationContext(); } } else { if (timerThread) { // For the timer thread, we should explicity impersonate back to what the HttpContext was // orginally impersonating. _rqTimerThreadImpersonationIctx = new ClientImpersonationContext(context, false); } else { // For a request thread, if we're told to not use hosting id, there's no need // to do anything special. Debug.Assert(_rqIctx == null, "_rqIctx == null"); return; } } #endif // !FEATURE_PAL } void RestoreImpersonation() { Debug.Assert(_rqChangeImpersonationRefCount != 0, "_rqChangeImpersonationRefCount != 0"); _rqChangeImpersonationRefCount--; if (_rqChangeImpersonationRefCount == 0) { Debug.Assert(!(_rqIctx != null && _rqTimerThreadImpersonationIctx != null), "Should not have mixed mode of impersonation"); if (_rqIctx != null) { _rqIctx.Undo(); _rqIctx = null; } if (_rqTimerThreadImpersonationIctx != null) { Debug.Assert(_rqContext != null, "_rqContext != null"); _rqTimerThreadImpersonationIctx.Undo(); _rqTimerThreadImpersonationIctx = null; } } } void PollLockedSessionCallback(object state) { Debug.Assert(_rqId != null, "_rqId != null"); Debug.Trace("SessionStateModuleOnAcquireState", "Polling callback called from timer, id=" + _rqId); bool isCompleted = false; Exception error = null; /* check whether we are currently in a callback */ if (Interlocked.CompareExchange(ref _rqInCallback, 1, 0) != 0) return; try { /* * check whether this callback is for the current request, * and whether sufficient time has passed since the last poll * to try again. */ int timerId = (int) state; if ( (timerId == _timerId) && (DateTime.UtcNow - _rqLastPollCompleted >= LOCKED_ITEM_POLLING_DELTA)) { ChangeImpersonation(_rqContext, true); try { isCompleted = GetSessionStateItem(); _rqLastPollCompleted = DateTime.UtcNow; if (isCompleted) { Debug.Assert(_timer != null, "_timer != null"); ResetPollTimer(); CompleteAcquireState(); } } finally { RestoreImpersonation(); } } } catch (Exception e) { ResetPollTimer(); error = e; } finally { Interlocked.Exchange(ref _rqInCallback, 0); } if (isCompleted || error != null) { _rqAr.Complete(false, null, error); } } void EndAcquireState(IAsyncResult ar) { ((HttpAsyncResult)ar).End(); } // Called by OnReleaseState to get the session id. string ReleaseStateGetSessionID() { if (_rqId == null) { Debug.Assert(s_allowInProcOptimization, "s_allowInProcOptimization"); DelayedGetSessionId(); } Debug.Assert(_rqId != null, "_rqId != null"); return _rqId; } /* * Release session state */ ///[To be supplied.] ////// void OnReleaseState(Object source, EventArgs eventArgs) { HttpApplication app; HttpContext context; bool setItemCalled = false; Debug.Trace("SessionStateOnReleaseState", "Beginning SessionStateModule::OnReleaseState"); Debug.Assert(!(_rqAddedCookie && !_rqIsNewSession), "If session id was added to the cookie, it must be a new session."); // !!! // Please note that due to InProc session id optimization, this function should not // use _rqId directly because it can still be null. Instead, use DelayedGetSessionId(). _releaseCalled = true; app = (HttpApplication)source; context = app.Context; ChangeImpersonation(context, false); try { if (_rqSessionState != null) { bool delayedSessionState = (_rqSessionState == s_delayedSessionState); Debug.Trace("SessionStateOnReleaseState", "Remove session state from context"); SessionStateUtility.RemoveHttpSessionStateFromContext(_rqContext, delayedSessionState); /* * Don't store untouched new sessions. */ if ( // The store doesn't have the session state. // ( Please note we aren't checking _rqIsNewSession because _rqIsNewSession // is lalso true if the item is converted from temp to perm in a GetItemXXX() call.) _rqSessionStateNotFound // OnStart is not defined && _sessionStartEventHandler == null // Nothing has been stored in session state && (delayedSessionState || !_rqSessionItems.Dirty) && (delayedSessionState || _rqStaticObjects == null || _rqStaticObjects.NeverAccessed) ) { Debug.Trace("SessionStateOnReleaseState", "Not storing unused new session."); } else if (_rqSessionState.IsAbandoned) { Debug.Trace("SessionStateOnReleaseState", "Removing session due to abandonment, SessionId=" + _rqId); if (_rqSessionStateNotFound) { // The store provider doesn't have it, and so we don't need to remove it from the store. // However, if the store provider supports session expiry, and we have a Session_End in global.asax, // we need to explicitly call Session_End. if (_supportSessionExpiry) { if (delayedSessionState) { Debug.Assert(s_allowDelayedStateStoreItemCreation, "s_allowDelayedStateStoreItemCreation"); Debug.Assert(_rqItem == null, "_rqItem == null"); InitStateStoreItem(false /*addToContext*/); } _onEndTarget.RaiseSessionOnEnd(ReleaseStateGetSessionID(), _rqItem); } } else { Debug.Assert(_rqItem != null, "_rqItem cannot null if it's not a new session"); // Remove it from the store because the session is abandoned. _store.RemoveItem(_rqContext, ReleaseStateGetSessionID(), _rqLockId, _rqItem); } } else if (!_rqReadonly) { // We need to save it since it isn't read-only // We save it only if there is no error, and if something has changed (unless it's a new session) if ( context.Error == null // no error && ( _rqSessionStateNotFound || _rqSessionItems.Dirty // SessionItems has changed. || (_rqStaticObjects != null && !_rqStaticObjects.NeverAccessed) // Static objects have been accessed || _rqItem.Timeout != _rqSessionState.Timeout // Timeout value has changed ) ) { if (delayedSessionState) { Debug.Assert(_rqIsNewSession, "Saving a session and delayedSessionState is true: _rqIsNewSession must be true"); Debug.Assert(s_allowDelayedStateStoreItemCreation, "Saving a session and delayedSessionState is true: s_allowDelayedStateStoreItemCreation"); Debug.Assert(_rqItem == null, "Saving a session and delayedSessionState is true: _rqItem == null"); InitStateStoreItem(false /*addToContext*/); } #if DBG if (_rqSessionItems.Dirty) { Debug.Trace("SessionStateOnReleaseState", "Setting new session due to dirty SessionItems, SessionId=" + _rqId); } else if (_rqStaticObjects != null && !_rqStaticObjects.NeverAccessed) { Debug.Trace("SessionStateOnReleaseState", "Setting new session due to accessed Static Objects, SessionId=" + _rqId); } else if (_rqSessionStateNotFound) { Debug.Trace("SessionStateOnReleaseState", "Setting new session because it's not found, SessionId=" + _rqId); } else { Debug.Trace("SessionStateOnReleaseState", "Setting new session due to options change, SessionId=" + _rqId + "\n\t_rq.timeout=" + _rqItem.Timeout.ToString(CultureInfo.InvariantCulture) + ", _rqSessionState.timeout=" + _rqSessionState.Timeout.ToString(CultureInfo.InvariantCulture)); } #endif if (_rqItem.Timeout != _rqSessionState.Timeout) { _rqItem.Timeout = _rqSessionState.Timeout; } s_sessionEverSet = true; setItemCalled = true; _store.SetAndReleaseItemExclusive(_rqContext, ReleaseStateGetSessionID(), _rqItem, _rqLockId, _rqSessionStateNotFound); } else if (_rqUninitializedItem) { _store.RemoveItem(_rqContext, ReleaseStateGetSessionID(), _rqLockId, _rqItem); } else { // Can't save it because of various reason. Just release our exclusive lock on it. Debug.Trace("SessionStateOnReleaseState", "Release exclusive lock on session, SessionId=" + _rqId); if (!_rqSessionStateNotFound) { Debug.Assert(_rqItem != null, "_rqItem cannot null if it's not a new session"); _store.ReleaseItemExclusive(_rqContext, ReleaseStateGetSessionID(), _rqLockId); } } } #if DBG else { Debug.Trace("SessionStateOnReleaseState", "Session is read-only, ignoring SessionId=" + _rqId); } #endif Debug.Trace("SessionStateOnReleaseState", "Returning from SessionStateModule::OnReleaseState"); } if (_rqAddedCookie && !setItemCalled && context.Response.IsBuffered()) { _idManager.RemoveSessionID(_rqContext); } } finally { RestoreImpersonation(); } // WOS 1679798: PERF: Session State Module should disable EndRequest on successful cleanup bool implementsIRequiresSessionState = context.RequiresSessionState; if (HttpRuntime.UseIntegratedPipeline && (context.NotificationContext.CurrentNotification == RequestNotification.ReleaseRequestState) && (s_canSkipEndRequestCall || !implementsIRequiresSessionState)) { context.DisableNotifications(RequestNotification.EndRequest, 0 /*postNotifications*/); _acquireCalled = false; _releaseCalled = false; ResetPerRequestFields(); } } /* * End of request processing. Possibly does release if skipped due to errors */ ///[To be supplied.] ////// void OnEndRequest(Object source, EventArgs eventArgs) { HttpApplication app; HttpContext context; String id; Debug.Trace("SessionStateOnEndRequest", "Beginning SessionStateModule::OnEndRequest"); app = (HttpApplication)source; context = app.Context; /* determine if the request requires state at all */ if (!context.RequiresSessionState) { return; } ChangeImpersonation(context, false); try { if (!_releaseCalled) { if (_acquireCalled) { /* * need to do release here if the request short-circuited due to an error */ OnReleaseState(source, eventArgs); } else { /* * 'advise' -- update session timeout */ if (_rqContext == null) { _rqContext = context; } // We haven't called BeginAcquireState. So we have to call these InitializeRequest // methods here. bool dummy; _store.InitializeRequest(_rqContext); _idManager.InitializeRequest(_rqContext, true, out dummy); id = _idManager.GetSessionID(context); if (id != null) { Debug.Trace("SessionStateOnEndRequest", "Resetting timeout for SessionId=" + id); _store.ResetItemTimeout(context, id); } #if DBG else { Debug.Trace("SessionStateOnEndRequest", "No session id found."); } #endif } } /* Notify the store we are finishing a request */ _store.EndRequest(_rqContext); } finally { _acquireCalled = false; _releaseCalled = false; RestoreImpersonation(); ResetPerRequestFields(); } Debug.Trace("SessionStateOnEndRequest", "Returning from SessionStateModule::OnEndRequest"); } internal static void ReadConnectionString(SessionStateSection config, ref string cntString, string propName) { ConfigsHelper.GetRegistryStringAttribute(ref cntString, config, propName); HandlerBase.CheckAndReadConnectionString(ref cntString, true); } internal bool SessionIDManagerUseCookieless { get { // See VSWhidbey 399907 if (!_usingAspnetSessionIdManager) { return s_configCookieless == HttpCookieMode.UseUri; } else { return ((SessionIDManager)_idManager).UseCookieless(_rqContext); } } } // DevDiv Bugs 151914: Release session state before executing child request internal void EnsureReleaseState(HttpApplication app) { if (HttpRuntime.UseIntegratedPipeline && _acquireCalled && !_releaseCalled) { try { OnReleaseState(app, null); } catch { } } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.[To be supplied.] ///
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- KeyTimeConverter.cs
- InfoCardMetadataExchangeClient.cs
- SymbolMethod.cs
- MSAAEventDispatcher.cs
- SoapEnumAttribute.cs
- DBSqlParser.cs
- AlternateView.cs
- Base64Encoding.cs
- Padding.cs
- Translator.cs
- SoapEnumAttribute.cs
- OrderedHashRepartitionStream.cs
- MenuItem.cs
- WorkflowPersistenceService.cs
- OciEnlistContext.cs
- DelayedRegex.cs
- HtmlImage.cs
- IdentityNotMappedException.cs
- ImageMap.cs
- TableColumn.cs
- Visual.cs
- UnsafeNativeMethods.cs
- QueryRewriter.cs
- RadialGradientBrush.cs
- QuaternionAnimation.cs
- RecommendedAsConfigurableAttribute.cs
- StreamingContext.cs
- MimeWriter.cs
- CellCreator.cs
- CallTemplateAction.cs
- documentsequencetextview.cs
- _OSSOCK.cs
- ExceptionList.cs
- PLINQETWProvider.cs
- TraceUtility.cs
- MessagePropertyFilter.cs
- AsymmetricAlgorithm.cs
- SmtpSpecifiedPickupDirectoryElement.cs
- MailFileEditor.cs
- HelpEvent.cs
- _SecureChannel.cs
- AssemblyNameEqualityComparer.cs
- InternalBufferOverflowException.cs
- ErrorInfoXmlDocument.cs
- InternalPolicyElement.cs
- CommentAction.cs
- TypeUsage.cs
- PeerCollaborationPermission.cs
- NetworkInterface.cs
- SynchronizedDispatch.cs
- GetWinFXPath.cs
- _SSPIWrapper.cs
- OperationAbortedException.cs
- RegexRunner.cs
- PointValueSerializer.cs
- MatrixValueSerializer.cs
- XmlMemberMapping.cs
- MatrixKeyFrameCollection.cs
- QueuePropertyVariants.cs
- EmbeddedObject.cs
- Avt.cs
- PresentationSource.cs
- Quad.cs
- NativeMethods.cs
- OracleTimeSpan.cs
- VisualStyleRenderer.cs
- XmlWriterTraceListener.cs
- ApplicationServiceHelper.cs
- EncodingDataItem.cs
- FormViewActionList.cs
- ClientRoleProvider.cs
- EventEntry.cs
- IssuanceLicense.cs
- PageAdapter.cs
- TdsParameterSetter.cs
- RadioButton.cs
- SafeSecurityHandles.cs
- TriggerAction.cs
- XmlDeclaration.cs
- SecureUICommand.cs
- TextElement.cs
- StructuredProperty.cs
- HttpAsyncResult.cs
- processwaithandle.cs
- OdbcConnectionPoolProviderInfo.cs
- ContextToken.cs
- ForEach.cs
- ConfigXmlElement.cs
- AccessDataSource.cs
- TimeSpanMinutesConverter.cs
- CriticalHandle.cs
- Point.cs
- PageCatalogPartDesigner.cs
- LinkClickEvent.cs
- XmlElement.cs
- DataFieldCollectionEditor.cs
- CryptoApi.cs
- DataRecordObjectView.cs
- AnnotationHighlightLayer.cs
- XmlUrlEditor.cs