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