SessionStateModule.cs 13 KB

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