/// <summary>
        /// Checks if any items have changed in the Session, and stores the results to Redis
        /// </summary>
        /// <param name="context">The HttpContext of the current web request</param>
        /// <param name="id">The Session Id and Redis key name</param>
        /// <param name="item">The Session properties</param>
        /// <param name="lockId">The object used to exclusively lock the Session (there shouldn't be one)</param>
        /// <param name="newItem">Whether or not the Session was created in this HttpContext</param>
        public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
        {
            try
            {
                string currentRedisHashId = RedisSessionStateStoreProvider.RedisHashIdFromSessionId(
                    new HttpContextWrapper(context),
                    id);

                LocalSharedSessionDictionary    sharedSessDict = new LocalSharedSessionDictionary();
                RedisSessionStateItemCollection redisItems     =
                    sharedSessDict.GetSessionForEndRequest(currentRedisHashId);

                if (redisItems != null)
                {
                    RedisSessionStateStoreProvider.SerializeToRedis(
                        new HttpContextWrapper(context),
                        redisItems,
                        currentRedisHashId,
                        this.SessionTimeout);
                }
            }
            catch (Exception e)
            {
                if (RedisSessionConfig.SessionExceptionLoggingDel != null)
                {
                    RedisSessionConfig.SessionExceptionLoggingDel(e);
                }
            }
        }
        public void OnBeforeTestExecute()
        {
            srsly = new RedisJSONSerializer();

            this.items = new RedisSessionStateItemCollection(
                new Dictionary<string, byte[]> { 
                    { "a", Encoding.UTF8.GetBytes(srsly.SerializeOne("a", "x")) },
                    { "b", Encoding.UTF8.GetBytes(srsly.SerializeOne("b", "y")) },
                    { "c", Encoding.UTF8.GetBytes(srsly.SerializeOne("c", "z")) }
                },
                "fakeCollection");
        }
        /// <summary>
        /// Helper method for serializing objects to Redis
        /// </summary>
        /// <param name="confirmedChangedObjects">keys and values that have definitely changed</param>
        /// <param name="allObjects">keys and values that have been accessed during the current HttpContext</param>
        /// <param name="allObjectsOriginalState">keys and serialized values before we tampered with them</param>
        /// <param name="deletedObjects">keys that were deleted during the current HttpContext</param>
        /// <param name="currentRedisHashId">The current Redis key name</param>
        /// <param name="redisConn">A connection to Redis</param>
        public static void SerializeToRedis(
            HttpContextBase context,
            RedisSessionStateItemCollection redisItems,
            string currentRedisHashId,
            TimeSpan expirationTimeout)
        {
            List <HashEntry>  setItems = new List <HashEntry>();
            List <RedisValue> delItems = new List <RedisValue>();

            RedisConnectionWrapper rConnWrap = RedisSessionStateStoreProvider.RedisConnWrapperFromContext(
                context);

            foreach (KeyValuePair <string, string> changedObj in
                     redisItems.GetChangedObjectsEnumerator())
            {
                if (changedObj.Value != null)
                {
                    setItems.Add(
                        new HashEntry(
                            changedObj.Key,
                            changedObj.Value));
                }
                else
                {
                    delItems.Add(changedObj.Key);
                }
            }

            IDatabase redisConn = rConnWrap.GetConnection();

            redisConn.KeyExpire(
                currentRedisHashId,
                expirationTimeout,
                CommandFlags.FireAndForget);

            if (setItems.Count > 0)
            {
                redisConn.HashSet(
                    currentRedisHashId,
                    setItems.ToArray(),
                    CommandFlags.FireAndForget);
            }
            if (delItems != null && delItems.Count > 0)
            {
                redisConn.HashDelete(
                    currentRedisHashId,
                    delItems.ToArray(),
                    CommandFlags.FireAndForget);
            }
        }
        /// <summary>
        /// Helper method for serializing objects to Redis
        /// </summary>
        /// <param name="confirmedChangedObjects">keys and values that have definitely changed</param>
        /// <param name="allObjects">keys and values that have been accessed during the current HttpContext</param>
        /// <param name="allObjectsOriginalState">keys and serialized values before we tampered with them</param>
        /// <param name="deletedObjects">keys that were deleted during the current HttpContext</param>
        /// <param name="currentRedisHashId">The current Redis key name</param>
        /// <param name="redisConn">A connection to Redis</param>
        public static void SerializeToRedis(HttpContextBase context, RedisSessionStateItemCollection redisItems, string currentRedisHashId, TimeSpan expirationTimeout)
        {
            RedisConnectionWrapper rConnWrap = RedisSessionStateStoreProvider.RedisConnWrapperFromContext(context);

            var setItems = new List <HashEntry>();
            var delItems = new List <RedisValue>();

            // Determine if we are adding or removing keys, separate them into their own lists
            //      note that redisItems.GetChangedObjectsEnumerator contains complex logic
            foreach (KeyValuePair <string, string> changedObj in redisItems.GetChangedObjectsEnumerator())
            {
                if (changedObj.Value != null)
                {
                    setItems.Add(new HashEntry(changedObj.Key, changedObj.Value));
                }
                else
                {
                    delItems.Add(changedObj.Key);
                }
            }

            IDatabase redisConn = rConnWrap.GetConnection(currentRedisHashId);

            if (setItems.Count > 0)
            {
                HashEntry[] writeItems = setItems.ToArray();
                redisConn.HashSet(currentRedisHashId, writeItems, CommandFlags.FireAndForget);

                // call appropriate delegate if set for changing keys
                if (RedisSessionConfig.RedisWriteFieldDel != null)
                {
                    RedisSessionConfig.RedisWriteFieldDel(context, writeItems, currentRedisHashId);
                }
            }
            if (delItems != null && delItems.Count > 0)
            {
                RedisValue[] removeItems = delItems.ToArray();
                redisConn.HashDelete(currentRedisHashId, removeItems, CommandFlags.FireAndForget);

                // call appropriate delegate if set for removing keys
                if (RedisSessionConfig.RedisRemoveFieldDel != null)
                {
                    RedisSessionConfig.RedisRemoveFieldDel(context, removeItems, currentRedisHashId);
                }
            }

            // always refresh the timeout of the session hash
            redisConn.KeyExpire(currentRedisHashId, expirationTimeout, CommandFlags.FireAndForget);
        }
        /// <summary>
        /// Initializes a new instance of the RedisSessionAccessor class, which provides access to a
        ///     local Redis items collection outside of the standard ASP.NET pipeline Session hooks
        /// </summary>
        /// <param name="context">The context of the current request</param>
        public RedisSessionAccessor(HttpContextBase context)
        {
            try
            {
                this.RequestContext = context;
                this.SharedSessions = new LocalSharedSessionDictionary();

                // if we have the session ID
                if (this.RequestContext.Request.Cookies[RedisSessionConfig.SessionHttpCookieName] != null)
                {
                    this.SessionRedisHashKey = RedisSessionStateStoreProvider.RedisHashIdFromSessionId(
                        this.RequestContext,
                        this.RequestContext.Request.Cookies[RedisSessionConfig.SessionHttpCookieName].Value);
                }

                if (!string.IsNullOrEmpty(this.SessionRedisHashKey))
                {
                    RedisSessionStateItemCollection items =
                        this.SharedSessions.GetSessionForBeginRequest(
                            this.SessionRedisHashKey,
                            (string redisKey) =>
                    {
                        return(RedisSessionStateStoreProvider.GetItemFromRedis(
                                   redisKey,
                                   this.RequestContext,
                                   RedisSessionConfig.SessionTimeout));
                    });

                    this.Session = new FakeHttpSessionState(
                        items,
                        this.RequestContext.Request.Cookies[RedisSessionConfig.SessionHttpCookieName].Value);
                }
            }
            catch (Exception exc)
            {
                if (RedisSessionConfig.RedisSessionAccessorExceptionLoggingDel != null)
                {
                    string errMsg = string.Format(
                        "RedisSessionAccessor unable to get Redis session for id: {0}",
                        this.SessionRedisHashKey);

                    RedisSessionConfig.RedisSessionAccessorExceptionLoggingDel(
                        context,
                        errMsg,
                        exc);
                }
            }
        }
Example #6
0
        public void Dispose()
        {
            // record with local shared session storage that we are done with the session so it gets
            //      cleared out sooner
            RedisSessionStateItemCollection items =
                this.SharedSessions.GetSessionForEndRequest(this.SessionRedisHashKey);

            if (items != null)
            {
                RedisSessionStateStoreProvider.SerializeToRedis(
                    this.RequestContext,
                    items,
                    this.SessionRedisHashKey,
                    RedisSessionConfig.SessionTimeout);
            }
        }
        /// <summary>
        /// Gets a Session from Redis, indicating a non-exclusive lock on the Session. Note that GetItemExclusive
        ///     calls this method internally, meaning we do not support locks at all retrieving the Session.
        /// </summary>
        /// <param name="context">The HttpContext of the current request</param>
        /// <param name="id">The Session Id, which is the key name in Redis</param>
        /// <param name="locked">Whether or not the Session is locked to exclusive access for a single request
        ///     thread</param>
        /// <param name="lockAge">The age of the lock</param>
        /// <param name="lockId">The object used to lock the Session</param>
        /// <param name="actions">Whether or not to initialize the Session (never)</param>
        /// <returns>The Session objects wrapped in a SessionStateStoreData object</returns>
        public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
        {
            locked  = false;
            lockAge = new TimeSpan(0);
            lockId  = null;
            actions = SessionStateActions.None;
            if (id == null)
            {
                return(this.CreateNewStoreData(context, Convert.ToInt32(this.SessionTimeout.TotalMinutes)));
            }
            try
            {
                string parsedRedisHashId = RedisSessionStateStoreProvider.RedisHashIdFromSessionId(
                    new HttpContextWrapper(context),
                    id);

                LocalSharedSessionDictionary sharedSessDict = new LocalSharedSessionDictionary();

                RedisSessionStateItemCollection items = sharedSessDict.GetSessionForBeginRequest(
                    parsedRedisHashId,
                    (redisKey) => {
                    return(RedisSessionStateStoreProvider.GetItemFromRedis(
                               redisKey,
                               new HttpContextWrapper(context),
                               this.SessionTimeout));
                });

                return(new SessionStateStoreData(
                           items,
                           SessionStateUtility.GetSessionStaticObjects(context),
                           Convert.ToInt32(this.SessionTimeout.TotalMinutes)));
            }
            catch (Exception e)
            {
                if (RedisSessionConfig.SessionExceptionLoggingDel != null)
                {
                    RedisSessionConfig.SessionExceptionLoggingDel(e);
                }
            }

            return(this.CreateNewStoreData(context, Convert.ToInt32(this.SessionTimeout.TotalMinutes)));
        }
        /// <summary>
        /// Gets a session for a given redis ID, and increments the count of the number of requests
        ///     that have accessed this redis ID
        /// </summary>
        /// <param name="redisHashId">The id of the session in Redis</param>
        /// <param name="getDel">The delegate to run to fetch the session from Redis</param>
        /// <returns>A RedisSessionStateItemCollection for the session</returns>
        public RedisSessionStateItemCollection GetSessionForBeginRequest(
            string redisHashId,
            Func <string, RedisSessionStateItemCollection> getDel)
        {
            SessionAndRefCount sessAndCount = LocalSharedSessionDictionary.localCache.AddOrUpdate(
                redisHashId,
                (redisKey) =>
            {
                RedisSessionStateItemCollection itms = getDel(redisHashId);

                return(new SessionAndRefCount(itms));
            },
                (redisKey, existingItem) => {
                Interlocked.Increment(ref existingItem.RequestReferences);
                existingItem.LastAccess = DateTime.Now;

                return(existingItem);
            });

            return(sessAndCount.Sess);
        }
        /// <summary>
        /// Checks if any items have changed in the Session, and stores the results to Redis
        /// </summary>
        /// <param name="context">The HttpContext of the current web request</param>
        /// <param name="id">The Session Id and Redis key name</param>
        /// <param name="item">The Session properties</param>
        /// <param name="lockId">The object used to exclusively lock the Session (there shouldn't be one)</param>
        /// <param name="newItem">Whether or not the Session was created in this HttpContext</param>
        public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
        {
            try
            {
                string currentRedisHashId = RedisSessionStateStoreProvider.RedisHashIdFromSessionId(
                    new HttpContextWrapper(context),
                    id);

                LocalSharedSessionDictionary    sharedSessDict = new LocalSharedSessionDictionary();
                RedisSessionStateItemCollection redisItems     =
                    sharedSessDict.GetSessionForEndRequest(currentRedisHashId);

                // we were unable to pull it from shared cache, meaning either this is the first request or
                //      something went wrong with the local cache. We still have all the parts needed to write
                //      to redis, however, by looking at SessionStateStoreData passed in from the Session module
                //      and the current hash key provided by the id parameter.
                if (redisItems == null)
                {
                    redisItems = (RedisSessionStateItemCollection)item.Items;
                }

                if (redisItems != null)
                {
                    RedisSessionStateStoreProvider.SerializeToRedis(
                        new HttpContextWrapper(context),
                        redisItems,
                        currentRedisHashId,
                        this.SessionTimeout);
                }
            }
            catch (Exception e)
            {
                if (RedisSessionConfig.SessionExceptionLoggingDel != null)
                {
                    RedisSessionConfig.SessionExceptionLoggingDel(e);
                }
            }
        }
 /// <summary>
 /// Initializes a new instance of the SessionAndRefCount class with a given item collection
 ///     and count of requests using it
 /// </summary>
 /// <param name="itms">The items in a session</param>
 /// <param name="count">The number of requests accessing this session</param>
 public LocalSessionInfo(RedisSessionStateItemCollection itms, int count)
 {
     this.SessionData         = itms;
     this.outstandingRequests = count;
     this.LastAccess          = DateTime.Now;
 }
 /// <summary>
 /// Initializes a new instance of the SessionAndRefCount class with a given item collection
 /// </summary>
 /// <param name="itms">The items in a session</param>
 public LocalSessionInfo(RedisSessionStateItemCollection itms)
     : this(itms, 1)
 {
 }
 /// <summary>
 /// Initializes a new instance of the SessionAndRefCount class with a given item collection
 ///     and count of requests using it
 /// </summary>
 /// <param name="itms">The items in a session</param>
 /// <param name="count">The number of requests accessing this session</param>
 public SessionAndRefCount(RedisSessionStateItemCollection itms, int count)
 {
     this.Sess = itms;
     this.RequestReferences = count;
     this.LastAccess        = DateTime.Now;
 }
 /// <summary>
 /// Initializes a new instance of the SessionAndRefCount class with a given item collection
 /// </summary>
 /// <param name="itms">The items in a session</param>
 public SessionAndRefCount(RedisSessionStateItemCollection itms)
     : this(itms, 1)
 {
 }
 /// <summary>
 /// Initializes a new instance of the SessionAndRefCount class with a given item collection
 ///     and count of requests using it
 /// </summary>
 /// <param name="itms">The items in a session</param>
 /// <param name="count">The number of requests accessing this session</param>
 public SessionAndRefCount(RedisSessionStateItemCollection itms, int count)
 {
     this.Sess = itms;
     this.RequestReferences = count;
     this.LastAccess = DateTime.Now;
 }
 /// <summary>
 /// Initializes a new instance of the SessionAndRefCount class with a given item collection
 /// </summary>
 /// <param name="itms">The items in a session</param>
 public SessionAndRefCount(RedisSessionStateItemCollection itms)
     : this(itms, 1)
 {
 }
 public void OnBeforeTestExecute()
 {
     this.items = new RedisSessionStateItemCollection();
     this.srsly = new RedisJSONSerializer();
 }
        /// <summary>
        /// Helper method for serializing objects to Redis
        /// </summary>
        /// <param name="confirmedChangedObjects">keys and values that have definitely changed</param>
        /// <param name="allObjects">keys and values that have been accessed during the current HttpContext</param>
        /// <param name="allObjectsOriginalState">keys and serialized values before we tampered with them</param>
        /// <param name="deletedObjects">keys that were deleted during the current HttpContext</param>
        /// <param name="currentRedisHashId">The current Redis key name</param>
        /// <param name="redisConn">A connection to Redis</param>
        public static void SerializeToRedis(
            HttpContextBase context,
            RedisSessionStateItemCollection redisItems,
            string currentRedisHashId,
            TimeSpan expirationTimeout)
        {
            List<HashEntry> setItems = new List<HashEntry>();
            List<RedisValue> delItems = new List<RedisValue>();

            RedisConnectionWrapper rConnWrap = RedisSessionStateStoreProvider.RedisConnWrapperFromContext(
                context);

            // Determine if we are adding or removing keys, separate them into their own lists
            //      note that redisItems.GetChangedObjectsEnumerator contains complex logic
            foreach (KeyValuePair<string, string> changedObj in
                redisItems.GetChangedObjectsEnumerator())
            {
                if (changedObj.Value != null)
                {
                    setItems.Add(
                        new HashEntry(
                            changedObj.Key,
                            changedObj.Value));
                }
                else
                {
                    delItems.Add(changedObj.Key);
                }
            }

            IDatabase redisConn = rConnWrap.GetConnection();

            if (setItems.Count > 0)
            {
                HashEntry[] writeItems = setItems.ToArray();
                redisConn.HashSet(
                    currentRedisHashId,
                    writeItems,
                    CommandFlags.FireAndForget);

                // call appropriate delegate if set for changing keys
                if(RedisSessionConfig.RedisWriteFieldDel != null)
                {
                    RedisSessionConfig.RedisWriteFieldDel(
                        context, 
                        writeItems, 
                        currentRedisHashId);
                }
            }
            if (delItems != null && delItems.Count > 0)
            {
                RedisValue[] removeItems = delItems.ToArray();
                redisConn.HashDelete(
                    currentRedisHashId,
                    removeItems,
                    CommandFlags.FireAndForget);

                // call appropriate delegate if set for removing keys
                if(RedisSessionConfig.RedisRemoveFieldDel != null)
                {
                    RedisSessionConfig.RedisRemoveFieldDel(
                        context,
                        removeItems,
                        currentRedisHashId);
                }
            }

            // always refresh the timeout of the session hash
            redisConn.KeyExpire(
                currentRedisHashId,
                expirationTimeout,
                CommandFlags.FireAndForget);
        }
 public FakeHttpSessionState(RedisSessionStateItemCollection items, string sessID)
 {
     this.Items     = items;
     this.sessionID = sessID;
 }