SessionStateModule.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  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. namespace System.Web.SessionState
  41. {
  42. class CallbackState
  43. {
  44. public HttpContext Context;
  45. public AutoResetEvent AutoEvent;
  46. public CallbackState (HttpContext context, AutoResetEvent e)
  47. {
  48. this.Context = context;
  49. this.AutoEvent = e;
  50. }
  51. }
  52. // CAS - no InheritanceDemand here as the class is sealed
  53. [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
  54. public sealed class SessionStateModule : IHttpModule
  55. {
  56. internal const string HeaderName = "AspFilterSessionId";
  57. internal const string CookielessFlagName = "_SessionIDManager_IsCookieLess";
  58. static object locker = new object ();
  59. internal static string CookieName {
  60. get {
  61. config = GetConfig ();
  62. if (config == null)
  63. return null;
  64. return config.CookieName;
  65. }
  66. }
  67. #if TARGET_J2EE
  68. static private SessionStateSection config {
  69. get {
  70. return (SessionStateSection) AppDomain.CurrentDomain.GetData ("SessionStateModule.config");
  71. }
  72. set {
  73. AppDomain.CurrentDomain.SetData ("SessionStateModule.config", value);
  74. }
  75. }
  76. static private Type handlerType
  77. {
  78. get {
  79. return (Type) AppDomain.CurrentDomain.GetData ("SessionStateModule.handlerType");
  80. }
  81. set {
  82. AppDomain.CurrentDomain.SetData ("SessionStateModule.handlerType", value);
  83. }
  84. }
  85. static private Type idManagerType
  86. {
  87. get {
  88. return (Type) AppDomain.CurrentDomain.GetData ("SessionStateModule.idManagerType");
  89. }
  90. set {
  91. AppDomain.CurrentDomain.SetData ("SessionStateModule.idManagerType", value);
  92. }
  93. }
  94. #else
  95. static SessionStateSection config;
  96. static Type handlerType;
  97. static Type idManagerType;
  98. #endif
  99. SessionStateStoreProviderBase handler;
  100. ISessionIDManager idManager;
  101. HttpApplication app;
  102. // Store state
  103. bool storeLocked;
  104. TimeSpan storeLockAge;
  105. object storeLockId = new object();
  106. SessionStateActions storeSessionAction;
  107. SessionStateStoreData storeData;
  108. // Session state
  109. bool isReadOnly;
  110. bool isNew;
  111. bool supportSessionIDReissue;
  112. bool supportsExpiration;
  113. string sessionId;
  114. HttpSessionStateContainer container;
  115. // config
  116. static TimeSpan executionTimeout;
  117. static int executionTimeoutMS;
  118. [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
  119. public SessionStateModule ()
  120. {
  121. }
  122. public void Dispose ()
  123. {
  124. if (handler!=null)
  125. handler.Dispose();
  126. }
  127. static SessionStateSection GetConfig ()
  128. {
  129. lock (locker) {
  130. if (config != null)
  131. return config;
  132. config = (SessionStateSection) WebConfigurationManager.GetSection ("system.web/sessionState");
  133. SessionStateMode handlerMode = config.Mode;
  134. #if TARGET_J2EE
  135. if (handlerMode == SessionStateMode.SQLServer || handlerMode == SessionStateMode.StateServer)
  136. throw new NotImplementedException("You must use web.xml to specify session state handling");
  137. #endif
  138. InitTypesFromConfig (config, handlerMode);
  139. HttpRuntimeSection runtime = WebConfigurationManager.GetSection ("system.web/httpruntime") as HttpRuntimeSection;
  140. if (runtime != null) {
  141. executionTimeout = runtime.ExecutionTimeout;
  142. executionTimeoutMS = executionTimeout.Milliseconds;
  143. }
  144. return config;
  145. }
  146. }
  147. static void InitTypesFromConfig (SessionStateSection config, SessionStateMode handlerMode)
  148. {
  149. if (handlerMode == SessionStateMode.StateServer)
  150. handlerType = typeof (SessionStateServerHandler);
  151. // if (handlerMode == SessionStateMode.SQLServer)
  152. // handlerType = typeof (SessionSQLServerHandler);
  153. if (handlerMode == SessionStateMode.InProc)
  154. handlerType = typeof (SessionInProcHandler);
  155. if (handlerMode == SessionStateMode.Custom)
  156. handlerType = GetCustomHandlerType (config);
  157. try {
  158. idManagerType = Type.GetType (config.SessionIDManagerType, true);
  159. } catch {
  160. idManagerType = typeof (SessionIDManager);
  161. }
  162. }
  163. static Type GetCustomHandlerType (SessionStateSection config)
  164. {
  165. return null;
  166. }
  167. [EnvironmentPermission (SecurityAction.Assert, Read = "MONO_XSP_STATIC_SESSION")]
  168. public void Init (HttpApplication app)
  169. {
  170. SessionStateSection cfg = GetConfig ();
  171. this.app = app;
  172. if (handlerType == null || idManagerType == null)
  173. throw new HttpException ("Cannot initialize the session state module. Missing handler or ID manager types.");
  174. app.BeginRequest += new EventHandler (OnBeginRequest);
  175. app.AcquireRequestState += new EventHandler (OnAcquireRequestState);
  176. app.ReleaseRequestState += new EventHandler (OnReleaseRequestState);
  177. app.EndRequest += new EventHandler (OnEndRequest);
  178. if (handler == null) {
  179. try {
  180. handler = Activator.CreateInstance (handlerType, new object [] {cfg}) as SessionStateStoreProviderBase;
  181. handler.Initialize (GetHandlerName (), GetHandlerConfig ());
  182. } catch (Exception ex) {
  183. throw new HttpException ("Failed to initialize session storage provider.", ex);
  184. }
  185. }
  186. if (idManager == null) {
  187. try {
  188. idManager = Activator.CreateInstance (idManagerType) as ISessionIDManager;
  189. idManager.Initialize ();
  190. } catch (Exception ex) {
  191. throw new HttpException ("Failed to initialize session ID manager.", ex);
  192. }
  193. }
  194. }
  195. string GetHandlerName ()
  196. {
  197. switch (config.Mode) {
  198. case SessionStateMode.InProc:
  199. case SessionStateMode.StateServer:
  200. case SessionStateMode.SQLServer:
  201. return null; // set by the handler
  202. case SessionStateMode.Custom:
  203. return "Custom Session State Handler";
  204. default:
  205. throw new HttpException ("Unknown session handler mode.");
  206. }
  207. }
  208. NameValueCollection GetHandlerConfig ()
  209. {
  210. switch (config.Mode) {
  211. case SessionStateMode.InProc:
  212. case SessionStateMode.StateServer:
  213. case SessionStateMode.SQLServer:
  214. return new NameValueCollection ();
  215. // TODO: implement
  216. case SessionStateMode.Custom:
  217. return new NameValueCollection ();
  218. default:
  219. throw new HttpException ("Unknown session handler mode.");
  220. }
  221. }
  222. internal static bool IsCookieLess (HttpContext context)
  223. {
  224. if (config.Cookieless == HttpCookieMode.UseCookies)
  225. return false;
  226. if (config.Cookieless == HttpCookieMode.UseUri)
  227. return true;
  228. object cookieless = context.Items [CookielessFlagName];
  229. if (cookieless == null)
  230. return false;
  231. return (bool)cookieless;
  232. }
  233. void OnBeginRequest (object o, EventArgs args)
  234. {
  235. HttpApplication application = (HttpApplication) o;
  236. HttpContext context = application.Context;
  237. string base_path = context.Request.BaseVirtualDir;
  238. string id = UrlUtils.GetSessionId (base_path);
  239. if (id == null)
  240. return;
  241. string new_path = UrlUtils.RemoveSessionId (base_path, context.Request.FilePath);
  242. context.Request.SetFilePath (new_path);
  243. context.Request.SetHeader (HeaderName, id);
  244. context.Response.SetAppPathModifier (String.Concat ("(", id, ")"));
  245. }
  246. void OnAcquireRequestState (object o, EventArgs args)
  247. {
  248. Console.WriteLine ("SessionStateModule.OnAcquireRequestState (hash {0})", this.GetHashCode ().ToString ("x"));
  249. HttpApplication application = (HttpApplication) o;
  250. HttpContext context = application.Context;
  251. if (!(context.Handler is IRequiresSessionState)) {
  252. Console.WriteLine ("Handler ({0}) does not require session state", context.Handler);
  253. return;
  254. }
  255. isReadOnly = (context.Handler is IReadOnlySessionState);
  256. if (idManager != null) {
  257. if (idManager.InitializeRequest (context, false, out supportSessionIDReissue))
  258. return; // Redirected, will come back here in a while
  259. sessionId = idManager.GetSessionID (context);
  260. }
  261. if (handler != null) {
  262. handler.InitializeRequest (context);
  263. GetStoreData (context);
  264. if (storeData == null && !storeLocked) {
  265. isNew = true;
  266. sessionId = idManager.CreateSessionID (context);
  267. Console.WriteLine ("New session ID allocated: {0}", sessionId);
  268. bool redirected = false;
  269. bool cookieAdded = false;
  270. idManager.SaveSessionID (context, sessionId, out redirected, out cookieAdded);
  271. if (redirected) {
  272. if (supportSessionIDReissue)
  273. handler.CreateUninitializedItem (context, sessionId, config.Timeout.Minutes);
  274. context.Response.End();
  275. return;
  276. } else
  277. storeData = handler.CreateNewStoreData (context, config.Timeout.Minutes);
  278. } else if (storeData == null && storeLocked) {
  279. WaitForStoreUnlock (context);
  280. } else if (storeData != null &&
  281. !storeLocked &&
  282. storeSessionAction == SessionStateActions.InitializeItem &&
  283. IsCookieLess (context)) {
  284. storeData = handler.CreateNewStoreData (context, config.Timeout.Minutes);
  285. }
  286. SessionSetup (context, isNew);
  287. }
  288. }
  289. void OnReleaseRequestState (object o, EventArgs args)
  290. {
  291. Console.WriteLine ("SessionStateModule.OnReleaseRequestState (hash {0})", this.GetHashCode ().ToString ("x"));
  292. Console.WriteLine ("\tsessionId == {0}", sessionId);
  293. if (handler == null)
  294. return;
  295. HttpApplication application = (HttpApplication) o;
  296. HttpContext context = application.Context;
  297. if (!(context.Handler is IRequiresSessionState))
  298. return;
  299. Console.WriteLine ("\trequest path == {0}", context.Request.FilePath);
  300. Console.WriteLine ("\tHandler ({0}) requires session state", context.Handler);
  301. if (!container.IsAbandoned) {
  302. Console.WriteLine ("\tnot abandoned");
  303. if (!isReadOnly) {
  304. Console.WriteLine ("\tnot read only, storing and releasing");
  305. handler.SetAndReleaseItemExclusive (context, sessionId, storeData, storeLockId, false);
  306. } else {
  307. Console.WriteLine ("\tread only, releasing");
  308. handler.ReleaseItemExclusive (context, sessionId, storeLockId);
  309. }
  310. handler.ResetItemTimeout (context, sessionId);
  311. } else {
  312. handler.ReleaseItemExclusive (context, sessionId, storeLockId);
  313. handler.RemoveItem (context, sessionId, storeLockId, storeData);
  314. }
  315. SessionStateUtility.RemoveHttpSessionStateFromContext (context);
  316. if (supportsExpiration)
  317. SessionStateUtility.RaiseSessionEnd (container, o, args);
  318. }
  319. void OnEndRequest (object o, EventArgs args)
  320. {
  321. if (handler == null)
  322. return;
  323. HttpApplication application = o as HttpApplication;
  324. if (application == null)
  325. return;
  326. if (handler != null)
  327. handler.EndRequest (application.Context);
  328. }
  329. void GetStoreData (HttpContext context)
  330. {
  331. if (sessionId == null)
  332. return;
  333. if (isReadOnly)
  334. storeData = handler.GetItem (context,
  335. sessionId,
  336. out storeLocked,
  337. out storeLockAge,
  338. out storeLockId,
  339. out storeSessionAction);
  340. else
  341. storeData = handler.GetItemExclusive (context,
  342. sessionId,
  343. out storeLocked,
  344. out storeLockAge,
  345. out storeLockId,
  346. out storeSessionAction);
  347. }
  348. void WaitForStoreUnlock (HttpContext context)
  349. {
  350. AutoResetEvent are = new AutoResetEvent (false);
  351. TimerCallback tc = new TimerCallback (this.StoreUnlockWaitCallback);
  352. CallbackState cs = new CallbackState (context, are);
  353. using (Timer timer = new Timer (tc, cs, 500, 500)) {
  354. try {
  355. are.WaitOne (executionTimeout, false);
  356. } catch {
  357. storeData = null;
  358. }
  359. }
  360. }
  361. void StoreUnlockWaitCallback (object s)
  362. {
  363. CallbackState state = s as CallbackState;
  364. GetStoreData (state.Context);
  365. if (storeData == null && storeLocked && (storeLockAge > executionTimeout)) {
  366. handler.ReleaseItemExclusive (state.Context, sessionId, storeLockId);
  367. state.AutoEvent.Set ();
  368. } else if (storeData != null && !storeLocked)
  369. state.AutoEvent.Set ();
  370. }
  371. void SessionSetup (HttpContext context, bool isNew)
  372. {
  373. if (storeData != null && sessionId != null) {
  374. container = new HttpSessionStateContainer (
  375. sessionId,
  376. storeData.Items,
  377. storeData.StaticObjects,
  378. storeData.Timeout,
  379. isNew,
  380. config.Cookieless,
  381. config.Mode,
  382. isReadOnly);
  383. SessionStateUtility.AddHttpSessionStateToContext (context, container);
  384. if (isNew) {
  385. supportsExpiration = handler.SetItemExpireCallback (OnSessionExpired);
  386. OnSessionStart ();
  387. }
  388. }
  389. }
  390. void OnSessionExpired (string id, SessionStateStoreData item)
  391. {
  392. }
  393. void OnSessionStart ()
  394. {
  395. if (Start != null)
  396. Start (this, EventArgs.Empty);
  397. }
  398. public event EventHandler Start;
  399. // This event is public, but only Session_[On]End in global.asax will be invoked if present.
  400. public event EventHandler End;
  401. }
  402. }
  403. #endif