2 // System.Web.SessionState.SesionStateModule
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 // Stefan Görling (stefan@gorling.se)
7 // Jackson Harper (jackson@ximian.com)
8 // Marek Habersack (grendello@gmail.com)
10 // Copyright (C) 2002-2006 Novell, Inc (http://www.novell.com)
11 // (C) 2003 Stefan Görling (http://www.gorling.se)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections.Specialized;
35 using System.Web.Configuration;
36 using System.Web.Caching;
37 using System.Web.Util;
38 using System.Security.Cryptography;
39 using System.Security.Permissions;
40 using System.Threading;
42 namespace System.Web.SessionState
46 public HttpContext Context;
47 public AutoResetEvent AutoEvent;
49 public CallbackState (HttpContext context, AutoResetEvent e)
51 this.Context = context;
56 // CAS - no InheritanceDemand here as the class is sealed
57 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
58 public sealed class SessionStateModule : IHttpModule
60 internal const string HeaderName = "AspFilterSessionId";
61 internal const string CookielessFlagName = "_SessionIDManager_IsCookieLess";
63 static object locker = new object ();
65 internal static string CookieName {
67 config = GetConfig ();
70 return config.CookieName;
75 static private SessionStateSection config {
77 return (SessionStateSection) AppDomain.CurrentDomain.GetData ("SessionStateModule.config");
80 AppDomain.CurrentDomain.SetData ("SessionStateModule.config", value);
84 static private Type handlerType
87 return (Type) AppDomain.CurrentDomain.GetData ("SessionStateModule.handlerType");
90 AppDomain.CurrentDomain.SetData ("SessionStateModule.handlerType", value);
94 static private Type idManagerType
97 return (Type) AppDomain.CurrentDomain.GetData ("SessionStateModule.idManagerType");
100 AppDomain.CurrentDomain.SetData ("SessionStateModule.idManagerType", value);
104 static SessionStateSection config;
105 static Type handlerType;
106 static Type idManagerType;
108 SessionStateStoreProviderBase handler;
109 ISessionIDManager idManager;
114 TimeSpan storeLockAge;
115 object storeLockId = new object();
116 SessionStateActions storeSessionAction;
117 SessionStateStoreData storeData;
122 bool supportSessionIDReissue;
123 bool supportsExpiration;
125 HttpSessionStateContainer container;
128 static TimeSpan executionTimeout;
129 static int executionTimeoutMS;
131 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
132 public SessionStateModule ()
136 public void Dispose ()
142 static SessionStateSection GetConfig ()
148 config = (SessionStateSection) WebConfigurationManager.GetSection ("system.web/sessionState");
149 SessionStateMode handlerMode = config.Mode;
152 if (handlerMode == SessionStateMode.SQLServer || handlerMode == SessionStateMode.StateServer)
153 throw new NotImplementedException("You must use web.xml to specify session state handling");
155 InitTypesFromConfig (config, handlerMode);
156 HttpRuntimeSection runtime = WebConfigurationManager.GetSection ("system.web/httpruntime") as HttpRuntimeSection;
157 if (runtime != null) {
158 executionTimeout = runtime.ExecutionTimeout;
159 executionTimeoutMS = executionTimeout.Milliseconds;
166 static void InitTypesFromConfig (SessionStateSection config, SessionStateMode handlerMode)
168 if (handlerMode == SessionStateMode.StateServer)
169 handlerType = typeof (SessionStateServerHandler);
171 // if (handlerMode == SessionStateMode.SQLServer)
172 // handlerType = typeof (SessionSQLServerHandler);
174 if (handlerMode == SessionStateMode.InProc)
175 handlerType = typeof (SessionInProcHandler);
177 if (handlerMode == SessionStateMode.Custom)
178 handlerType = GetCustomHandlerType (config);
181 idManagerType = Type.GetType (config.SessionIDManagerType, true);
183 idManagerType = typeof (SessionIDManager);
187 static Type GetCustomHandlerType (SessionStateSection config)
192 [EnvironmentPermission (SecurityAction.Assert, Read = "MONO_XSP_STATIC_SESSION")]
193 public void Init (HttpApplication app)
195 SessionStateSection cfg = GetConfig ();
197 if (handlerType == null || idManagerType == null)
198 throw new HttpException ("Cannot initialize the session state module. Missing handler or ID manager types.");
199 app.BeginRequest += new EventHandler (OnBeginRequest);
200 app.AcquireRequestState += new EventHandler (OnAcquireRequestState);
201 app.ReleaseRequestState += new EventHandler (OnReleaseRequestState);
202 app.EndRequest += new EventHandler (OnEndRequest);
204 if (handler == null) {
206 handler = Activator.CreateInstance (handlerType, new object [] {cfg}) as SessionStateStoreProviderBase;
207 handler.Initialize (GetHandlerName (), GetHandlerConfig ());
208 } catch (Exception ex) {
209 throw new HttpException ("Failed to initialize session storage provider.", ex);
213 if (idManager == null) {
215 idManager = Activator.CreateInstance (idManagerType) as ISessionIDManager;
216 idManager.Initialize ();
217 } catch (Exception ex) {
218 throw new HttpException ("Failed to initialize session ID manager.", ex);
223 string GetHandlerName ()
225 switch (config.Mode) {
226 case SessionStateMode.InProc:
227 case SessionStateMode.StateServer:
228 case SessionStateMode.SQLServer:
229 return null; // set by the handler
231 case SessionStateMode.Custom:
232 return "Custom Session State Handler";
235 throw new HttpException ("Unknown session handler mode.");
239 NameValueCollection GetHandlerConfig ()
241 switch (config.Mode) {
242 case SessionStateMode.InProc:
243 case SessionStateMode.StateServer:
244 case SessionStateMode.SQLServer:
245 return new NameValueCollection ();
248 case SessionStateMode.Custom:
249 return new NameValueCollection ();
252 throw new HttpException ("Unknown session handler mode.");
256 internal static bool IsCookieLess (HttpContext context)
258 if (config.Cookieless == HttpCookieMode.UseCookies)
260 if (config.Cookieless == HttpCookieMode.UseUri)
262 object cookieless = context.Items [CookielessFlagName];
263 if (cookieless == null)
265 return (bool)cookieless;
268 void OnBeginRequest (object o, EventArgs args)
270 HttpApplication application = (HttpApplication) o;
271 HttpContext context = application.Context;
272 string base_path = context.Request.BaseVirtualDir;
273 string id = UrlUtils.GetSessionId (base_path);
278 string new_path = UrlUtils.RemoveSessionId (base_path, context.Request.FilePath);
279 context.Request.SetFilePath (new_path);
280 context.Request.SetHeader (HeaderName, id);
281 context.Response.SetAppPathModifier (String.Concat ("(", id, ")"));
284 void OnAcquireRequestState (object o, EventArgs args)
286 Console.WriteLine ("SessionStateModule.OnAcquireRequestState (hash {0})", this.GetHashCode ().ToString ("x"));
287 HttpApplication application = (HttpApplication) o;
288 HttpContext context = application.Context;
290 if (!(context.Handler is IRequiresSessionState)) {
291 Console.WriteLine ("Handler ({0}) does not require session state", context.Handler);
294 isReadOnly = (context.Handler is IReadOnlySessionState);
296 if (idManager != null) {
297 if (idManager.InitializeRequest (context, false, out supportSessionIDReissue))
298 return; // Redirected, will come back here in a while
299 sessionId = idManager.GetSessionID (context);
302 if (handler != null) {
303 handler.InitializeRequest (context);
304 GetStoreData (context);
305 if (storeData == null && !storeLocked) {
307 sessionId = idManager.CreateSessionID (context);
308 Console.WriteLine ("New session ID allocated: {0}", sessionId);
309 bool redirected = false;
310 bool cookieAdded = false;
311 idManager.SaveSessionID (context, sessionId, out redirected, out cookieAdded);
313 if (supportSessionIDReissue)
314 handler.CreateUninitializedItem (context, sessionId, config.Timeout.Minutes);
315 context.Response.End();
318 storeData = handler.CreateNewStoreData (context, config.Timeout.Minutes);
319 } else if (storeData == null && storeLocked) {
320 WaitForStoreUnlock (context);
321 } else if (storeData != null &&
323 storeSessionAction == SessionStateActions.InitializeItem &&
324 IsCookieLess (context)) {
325 storeData = handler.CreateNewStoreData (context, config.Timeout.Minutes);
328 SessionSetup (context, isNew);
332 void OnReleaseRequestState (object o, EventArgs args)
334 Console.WriteLine ("SessionStateModule.OnReleaseRequestState (hash {0})", this.GetHashCode ().ToString ("x"));
335 Console.WriteLine ("\tsessionId == {0}", sessionId);
339 HttpApplication application = (HttpApplication) o;
340 HttpContext context = application.Context;
341 if (!(context.Handler is IRequiresSessionState))
343 Console.WriteLine ("\trequest path == {0}", context.Request.FilePath);
344 Console.WriteLine ("\tHandler ({0}) requires session state", context.Handler);
346 if (!container.IsAbandoned) {
347 Console.WriteLine ("\tnot abandoned");
349 Console.WriteLine ("\tnot read only, storing and releasing");
350 handler.SetAndReleaseItemExclusive (context, sessionId, storeData, storeLockId, false);
352 Console.WriteLine ("\tread only, releasing");
353 handler.ReleaseItemExclusive (context, sessionId, storeLockId);
355 handler.ResetItemTimeout (context, sessionId);
357 handler.ReleaseItemExclusive (context, sessionId, storeLockId);
358 handler.RemoveItem (context, sessionId, storeLockId, storeData);
360 SessionStateUtility.RemoveHttpSessionStateFromContext (context);
361 if (supportsExpiration)
362 SessionStateUtility.RaiseSessionEnd (container, o, args);
365 void OnEndRequest (object o, EventArgs args)
370 HttpApplication application = o as HttpApplication;
371 if (application == null)
374 handler.EndRequest (application.Context);
377 void GetStoreData (HttpContext context)
379 if (sessionId == null)
383 storeData = handler.GetItem (context,
388 out storeSessionAction);
390 storeData = handler.GetItemExclusive (context,
395 out storeSessionAction);
398 void WaitForStoreUnlock (HttpContext context)
400 AutoResetEvent are = new AutoResetEvent (false);
401 TimerCallback tc = new TimerCallback (this.StoreUnlockWaitCallback);
402 CallbackState cs = new CallbackState (context, are);
403 using (Timer timer = new Timer (tc, cs, 500, 500)) {
405 are.WaitOne (executionTimeout, false);
412 void StoreUnlockWaitCallback (object s)
414 CallbackState state = s as CallbackState;
415 GetStoreData (state.Context);
416 if (storeData == null && storeLocked && (storeLockAge > executionTimeout)) {
417 handler.ReleaseItemExclusive (state.Context, sessionId, storeLockId);
418 state.AutoEvent.Set ();
419 } else if (storeData != null && !storeLocked)
420 state.AutoEvent.Set ();
423 void SessionSetup (HttpContext context, bool isNew)
425 if (storeData != null && sessionId != null) {
426 container = new HttpSessionStateContainer (
429 storeData.StaticObjects,
435 SessionStateUtility.AddHttpSessionStateToContext (context, container);
437 supportsExpiration = handler.SetItemExpireCallback (OnSessionExpired);
443 void OnSessionExpired (string id, SessionStateStoreData item)
447 void OnSessionStart ()
450 Start (this, EventArgs.Empty);
453 public event EventHandler Start;
455 // This event is public, but only Session_[On]End in global.asax will be invoked if present.
456 public event EventHandler End;