First of all, let’s examine what hooks exist in the ASP.NET pipeline for triggering the SessionStateModule to load session state for an HTTP request. Our eventual goal is to load the session state, only when it is needed, into the HttpContext object before the Http Handler’s ProcessRequest method executes. Examining the HttpApplication class, we can see the list of events that are fired by the ASP.NET runtime. The important one is the AcquireRequestState event as it is the event that the SessionStateModule utilizes to determine if session state needs to be loaded. Looking at the event list, we can see that shortly after AcquireRequestState finishes, the HTTP handler itself is invoked. During AcquireRequestState, the SessionStateModule examines the Handler property of the current HttpContext object to see if it implements one of the IRequiresSessionState or IReadOnlySessionState interfaces. If it does, then session state is loaded.
Example:
public void AcquireRequestStateHandler(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
if (context.Handler is IRequiresSessionState)
{
// ...
}
}
Conceivably, if we could change the interface of the HTTP handler at runtime before AcquireRequestState runs, then we could control whether the session state is loaded or not. Fortunately, the Handler property is a writable property. Therefore, by creating "wrapper" HTTP handlers we can wrap the current handler at runtime before the AcquireRequestState event runs. Each wrapper type implements an interface corresponding to a type of session state requirement.
Example wrappers:
///
/// Wraps another IHttpHandler and indicates to the SessionStateModule
/// that read-only session state is required for the request.
///
public class ReadStateHandlerWrapper : IHttpHandler, IReadOnlySessionState
{
private IHttpHandler _wrappedHandler;
public ReadStateHandlerWrapper(IHttpHandler wrappedHandler)
{
_wrappedHandler = wrappedHandler;
}
public bool IsReusable
{
get { return _wrappedHandler.IsReusable; }
}
public void ProcessRequest(HttpContext context)
{
_wrappedHandler.ProcessRequest(context);
}
}
///
/// Wraps another IHttpHandler and indicates to the SessionStateModule
/// that read/write session state is required for the request.
///
public class ReadWriteStateHandlerWrapper : IHttpHandler, IRequiresSessionState
{
private IHttpHandler _wrappedHandler;
public ReadWriteStateHandlerWrapper(IHttpHandler wrappedHandler)
{
_wrappedHandler = wrappedHandler;
}
public bool IsReusable
{
get { return _wrappedHandler.IsReusable; }
}
public void ProcessRequest(HttpContext context)
{
_wrappedHandler.ProcessRequest(context);
}
}
Looking again at the HttpApplication events, we can see that an event named PostMapRequestHandler runs before AcquireRequestState executes. All that remains is to create a custom HTTP module that hooks into PostMapRequestHandler, and implements runtime logic to determine if session state is required for the current request or not.
Example module:
public class StateInitModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.PostMapRequestHandler += new EventHandler(PostMapRequestHandler);
}
void PostMapRequestHandler(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
// Step 1. Determine if a handler is executing that we care about
// Example: check it by type name
if (null != context.Handler
&& "MyHandlerTypeName" == context.Handler.GetType().Name)
{
// Step 2. Determine if the handler requires session state or not
// This will be specific to your particular application.
// For example, you may examine a query string parameter.
if(/* ... Logic to determine read-only access requirement */)
{
context.Handler = new ReadStateHandlerWrapper(context.Handler);
}
else if(/* ... Logic to determine read/write access requirement */)
{
context.Handler = new ReadWriteStateHandlerWrapper(context.Handler);
} // else do not alter the Handler property and state will not be loaded
}
}
}
Keep in mind that HTTP modules execute in front of all requests so you should try to keep your logic as light and efficient as possible. Obviously each particular application will need to define its own heuristic for determining if a given HTTP handler request requires session state or not, but the above pattern will provide the capability to implement the logic at runtime.

0 comments:
Post a Comment