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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="HttpRequestCacheValidator.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 HTTP Caching validators as per RFC2616
  18. Author:
  19.     Alexei Vopilov    21-Dec-2002
  20. Revision History:
  21.     Jan 25 2004 - Changed the visibility of the class from public to internal.
  22. --*/
  23. namespace System.Net.Cache
  24. {
  25.     using System;
  26.     using System.Net;
  27.     using System.IO;
  28.     using System.Collections;
  29.     using System.Text;
  30.     using System.Collections.Specialized;
  31.     using System.Globalization;
  32.     using System.Threading;
  33.    
  34.    
  35.     /// <summary> The class represents an adavanced way for an application to control caching protocol </summary>
  36.     internal class HttpRequestCacheValidator : RequestCacheValidator
  37.     {
  38.         internal const string Warning_110 = "110 Response is stale";
  39.         internal const string Warning_111 = "111 Revalidation failed";
  40.         internal const string Warning_112 = "112 Disconnected operation";
  41.         internal const string Warning_113 = "113 Heuristic expiration";
  42.        
  43.         private struct RequestVars
  44.         {
  45.             internal HttpMethod Method;
  46.             internal bool IsCacheRange;
  47.             internal bool IsUserRange;
  48.             internal string IfHeader1;
  49.             internal string Validator1;
  50.             internal string IfHeader2;
  51.             internal string Validator2;
  52.         }
  53.        
  54.         private HttpRequestCachePolicy m_HttpPolicy;
  55.        
  56.         private HttpStatusCode m_StatusCode;
  57.         private string m_StatusDescription;
  58.         private Version m_HttpVersion;
  59.         private WebHeaderCollection m_Headers;
  60.         private NameValueCollection m_SystemMeta;
  61.        
  62.         private bool m_DontUpdateHeaders;
  63.         private bool m_HeuristicExpiration;
  64.        
  65.         private Vars m_CacheVars;
  66.         private Vars m_ResponseVars;
  67.         private RequestVars m_RequestVars;
  68.        
  69.         private struct Vars
  70.         {
  71.             internal DateTime Date;
  72.             internal DateTime Expires;
  73.             internal DateTime LastModified;
  74.             internal long EntityLength;
  75.             internal TimeSpan Age;
  76.             internal TimeSpan MaxAge;
  77.             internal ResponseCacheControl CacheControl;
  78.             internal long RangeStart;
  79.             internal long RangeEnd;
  80.            
  81.             internal void Initialize()
  82.             {
  83.                 EntityLength = RangeStart = RangeEnd = -1;
  84.                 Date = DateTime.MinValue;
  85.                 Expires = DateTime.MinValue;
  86.                 LastModified = DateTime.MinValue;
  87.                 Age = TimeSpan.MinValue;
  88.                 MaxAge = TimeSpan.MinValue;
  89.             }
  90.         }
  91.        
  92.        
  93.         //public
  94.         internal HttpStatusCode CacheStatusCode {
  95.             get { return m_StatusCode; }
  96.             set { m_StatusCode = value; }
  97.         }
  98.         //public
  99.         internal string CacheStatusDescription {
  100.             get { return m_StatusDescription; }
  101.             set { m_StatusDescription = value; }
  102.         }
  103.         //public
  104.         internal Version CacheHttpVersion {
  105.             get { return m_HttpVersion; }
  106.             set { m_HttpVersion = value; }
  107.         }
  108.        
  109.         //public
  110.         internal WebHeaderCollection CacheHeaders {
  111.             get { return m_Headers; }
  112.             set { m_Headers = value; }
  113.         }
  114.        
  115.         //public
  116.         internal new HttpRequestCachePolicy Policy {
  117.             get {
  118.                 if (m_HttpPolicy != null)
  119.                     return m_HttpPolicy;
  120.                 m_HttpPolicy = base.Policy as HttpRequestCachePolicy;
  121.                 if (m_HttpPolicy != null)
  122.                     return m_HttpPolicy;
  123.                 // promote base policy to Http one
  124.                 m_HttpPolicy = new HttpRequestCachePolicy((HttpRequestCacheLevel)base.Policy.Level);
  125.                 return m_HttpPolicy;
  126.             }
  127.         }
  128.        
  129.         internal NameValueCollection SystemMeta {
  130.             get { return m_SystemMeta; }
  131.             set { m_SystemMeta = value; }
  132.         }
  133.         internal HttpMethod RequestMethod {
  134.             get { return m_RequestVars.Method; }
  135.             set { m_RequestVars.Method = value; }
  136.         }
  137.         internal bool RequestRangeCache {
  138.             get { return m_RequestVars.IsCacheRange; }
  139.             set { m_RequestVars.IsCacheRange = value; }
  140.         }
  141.         internal bool RequestRangeUser {
  142.             get { return m_RequestVars.IsUserRange; }
  143.             set { m_RequestVars.IsUserRange = value; }
  144.         }
  145.         internal string RequestIfHeader1 {
  146.             get { return m_RequestVars.IfHeader1; }
  147.             set { m_RequestVars.IfHeader1 = value; }
  148.         }
  149.         internal string RequestValidator1 {
  150.             get { return m_RequestVars.Validator1; }
  151.             set { m_RequestVars.Validator1 = value; }
  152.         }
  153.         internal string RequestIfHeader2 {
  154.             get { return m_RequestVars.IfHeader2; }
  155.             set { m_RequestVars.IfHeader2 = value; }
  156.         }
  157.         internal string RequestValidator2 {
  158.             get { return m_RequestVars.Validator2; }
  159.             set { m_RequestVars.Validator2 = value; }
  160.         }
  161.        
  162.         internal bool CacheDontUpdateHeaders {
  163.             get { return m_DontUpdateHeaders; }
  164.             set { m_DontUpdateHeaders = value; }
  165.         }
  166.        
  167.         internal DateTime CacheDate {
  168.             get { return m_CacheVars.Date; }
  169.             set { m_CacheVars.Date = value; }
  170.         }
  171.         internal DateTime CacheExpires {
  172.             get { return m_CacheVars.Expires; }
  173.             set { m_CacheVars.Expires = value; }
  174.         }
  175.         internal DateTime CacheLastModified {
  176.             get { return m_CacheVars.LastModified; }
  177.             set { m_CacheVars.LastModified = value; }
  178.         }
  179.         internal long CacheEntityLength {
  180.             get { return m_CacheVars.EntityLength; }
  181.             set { m_CacheVars.EntityLength = value; }
  182.         }
  183.         internal TimeSpan CacheAge {
  184.             get { return m_CacheVars.Age; }
  185.             set { m_CacheVars.Age = value; }
  186.         }
  187.         internal TimeSpan CacheMaxAge {
  188.             get { return m_CacheVars.MaxAge; }
  189.             set { m_CacheVars.MaxAge = value; }
  190.         }
  191.         internal bool HeuristicExpiration {
  192.             get { return m_HeuristicExpiration; }
  193.             set { m_HeuristicExpiration = value; }
  194.         }
  195.        
  196.         internal ResponseCacheControl CacheCacheControl {
  197.             get { return m_CacheVars.CacheControl; }
  198.             set { m_CacheVars.CacheControl = value; }
  199.         }
  200.        
  201.         internal DateTime ResponseDate {
  202.             get { return m_ResponseVars.Date; }
  203.             set { m_ResponseVars.Date = value; }
  204.         }
  205.         internal DateTime ResponseExpires {
  206.             get { return m_ResponseVars.Expires; }
  207.             set { m_ResponseVars.Expires = value; }
  208.         }
  209.         internal DateTime ResponseLastModified {
  210.             get { return m_ResponseVars.LastModified; }
  211.             set { m_ResponseVars.LastModified = value; }
  212.         }
  213.         internal long ResponseEntityLength {
  214.             get { return m_ResponseVars.EntityLength; }
  215.             set { m_ResponseVars.EntityLength = value; }
  216.         }
  217.         internal long ResponseRangeStart {
  218.             get { return m_ResponseVars.RangeStart; }
  219.             set { m_ResponseVars.RangeStart = value; }
  220.         }
  221.         internal long ResponseRangeEnd {
  222.             get { return m_ResponseVars.RangeEnd; }
  223.             set { m_ResponseVars.RangeEnd = value; }
  224.         }
  225.         internal TimeSpan ResponseAge {
  226.             get { return m_ResponseVars.Age; }
  227.             set { m_ResponseVars.Age = value; }
  228.         }
  229.         internal ResponseCacheControl ResponseCacheControl {
  230.             get { return m_ResponseVars.CacheControl; }
  231.             set { m_ResponseVars.CacheControl = value; }
  232.         }
  233.        
  234.         //
  235.         private void ZeroPrivateVars()
  236.         {
  237.             // Set default values for private members here
  238.             m_RequestVars = new RequestVars();
  239.            
  240.             m_HttpPolicy = null;
  241.             m_StatusCode = (HttpStatusCode)0;
  242.             m_StatusDescription = null;
  243.             m_HttpVersion = null;
  244.             m_Headers = null;
  245.             m_SystemMeta = null;
  246.             m_DontUpdateHeaders = false;
  247.             m_HeuristicExpiration = false;
  248.            
  249.             m_CacheVars = new Vars();
  250.             m_CacheVars.Initialize();
  251.            
  252.             m_ResponseVars = new Vars();
  253.             m_ResponseVars.Initialize();
  254.         }
  255.        
  256.         //public
  257.         internal override RequestCacheValidator CreateValidator()
  258.         {
  259.             return new HttpRequestCacheValidator(StrictCacheErrors, UnspecifiedMaxAge);
  260.         }
  261.        
  262. /*
  263.         //             
  264.         internal HttpRequestCacheValidator(): base()
  265.         {
  266.         }
  267.         */       
  268.        
  269.         //public
  270.         internal HttpRequestCacheValidator(bool strictCacheErrors, TimeSpan unspecifiedMaxAge) : base(strictCacheErrors, unspecifiedMaxAge)
  271.         {
  272.         }
  273.        
  274.         //
  275.         // This validation method is called first and before any Cache access is done.
  276.         // Given the request instance the code has to decide whether the request is ever suitable for caching.
  277.         //
  278.         // Returns:
  279.         // Continue = Proceed to the next protocol stage.
  280.         // DoNotTakeFromCache = Don't used caches value for this request
  281.         // DoNotUseCache = Cache is not used for this request and response is not cached.
  282.         protected internal override CacheValidationStatus ValidateRequest()
  283.         {
  284.            
  285.             // cleanup context after previous request
  286.             ZeroPrivateVars();
  287.            
  288.             string method = Request.Method.ToUpper(CultureInfo.InvariantCulture);
  289.             if (Logging.On)
  290.                 Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_request_method, method));
  291.            
  292.             switch (method) {
  293.                 case "GET":
  294.                     RequestMethod = HttpMethod.Get;
  295.                     break;
  296.                 case "POST":
  297.                     RequestMethod = HttpMethod.Post;
  298.                     break;
  299.                 case "HEAD":
  300.                     RequestMethod = HttpMethod.Head;
  301.                     break;
  302.                 case "PUT":
  303.                     RequestMethod = HttpMethod.Put;
  304.                     break;
  305.                 case "DELETE":
  306.                     RequestMethod = HttpMethod.Delete;
  307.                     break;
  308.                 case "OPTIONS":
  309.                     RequestMethod = HttpMethod.Options;
  310.                     break;
  311.                 case "TRACE":
  312.                     RequestMethod = HttpMethod.Trace;
  313.                     break;
  314.                 case "CONNECT":
  315.                     RequestMethod = HttpMethod.Connect;
  316.                     break;
  317.                 default:
  318.                     RequestMethod = HttpMethod.Other;
  319.                     break;
  320.             }
  321.            
  322.             // Apply our best knowledge of HTTP caching and return the result
  323.             // that can be hooked up and revised by the upper level
  324.             return Rfc2616.OnValidateRequest(this);
  325.         }
  326.        
  327.         //
  328.         // This validation method is called after caching protocol has retrieved the metadata of a cached entry.
  329.         // Given the cached entry context, the request instance and the effective caching policy,
  330.         // the handler has to decide whether a cached item can be considered as fresh.
  331.         protected internal override CacheFreshnessStatus ValidateFreshness()
  332.         {
  333.            
  334.             // Transfer cache entry metadata into status line and headers.
  335.             string s = ParseStatusLine();
  336.            
  337.             if (Logging.On) {
  338.                 if ((int)CacheStatusCode == 0) {
  339.                     Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_http_status_parse_failure, (s == null ? "null" : s)));
  340.                 }
  341.                 else {
  342.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_http_status_line, (CacheHttpVersion != null ? CacheHttpVersion.ToString() : "null"), (int)CacheStatusCode, CacheStatusDescription));
  343.                 }
  344.                
  345.             }
  346.            
  347.             CreateCacheHeaders((int)CacheStatusCode != 0);
  348.             CreateSystemMeta();
  349.            
  350.             // We will need quick access to cache-control and other headers coming with the cached item
  351.             FetchHeaderValues(true);
  352.            
  353.             if (Logging.On)
  354.                 Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_cache_control, CacheCacheControl.ToString()));
  355.            
  356.             // Now we try to apply our best knowledge of HTTP caching and return the result
  357.             // that can be hooked up and revised on the upper level
  358.             return Rfc2616.OnValidateFreshness(this);
  359.         }
  360.        
  361.         /// <remarks> This method may add headers under the "Warning" header name </remarks>
  362.         protected internal override CacheValidationStatus ValidateCache()
  363.         {
  364.            
  365.             if (this.Policy.Level != HttpRequestCacheLevel.Revalidate && base.Policy.Level >= RequestCacheLevel.Reload) {
  366.                 // For those policies cache is never returned
  367.                 GlobalLog.Assert("OnValidateCache()", "This validator should not be called for policy = " + Policy.ToString());
  368.                 if (Logging.On)
  369.                     Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_validator_invalid_for_policy, Policy.ToString()));
  370.                 return CacheValidationStatus.DoNotTakeFromCache;
  371.             }
  372.            
  373.             // First check is do we have a cached entry at all?
  374.             // Also we include some very special case where cache got a 304 (NotModified) response somehow
  375.             if (CacheStream == Stream.Null || (int)CacheStatusCode == 0 || CacheStatusCode == HttpStatusCode.NotModified) {
  376.                 if (this.Policy.Level == HttpRequestCacheLevel.CacheOnly) {
  377.                     // Throw because entry was not found and it's cache-only policy
  378.                     FailRequest(WebExceptionStatus.CacheEntryNotFound);
  379.                 }
  380.                 return CacheValidationStatus.DoNotTakeFromCache;
  381.             }
  382.            
  383.             if (RequestMethod == HttpMethod.Head) {
  384.                 // For a HEAD request we release the cache entry stream asap since we will have to suppress it anyway
  385.                 CacheStream.Close();
  386.                 CacheStream = new SyncMemoryStream(new byte[] {});
  387.             }
  388.            
  389.             // Apply our best knowledge of HTTP caching and return the result
  390.             // that can be hooked up and revised by the upper level
  391.            
  392.             CacheValidationStatus result = CacheValidationStatus.DoNotTakeFromCache;
  393.            
  394.             //
  395.             // Before request submission validation
  396.             //
  397.            
  398.             // If we return from cache we should remove existing 1xx warnings
  399.             RemoveWarnings_1xx();
  400.            
  401.             // default values for a response from cache.
  402.             CacheStreamOffset = 0;
  403.             CacheStreamLength = CacheEntry.StreamSize;
  404.            
  405.             result = Rfc2616.OnValidateCache(this);
  406.             if (result != CacheValidationStatus.ReturnCachedResponse && this.Policy.Level == HttpRequestCacheLevel.CacheOnly) {
  407.                 // Throw because entry was not found and it's cache-only policy
  408.                 FailRequest(WebExceptionStatus.CacheEntryNotFound);
  409.             }
  410.            
  411.             if (result == CacheValidationStatus.ReturnCachedResponse) {
  412.                 if (CacheFreshnessStatus == CacheFreshnessStatus.Stale) {
  413.                     CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_110);
  414.                 }
  415.                 if (base.Policy.Level == RequestCacheLevel.CacheOnly) {
  416.                     CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_112);
  417.                 }
  418.                 if (HeuristicExpiration && (int)CacheAge.TotalSeconds >= 24 * 3600) {
  419.                     CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_113);
  420.                 }
  421.             }
  422.            
  423.             if (result == CacheValidationStatus.DoNotTakeFromCache) {
  424.                 // We signal that current cache entry can be only replaced and not updated
  425.                 CacheStatusCode = (HttpStatusCode)0;
  426.             }
  427.             else if (result == CacheValidationStatus.ReturnCachedResponse) {
  428.                 CacheHeaders[HttpKnownHeaderNames.Age] = ((int)(CacheAge.TotalSeconds)).ToString(NumberFormatInfo.InvariantInfo);
  429.             }
  430.             return result;
  431.         }
  432.         //
  433.         // This is (optionally) called after receiveing a live response
  434.         //
  435.         protected internal override CacheValidationStatus RevalidateCache()
  436.         {
  437.             if (this.Policy.Level != HttpRequestCacheLevel.Revalidate && base.Policy.Level >= RequestCacheLevel.Reload) {
  438.                 // For those policies cache is never returned
  439.                 GlobalLog.Assert("RevalidateCache()", "This validator should not be called for policy = " + Policy.ToString());
  440.                 if (Logging.On)
  441.                     Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_validator_invalid_for_policy, Policy.ToString()));
  442.                 return CacheValidationStatus.DoNotTakeFromCache;
  443.             }
  444.            
  445.             // First check is do we have a cached entry at all?
  446.             // Also we include some very special case where cache got a 304 (NotModified) response somehow
  447.             if (CacheStream == Stream.Null || (int)CacheStatusCode == 0 || CacheStatusCode == HttpStatusCode.NotModified) {
  448.                 return CacheValidationStatus.DoNotTakeFromCache;
  449.             }
  450.            
  451.             //
  452.             // This is a second+ time validation after receiving at least one response
  453.             //
  454.            
  455.             // Apply our best knowledge of HTTP caching and return the result
  456.             // that can be hooked up and revised by the upper level
  457.             CacheValidationStatus result = CacheValidationStatus.DoNotTakeFromCache;
  458.            
  459.             HttpWebResponse resp = Response as HttpWebResponse;
  460.             if (resp == null) {
  461.                 // This will result to an application error
  462.                 return CacheValidationStatus.DoNotTakeFromCache;
  463.             }
  464.            
  465.             if (resp.StatusCode >= HttpStatusCode.InternalServerError) {
  466.                 // If server returned a 5XX server error
  467.                 if (Rfc2616.Common.ValidateCacheOn5XXResponse(this) == CacheValidationStatus.ReturnCachedResponse) {
  468.                     // We can substitute the response from cache
  469.                     if (CacheFreshnessStatus == CacheFreshnessStatus.Stale) {
  470.                         CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_110);
  471.                     }
  472.                     if (HeuristicExpiration && (int)CacheAge.TotalSeconds >= 24 * 3600) {
  473.                         CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_113);
  474.                     }
  475.                     // We actually failed to reach the origin server hence we don't reset the current Cache Age
  476.                 }
  477.             }
  478.             else {
  479.                
  480.                 // if there was already one retry, then cache should not be taken into account
  481.                 if (ResponseCount > 1) {
  482.                     result = CacheValidationStatus.DoNotTakeFromCache;
  483.                 }
  484.                 else {
  485.                     /*
  486.                     Section 13.2.3:
  487.                     HTTP/1.1 uses the Age response-header to convey the estimated age
  488.                     of the response message when obtained from a cache.
  489.                     The Age field value is the cache's estimate of the amount of time
  490.                     since the response was generated or >>revalidated<< by the origin server.
  491.                     */                   
  492.                     // Reset Cache Age to be 0 seconds
  493. CacheAge = TimeSpan.Zero;
  494.                     result = Rfc2616.Common.ValidateCacheAfterResponse(this, resp);
  495.                 }
  496.             }
  497.            
  498.             if (result == CacheValidationStatus.ReturnCachedResponse) {
  499.                 CacheHeaders[HttpKnownHeaderNames.Age] = ((int)(CacheAge.TotalSeconds)).ToString(NumberFormatInfo.InvariantInfo);
  500.             }
  501.             return result;
  502.         }
  503.        
  504.         /// <summary>
  505.         /// <para>
  506.         /// This validation method is responsible to answer whether the live response is sufficient to make
  507.         /// the final decision for caching protocol.
  508.         /// This is useful in case of possible failure or inconsistent results received from
  509.         /// the remote cache.
  510.         /// </para>
  511.         /// </summary>
  512.         /// <remarks> Invalid response from this method means the request was internally modified and should be retried </remarks>
  513.         protected internal override CacheValidationStatus ValidateResponse()
  514.         {
  515.            
  516.             if (this.Policy.Level != HttpRequestCacheLevel.CacheOrNextCacheOnly && this.Policy.Level != HttpRequestCacheLevel.Default && this.Policy.Level != HttpRequestCacheLevel.Revalidate) {
  517.                 // Those policy levels do not modify requests
  518.                 if (Logging.On)
  519.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_response_valid_based_on_policy, Policy.ToString()));
  520.                 return CacheValidationStatus.Continue;
  521.             }
  522.            
  523.             // We will need quick access to cache controls coming with the live response
  524.             HttpWebResponse resp = Response as HttpWebResponse;
  525.             if (resp == null) {
  526.                 if (Logging.On)
  527.                     Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_null_response_failure));
  528.                 return CacheValidationStatus.Continue;
  529.             }
  530.            
  531.             FetchHeaderValues(false);
  532.             if (Logging.On)
  533.                 Logging.PrintInfo(Logging.RequestCache, "StatusCode=" + ((int)resp.StatusCode).ToString(CultureInfo.InvariantCulture) + ' ' + resp.StatusCode.ToString() + (resp.StatusCode == HttpStatusCode.PartialContent ? ", Content-Range: " + resp.Headers[HttpKnownHeaderNames.ContentRange] : string.Empty));
  534.            
  535.            
  536.             // Apply our best knowledge of HTTP caching and return the result
  537.             // that can be hooked up and revised by the upper level
  538.             return Rfc2616.OnValidateResponse(this);
  539.         }
  540.        
  541.         /// <summary>
  542.         /// <para>
  543.         /// This action handler is responsible for making final decision on whether
  544.         /// a received response can be cached.
  545.         /// </para>
  546.         /// </summary>
  547.         /// <remarks> Invalid result from this method means the response must not be cached </remarks>
  548.         protected internal override CacheValidationStatus UpdateCache()
  549.         {
  550.            
  551.             if (this.Policy.Level == HttpRequestCacheLevel.NoCacheNoStore) {
  552.                 if (Logging.On)
  553.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_removed_existing_based_on_policy, Policy.ToString()));
  554.                 return CacheValidationStatus.RemoveFromCache;
  555.             }
  556.             if (this.Policy.Level == HttpRequestCacheLevel.CacheOnly) {
  557.                 if (Logging.On)
  558.                     Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_not_updated_based_on_policy, Policy.ToString()));
  559.                 return CacheValidationStatus.DoNotUpdateCache;
  560.             }
  561.            
  562.             if (CacheHeaders == null)
  563.                 CacheHeaders = new WebHeaderCollection();
  564.            
  565.             if (SystemMeta == null)
  566.                 SystemMeta = new NameValueCollection(1, CaseInsensitiveAscii.StaticInstance);
  567.            
  568.             if (ResponseCacheControl == null) {
  569.                 //ValidateResponse was not invoked
  570.                 FetchHeaderValues(false);
  571.             }
  572.            
  573.             // Apply our best knowledge of HTTP caching and return the result
  574.             // that can be hooked up and revised by the upper level
  575.             CacheValidationStatus result = Rfc2616.OnUpdateCache(this);
  576.            
  577.             if (result == CacheValidationStatus.UpdateResponseInformation || result == CacheValidationStatus.CacheResponse) {
  578.                 FinallyUpdateCacheEntry();
  579.             }
  580.             return result;
  581.         }
  582.        
  583.         //
  584.         //
  585.         //
  586.         private void FinallyUpdateCacheEntry()
  587.         {
  588.             // Transfer the context status line back to the metadata
  589.            
  590.             CacheEntry.EntryMetadata = null;
  591.             CacheEntry.SystemMetadata = null;
  592.            
  593.             if (CacheHeaders == null) {
  594.                 //must be an entry update without updating the headers
  595.                 return;
  596.             }
  597.            
  598.             CacheEntry.EntryMetadata = new StringCollection();
  599.             CacheEntry.SystemMetadata = new StringCollection();
  600.            
  601.             if (CacheHttpVersion == null) {
  602.                 if (Logging.On)
  603.                     Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_invalid_http_version));
  604.                 CacheHttpVersion = new Version(1, 0);
  605.             }
  606.             // HTTP/1.1 200 OK
  607.             System.Text.StringBuilder sb = new System.Text.StringBuilder(CacheStatusDescription.Length + 20);
  608.             sb.Append("HTTP/");
  609.             sb.Append(CacheHttpVersion.ToString(2));
  610.             sb.Append(' ');
  611.             sb.Append(((int)CacheStatusCode).ToString(NumberFormatInfo.InvariantInfo));
  612.             sb.Append(' ');
  613.             sb.Append(CacheStatusDescription);
  614.            
  615.             // Fetch the status line into cache metadata
  616.             CacheEntry.EntryMetadata.Add(sb.ToString());
  617.            
  618.             UpdateStringCollection(CacheEntry.EntryMetadata, CacheHeaders, false);
  619.            
  620.             if (SystemMeta != null) {
  621.                 UpdateStringCollection(CacheEntry.SystemMetadata, SystemMeta, true);
  622.             }
  623.            
  624.             // Update other entry values
  625.             if (ResponseExpires != DateTime.MinValue) {
  626.                 CacheEntry.ExpiresUtc = ResponseExpires;
  627.             }
  628.            
  629.             if (ResponseLastModified != DateTime.MinValue) {
  630.                 CacheEntry.LastModifiedUtc = ResponseLastModified;
  631.             }
  632.            
  633.             if (this.Policy.Level == HttpRequestCacheLevel.Default) {
  634.                 CacheEntry.MaxStale = this.Policy.MaxStale;
  635.             }
  636.            
  637.             CacheEntry.LastSynchronizedUtc = DateTime.UtcNow;
  638.         }
  639.         //
  640.         //
  641.         //
  642.         private static void UpdateStringCollection(StringCollection result, NameValueCollection cc, bool winInetCompat)
  643.         {
  644.             StringBuilder sb;
  645.            
  646.             // Transfer headers
  647.             for (int i = 0; i < cc.Count; ++i) {
  648.                 sb = new StringBuilder(40);
  649.                 string key = cc.GetKey(i) as string;
  650.                 sb.Append(key).Append(':');
  651.                
  652.                 string[] val = cc.GetValues(i);
  653.                 if (val.Length != 0) {
  654.                     if (winInetCompat) {
  655.                         sb.Append(val[0]);
  656.                     }
  657.                     else {
  658.                         sb.Append(' ').Append(val[0]);
  659.                     }
  660.                 }
  661.                
  662.                 for (int j = 1; j < val.Length; ++j) {
  663.                     sb.Append(key).Append(", ").Append(val[j]);
  664.                 }
  665.                 result.Add(sb.ToString());
  666.             }
  667.             // Transfer last \r\n
  668.             result.Add(string.Empty);
  669.         }
  670.        
  671.         // The format is
  672.         // HTTP/X.Y SP NUMBER SP STRING
  673.         // HTTP/1.1 200 OK
  674.         //
  675.         private string ParseStatusLine()
  676.         {
  677.            
  678.             // This will indicate an invalid result
  679.             CacheStatusCode = (HttpStatusCode)0;
  680.            
  681.             if (CacheEntry.EntryMetadata == null || CacheEntry.EntryMetadata.Count == 0) {
  682.                 return null;
  683.             }
  684.            
  685.             string s = CacheEntry.EntryMetadata[0];
  686.            
  687.             if (s == null) {
  688.                 return null;
  689.             }
  690.            
  691.             int idx = 0;
  692.             char ch = (char)0;
  693.             while (++idx < s.Length && (ch = s[idx]) != '/') {
  694.                 ;
  695.             }
  696.            
  697.             if (idx == s.Length) {
  698.                 return s;
  699.             }
  700.            
  701.             int major = -1;
  702.             int minor = -1;
  703.             int status = -1;
  704.            
  705.             while (++idx < s.Length && (ch = s[idx]) >= '0' && ch <= '9') {
  706.                 major = (major < 0 ? 0 : major * 10) + (ch - '0');
  707.             }
  708.            
  709.             if (major < 0 || ch != '.') {
  710.                 return s;
  711.             }
  712.            
  713.             while (++idx < s.Length && (ch = s[idx]) >= '0' && ch <= '9') {
  714.                 minor = (minor < 0 ? 0 : minor * 10) + (ch - '0');
  715.             }
  716.            
  717.             if (minor < 0 || (ch != ' ' && ch != '\t')) {
  718.                 return s;
  719.             }
  720.            
  721.             while (++idx < s.Length && ((ch = s[idx]) == ' ' || ch == '\t'))
  722.                 ;
  723.            
  724.             if (idx >= s.Length) {
  725.                 return s;
  726.             }
  727.            
  728.             while (ch >= '0' && ch <= '9') {
  729.                 status = (status < 0 ? 0 : status * 10) + (ch - '0');
  730.                 if (++idx == s.Length)
  731.                     break;
  732.                 ch = s[idx];
  733.             }
  734.            
  735.             if (status < 0 || (idx <= s.Length && (ch != ' ' && ch != '\t'))) {
  736.                 return s;
  737.             }
  738.            
  739.             while (idx < s.Length && (s[idx] == ' ' || s[idx] == '\t'))
  740.                 ++idx;
  741.            
  742.             CacheStatusDescription = s.Substring(idx);
  743.            
  744.             CacheHttpVersion = new Version(major, minor);
  745.             CacheStatusCode = (HttpStatusCode)status;
  746.             return s;
  747.         }
  748.         //
  749.         private void CreateCacheHeaders(bool ignoreFirstString)
  750.         {
  751.            
  752.             if (CacheHeaders == null)
  753.                 CacheHeaders = new WebHeaderCollection();
  754.            
  755.             if (CacheEntry.EntryMetadata == null || CacheEntry.EntryMetadata.Count == 0) {
  756.                 if (Logging.On)
  757.                     Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_no_http_response_header));
  758.                 return;
  759.             }
  760.            
  761.             string s = ParseNameValues(CacheHeaders, CacheEntry.EntryMetadata, ignoreFirstString ? 1 : 0);
  762.             if (s != null) {
  763.                 if (Logging.On)
  764.                     Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_http_header_parse_error, s));
  765.                 CacheHeaders.Clear();
  766.             }
  767.         }
  768.         //
  769.         private void CreateSystemMeta()
  770.         {
  771.             if (SystemMeta == null) {
  772.                 SystemMeta = new NameValueCollection((CacheEntry.EntryMetadata == null || CacheEntry.EntryMetadata.Count == 0 ? 2 : CacheEntry.EntryMetadata.Count), CaseInsensitiveAscii.StaticInstance);
  773.             }
  774.             if (CacheEntry.EntryMetadata == null || CacheEntry.EntryMetadata.Count == 0) {
  775.                 return;
  776.             }
  777.            
  778.             string s = ParseNameValues(SystemMeta, CacheEntry.SystemMetadata, 0);
  779.             if (s != null) {
  780.                 if (Logging.On)
  781.                     Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_metadata_name_value_parse_error, s));
  782.             }
  783.         }
  784.         //
  785.         // Returns null on success, otherwise the offending string.
  786.         //
  787.         private string ParseNameValues(NameValueCollection cc, StringCollection sc, int start)
  788.         {
  789.             WebHeaderCollection wc = cc as WebHeaderCollection;
  790.            
  791.             string lastHeaderName = null;
  792.             if (sc != null) {
  793.                 for (int i = start; i < sc.Count; ++i) {
  794.                     string s = sc[i];
  795.                     if (s == null || s.Length == 0) {
  796.                         //An empty string stands for \r\n
  797.                         //Treat that as the end of headers and ignore the rest
  798.                         return null;
  799.                     }
  800.                    
  801.                     if (s[0] == ' ' || s[0] == '\t') {
  802.                         if (lastHeaderName == null) {
  803.                             return s;
  804.                         }
  805.                         if (wc != null)
  806.                             wc.AddInternal(lastHeaderName, s);
  807.                         else
  808.                             cc.Add(lastHeaderName, s);
  809.                     }
  810.                    
  811.                     int colpos = s.IndexOf(':');
  812.                     if (colpos < 0) {
  813.                         return s;
  814.                     }
  815.                     lastHeaderName = s.Substring(0, colpos);
  816.                     while (++colpos < s.Length && (s[colpos] == ' ' || s[colpos] == '\t')) {
  817.                         ;
  818.                     }
  819.                    
  820.                     try {
  821.                         if (wc != null)
  822.                             wc.AddInternal(lastHeaderName, s.Substring(colpos));
  823.                         else
  824.                             cc.Add(lastHeaderName, s.Substring(colpos));
  825.                     }
  826.                     catch (Exception e) {
  827.                         if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException)
  828.                             throw;
  829.                         // Otherwise the value of 's' will be used to log an error.
  830.                         // The fact that we cannot parse headers may stand for corrupted metadata that we try to ignore
  831.                         return s;
  832.                     }
  833.                 }
  834.             }
  835.             return null;
  836.         }
  837.         //
  838.         //
  839.         //
  840.         private void FetchHeaderValues(bool forCache)
  841.         {
  842.            
  843.             WebHeaderCollection cc = forCache ? CacheHeaders : Response.Headers;
  844.            
  845.            
  846.             FetchCacheControl(cc.CacheControl, forCache);
  847.            
  848.             // Parse Date Header
  849.             string s = cc.Date;
  850.            
  851.             DateTime date = DateTime.MinValue;
  852.             if (s != null && HttpDateParse.ParseHttpDate(s, out date)) {
  853.                 date = date.ToUniversalTime();
  854.             }
  855.             if (forCache) {
  856.                 CacheDate = date;
  857.             }
  858.             else {
  859.                 ResponseDate = date;
  860.             }
  861.            
  862.             // Parse Expires Header
  863.             s = cc.Expires;
  864.            
  865.             date = DateTime.MinValue;
  866.             if (s != null && HttpDateParse.ParseHttpDate(s, out date)) {
  867.                 date = date.ToUniversalTime();
  868.             }
  869.             if (forCache) {
  870.                 CacheExpires = date;
  871.             }
  872.             else {
  873.                 ResponseExpires = date;
  874.             }
  875.            
  876.             // Parse LastModified Header
  877.             s = cc.LastModified;
  878.            
  879.             date = DateTime.MinValue;
  880.             if (s != null && HttpDateParse.ParseHttpDate(s, out date)) {
  881.                 date = date.ToUniversalTime();
  882.             }
  883.             if (forCache) {
  884.                 CacheLastModified = date;
  885.             }
  886.             else {
  887.                 ResponseLastModified = date;
  888.             }
  889.            
  890.             long totalLength = -1;
  891.             long startRange = -1;
  892.             long end = -1;
  893.            
  894.             HttpWebResponse resp = Response as HttpWebResponse;
  895.             if ((forCache ? CacheStatusCode : resp.StatusCode) != HttpStatusCode.PartialContent) {
  896.                
  897.                 // Parse Content-Length Header
  898.                 s = cc.ContentLength;
  899.                 if (s != null && s.Length != 0) {
  900.                     int i = 0;
  901.                     char ch = s[0];
  902.                     while (i < s.Length && ch == ' ') {
  903.                         ch = s[++i];
  904.                     }
  905.                     if (i != s.Length && ch >= '0' && ch <= '9') {
  906.                         totalLength = ch - '0';
  907.                         while (++i < s.Length && (ch = s[i]) >= '0' && ch <= '9') {
  908.                             totalLength = totalLength * 10 + (ch - '0');
  909.                         }
  910.                     }
  911.                 }
  912.             }
  913.             else {
  914.                 //Parse Content-Range
  915.                 s = cc[HttpKnownHeaderNames.ContentRange];
  916.                 if (s == null || !Rfc2616.Common.GetBytesRange(s, ref startRange, ref end, ref totalLength, false)) {
  917.                     if (Logging.On)
  918.                         Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_content_range_error, (s == null ? "<null>" : s)));
  919.                     startRange = end = totalLength = -1;
  920.                 }
  921.                 else if (forCache && totalLength == CacheEntry.StreamSize) {
  922.                     // This is a whole response, step back to 200
  923.                     startRange = -1;
  924.                     end = -1;
  925.                     CacheStatusCode = HttpStatusCode.OK;
  926.                     CacheStatusDescription = Rfc2616.Common.OkDescription;
  927.                 }
  928.             }
  929.            
  930.             if (forCache) {
  931.                 CacheEntityLength = totalLength;
  932.                 ResponseRangeStart = startRange;
  933.                 ResponseRangeEnd = end;
  934.                
  935.             }
  936.             else {
  937.                 ResponseEntityLength = totalLength;
  938.                 ResponseRangeStart = startRange;
  939.                 ResponseRangeEnd = end;
  940.             }
  941.            
  942.             //Parse Age Header
  943.             TimeSpan span = TimeSpan.MinValue;
  944.             s = cc[HttpKnownHeaderNames.Age];
  945.             if (s != null) {
  946.                 int i = 0;
  947.                 int sec = 0;
  948.                 while (i < s.Length && s[i++] == ' ') {
  949.                     ;
  950.                 }
  951.                 while (i < s.Length && s[i] >= '0' && s[i] <= '9') {
  952.                     sec = sec * 10 + (s[i++] - '0');
  953.                 }
  954.                 span = TimeSpan.FromSeconds(sec);
  955.             }
  956.            
  957.             if (forCache) {
  958.                 CacheAge = span;
  959.             }
  960.             else {
  961.                 ResponseAge = span;
  962.             }
  963.         }
  964.        
  965.         const long LO = 9007336695791648l;
  966.         const int LOI = 2097184;
  967.         const long _prox = 'p' | ('r' << 16) | ((long)'o' << 32) | ((long)'x' << 48);
  968.         const long _y_re = 'y' | ('-' << 16) | ((long)'r' << 32) | ((long)'e' << 48);
  969.         const long _vali = 'v' | ('a' << 16) | ((long)'l' << 32) | ((long)'i' << 48);
  970.         const long _date = 'd' | ('a' << 16) | ((long)'t' << 32) | ((long)'e' << 48);
  971.        
  972.         const long _publ = 'p' | ('u' << 16) | ((long)'b' << 32) | ((long)'l' << 48);
  973.         const int _ic = 'i' | ('c' << 16);
  974.        
  975.         const long _priv = 'p' | ('r' << 16) | ((long)'i' << 32) | ((long)'v' << 48);
  976.         const int _at = 'a' | ('t' << 16);
  977.        
  978.         const long _no_c = 'n' | ('o' << 16) | ((long)'-' << 32) | ((long)'c' << 48);
  979.         const long _ache = 'a' | ('c' << 16) | ((long)'h' << 32) | ((long)'e' << 48);
  980.        
  981.         const long _no_s = 'n' | ('o' << 16) | ((long)'-' << 32) | ((long)'s' << 48);
  982.         const long _tore = 't' | ('o' << 16) | ((long)'r' << 32) | ((long)'e' << 48);
  983.        
  984.         const long _must = 'm' | ('u' << 16) | ((long)'s' << 32) | ((long)'t' << 48);
  985.         const long __rev = '-' | ('r' << 16) | ((long)'e' << 32) | ((long)'v' << 48);
  986.         const long _alid = 'a' | ('l' << 16) | ((long)'i' << 32) | ((long)'d' << 48);
  987.        
  988.         const long _max_ = 'm' | ('a' << 16) | ((long)'x' << 32) | ((long)'-' << 48);
  989.         const int _ag = 'a' | ('g' << 16);
  990.        
  991.         const long _s_ma = 's' | ('-' << 16) | ((long)'m' << 32) | ((long)'a' << 48);
  992.         const long _xage = 'x' | ('a' << 16) | ((long)'g' << 32) | ((long)'e' << 48);
  993.         //
  994.         //
  995.         //
  996.         unsafe private void FetchCacheControl(string s, bool forCache)
  997.         {
  998.             //Initialize it
  999.             ResponseCacheControl control = new ResponseCacheControl();
  1000.             if (forCache) {
  1001.                 CacheCacheControl = control;
  1002.             }
  1003.             else {
  1004.                 ResponseCacheControl = control;
  1005.             }
  1006.            
  1007.             if (s != null && s.Length != 0) {
  1008.                 fixed (char* sp = s) {
  1009.                     int len = s.Length;
  1010.                     for (int i = 0; i < len - 4; ++i) {
  1011.                         if (sp[i] < ' ' || sp[i] >= 127) {
  1012.                             if (Logging.On)
  1013.                                 Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_cache_control_error, s));
  1014.                             //invalid format
  1015.                             return;
  1016.                         }
  1017.                         if (sp[i] == ' ' || sp[i] == ',') {
  1018.                             continue;
  1019.                         }
  1020.                        
  1021.                         // These if-else are two logically identical blocks that differ only in the way of how text search is done.
  1022.                         // The text search is done differently for 32 and X-bits platforms.
  1023.                         // ATTN: You are responsible for keeping the rest of the logic in sync.
  1024.                         if (IntPtr.Size == 4) {
  1025.                             // We are on 32-bits platform
  1026.                            
  1027.                             long* mask = (long*)&(sp[i]);
  1028.                             //making interested chars lowercase, others are ignored anyway
  1029.                             switch (*mask | LO) {
  1030.                                 case _prox:
  1031.                                    
  1032.                                     if (i + 16 > len)
  1033.                                         continue;
  1034.                                     if ((*(mask + 1) | LO) != _y_re || (*(mask + 2) | LO) != _vali || (*(mask + 3) | LO) != _date)
  1035.                                         continue;
  1036.                                     control.ProxyRevalidate = true;
  1037.                                     i += 15;
  1038.                                     break;
  1039.                                 case _publ:
  1040.                                    
  1041.                                     if (i + 6 > len)
  1042.                                         return;
  1043.                                     if ((*((int*)(mask + 1)) | LOI) != _ic)
  1044.                                         continue;
  1045.                                     control.Public = true;
  1046.                                     i += 5;
  1047.                                     break;
  1048.                                 case _priv:
  1049.                                    
  1050.                                     if (i + 7 > len)
  1051.                                         return;
  1052.                                     if ((*((int*)(mask + 1)) | LOI) != _at || (sp[i + 6] | 32) != 'e')
  1053.                                         continue;
  1054.                                     control.Private = true;
  1055.                                     i += 6;
  1056.                                     // Check for a case: private = "name1,name2"
  1057.                                     while (i < len && sp[i] == ' ') {
  1058.                                         ++i;
  1059.                                     }
  1060.                                    
  1061.                                     if (i >= len || sp[i] != '=') {
  1062.                                         --i;
  1063.                                         break;
  1064.                                     }
  1065.                                    
  1066.                                     while (i < len && sp[++i] == ' ') {
  1067.                                         ;
  1068.                                     }
  1069.                                    
  1070.                                     if (i >= len || sp[i] != '"') {
  1071.                                         --i;
  1072.                                         break;
  1073.                                     }
  1074.                                    
  1075.                                     System.Collections.ArrayList privateList = new System.Collections.ArrayList();
  1076.                                     ++i;
  1077.                                     while (i < len && sp[i] != '"') {
  1078.                                        
  1079.                                         while (i < len && sp[i] == ' ') {
  1080.                                             ++i;
  1081.                                         }
  1082.                                         int start = i;
  1083.                                         while (i < len && sp[i] != ' ' && sp[i] != ',' && sp[i] != '"') {
  1084.                                             ++i;
  1085.                                         }
  1086.                                         if (start != i) {
  1087.                                             privateList.Add(s.Substring(start, i - start));
  1088.                                         }
  1089.                                         while (i < len && sp[i] != ',' && sp[i] != '"') {
  1090.                                             ++i;
  1091.                                         }
  1092.                                     }
  1093.                                     if (privateList.Count != 0) {
  1094.                                         control.PrivateHeaders = (string[])privateList.ToArray(typeof(string));
  1095.                                     }
  1096.                                     break;
  1097.                                 case _no_c:
  1098.                                    
  1099.                                     if (i + 8 > len)
  1100.                                         return;
  1101.                                     if ((*(mask + 1) | LOI) != _ache)
  1102.                                         continue;
  1103.                                     control.NoCache = true;
  1104.                                     i += 7;
  1105.                                     // Check for a case: no-cache = "name1,name2"
  1106.                                     while (i < len && sp[i] == ' ') {
  1107.                                         ++i;
  1108.                                     }
  1109.                                    
  1110.                                     if (i >= len || sp[i] != '=') {
  1111.                                         --i;
  1112.                                         break;
  1113.                                     }
  1114.                                    
  1115.                                     while (i < len && sp[++i] == ' ') {
  1116.                                         ;
  1117.                                     }
  1118.                                    
  1119.                                     if (i >= len || sp[i] != '"') {
  1120.                                         --i;
  1121.                                         break;
  1122.                                     }
  1123.                                    
  1124.                                     System.Collections.ArrayList nocacheList = new System.Collections.ArrayList();
  1125.                                     ++i;
  1126.                                     while (i < len && sp[i] != '"') {
  1127.                                        
  1128.                                         while (i < len && sp[i] == ' ') {
  1129.                                             ++i;
  1130.                                         }
  1131.                                         int start = i;
  1132.                                         while (i < len && sp[i] != ' ' && sp[i] != ',' && sp[i] != '"') {
  1133.                                             ++i;
  1134.                                         }
  1135.                                         if (start != i) {
  1136.                                             nocacheList.Add(s.Substring(start, i - start));
  1137.                                         }
  1138.                                         while (i < len && sp[i] != ',' && sp[i] != '"') {
  1139.                                             ++i;
  1140.                                         }
  1141.                                     }
  1142.                                     if (nocacheList.Count != 0) {
  1143.                                         control.NoCacheHeaders = (string[])nocacheList.ToArray(typeof(string));
  1144.                                     }
  1145.                                     break;
  1146.                                 case _no_s:
  1147.                                    
  1148.                                     if (i + 8 > len)
  1149.                                         return;
  1150.                                     if ((*(mask + 1) | LOI) != _tore)
  1151.                                         continue;
  1152.                                     control.NoStore = true;
  1153.                                     i += 7;
  1154.                                     break;
  1155.                                 case _must:
  1156.                                    
  1157.                                     if (i + 15 > len)
  1158.                                         continue;
  1159.                                    
  1160.                                     if ((*(mask + 1) | LO) != __rev || (*(mask + 2) | LO) != _alid || (*(int*)(mask + 3) | LOI) != _at || (sp[i + 14] | 32) != 'e')
  1161.                                         continue;
  1162.                                     control.MustRevalidate = true;
  1163.                                     i += 14;
  1164.                                     break;
  1165.                                 case _max_:
  1166.                                    
  1167.                                     if (i + 7 > len)
  1168.                                         return;
  1169.                                     if ((*((int*)(mask + 1)) | LOI) != _ag || (sp[i + 6] | 32) != 'e')
  1170.                                         continue;
  1171.                                     i += 7;
  1172.                                     while (i < len && sp[i] == ' ') {
  1173.                                         ++i;
  1174.                                     }
  1175.                                     if (i == len || sp[i++] != '=')
  1176.                                         return;
  1177.                                     while (i < len && sp[i] == ' ') {
  1178.                                         ++i;
  1179.                                     }
  1180.                                     if (i == len)
  1181.                                         return;
  1182.                                     control.MaxAge = 0;
  1183.                                     while (i < len && sp[i] >= '0' && sp[i] <= '9') {
  1184.                                         control.MaxAge = control.MaxAge * 10 + (sp[i++] - '0');
  1185.                                     }
  1186.                                     --i;
  1187.                                     break;
  1188.                                 case _s_ma:
  1189.                                    
  1190.                                     if (i + 8 > len)
  1191.                                         return;
  1192.                                     if ((*(mask + 1) | LOI) != _xage)
  1193.                                         continue;
  1194.                                     i += 8;
  1195.                                     while (i < len && sp[i] == ' ') {
  1196.                                         ++i;
  1197.                                     }
  1198.                                     if (i == len || sp[i++] != '=')
  1199.                                         return;
  1200.                                     while (i < len && sp[i] == ' ') {
  1201.                                         ++i;
  1202.                                     }
  1203.                                     if (i == len)
  1204.                                         return;
  1205.                                     control.SMaxAge = 0;
  1206.                                     while (i < len && sp[i] >= '0' && sp[i] <= '9') {
  1207.                                         control.SMaxAge = control.SMaxAge * 10 + (sp[i++] - '0');
  1208.                                     }
  1209.                                     --i;
  1210.                                     break;
  1211.                             }
  1212.                         }
  1213.                         else {
  1214.                             if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "proxy-revalidate")) {
  1215.                                 control.ProxyRevalidate = true;
  1216.                                 i += 15;
  1217.                             }
  1218.                             else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "public")) {
  1219.                                 control.Public = true;
  1220.                                 i += 5;
  1221.                             }
  1222.                             else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "private")) {
  1223.                                 control.Private = true;
  1224.                                 i += 6;
  1225.                                 // Check for a case: private = "name1,name2"
  1226.                                 while (i < len && sp[i] == ' ') {
  1227.                                     ++i;
  1228.                                 }
  1229.                                
  1230.                                 if (i >= len || sp[i] != '=') {
  1231.                                     --i;
  1232.                                     break;
  1233.                                 }
  1234.                                
  1235.                                 while (i < len && sp[++i] == ' ') {
  1236.                                     ;
  1237.                                 }
  1238.                                
  1239.                                 if (i >= len || sp[i] != '"') {
  1240.                                     --i;
  1241.                                     break;
  1242.                                 }
  1243.                                
  1244.                                 System.Collections.ArrayList privateList = new System.Collections.ArrayList();
  1245.                                 ++i;
  1246.                                 while (i < len && sp[i] != '"') {
  1247.                                    
  1248.                                     while (i < len && sp[i] == ' ') {
  1249.                                         ++i;
  1250.                                     }
  1251.                                     int start = i;
  1252.                                     while (i < len && sp[i] != ' ' && sp[i] != ',' && sp[i] != '"') {
  1253.                                         ++i;
  1254.                                     }
  1255.                                     if (start != i) {
  1256.                                         privateList.Add(s.Substring(start, i - start));
  1257.                                     }
  1258.                                     while (i < len && sp[i] != ',' && sp[i] != '"') {
  1259.                                         ++i;
  1260.                                     }
  1261.                                 }
  1262.                                 if (privateList.Count != 0) {
  1263.                                     control.PrivateHeaders = (string[])privateList.ToArray(typeof(string));
  1264.                                 }
  1265.                             }
  1266.                             else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "no-cache")) {
  1267.                                 control.NoCache = true;
  1268.                                 i += 7;
  1269.                                 // Check for a case: no-cache = "name1,name2"
  1270.                                 while (i < len && sp[i] == ' ') {
  1271.                                     ++i;
  1272.                                 }
  1273.                                
  1274.                                 if (i >= len || sp[i] != '=') {
  1275.                                     --i;
  1276.                                     break;
  1277.                                 }
  1278.                                
  1279.                                 while (i < len && sp[++i] == ' ') {
  1280.                                     ;
  1281.                                 }
  1282.                                
  1283.                                 if (i >= len || sp[i] != '"') {
  1284.                                     --i;
  1285.                                     break;
  1286.                                 }
  1287.                                
  1288.                                 System.Collections.ArrayList nocacheList = new System.Collections.ArrayList();
  1289.                                 ++i;
  1290.                                 while (i < len && sp[i] != '"') {
  1291.                                    
  1292.                                     while (i < len && sp[i] == ' ') {
  1293.                                         ++i;
  1294.                                     }
  1295.                                     int start = i;
  1296.                                     while (i < len && sp[i] != ' ' && sp[i] != ',' && sp[i] != '"') {
  1297.                                         ++i;
  1298.                                     }
  1299.                                     if (start != i) {
  1300.                                         nocacheList.Add(s.Substring(start, i - start));
  1301.                                     }
  1302.                                     while (i < len && sp[i] != ',' && sp[i] != '"') {
  1303.                                         ++i;
  1304.                                     }
  1305.                                 }
  1306.                                 if (nocacheList.Count != 0) {
  1307.                                     control.NoCacheHeaders = (string[])nocacheList.ToArray(typeof(string));
  1308.                                 }
  1309.                             }
  1310.                             else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "no-store")) {
  1311.                                 control.NoStore = true;
  1312.                                 i += 7;
  1313.                             }
  1314.                             else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "must-revalidate")) {
  1315.                                 control.MustRevalidate = true;
  1316.                                 i += 14;
  1317.                             }
  1318.                             else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "max-age")) {
  1319.                                 i += 7;
  1320.                                 while (i < len && sp[i] == ' ') {
  1321.                                     ++i;
  1322.                                 }
  1323.                                 if (i == len || sp[i++] != '=')
  1324.                                     return;
  1325.                                 while (i < len && sp[i] == ' ') {
  1326.                                     ++i;
  1327.                                 }
  1328.                                 if (i == len)
  1329.                                     return;
  1330.                                 control.MaxAge = 0;
  1331.                                 while (i < len && sp[i] >= '0' && sp[i] <= '9') {
  1332.                                     control.MaxAge = control.MaxAge * 10 + (sp[i++] - '0');
  1333.                                 }
  1334.                                 --i;
  1335.                             }
  1336.                             else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "smax-age")) {
  1337.                                 i += 8;
  1338.                                 while (i < len && sp[i] == ' ') {
  1339.                                     ++i;
  1340.                                 }
  1341.                                 if (i == len || sp[i++] != '=')
  1342.                                     return;
  1343.                                 while (i < len && sp[i] == ' ') {
  1344.                                     ++i;
  1345.                                 }
  1346.                                 if (i == len)
  1347.                                     return;
  1348.                                 control.SMaxAge = 0;
  1349.                                 while (i < len && sp[i] >= '0' && sp[i] <= '9') {
  1350.                                     control.SMaxAge = control.SMaxAge * 10 + (sp[i++] - '0');
  1351.                                 }
  1352.                                 --i;
  1353.                             }
  1354.                         }
  1355.                     }
  1356.                 }
  1357.             }
  1358.         }
  1359.        
  1360. /*
  1361.           - any stored Warning headers with warn-code 1xx (see section
  1362.         14.46) MUST be deleted from the cache entry and the forwarded
  1363.         response.
  1364.           - any stored Warning headers with warn-code 2xx MUST be retained
  1365.         in the cache entry and the forwarded response.
  1366.         */       
  1367.         private void RemoveWarnings_1xx()
  1368.         {
  1369.            
  1370.             string[] warnings = CacheHeaders.GetValues(HttpKnownHeaderNames.Warning);
  1371.             if (warnings == null) {
  1372.                 return;
  1373.             }
  1374.             ArrayList remainingWarnings = new ArrayList();
  1375.             ParseHeaderValues(warnings, ParseWarningsCallback, remainingWarnings);
  1376.             CacheHeaders.Remove(HttpKnownHeaderNames.Warning);
  1377.             for (int i = 0; i < remainingWarnings.Count; ++i) {
  1378.                 CacheHeaders.Add(HttpKnownHeaderNames.Warning, (string)remainingWarnings[i]);
  1379.             }
  1380.         }
  1381.        
  1382.         private static readonly ParseCallback ParseWarningsCallback = new ParseCallback(ParseWarningsCallbackMethod);
  1383.         private static void ParseWarningsCallbackMethod(string s, int start, int end, IList list)
  1384.         {
  1385.             if (end >= start && s[start] != '1') {
  1386.                 ParseValuesCallbackMethod(s, start, end, list);
  1387.             }
  1388.         }
  1389.         //
  1390.         // This is used by other classes to get the list if values from a header string
  1391.         //
  1392.         internal delegate void ParseCallback(string s, int start, int end, IList list);
  1393.         static internal readonly ParseCallback ParseValuesCallback = new ParseCallback(ParseValuesCallbackMethod);
  1394.         private static void ParseValuesCallbackMethod(string s, int start, int end, IList list)
  1395.         {
  1396.            
  1397.             // Deal with the cases: '' ' ' 'value' 'value '
  1398.             while (end >= start && s[end] == ' ') {
  1399.                 --end;
  1400.             }
  1401.             if (end >= start) {
  1402.                 list.Add(s.Substring(start, end - start + 1));
  1403.             }
  1404.         }
  1405.        
  1406.        
  1407.         //
  1408.         // Parses header values calls a callback one value after other.
  1409.         // Note a single string can contain multiple values and any value may have a quoted string in.
  1410.         // The parser will not cut trailing spaces when invoking a callback
  1411.         //
  1412.         static internal void ParseHeaderValues(string[] values, ParseCallback calback, IList list)
  1413.         {
  1414.            
  1415.             if (values == null) {
  1416.                 return;
  1417.             }
  1418.             for (int i = 0; i < values.Length; ++i) {
  1419.                 string val = values[i];
  1420.                
  1421.                 int end = 0;
  1422.                 int start = 0;
  1423.                 while (end < val.Length) {
  1424.                     //skip spaces
  1425.                     while (start < val.Length && val[start] == ' ') {
  1426.                         ++start;
  1427.                     }
  1428.                    
  1429.                     if (start == val.Length) {
  1430.                         //empty header value
  1431.                         break;
  1432.                     }
  1433.                    
  1434.                     // find comma or quote
  1435.                     end = start;
  1436.                     find_comma:
  1437.                     while (end < val.Length && val[end] != ',' && val[end] != '"') {
  1438.                         ++end;
  1439.                     }
  1440.                    
  1441.                     if (end == val.Length) {
  1442.                         calback(val, start, end - 1, list);
  1443.                         break;
  1444.                     }
  1445.                    
  1446.                     if (val[end] == '"') {
  1447.                         while (++end < val.Length && val[end] != '"') {
  1448.                             ;
  1449.                         }
  1450.                         if (end == val.Length) {
  1451.                             //warning: no closing quote, accepting
  1452.                             calback(val, start, end - 1, list);
  1453.                             break;
  1454.                         }
  1455.                         goto find_comma;
  1456.                     }
  1457.                     else {
  1458.                         //Comma
  1459.                         calback(val, start, end - 1, list);
  1460.                         // skip leading spaces
  1461.                         while (++end < val.Length && val[end] == ' ') {
  1462.                             ;
  1463.                         }
  1464.                         if (end >= val.Length) {
  1465.                             break;
  1466.                         }
  1467.                         start = end;
  1468.                     }
  1469.                 }
  1470.             }
  1471.         }
  1472.     }
  1473.     //
  1474.     //
  1475.     //
  1476.     //ATTN: The values order is importent
  1477.     internal enum HttpMethod
  1478.     {
  1479.         Other = -1,
  1480.         Head = 0,
  1481.         Get,
  1482.         Post,
  1483.         Put,
  1484.         Delete,
  1485.         Options,
  1486.         Trace,
  1487.         Connect
  1488.     }
  1489.     //
  1490.     //
  1491.     //
  1492.     internal class ResponseCacheControl
  1493.     {
  1494.         internal bool Public;
  1495.         internal bool Private;
  1496.         internal string[] PrivateHeaders;
  1497.         internal bool NoCache;
  1498.         internal string[] NoCacheHeaders;
  1499.         internal bool NoStore;
  1500.         internal bool MustRevalidate;
  1501.         internal bool ProxyRevalidate;
  1502.         internal int MaxAge;
  1503.         internal int SMaxAge;
  1504.        
  1505.         internal ResponseCacheControl()
  1506.         {
  1507.             MaxAge = SMaxAge = -1;
  1508.         }
  1509.        
  1510.         internal bool IsNotEmpty {
  1511.             get { return (Public || Private || NoCache || NoStore || MustRevalidate || ProxyRevalidate || MaxAge != -1 || SMaxAge != -1); }
  1512.         }
  1513.        
  1514.         public override string ToString()
  1515.         {
  1516.             System.Text.StringBuilder sb = new System.Text.StringBuilder();
  1517.            
  1518.             if (Public) {
  1519.                 sb.Append(" public");
  1520.             }
  1521.             if (Private) {
  1522.                 sb.Append(" private");
  1523.                 if (PrivateHeaders != null) {
  1524.                     sb.Append('=');
  1525.                     for (int i = 0; i < PrivateHeaders.Length - 1; ++i) {
  1526.                         sb.Append(PrivateHeaders[i]).Append(',');
  1527.                     }
  1528.                     sb.Append(PrivateHeaders[PrivateHeaders.Length - 1]);
  1529.                 }
  1530.             }
  1531.             if (NoCache) {
  1532.                 sb.Append(" no-cache");
  1533.                 if (NoCacheHeaders != null) {
  1534.                     sb.Append('=');
  1535.                     for (int i = 0; i < NoCacheHeaders.Length - 1; ++i) {
  1536.                         sb.Append(NoCacheHeaders[i]).Append(',');
  1537.                     }
  1538.                     sb.Append(NoCacheHeaders[NoCacheHeaders.Length - 1]);
  1539.                 }
  1540.             }
  1541.             if (NoStore) {
  1542.                 sb.Append(" no-store");
  1543.             }
  1544.             if (MustRevalidate) {
  1545.                 sb.Append(" must-revalidate");
  1546.             }
  1547.             if (ProxyRevalidate) {
  1548.                 sb.Append(" proxy-revalidate");
  1549.             }
  1550.             if (MaxAge != -1) {
  1551.                 sb.Append(" max-age=").Append(MaxAge);
  1552.             }
  1553.             if (SMaxAge != -1) {
  1554.                 sb.Append(" s-maxage=").Append(SMaxAge);
  1555.             }
  1556.             return sb.ToString();
  1557.         }
  1558.     }
  1559.    
  1560. }

Developer Fusion