SessionStateModule.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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. #if TRACE
  249. Console.WriteLine ("SessionStateModule.OnAcquireRequestState (hash {0})", this.GetHashCode ().ToString ("x"));
  250. #endif
  251. HttpApplication application = (HttpApplication) o;
  252. HttpContext context = application.Context;
  253. if (!(context.Handler is IRequiresSessionState)) {
  254. #if TRACE
  255. Console.WriteLine ("Handler ({0}) does not require session state", context.Handler);
  256. #endif
  257. return;
  258. }
  259. isReadOnly = (context.Handler is IReadOnlySessionState);
  260. if (idManager != null) {
  261. if (idManager.InitializeRequest (context, false, out supportSessionIDReissue))
  262. return; // Redirected, will come back here in a while
  263. sessionId = idManager.GetSessionID (context);
  264. }
  265. if (handler != null) {
  266. handler.InitializeRequest (context);
  267. GetStoreData (context);
  268. if (storeData == null && !storeLocked) {
  269. isNew = true;
  270. sessionId = idManager.CreateSessionID (context);
  271. #if TRACE
  272. Console.WriteLine ("New session ID allocated: {0}", sessionId);
  273. #endif
  274. bool redirected = false;
  275. bool cookieAdded = false;
  276. idManager.SaveSessionID (context, sessionId, out redirected, out cookieAdded);
  277. if (redirected) {
  278. if (supportSessionIDReissue)
  279. handler.CreateUninitializedItem (context, sessionId, config.Timeout.Minutes);
  280. context.Response.End();
  281. return;
  282. } else
  283. storeData = handler.CreateNewStoreData (context, config.Timeout.Minutes);
  284. } else if (storeData == null && storeLocked) {
  285. WaitForStoreUnlock (context);
  286. } else if (storeData != null &&
  287. !storeLocked &&
  288. storeSessionAction == SessionStateActions.InitializeItem &&
  289. IsCookieLess (context)) {
  290. storeData = handler.CreateNewStoreData (context, config.Timeout.Minutes);
  291. }
  292. SessionSetup (context, isNew);
  293. }
  294. }
  295. void OnReleaseRequestState (object o, EventArgs args)
  296. {
  297. #if TRACE
  298. Console.WriteLine ("SessionStateModule.OnReleaseRequestState (hash {0})", this.GetHashCode ().ToString ("x"));
  299. Console.WriteLine ("\tsessionId == {0}", sessionId);
  300. #endif
  301. if (handler == null)
  302. return;
  303. HttpApplication application = (HttpApplication) o;
  304. HttpContext context = application.Context;
  305. if (!(context.Handler is IRequiresSessionState))
  306. return;
  307. #if TRACE
  308. Console.WriteLine ("\trequest path == {0}", context.Request.FilePath);
  309. Console.WriteLine ("\tHandler ({0}) requires session state", context.Handler);
  310. #endif
  311. if (!container.IsAbandoned) {
  312. #if TRACE
  313. Console.WriteLine ("\tnot abandoned");
  314. #endif
  315. if (!isReadOnly) {
  316. #if TRACE
  317. Console.WriteLine ("\tnot read only, storing and releasing");
  318. #endif
  319. handler.SetAndReleaseItemExclusive (context, sessionId, storeData, storeLockId, false);
  320. } else {
  321. #if TRACE
  322. Console.WriteLine ("\tread only, releasing");
  323. #endif
  324. handler.ReleaseItemExclusive (context, sessionId, storeLockId);
  325. }
  326. handler.ResetItemTimeout (context, sessionId);
  327. } else {
  328. handler.ReleaseItemExclusive (context, sessionId, storeLockId);
  329. handler.RemoveItem (context, sessionId, storeLockId, storeData);
  330. }
  331. SessionStateUtility.RemoveHttpSessionStateFromContext (context);
  332. if (supportsExpiration)
  333. SessionStateUtility.RaiseSessionEnd (container, o, args);
  334. }
  335. void OnEndRequest (object o, EventArgs args)
  336. {
  337. if (handler == null)
  338. return;
  339. HttpApplication application = o as HttpApplication;
  340. if (application == null)
  341. return;
  342. if (handler != null)
  343. handler.EndRequest (application.Context);
  344. }
  345. void GetStoreData (HttpContext context)
  346. {
  347. if (sessionId == null)
  348. return;
  349. if (isReadOnly)
  350. storeData = handler.GetItem (context,
  351. sessionId,
  352. out storeLocked,
  353. out storeLockAge,
  354. out storeLockId,
  355. out storeSessionAction);
  356. else
  357. storeData = handler.GetItemExclusive (context,
  358. sessionId,
  359. out storeLocked,
  360. out storeLockAge,
  361. out storeLockId,
  362. out storeSessionAction);
  363. }
  364. void WaitForStoreUnlock (HttpContext context)
  365. {
  366. AutoResetEvent are = new AutoResetEvent (false);
  367. TimerCallback tc = new TimerCallback (this.StoreUnlockWaitCallback);
  368. CallbackState cs = new CallbackState (context, are);
  369. using (Timer timer = new Timer (tc, cs, 500, 500)) {
  370. try {
  371. are.WaitOne (executionTimeout, false);
  372. } catch {
  373. storeData = null;
  374. }
  375. }
  376. }
  377. void StoreUnlockWaitCallback (object s)
  378. {
  379. CallbackState state = s as CallbackState;
  380. GetStoreData (state.Context);
  381. if (storeData == null && storeLocked && (storeLockAge > executionTimeout)) {
  382. handler.ReleaseItemExclusive (state.Context, sessionId, storeLockId);
  383. state.AutoEvent.Set ();
  384. } else if (storeData != null && !storeLocked)
  385. state.AutoEvent.Set ();
  386. }
  387. void SessionSetup (HttpContext context, bool isNew)
  388. {
  389. if (storeData != null && sessionId != null) {
  390. container = new HttpSessionStateContainer (
  391. sessionId,
  392. storeData.Items,
  393. storeData.StaticObjects,
  394. storeData.Timeout,
  395. isNew,
  396. config.Cookieless,
  397. config.Mode,
  398. isReadOnly);
  399. SessionStateUtility.AddHttpSessionStateToContext (context, container);
  400. if (isNew) {
  401. supportsExpiration = handler.SetItemExpireCallback (OnSessionExpired);
  402. OnSessionStart ();
  403. }
  404. }
  405. }
  406. void OnSessionExpired (string id, SessionStateStoreData item)
  407. {
  408. }
  409. void OnSessionStart ()
  410. {
  411. if (Start != null)
  412. Start (this, EventArgs.Empty);
  413. }
  414. public event EventHandler Start;
  415. // This event is public, but only Session_[On]End in global.asax will be invoked if present.
  416. public event EventHandler End;
  417. }
  418. }
  419. #endif