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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="FtpRequestCacheValidator.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 implements FTP Caching validators
  18. Author:
  19.     Alexei Vopilov    3-Aug-2004
  20. Revision History:
  21. --*/
  22. namespace System.Net.Cache
  23. {
  24.     using System;
  25.     using System.Net;
  26.     using System.IO;
  27.     using System.Collections;
  28.     using System.Text;
  29.     using System.Collections.Specialized;
  30.     using System.Globalization;
  31.     using System.Threading;
  32.    
  33.    
  34.     // The class represents an adavanced way for an application to control caching protocol
  35.     internal class FtpRequestCacheValidator : HttpRequestCacheValidator
  36.     {
  37.        
  38.         DateTime m_LastModified;
  39.         bool m_HttpProxyMode;
  40.        
  41.         private bool HttpProxyMode {
  42.             get { return m_HttpProxyMode; }
  43.         }
  44.         internal new RequestCachePolicy Policy {
  45.             get { return ((RequestCacheValidator)this).Policy; }
  46.         }
  47.        
  48.         //
  49.         private void ZeroPrivateVars()
  50.         {
  51.             m_LastModified = DateTime.MinValue;
  52.             m_HttpProxyMode = false;
  53.         }
  54.        
  55.         //public
  56.         internal override RequestCacheValidator CreateValidator()
  57.         {
  58.             return new FtpRequestCacheValidator(StrictCacheErrors, UnspecifiedMaxAge);
  59.         }
  60.        
  61.         //public
  62.         internal FtpRequestCacheValidator(bool strictCacheErrors, TimeSpan unspecifiedMaxAge) : base(strictCacheErrors, unspecifiedMaxAge)
  63.         {
  64.         }
  65.        
  66.         //
  67.         // This validation method is called first and before any Cache access is done.
  68.         // Given the request instance the code has to decide whether the request is ever suitable for caching.
  69.         //
  70.         // Returns:
  71.         // Continue = Proceed to the next protocol stage.
  72.         // DoNotTakeFromCache = Don't used caches value for this request
  73.         // DoNotUseCache = Cache is not used for this request and response is not cached.
  74.         protected internal override CacheValidationStatus ValidateRequest()
  75.         {
  76.             // cleanup context after previous request
  77.             ZeroPrivateVars();
  78.            
  79.             if (Request is HttpWebRequest) {
  80.                 m_HttpProxyMode = true;
  81.                 if (Logging.On)
  82.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_ftp_proxy_doesnt_support_partial));
  83.                 return base.ValidateRequest();
  84.             }
  85.            
  86.             if (Policy.Level == RequestCacheLevel.BypassCache)
  87.                 return CacheValidationStatus.DoNotUseCache;
  88.            
  89.             string method = Request.Method.ToUpper(CultureInfo.InvariantCulture);
  90.             if (Logging.On)
  91.                 Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_ftp_method, method));
  92.            
  93.             switch (method) {
  94.                 case WebRequestMethods.Ftp.DownloadFile:
  95.                     RequestMethod = HttpMethod.Get;
  96.                     break;
  97.                 case WebRequestMethods.Ftp.UploadFile:
  98.                     RequestMethod = HttpMethod.Put;
  99.                     break;
  100.                 case WebRequestMethods.Ftp.AppendFile:
  101.                     RequestMethod = HttpMethod.Put;
  102.                     break;
  103.                 case WebRequestMethods.Ftp.Rename:
  104.                     RequestMethod = HttpMethod.Put;
  105.                     break;
  106.                 case WebRequestMethods.Ftp.DeleteFile:
  107.                     RequestMethod = HttpMethod.Delete;
  108.                     break;
  109.                 default:
  110.                    
  111.                     RequestMethod = HttpMethod.Other;
  112.                     break;
  113.             }
  114.            
  115.             if ((RequestMethod != HttpMethod.Get || !((FtpWebRequest)Request).UseBinary) && Policy.Level == RequestCacheLevel.CacheOnly) {
  116.                 // Throw because the request must hit the wire and it's cache-only policy
  117.                 FailRequest(WebExceptionStatus.RequestProhibitedByCachePolicy);
  118.             }
  119.            
  120.             if (method != WebRequestMethods.Ftp.DownloadFile)
  121.                 return CacheValidationStatus.DoNotTakeFromCache;
  122.            
  123.             if (!((FtpWebRequest)Request).UseBinary) {
  124.                 if (Logging.On)
  125.                     Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_ftp_supports_bin_only));
  126.                 return CacheValidationStatus.DoNotUseCache;
  127.             }
  128.            
  129.             if (Policy.Level >= RequestCacheLevel.Reload)
  130.                 return CacheValidationStatus.DoNotTakeFromCache;
  131.            
  132.             return CacheValidationStatus.Continue;
  133.         }
  134.        
  135.         //
  136.         // This validation method is called after caching protocol has retrieved the metadata of a cached entry.
  137.         // Given the cached entry context, the request instance and the effective caching policy,
  138.         // the handler has to decide whether a cached item can be considered as fresh.
  139.         protected internal override CacheFreshnessStatus ValidateFreshness()
  140.         {
  141.            
  142.             if (HttpProxyMode) {
  143.                 if (CacheStream != Stream.Null) {
  144.                     if (Logging.On)
  145.                         Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_replacing_entry_with_HTTP_200));
  146.                    
  147.                     // HTTP validator cannot parse FTP status code and other metadata
  148.                     if (CacheEntry.EntryMetadata == null)
  149.                         CacheEntry.EntryMetadata = new StringCollection();
  150.                    
  151.                     CacheEntry.EntryMetadata.Clear();
  152.                     CacheEntry.EntryMetadata.Add("HTTP/1.1 200 OK");
  153.                 }
  154.                 return base.ValidateFreshness();
  155.             }
  156.            
  157.             DateTime nowDate = DateTime.UtcNow;
  158.            
  159.             if (Logging.On)
  160.                 Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_now_time, nowDate.ToString("r", CultureInfo.InvariantCulture)));
  161.            
  162.             // If absolute Expires can be recovered
  163.             if (CacheEntry.ExpiresUtc != DateTime.MinValue) {
  164.                 //Take absolute Expires value
  165.                 if (Logging.On)
  166.                     Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_max_age_absolute, CacheEntry.ExpiresUtc.ToString("r", CultureInfo.InvariantCulture)));
  167.                 if (CacheEntry.ExpiresUtc < nowDate) {
  168.                     return CacheFreshnessStatus.Stale;
  169.                 }
  170.                 return CacheFreshnessStatus.Fresh;
  171.             }
  172.            
  173.             TimeSpan age = TimeSpan.MaxValue;
  174.            
  175.             if (CacheEntry.LastSynchronizedUtc != DateTime.MinValue) {
  176.                 age = nowDate - CacheEntry.LastSynchronizedUtc;
  177.                 if (Logging.On)
  178.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_age1, ((int)age.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo), CacheEntry.LastSynchronizedUtc.ToString("r", CultureInfo.InvariantCulture)));
  179.             }
  180.            
  181.             //
  182.             // Heruistic expiration
  183.             //
  184.             if (CacheEntry.LastModifiedUtc != DateTime.MinValue) {
  185.                 TimeSpan span = (nowDate - CacheEntry.LastModifiedUtc);
  186.                 int maxAgeSeconds = (int)(span.TotalSeconds / 10);
  187.                 if (Logging.On)
  188.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_no_max_age_use_10_percent, maxAgeSeconds.ToString(NumberFormatInfo.InvariantInfo), CacheEntry.LastModifiedUtc.ToString("r", CultureInfo.InvariantCulture)));
  189.                 if (age.TotalSeconds < maxAgeSeconds) {
  190.                     return CacheFreshnessStatus.Fresh;
  191.                 }
  192.                 return CacheFreshnessStatus.Stale;
  193.             }
  194.            
  195.             // Else we can only rely on UnspecifiedMaxAge hint
  196.             if (Logging.On)
  197.                 Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_no_max_age_use_default, ((int)(UnspecifiedMaxAge.TotalSeconds)).ToString(NumberFormatInfo.InvariantInfo)));
  198.             if (UnspecifiedMaxAge >= age) {
  199.                 return CacheFreshnessStatus.Fresh;
  200.             }
  201.            
  202.             return CacheFreshnessStatus.Stale;
  203.             //return OnValidateFreshness(this);
  204.         }
  205.        
  206.         // This method may add headers under the "Warning" header name
  207.         protected internal override CacheValidationStatus ValidateCache()
  208.         {
  209.             if (HttpProxyMode)
  210.                 return base.ValidateCache();
  211.            
  212.             if (Policy.Level >= RequestCacheLevel.Reload) {
  213.                 // For those policies cache is never returned
  214.                 GlobalLog.Assert("OnValidateCache()", "This validator should not be called for policy = " + Policy.ToString());
  215.                 if (Logging.On)
  216.                     Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_validator_invalid_for_policy, Policy.ToString()));
  217.                 return CacheValidationStatus.DoNotTakeFromCache;
  218.             }
  219.            
  220.             // First check is do we have a cached entry at all?
  221.             if (CacheStream == Stream.Null || CacheEntry.IsPartialEntry) {
  222.                 if (Policy.Level == RequestCacheLevel.CacheOnly) {
  223.                     // Throw because entry was not found and it's cache-only policy
  224.                     FailRequest(WebExceptionStatus.CacheEntryNotFound);
  225.                 }
  226.                 if (CacheStream == Stream.Null) {
  227.                     return CacheValidationStatus.DoNotTakeFromCache;
  228.                 }
  229.                 // Otherwise it's a partial entry and we can go on the wire
  230.             }
  231.            
  232.             CacheStreamOffset = 0l;
  233.             CacheStreamLength = CacheEntry.StreamSize;
  234.            
  235.             //
  236.             // Before request submission validation
  237.             //
  238.             if (Policy.Level == RequestCacheLevel.Revalidate || CacheEntry.IsPartialEntry) {
  239.                 return TryConditionalRequest();
  240.             }
  241.            
  242.             long contentOffset = Request is FtpWebRequest ? ((FtpWebRequest)Request).ContentOffset : 0l;
  243.            
  244.             if (CacheFreshnessStatus == CacheFreshnessStatus.Fresh || Policy.Level == RequestCacheLevel.CacheOnly || Policy.Level == RequestCacheLevel.CacheIfAvailable) {
  245.                 if (contentOffset != 0) {
  246.                     if (contentOffset >= CacheStreamLength) {
  247.                         if (Policy.Level == RequestCacheLevel.CacheOnly) {
  248.                             // Throw because request is outside of cached size and it's cache-only policy
  249.                             FailRequest(WebExceptionStatus.CacheEntryNotFound);
  250.                         }
  251.                         return CacheValidationStatus.DoNotTakeFromCache;
  252.                     }
  253.                     CacheStreamOffset = contentOffset;
  254.                 }
  255.                 return CacheValidationStatus.ReturnCachedResponse;
  256.             }
  257.            
  258.             return CacheValidationStatus.DoNotTakeFromCache;
  259.         }
  260.         //
  261.         // This is (optionally) called after receiveing a live response
  262.         //
  263.         protected internal override CacheValidationStatus RevalidateCache()
  264.         {
  265.             if (HttpProxyMode)
  266.                 return base.RevalidateCache();
  267.            
  268.            
  269.             if (Policy.Level >= RequestCacheLevel.Reload) {
  270.                 // For those policies cache is never returned
  271.                 GlobalLog.Assert("RevalidateCache()", "This validator should not be called for policy = " + Policy.ToString());
  272.                 if (Logging.On)
  273.                     Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_validator_invalid_for_policy, Policy.ToString()));
  274.                 return CacheValidationStatus.DoNotTakeFromCache;
  275.             }
  276.            
  277.             // First check is do we still hold on a cached entry?
  278.             if (CacheStream == Stream.Null) {
  279.                 return CacheValidationStatus.DoNotTakeFromCache;
  280.             }
  281.            
  282.             //
  283.             // This is a second+ time validation after receiving at least one response
  284.             //
  285.            
  286.             CacheValidationStatus result = CacheValidationStatus.DoNotTakeFromCache;
  287.            
  288.             FtpWebResponse resp = Response as FtpWebResponse;
  289.             if (resp == null) {
  290.                 // This will result to an application error
  291.                 return CacheValidationStatus.DoNotTakeFromCache;
  292.             }
  293.            
  294.             if (resp.StatusCode == FtpStatusCode.FileStatus) {
  295.                 if (Logging.On)
  296.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_response_last_modified, resp.LastModified.ToUniversalTime().ToString("r", CultureInfo.InvariantCulture), resp.ContentLength));
  297.                 if (Logging.On)
  298.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_cache_last_modified, CacheEntry.LastModifiedUtc.ToString("r", CultureInfo.InvariantCulture), CacheEntry.StreamSize));
  299.                
  300.                 if (CacheStreamOffset != 0l && CacheEntry.IsPartialEntry) {
  301.                     //should never happen
  302.                     if (Logging.On)
  303.                         Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_partial_and_non_zero_content_offset, CacheStreamOffset.ToString(CultureInfo.InvariantCulture)));
  304.                     result = CacheValidationStatus.DoNotTakeFromCache;
  305.                 }
  306.                
  307.                 if (resp.LastModified.ToUniversalTime() == CacheEntry.LastModifiedUtc) {
  308.                     if (CacheEntry.IsPartialEntry) {
  309.                         // A caller will need to use Validator.CacheEntry.StreamSize to figure out what the restart point is
  310.                        
  311.                         if (resp.ContentLength > 0)
  312.                             this.CacheStreamLength = resp.ContentLength;
  313.                         else
  314.                             this.CacheStreamLength = -1;
  315.                        
  316.                         result = CacheValidationStatus.CombineCachedAndServerResponse;
  317.                     }
  318.                     else if (resp.ContentLength == CacheEntry.StreamSize) {
  319.                         result = CacheValidationStatus.ReturnCachedResponse;
  320.                     }
  321.                     else
  322.                         result = CacheValidationStatus.DoNotTakeFromCache;
  323.                 }
  324.                 else
  325.                     result = CacheValidationStatus.DoNotTakeFromCache;
  326.             }
  327.             else {
  328.                 result = CacheValidationStatus.DoNotTakeFromCache;
  329.             }
  330.            
  331.             return result;
  332.         }
  333.        
  334.         //
  335.         // This validation method is responsible to answer whether the live response is sufficient to make
  336.         // the final decision for caching protocol.
  337.         // This is useful in case of possible failure or inconsistent results received from
  338.         // the remote cache.
  339.         //
  340.         /// Invalid response from this method means the request was internally modified and should be retried </remarks>
  341.         protected internal override CacheValidationStatus ValidateResponse()
  342.         {
  343.             if (HttpProxyMode)
  344.                 return base.ValidateResponse();
  345.            
  346.             if (Policy.Level != RequestCacheLevel.Default && Policy.Level != RequestCacheLevel.Revalidate) {
  347.                 // Those policy levels do not modify requests
  348.                 if (Logging.On)
  349.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_response_valid_based_on_policy, Policy.ToString()));
  350.                 return CacheValidationStatus.Continue;
  351.             }
  352.            
  353.             FtpWebResponse resp = Response as FtpWebResponse;
  354.            
  355.             if (resp == null) {
  356.                 if (Logging.On)
  357.                     Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_null_response_failure));
  358.                 return CacheValidationStatus.Continue;
  359.             }
  360.            
  361.             if (Logging.On)
  362.                 Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_ftp_response_status, ((int)resp.StatusCode).ToString(CultureInfo.InvariantCulture), resp.StatusCode.ToString()));
  363.            
  364.             // If there was a retry already, it should go with cache disabled so by default we won't retry it again
  365.             if (ResponseCount > 1) {
  366.                 if (Logging.On)
  367.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_resp_valid_based_on_retry, ResponseCount));
  368.                 return CacheValidationStatus.Continue;
  369.             }
  370.            
  371.             if (resp.StatusCode != FtpStatusCode.OpeningData && resp.StatusCode != FtpStatusCode.FileStatus) {
  372.                 return CacheValidationStatus.RetryResponseFromServer;
  373.             }
  374.             return CacheValidationStatus.Continue;
  375.         }
  376.        
  377.         ///This action handler is responsible for making final decision on whether
  378.         // a received response can be cached.
  379.         // Invalid result from this method means the response must not be cached
  380.         protected internal override CacheValidationStatus UpdateCache()
  381.         {
  382.             if (HttpProxyMode)
  383.                 return base.UpdateCache();
  384.            
  385.             // An combined cace+wire response is not supported if user has specified a restart offset.
  386.             CacheStreamOffset = 0l;
  387.            
  388.             if (RequestMethod == HttpMethod.Other) {
  389.                 if (Logging.On)
  390.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_not_updated_based_on_policy, Request.Method));
  391.                 return CacheValidationStatus.DoNotUpdateCache;
  392.             }
  393.            
  394.             if (ValidationStatus == CacheValidationStatus.RemoveFromCache) {
  395.                 if (Logging.On)
  396.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_removed_existing_invalid_entry));
  397.                 return CacheValidationStatus.RemoveFromCache;
  398.             }
  399.            
  400.             if (Policy.Level == RequestCacheLevel.CacheOnly) {
  401.                 if (Logging.On)
  402.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_not_updated_based_on_policy, Policy.ToString()));
  403.                 return CacheValidationStatus.DoNotUpdateCache;
  404.             }
  405.            
  406.             FtpWebResponse resp = Response as FtpWebResponse;
  407.            
  408.             if (resp == null) {
  409.                 if (Logging.On)
  410.                     Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_not_updated_because_no_response));
  411.                 return CacheValidationStatus.DoNotUpdateCache;
  412.             }
  413.            
  414.             //
  415.             // Check on cache removal based on the request method
  416.             //
  417.             if (RequestMethod == HttpMethod.Delete || RequestMethod == HttpMethod.Put) {
  418.                 if (RequestMethod == HttpMethod.Delete || resp.StatusCode == FtpStatusCode.OpeningData || resp.StatusCode == FtpStatusCode.DataAlreadyOpen || resp.StatusCode == FtpStatusCode.FileActionOK || resp.StatusCode == FtpStatusCode.ClosingData) {
  419.                     if (Logging.On)
  420.                         Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_removed_existing_based_on_method, Request.Method));
  421.                     return CacheValidationStatus.RemoveFromCache;
  422.                 }
  423.                 if (Logging.On)
  424.                     Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_existing_not_removed_because_unexpected_response_status, (int)resp.StatusCode, resp.StatusCode.ToString()));
  425.                 return CacheValidationStatus.DoNotUpdateCache;
  426.             }
  427.            
  428.             if (Policy.Level == RequestCacheLevel.NoCacheNoStore) {
  429.                 if (Logging.On)
  430.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_removed_existing_based_on_policy, Policy.ToString()));
  431.                 return CacheValidationStatus.RemoveFromCache;
  432.             }
  433.            
  434.             if (ValidationStatus == CacheValidationStatus.ReturnCachedResponse) {
  435.                 // have a response still returning from cache means just revalidated the entry.
  436.                 return UpdateCacheEntryOnRevalidate();
  437.             }
  438.            
  439.             if (resp.StatusCode != FtpStatusCode.OpeningData && resp.StatusCode != FtpStatusCode.DataAlreadyOpen && resp.StatusCode != FtpStatusCode.ClosingData) {
  440.                 if (Logging.On)
  441.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_not_updated_based_on_ftp_response_status, FtpStatusCode.OpeningData.ToString() + "|" + FtpStatusCode.DataAlreadyOpen.ToString() + "|" + FtpStatusCode.ClosingData.ToString(), resp.StatusCode.ToString()));
  442.                 return CacheValidationStatus.DoNotUpdateCache;
  443.             }
  444.            
  445.             // Check on no-update or cache removal if restart action has invalidated existing cache entry
  446.             if (((FtpWebRequest)Request).ContentOffset != 0l) {
  447.                 if (Logging.On)
  448.                     Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_update_not_supported_for_ftp_restart, ((FtpWebRequest)Request).ContentOffset.ToString(CultureInfo.InvariantCulture)));
  449.                 if (CacheEntry.LastModifiedUtc != DateTime.MinValue && resp.LastModified.ToUniversalTime() != CacheEntry.LastModifiedUtc) {
  450.                     if (Logging.On)
  451.                         Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_removed_entry_because_ftp_restart_response_changed, CacheEntry.LastModifiedUtc.ToString("r", CultureInfo.InvariantCulture), resp.LastModified.ToUniversalTime().ToString("r", CultureInfo.InvariantCulture)));
  452.                     return CacheValidationStatus.RemoveFromCache;
  453.                 }
  454.                 return CacheValidationStatus.DoNotUpdateCache;
  455.             }
  456.            
  457.             return UpdateCacheEntryOnStore();
  458.         }
  459.        
  460.         //
  461.         //
  462.         //
  463.         private CacheValidationStatus UpdateCacheEntryOnStore()
  464.         {
  465.             CacheEntry.EntryMetadata = null;
  466.             CacheEntry.SystemMetadata = null;
  467.            
  468.             FtpWebResponse resp = Response as FtpWebResponse;
  469.             if (resp.LastModified != DateTime.MinValue) {
  470.                 CacheEntry.LastModifiedUtc = resp.LastModified.ToUniversalTime();
  471.             }
  472.            
  473.             ResponseEntityLength = Response.ContentLength;
  474.             CacheEntry.StreamSize = ResponseEntityLength;
  475.             //This is passed down to cache on what size to expect
  476.             CacheEntry.LastSynchronizedUtc = DateTime.UtcNow;
  477.             return CacheValidationStatus.CacheResponse;
  478.         }
  479.         //
  480.         //
  481.         private CacheValidationStatus UpdateCacheEntryOnRevalidate()
  482.         {
  483.             if (Logging.On)
  484.                 Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_last_synchronized, CacheEntry.LastSynchronizedUtc.ToString("r", CultureInfo.InvariantCulture)));
  485.            
  486.             DateTime nowUtc = DateTime.UtcNow;
  487.             if (CacheEntry.LastSynchronizedUtc + TimeSpan.FromMinutes(1) >= nowUtc) {
  488.                 if (Logging.On)
  489.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_suppress_update_because_synched_last_minute));
  490.                 return CacheValidationStatus.DoNotUpdateCache;
  491.             }
  492.            
  493.             CacheEntry.EntryMetadata = null;
  494.             CacheEntry.SystemMetadata = null;
  495.            
  496.             CacheEntry.LastSynchronizedUtc = nowUtc;
  497.            
  498.             if (Logging.On)
  499.                 Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_updating_last_synchronized, CacheEntry.LastSynchronizedUtc.ToString("r", CultureInfo.InvariantCulture)));
  500.            
  501.             return CacheValidationStatus.UpdateResponseInformation;
  502.         }
  503.        
  504.         //
  505.         private CacheValidationStatus TryConditionalRequest()
  506.         {
  507.             FtpWebRequest request = Request as FtpWebRequest;
  508.             if (request == null || !request.UseBinary)
  509.                 return CacheValidationStatus.DoNotTakeFromCache;
  510.            
  511.             if (request.ContentOffset != 0l) {
  512.                 if (CacheEntry.IsPartialEntry || request.ContentOffset >= CacheStreamLength)
  513.                     return CacheValidationStatus.DoNotTakeFromCache;
  514.                 CacheStreamOffset = request.ContentOffset;
  515.             }
  516.             return CacheValidationStatus.Continue;
  517.         }
  518.        
  519.     }
  520. }

Developer Fusion