SessionStateModule.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. //
  2. // System.Web.SessionState.SesionStateModule
  3. //
  4. // Authors:
  5. // Gonzalo Paniagua Javier ([email protected])
  6. // Stefan Görling ([email protected])
  7. // Jackson Harper ([email protected])
  8. // Marek Habersack ([email protected])
  9. //
  10. // Copyright (C) 2002-2006 Novell, Inc (http://www.novell.com)
  11. // (C) 2003 Stefan Görling (http://www.gorling.se)
  12. //
  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:
  20. //
  21. // The above copyright notice and this permission notice shall be
  22. // included in all copies or substantial portions of the Software.
  23. //
  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.
  31. //
  32. #if NET_2_0
  33. using System.Collections.Specialized;
  34. using System.ComponentModel;
  35. using System.Web.Configuration;
  36. using System.Web.Caching;
  37. using System.Web.Util;
  38. using System.Security.Permissions;
  39. using System.Threading;
  40. using System.Configuration;
  41. using System.Diagnostics;
  42. namespace System.Web.SessionState
  43. {
  44. // CAS - no InheritanceDemand here as the class is sealed
  45. [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
  46. public sealed class SessionStateModule : IHttpModule
  47. {
  48. class CallbackState
  49. {
  50. public readonly HttpContext Context;
  51. public readonly AutoResetEvent AutoEvent;
  52. public readonly string SessionId;
  53. public readonly bool IsReadOnly;
  54. public CallbackState (HttpContext context, AutoResetEvent e, string sessionId, bool isReadOnly) {
  55. this.Context = context;
  56. this.AutoEvent = e;
  57. this.SessionId = sessionId;
  58. this.IsReadOnly = isReadOnly;
  59. }
  60. }
  61. internal const string HeaderName = "AspFilterSessionId";
  62. internal const string CookielessFlagName = "_SessionIDManager_IsCookieLess";
  63. static readonly object startEvent = new object ();
  64. static readonly object endEvent = new object ();
  65. SessionStateSection config;
  66. SessionStateStoreProviderBase handler;
  67. ISessionIDManager idManager;
  68. bool supportsExpiration;
  69. HttpApplication app;
  70. // Store state
  71. bool storeLocked;
  72. TimeSpan storeLockAge;
  73. object storeLockId;
  74. SessionStateActions storeSessionAction;
  75. bool storeIsNew;
  76. // Session state
  77. SessionStateStoreData storeData;
  78. HttpSessionStateContainer container;
  79. // config
  80. TimeSpan executionTimeout;
  81. //int executionTimeoutMS;
  82. EventHandlerList events = new EventHandlerList ();
  83. public event EventHandler Start {
  84. add { events.AddHandler (startEvent, value); }
  85. remove { events.RemoveHandler (startEvent, value); }
  86. }
  87. // This event is public, but only Session_[On]End in global.asax will be invoked if present.
  88. public event EventHandler End {
  89. add { events.AddHandler (endEvent, value); }
  90. remove { events.RemoveHandler (endEvent, value); }
  91. }
  92. [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
  93. public SessionStateModule () {
  94. }
  95. public void Dispose () {
  96. app.BeginRequest -= new EventHandler (OnBeginRequest);
  97. app.AcquireRequestState -= new EventHandler (OnAcquireRequestState);
  98. app.ReleaseRequestState -= new EventHandler (OnReleaseRequestState);
  99. app.EndRequest -= new EventHandler (OnEndRequest);
  100. handler.Dispose ();
  101. }
  102. [EnvironmentPermission (SecurityAction.Assert, Read = "MONO_XSP_STATIC_SESSION")]
  103. public void Init (HttpApplication app)
  104. {
  105. 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));
  112. break;
  113. case SessionStateMode.Off:
  114. return;
  115. #if TARGET_J2EE
  116. default:
  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;
  123. #else
  124. case SessionStateMode.InProc:
  125. settings = new ProviderSettings (null, typeof (SessionInProcHandler).AssemblyQualifiedName);
  126. break;
  127. case SessionStateMode.SQLServer:
  128. settings = new ProviderSettings (null, typeof (SessionSQLServerHandler).AssemblyQualifiedName);
  129. break;
  130. case SessionStateMode.StateServer:
  131. settings = new ProviderSettings (null, typeof (SessionStateServerHandler).AssemblyQualifiedName);
  132. break;
  133. default:
  134. throw new NotImplementedException (String.Format ("The mode '{0}' is not implemented.", config.Mode));
  135. #endif
  136. }
  137. handler = (SessionStateStoreProviderBase) ProvidersHelper.InstantiateProvider (settings, typeof (SessionStateStoreProviderBase));
  138. if (String.IsNullOrEmpty(config.SessionIDManagerType)) {
  139. idManager = new SessionIDManager ();
  140. } else {
  141. Type idManagerType = HttpApplication.LoadType (config.SessionIDManagerType, true);
  142. idManager = (ISessionIDManager)Activator.CreateInstance (idManagerType);
  143. }
  144. try {
  145. idManager.Initialize ();
  146. } catch (Exception ex) {
  147. throw new HttpException ("Failed to initialize session ID manager.", ex);
  148. }
  149. supportsExpiration = handler.SetItemExpireCallback (OnSessionExpired);
  150. HttpRuntimeSection runtime = WebConfigurationManager.GetSection ("system.web/httpRuntime") as HttpRuntimeSection;
  151. executionTimeout = runtime.ExecutionTimeout;
  152. //executionTimeoutMS = executionTimeout.Milliseconds;
  153. this.app = app;
  154. app.BeginRequest += new EventHandler (OnBeginRequest);
  155. app.AcquireRequestState += new EventHandler (OnAcquireRequestState);
  156. app.ReleaseRequestState += new EventHandler (OnReleaseRequestState);
  157. app.EndRequest += new EventHandler (OnEndRequest);
  158. }
  159. internal static bool IsCookieLess (HttpContext context, SessionStateSection config) {
  160. if (config.Cookieless == HttpCookieMode.UseCookies)
  161. return false;
  162. if (config.Cookieless == HttpCookieMode.UseUri)
  163. return true;
  164. object cookieless = context.Items [CookielessFlagName];
  165. if (cookieless == null)
  166. return false;
  167. return (bool) cookieless;
  168. }
  169. void OnBeginRequest (object o, EventArgs args)
  170. {
  171. HttpApplication application = (HttpApplication) o;
  172. HttpContext context = application.Context;
  173. string file_path = context.Request.FilePath;
  174. string base_path = VirtualPathUtility.GetDirectory (file_path);
  175. string id = UrlUtils.GetSessionId (base_path);
  176. if (id == null)
  177. return;
  178. string new_path = UrlUtils.RemoveSessionId (base_path, file_path);
  179. context.Request.SetFilePath (new_path);
  180. context.Request.SetHeader (HeaderName, id);
  181. context.Response.SetAppPathModifier (id);
  182. }
  183. void OnAcquireRequestState (object o, EventArgs args) {
  184. Trace.WriteLine ("SessionStateModule.OnAcquireRequestState (hash " + this.GetHashCode ().ToString ("x") + ")");
  185. HttpApplication application = (HttpApplication) o;
  186. HttpContext context = application.Context;
  187. if (!(context.Handler is IRequiresSessionState)) {
  188. Trace.WriteLine ("Handler (" + context.Handler + ") does not require session state");
  189. return;
  190. }
  191. bool isReadOnly = (context.Handler is IReadOnlySessionState);
  192. bool supportSessionIDReissue;
  193. if (idManager.InitializeRequest (context, false, out supportSessionIDReissue))
  194. return; // Redirected, will come back here in a while
  195. string sessionId = idManager.GetSessionID (context);
  196. handler.InitializeRequest (context);
  197. GetStoreData (context, sessionId, isReadOnly);
  198. storeIsNew = false;
  199. if (storeData == null && !storeLocked) {
  200. storeIsNew = true;
  201. sessionId = idManager.CreateSessionID (context);
  202. Trace.WriteLine ("New session ID allocated: " + sessionId);
  203. bool redirected;
  204. bool cookieAdded;
  205. idManager.SaveSessionID (context, sessionId, out redirected, out cookieAdded);
  206. if (redirected) {
  207. if (supportSessionIDReissue)
  208. handler.CreateUninitializedItem (context, sessionId, (int)config.Timeout.TotalMinutes);
  209. context.Response.End ();
  210. return;
  211. }
  212. else
  213. storeData = handler.CreateNewStoreData (context, (int)config.Timeout.TotalMinutes);
  214. }
  215. else if (storeData == null && storeLocked) {
  216. WaitForStoreUnlock (context, sessionId, isReadOnly);
  217. }
  218. else if (storeData != null &&
  219. !storeLocked &&
  220. storeSessionAction == SessionStateActions.InitializeItem &&
  221. IsCookieLess (context, config)) {
  222. storeData = handler.CreateNewStoreData (context, (int)config.Timeout.TotalMinutes);
  223. }
  224. container = CreateContainer (sessionId, storeData, storeIsNew, isReadOnly);
  225. SessionStateUtility.AddHttpSessionStateToContext (app.Context, container);
  226. if (storeIsNew) {
  227. OnSessionStart ();
  228. HttpSessionState hss = app.Session;
  229. if (hss != null)
  230. storeData.Timeout = hss.Timeout;
  231. }
  232. }
  233. void OnReleaseRequestState (object o, EventArgs args) {
  234. Trace.WriteLine ("SessionStateModule.OnReleaseRequestState (hash " + this.GetHashCode ().ToString ("x") + ")");
  235. HttpApplication application = (HttpApplication) o;
  236. HttpContext context = application.Context;
  237. if (!(context.Handler is IRequiresSessionState))
  238. return;
  239. Trace.WriteLine ("\tsessionId == " + container.SessionID);
  240. Trace.WriteLine ("\trequest path == " + context.Request.FilePath);
  241. Trace.WriteLine ("\tHandler (" + context.Handler + ") requires session state");
  242. try {
  243. if (!container.IsAbandoned) {
  244. Trace.WriteLine ("\tnot abandoned");
  245. if (!container.IsReadOnly) {
  246. Trace.WriteLine ("\tnot read only, storing and releasing");
  247. handler.SetAndReleaseItemExclusive (context, container.SessionID, storeData, storeLockId, storeIsNew);
  248. }
  249. else {
  250. Trace.WriteLine ("\tread only, releasing");
  251. handler.ReleaseItemExclusive (context, container.SessionID, storeLockId);
  252. }
  253. handler.ResetItemTimeout (context, container.SessionID);
  254. }
  255. else {
  256. handler.ReleaseItemExclusive (context, container.SessionID, storeLockId);
  257. handler.RemoveItem (context, container.SessionID, storeLockId, storeData);
  258. if (supportsExpiration)
  259. #if TARGET_J2EE
  260. ;
  261. else
  262. #else
  263. // Make sure the expiration handler is not called after we will have raised
  264. // the session end event.
  265. handler.SetItemExpireCallback (null);
  266. #endif
  267. SessionStateUtility.RaiseSessionEnd (container, this, args);
  268. }
  269. SessionStateUtility.RemoveHttpSessionStateFromContext (context);
  270. }
  271. finally {
  272. container = null;
  273. storeData = null;
  274. }
  275. }
  276. void OnEndRequest (object o, EventArgs args) {
  277. if (handler == null)
  278. return;
  279. if (container != null)
  280. OnReleaseRequestState (o, args);
  281. HttpApplication application = o as HttpApplication;
  282. if (application == null)
  283. return;
  284. if (handler != null)
  285. handler.EndRequest (application.Context);
  286. }
  287. void GetStoreData (HttpContext context, string sessionId, bool isReadOnly) {
  288. storeData = (isReadOnly) ?
  289. handler.GetItem (context,
  290. sessionId,
  291. out storeLocked,
  292. out storeLockAge,
  293. out storeLockId,
  294. out storeSessionAction)
  295. :
  296. handler.GetItemExclusive (context,
  297. sessionId,
  298. out storeLocked,
  299. out storeLockAge,
  300. out storeLockId,
  301. out storeSessionAction);
  302. if (storeLockId == null)
  303. storeLockId = 0;
  304. }
  305. void WaitForStoreUnlock (HttpContext context, string sessionId, bool isReadonly) {
  306. AutoResetEvent are = new AutoResetEvent (false);
  307. TimerCallback tc = new TimerCallback (StoreUnlockWaitCallback);
  308. CallbackState cs = new CallbackState (context, are, sessionId, isReadonly);
  309. using (Timer timer = new Timer (tc, cs, 500, 500)) {
  310. try {
  311. are.WaitOne (executionTimeout, false);
  312. }
  313. catch {
  314. storeData = null;
  315. }
  316. }
  317. }
  318. void StoreUnlockWaitCallback (object s) {
  319. CallbackState state = (CallbackState) s;
  320. GetStoreData (state.Context, state.SessionId, state.IsReadOnly);
  321. if (storeData == null && storeLocked && (storeLockAge > executionTimeout)) {
  322. handler.ReleaseItemExclusive (state.Context, state.SessionId, storeLockId);
  323. state.AutoEvent.Set ();
  324. }
  325. else if (storeData != null && !storeLocked)
  326. state.AutoEvent.Set ();
  327. }
  328. HttpSessionStateContainer CreateContainer (string sessionId, SessionStateStoreData data, bool isNew, bool isReadOnly) {
  329. if (data == null)
  330. return new HttpSessionStateContainer (
  331. sessionId, null, null, 0, isNew,
  332. config.Cookieless, config.Mode, isReadOnly);
  333. return new HttpSessionStateContainer (
  334. sessionId,
  335. data.Items,
  336. data.StaticObjects,
  337. data.Timeout,
  338. isNew,
  339. config.Cookieless,
  340. config.Mode,
  341. isReadOnly);
  342. }
  343. void OnSessionExpired (string id, SessionStateStoreData item) {
  344. SessionStateUtility.RaiseSessionEnd (
  345. CreateContainer (id, item, false, true),
  346. this, EventArgs.Empty);
  347. }
  348. void OnSessionStart () {
  349. EventHandler eh = events [startEvent] as EventHandler;
  350. if (eh != null)
  351. eh (this, EventArgs.Empty);
  352. }
  353. }
  354. }
  355. #endif