2 // System.Web.SessionState.SessionInProcHandler
5 // Marek Habersack <grendello@gmail.com>
7 // (C) 2006 Marek Habersack
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
34 using System.Collections.Specialized;
35 using System.Configuration.Provider;
36 using System.Web.Caching;
37 using System.Web.Configuration;
38 using System.Threading;
40 namespace System.Web.SessionState
42 internal sealed class InProcSessionItem
45 public bool cookieless;
46 public ISessionStateItemCollection items;
47 public DateTime lockedTime;
48 public DateTime expiresAt;
49 public ReaderWriterLock rwlock;
52 public bool resettingTimeout;
53 public HttpStaticObjectsCollection staticItems;
55 internal InProcSessionItem ()
58 this.cookieless = false;
60 this.staticItems = null;
61 this.lockedTime = DateTime.MinValue;
62 this.expiresAt = DateTime.MinValue;
63 this.rwlock = new ReaderWriterLock ();
64 this.lockId = Int32.MinValue;
66 this.resettingTimeout = false;
70 internal class SessionInProcHandler : SessionStateStoreProviderBase
72 private const string CachePrefix = "@@@InProc@";
73 private const Int32 lockAcquireTimeout = 30000;
75 CacheItemRemovedCallback removedCB;
76 //NameValueCollection privateConfig;
77 SessionStateItemExpireCallback expireCallback;
78 HttpStaticObjectsCollection staticObjects;
80 public override SessionStateStoreData CreateNewStoreData (HttpContext context, int timeout)
82 return new SessionStateStoreData (new SessionStateItemCollection (),
83 staticObjects, timeout);
86 void InsertSessionItem (InProcSessionItem item, int timeout, string id)
88 if (item == null || String.IsNullOrEmpty (id))
91 HttpRuntime.InternalCache.Insert (id,
94 Cache.NoAbsoluteExpiration,
95 TimeSpan.FromMinutes (timeout),
96 CacheItemPriority.AboveNormal,
100 public override void CreateUninitializedItem (HttpContext context, string id, int timeout)
102 EnsureGoodId (id, true);
103 InProcSessionItem item = new InProcSessionItem ();
104 item.expiresAt = DateTime.UtcNow.AddMinutes (timeout);
105 item.timeout = timeout;
106 InsertSessionItem (item, timeout, CachePrefix + id);
109 public override void Dispose ()
113 public override void EndRequest (HttpContext context)
117 SessionStateStoreData GetItemInternal (HttpContext context,
120 out TimeSpan lockAge,
122 out SessionStateActions actions,
126 lockAge = TimeSpan.MinValue;
127 lockId = Int32.MinValue;
128 actions = SessionStateActions.None;
133 Cache cache = HttpRuntime.InternalCache;
134 string CacheId = CachePrefix + id;
135 InProcSessionItem item = cache [CacheId] as InProcSessionItem;
141 item.rwlock.AcquireReaderLock (lockAcquireTimeout);
144 lockAge = DateTime.UtcNow.Subtract (item.lockedTime);
145 lockId = item.lockId;
148 item.rwlock.ReleaseReaderLock ();
150 item.rwlock.AcquireWriterLock (lockAcquireTimeout);
152 item.lockedTime = DateTime.UtcNow;
154 lockId = item.lockId;
156 if (item.items == null) {
157 actions = SessionStateActions.InitializeItem;
158 item.items = new SessionStateItemCollection ();
160 if (item.staticItems == null)
161 item.staticItems = staticObjects;
163 return new SessionStateStoreData (item.items,
167 // we want such errors to be passed to the application.
170 if (item.rwlock.IsReaderLockHeld)
171 item.rwlock.ReleaseReaderLock ();
172 if (item.rwlock.IsWriterLockHeld)
173 item.rwlock.ReleaseWriterLock ();
177 public override SessionStateStoreData GetItem (HttpContext context,
180 out TimeSpan lockAge,
182 out SessionStateActions actions)
184 EnsureGoodId (id, false);
185 return GetItemInternal (context, id, out locked, out lockAge, out lockId, out actions, false);
188 public override SessionStateStoreData GetItemExclusive (HttpContext context,
191 out TimeSpan lockAge,
193 out SessionStateActions actions)
195 EnsureGoodId (id, false);
196 return GetItemInternal (context, id, out locked, out lockAge, out lockId, out actions, true);
199 public override void Initialize (string name, NameValueCollection config)
201 if (String.IsNullOrEmpty (name))
202 name = "Session InProc handler";
203 removedCB = new CacheItemRemovedCallback (OnSessionRemoved);
204 //privateConfig = config;
205 base.Initialize (name, config);
208 public override void InitializeRequest (HttpContext context)
210 staticObjects = HttpApplicationFactory.ApplicationState.SessionObjects.Clone ();
213 public override void ReleaseItemExclusive (HttpContext context,
217 EnsureGoodId (id, true);
218 string CacheId = CachePrefix + id;
219 InProcSessionItem item = HttpRuntime.InternalCache [CacheId] as InProcSessionItem;
221 if (item == null || lockId == null || lockId.GetType() != typeof(Int32) || item.lockId != (Int32)lockId)
225 item.rwlock.AcquireWriterLock (lockAcquireTimeout);
230 if (item.rwlock.IsWriterLockHeld)
231 item.rwlock.ReleaseWriterLock ();
235 public override void RemoveItem (HttpContext context,
238 SessionStateStoreData item)
240 EnsureGoodId (id, true);
241 string CacheId = CachePrefix + id;
242 Cache cache = HttpRuntime.InternalCache;
243 InProcSessionItem inProcItem = cache [CacheId] as InProcSessionItem;
245 if (inProcItem == null || lockId == null || lockId.GetType() != typeof(Int32) || inProcItem.lockId != (Int32)lockId)
249 inProcItem.rwlock.AcquireWriterLock (lockAcquireTimeout);
250 cache.Remove (CacheId);
254 if (inProcItem.rwlock.IsWriterLockHeld)
255 inProcItem.rwlock.ReleaseWriterLock ();
259 public override void ResetItemTimeout (HttpContext context, string id)
261 EnsureGoodId (id, true);
262 string CacheId = CachePrefix + id;
263 Cache cache = HttpRuntime.InternalCache;
264 InProcSessionItem item = cache [CacheId] as InProcSessionItem;
270 item.rwlock.AcquireWriterLock (lockAcquireTimeout);
271 item.resettingTimeout = true;
272 cache.Remove (CacheId);
273 InsertSessionItem (item, item.timeout, CacheId);
277 if (item.rwlock.IsWriterLockHeld)
278 item.rwlock.ReleaseWriterLock ();
282 /* In certain situations the 'item' parameter passed to SetAndReleaseItemExclusive
283 may be null. The issue was reported in bug #333898, but the reporter cannot
284 provide a test case that triggers the issue. Added work around the problem
285 in the way that should have the least impact on the rest of the code. If 'item'
286 is null, then the new session item is created without the items and staticItems
287 collections - they will be initialized to defaults when retrieving the session
288 item. This is not a correct fix, but since there is no test case this is the best
289 what can be done right now.
291 public override void SetAndReleaseItemExclusive (HttpContext context,
293 SessionStateStoreData item,
297 EnsureGoodId (id, true);
298 string CacheId = CachePrefix + id;
299 Cache cache = HttpRuntime.InternalCache;
300 InProcSessionItem inProcItem = cache [CacheId] as InProcSessionItem;
301 ISessionStateItemCollection itemItems = null;
302 int itemTimeout = 20;
303 HttpStaticObjectsCollection itemStaticItems = null;
306 itemItems = item.Items;
307 itemTimeout = item.Timeout;
308 itemStaticItems = item.StaticObjects;
311 if (newItem || inProcItem == null) {
312 inProcItem = new InProcSessionItem ();
313 inProcItem.timeout = itemTimeout;
314 inProcItem.expiresAt = DateTime.UtcNow.AddMinutes (itemTimeout);
315 if (lockId.GetType() == typeof(Int32))
316 inProcItem.lockId = (Int32)lockId;
318 if (lockId == null || lockId.GetType() != typeof(Int32) || inProcItem.lockId != (Int32)lockId)
320 inProcItem.resettingTimeout = true;
321 cache.Remove (CacheId);
325 inProcItem.rwlock.AcquireWriterLock (lockAcquireTimeout);
326 inProcItem.locked = false;
327 inProcItem.items = itemItems;
328 inProcItem.staticItems = itemStaticItems;
329 InsertSessionItem (inProcItem, itemTimeout, CacheId);
333 if (inProcItem.rwlock.IsWriterLockHeld)
334 inProcItem.rwlock.ReleaseWriterLock ();
338 public override bool SetItemExpireCallback (SessionStateItemExpireCallback expireCallback)
340 this.expireCallback = expireCallback;
344 void EnsureGoodId (string id, bool throwOnNull)
348 throw new HttpException ("Session ID is invalid");
352 if (id.Length > SessionIDManager.SessionIDMaxLength)
353 throw new HttpException ("Session ID too long");
356 void OnSessionRemoved (string key, object value, CacheItemRemovedReason reason)
358 if (expireCallback != null) {
359 if (value is SessionStateStoreData)
360 expireCallback (key, (SessionStateStoreData)value);
361 else if (value is InProcSessionItem) {
362 InProcSessionItem item = (InProcSessionItem)value;
363 if (item.resettingTimeout) {
364 item.resettingTimeout = false;
369 new SessionStateStoreData (
374 expireCallback (key, null);