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;
41 using System.Configuration;
43 namespace System.Web.SessionState
45 // CAS - no InheritanceDemand here as the class is sealed
46 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
47 public sealed class SessionStateModule : IHttpModule
51 public readonly HttpContext Context;
52 public readonly AutoResetEvent AutoEvent;
53 public readonly string SessionId;
54 public readonly bool IsReadOnly;
56 public CallbackState (HttpContext context, AutoResetEvent e, string sessionId, bool isReadOnly) {
57 this.Context = context;
59 this.SessionId = sessionId;
60 this.IsReadOnly = isReadOnly;
64 internal const string HeaderName = "AspFilterSessionId";
65 internal const string CookielessFlagName = "_SessionIDManager_IsCookieLess";
67 SessionStateSection config;
69 SessionStateStoreProviderBase handler;
70 ISessionIDManager idManager;
71 bool supportsExpiration;
77 TimeSpan storeLockAge;
79 SessionStateActions storeSessionAction;
82 SessionStateStoreData storeData;
83 HttpSessionStateContainer container;
86 TimeSpan executionTimeout;
87 //int executionTimeoutMS;
89 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
90 public SessionStateModule () {
93 public void Dispose () {
94 app.BeginRequest -= new EventHandler (OnBeginRequest);
95 app.AcquireRequestState -= new EventHandler (OnAcquireRequestState);
96 app.ReleaseRequestState -= new EventHandler (OnReleaseRequestState);
97 app.EndRequest -= new EventHandler (OnEndRequest);
101 [EnvironmentPermission (SecurityAction.Assert, Read = "MONO_XSP_STATIC_SESSION")]
102 public void Init (HttpApplication app) {
104 config = (SessionStateSection) WebConfigurationManager.GetSection ("system.web/sessionState");
106 ProviderSettings settings;
107 switch (config.Mode) {
108 case SessionStateMode.Custom:
109 settings = config.Providers [config.CustomProvider];
110 if (settings == null)
111 throw new HttpException (String.Format ("Cannot find '{0}' provider.", config.CustomProvider));
113 case SessionStateMode.Off:
117 config = new SessionStateSection ();
118 config.Mode = SessionStateMode.Custom;
119 config.CustomProvider = "ServletSessionStateStore";
120 config.SessionIDManagerType = "Mainsoft.Web.SessionState.ServletSessionIDManager";
121 config.Providers.Add (new ProviderSettings ("ServletSessionStateStore", "Mainsoft.Web.SessionState.ServletSessionStateStoreProvider"));
122 goto case SessionStateMode.Custom;
124 case SessionStateMode.InProc:
125 settings = new ProviderSettings (null, typeof (SessionInProcHandler).AssemblyQualifiedName);
127 case SessionStateMode.SQLServer:
128 //settings = new ProviderSettings (null, typeof (SessionInProcHandler).AssemblyQualifiedName);
131 throw new NotImplementedException (String.Format ("The mode '{0}' is not implemented.", config.Mode));
132 case SessionStateMode.StateServer:
133 settings = new ProviderSettings (null, typeof (SessionStateServerHandler).AssemblyQualifiedName);
138 handler = (SessionStateStoreProviderBase) ProvidersHelper.InstantiateProvider (settings, typeof (SessionStateStoreProviderBase));
140 if (String.IsNullOrEmpty(config.SessionIDManagerType)) {
141 idManager = new SessionIDManager ();
143 Type idManagerType = HttpApplication.LoadType (config.SessionIDManagerType, true);
144 idManager = (ISessionIDManager)Activator.CreateInstance (idManagerType);
148 idManager.Initialize ();
149 } catch (Exception ex) {
150 throw new HttpException ("Failed to initialize session ID manager.", ex);
153 supportsExpiration = handler.SetItemExpireCallback (OnSessionExpired);
154 HttpRuntimeSection runtime = WebConfigurationManager.GetSection ("system.web/httpRuntime") as HttpRuntimeSection;
155 executionTimeout = runtime.ExecutionTimeout;
156 //executionTimeoutMS = executionTimeout.Milliseconds;
160 app.BeginRequest += new EventHandler (OnBeginRequest);
161 app.AcquireRequestState += new EventHandler (OnAcquireRequestState);
162 app.ReleaseRequestState += new EventHandler (OnReleaseRequestState);
163 app.EndRequest += new EventHandler (OnEndRequest);
166 internal static bool IsCookieLess (HttpContext context, SessionStateSection config) {
167 if (config.Cookieless == HttpCookieMode.UseCookies)
169 if (config.Cookieless == HttpCookieMode.UseUri)
171 object cookieless = context.Items [CookielessFlagName];
172 if (cookieless == null)
174 return (bool) cookieless;
177 void OnBeginRequest (object o, EventArgs args) {
178 HttpApplication application = (HttpApplication) o;
179 HttpContext context = application.Context;
180 string base_path = context.Request.BaseVirtualDir;
181 string id = UrlUtils.GetSessionId (base_path);
186 string new_path = UrlUtils.RemoveSessionId (base_path, context.Request.FilePath);
187 context.Request.SetFilePath (new_path);
188 context.Request.SetHeader (HeaderName, id);
189 context.Response.SetAppPathModifier (String.Concat ("(", id, ")"));
192 void OnAcquireRequestState (object o, EventArgs args) {
194 Console.WriteLine ("SessionStateModule.OnAcquireRequestState (hash {0})", this.GetHashCode ().ToString ("x"));
196 HttpApplication application = (HttpApplication) o;
197 HttpContext context = application.Context;
199 if (!(context.Handler is IRequiresSessionState)) {
201 Console.WriteLine ("Handler ({0}) does not require session state", context.Handler);
205 bool isReadOnly = (context.Handler is IReadOnlySessionState);
207 bool supportSessionIDReissue;
208 if (idManager.InitializeRequest (context, false, out supportSessionIDReissue))
209 return; // Redirected, will come back here in a while
210 string sessionId = idManager.GetSessionID (context);
213 handler.InitializeRequest (context);
215 GetStoreData (context, sessionId, isReadOnly);
218 if (storeData == null && !storeLocked) {
220 sessionId = idManager.CreateSessionID (context);
222 Console.WriteLine ("New session ID allocated: {0}", sessionId);
226 idManager.SaveSessionID (context, sessionId, out redirected, out cookieAdded);
228 if (supportSessionIDReissue)
229 handler.CreateUninitializedItem (context, sessionId, config.Timeout.Minutes);
230 context.Response.End ();
234 storeData = handler.CreateNewStoreData (context, config.Timeout.Minutes);
236 else if (storeData == null && storeLocked) {
237 WaitForStoreUnlock (context, sessionId, isReadOnly);
239 else if (storeData != null &&
241 storeSessionAction == SessionStateActions.InitializeItem &&
242 IsCookieLess (context, config)) {
243 storeData = handler.CreateNewStoreData (context, config.Timeout.Minutes);
246 container = CreateContainer (sessionId, storeData, isNew, isReadOnly);
247 SessionStateUtility.AddHttpSessionStateToContext (app.Context, container);
252 void OnReleaseRequestState (object o, EventArgs args) {
255 Console.WriteLine ("SessionStateModule.OnReleaseRequestState (hash {0})", this.GetHashCode ().ToString ("x"));
258 HttpApplication application = (HttpApplication) o;
259 HttpContext context = application.Context;
260 if (!(context.Handler is IRequiresSessionState))
264 Console.WriteLine ("\tsessionId == {0}", container.SessionID);
265 Console.WriteLine ("\trequest path == {0}", context.Request.FilePath);
266 Console.WriteLine ("\tHandler ({0}) requires session state", context.Handler);
269 if (!container.IsAbandoned) {
271 Console.WriteLine ("\tnot abandoned");
273 if (!container.IsReadOnly) {
275 Console.WriteLine ("\tnot read only, storing and releasing");
277 handler.SetAndReleaseItemExclusive (context, container.SessionID, storeData, storeLockId, false);
281 Console.WriteLine ("\tread only, releasing");
283 handler.ReleaseItemExclusive (context, container.SessionID, storeLockId);
285 handler.ResetItemTimeout (context, container.SessionID);
288 handler.ReleaseItemExclusive (context, container.SessionID, storeLockId);
289 handler.RemoveItem (context, container.SessionID, storeLockId, storeData);
290 if (supportsExpiration)
291 // Make sure the expiration handler is not called after we will have raised
292 // the session end event.
293 handler.SetItemExpireCallback (null);
294 SessionStateUtility.RaiseSessionEnd (container, this, args);
296 SessionStateUtility.RemoveHttpSessionStateFromContext (context);
304 void OnEndRequest (object o, EventArgs args) {
308 if (container != null)
309 OnReleaseRequestState (o, args);
311 HttpApplication application = o as HttpApplication;
312 if (application == null)
315 handler.EndRequest (application.Context);
318 void GetStoreData (HttpContext context, string sessionId, bool isReadOnly) {
319 storeData = (isReadOnly) ?
320 handler.GetItem (context,
325 out storeSessionAction)
327 handler.GetItemExclusive (context,
332 out storeSessionAction);
335 void WaitForStoreUnlock (HttpContext context, string sessionId, bool isReadonly) {
336 AutoResetEvent are = new AutoResetEvent (false);
337 TimerCallback tc = new TimerCallback (StoreUnlockWaitCallback);
338 CallbackState cs = new CallbackState (context, are, sessionId, isReadonly);
339 using (Timer timer = new Timer (tc, cs, 500, 500)) {
341 are.WaitOne (executionTimeout, false);
349 void StoreUnlockWaitCallback (object s) {
350 CallbackState state = (CallbackState) s;
352 GetStoreData (state.Context, state.SessionId, state.IsReadOnly);
354 if (storeData == null && storeLocked && (storeLockAge > executionTimeout)) {
355 handler.ReleaseItemExclusive (state.Context, state.SessionId, storeLockId);
356 state.AutoEvent.Set ();
358 else if (storeData != null && !storeLocked)
359 state.AutoEvent.Set ();
362 HttpSessionStateContainer CreateContainer (string sessionId, SessionStateStoreData data, bool isNew, bool isReadOnly) {
364 return new HttpSessionStateContainer (
365 sessionId, null, null, 0, isNew,
366 config.Cookieless, config.Mode, isReadOnly);
368 return new HttpSessionStateContainer (
379 void OnSessionExpired (string id, SessionStateStoreData item) {
380 SessionStateUtility.RaiseSessionEnd (
381 CreateContainer (id, item, false, true),
382 this, EventArgs.Empty);
385 void OnSessionStart () {
387 Start (this, EventArgs.Empty);
390 public event EventHandler Start;
392 // This event is public, but only Session_[On]End in global.asax will be invoked if present.
393 public event EventHandler End;