3

I implemented a Custom Session State Module and added to the application as follows (web.config):

<system.webServer>
    <modules>
        <remove name="Session" />
        <add name="Session" type="CustomServerModules.StateServer.SessionState.SessionStateModule" preCondition="managedHandler" />
    </modules>
</system.webServer>

My IIS Application Pool is set to Integrated v4.0
I added SessionStateData (exactly HttpSessionStateContainer object) to the HttpContext on BeginAcquireState event (according to Microsoft's SessionStateModule):

SessionStateUtility.AddHttpSessionStateToContext(_rqContext, _rqSessionState);

But my problem is Session Object in Page has no value and it throws exception:

Session state can only be used when enableSessionState is set to true, either in a configuration file or in the Page directive. Please also make sure that System.Web.SessionStateModule or a custom session state module is included in the <configuration>\\<system.web>\\<httpModules> section in the application configuration.

Also HttpContext.Current.Session is null.

Now the question is: Why is the Session not accessible in application pages in this custom mode? How can I trace the behavior of IIS and ASP.NET when creating and initiating HttpApplications and Page instances to trace the Session object?

UPDATE
I included the AcquireRequest Way of my Session HttpModule as follows. in this section the new Session will add to the HttpContext and Session_Start will raise:

public sealed class SessionStateModule : IHttpModule
{
    /*
     * Add a Session_OnStart event handler.
     */
    public event EventHandler Start
    {
        add
        {
            _sessionStartEventHandler += value;
        }
        remove
        {
            _sessionStartEventHandler -= value;
        }
    }

    /*
     * IHttpModule Member
     */
    public void Init(HttpApplication app)
    {
        bool initModuleCalled = false;

        string applicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath;
        var webConfig = WebConfigurationManager.OpenWebConfiguration(applicationName);
        s_sessionWebConfig = (SessionStateSection)webConfig.GetSection("system.web/sessionState");

        lck = new ReaderWriterLockSlim();
        if (!s_oneTimeInit)
        {
            lck.EnterWriteLock();
            try
            {
                if (!s_oneTimeInit)
                {
                    initializeModuleFromWebConfig(app);
                    initModuleCalled = true;

                    s_Application = app;
                    s_timeOut = (int)s_sessionWebConfig.Timeout.TotalMinutes;
                    s_useHostingIdentity = s_sessionWebConfig.UseHostingIdentity; // default value of UseHostingIdentity is true.
                    s_configExecutionTimeout = ((HttpRuntimeSection)webConfig.GetSection("system.web/httpRuntime")).ExecutionTimeout; // DefaultValue of ExecutionTimeout = "00:01:50"

                    s_configRegenerateExpiredSessionId = s_sessionWebConfig.RegenerateExpiredSessionId;
                    s_configCookieless = s_sessionWebConfig.Cookieless;
                    s_configMode = s_sessionWebConfig.Mode;

                    // The last thing to set in this if-block.
                    s_oneTimeInit = true;
                }
            }
            finally
            {
                lck.ExitWriteLock();
            }
        }

        if (!initModuleCalled)
        {
            initializeModuleFromWebConfig(app);
        }
    }

    private void initializeModuleFromWebConfig(HttpApplication app)
    {
        #region registering application (HttpApplication) event handlers

        app.AddOnAcquireRequestStateAsync(
            new BeginEventHandler(this.app_BeginAcquireState),
            new EndEventHandler(this.app_EndAcquireState));

        app.ReleaseRequestState += app_ReleaseRequestState;
        app.EndRequest += app_EndRequest;

        #endregion

        #region instantiating SessionStateStoreProvider

        string providerName = s_sessionWebConfig.CustomProvider;
        ProviderSettings ps;

        _store = (SessionStateStoreProviderBase)
            ProvidersHelper.InstantiateProvider(ps, typeof(SessionStateStoreProviderBase));

        #endregion

        _idManager = initSessionIDManager(s_sessionWebConfig);
    }

    private IAsyncResult app_BeginAcquireState(object source, EventArgs e, AsyncCallback cb, object extraData)
    {
        bool requiresState;
        bool isCompleted = true;
        bool skipReadingId = false;

        _acquireCalled = true;
        _releaseCalled = false;

        resetPerRequestFields();

        _rqContext = ((HttpApplication)source).Context;
        _rqAr = new HttpAsyncResult(cb, extraData);

        try
        {
            /* notifying the store that we are beginning to get process request */
            _store.InitializeRequest(_rqContext);

            /* determine if the request requires state at all */
            requiresState = _rqContext.Handler is IRequiresSessionState;  //originally was: _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);
                return _rqAr;
            }

            /* Get sessionid */
            _rqId = _idManager.GetSessionID(_rqContext);

            if (requiresState)
            {
                // Still need to update the sliding timeout to keep session alive.
                if (_rqId != null)
                {
                    _store.ResetItemTimeout(_rqContext, _rqId);
                }

                _rqAr.Complete(true, null, null);
                return _rqAr;
            }

            _rqExecutionTimeout = s_configExecutionTimeout;

            /* determine if we need just read-only access */
            _rqReadonly = _rqContext.Handler is IReadOnlySessionState; // originally was: _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);

                    // commented because this is in case of inProc and OutOfProc
                    //if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest);
                    //return _rqAr;
                }
            }

            if (isCompleted)
            {
                completeAcquireState(source, e);
                _rqAr.Complete(true, null, null);
            }

            return _rqAr;
        }
        finally
        {

        }
    }

    // Called when AcquireState is done.  This function will add the returned
    // SessionStateStore item to the request context.
    private void completeAcquireState(object source, EventArgs e)
    {
        if (_rqItem != null)
        {
            _rqSessionStateNotFound = false;

            if ((_rqActionFlags & SessionStateActions.InitializeItem) != 0)
            {
                //Initialize an uninit item.
                _rqIsNewSession = true;
            }
            else
            {
                _rqIsNewSession = false;
            }
        }
        else
        {
            _rqIsNewSession = true;
            _rqSessionStateNotFound = 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();

                if (redirected)
                {
                    // Will redirect because we've reissued a new id and it's cookieless
                    createUninitializedSessionState();
                    return;
                }
            }
        }


        initStateStoreItem(true);

        if (_rqIsNewSession)
        {
            raiseOnStart(source, e);
        }
    }

    private void initStateStoreItem(bool addToContext)
    {
        try
        {
            if (_rqItem == null)
            {
                //Creating new session state
                _rqItem = _store.CreateNewStoreData(_rqContext, s_timeOut);
            }

            _rqSessionItems = _rqItem.Items;
            if (_rqSessionItems == null)
            {
                throw new HttpException("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(
                            _rqId,
                            _rqSessionItems,
                            _rqStaticObjects,
                            _rqItem.Timeout,
                            _rqIsNewSession,
                            s_configCookieless,
                            s_configMode,
                            _rqReadonly);

            if (addToContext)
            {
                SessionStateUtility.AddHttpSessionStateToContext(_rqContext, _rqSessionState);
            }
        }
        finally
        {

        }
    }

    private void app_EndAcquireState(IAsyncResult ar)
    {
        ((HttpAsyncResult)ar).End();
    }
}

General Overview of code above listed below:

  1. Module's Init() method will instantiate store provider and add handlers for AcquireRequest, ReleaseRequest and EndRequest of HttpApplication's Events. also it instantiate SessionIDManger.
  2. app_BeginAcquireState() will create new session item in createUninitializedSessionState() that stores new item in data store.
  3. after that completeAcquireState() calls to Add new session item to the HttpContext by calling initStateStoreItem(). also Session_Start of Application's Global.asax will call in this method successfully.
  4. session item successfully injected to the current context with SessionStateUtility.AddHttpSessionStateToContext(_rqContext, _rqSessionState);
Mehdi
  • 5,435
  • 6
  • 37
  • 57
  • It says clearly that `Session state can only be used when enableSessionState is set to true`. Did you try that? – Khanh TO Mar 14 '15 at 09:10
  • Yes. Session State is Enabled. but when Session object is null, the Page's Session getter throws this kind of HttpException. it is some problem caused when i removed default microsoft's Session http module and add my own. but i can't understand why session object in application pages is null! – Mehdi Mar 14 '15 at 13:43
  • Did you implement a session state provider: https://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstatestoreproviderbase(v=vs.140).aspx ? How did you implement your Session State Module? – Khanh TO Mar 15 '15 at 02:57
  • yes, i used [RedisSessionStateProvider](https://www.nuget.org/packages/Microsoft.Web.RedisSessionStateProvider/) for my Session data Provider and it works properly. i added **acquireRequest** event of my module to the end of question please take a look at that if it helps. – Mehdi Mar 15 '15 at 08:07
  • The error message says that you have to include a session state module at `\\\\`. Currently you are including it at `\\\\`. See for instance [this question](http://stackoverflow.com/questions/355261/whats-the-difference-between-system-web-and-system-webserver) for more info about these two. – user1429080 Mar 16 '15 at 07:08
  • i use **IIS 8.5** and **Integrated** Application Pool. in this situation httpModules only will load modules from new Web.config schema. i use `APPCMD.EXE MIGRATE CONFIG` command to make web.config compatible with IIS version. so my custom module now load successfully. i think microsoft's exception message is too old and not match to new schema. – Mehdi Mar 16 '15 at 14:11

0 Answers0