Jul 17 2008

ASP.NET Session State for Native HTTP Requests in IIS 7.0

In ASP.NET 2.0 and ASP.NET 3.5 applications hosted under IIS 7.0 and are running with the Integrated Mode, ASP.NET Session State does not get initialized when a native or non-managed request enters the Integrated HTTP Request pipeline.

If you look at the IIS 7.0's applicationHost.config configuration file located at: %windir%/System32/inetsrv/config you will notice that the SessionStateModule is defined to handle only managed requests as follows:

<add name="Session" type="System.Web.SessionState.SessionStateModule" preCondition="managedHandler" /> 

You notice that the preCondition attribute has the value of managedHandler which means that this module would be initialized only for managed requests, i.e. .aspx, .asmx, etc ...

First of all to be able to make this module get initialized for non-managed resources, you need to remove this entry and then add the same entry but this time having the preCondition attribute set to empty string.

<remove name="Session" />
<add name="Session" type="System.Web.SessionState.SessionStateModule" preCondition="" />

With the above configuration added to the application's web.config configuration file under the <modules /> configuration section located in <system.webServer /> configuration section group.

Now, the Session State is still not initialized for non-managed resources, why?

The SessionStateModule is usually initialized in the AcquireRequestState HttpApplication's event. This event usually gets called after the MapRequestHandler and PostMapRequestHandler events. This means, the AcquireRequestState event fires after the ASP.NET Runtime has decided on the HttpHandler for the current HTTP Request.

The SessionStateModule does a check if the current HTTP Request's Handler implements the IRequiresSessionState marker interface. If not, the SessionStateModule stops execution and no ASP.NET Session State gets initialized. Why? There is no native or non-managed Handler that can implement the managed IRequiresSessionState marker interface and hence if the SessionStateModule finds out that the Handler created for the current request does not implement the IRequiresSessionState, no Session State gets initialized.

Now, when a non-managed or native request enters the Integrated or Unified HTTP Request Pipeline, ASP.NET Runtime does not create an instance of a managed Handler, the HttpHandler for the current HttpContext is null and not initialized. This means that the SessionStateModule, that has already been mapped for native and managed requests, checks that the current HttpContext's Handler is null and hence there is already no Handler instance created to check if it implements the IRequiresSessionState marker interface.

The trick to solve this problem is provided by Mike Volodarsky (Program Manager in the IIS team) that can be read in this www.iis.net forum post (https://forums.iis.net/p/1094546/1648944.aspx#1648944)

What should be done is fake the SessionStateModule with an instance of a managed HttpHandler that implements IRequiresSessionState marker interface that gets created for native requests.

What are you talking about Bilal?

Well yes. The idea is to develop a custom HttpHandler that implements the IRequiresSessionState marker interface with empty implementation, this handler will be used as a temporary handler only as you will see later in the code.

Next, what we need to do is the following:

In the MapRequestHandler event, a managed handler is created when the HTTP request is for a managed resource. This means, in the PostMapRequestHandler event you can check to see if ASP.NET runtime has created an HttpHandler for the current request, if not, then this means the HTTP request is for a native resource and not a managed one. At this moment, you can initialize a new instance of the custom dummy handler that was introduced above, set it to the Handler of the current HTTP request.

After the PostMapRequestHandler executes, the AcquireRequestState event fires. At this moment, the SessionStateModule starts executing. Now, the SessionStateModule will find out that the current HTTP request has a valid HttpHandler that implements the IRequiresSessionState marker interface, this means now the ASP.NET Session State will get initialized successfully!!

In the PostAcquireRequestState event the current HTTP request's Handler should be reverted back to its original Handler (i.e. Handler created by ASP.NET runtime which is in this case NULL because the request originally is for a native and non-managed request). This is very important because if the custom dummy  HttpHandler was kept as the Handler for the current request, the ASP.NET Runtime would execute that Handler when it is time during the Integrated HTTP Request Pipeline to execute the Handler for the current request.

Therefore, you should check if the current request's Handler is of type the custom dummy HttpHandler then you should set it back to NULL, where NULL represents the Handler's instance that was originally created by the ASP.NET runtime.

The code below shows both the custom dummy HttpHandler and the custom module used to configure the PostAcquireRequestState and PostMapRequestHandler events.

CustomNativeHandler

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.SessionState;
using System.Web.Security;

public class CustomNativeHandler : IHttpHandler, IRequiresSessionState
{
    public bool IsReusable
    {
        get { return false; }
    }

    public void ProcessRequest(HttpContext context)
    {
        throw new NotImplementedException();
    }

   public CustomNativeHandler()
    { }
}

 

SessionStateForNativeRequestsModule

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;

public class SessionStateForNativeRequestsModule : IHttpModule
{
    public void Dispose()
    {
        throw new NotImplementedException();
    }

    public void Init(HttpApplication context)
    {
        // Subscribe to the PostMapRequestHandler
        context.PostMapRequestHandler += new EventHandler(context_PostMapRequestHandler);

        // Subscribe to the PostAcquireRequestState
        context.PostAcquireRequestState += new EventHandler(context_PostAcquireRequestState);
    }

    void context_PostAcquireRequestState(object sender, EventArgs e)
    {
        // Get an instance of the current Context
        HttpContext context = ((HttpApplication)sender).Context;

        // Use "as" so that if the current handler
        // is not a CustomNativeHandler, then no exception
        // will be thrown, like in the case of casting.
        CustomNativeHandler customHandler =
            context.Handler as CustomNativeHandler;

        // If the current handler is our custom dummy handler
        // then revert back to the managed handler that was
        // originally created by the .NET Framework for the
        // current request, which is null in this case
        // since .NET Framework does not create a managed Handler
        // for a native request
        if (customHandler != null)
            context.Handler = null;
    }

    void context_PostMapRequestHandler(object sender, EventArgs e)
    {
        // Get an instance of the current Context
        HttpContext context = ((HttpApplication)sender).Context;

        // If the current HttpHandler is null, then this must be
        // a native request being processed.
        // Create a new instance of the CustomNativeHandler
        // and set it to the current request Handler.
        if (context.Handler == null)
            context.Handler = new CustomNativeHandler();
    }
}

 

To configure the above custom module in an application's web.config configuration file, you add the following section:

    <system.webServer>
        <validation validateIntegratedModeConfiguration="false"/>
        <modules>
            <add name="SessionStateForNativeRequestsModule" type="SessionStateForNativeRequestsModule"/>

            <remove name="Session" />
            <add
                name="Session"
                type="System.Web.SessionState.SessionStateModule"
                preCondition="" />

        </modules>
    </system.webServer>
 

Hope this helps,
Regards

Tags: , , ,

Comments are closed