The Labs \ Source Viewer \ SSCLI \ System.Net.Cache \ RequestCacheProtocol

  1. //------------------------------------------------------------------------------
  2. // <copyright file="_RequestCacheProtocol.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. /*++
  16. Abstract:
  17.     The class is a cache protocol engine.
  18.     An application protocol such as HttpWebRequest or FtpWebRequest
  19.     gets all cache-related answers by talking to this class
  20.     Sometime in the future it will become public.
  21. Author:
  22.     Alexei Vopilov    21-Dec-2002
  23. Revision History:
  24.     Aug 25 2003 - moved into separate file and revised as per Whidbey-M3 spec.
  25. --*/
  26. namespace System.Net.Cache
  27. {
  28.     using System;
  29.     using System.Diagnostics;
  30.     using System.Text;
  31.     using System.IO;
  32.     using System.Collections.Specialized;
  33.     using System.Threading;
  34.     using System.Globalization;
  35.    
  36.     //
  37.     //
  38.     //
  39.     internal class RequestCacheProtocol
  40.     {
  41.        
  42.         private CacheValidationStatus _ProtocolStatus;
  43.         private Exception _ProtocolException;
  44.         private Stream _ResponseStream;
  45.         private long _ResponseStreamLength;
  46.         private RequestCacheValidator _Validator;
  47.         private RequestCache _RequestCache;
  48.         private bool _IsCacheFresh;
  49.         private bool _CanTakeNewRequest;
  50.        
  51.         // private string[] _ResponseMetadata;
  52.         // private string _CacheRetrieveKey;
  53.         // private string _CacheStoreKey;
  54.        
  55.         //
  56.         // Public properties
  57.         //
  58.         internal CacheValidationStatus ProtocolStatus {
  59.             get { return _ProtocolStatus; }
  60.         }
  61.         internal Exception ProtocolException {
  62.             get { return _ProtocolException; }
  63.         }
  64.         internal Stream ResponseStream {
  65.             get { return _ResponseStream; }
  66.         }
  67.         internal long ResponseStreamLength {
  68.             get { return _ResponseStreamLength; }
  69.         }
  70.         internal RequestCacheValidator Validator {
  71.             get { return _Validator; }
  72.         }
  73.         internal bool IsCacheFresh {
  74.             get { return _Validator != null && _Validator.CacheFreshnessStatus == CacheFreshnessStatus.Fresh; }
  75.         }
  76.        
  77.         // internal string[] ResponseMetadata {get {return _ResponseMetadata;}}
  78.         // internal string CacheRetrieveKey {get {return _CacheRetrieveKey;}}
  79.         // internal string CacheStoreKey {get {return _CacheStoreKey;}}
  80.        
  81.         //
  82.         // Public methods
  83.         //
  84.         internal RequestCacheProtocol(RequestCache cache, RequestCacheValidator defaultValidator)
  85.         {
  86.             _RequestCache = cache;
  87.             _Validator = defaultValidator;
  88.             _CanTakeNewRequest = true;
  89.         }
  90.         //
  91.         internal CacheValidationStatus GetRetrieveStatus(Uri cacheUri, WebRequest request)
  92.         {
  93.            
  94.             if (cacheUri == null)
  95.                 throw new ArgumentNullException("cacheUri");
  96.            
  97.             if (request == null)
  98.                 throw new ArgumentNullException("request");
  99.            
  100.             if (!_CanTakeNewRequest || _ProtocolStatus == CacheValidationStatus.RetryResponseFromServer)
  101.                 return CacheValidationStatus.Continue;
  102.             _CanTakeNewRequest = false;
  103.            
  104.            
  105.             // Reset protocol state
  106.             _ResponseStream = null;
  107.             _ResponseStreamLength = 0l;
  108.             _ProtocolStatus = CacheValidationStatus.Continue;
  109.             _ProtocolException = null;
  110.            
  111.             if (Logging.On)
  112.                 Logging.Enter(Logging.RequestCache, this, "GetRetrieveStatus", request);
  113.             try {
  114.                 if (request.CachePolicy == null || request.CachePolicy.Level == RequestCacheLevel.BypassCache) {
  115.                     _ProtocolStatus = CacheValidationStatus.DoNotUseCache;
  116.                     return _ProtocolStatus;
  117.                 }
  118.                
  119.                 if (_RequestCache == null || _Validator == null) {
  120.                     _ProtocolStatus = CacheValidationStatus.DoNotUseCache;
  121.                     return _ProtocolStatus;
  122.                 }
  123.                
  124.                 _Validator.FetchRequest(cacheUri, request);
  125.                
  126.                 switch (_ProtocolStatus = ValidateRequest()) {
  127.                     case CacheValidationStatus.Continue:
  128.                         // This is a green light for cache protocol
  129.                         break;
  130.                     case CacheValidationStatus.DoNotTakeFromCache:
  131.                     case CacheValidationStatus.DoNotUseCache:
  132.                        
  133.                         // no cache but response can be cached
  134.                         // ignore cache entirely
  135.                         break;
  136.                     case CacheValidationStatus.Fail:
  137.                        
  138.                         _ProtocolException = new InvalidOperationException(SR.GetString(SR.net_cache_validator_fail, "ValidateRequest"));
  139.                         break;
  140.                     default:
  141.                        
  142.                         _ProtocolStatus = CacheValidationStatus.Fail;
  143.                         _ProtocolException = new InvalidOperationException(SR.GetString(SR.net_cache_validator_result, "ValidateRequest", _Validator.ValidationStatus.ToString()));
  144.                         if (Logging.On)
  145.                             Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_unexpected_status, "ValidateRequest()", _Validator.ValidationStatus.ToString()));
  146.                         break;
  147.                 }
  148.                
  149.                 if (_ProtocolStatus != CacheValidationStatus.Continue)
  150.                     return _ProtocolStatus;
  151.                
  152.                 //
  153.                 // Proceed with validation
  154.                 //
  155.                 CheckRetrieveBeforeSubmit();
  156.             }
  157.             catch (Exception e) {
  158.                 _ProtocolException = e;
  159.                 _ProtocolStatus = CacheValidationStatus.Fail;
  160.                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException)
  161.                     throw;
  162.                
  163.                 if (Logging.On)
  164.                     Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_object_and_exception, "CacheProtocol#" + this.GetHashCode().ToString(NumberFormatInfo.InvariantInfo), (e is WebException ? e.Message : e.ToString())));
  165.             }
  166.             finally {
  167.                 if (Logging.On)
  168.                     Logging.Exit(Logging.RequestCache, this, "GetRetrieveStatus", "result = " + _ProtocolStatus.ToString());
  169.             }
  170.             return _ProtocolStatus;
  171.         }
  172.         //
  173.         // This optional method is only for protocols supporting a revalidation concept
  174.         // For a retried request this method must be called again.
  175.         //
  176.         internal CacheValidationStatus GetRevalidateStatus(WebResponse response, Stream responseStream)
  177.         {
  178.             if (response == null)
  179.                 throw new ArgumentNullException("response");
  180.            
  181.             if (_ProtocolStatus == CacheValidationStatus.DoNotUseCache)
  182.                 return CacheValidationStatus.DoNotUseCache;
  183.            
  184.             // If we returned cached response, switch the state to not call cache anymore.
  185.             if (_ProtocolStatus == CacheValidationStatus.ReturnCachedResponse) {
  186.                 _ProtocolStatus = CacheValidationStatus.DoNotUseCache;
  187.                 return _ProtocolStatus;
  188.             }
  189.            
  190.             try {
  191.                 if (Logging.On)
  192.                     Logging.Enter(Logging.RequestCache, this, "GetRevalidateStatus", (_Validator == null ? null : _Validator.Request));
  193.                
  194.                 _Validator.FetchResponse(response);
  195.                
  196.                 if (_ProtocolStatus != CacheValidationStatus.Continue && _ProtocolStatus != CacheValidationStatus.RetryResponseFromServer) {
  197.                     if (Logging.On)
  198.                         Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_revalidation_not_needed, "GetRevalidateStatus()"));
  199.                     return _ProtocolStatus;
  200.                 }
  201.                 CheckRetrieveOnResponse(responseStream);
  202.             }
  203.             finally {
  204.                 if (Logging.On)
  205.                     Logging.Exit(Logging.RequestCache, this, "GetRevalidateStatus", "result = " + _ProtocolStatus.ToString());
  206.             }
  207.             return _ProtocolStatus;
  208.         }
  209.         //
  210.         // Returns UpdateResponseInformation if passed response stream has to be replaced (cache is updated in some way)
  211.         // Returns Fail if request is to fail
  212.         // Any other return value should be ignored
  213.         //
  214.         internal CacheValidationStatus GetUpdateStatus(WebResponse response, Stream responseStream)
  215.         {
  216.             if (response == null)
  217.                 throw new ArgumentNullException("response");
  218.            
  219.             if (_ProtocolStatus == CacheValidationStatus.DoNotUseCache)
  220.                 return CacheValidationStatus.DoNotUseCache;
  221.            
  222.             try {
  223.                 if (Logging.On)
  224.                     Logging.Enter(Logging.RequestCache, this, "GetUpdateStatus", null);
  225.                
  226.                 if (_Validator.Response == null)
  227.                     _Validator.FetchResponse(response);
  228.                
  229.                 if (_ProtocolStatus == CacheValidationStatus.RemoveFromCache) {
  230.                     EnsureCacheRemoval(_Validator.CacheKey);
  231.                     return _ProtocolStatus;
  232.                 }
  233.                
  234.                 if (_ProtocolStatus != CacheValidationStatus.DoNotTakeFromCache && _ProtocolStatus != CacheValidationStatus.ReturnCachedResponse && _ProtocolStatus != CacheValidationStatus.CombineCachedAndServerResponse) {
  235.                     if (Logging.On)
  236.                         Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_not_updated_based_on_cache_protocol_status, "GetUpdateStatus()", _ProtocolStatus.ToString()));
  237.                     return _ProtocolStatus;
  238.                 }
  239.                
  240.                 CheckUpdateOnResponse(responseStream);
  241.             }
  242.             catch (Exception e) {
  243.                 _ProtocolException = e;
  244.                 _ProtocolStatus = CacheValidationStatus.Fail;
  245.                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
  246.                     throw;
  247.                 }
  248.                 if (Logging.On)
  249.                     Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_object_and_exception, "CacheProtocol#" + this.GetHashCode().ToString(NumberFormatInfo.InvariantInfo), (e is WebException ? e.Message : e.ToString())));
  250.             }
  251.             finally {
  252.                 if (Logging.On)
  253.                     Logging.Exit(Logging.RequestCache, this, "GetUpdateStatus", "result = " + _ProtocolStatus.ToString());
  254.             }
  255.             return _ProtocolStatus;
  256.         }
  257.         //
  258.         // This must be the last call before starting a new request on this protocol instance
  259.         //
  260.         internal void Reset()
  261.         {
  262.             _CanTakeNewRequest = true;
  263.         }
  264.        
  265.         //
  266.         internal void Abort()
  267.         {
  268.             // if _CanTakeNewRequest==true we should not be holding any cache stream
  269.             // Also we check on Abort() reentrancy this way.
  270.             if (_CanTakeNewRequest)
  271.                 return;
  272.            
  273.             // in case of abnormal termination this will release cache entry sooner than does it's finalizer
  274.             Stream stream = _ResponseStream;
  275.             if (stream != null) {
  276.                 try {
  277.                     if (Logging.On)
  278.                         Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_closing_cache_stream, "CacheProtocol#" + this.GetHashCode().ToString(NumberFormatInfo.InvariantInfo), "Abort()", stream.GetType().FullName, _Validator.CacheKey));
  279.                     ICloseEx closeEx = stream as ICloseEx;
  280.                     if (closeEx != null)
  281.                         closeEx.CloseEx(CloseExState.Abort | CloseExState.Silent);
  282.                     else
  283.                         stream.Close();
  284.                 }
  285.                 catch (Exception e) {
  286.                    
  287.                     if (NclUtilities.IsFatal(e))
  288.                         throw;
  289.                    
  290.                     if (Logging.On)
  291.                         Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_exception_ignored, "CacheProtocol#" + this.GetHashCode().ToString(NumberFormatInfo.InvariantInfo), "stream.Close()", e.ToString()));
  292.                 }
  293.             }
  294.             Reset();
  295.         }
  296.        
  297.         //
  298.         // Private methods
  299.         //
  300.        
  301.         //
  302.         // This method may be invoked as part of the request submission but before issuing a live request
  303.         //
  304.         private void CheckRetrieveBeforeSubmit()
  305.         {
  306.            
  307.             GlobalLog.Assert(_ProtocolStatus == CacheValidationStatus.Continue, "CheckRetrieveBeforeSubmit()|Unexpected _ProtocolStatus = {0}", _ProtocolStatus);
  308.            
  309.             try {
  310.                
  311.                 while (true) {
  312.                     RequestCacheEntry cacheEntry;
  313.                    
  314.                     if (_Validator.CacheStream != null && _Validator.CacheStream != Stream.Null) {
  315.                         // Reset to Initial state
  316.                         _Validator.CacheStream.Close();
  317.                         _Validator.CacheStream = Stream.Null;
  318.                     }
  319.                    
  320.                     if (_Validator.StrictCacheErrors) {
  321.                         _Validator.CacheStream = _RequestCache.Retrieve(_Validator.CacheKey, out cacheEntry);
  322.                     }
  323.                     else {
  324.                         Stream stream;
  325.                         _RequestCache.TryRetrieve(_Validator.CacheKey, out cacheEntry, out stream);
  326.                         _Validator.CacheStream = stream;
  327.                     }
  328.                    
  329.                     if (cacheEntry == null) {
  330.                         cacheEntry = new RequestCacheEntry();
  331.                         cacheEntry.IsPrivateEntry = _RequestCache.IsPrivateCache;
  332.                         _Validator.FetchCacheEntry(cacheEntry);
  333.                     }
  334.                    
  335.                     if (_Validator.CacheStream == null) {
  336.                         // If entry does not have a stream an empty stream wrapper must be returned.
  337.                         // A null or Stream.Null value stands for non existent cache entry.
  338.                         _Validator.CacheStream = Stream.Null;
  339.                     }
  340.                    
  341.                     ValidateFreshness(cacheEntry);
  342.                    
  343.                     _ProtocolStatus = ValidateCache();
  344.                    
  345.                     // This will tell us what to do next
  346.                     switch (_ProtocolStatus) {
  347.                         case CacheValidationStatus.ReturnCachedResponse:
  348.                            
  349.                             if (_Validator.CacheStream == null || _Validator.CacheStream == Stream.Null) {
  350.                                 if (Logging.On)
  351.                                     Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_no_cache_entry, "ValidateCache()"));
  352.                                 _ProtocolStatus = CacheValidationStatus.Fail;
  353.                                 _ProtocolException = new InvalidOperationException(SR.GetString(SR.net_cache_no_stream, _Validator.CacheKey));
  354.                                 break;
  355.                             }
  356.                            
  357.                             // Final decision is made, check on a range response from cache
  358.                             Stream stream = _Validator.CacheStream;
  359.                             // The entry can now be replaced as we are not going for cache entry metadata-only update
  360.                             _RequestCache.UnlockEntry(_Validator.CacheStream);
  361.                            
  362.                             if (_Validator.CacheStreamOffset != 0l || _Validator.CacheStreamLength != _Validator.CacheEntry.StreamSize) {
  363.                                 stream = new RangeStream(stream, _Validator.CacheStreamOffset, _Validator.CacheStreamLength);
  364.                                 if (Logging.On)
  365.                                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_returned_range_cache, "ValidateCache()", _Validator.CacheStreamOffset, _Validator.CacheStreamLength));
  366.                             }
  367.                             _ResponseStream = stream;
  368.                             _ResponseStreamLength = _Validator.CacheStreamLength;
  369.                             break;
  370.                         case CacheValidationStatus.Continue:
  371.                            
  372.                             // copy a cache stream ref
  373.                             _ResponseStream = _Validator.CacheStream;
  374.                             break;
  375.                         case CacheValidationStatus.RetryResponseFromCache:
  376.                            
  377.                             // loop thought cache retrieve
  378.                             continue;
  379.                         case CacheValidationStatus.DoNotTakeFromCache:
  380.                         case CacheValidationStatus.DoNotUseCache:
  381.                            
  382.                             break;
  383.                         case CacheValidationStatus.Fail:
  384.                            
  385.                             _ProtocolException = new InvalidOperationException(SR.GetString(SR.net_cache_validator_fail, "ValidateCache"));
  386.                             break;
  387.                         default:
  388.                            
  389.                             _ProtocolStatus = CacheValidationStatus.Fail;
  390.                             _ProtocolException = new InvalidOperationException(SR.GetString(SR.net_cache_validator_result, "ValidateCache", _Validator.ValidationStatus.ToString()));
  391.                             if (Logging.On)
  392.                                 Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_unexpected_status, "ValidateCache()", _Validator.ValidationStatus.ToString()));
  393.                             break;
  394.                     }
  395.                     break;
  396.                 }
  397.             }
  398.             catch (Exception e) {
  399.                 _ProtocolStatus = CacheValidationStatus.Fail;
  400.                 _ProtocolException = e;
  401.                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
  402.                     throw;
  403.                 }
  404.                 if (Logging.On)
  405.                     Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_object_and_exception, "CacheProtocol#" + this.GetHashCode().ToString(NumberFormatInfo.InvariantInfo), (e is WebException ? e.Message : e.ToString())));
  406.             }
  407.             finally {
  408.                 // This is to release cache entry on error
  409.                 if (_ResponseStream == null && _Validator.CacheStream != null && _Validator.CacheStream != Stream.Null) {
  410.                     _Validator.CacheStream.Close();
  411.                     _Validator.CacheStream = Stream.Null;
  412.                 }
  413.             }
  414.         }
  415.         //
  416.         private void CheckRetrieveOnResponse(Stream responseStream)
  417.         {
  418.            
  419.             GlobalLog.Assert(_ProtocolStatus == CacheValidationStatus.Continue || _ProtocolStatus == CacheValidationStatus.RetryResponseFromServer, "CheckRetrieveOnResponse()|Unexpected _ProtocolStatus = ", _ProtocolStatus);
  420.             bool closeCacheStream = true;
  421.            
  422.             try {
  423.                 // This will inspect the live response on the correctness matter
  424.                 switch (_ProtocolStatus = ValidateResponse()) {
  425.                     case CacheValidationStatus.Continue:
  426.                        
  427.                         closeCacheStream = false;
  428.                         // The response looks good
  429.                         break;
  430.                     case CacheValidationStatus.RetryResponseFromServer:
  431.                        
  432.                         closeCacheStream = false;
  433.                         break;
  434.                     case CacheValidationStatus.Fail:
  435.                        
  436.                         _ProtocolStatus = CacheValidationStatus.Fail;
  437.                         _ProtocolException = new InvalidOperationException(SR.GetString(SR.net_cache_validator_fail, "ValidateResponse"));
  438.                         break;
  439.                     case CacheValidationStatus.DoNotUseCache:
  440.                        
  441.                         break;
  442.                     default:
  443.                        
  444.                         _ProtocolStatus = CacheValidationStatus.Fail;
  445.                         _ProtocolException = new InvalidOperationException(SR.GetString(SR.net_cache_validator_result, "ValidateResponse", _Validator.ValidationStatus.ToString()));
  446.                         if (Logging.On)
  447.                             Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_unexpected_status, "ValidateResponse()", _Validator.ValidationStatus.ToString()));
  448.                         break;
  449.                 }
  450.                
  451.             }
  452.             catch (Exception e) {
  453.                 closeCacheStream = true;
  454.                 _ProtocolException = e;
  455.                 _ProtocolStatus = CacheValidationStatus.Fail;
  456.                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
  457.                     throw;
  458.                 }
  459.                 if (Logging.On)
  460.                     Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_object_and_exception, "CacheProtocol#" + this.GetHashCode().ToString(NumberFormatInfo.InvariantInfo), (e is WebException ? e.Message : e.ToString())));
  461.             }
  462.             finally {
  463.                 // This is to release cache entry in case we are not interested in it
  464.                 if (closeCacheStream && _ResponseStream != null) {
  465.                     _ResponseStream.Close();
  466.                     _ResponseStream = null;
  467.                     _Validator.CacheStream = Stream.Null;
  468.                 }
  469.             }
  470.            
  471.             if (_ProtocolStatus != CacheValidationStatus.Continue) {
  472.                 return;
  473.             }
  474.            
  475.             //
  476.             // only CacheValidationStatus.Continue goes here with closeCacheStream == false
  477.             //
  478.            
  479.             try {
  480.                 //
  481.                 switch (_ProtocolStatus = RevalidateCache()) {
  482.                     case CacheValidationStatus.DoNotUseCache:
  483.                     case CacheValidationStatus.RemoveFromCache:
  484.                     case CacheValidationStatus.DoNotTakeFromCache:
  485.                        
  486.                         closeCacheStream = true;
  487.                         break;
  488.                     case CacheValidationStatus.ReturnCachedResponse:
  489.                        
  490.                         if (_Validator.CacheStream == null || _Validator.CacheStream == Stream.Null) {
  491.                             _ProtocolStatus = CacheValidationStatus.Fail;
  492.                             _ProtocolException = new InvalidOperationException(SR.GetString(SR.net_cache_no_stream, _Validator.CacheKey));
  493.                             if (Logging.On)
  494.                                 Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_null_cached_stream, "RevalidateCache()"));
  495.                             break;
  496.                         }
  497.                        
  498.                         Stream stream = _Validator.CacheStream;
  499.                        
  500.                         if (_Validator.CacheStreamOffset != 0l || _Validator.CacheStreamLength != _Validator.CacheEntry.StreamSize) {
  501.                             stream = new RangeStream(stream, _Validator.CacheStreamOffset, _Validator.CacheStreamLength);
  502.                             if (Logging.On)
  503.                                 Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_returned_range_cache, "RevalidateCache()", _Validator.CacheStreamOffset, _Validator.CacheStreamLength));
  504.                         }
  505.                         _ResponseStream = stream;
  506.                         _ResponseStreamLength = _Validator.CacheStreamLength;
  507.                         break;
  508.                     case CacheValidationStatus.CombineCachedAndServerResponse:
  509.                        
  510.                        
  511.                         if (_Validator.CacheStream == null || _Validator.CacheStream == Stream.Null) {
  512.                             _ProtocolStatus = CacheValidationStatus.Fail;
  513.                             _ProtocolException = new InvalidOperationException(SR.GetString(SR.net_cache_no_stream, _Validator.CacheKey));
  514.                             if (Logging.On)
  515.                                 Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_requested_combined_but_null_cached_stream, "RevalidateCache()"));
  516.                             break;
  517.                         }
  518.                         if (responseStream != null) {
  519.                             stream = new CombinedReadStream(_Validator.CacheStream, responseStream);
  520.                         }
  521.                         else {
  522.                             // So Abort can close the cache stream
  523.                             stream = _Validator.CacheStream;
  524.                         }
  525.                         _ResponseStream = stream;
  526.                         _ResponseStreamLength = _Validator.CacheStreamLength;
  527.                         break;
  528.                     case CacheValidationStatus.Fail:
  529.                        
  530.                        
  531.                         closeCacheStream = true;
  532.                         _ProtocolException = new InvalidOperationException(SR.GetString(SR.net_cache_validator_fail, "RevalidateCache"));
  533.                         break;
  534.                     default:
  535.                        
  536.                         closeCacheStream = true;
  537.                         _ProtocolStatus = CacheValidationStatus.Fail;
  538.                         _ProtocolException = new InvalidOperationException(SR.GetString(SR.net_cache_validator_result, "RevalidateCache", _Validator.ValidationStatus.ToString()));
  539.                         if (Logging.On)
  540.                             Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_unexpected_status, "RevalidateCache()", _Validator.ValidationStatus.ToString()));
  541.                         break;
  542.                 }
  543.             }
  544.             catch (Exception e) {
  545.                 closeCacheStream = true;
  546.                 _ProtocolException = e;
  547.                 _ProtocolStatus = CacheValidationStatus.Fail;
  548.                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
  549.                     throw;
  550.                 }
  551.                 if (Logging.On)
  552.                     Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_object_and_exception, "CacheProtocol#" + this.GetHashCode().ToString(NumberFormatInfo.InvariantInfo), (e is WebException ? e.Message : e.ToString())));
  553.             }
  554.             finally {
  555.                 // This is to release cache entry in case we are not interested in it
  556.                 if (closeCacheStream && _ResponseStream != null) {
  557.                     _ResponseStream.Close();
  558.                     _ResponseStream = null;
  559.                     _Validator.CacheStream = Stream.Null;
  560.                 }
  561.             }
  562.         }
  563.         //
  564.         // This will decide on cache update and construct the effective response stream
  565.         //
  566.         private void CheckUpdateOnResponse(Stream responseStream)
  567.         {
  568.            
  569.             if (_Validator.CacheEntry == null) {
  570.                 // There was no chance to create an empty entry yet
  571.                 RequestCacheEntry cacheEntry = new RequestCacheEntry();
  572.                 cacheEntry.IsPrivateEntry = _RequestCache.IsPrivateCache;
  573.                 _Validator.FetchCacheEntry(cacheEntry);
  574.             }
  575.            
  576.             // With NoCache we may end up storing whole response as a new entry in Cache.
  577.             // Otherwise we may end up updating Context+Metadata or just Context
  578.             //
  579.             // In any case we may end up doing nothing.
  580.             //
  581.             string retrieveKey = _Validator.CacheKey;
  582.            
  583.             bool unlockEntry = true;
  584.             try {
  585.                 switch (_ProtocolStatus = UpdateCache()) {
  586.                     case CacheValidationStatus.RemoveFromCache:
  587.                        
  588.                         EnsureCacheRemoval(retrieveKey);
  589.                         unlockEntry = false;
  590.                         break;
  591.                     case CacheValidationStatus.UpdateResponseInformation:
  592.                        
  593.                         // NB: Just invoked validator must have updated CacheEntry and transferred
  594.                         // ONLY allowed headers from the response to the Context.xxxMetadata member
  595.                        
  596.                         _ResponseStream = new MetadataUpdateStream(responseStream, _RequestCache, _Validator.CacheKey, _Validator.CacheEntry.ExpiresUtc, _Validator.CacheEntry.LastModifiedUtc, _Validator.CacheEntry.LastSynchronizedUtc, _Validator.CacheEntry.MaxStale, _Validator.CacheEntry.EntryMetadata, _Validator.CacheEntry.SystemMetadata, _Validator.StrictCacheErrors
  597.                         );
  598.                         //
  599.                         // This can be looked as a design hole since we have to keep the entry
  600.                         // locked for the case when we want to update that previously retrieved entry.
  601.                         // I think RequestCache contract should allow to detect that a new physical cache entry
  602.                         // does not match to the "entry being updated" and so to should ignore updates on replaced entries.
  603.                         //
  604.                         unlockEntry = false;
  605.                         _ProtocolStatus = CacheValidationStatus.UpdateResponseInformation;
  606.                         break;
  607.                     case CacheValidationStatus.CacheResponse:
  608.                        
  609.                         // NB: Just invoked validator must have updated CacheEntry and transferred
  610.                         // ONLY allowed headers from the response to the Context.xxxMetadata member
  611.                        
  612.                         Stream stream;
  613.                         if (_Validator.StrictCacheErrors) {
  614.                             stream = _RequestCache.Store(_Validator.CacheKey, _Validator.CacheEntry.StreamSize, _Validator.CacheEntry.ExpiresUtc, _Validator.CacheEntry.LastModifiedUtc, _Validator.CacheEntry.MaxStale, _Validator.CacheEntry.EntryMetadata, _Validator.CacheEntry.SystemMetadata);
  615.                         }
  616.                         else {
  617.                             _RequestCache.TryStore(_Validator.CacheKey, _Validator.CacheEntry.StreamSize, _Validator.CacheEntry.ExpiresUtc, _Validator.CacheEntry.LastModifiedUtc, _Validator.CacheEntry.MaxStale, _Validator.CacheEntry.EntryMetadata, _Validator.CacheEntry.SystemMetadata, out stream);
  618.                         }
  619.                        
  620.                         // Wrap the response stream into forwarding one
  621.                         _ResponseStream = new ForwardingReadStream(responseStream, stream, _Validator.CacheStreamOffset, _Validator.StrictCacheErrors);
  622.                         _ProtocolStatus = CacheValidationStatus.UpdateResponseInformation;
  623.                         break;
  624.                     case CacheValidationStatus.DoNotUseCache:
  625.                     case CacheValidationStatus.DoNotUpdateCache:
  626.                        
  627.                         break;
  628.                     case CacheValidationStatus.Fail:
  629.                        
  630.                         _ProtocolException = new InvalidOperationException(SR.GetString(SR.net_cache_validator_fail, "UpdateCache"));
  631.                         break;
  632.                     default:
  633.                         _ProtocolStatus = CacheValidationStatus.Fail;
  634.                         _ProtocolException = new InvalidOperationException(SR.GetString(SR.net_cache_validator_result, "UpdateCache", _Validator.ValidationStatus.ToString()));
  635.                         if (Logging.On)
  636.                             Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_unexpected_status, "UpdateCache()", _Validator.ValidationStatus.ToString()));
  637.                         break;
  638.                 }
  639.             }
  640.             finally {
  641.                 if (unlockEntry) {
  642.                     // The entry can now be replaced as we are not going for cache entry metadata-only update
  643.                     _RequestCache.UnlockEntry(_Validator.CacheStream);
  644.                 }
  645.             }
  646.         }
  647.         //
  648.         private CacheValidationStatus ValidateRequest()
  649.         {
  650.            
  651.             if (Logging.On)
  652.                 Logging.PrintInfo(Logging.RequestCache, "Request#" + _Validator.Request.GetHashCode().ToString(NumberFormatInfo.InvariantInfo) + ", Policy = " + _Validator.Request.CachePolicy.ToString() + ", Cache Uri = " + _Validator.Uri);
  653.            
  654.             CacheValidationStatus result = _Validator.ValidateRequest();
  655.             _Validator.SetValidationStatus(result);
  656.            
  657.             if (Logging.On)
  658.                 Logging.PrintInfo(Logging.RequestCache, "Selected cache Key = " + _Validator.CacheKey);
  659.             return result;
  660.         }
  661.         //
  662.         //
  663.         //
  664.         private void ValidateFreshness(RequestCacheEntry fetchEntry)
  665.         {
  666.            
  667.             _Validator.FetchCacheEntry(fetchEntry);
  668.            
  669.             if (_Validator.CacheStream == null || _Validator.CacheStream == Stream.Null) {
  670.                 if (Logging.On)
  671.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_entry_not_found_freshness_undefined, "ValidateFreshness()"));
  672.                 _Validator.SetFreshnessStatus(CacheFreshnessStatus.Undefined);
  673.                 return;
  674.             }
  675.            
  676.             if (Logging.On) {
  677.                 if (Logging.IsVerbose(Logging.RequestCache)) {
  678.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_dumping_cache_context));
  679.                    
  680.                     if (fetchEntry == null) {
  681.                         Logging.PrintInfo(Logging.RequestCache, "<null>");
  682.                     }
  683.                     else {
  684.                         string[] context = fetchEntry.ToString(Logging.IsVerbose(Logging.RequestCache)).Split(RequestCache.LineSplits);
  685.                        
  686.                         for (int i = 0; i < context.Length; ++i) {
  687.                             if (context[i].Length != 0) {
  688.                                 Logging.PrintInfo(Logging.RequestCache, context[i]);
  689.                             }
  690.                         }
  691.                     }
  692.                 }
  693.             }
  694.            
  695.             CacheFreshnessStatus result = _Validator.ValidateFreshness();
  696.             _Validator.SetFreshnessStatus(result);
  697.             _IsCacheFresh = result == CacheFreshnessStatus.Fresh;
  698.            
  699.             if (Logging.On)
  700.                 Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_result, "ValidateFreshness()", result.ToString()));
  701.         }
  702.        
  703.         //
  704.         //
  705.         //
  706.         private CacheValidationStatus ValidateCache()
  707.         {
  708.             CacheValidationStatus result = _Validator.ValidateCache();
  709.             _Validator.SetValidationStatus(result);
  710.            
  711.             if (Logging.On)
  712.                 Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_result, "ValidateCache()", result.ToString()));
  713.             return result;
  714.         }
  715.         //
  716.         private CacheValidationStatus RevalidateCache()
  717.         {
  718.            
  719.             CacheValidationStatus result = _Validator.RevalidateCache();
  720.             _Validator.SetValidationStatus(result);
  721.            
  722.             if (Logging.On)
  723.                 Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_result, "RevalidateCache()", result.ToString()));
  724.             return result;
  725.         }
  726.         //
  727.         private CacheValidationStatus ValidateResponse()
  728.         {
  729.            
  730.             CacheValidationStatus result = _Validator.ValidateResponse();
  731.             _Validator.SetValidationStatus(result);
  732.            
  733.             if (Logging.On)
  734.                 Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_result, "ValidateResponse()", result.ToString()));
  735.             return result;
  736.         }
  737.         //
  738.         private CacheValidationStatus UpdateCache()
  739.         {
  740.             CacheValidationStatus result = _Validator.UpdateCache();
  741.             _Validator.SetValidationStatus(result);
  742.            
  743.             return result;
  744.         }
  745.         //
  746.         private void EnsureCacheRemoval(string retrieveKey)
  747.         {
  748.             // The entry can now be replaced as we are not going for cache entry metadata-only update
  749.             _RequestCache.UnlockEntry(_Validator.CacheStream);
  750.            
  751.             if (_Validator.StrictCacheErrors) {
  752.                 _RequestCache.Remove(retrieveKey);
  753.             }
  754.             else {
  755.                 _RequestCache.TryRemove(retrieveKey);
  756.             }
  757.            
  758.             // We may need to remove yet another reference from the cache
  759.             if (retrieveKey != _Validator.CacheKey) {
  760.                 if (_Validator.StrictCacheErrors) {
  761.                     _RequestCache.Remove(_Validator.CacheKey);
  762.                 }
  763.                 else {
  764.                     _RequestCache.TryRemove(_Validator.CacheKey);
  765.                 }
  766.             }
  767.         }
  768.        
  769.     }
  770. }

Developer Fusion