1 //------------------------------------------------------------------------------
2 // <copyright file="StateRuntime.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 /*
8  * StateWebRuntime
9  *
10  * Copyright (c) 1998-1999, Microsoft Corporation
11  *
12  */
13 
14 namespace System.Web.SessionState {
15     using System.Configuration;
16     using System.Globalization;
17     using System.IO;
18     using System.Runtime.InteropServices;
19     using System.Security.Permissions;
20     using System.Threading;
21     using System.Web;
22     using System.Web.Caching;
23     using System.Web.Configuration;
24     using System.Web.Util;
25 
26 
27     /// <internalonly/>
28     /// <devdoc>
29     /// </devdoc>
30     [ComImport, Guid("7297744b-e188-40bf-b7e9-56698d25cf44"), System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
31     public interface IStateRuntime {
32 
33         /// <devdoc>
34         ///    <para>[To be supplied.]</para>
35         /// </devdoc>
36         [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
37         [SecurityPermission(SecurityAction.InheritanceDemand, Unrestricted = true)]
StopProcessing()38         void StopProcessing();
39 
40         /// <devdoc>
41         ///    <para>[To be supplied.]</para>
42         /// </devdoc>
43 
44         [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
45         [SecurityPermission(SecurityAction.InheritanceDemand, Unrestricted = true)]
ProcessRequest( [In, MarshalAs(UnmanagedType.SysInt)] IntPtr tracker, [In, MarshalAs(UnmanagedType.I4)] int verb, [In, MarshalAs(UnmanagedType.LPWStr)] string uri, [In, MarshalAs(UnmanagedType.I4)] int exclusive, [In, MarshalAs(UnmanagedType.I4)] int timeout, [In, MarshalAs(UnmanagedType.I4)] int lockCookieExists, [In, MarshalAs(UnmanagedType.I4)] int lockCookie, [In, MarshalAs(UnmanagedType.I4)] int contentLength, [In, MarshalAs(UnmanagedType.SysInt)] IntPtr content)46         void ProcessRequest(
47                [In, MarshalAs(UnmanagedType.SysInt)]
48                IntPtr tracker,
49                [In, MarshalAs(UnmanagedType.I4)]
50                int verb,
51                [In, MarshalAs(UnmanagedType.LPWStr)]
52                string uri,
53                [In, MarshalAs(UnmanagedType.I4)]
54                int exclusive,
55                [In, MarshalAs(UnmanagedType.I4)]
56                int timeout,
57                [In, MarshalAs(UnmanagedType.I4)]
58                int lockCookieExists,
59                [In, MarshalAs(UnmanagedType.I4)]
60                int lockCookie,
61                [In, MarshalAs(UnmanagedType.I4)]
62                int contentLength,
63                [In, MarshalAs(UnmanagedType.SysInt)]
64                IntPtr content);
65 
66         [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
67         [SecurityPermission(SecurityAction.InheritanceDemand, Unrestricted = true)]
ProcessRequest( [In, MarshalAs(UnmanagedType.SysInt)] IntPtr tracker, [In, MarshalAs(UnmanagedType.I4)] int verb, [In, MarshalAs(UnmanagedType.LPWStr)] string uri, [In, MarshalAs(UnmanagedType.I4)] int exclusive, [In, MarshalAs(UnmanagedType.I4)] int extraFlags, [In, MarshalAs(UnmanagedType.I4)] int timeout, [In, MarshalAs(UnmanagedType.I4)] int lockCookieExists, [In, MarshalAs(UnmanagedType.I4)] int lockCookie, [In, MarshalAs(UnmanagedType.I4)] int contentLength, [In, MarshalAs(UnmanagedType.SysInt)] IntPtr content)68         void ProcessRequest(
69                [In, MarshalAs(UnmanagedType.SysInt)]
70                IntPtr tracker,
71                [In, MarshalAs(UnmanagedType.I4)]
72                int verb,
73                [In, MarshalAs(UnmanagedType.LPWStr)]
74                string uri,
75                [In, MarshalAs(UnmanagedType.I4)]
76                int exclusive,
77                [In, MarshalAs(UnmanagedType.I4)]
78                int extraFlags,
79                [In, MarshalAs(UnmanagedType.I4)]
80                int timeout,
81                [In, MarshalAs(UnmanagedType.I4)]
82                int lockCookieExists,
83                [In, MarshalAs(UnmanagedType.I4)]
84                int lockCookie,
85                [In, MarshalAs(UnmanagedType.I4)]
86                int contentLength,
87                [In, MarshalAs(UnmanagedType.SysInt)]
88                IntPtr content);
89 
90     }
91 
92 
93     /// <internalonly/>
94     /// <devdoc>
95     /// </devdoc>
96     [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
97     public sealed class StateRuntime : IStateRuntime {
StateRuntime()98         static StateRuntime() {
99             WebConfigurationFileMap webFileMap = new WebConfigurationFileMap();
100             UserMapPath mapPath = new UserMapPath(webFileMap);
101             HttpConfigurationSystem.EnsureInit(mapPath, false, true);
102 
103             StateApplication app = new StateApplication();
104 
105             HttpApplicationFactory.SetCustomApplication(app);
106 
107             PerfCounters.OpenStateCounters();
108             ResetStateServerCounters();
109         }
110 
111 
112         /// <devdoc>
113         ///    <para>
114         ///       Initializes a new instance of the <see cref='System.Web.State.StateRuntime'/>
115         ///       class.
116         ///     </para>
117         /// </devdoc>
StateRuntime()118         public StateRuntime() {
119         }
120 
121         /*
122          * Shutdown runtime
123          */
124 
125         /// <devdoc>
126         ///    <para>[To be supplied.]</para>
127         /// </devdoc>
StopProcessing()128         public void StopProcessing() {
129             ResetStateServerCounters();
130             HttpRuntime.Close();
131         }
132 
ResetStateServerCounters()133         static void ResetStateServerCounters() {
134             PerfCounters.SetStateServiceCounter(StateServicePerfCounter.STATE_SERVICE_SESSIONS_TOTAL, 0);
135             PerfCounters.SetStateServiceCounter(StateServicePerfCounter.STATE_SERVICE_SESSIONS_ACTIVE, 0);
136             PerfCounters.SetStateServiceCounter(StateServicePerfCounter.STATE_SERVICE_SESSIONS_TIMED_OUT, 0);
137             PerfCounters.SetStateServiceCounter(StateServicePerfCounter.STATE_SERVICE_SESSIONS_ABANDONED, 0);
138         }
139 
ProcessRequest( IntPtr tracker, int verb, string uri, int exclusive, int timeout, int lockCookieExists, int lockCookie, int contentLength, IntPtr content )140         public void ProcessRequest(
141                   IntPtr tracker,
142                   int verb,
143                   string uri,
144                   int exclusive,
145                   int timeout,
146                   int lockCookieExists,
147                   int lockCookie,
148                   int contentLength,
149                   IntPtr content
150                   ) {
151             ProcessRequest(
152                   tracker,
153                   verb,
154                   uri,
155                   exclusive,
156                   0,
157                   timeout,
158                   lockCookieExists,
159                   lockCookie,
160                   contentLength,
161                   content);
162         }
163 
164         /*
165          * Process one ISAPI request
166          *
167          * @param ecb ECB
168          */
169 
170         /// <devdoc>
171         ///    <para>[To be supplied.]</para>
172         /// </devdoc>
ProcessRequest( IntPtr tracker, int verb, string uri, int exclusive, int extraFlags, int timeout, int lockCookieExists, int lockCookie, int contentLength, IntPtr content )173         public void ProcessRequest(
174                   IntPtr tracker,
175                   int verb,
176                   string uri,
177                   int exclusive,
178                   int extraFlags,
179                   int timeout,
180                   int lockCookieExists,
181                   int lockCookie,
182                   int contentLength,
183                   IntPtr content
184                   ) {
185 
186             StateHttpWorkerRequest  wr;
187 
188             wr = new StateHttpWorkerRequest(
189                        tracker, (UnsafeNativeMethods.StateProtocolVerb) verb, uri,
190                        (UnsafeNativeMethods.StateProtocolExclusive) exclusive, extraFlags, timeout,
191                        lockCookieExists, lockCookie, contentLength, content);
192 
193             HttpRuntime.ProcessRequest(wr);
194         }
195     }
196 
197     internal static class StateHeaders {
198         internal const String EXCLUSIVE_NAME = "Http_Exclusive";
199         internal const String EXCLUSIVE_VALUE_ACQUIRE = "acquire";
200         internal const String EXCLUSIVE_VALUE_RELEASE = "release";
201         internal const String TIMEOUT_NAME = "Http_Timeout";
202         internal const String TIMEOUT_NAME_RAW = "Timeout";
203         internal const String LOCKCOOKIE_NAME = "Http_LockCookie";
204         internal const String LOCKCOOKIE_NAME_RAW = "LockCookie";
205         internal const String LOCKDATE_NAME = "Http_LockDate";
206         internal const String LOCKDATE_NAME_RAW = "LockDate";
207         internal const String LOCKAGE_NAME = "Http_LockAge";
208         internal const String LOCKAGE_NAME_RAW = "LockAge";
209         internal const String EXTRAFLAGS_NAME = "Http_ExtraFlags";
210         internal const String EXTRAFLAGS_NAME_RAW = "ExtraFlags";
211         internal const String ACTIONFLAGS_NAME = "Http_ActionFlags";
212         internal const String ACTIONFLAGS_NAME_RAW = "ActionFlags";
213     };
214 
215     internal sealed class CachedContent {
216         internal byte[]             _content;
217         internal IntPtr             _stateItem; // The pointer to the native memory that points to the psi
218         internal bool               _locked;
219         internal DateTime           _utcLockDate;
220         internal TimeSpan           _slidingExpiration;
221         internal int                _lockCookie;
222         internal int                _extraFlags;
223         #pragma warning disable 0649
224         internal ReadWriteSpinLock  _spinLock;
225         #pragma warning restore 0649
226 
CachedContent( byte [] content, IntPtr stateItem, bool locked, DateTime utcLockDate, TimeSpan slidingExpiration, int lockCookie, int extraFlags)227         internal CachedContent(
228                 byte []     content,
229                 IntPtr      stateItem,
230                 bool        locked,
231                 DateTime    utcLockDate,
232                 TimeSpan    slidingExpiration,
233                 int         lockCookie,
234                 int         extraFlags) {
235 
236             _content = content;
237             _stateItem = stateItem;
238             _locked = locked;
239             _utcLockDate = utcLockDate;
240             _slidingExpiration = slidingExpiration;
241             _lockCookie = lockCookie;
242             _extraFlags = extraFlags;
243         }
244     }
245 
246     internal class StateApplication : IHttpHandler {
247         CacheItemRemovedCallback _removedHandler;
248 
StateApplication()249         internal StateApplication() {
250             if (!HttpRuntime.IsFullTrust) {
251                 // DevDiv #89021: This type passes user-supplied data to unmanaged code, so we need
252                 // to ensure that it can only be used from within a FullTrust environment.
253                 throw new InvalidOperationException(SR.GetString(SR.StateApplication_FullTrustOnly));
254             }
255 
256             _removedHandler = new CacheItemRemovedCallback(this.OnCacheItemRemoved);
257         }
258 
ProcessRequest(HttpContext context)259         public void ProcessRequest(HttpContext context) {
260             // Don't send content-type header.
261             context.Response.ContentType = null;
262 
263             switch (context.Request.HttpVerb) {
264                 case HttpVerb.GET:
265                     DoGet(context);
266                     break;
267 
268                 case HttpVerb.PUT:
269                     DoPut(context);
270                     break;
271 
272                 case HttpVerb.HEAD:
273                     DoHead(context);
274                     break;
275 
276                 case HttpVerb.DELETE:
277                     DoDelete(context);
278                     break;
279 
280                 default:
281                     DoUnknown(context);
282                     break;
283             }
284         }
285 
286         public bool IsReusable {
287             get { return true; }
288         }
289 
CreateKey(HttpRequest request)290         private string CreateKey(HttpRequest request) {
291             return CacheInternal.PrefixStateApplication + HttpUtility.UrlDecode(request.RawUrl);
292         }
293 
ReportInvalidHeader(HttpContext context, String header)294         private void ReportInvalidHeader(HttpContext context, String header) {
295             HttpResponse    response;
296 
297             response = context.Response;
298             response.StatusCode = 400;
299             response.Write("<html><head><title>Bad Request</title></head>\r\n");
300             response.Write("<body><h1>Http/1.1 400 Bad Request</h1>");
301             response.Write("Invalid header <b>" + header + "</b></body></html>");
302         }
303 
ReportLocked(HttpContext context, CachedContent content)304         private void ReportLocked(HttpContext context, CachedContent content) {
305             HttpResponse    response;
306             DateTime        localLockDate;
307             long            lockAge;
308 
309             // Note that due to a bug in the RTM state server client,
310             // we cannot add to body of the response when sending this
311             // message, otherwise the client will leak memory.
312             response = context.Response;
313             response.StatusCode = 423;
314             localLockDate = DateTimeUtil.ConvertToLocalTime(content._utcLockDate);
315             lockAge = (DateTime.UtcNow - content._utcLockDate).Ticks / TimeSpan.TicksPerSecond;
316             response.AppendHeader(StateHeaders.LOCKDATE_NAME_RAW, localLockDate.Ticks.ToString(CultureInfo.InvariantCulture));
317             response.AppendHeader(StateHeaders.LOCKAGE_NAME_RAW, lockAge.ToString(CultureInfo.InvariantCulture));
318             response.AppendHeader(StateHeaders.LOCKCOOKIE_NAME_RAW, content._lockCookie.ToString(CultureInfo.InvariantCulture));
319         }
320 
ReportActionFlags(HttpContext context, int flags)321         private void ReportActionFlags(HttpContext context, int flags) {
322             HttpResponse    response;
323 
324             // Note that due to a bug in the RTM state server client,
325             // we cannot add to body of the response when sending this
326             // message, otherwise the client will leak memory.
327             response = context.Response;
328             response.AppendHeader(StateHeaders.ACTIONFLAGS_NAME_RAW, flags.ToString(CultureInfo.InvariantCulture));
329         }
330 
ReportNotFound(HttpContext context)331         private void ReportNotFound(HttpContext context) {
332             context.Response.StatusCode = 404;
333         }
334 
GetOptionalNonNegativeInt32HeaderValue(HttpContext context, string header, out int value)335         bool GetOptionalNonNegativeInt32HeaderValue(HttpContext context, string header, out int value)
336         {
337             bool headerValid;
338             string valueAsString;
339 
340             value = -1;
341             valueAsString = context.Request.Headers[header];
342             if (valueAsString == null) {
343                 headerValid = true;
344             }
345             else {
346                 headerValid = false;
347                 try {
348                     value = Int32.Parse(valueAsString, CultureInfo.InvariantCulture);
349                     if (value >= 0) {
350                         headerValid = true;
351                     }
352                 }
353                 catch {
354                 }
355             }
356 
357             if (!headerValid) {
358                 ReportInvalidHeader(context, header);
359             }
360 
361             return headerValid;
362         }
363 
GetRequiredNonNegativeInt32HeaderValue(HttpContext context, string header, out int value)364         bool GetRequiredNonNegativeInt32HeaderValue(HttpContext context, string header, out int value)
365         {
366             bool headerValid = GetOptionalNonNegativeInt32HeaderValue(context, header, out value);
367             if (headerValid && value == -1) {
368                 headerValid = false;
369                 ReportInvalidHeader(context, header);
370             }
371 
372             return headerValid;
373         }
374 
GetOptionalInt32HeaderValue(HttpContext context, string header, out int value, out bool found)375         bool GetOptionalInt32HeaderValue(HttpContext context, string header, out int value, out bool found)
376         {
377             bool headerValid;
378             string valueAsString;
379 
380             found = false;
381 
382             value = 0;
383             valueAsString = context.Request.Headers[header];
384             if (valueAsString == null) {
385                 headerValid = true;
386             }
387             else {
388                 headerValid = false;
389                 try {
390                     value = Int32.Parse(valueAsString, CultureInfo.InvariantCulture);
391                     headerValid = true;
392                     found = true;
393                 }
394                 catch {
395                 }
396             }
397 
398             if (!headerValid) {
399                 ReportInvalidHeader(context, header);
400             }
401 
402             return headerValid;
403         }
404 
405         /*
406          * Check Exclusive header for get, getexlusive, releaseexclusive
407          * use the path as the id
408          * Create the cache key
409          * follow inproc.
410          */
DoGet(HttpContext context)411         internal /*public*/ void DoGet(HttpContext context) {
412             HttpRequest     request = context.Request;
413             HttpResponse    response = context.Response;
414             Stream          responseStream;
415             byte[]          buf;
416             string          exclusiveAccess;
417             string          key;
418             CachedContent   content;
419             int             lockCookie;
420             int             timeout;
421 
422             key = CreateKey(request);
423             content = (CachedContent) HttpRuntime.Cache.InternalCache.Get(key);
424             if (content == null) {
425                 ReportNotFound(context);
426                 return;
427             }
428 
429             exclusiveAccess = request.Headers[StateHeaders.EXCLUSIVE_NAME];
430             content._spinLock.AcquireWriterLock();
431             try {
432                 if (content._content == null) {
433                     ReportNotFound(context);
434                     return;
435                 }
436 
437                 int initialFlags;
438 
439                 initialFlags = content._extraFlags;
440                 if ((initialFlags & (int)SessionStateItemFlags.Uninitialized) != 0) {
441                     // It is an uninitialized item.  We have to remove that flag.
442                     // We only allow one request to do that.
443                     // For details, see inline doc for SessionStateItemFlags.Uninitialized flag.
444 
445                     // If initialFlags != return value of CompareExchange, it means another request has
446                     // removed the flag.
447 
448                     if (initialFlags == Interlocked.CompareExchange(
449                                             ref content._extraFlags,
450                                             initialFlags & (~((int)SessionStateItemFlags.Uninitialized)),
451                                             initialFlags)) {
452                         ReportActionFlags(context, (int)SessionStateActions.InitializeItem);
453                     }
454                 }
455 
456                 if (exclusiveAccess == StateHeaders.EXCLUSIVE_VALUE_RELEASE) {
457                     if (!GetRequiredNonNegativeInt32HeaderValue(context, StateHeaders.LOCKCOOKIE_NAME, out lockCookie))
458                         return;
459 
460                     if (content._locked) {
461                         if (lockCookie == content._lockCookie) {
462                             content._locked = false;
463                         }
464                         else {
465                             ReportLocked(context, content);
466                         }
467                     }
468                     else {
469                         // should be locked but isn't.
470                         context.Response.StatusCode = 200;
471                     }
472                 }
473                 else {
474                     if (content._locked) {
475                         ReportLocked(context, content);
476                         return;
477                     }
478 
479                     if (exclusiveAccess == StateHeaders.EXCLUSIVE_VALUE_ACQUIRE) {
480                         content._locked = true;
481                         content._utcLockDate = DateTime.UtcNow;
482                         content._lockCookie++;
483 
484                         response.AppendHeader(StateHeaders.LOCKCOOKIE_NAME_RAW, (content._lockCookie).ToString(CultureInfo.InvariantCulture));
485                     }
486 
487                     timeout = (int)(content._slidingExpiration.Ticks / TimeSpan.TicksPerMinute);
488                     response.AppendHeader(StateHeaders.TIMEOUT_NAME_RAW, (timeout).ToString(CultureInfo.InvariantCulture));
489                     responseStream = response.OutputStream;
490                     buf = content._content;
491                     responseStream.Write(buf, 0, buf.Length);
492                     response.Flush();
493                 }
494             }
495             finally {
496                 content._spinLock.ReleaseWriterLock();
497             }
498         }
499 
500 
DoPut(HttpContext context)501         internal /*public*/ void DoPut(HttpContext context) {
502             IntPtr  stateItemDelete;
503 
504             stateItemDelete = FinishPut(context);
505             if (stateItemDelete != IntPtr.Zero) {
506                 UnsafeNativeMethods.STWNDDeleteStateItem(stateItemDelete);
507             }
508         }
509 
FinishPut(HttpContext context)510         unsafe IntPtr FinishPut(HttpContext context) {
511             HttpRequest         request = context.Request;
512             HttpResponse        response = context.Response;
513             Stream              requestStream;
514             byte[]              buf;
515             int                 timeoutMinutes;
516             TimeSpan            timeout;
517             int                 extraFlags;
518             string              key;
519             CachedContent       content;
520             CachedContent       contentCurrent;
521             int                 lockCookie;
522             int                 lockCookieNew = 1;
523             IntPtr              stateItem;
524             CacheStoreProvider         cacheInternal = HttpRuntime.Cache.InternalCache;
525 
526             /* create the content */
527             requestStream = request.InputStream;
528             int bufferSize = (int)(requestStream.Length - requestStream.Position);
529             buf = new byte[bufferSize];
530             requestStream.Read(buf, 0 , buf.Length);
531 
532             fixed (byte * pBuf = buf) {
533                 // The ctor of StateHttpWorkerRequest convert the native pointer address
534                 // into an array of bytes, and in our we revert it back to an IntPtr
535                 stateItem = (IntPtr)(*((void **)pBuf));
536             }
537 
538             /* get headers */
539             if (!GetOptionalNonNegativeInt32HeaderValue(context, StateHeaders.TIMEOUT_NAME, out timeoutMinutes)) {
540                 return stateItem;
541             }
542 
543             if (timeoutMinutes == -1) {
544                 timeoutMinutes = SessionStateModule.TIMEOUT_DEFAULT;
545             }
546 
547             if (timeoutMinutes > SessionStateModule.MAX_CACHE_BASED_TIMEOUT_MINUTES) {
548                 ReportInvalidHeader(context, StateHeaders.TIMEOUT_NAME);
549                 return stateItem;
550             }
551 
552             timeout = new TimeSpan(0, timeoutMinutes, 0);
553 
554             bool found;
555             if (!GetOptionalInt32HeaderValue(context, StateHeaders.EXTRAFLAGS_NAME, out extraFlags, out found)) {
556                 return stateItem;
557             }
558 
559             if (!found) {
560                 extraFlags = 0;
561             }
562 
563             /* lookup current value */
564             key = CreateKey(request);
565             contentCurrent = (CachedContent) cacheInternal.Get(key);
566             if (contentCurrent != null) {
567                 // DevDivBugs 146875: Expired Session State race condition
568                 // We make sure we do not overwrite an already existing item with an uninitialized item.
569                 if (((int)SessionStateItemFlags.Uninitialized & extraFlags) == 1) {
570                     return stateItem;
571                 }
572 
573                 if (!GetOptionalNonNegativeInt32HeaderValue(context, StateHeaders.LOCKCOOKIE_NAME, out lockCookie)) {
574                     return stateItem;
575                 }
576 
577                 contentCurrent._spinLock.AcquireWriterLock();
578                 try {
579                     if (contentCurrent._content == null) {
580                         ReportNotFound(context);
581                         return stateItem;
582                     }
583 
584                     /* Only set the item if we are the owner */
585                     if (contentCurrent._locked && (lockCookie == -1 || lockCookie != contentCurrent._lockCookie)) {
586                         ReportLocked(context, contentCurrent);
587                         return stateItem;
588                     }
589 
590                     if (contentCurrent._slidingExpiration == timeout && contentCurrent._content != null) {
591                         /* delete the old state item */
592                         IntPtr stateItemOld = contentCurrent._stateItem;
593 
594                         /* change the item in place */
595                         contentCurrent._content = buf;
596                         contentCurrent._stateItem = stateItem;
597                         contentCurrent._locked = false;
598                         return stateItemOld;
599                     }
600 
601                     /*
602                         The timeout has changed.  In this case, we are removing the old item and
603                         inserting a new one.
604                         Update _extraFlags to ignore the cache item removed callback (this way,
605                         we will not decrease the number of active sessions).
606                      */
607                     contentCurrent._extraFlags |= (int)SessionStateItemFlags.IgnoreCacheItemRemoved;
608 
609                     /*
610                      * If not locked, keep it locked until it is completely replaced.
611                      * Prevent overwriting when we drop the lock.
612                      */
613                     contentCurrent._locked = true;
614                     contentCurrent._lockCookie = 0;
615                     lockCookieNew = lockCookie;
616                 }
617                 finally {
618                     contentCurrent._spinLock.ReleaseWriterLock();
619                 }
620             }
621 
622             content = new CachedContent(buf, stateItem, false, DateTime.MinValue, timeout, lockCookieNew, extraFlags);
623             cacheInternal.Insert(key, content, new CacheInsertOptions() {
624                                                     SlidingExpiration = timeout,
625                                                     Priority = CacheItemPriority.NotRemovable,
626                                                     OnRemovedCallback = _removedHandler
627                                                 });
628 
629             if (contentCurrent == null) {
630                 IncrementStateServiceCounter(StateServicePerfCounter.STATE_SERVICE_SESSIONS_TOTAL);
631                 IncrementStateServiceCounter(StateServicePerfCounter.STATE_SERVICE_SESSIONS_ACTIVE);
632             }
633 
634             return IntPtr.Zero;
635         }
636 
DoDelete(HttpContext context)637         internal /*public*/ void DoDelete(HttpContext context) {
638             string          key = CreateKey(context.Request);
639             CacheStoreProvider     cacheInternal = HttpRuntime.Cache.InternalCache;
640             CachedContent   content = (CachedContent) cacheInternal.Get(key);
641 
642             /* If the item isn't there, we probably took too long to run. */
643             if (content == null) {
644                 ReportNotFound(context);
645                 return;
646             }
647 
648             int lockCookie;
649             if (!GetOptionalNonNegativeInt32HeaderValue(context, StateHeaders.LOCKCOOKIE_NAME, out lockCookie))
650                 return;
651 
652             content._spinLock.AcquireWriterLock();
653             try {
654                 if (content._content == null) {
655                     ReportNotFound(context);
656                     return;
657                 }
658 
659                 /* Only remove the item if we are the owner */
660                 if (content._locked && (lockCookie == -1 || content._lockCookie != lockCookie)) {
661                     ReportLocked(context, content);
662                     return;
663                 }
664 
665                 /*
666                  * If not locked, keep it locked until it is completely removed.
667                  * Prevent overwriting when we drop the lock.
668                  */
669                 content._locked = true;
670                 content._lockCookie = 0;
671             }
672             finally {
673                 content._spinLock.ReleaseWriterLock();
674             }
675 
676 
677             cacheInternal.Remove(key);
678         }
679 
DoHead(HttpContext context)680         internal /*public*/ void DoHead(HttpContext context) {
681             string  key;
682             Object  item;
683 
684             key = CreateKey(context.Request);
685             item = HttpRuntime.Cache.InternalCache.Get(key);
686             if (item == null) {
687                 ReportNotFound(context);
688             }
689         }
690 
691         /*
692          * Unknown Http verb. Responds with "400 Bad Request".
693          * Override this method to report different Http code.
694          */
DoUnknown(HttpContext context)695         internal /*public*/ void DoUnknown(HttpContext context) {
696             context.Response.StatusCode = 400;
697         }
698 
OnCacheItemRemoved(String key, Object value, CacheItemRemovedReason reason)699         unsafe void OnCacheItemRemoved(String key, Object value, CacheItemRemovedReason reason) {
700             CachedContent   content;
701             IntPtr          stateItem;
702 
703             content = (CachedContent) value;
704 
705             content._spinLock.AcquireWriterLock();
706             try {
707                 stateItem = content._stateItem;
708                 content._content = null;
709                 content._stateItem = IntPtr.Zero;
710             }
711             finally {
712                 content._spinLock.ReleaseWriterLock();
713             }
714 
715             UnsafeNativeMethods.STWNDDeleteStateItem(stateItem);
716 
717             /* If _extraFlags have IgnoreCacheItemRemoved specified,
718                 don't update the counters.
719              */
720             if ((content._extraFlags & (int)SessionStateItemFlags.IgnoreCacheItemRemoved) != 0) {
721                 Debug.Trace("OnCacheItemRemoved", "OnCacheItemRemoved ignored (item removed, but counters not updated)");
722                 return;
723             }
724 
725             switch (reason) {
726                 case CacheItemRemovedReason.Expired:
727                     IncrementStateServiceCounter(StateServicePerfCounter.STATE_SERVICE_SESSIONS_TIMED_OUT);
728                     break;
729 
730                 case CacheItemRemovedReason.Removed:
731                     IncrementStateServiceCounter(StateServicePerfCounter.STATE_SERVICE_SESSIONS_ABANDONED);
732                     break;
733 
734                 default:
735                     break;
736             }
737 
738             DecrementStateServiceCounter(StateServicePerfCounter.STATE_SERVICE_SESSIONS_ACTIVE);
739         }
740 
DecrementStateServiceCounter(StateServicePerfCounter counter)741         private void DecrementStateServiceCounter(StateServicePerfCounter counter) {
742             if (HttpRuntime.ShutdownInProgress) {
743                 return;
744             }
745 
746             PerfCounters.DecrementStateServiceCounter(counter);
747         }
748 
IncrementStateServiceCounter(StateServicePerfCounter counter)749         private void IncrementStateServiceCounter(StateServicePerfCounter counter) {
750             if (HttpRuntime.ShutdownInProgress) {
751                 return;
752             }
753 
754             PerfCounters.IncrementStateServiceCounter(counter);
755         }
756 
757     }
758 }
759