Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web / State / SessionStateModule.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SessionStateModule.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 /*
8  * SessionStateModule
9  *
10  * Copyright (c) 1998-2002, Microsoft Corporation
11  *
12  */
13
14 namespace System.Web.SessionState {
15
16     using System;
17     using System.Threading;
18     using System.Collections;
19     using System.Configuration;
20     using System.IO;
21     using System.Web.Caching;
22     using System.Web.Util;
23     using System.Web.Configuration;
24     using System.Xml;
25     using System.Security.Cryptography;
26     using System.Data.SqlClient;
27     using System.Globalization;
28     using System.Security.Permissions;
29     using System.Text;
30     using System.Threading.Tasks;
31     using System.Web.Hosting;
32     using System.Web.Management;
33     using Microsoft.Win32;
34     using System.Collections.Concurrent;
35     using System.Collections.Generic;
36
37     public delegate void SessionStateItemExpireCallback(
38             string id, SessionStateStoreData item);
39
40     class SessionOnEndTargetWorkItem {
41         SessionOnEndTarget  _target;
42         HttpSessionState    _sessionState;
43
44         internal SessionOnEndTargetWorkItem(SessionOnEndTarget target, HttpSessionState sessionState) {
45             _target = target;
46             _sessionState = sessionState;
47         }
48
49         internal void RaiseOnEndCallback() {
50             _target.RaiseOnEnd(_sessionState);
51         }
52     }
53
54     /*
55      * Calls the OnSessionEnd event. We use an object other than the SessionStateModule
56      * because the state of the module is unknown - it could have been disposed
57      * when a session ends.
58      */
59     class SessionOnEndTarget {
60         internal int _sessionEndEventHandlerCount;
61
62         internal SessionOnEndTarget() {
63         }
64
65         internal int SessionEndEventHandlerCount {
66             get {
67                 return _sessionEndEventHandlerCount;
68             }
69             set {
70                 _sessionEndEventHandlerCount = value;
71             }
72         }
73
74         internal void RaiseOnEnd(HttpSessionState sessionState) {
75             Debug.Trace("SessionOnEnd", "Firing OnSessionEnd for " + sessionState.SessionID);
76
77             if (_sessionEndEventHandlerCount > 0) {
78                 HttpApplicationFactory.EndSession(sessionState, this, EventArgs.Empty);
79             }
80         }
81
82         internal void RaiseSessionOnEnd(String id, SessionStateStoreData item) {
83             HttpSessionStateContainer sessionStateContainer = new HttpSessionStateContainer(
84                     id,
85                     item.Items,
86                     item.StaticObjects,
87                     item.Timeout,
88                     false,
89                     SessionStateModule.s_configCookieless,
90                     SessionStateModule.s_configMode,
91                     true);
92
93             HttpSessionState    sessionState = new HttpSessionState(sessionStateContainer);
94
95             if (HttpRuntime.ShutdownInProgress) {
96                 // call directly when shutting down
97                 RaiseOnEnd(sessionState);
98             }
99             else {
100                 // post via thread pool
101                 SessionOnEndTargetWorkItem workItem = new SessionOnEndTargetWorkItem(this, sessionState);
102                 WorkItem.PostInternal(new WorkItemCallback(workItem.RaiseOnEndCallback));
103             }
104         }
105
106     }
107
108
109     /*
110      * The sesssion state module provides session state services
111      * for an application.
112      */
113
114     /// <devdoc>
115     ///    <para>[To be supplied.]</para>
116     /// </devdoc>
117     public sealed class SessionStateModule : ISessionStateModule {
118
119         internal const string SQL_CONNECTION_STRING_DEFAULT = "data source=localhost;Integrated Security=SSPI";
120         internal const string STATE_CONNECTION_STRING_DEFAULT = "tcpip=loopback:42424";
121         internal const int                  TIMEOUT_DEFAULT = 20;
122         internal const SessionStateMode     MODE_DEFAULT = SessionStateMode.InProc;
123
124         private static long                 LOCKED_ITEM_POLLING_INTERVAL = 500; // in milliseconds
125         static readonly TimeSpan            LOCKED_ITEM_POLLING_DELTA = new TimeSpan(250 * TimeSpan.TicksPerMillisecond);
126
127         static readonly TimeSpan            DEFAULT_DBG_EXECUTION_TIMEOUT = new TimeSpan(0, 0, System.Web.Compilation.PageCodeDomTreeGenerator.DebugScriptTimeout);
128
129         // When we are using Cache to store session state (InProc and StateServer),
130         // can't specify a timeout value larger than 1 year because CacheEntry ctor
131         // will throw an exception.
132         internal const int                  MAX_CACHE_BASED_TIMEOUT_MINUTES = 365 * 24 * 60;
133
134         bool                                s_oneTimeInit;
135         static int                          s_timeout;
136
137         #pragma warning disable 0649
138         static ReadWriteSpinLock            s_lock;
139         #pragma warning restore 0649
140
141         static bool                         s_trustLevelInsufficient;
142
143         static TimeSpan                     s_configExecutionTimeout;
144
145         static bool                         s_configRegenerateExpiredSessionId;
146         static bool                         s_useHostingIdentity;
147         internal static HttpCookieMode      s_configCookieless;
148         internal static SessionStateMode    s_configMode;
149         
150         // This is used as a perf optimization for IIS7 Integrated Mode.  If session state is released
151         // in ReleaseState, we can disable the EndRequest notification if the mode is InProc or StateServer
152         // because neither InProcSessionStateStore.EndRequest nor OutOfProcSessionStateStore.EndRequest
153         // are implemented.
154         static bool                         s_canSkipEndRequestCall;
155
156         private static bool s_PollIntervalRegLookedUp = false;
157         private static object s_PollIntervalRegLock = new object();
158         
159         private static ConcurrentDictionary<string, int> s_queuedRequestsNumPerSession = new ConcurrentDictionary<string, int>();
160         //
161         // Check if we can optmize for InProc case.
162         // Optimization details:
163         //
164         // If we are in InProc mode, and cookieless=false, in certain scenarios we
165         // can avoid reading the session ID from the cookies because that's an expensive operation.
166         // To allow that, we use s_sessionEverSet to keep track of whether we've ever created
167         // any session state.
168         //
169         // If no session has ever be created, we can optimize in the following two cases:
170         //
171         // Case 1: Page has disabled session state
172         // In BeginAcquireState, we usually read the session ID, and reset the timeout value
173         // of the session state.  However, since no session has ever been created, we can
174         // skip both reading the session id and resetting the timeout.
175         //
176         // Case 2: Page has enabled session state
177         // In this case, we will delay reading (and creating it if not found) the session ID
178         // until it's really needed. (e.g. from HttpSessionStateContainer.SessionID)
179         //
180         // Please note that we optimize only if the app is using SessionIDManager
181         // as the session ID provider; otherwise, we do not have knowledge about
182         // the provider in order to optimize safely.
183         //
184         // And we will delay reading the id only if we are using cookie to store the session ID.  If we
185         // use cookieless, in the delayed session ID creation scenario, cookieless requires a redirect,
186         // and it'll be bad to do that in the middle of a page execution.
187         //
188         static bool                         s_allowInProcOptimization;
189         static bool                         s_sessionEverSet;
190
191         //
192         // Another optimization is to delay the creation of a new session state store item
193         // until it's needed.
194         static bool                         s_allowDelayedStateStoreItemCreation;
195         static HttpSessionStateContainer    s_delayedSessionState = new HttpSessionStateContainer();
196
197         /* per application vars */
198         EventHandler                   _sessionStartEventHandler;
199         Timer                          _timer;
200         TimerCallback                  _timerCallback;
201         volatile int                   _timerId;
202         ISessionIDManager              _idManager;
203         bool                           _usingAspnetSessionIdManager;
204         SessionStateStoreProviderBase  _store;
205         bool                           _supportSessionExpiry;
206         IPartitionResolver             _partitionResolver;
207         bool                           _ignoreImpersonation;
208         readonly SessionOnEndTarget    _onEndTarget = new SessionOnEndTarget();
209
210         /* per request data goes in _rq* variables */
211         bool                            _acquireCalled;
212         bool                            _releaseCalled;
213         HttpSessionStateContainer       _rqSessionState;
214         String                          _rqId;
215         bool                            _rqIdNew;
216         ISessionStateItemCollection     _rqSessionItems;
217         HttpStaticObjectsCollection     _rqStaticObjects;
218         bool                            _rqIsNewSession;
219         bool                            _rqSessionStateNotFound;
220         bool                            _rqReadonly;
221         HttpContext                     _rqContext;
222         HttpAsyncResult                 _rqAr;
223         SessionStateStoreData           _rqItem;
224         object                          _rqLockId;  // The id of its SessionStateItem ownership
225                                                     // If the ownership change hands (e.g. this ownership
226                                                     // times out), the lockId of the item at the store
227                                                     // will change.
228         int                             _rqInCallback;
229         DateTime                        _rqLastPollCompleted;
230         TimeSpan                        _rqExecutionTimeout;
231         bool                            _rqAddedCookie;
232         SessionStateActions             _rqActionFlags;
233         ImpersonationContext            _rqIctx;
234         internal int                    _rqChangeImpersonationRefCount;
235         ImpersonationContext            _rqTimerThreadImpersonationIctx;
236         bool                            _rqSupportSessionIdReissue;
237
238         /// <devdoc>
239         ///    <para>
240         ///       Initializes a new instance of the <see cref='System.Web.State.SessionStateModule'/>
241         ///       class.
242         ///     </para>
243         /// </devdoc>
244         [SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
245         public SessionStateModule() {
246         }
247
248         static bool CheckTrustLevel(SessionStateSection config) {
249             switch (config.Mode) {
250                 case SessionStateMode.SQLServer:
251                 case SessionStateMode.StateServer:
252                     return HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium);
253
254                 default:
255                 case SessionStateMode.Off:
256                 case SessionStateMode.InProc: // In-proc session doesn't require any trust level (part of ASURT 124513)
257                     return true;
258             }
259         }
260
261         [AspNetHostingPermission(SecurityAction.Assert, Level=AspNetHostingPermissionLevel.Low)]
262         private SessionStateStoreProviderBase SecureInstantiateProvider(ProviderSettings settings) {
263             return (SessionStateStoreProviderBase)ProvidersHelper.InstantiateProvider(settings, typeof(SessionStateStoreProviderBase));
264         }
265
266         // Create an instance of the custom store as specified in the config file
267         SessionStateStoreProviderBase InitCustomStore(SessionStateSection config) {
268             string          providerName = config.CustomProvider;
269             ProviderSettings  ps;
270
271             if (String.IsNullOrEmpty(providerName)) {
272                 throw new ConfigurationErrorsException(
273                         SR.GetString(SR.Invalid_session_custom_provider, providerName),
274                         config.ElementInformation.Properties["customProvider"].Source, config.ElementInformation.Properties["customProvider"].LineNumber);
275             }
276
277             ps = config.Providers[providerName];
278             if (ps == null) {
279                 throw new ConfigurationErrorsException(
280                         SR.GetString(SR.Missing_session_custom_provider, providerName),
281                         config.ElementInformation.Properties["customProvider"].Source, config.ElementInformation.Properties["customProvider"].LineNumber);
282             }
283
284             return SecureInstantiateProvider(ps);
285         }
286
287         IPartitionResolver InitPartitionResolver(SessionStateSection config) {
288             string  partitionResolverType = config.PartitionResolverType;
289             Type    resolverType;
290             IPartitionResolver  iResolver;
291
292             if (String.IsNullOrEmpty(partitionResolverType)) {
293                 return null;
294             }
295
296             if (config.Mode != SessionStateMode.StateServer &&
297                 config.Mode != SessionStateMode.SQLServer) {
298                     throw new ConfigurationErrorsException(SR.GetString(SR.Cant_use_partition_resolve),
299                         config.ElementInformation.Properties["partitionResolverType"].Source, config.ElementInformation.Properties["partitionResolverType"].LineNumber);
300             }
301
302
303             resolverType = ConfigUtil.GetType(partitionResolverType, "partitionResolverType", config);
304             ConfigUtil.CheckAssignableType(typeof(IPartitionResolver), resolverType, config, "partitionResolverType");
305
306             iResolver = (IPartitionResolver)HttpRuntime.CreatePublicInstance(resolverType);
307             iResolver.Initialize();
308
309             return iResolver;
310         }
311
312         ISessionIDManager InitSessionIDManager(SessionStateSection config) {
313             string  sessionIDManagerType = config.SessionIDManagerType;
314             ISessionIDManager  iManager;
315
316             if (String.IsNullOrEmpty(sessionIDManagerType)) {
317                 iManager = new SessionIDManager();
318                 _usingAspnetSessionIdManager = true;
319             }
320             else {
321                 Type    managerType;
322
323                 managerType = ConfigUtil.GetType(sessionIDManagerType, "sessionIDManagerType", config);
324                 ConfigUtil.CheckAssignableType(typeof(ISessionIDManager), managerType, config, "sessionIDManagerType");
325
326                 iManager = (ISessionIDManager)HttpRuntime.CreatePublicInstance(managerType);
327             }
328
329             iManager.Initialize();
330
331             return iManager;
332         }
333
334         void InitModuleFromConfig(HttpApplication app, SessionStateSection config) {
335             if (config.Mode == SessionStateMode.Off) {
336                 return;
337             }
338
339             app.AddOnAcquireRequestStateAsync(
340                     new BeginEventHandler(this.BeginAcquireState),
341                     new EndEventHandler(this.EndAcquireState));
342
343             app.ReleaseRequestState += new EventHandler(this.OnReleaseState);
344             app.EndRequest += new EventHandler(this.OnEndRequest);
345
346             _partitionResolver = InitPartitionResolver(config);
347
348             switch (config.Mode) {
349                 case SessionStateMode.InProc:
350                     if (HttpRuntime.UseIntegratedPipeline) {
351                         s_canSkipEndRequestCall = true;
352                     }
353                     _store = new InProcSessionStateStore();
354                     _store.Initialize(null, null);
355                     break;
356
357 #if !FEATURE_PAL // FEATURE_PAL does not enable out of proc session state
358                 case SessionStateMode.StateServer:
359                     if (HttpRuntime.UseIntegratedPipeline) {
360                         s_canSkipEndRequestCall = true;
361                     }
362                     _store = new OutOfProcSessionStateStore();
363                     ((OutOfProcSessionStateStore)_store).Initialize(null, null, _partitionResolver);
364                     break;
365
366                 case SessionStateMode.SQLServer:
367                     _store = new SqlSessionStateStore();
368                     ((SqlSessionStateStore)_store).Initialize(null, null, _partitionResolver);
369 #if DBG
370                     ((SqlSessionStateStore)_store).SetModule(this);
371 #endif
372                     break;
373 #else // !FEATURE_PAL
374                 case SessionStateMode.StateServer:
375                     throw new NotImplementedException("ROTORTODO");
376                     break;
377
378                 case SessionStateMode.SQLServer:
379                     throw new NotImplementedException("ROTORTODO");
380                     break;
381 #endif // !FEATURE_PAL
382
383                 case SessionStateMode.Custom:
384                     _store = InitCustomStore(config);
385                     break;
386
387                 default:
388                     break;
389             }
390
391             // We depend on SessionIDManager to manage session id
392             _idManager = InitSessionIDManager(config);
393
394             if ((config.Mode == SessionStateMode.InProc || config.Mode == SessionStateMode.StateServer) &&
395                 _usingAspnetSessionIdManager) {
396                 // If we're using InProc mode or StateServer mode, and also using our own session id module,
397                 // we know we don't care about impersonation in our all session state store read/write
398                 // and session id read/write.
399                 _ignoreImpersonation = true;
400             }
401
402         }
403
404         public void Init(HttpApplication app) {
405             bool initModuleCalled = false;
406             SessionStateSection config = RuntimeConfig.GetAppConfig().SessionState;
407
408             if (!s_oneTimeInit) {
409                 s_lock.AcquireWriterLock();
410                 try {
411                     if (!s_oneTimeInit) {
412                         InitModuleFromConfig(app, config);
413                         initModuleCalled = true;
414
415                         if (!CheckTrustLevel(config))
416                             s_trustLevelInsufficient = true;
417
418                         s_timeout = (int)config.Timeout.TotalMinutes;
419
420                         s_useHostingIdentity = config.UseHostingIdentity;
421
422                         // See if we can try InProc optimization. See inline doc of s_allowInProcOptimization
423                         // for details.
424                         if (config.Mode == SessionStateMode.InProc &&
425                             _usingAspnetSessionIdManager) {
426                             s_allowInProcOptimization = true;
427                         }
428
429                         if (config.Mode != SessionStateMode.Custom &&
430                             config.Mode != SessionStateMode.Off &&
431                             !config.RegenerateExpiredSessionId) {
432                             s_allowDelayedStateStoreItemCreation = true;
433                         }
434
435                         s_configExecutionTimeout = RuntimeConfig.GetConfig().HttpRuntime.ExecutionTimeout;
436
437                         s_configRegenerateExpiredSessionId = config.RegenerateExpiredSessionId;
438                         s_configCookieless = config.Cookieless;
439                         s_configMode = config.Mode;
440
441                         // The last thing to set in this if-block.
442                         s_oneTimeInit = true;
443
444                         Debug.Trace("SessionStateModuleInit",
445                                     "Configuration: _mode=" + config.Mode +
446                                     ";Timeout=" + config.Timeout +
447                                     ";CookieMode=" + config.Cookieless +
448                                     ";SqlConnectionString=" + config.SqlConnectionString +
449                                     ";StateConnectionString=" + config.StateConnectionString +
450                                     ";s_allowInProcOptimization=" + s_allowInProcOptimization +
451                                     ";s_allowDelayedStateStoreItemCreation=" + s_allowDelayedStateStoreItemCreation);
452
453                     }
454                 }
455                 finally {
456                     s_lock.ReleaseWriterLock();
457                 }
458             }
459
460             if (!initModuleCalled) {
461                 InitModuleFromConfig(app, config);
462             }
463
464             if (s_trustLevelInsufficient) {
465                 throw new HttpException(SR.GetString(SR.Session_state_need_higher_trust));
466             }
467         }
468
469
470         /// <devdoc>
471         ///    <para>[To be supplied.]</para>
472         /// </devdoc>
473         public void Dispose() {
474             if (_timer != null) {
475                 ((IDisposable)_timer).Dispose();
476             }
477
478             if (_store != null) {
479                 _store.Dispose();
480             }
481         }
482
483         void ResetPerRequestFields() {
484             Debug.Assert(_rqIctx == null, "_rqIctx == null");
485             Debug.Assert(_rqChangeImpersonationRefCount == 0, "_rqChangeImpersonationRefCount == 0");
486
487             _rqSessionState = null;
488             _rqId = null;
489             _rqSessionItems = null;
490             _rqStaticObjects = null;
491             _rqIsNewSession = false;
492             _rqSessionStateNotFound = true;
493             _rqReadonly = false;
494             _rqItem = null;
495             _rqContext = null;
496             _rqAr = null;
497             _rqLockId = null;
498             _rqInCallback = 0;
499             _rqLastPollCompleted = DateTime.MinValue;
500             _rqExecutionTimeout = TimeSpan.Zero;
501             _rqAddedCookie = false;
502             _rqIdNew = false;
503             _rqActionFlags = 0;
504             _rqIctx = null;
505             _rqChangeImpersonationRefCount = 0;
506             _rqTimerThreadImpersonationIctx = null;
507             _rqSupportSessionIdReissue = false;
508         }
509
510         /*
511          * Add a OnStart event handler.
512          *
513          * @param sessionEventHandler
514          */
515
516         /// <devdoc>
517         ///    <para>[To be supplied.]</para>
518         /// </devdoc>
519         public event EventHandler Start {
520             add {
521                 _sessionStartEventHandler += value;
522             }
523             remove {
524                 _sessionStartEventHandler -= value;
525             }
526         }
527
528         void RaiseOnStart(EventArgs e) {
529             if (_sessionStartEventHandler == null)
530                 return;
531
532             Debug.Trace("SessionStateModuleRaiseOnStart",
533                 "Session_Start called for session id:" + _rqId);
534
535             // Session_OnStart for ASPCOMPAT pages has to be raised from an STA thread
536             // 
537             if (HttpRuntime.ApartmentThreading || _rqContext.InAspCompatMode) {
538 #if !FEATURE_PAL // FEATURE_PAL does not enable COM
539                 AspCompatApplicationStep.RaiseAspCompatEvent(
540                     _rqContext,
541                     _rqContext.ApplicationInstance,
542                     null,
543                     _sessionStartEventHandler,
544                     this,
545                     e);
546 #else // !FEATURE_PAL
547                 throw new NotImplementedException ("ROTORTODO");
548 #endif // !FEATURE_PAL
549
550             }
551             else {
552                 if (HttpContext.Current == null) {
553                     // This can happen if it's called by a timer thread
554                     DisposableHttpContextWrapper.SwitchContext(_rqContext);
555                 }
556
557                 _sessionStartEventHandler(this, e);
558             }
559         }
560
561         /*
562          * Fire the OnStart event.
563          *
564          * @param e
565          */
566         void OnStart(EventArgs e) {
567             RaiseOnStart(e);
568         }
569
570         /*
571          * Add a OnEnd event handler.
572          *
573          * @param sessionEventHandler
574          */
575
576         /// <devdoc>
577         ///    <para>[To be supplied.]</para>
578         /// </devdoc>
579         public event EventHandler End {
580             add {
581                 lock(_onEndTarget) {
582                     if (_store != null && _onEndTarget.SessionEndEventHandlerCount == 0) {
583                         _supportSessionExpiry = _store.SetItemExpireCallback(
584                                 new SessionStateItemExpireCallback(_onEndTarget.RaiseSessionOnEnd));
585                     }
586                     ++_onEndTarget.SessionEndEventHandlerCount;
587                 }
588             }
589             remove {
590                 lock(_onEndTarget) {
591                     --_onEndTarget.SessionEndEventHandlerCount;
592                     // 
593                     if (_store != null && _onEndTarget.SessionEndEventHandlerCount == 0) {
594                         _store.SetItemExpireCallback(null);
595                         _supportSessionExpiry = false;
596                     }
597                 }
598             }
599         }
600
601         /*
602          * Acquire session state
603          */
604         IAsyncResult BeginAcquireState(Object source, EventArgs e, AsyncCallback cb, Object extraData) {
605             bool                requiresState;
606             bool                isCompleted = true;
607             bool                skipReadingId = false;
608
609             Debug.Trace("SessionStateModuleOnAcquireState", "Beginning SessionStateModule::OnAcquireState");
610
611             _acquireCalled = true;
612             _releaseCalled = false;
613             ResetPerRequestFields();
614
615             _rqContext = ((HttpApplication)source).Context;
616             _rqAr = new HttpAsyncResult(cb, extraData);
617
618             ChangeImpersonation(_rqContext, false);
619
620             try {
621                 if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_BEGIN, _rqContext.WorkerRequest);
622
623                 /* Notify the store we are beginning to get process request */
624                 _store.InitializeRequest(_rqContext);
625
626                 /* determine if the request requires state at all */
627                 requiresState = _rqContext.RequiresSessionState;
628
629                 // SessionIDManager may need to do a redirect if cookieless setting is AutoDetect
630                 if (_idManager.InitializeRequest(_rqContext, false, out _rqSupportSessionIdReissue)) {
631                     _rqAr.Complete(true, null, null);
632                     if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest);
633                     return _rqAr;
634                 }
635
636                 // See if we can skip reading the session id.  See inline doc of s_allowInProcOptimization
637                 // for details.
638                 if (s_allowInProcOptimization &&
639                     !s_sessionEverSet &&
640                      (!requiresState ||             // Case 1
641                       !((SessionIDManager)_idManager).UseCookieless(_rqContext)) ) {  // Case 2
642
643                     skipReadingId = true;
644
645 #if DBG
646                     if (!requiresState) {
647                         // Case 1
648                         Debug.Trace("SessionStateModuleOnAcquireState", "Skip reading id because page has disabled session state");
649                     }
650                     else {
651                         // Case 2
652                         Debug.Trace("SessionStateModuleOnAcquireState", "Delay reading id because we're using InProc optimization, and we are not using cookieless");
653                     }
654 #endif
655                 }
656                 else {
657                     /* Get sessionid */
658                     _rqId = _idManager.GetSessionID(_rqContext);
659                     Debug.Trace("SessionStateModuleOnAcquireState", "Current request id=" + _rqId);
660                 }
661
662                 if (!requiresState) {
663                     if (_rqId == null) {
664                         Debug.Trace("SessionStateModuleOnAcquireState",
665                                     "Handler does not require state, " +
666                                     "session id skipped or no id found, " +
667                                     "skipReadingId=" + skipReadingId +
668                                     "\nReturning from SessionStateModule::OnAcquireState");
669                     }
670                     else {
671                         Debug.Trace("SessionStateModuleOnAcquireState",
672                                     "Handler does not require state, " +
673                                     "resetting timeout for SessionId=" + _rqId +
674                                     "\nReturning from SessionStateModule::OnAcquireState");
675
676                         // Still need to update the sliding timeout to keep session alive.
677                         // There is a plan to skip this for perf reason.  But it was postponed to
678                         // after Whidbey.
679                         _store.ResetItemTimeout(_rqContext, _rqId);
680                     }
681
682                     _rqAr.Complete(true, null, null);
683
684                     if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest);
685                     return _rqAr;
686                 }
687
688                 _rqExecutionTimeout = _rqContext.Timeout;
689
690                 // If the page is marked as DEBUG, HttpContext.Timeout will return a very large value (~1 year)
691                 // In this case, we want to use the executionTimeout value specified in the config to avoid
692                 // PollLockedSession to run forever.
693                 if (_rqExecutionTimeout == DEFAULT_DBG_EXECUTION_TIMEOUT) {
694                     _rqExecutionTimeout = s_configExecutionTimeout;
695                 }
696
697                 /* determine if we need just read-only access */
698                 _rqReadonly = _rqContext.ReadOnlySessionState;
699
700                 if (_rqId != null) {
701                     /* get the session state corresponding to this session id */
702                     isCompleted = GetSessionStateItem();
703                 }
704                 else if (!skipReadingId) {
705                     /* if there's no id yet, create it */
706                     bool    redirected = CreateSessionId();
707
708                     _rqIdNew = true;
709
710                     if (redirected) {
711                         if (s_configRegenerateExpiredSessionId) {
712                             // See inline comments in CreateUninitializedSessionState()
713                             CreateUninitializedSessionState();
714                         }
715
716                         _rqAr.Complete(true, null, null);
717
718                         if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest);
719                         return _rqAr;
720                     }
721                 }
722
723                 if (isCompleted) {
724                     CompleteAcquireState();
725                     _rqAr.Complete(true, null, null);
726                 }
727
728                 return _rqAr;
729             }
730             finally {
731                 RestoreImpersonation();
732             }
733         }
734
735         internal bool CreateSessionId() {
736             // CreateSessionId should be called only if:
737             Debug.Assert(_rqId == null ||               // Session id isn't found in the request, OR
738
739                 (_rqSessionStateNotFound &&             // The session state isn't found, AND
740                  s_configRegenerateExpiredSessionId &&  // We are regenerating expired session id, AND
741                  _rqSupportSessionIdReissue &&          // This request supports session id re-issue, AND
742                  !_rqIdNew),                            // The above three condition should imply the session id
743                                                         // isn't just created, but is sent by the request.
744                 "CreateSessionId should be called only if we're generating new id, or re-generating expired one");
745             Debug.Assert(_rqChangeImpersonationRefCount > 0, "Must call ChangeImpersonation first");
746
747             bool redirected;
748             _rqId = _idManager.CreateSessionID(_rqContext);
749             _idManager.SaveSessionID(_rqContext, _rqId, out redirected, out _rqAddedCookie);
750
751             return redirected;
752         }
753
754         internal void EnsureStateStoreItemLocked() {
755             // DevDiv 665141: 
756             // Ensure ownership of the session state item here as the session ID now can be put on the wire (by Response.Flush)
757             // and the client can initiate a request before this one reaches OnReleaseState and thus causing a race condition.
758             // Note: It changes when we call into the Session Store provider. Now it may happen at BeginAcquireState instead of OnReleaseState.
759
760             // Item is locked yet here only if this is a new session
761             if (!_rqSessionStateNotFound) {
762                 return;
763             }
764
765             Debug.Assert(_rqId != null, "Session State ID must exist");
766             Debug.Assert(_rqItem != null, "Session State item must exist");
767
768             ChangeImpersonation(_rqContext, false);
769
770             try {
771                 // Store the item if already have been created
772                 _store.SetAndReleaseItemExclusive(_rqContext, _rqId, _rqItem, _rqLockId, true /*_rqSessionStateNotFound*/);
773
774                 // Lock Session State Item in Session State Store
775                 LockSessionStateItem();
776             }
777             catch {
778                 throw;
779             }
780             finally {
781                 RestoreImpersonation();
782             }
783
784             // Mark as old session here. The SessionState is fully initialized, the item is locked
785             _rqSessionStateNotFound = false;
786             s_sessionEverSet = true;
787         }
788  
789         // Called when AcquireState is done.  This function will add the returned
790         // SessionStateStore item to the request context.
791         void CompleteAcquireState() {
792             Debug.Trace("SessionStateModuleOnAcquireState", "Item retrieved=" + (_rqItem != null).ToString(CultureInfo.InvariantCulture));
793             bool delayInitStateStoreItem = false;
794
795             Debug.Assert(!(s_allowDelayedStateStoreItemCreation && s_configRegenerateExpiredSessionId),
796                 "!(s_allowDelayedStateStoreItemCreation && s_configRegenerateExpiredSessionId)");
797
798             try {
799                 if (_rqItem != null) {
800                     _rqSessionStateNotFound = false;
801
802                     if ((_rqActionFlags & SessionStateActions.InitializeItem) != 0) {
803                         Debug.Trace("SessionStateModuleOnAcquireState", "Initialize an uninit item");
804                         _rqIsNewSession = true;
805                     }
806                     else {
807                         _rqIsNewSession = false;
808                     }
809                 }
810                 else {
811                     _rqIsNewSession = true;
812                     _rqSessionStateNotFound = true;
813
814                     if (s_allowDelayedStateStoreItemCreation) {
815                         Debug.Trace("SessionStateModuleOnAcquireState", "Delay creating new session state");
816                         delayInitStateStoreItem = true;
817                     }
818
819                     // We couldn't find the session state.
820                     if (!_rqIdNew &&                            // If the request has a session id, that means the session state has expired
821                         s_configRegenerateExpiredSessionId &&   // And we're asked to regenerate expired session
822                         _rqSupportSessionIdReissue) {           // And this request support session id reissue
823
824                         // We will generate a new session id for this expired session state
825                         bool redirected = CreateSessionId();
826
827                         Debug.Trace("SessionStateModuleOnAcquireState", "Complete re-creating new id; redirected=" + redirected);
828
829                         if (redirected) {
830                             Debug.Trace("SessionStateModuleOnAcquireState", "Will redirect because we've reissued a new id and it's cookieless");
831                             CreateUninitializedSessionState();
832                             return;
833                         }
834                     }
835                 }
836
837                 if (delayInitStateStoreItem) {
838                     _rqSessionState = s_delayedSessionState;
839                 }
840                 else {
841                     InitStateStoreItem(true);
842                 }
843
844                 // Set session state module
845                 SessionStateUtility.AddHttpSessionStateModuleToContext(_rqContext, this, delayInitStateStoreItem);
846
847                 if (_rqIsNewSession) {
848                     Debug.Trace("SessionStateModuleOnAcquireState", "Calling OnStart");
849                     OnStart(EventArgs.Empty);
850                 }
851             }
852             finally {
853                 if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest);
854             }
855
856 #if DBG
857             if (_rqIsNewSession) {
858                 if (_rqId == null) {
859                     Debug.Assert(s_allowInProcOptimization, "s_allowInProcOptimization");
860                     Debug.Trace("SessionStateModuleOnAcquireState", "New session: session id reading is delayed"+
861                                 "\nReturning from SessionStateModule::OnAcquireState");
862                 }
863                 else {
864                     Debug.Trace("SessionStateModuleOnAcquireState", "New session: SessionId= " + _rqId +
865                                 "\nReturning from SessionStateModule::OnAcquireState");
866                 }
867
868             }
869             else {
870                 Debug.Trace("SessionStateModuleOnAcquireState", "Retrieved old session, SessionId= " + _rqId +
871                             "\nReturning from SessionStateModule::OnAcquireState");
872
873             }
874 #endif
875         }
876
877         void CreateUninitializedSessionState() {
878             Debug.Assert(_rqChangeImpersonationRefCount > 0, "Must call ChangeImpersonation first");
879
880             // When we generate a new session id in cookieless case, and if "reissueExpiredSession" is
881             // true, we need to generate a new temporary empty session and save it
882             // under the new session id, otherwise when the next request (i.e. when the browser is
883             // redirected back to the web server) comes in, we will think it's accessing an expired session.
884             _store.CreateUninitializedItem(_rqContext, _rqId, s_timeout);
885         }
886
887         internal void InitStateStoreItem(bool addToContext) {
888             Debug.Assert(_rqId != null || s_allowInProcOptimization, "_rqId != null || s_allowInProcOptimization");
889
890             ChangeImpersonation(_rqContext, false);
891             try {
892
893                 if (_rqItem == null) {
894                     Debug.Trace("InitStateStoreItem", "Creating new session state");
895                     _rqItem = _store.CreateNewStoreData(_rqContext, s_timeout);
896                 }
897
898                 _rqSessionItems = _rqItem.Items;
899                 if (_rqSessionItems == null) {
900                     throw new HttpException(SR.GetString(SR.Null_value_for_SessionStateItemCollection));
901                 }
902
903                 // No check for null because we allow our custom provider to return a null StaticObjects.
904                 _rqStaticObjects = _rqItem.StaticObjects;
905
906                 _rqSessionItems.Dirty = false;
907
908                 _rqSessionState = new HttpSessionStateContainer(
909                         this,
910                         _rqId,            // could be null if we're using InProc optimization
911                         _rqSessionItems,
912                         _rqStaticObjects,
913                         _rqItem.Timeout,
914                         _rqIsNewSession,
915                         s_configCookieless,
916                         s_configMode,
917                         _rqReadonly);
918
919                 if (addToContext) {
920                     SessionStateUtility.AddHttpSessionStateToContext(_rqContext, _rqSessionState);
921                 }
922             }
923             finally {
924                 RestoreImpersonation();
925             }
926         }
927
928         // Used for InProc session id optimization
929         internal string DelayedGetSessionId() {
930             Debug.Assert(s_allowInProcOptimization, "Shouldn't be called if we don't allow InProc optimization");
931             Debug.Assert(_rqId == null, "Shouldn't be called if we already have the id");
932             Debug.Assert(!((SessionIDManager)_idManager).UseCookieless(_rqContext), "We can delay session id only if we are not using cookieless");
933
934             Debug.Trace("DelayedOperation", "Delayed getting session id");
935
936             bool    redirected;
937
938             ChangeImpersonation(_rqContext, false);
939             try {
940                 _rqId = _idManager.GetSessionID(_rqContext);
941
942                 if (_rqId == null) {
943                     Debug.Trace("DelayedOperation", "Delayed creating session id");
944
945                     redirected = CreateSessionId();
946                     Debug.Assert(!redirected, "DelayedGetSessionId shouldn't redirect us here.");
947                 }
948             }
949             finally {
950                 RestoreImpersonation();
951             }
952
953             return _rqId;
954         }
955
956         void LockSessionStateItem() {
957             bool locked;
958             TimeSpan lockAge;
959
960             Debug.Assert(_rqId != null, "_rqId != null");
961             Debug.Assert(_rqChangeImpersonationRefCount > 0, "Must call ChangeImpersonation first");
962
963             if (!_rqReadonly) {
964                 SessionStateStoreData storedItem = _store.GetItemExclusive(_rqContext, _rqId, out locked, out lockAge, out _rqLockId, out _rqActionFlags);
965                 Debug.Assert(storedItem != null, "Must succeed in locking session state item.");
966             }
967         }
968         
969         bool GetSessionStateItem() {
970             bool            isCompleted = true;
971             bool            locked;
972             TimeSpan        lockAge;
973
974             Debug.Assert(_rqId != null, "_rqId != null");
975             Debug.Assert(_rqChangeImpersonationRefCount > 0, "Must call ChangeImpersonation first");
976
977             if (_rqReadonly) {
978                 _rqItem = _store.GetItem(_rqContext, _rqId, out locked, out lockAge, out _rqLockId, out _rqActionFlags);
979             }
980             else {
981                 _rqItem = _store.GetItemExclusive(_rqContext, _rqId, out locked, out lockAge, out _rqLockId, out _rqActionFlags);
982                 
983                 // DevDiv Bugs 146875: WebForm and WebService Session Access Concurrency Issue
984                 // If we have an expired session, we need to insert the state in the store here to
985                 // ensure serialized access in case more than one entity requests it simultaneously.
986                 // If the state has already been created before, CreateUninitializedSessionState is a no-op.
987                 if (_rqItem == null && locked == false && _rqId != null) {
988                     if (!(s_configCookieless == HttpCookieMode.UseUri && s_configRegenerateExpiredSessionId == true)) {
989                         CreateUninitializedSessionState();
990                         _rqItem = _store.GetItemExclusive(_rqContext, _rqId, out locked, out lockAge, out _rqLockId, out _rqActionFlags);
991                     }
992                 }
993             }
994
995             // We didn't get it because it's locked....
996             if (_rqItem == null && locked) {
997                 // 
998                 if (lockAge >= _rqExecutionTimeout) {
999                     /* Release the lock on the item, which is held by another thread*/
1000                     Debug.Trace("SessionStateModuleOnAcquireState",
1001                                 "Lock timed out, lockAge=" + lockAge +
1002                                 ", id=" + _rqId);
1003
1004                     _store.ReleaseItemExclusive(_rqContext, _rqId, _rqLockId);
1005                 }
1006
1007                 Debug.Trace("SessionStateModuleOnAcquireState",
1008                             "Item is locked, will poll, id=" + _rqId);
1009
1010                 isCompleted = false;
1011                 PollLockedSession();
1012             }
1013
1014             return isCompleted;
1015         }
1016
1017         void PollLockedSession() {
1018             EnsureRequestTimeout();                        
1019
1020             if (_timerCallback == null) {
1021                 _timerCallback = new TimerCallback(this.PollLockedSessionCallback);
1022             }
1023
1024             if (_timer == null) {
1025                 _timerId++;
1026
1027                 // Only call this method once when setting up timer to poll the session item.
1028                 // It should not be called in timer's callback
1029                 QueueRef();
1030 #if DBG
1031                 if (!Debug.IsTagPresent("Timer") || Debug.IsTagEnabled("Timer"))
1032 #endif
1033                 {
1034                     if (!s_PollIntervalRegLookedUp)
1035                         LookUpRegForPollInterval();
1036                     _timer = new Timer(_timerCallback, _timerId, LOCKED_ITEM_POLLING_INTERVAL, LOCKED_ITEM_POLLING_INTERVAL);
1037                 }
1038             }
1039         }
1040
1041         private void EnsureRequestTimeout() {
1042             // Request may be blocked in acquiring state longer than execution timeout.
1043             // In that case, it will be timeout anyway after it gets the session item.
1044             // So it makes sense to timeout it when waiting longer than executionTimeout.
1045             if (_rqContext.HasTimeoutExpired) {
1046                 throw new HttpException(SR.GetString(SR.Request_timed_out));
1047             }
1048         }
1049
1050         private static bool IsRequestQueueEnabled {
1051             get {
1052                 return (AppSettings.RequestQueueLimitPerSession != AppSettings.UnlimitedRequestsPerSession);
1053             }
1054         } 
1055
1056         private void QueueRef() {
1057             if (!IsRequestQueueEnabled || _rqId == null) { 
1058                 return;
1059             }
1060
1061             //
1062             // Check the limit
1063             int count = 0;
1064             s_queuedRequestsNumPerSession.TryGetValue(_rqId, out count);
1065
1066             if (count >= AppSettings.RequestQueueLimitPerSession) {
1067                 throw new HttpException(SR.GetString(SR.Request_Queue_Limit_Per_Session_Exceeded));
1068             }
1069
1070             //
1071             // Add ref
1072             s_queuedRequestsNumPerSession.AddOrUpdate(_rqId, 1, (key, value) => value + 1);
1073         }
1074
1075         private void DequeRef() {
1076             if (!IsRequestQueueEnabled || _rqId == null) { 
1077                 return;
1078             }
1079
1080             // Decrement the counter
1081             if (s_queuedRequestsNumPerSession.AddOrUpdate(_rqId, 0, (key, value) => value - 1) == 0) {
1082                 //
1083                 // Remove the element when no more references
1084                 ((ICollection<KeyValuePair<string, int>>)s_queuedRequestsNumPerSession).Remove(new KeyValuePair<string,int>(_rqId, 0));
1085             }
1086         }
1087
1088         [RegistryPermission(SecurityAction.Assert, Unrestricted = true)]
1089         private static void LookUpRegForPollInterval() {
1090             lock (s_PollIntervalRegLock) {
1091                 if (s_PollIntervalRegLookedUp)
1092                     return;
1093                 try {
1094                     object o = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET", "SessionStateLockedItemPollInterval", 0);
1095                     if (o != null && (o is int || o is uint) && ((int)o) > 0)
1096                         LOCKED_ITEM_POLLING_INTERVAL = (int) o;
1097                     s_PollIntervalRegLookedUp = true;
1098                 }
1099                 catch { // ignore exceptions
1100                 }
1101             }
1102         }
1103
1104
1105         void ResetPollTimer() {
1106             _timerId++;
1107             if (_timer != null) {
1108                 ((IDisposable)_timer).Dispose();
1109                 _timer = null;
1110             }
1111         }
1112
1113         void ChangeImpersonation(HttpContext context, bool timerThread) {
1114 #if !FEATURE_PAL // FEATURE_PAL doesn't enable impersonation
1115             _rqChangeImpersonationRefCount++;
1116
1117             if (_ignoreImpersonation) {
1118                 return;
1119             }
1120
1121             // If SQL store isn't using integrated security, and we're using our own session id module,
1122             // we know we don't care about impersonation in our all session state store read/write
1123             // and session id read/write.
1124             if (s_configMode == SessionStateMode.SQLServer &&
1125                 ((SqlSessionStateStore)_store).KnowForSureNotUsingIntegratedSecurity &&
1126                 _usingAspnetSessionIdManager) {
1127                 return;
1128             }
1129
1130             // Please note that there are two types of calls coming in.  One is from a request thread,
1131             // where timerThread==false; the other is from PollLockedSessionCallback, where
1132             // timerThread==true.
1133
1134             if (s_useHostingIdentity) {
1135                 // If we're told to use Application Identity, in each case we should impersonate,
1136                 // if not called yet.
1137                 if (_rqIctx == null) {
1138                     _rqIctx = new ApplicationImpersonationContext();
1139                 }
1140             }
1141             else {
1142                 if (timerThread) {
1143                     // For the timer thread, we should explicity impersonate back to what the HttpContext was
1144                     // orginally impersonating.
1145                     _rqTimerThreadImpersonationIctx = new ClientImpersonationContext(context, false);
1146                 }
1147                 else {
1148                     // For a request thread, if we're told to not use hosting id, there's no need
1149                     // to do anything special.
1150                     Debug.Assert(_rqIctx == null, "_rqIctx == null");
1151                     return;
1152                 }
1153             }
1154 #endif // !FEATURE_PAL
1155         }
1156
1157         void RestoreImpersonation() {
1158             Debug.Assert(_rqChangeImpersonationRefCount != 0, "_rqChangeImpersonationRefCount != 0");
1159
1160             _rqChangeImpersonationRefCount--;
1161
1162             if (_rqChangeImpersonationRefCount == 0) {
1163                 Debug.Assert(!(_rqIctx != null && _rqTimerThreadImpersonationIctx != null), "Should not have mixed mode of impersonation");
1164
1165                 if (_rqIctx != null) {
1166                     _rqIctx.Undo();
1167                     _rqIctx = null;
1168                 }
1169
1170                 if (_rqTimerThreadImpersonationIctx != null) {
1171                     Debug.Assert(_rqContext != null, "_rqContext != null");
1172                     _rqTimerThreadImpersonationIctx.Undo();
1173                     _rqTimerThreadImpersonationIctx = null;
1174                 }
1175             }
1176         }
1177
1178         void PollLockedSessionCallback(object state) {
1179             Debug.Assert(_rqId != null, "_rqId != null");
1180             Debug.Trace("SessionStateModuleOnAcquireState",
1181                         "Polling callback called from timer, id=" + _rqId);
1182
1183             bool isCompleted = false;
1184             Exception error = null;
1185
1186             /* check whether we are currently in a callback */
1187             if (Interlocked.CompareExchange(ref _rqInCallback, 1, 0) != 0)
1188                 return;
1189
1190             try {
1191                 /*
1192                  * check whether this callback is for the current request,
1193                  * and whether sufficient time has passed since the last poll
1194                  * to try again.
1195                  */
1196                 int timerId = (int) state;
1197                 if (    (timerId == _timerId) &&
1198                         (DateTime.UtcNow - _rqLastPollCompleted >= LOCKED_ITEM_POLLING_DELTA)) {
1199
1200                     ChangeImpersonation(_rqContext, true);
1201
1202                     try {
1203                         isCompleted = GetSessionStateItem();
1204                         _rqLastPollCompleted = DateTime.UtcNow;
1205                         if (isCompleted) {
1206                             Debug.Assert(_timer != null, "_timer != null");
1207                             ResetPollTimer();
1208                             CompleteAcquireState();
1209                         }
1210                     }
1211                     finally {
1212                         RestoreImpersonation();
1213                     }
1214                 }
1215             }
1216             catch (Exception e) {
1217                 ResetPollTimer();
1218                 error = e;
1219             }
1220             finally {
1221                 Interlocked.Exchange(ref _rqInCallback, 0);
1222             }
1223
1224             if (isCompleted || error != null) {
1225                 DequeRef();
1226
1227                 _rqAr.Complete(false, null, error);
1228             }
1229         }
1230
1231
1232         void EndAcquireState(IAsyncResult ar) {
1233             ((HttpAsyncResult)ar).End();
1234         }
1235
1236         // Called by OnReleaseState to get the session id.
1237         string ReleaseStateGetSessionID() {
1238             if (_rqId == null) {
1239                 Debug.Assert(s_allowInProcOptimization, "s_allowInProcOptimization");
1240                 DelayedGetSessionId();
1241             }
1242
1243             Debug.Assert(_rqId != null, "_rqId != null");
1244             return _rqId;
1245         }
1246
1247         /*
1248          * Release session state
1249          */
1250
1251         /// <devdoc>
1252         ///    <para>[To be supplied.]</para>
1253         /// </devdoc>
1254         void OnReleaseState(Object source, EventArgs eventArgs) {
1255             HttpApplication             app;
1256             HttpContext                 context;
1257             bool                        setItemCalled = false;
1258
1259             Debug.Trace("SessionStateOnReleaseState", "Beginning SessionStateModule::OnReleaseState");
1260
1261             Debug.Assert(!(_rqAddedCookie && !_rqIsNewSession),
1262                 "If session id was added to the cookie, it must be a new session.");
1263
1264             // !!!
1265             // Please note that due to InProc session id optimization, this function should not
1266             // use _rqId directly because it can still be null.  Instead, use DelayedGetSessionId().
1267
1268             _releaseCalled = true;
1269
1270             app = (HttpApplication)source;
1271             context = app.Context;
1272
1273             ChangeImpersonation(context, false);
1274
1275             try {
1276                 if (_rqSessionState != null) {
1277                     bool delayedSessionState = (_rqSessionState == s_delayedSessionState);
1278
1279                     Debug.Trace("SessionStateOnReleaseState", "Remove session state from context");
1280                     SessionStateUtility.RemoveHttpSessionStateFromContext(_rqContext, delayedSessionState);
1281
1282                     /*
1283                      * Don't store untouched new sessions.
1284                      */
1285
1286                     if (
1287                                 // The store doesn't have the session state.
1288                                 // ( Please note we aren't checking _rqIsNewSession because _rqIsNewSession
1289                                 // is lalso true if the item is converted from temp to perm in a GetItemXXX() call.)
1290                                 _rqSessionStateNotFound
1291
1292                                 // OnStart is not defined
1293                                && _sessionStartEventHandler == null
1294
1295                                // Nothing has been stored in session state
1296                                && (delayedSessionState || !_rqSessionItems.Dirty)
1297                                && (delayedSessionState || _rqStaticObjects == null || _rqStaticObjects.NeverAccessed)
1298                         ) {
1299
1300                         Debug.Trace("SessionStateOnReleaseState", "Not storing unused new session.");
1301                     }
1302                     else if (_rqSessionState.IsAbandoned) {
1303                         Debug.Trace("SessionStateOnReleaseState", "Removing session due to abandonment, SessionId=" + _rqId);
1304
1305                         if (_rqSessionStateNotFound) {
1306                             // The store provider doesn't have it, and so we don't need to remove it from the store.
1307
1308                             // However, if the store provider supports session expiry, and we have a Session_End in global.asax,
1309                             // we need to explicitly call Session_End.
1310                             if (_supportSessionExpiry) {
1311                                 if (delayedSessionState) {
1312                                     Debug.Assert(s_allowDelayedStateStoreItemCreation, "s_allowDelayedStateStoreItemCreation");
1313                                     Debug.Assert(_rqItem == null, "_rqItem == null");
1314
1315                                     InitStateStoreItem(false /*addToContext*/);
1316                                 }
1317
1318                                 _onEndTarget.RaiseSessionOnEnd(ReleaseStateGetSessionID(), _rqItem);
1319                             }
1320                         }
1321                         else {
1322                             Debug.Assert(_rqItem != null, "_rqItem cannot null if it's not a new session");
1323
1324                             // Remove it from the store because the session is abandoned.
1325                             _store.RemoveItem(_rqContext, ReleaseStateGetSessionID(), _rqLockId, _rqItem);
1326                         }
1327                     }
1328                     else if (!_rqReadonly ||
1329                              (_rqReadonly &&
1330                               _rqIsNewSession &&
1331                               _sessionStartEventHandler != null &&
1332                               !SessionIDManagerUseCookieless)) {
1333                         // We need to save it since it isn't read-only
1334                         // See Dev10 588711: Issuing a redirect from inside of Session_Start event 
1335                         // triggers an infinite loop when using pages with read-only session state
1336
1337                         // We save it only if there is no error, and if something has changed (unless it's a new session)
1338                         if (    context.Error == null   // no error
1339                                 && (    _rqSessionStateNotFound
1340                                     || _rqSessionItems.Dirty    // SessionItems has changed.
1341                                     || (_rqStaticObjects != null && !_rqStaticObjects.NeverAccessed) // Static objects have been accessed
1342                                     || _rqItem.Timeout != _rqSessionState.Timeout   // Timeout value has changed
1343                                     )
1344                             ) {
1345
1346                             if (delayedSessionState) {
1347                                 Debug.Assert(_rqIsNewSession, "Saving a session and delayedSessionState is true: _rqIsNewSession must be true");
1348                                 Debug.Assert(s_allowDelayedStateStoreItemCreation, "Saving a session and delayedSessionState is true: s_allowDelayedStateStoreItemCreation");
1349                                 Debug.Assert(_rqItem == null, "Saving a session and delayedSessionState is true: _rqItem == null");
1350
1351                                 InitStateStoreItem(false /*addToContext*/);
1352                             }
1353
1354 #if DBG
1355                             if (_rqSessionItems.Dirty) {
1356                                 Debug.Trace("SessionStateOnReleaseState", "Setting new session due to dirty SessionItems, SessionId=" + _rqId);
1357                             }
1358                             else if (_rqStaticObjects != null && !_rqStaticObjects.NeverAccessed) {
1359                                 Debug.Trace("SessionStateOnReleaseState", "Setting new session due to accessed Static Objects, SessionId=" + _rqId);
1360                             }
1361                             else if (_rqSessionStateNotFound) {
1362                                 Debug.Trace("SessionStateOnReleaseState", "Setting new session because it's not found, SessionId=" + _rqId);
1363                             }
1364                             else {
1365                                 Debug.Trace("SessionStateOnReleaseState", "Setting new session due to options change, SessionId=" + _rqId +
1366                                             "\n\t_rq.timeout=" + _rqItem.Timeout.ToString(CultureInfo.InvariantCulture) +
1367                                             ", _rqSessionState.timeout=" + _rqSessionState.Timeout.ToString(CultureInfo.InvariantCulture));
1368                             }
1369 #endif
1370                             if (_rqItem.Timeout != _rqSessionState.Timeout) {
1371                                 _rqItem.Timeout = _rqSessionState.Timeout;
1372                             }
1373
1374                             s_sessionEverSet = true;
1375                             setItemCalled = true;
1376                             _store.SetAndReleaseItemExclusive(_rqContext, ReleaseStateGetSessionID(), _rqItem, _rqLockId, _rqSessionStateNotFound);
1377                         }
1378                         else {
1379                             // Can't save it because of various reason.  Just release our exclusive lock on it.
1380                             Debug.Trace("SessionStateOnReleaseState", "Release exclusive lock on session, SessionId=" + _rqId);
1381
1382                             if (!_rqSessionStateNotFound) {
1383                                 Debug.Assert(_rqItem != null, "_rqItem cannot null if it's not a new session");
1384                                 _store.ReleaseItemExclusive(_rqContext, ReleaseStateGetSessionID(), _rqLockId);
1385                             }
1386                         }
1387                     }
1388 #if DBG
1389                     else {
1390                         Debug.Trace("SessionStateOnReleaseState", "Session is read-only, ignoring SessionId=" + _rqId);
1391                     }
1392 #endif
1393
1394                     Debug.Trace("SessionStateOnReleaseState", "Returning from SessionStateModule::OnReleaseState");
1395                 }
1396
1397                 if (_rqAddedCookie && !setItemCalled && context.Response.IsBuffered()) {
1398                     _idManager.RemoveSessionID(_rqContext);
1399                 }
1400
1401             }
1402             finally {
1403                 RestoreImpersonation();
1404             }
1405
1406             // WOS 1679798: PERF: Session State Module should disable EndRequest on successful cleanup
1407             bool implementsIRequiresSessionState = context.RequiresSessionState;
1408             if (HttpRuntime.UseIntegratedPipeline 
1409                 && (context.NotificationContext.CurrentNotification == RequestNotification.ReleaseRequestState) 
1410                 && (s_canSkipEndRequestCall || !implementsIRequiresSessionState)) {
1411                 context.DisableNotifications(RequestNotification.EndRequest, 0 /*postNotifications*/);
1412                 _acquireCalled = false;
1413                 _releaseCalled = false;
1414                 ResetPerRequestFields();
1415             }
1416         }
1417
1418         /*
1419          * End of request processing. Possibly does release if skipped due to errors
1420          */
1421
1422         /// <devdoc>
1423         ///    <para>[To be supplied.]</para>
1424         /// </devdoc>
1425         void OnEndRequest(Object source, EventArgs eventArgs) {
1426             HttpApplication app;
1427             HttpContext context;
1428             String id;
1429
1430             Debug.Trace("SessionStateOnEndRequest", "Beginning SessionStateModule::OnEndRequest");
1431
1432             app = (HttpApplication)source;
1433             context = app.Context;
1434
1435             /* determine if the request requires state at all */
1436             if (!context.RequiresSessionState) {
1437                 return;
1438             }
1439
1440             ChangeImpersonation(context, false);
1441
1442             try {
1443                 if (!_releaseCalled) {
1444                     if (_acquireCalled) {
1445                         /*
1446                          * need to do release here if the request short-circuited due to an error
1447                          */
1448                         OnReleaseState(source, eventArgs);
1449                     }
1450                     else {
1451                         /*
1452                          * 'advise' -- update session timeout
1453                          */
1454
1455                         if (_rqContext == null) {
1456                             _rqContext = context;
1457                         }
1458
1459                         // We haven't called BeginAcquireState.  So we have to call these InitializeRequest
1460                         // methods here.
1461                         bool    dummy;
1462                         _store.InitializeRequest(_rqContext);
1463                         _idManager.InitializeRequest(_rqContext, true, out dummy);
1464
1465                         id = _idManager.GetSessionID(context);
1466                         if (id != null) {
1467                             Debug.Trace("SessionStateOnEndRequest", "Resetting timeout for SessionId=" + id);
1468                             _store.ResetItemTimeout(context, id);
1469                         }
1470 #if DBG
1471                         else {
1472                             Debug.Trace("SessionStateOnEndRequest", "No session id found.");
1473                         }
1474 #endif
1475                     }
1476                 }
1477
1478                 /* Notify the store we are finishing a request */
1479                 _store.EndRequest(_rqContext);
1480             }
1481             finally {
1482                 _acquireCalled = false;
1483                 _releaseCalled = false;
1484                 RestoreImpersonation();
1485                 ResetPerRequestFields();
1486             }
1487
1488             Debug.Trace("SessionStateOnEndRequest", "Returning from SessionStateModule::OnEndRequest");
1489         }
1490
1491         internal static void ReadConnectionString(SessionStateSection config, ref string cntString, string propName) {
1492             ConfigsHelper.GetRegistryStringAttribute(ref cntString, config, propName);
1493             HandlerBase.CheckAndReadConnectionString(ref cntString, true);
1494         }
1495
1496         internal bool SessionIDManagerUseCookieless {
1497             get {
1498                 // See VSWhidbey 399907
1499                 if (!_usingAspnetSessionIdManager) {
1500                     return s_configCookieless == HttpCookieMode.UseUri;
1501                 }
1502                 else {
1503                     return ((SessionIDManager)_idManager).UseCookieless(_rqContext);
1504                 }
1505             }
1506         }
1507         
1508         public void ReleaseSessionState(HttpContext context) {
1509             if (HttpRuntime.UseIntegratedPipeline && _acquireCalled && !_releaseCalled) {
1510                 try {
1511                     OnReleaseState(context.ApplicationInstance, null);
1512                 }
1513                 catch { }
1514             }
1515         }
1516
1517         public Task ReleaseSessionStateAsync(HttpContext context) {
1518             ReleaseSessionState(context);
1519             return TaskAsyncHelper.CompletedTask;
1520         }
1521     }
1522 }