Click here to Skip to main content
15,895,827 members
Articles / Web Development / ASP.NET

Custom session state management in ASP.NET 1.1

Rate me:
Please Sign up or sign in to vote.
4.48/5 (10 votes)
10 Apr 2006MIT7 min read 146.2K   1.6K   55  
This article discusses a custom session state management mechanism that replaces ASP.NET 1.1 SessionStateModule.
using System;
using System.Diagnostics;
using System.Threading;
using System.Web;
using System.Web.SessionState;
using System.Reflection;
using System.Reflection.Emit;

namespace StateHijack
{
	/// <summary>
	/// Custom implementation of SessionStateModule that aggregates "standard"
	/// ASP.NET System.Web.SessionState.SessionStateModule and introduces new
	/// custom StateClientManager class.
	/// </summary>
	public class SessionStateModule: IHttpModule
	{
		private static Assembly _webAsm = null;
		private static Type _mgrType = null;
		private object _mgr = null;
		private SessionStateModuleWrapper _origModuleWrapper = null;

		private Type CreateStateClientManagerType()
		{
			AppDomain curDomain = Thread.GetDomain();

			AssemblyName asmName = new AssemblyName();
			asmName.Name = "StateHijack.StateClientManager";

			AssemblyBuilder asmBuilder = curDomain.DefineDynamicAssembly(
				asmName, 
				AssemblyBuilderAccess.RunAndSave);

			ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule(
				"StateClientManager",
				"StateHijack.StateClientManager.dll");
			
			StateClientManagerFactory mgrFactory = new StateClientManagerFactory();
			Type retVal = mgrFactory.Create(
				"StateClientManager",
				modBuilder,
				typeof(StateClientManagerImp),
				_webAsm.GetType( "System.Web.SessionState.IStateClientManager",	true) );

			// You may want to save this generated assembly for
			// testing (ildasm-ing and reflector-ing) purposes.
			// asmBuilder.Save("StateHijack.StateClientManager.dll");

			return retVal;
		}

		private void InitializeUnderlyingObjects()
		{
			if ( _webAsm == null )
			{
				_webAsm = Assembly.GetAssembly(
					typeof(System.Web.SessionState.SessionStateModule));

				// Generate our custom StateClientManager class
				_mgrType = CreateStateClientManagerType();
			}

			if (_origModuleWrapper == null)
			{
				// Create an instance of the original SessionStateModule
				_origModuleWrapper = new SessionStateModuleWrapper();
				_origModuleWrapper.InnerObject =
					new System.Web.SessionState.SessionStateModule();

				// Create an instance of the newly generated StateClientManager class
				_mgr = Activator.CreateInstance(_mgrType);
			}
		}

		public void Init(HttpApplication app)
		{
			lock (this)
			{
				InitializeUnderlyingObjects();

				// Mimic original SessionStateModule.Init  behavior
				ConfigWrapper config = new ConfigWrapper();
				config.InnerObject = HttpContext.GetAppConfig("system.web/sessionState");
				if (config.InnerObject == null)
				{
					config.Ctor();
				}

				InitModuleFromConfig(app, config.InnerObject, true);
				
				// For OutOfProc and SQLServer, this call checks
				// HttpRuntime.HasAspNetHostingPermission(
				//       AspNetHostingPermissionLevel.Medium);
				if (!_origModuleWrapper.CheckTrustLevel(config.InnerObject))
				{
					_origModuleWrapper.s_trustLevelInsufficient = true;
				}

				_origModuleWrapper.s_config = config.InnerObject;
			} // lock

			if (_origModuleWrapper._mgr == null)
			{
				InitModuleFromConfig(app, _origModuleWrapper.s_config, false);
			}

			if (_origModuleWrapper.s_trustLevelInsufficient)
			{
				throw new HttpException("Session state need higher trust");
			}
		}

		public void InitModuleFromConfig(
			HttpApplication app,
			object configObject,
			bool configInit) 
		{
			ConfigWrapper config = new ConfigWrapper();
			config.InnerObject = configObject;

			// Mimic original SessionStateModule.InitModuleFromConfig behavior
			if (config._mode == SessionStateMode.Off)
			{
				return;
			}

			if (config._isCookieless)
			{
				// Cookieless functionality requires adding this handler
				app.BeginRequest += new EventHandler( this.OnBeginRequest );

				// Add session id to the path
				HttpContextWrapper curContext = new HttpContextWrapper();
				curContext.InnerObject = curContext.Current;

				_origModuleWrapper.s_appPath =
					curContext.InnerObject.Request.ApplicationPath;

				if (_origModuleWrapper.s_appPath[
					_origModuleWrapper.s_appPath.Length - 1] != '/')
				{
					_origModuleWrapper.s_appPath += "/";
				}

				_origModuleWrapper.s_iSessionId =
					_origModuleWrapper.s_appPath.Length;
				_origModuleWrapper.s_iRestOfPath =
					_origModuleWrapper.s_iSessionId + 0x1a;
			}

			// Add event handlers
			app.AddOnAcquireRequestStateAsync(
				new BeginEventHandler(this.BeginAcquireState),
				new EndEventHandler(this.EndAcquireState));
			app.ReleaseRequestState +=(new EventHandler(this.OnReleaseState));
			app.EndRequest +=(new EventHandler(this.OnEndRequest));

			// Instead of analyzing config and choosing between InProc, OutOfProc, SQL etc,
			// "patch" original SessionStateModule object, make _mgr point to
			// our instance of StateClientManager. We could even provide wrappers
			// for standard state client managers (InProc, OutOfProc, SqlServer)
			// and create correspondent objects here (see that switch() statement in the
			// original SessionStateModule.InitModuleFromConfig), but:
			// - that would require some hacking on sessionState section handler, since
			//   "custom" sessionState managers are not supported in 1.1;
			// - we do not have a goal to come up with a "better ASP.NET";
			// - ASP.NET team has already done some part of the job in .NET 2.0

			_origModuleWrapper._mgr = _mgr;

			if (configInit)
			{
				// For the sake of consistency, call IStateClientManager.SetStateModule,
				// but it does not do anything anyways, see comments within
				MethodInfo setStateModuleInfo = _mgrType.GetMethod(
						"SetStateModule",
						BindingFlags.Instance | BindingFlags.Public );
				object[] invokeParams = new object[1] { _origModuleWrapper.InnerObject };
				setStateModuleInfo.Invoke( _mgr, invokeParams );
			}
		}

		public void Dispose()
		{
			lock(this)
			{
				_mgr = null;
				_origModuleWrapper = null;
			}
			_origModuleWrapper.InnerObject.Dispose();
		}

		private void OnBeginRequest(object source, EventArgs eventArgs)
		{
			_origModuleWrapper.OnBeginRequest( source, eventArgs );
		}

		private IAsyncResult BeginAcquireState(
			object source, EventArgs e,	AsyncCallback cb, object extraData)
		{
			return _origModuleWrapper.BeginAcquireState( source, e, cb, extraData );
		}
		private void EndAcquireState(IAsyncResult ar)
		{
			_origModuleWrapper.EndAcquireState( ar );
		}

		private void OnReleaseState(object source, EventArgs eventArgs)
		{
			_origModuleWrapper.OnReleaseState( source, eventArgs );
		}

		private void OnEndRequest(object source, EventArgs eventArgs)
		{
			_origModuleWrapper.OnEndRequest( source, eventArgs );
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Web Developer
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions