HttpRequestCacheValidator.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / Net / System / Net / Cache / HttpRequestCacheValidator.cs / 1 / HttpRequestCacheValidator.cs

                            /*++ 
Copyright (c) Microsoft Corporation

Module Name:
 
    HttpRequestCacheValidator.cs
 
Abstract: 
    The class implements HTTP Caching validators as per RFC2616
 

Author:

    Alexei Vopilov    21-Dec-2002 

Revision History: 
 
    Jan 25 2004 - Changed the visibility of the class from public to internal.
 
--*/
namespace System.Net.Cache {
    using System;
    using System.Net; 
    using System.IO;
    using System.Collections; 
    using System.Text; 
    using System.Collections.Specialized;
    using System.Globalization; 
    using System.Threading;


    ///  The class represents an adavanced way for an application to control caching protocol  
    internal class HttpRequestCacheValidator: RequestCacheValidator {
        internal const string Warning_110 = "110 Response is stale"; 
        internal const string Warning_111 = "111 Revalidation failed"; 
        internal const string Warning_112 = "112 Disconnected operation";
        internal const string Warning_113 = "113 Heuristic expiration"; 

        private struct RequestVars {
            internal HttpMethod   Method;
            internal bool         IsCacheRange; 
            internal bool         IsUserRange;
            internal string       IfHeader1; 
            internal string       Validator1; 
            internal string       IfHeader2;
            internal string       Validator2; 
        }

        private HttpRequestCachePolicy m_HttpPolicy;
 
        private HttpStatusCode  m_StatusCode;
        private string          m_StatusDescription; 
        private Version         m_HttpVersion; 
        private WebHeaderCollection m_Headers;
        private NameValueCollection m_SystemMeta; 

        private bool            m_DontUpdateHeaders;
        private bool            m_HeuristicExpiration;
 
        private Vars            m_CacheVars;
        private Vars            m_ResponseVars; 
        private RequestVars     m_RequestVars; 

        private struct Vars { 
            internal DateTime         Date;
            internal DateTime         Expires;
            internal DateTime         LastModified;
            internal long             EntityLength; 
            internal TimeSpan         Age;
            internal TimeSpan         MaxAge; 
            internal ResponseCacheControl CacheControl; 
            internal long             RangeStart;
            internal long             RangeEnd; 

            internal void Initialize() {
                EntityLength = RangeStart = RangeEnd = -1;
                Date = DateTime.MinValue; 
                Expires = DateTime.MinValue;
                LastModified = DateTime.MinValue; 
                Age = TimeSpan.MinValue; 
                MaxAge = TimeSpan.MinValue;
            } 
        }


        //public 
        internal HttpStatusCode CacheStatusCode           {get{return m_StatusCode;}          set{m_StatusCode = value;}}
        //public 
        internal string         CacheStatusDescription    {get{return m_StatusDescription;}   set{m_StatusDescription = value;}} 
        //public
        internal Version        CacheHttpVersion          {get{return m_HttpVersion;}         set{m_HttpVersion = value;}} 

        //public
        internal WebHeaderCollection CacheHeaders         {get{return m_Headers;}             set{m_Headers = value;}}
 
        //public
        internal new HttpRequestCachePolicy   Policy      { 
                                                            get { 
                                                                if(m_HttpPolicy != null) return m_HttpPolicy;
                                                                m_HttpPolicy = base.Policy as HttpRequestCachePolicy; 
                                                                if(m_HttpPolicy != null) return m_HttpPolicy;
                                                                // promote base policy to Http one
                                                                m_HttpPolicy = new HttpRequestCachePolicy((HttpRequestCacheLevel)base.Policy.Level);
                                                                return m_HttpPolicy; 
                                                            }
                                                        } 
 
        internal NameValueCollection SystemMeta         {get{return m_SystemMeta;}                  set{m_SystemMeta = value;}}
        internal HttpMethod   RequestMethod             {get{return m_RequestVars.Method;}          set{m_RequestVars.Method = value;}} 
        internal bool         RequestRangeCache         {get{return m_RequestVars.IsCacheRange;}    set{m_RequestVars.IsCacheRange = value;}}
        internal bool         RequestRangeUser          {get{return m_RequestVars.IsUserRange;}     set{m_RequestVars.IsUserRange = value;}}
        internal string       RequestIfHeader1          {get{return m_RequestVars.IfHeader1;}       set{m_RequestVars.IfHeader1 = value;}}
        internal string       RequestValidator1         {get{return m_RequestVars.Validator1;}      set{m_RequestVars.Validator1 = value;}} 
        internal string       RequestIfHeader2          {get{return m_RequestVars.IfHeader2;}       set{m_RequestVars.IfHeader2 = value;}}
        internal string       RequestValidator2         {get{return m_RequestVars.Validator2;}      set{m_RequestVars.Validator2 = value;}} 
 
        internal bool         CacheDontUpdateHeaders    {get{return m_DontUpdateHeaders;}           set{m_DontUpdateHeaders = value;}}
 
        internal DateTime      CacheDate               {get{return m_CacheVars.Date;}              set{m_CacheVars.Date = value;}}
        internal DateTime      CacheExpires            {get{return m_CacheVars.Expires;}           set{m_CacheVars.Expires = value;}}
        internal DateTime      CacheLastModified       {get{return m_CacheVars.LastModified;}      set{m_CacheVars.LastModified = value;}}
        internal long          CacheEntityLength       {get{return m_CacheVars.EntityLength ;}     set{m_CacheVars.EntityLength  = value;}} 
        internal TimeSpan      CacheAge                {get{return m_CacheVars.Age;}               set{m_CacheVars.Age = value;}}
        internal TimeSpan      CacheMaxAge             {get{return m_CacheVars.MaxAge;}            set{m_CacheVars.MaxAge = value;}} 
        internal bool          HeuristicExpiration     {get{return m_HeuristicExpiration;}         set{m_HeuristicExpiration = value;}} 

        internal ResponseCacheControl     CacheCacheControl    {get{return m_CacheVars.CacheControl;}      set{m_CacheVars.CacheControl = value;}} 

        internal DateTime      ResponseDate            {get{return m_ResponseVars.Date;}         set{m_ResponseVars.Date = value;}}
        internal DateTime      ResponseExpires         {get{return m_ResponseVars.Expires;}      set{m_ResponseVars.Expires = value;}}
        internal DateTime      ResponseLastModified    {get{return m_ResponseVars.LastModified;} set{m_ResponseVars.LastModified = value;}} 
        internal long          ResponseEntityLength    {get{return m_ResponseVars.EntityLength ;}set{m_ResponseVars.EntityLength  = value;}}
        internal long          ResponseRangeStart      {get{return m_ResponseVars.RangeStart;}   set{m_ResponseVars.RangeStart = value;}} 
        internal long          ResponseRangeEnd        {get{return m_ResponseVars.RangeEnd;}     set{m_ResponseVars.RangeEnd = value;}} 
        internal TimeSpan      ResponseAge             {get{return m_ResponseVars.Age;}          set{m_ResponseVars.Age = value;}}
        internal ResponseCacheControl  ResponseCacheControl {get{return m_ResponseVars.CacheControl;} set{m_ResponseVars.CacheControl = value;}} 

        //
        private void ZeroPrivateVars()
        { 
            // Set default values for private members here
            m_RequestVars = new RequestVars(); 
 
            m_HttpPolicy        = null;
            m_StatusCode        = (HttpStatusCode)0; 
            m_StatusDescription = null;
            m_HttpVersion       = null;
            m_Headers           = null;
            m_SystemMeta        = null; 
            m_DontUpdateHeaders = false;
            m_HeuristicExpiration = false; 
 
            m_CacheVars   = new Vars();
            m_CacheVars.Initialize(); 

            m_ResponseVars= new Vars();
            m_ResponseVars.Initialize();
        } 

        //public 
        internal override RequestCacheValidator CreateValidator() 
        {
            return new HttpRequestCacheValidator(StrictCacheErrors, UnspecifiedMaxAge); 
        }

        /*
        //public 
        // Consider removing.
        internal HttpRequestCacheValidator(): base() 
        { 
        }
        */ 

        //public
        internal HttpRequestCacheValidator(bool strictCacheErrors, TimeSpan unspecifiedMaxAge): base(strictCacheErrors, unspecifiedMaxAge)
        { 
        }
 
        // 
        // This validation method is called first and before any Cache access is done.
        // Given the request instance the code has to decide whether the request is ever suitable for caching. 
        //
        // Returns:
        // Continue           = Proceed to the next protocol stage.
        // DoNotTakeFromCache = Don't used caches value for this request 
        // DoNotUseCache      = Cache is not used for this request and response is not cached.
        protected internal override CacheValidationStatus ValidateRequest() { 
 
            // cleanup context after previous  request
            ZeroPrivateVars(); 

            string method = Request.Method.ToUpper(CultureInfo.InvariantCulture);
            if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_request_method, method));
 
            switch (method) {
                case "GET" :    RequestMethod = HttpMethod.Get;      break; 
                case "POST":    RequestMethod = HttpMethod.Post;     break; 
                case "HEAD":    RequestMethod = HttpMethod.Head;     break;
                case "PUT" :    RequestMethod = HttpMethod.Put;      break; 
                case "DELETE":  RequestMethod = HttpMethod.Delete;   break;
                case "OPTIONS": RequestMethod = HttpMethod.Options;  break;
                case "TRACE":   RequestMethod = HttpMethod.Trace;    break;
                case "CONNECT": RequestMethod = HttpMethod.Connect;  break; 
                default:        RequestMethod = HttpMethod.Other;    break;
            } 
 
            // Apply our best knowledge of HTTP caching and return the result
            // that can be hooked up and revised by the upper level 
            return Rfc2616.OnValidateRequest(this);
        }

        // 
        // This validation method is called after caching protocol has retrieved the metadata of a cached entry.
        // Given the cached entry context, the request instance and the effective caching policy, 
        // the handler has to decide whether a cached item can be considered as fresh. 
        protected internal override CacheFreshnessStatus ValidateFreshness()
        { 

            // Transfer cache entry metadata into status line and headers.
            string s = ParseStatusLine();
 
            if(Logging.On) {
                if ((int) CacheStatusCode == 0) { 
                    Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_http_status_parse_failure, (s == null ? "null" : s))); 
                }
                else { 
                    Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_http_status_line, (CacheHttpVersion != null ? CacheHttpVersion.ToString() : "null"), (int)CacheStatusCode, CacheStatusDescription));
                }

            } 

            CreateCacheHeaders((int)CacheStatusCode != 0); 
            CreateSystemMeta(); 

            // We will need quick access to cache-control and other headers coming with the cached item 
            FetchHeaderValues(true);

            if(Logging.On) Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_cache_control, CacheCacheControl.ToString()));
 
            // Now we try to apply our best knowledge of HTTP caching and return the result
            // that can be hooked up and revised on the upper level 
            return Rfc2616.OnValidateFreshness(this); 
        }
 
        ///   This method may add headers under the "Warning" header name 
        protected internal override CacheValidationStatus ValidateCache() {

            if (this.Policy.Level != HttpRequestCacheLevel.Revalidate && base.Policy.Level >= RequestCacheLevel.Reload) 
            {
                // For those policies cache is never returned 
                GlobalLog.Assert("OnValidateCache()", "This validator should not be called for policy = " + Policy.ToString()); 
                if(Logging.On)Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_validator_invalid_for_policy, Policy.ToString()));
                return CacheValidationStatus.DoNotTakeFromCache; 
            }

            // First check is do we have a cached entry at all?
            // Also we include some very special case where cache got a 304 (NotModified) response somehow 
            if (CacheStream == Stream.Null || (int)CacheStatusCode == 0 || CacheStatusCode == HttpStatusCode.NotModified)
            { 
                if (this.Policy.Level == HttpRequestCacheLevel.CacheOnly) 
                {
                    // Throw because entry was not found and it's cache-only policy 
                    FailRequest(WebExceptionStatus.CacheEntryNotFound);
                }
                return CacheValidationStatus.DoNotTakeFromCache;
            } 

            if (RequestMethod == HttpMethod.Head) 
            { 
                // For a HEAD request we release the cache entry stream asap since we will have to suppress it anyway
                CacheStream.Close(); 
                CacheStream = new SyncMemoryStream(new byte[] {});
            }

            // Apply our best knowledge of HTTP caching and return the result 
            // that can be hooked up and revised by the upper level
 
            CacheValidationStatus result = CacheValidationStatus.DoNotTakeFromCache; 

            // 
            // Before request submission validation
            //

            // If we return from cache we should remove existing 1xx warnings 
            RemoveWarnings_1xx();
 
            // default values for a response from cache. 
            CacheStreamOffset       = 0;
            CacheStreamLength       = CacheEntry.StreamSize; 

            result = Rfc2616.OnValidateCache(this);
            if (result != CacheValidationStatus.ReturnCachedResponse && this.Policy.Level == HttpRequestCacheLevel.CacheOnly) {
                // Throw because entry was not found and it's cache-only policy 
                FailRequest(WebExceptionStatus.CacheEntryNotFound);
            } 
 
            if (result == CacheValidationStatus.ReturnCachedResponse)
            { 
                if (CacheFreshnessStatus == CacheFreshnessStatus.Stale) {
                    CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_110);
                }
                if (base.Policy.Level == RequestCacheLevel.CacheOnly) { 
                    CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_112);
                } 
                if (HeuristicExpiration && (int)CacheAge.TotalSeconds >= 24*3600) { 
                    CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_113);
                } 
            }

            if (result == CacheValidationStatus.DoNotTakeFromCache) {
                // We signal that current cache entry can be only replaced and not updated 
                CacheStatusCode = (HttpStatusCode) 0;
            } 
            else if (result == CacheValidationStatus.ReturnCachedResponse) { 
                CacheHeaders[HttpKnownHeaderNames.Age] = ((int)(CacheAge.TotalSeconds)).ToString(NumberFormatInfo.InvariantInfo);
            } 
            return result;
        }
        //
        // This is (optionally) called after receiveing a live response 
        //
        protected internal override CacheValidationStatus RevalidateCache() 
        { 
            if (this.Policy.Level != HttpRequestCacheLevel.Revalidate && base.Policy.Level >= RequestCacheLevel.Reload)
            { 
                // For those policies cache is never returned
                GlobalLog.Assert("RevalidateCache()", "This validator should not be called for policy = " + Policy.ToString());
                if(Logging.On)Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_validator_invalid_for_policy, Policy.ToString()));
                return CacheValidationStatus.DoNotTakeFromCache; 
            }
 
            // First check is do we have a cached entry at all? 
            // Also we include some very special case where cache got a 304 (NotModified) response somehow
            if (CacheStream == Stream.Null || (int)CacheStatusCode == 0 || CacheStatusCode == HttpStatusCode.NotModified) 
            {
                return CacheValidationStatus.DoNotTakeFromCache;
            }
 
            //
            // This is a second+ time validation after receiving at least one response 
            // 

            // Apply our best knowledge of HTTP caching and return the result 
            // that can be hooked up and revised by the upper level
            CacheValidationStatus result = CacheValidationStatus.DoNotTakeFromCache;

            HttpWebResponse resp = Response as HttpWebResponse; 
            if (resp == null)
            { 
                // This will result to an application error 
                return CacheValidationStatus.DoNotTakeFromCache;
            } 

            if (resp.StatusCode >= HttpStatusCode.InternalServerError) {
                // If server returned a 5XX server error
                if (Rfc2616.Common.ValidateCacheOn5XXResponse(this) == CacheValidationStatus.ReturnCachedResponse) { 
                    // We can substitute the response from cache
                    if (CacheFreshnessStatus == CacheFreshnessStatus.Stale) { 
                        CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_110); 
                    }
                    if (HeuristicExpiration && (int)CacheAge.TotalSeconds >= 24*3600) { 
                        CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_113);
                    }
                    // We actually failed to reach the origin server hence we don't reset the current Cache Age
                } 
            }
            else { 
 
                // if there was already one retry, then cache should not be taken into account
                if (ResponseCount > 1) { 
                    result =  CacheValidationStatus.DoNotTakeFromCache;
                }
                else {
                    /* 
                    Section 13.2.3:
                    HTTP/1.1 uses the Age response-header to convey the estimated age 
                    of the response message when obtained from a cache. 
                    The Age field value is the cache's estimate of the amount of time
                    since the response was generated or >>revalidated<< by the origin server. 
                    */
                    // Reset Cache Age to be 0 seconds
                    CacheAge = TimeSpan.Zero;
                    result = Rfc2616.Common.ValidateCacheAfterResponse(this, resp); 
                }
            } 
 
            if (result == CacheValidationStatus.ReturnCachedResponse)
            { 
                CacheHeaders[HttpKnownHeaderNames.Age] = ((int)(CacheAge.TotalSeconds)).ToString(NumberFormatInfo.InvariantInfo);
            }
            return result;
        } 

        ///  
        ///  
        /// This validation method is responsible to answer whether the live response is sufficient to make
        /// the final decision for caching protocol. 
        /// This is useful in case of possible failure or inconsistent results received from
        /// the remote cache.
        /// 
        ///  
        ///   Invalid response from this method means the request was internally modified and should be retried 
        protected internal override CacheValidationStatus ValidateResponse() { 
 
            if (this.Policy.Level != HttpRequestCacheLevel.CacheOrNextCacheOnly &&
                this.Policy.Level != HttpRequestCacheLevel.Default && 
                this.Policy.Level != HttpRequestCacheLevel.Revalidate)
            {
                // Those policy levels do not modify requests
                if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_response_valid_based_on_policy, Policy.ToString())); 
                return CacheValidationStatus.Continue;
            } 
 
            // We will need quick access to cache controls coming with the live response
            HttpWebResponse resp = Response as HttpWebResponse; 
            if (resp == null) {
                if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_null_response_failure));
                return CacheValidationStatus.Continue;
            } 

            FetchHeaderValues(false); 
            if(Logging.On) 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)
                                                      );

 
            // Apply our best knowledge of HTTP caching and return the result
            // that can be hooked up and revised by the upper level 
            return Rfc2616.OnValidateResponse(this); 
        }
 
        /// 
        /// 
        /// This action handler is responsible for making final decision on whether
        /// a received response can be cached. 
        /// 
        ///  
        ///   Invalid result from this method means the response must not be cached  
        protected internal override CacheValidationStatus UpdateCache() {
 
            if (this.Policy.Level == HttpRequestCacheLevel.NoCacheNoStore) {
                if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_removed_existing_based_on_policy, Policy.ToString()));
                return CacheValidationStatus.RemoveFromCache;
            } 
            if (this.Policy.Level == HttpRequestCacheLevel.CacheOnly) {
                if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_not_updated_based_on_policy, Policy.ToString())); 
                return CacheValidationStatus.DoNotUpdateCache; 
            }
 
            if (CacheHeaders == null)
                CacheHeaders = new WebHeaderCollection();

            if (SystemMeta == null) 
                SystemMeta = new NameValueCollection(1, CaseInsensitiveAscii.StaticInstance);
 
            if (ResponseCacheControl == null) { 
                //ValidateResponse was not invoked
                FetchHeaderValues(false); 
            }

            // Apply our best knowledge of HTTP caching and return the result
            // that can be hooked up and revised by the upper level 
            CacheValidationStatus result = Rfc2616.OnUpdateCache(this);
 
            if (result == CacheValidationStatus.UpdateResponseInformation || result == CacheValidationStatus.CacheResponse) 
            {
                FinallyUpdateCacheEntry(); 
            }
            return result;
        }
 
        //
        // 
        // 
        private void FinallyUpdateCacheEntry() {
            // Transfer the context status line back to the metadata 

            CacheEntry.EntryMetadata  = null;
            CacheEntry.SystemMetadata = null;
 
            if (CacheHeaders == null) {
                //must be an entry update without updating the headers 
                return; 
            }
 
            CacheEntry.EntryMetadata  = new StringCollection();
            CacheEntry.SystemMetadata = new StringCollection();

            if (CacheHttpVersion == null) { 
                if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_invalid_http_version));
                CacheHttpVersion = new Version(1, 0); 
            } 
            // HTTP/1.1 200 OK
            System.Text.StringBuilder sb = new System.Text.StringBuilder(CacheStatusDescription.Length + 20); 
            sb.Append("HTTP/");
            sb.Append(CacheHttpVersion.ToString(2));
            sb.Append(' ');
            sb.Append(((int)CacheStatusCode).ToString(NumberFormatInfo.InvariantInfo)); 
            sb.Append(' ');
            sb.Append(CacheStatusDescription); 
 
            // Fetch the status line into cache metadata
            CacheEntry.EntryMetadata.Add(sb.ToString()); 

            UpdateStringCollection(CacheEntry.EntryMetadata,  CacheHeaders, false);

            if (SystemMeta != null) 
            {
                UpdateStringCollection(CacheEntry.SystemMetadata, SystemMeta, true); 
            } 

            // Update other entry values 
            if (ResponseExpires != DateTime.MinValue) {
                CacheEntry.ExpiresUtc = ResponseExpires;
            }
 
            if (ResponseLastModified != DateTime.MinValue)
            { 
                CacheEntry.LastModifiedUtc = ResponseLastModified; 
            }
 
            if (this.Policy.Level == HttpRequestCacheLevel.Default)
            {
                    CacheEntry.MaxStale = this.Policy.MaxStale;
            } 

            CacheEntry.LastSynchronizedUtc = DateTime.UtcNow; 
        } 
        //
        // 
        //
        private static void UpdateStringCollection(StringCollection result, NameValueCollection cc, bool winInetCompat)
        {
            StringBuilder sb; 

            // Transfer headers 
            for (int i=0; i < cc.Count; ++i) 
            {
                    sb = new StringBuilder(40); 
                    string key   = cc.GetKey(i) as string;
                    sb.Append(key).Append(':');

                    string[] val = cc.GetValues(i); 
                    if (val.Length != 0)
                    { 
                        if (winInetCompat) 
                            {sb.Append(val[0]);}
                        else 
                            {sb.Append(' ').Append(val[0]);}
                    }

                    for (int j = 1; j < val.Length; ++j) 
                    {
                        sb.Append(key).Append(", ").Append(val[j]); 
                    } 
                    result.Add(sb.ToString());
            } 
            // Transfer last \r\n
            result.Add(string.Empty);
        }
 
        // The format is
        // HTTP/X.Y SP NUMBER SP STRING 
        // HTTP/1.1 200 OK 
        //
        private string ParseStatusLine() { 

            // This will indicate an invalid result
            CacheStatusCode = (HttpStatusCode)0;
 
            if (CacheEntry.EntryMetadata == null || CacheEntry.EntryMetadata.Count == 0)
            { 
                return null; 
            }
 
            string s = CacheEntry.EntryMetadata[0];

            if (s == null) {
                return null; 
            }
 
            int  idx = 0; 
            char ch = (char)0;
            while (++idx < s.Length && (ch=s[idx]) != '/') { 
                ;
            }

            if (idx == s.Length) {return s;} 

            int major = -1; 
            int minor = -1; 
            int status= -1;
 
            while (++idx < s.Length && (ch=s[idx]) >= '0' && ch <= '9') {
                major = (major<0? 0: major*10) +(ch - '0');
            }
 
            if (major < 0 || ch != '.') {return s;}
 
            while (++idx < s.Length && (ch=s[idx]) >= '0' && ch <= '9') { 
                minor = (minor<0? 0: minor*10) + (ch - '0');
            } 

            if (minor < 0 || (ch != ' ' && ch != '\t')) {return s;}

            while (++idx < s.Length && ((ch=s[idx]) == ' ' || ch == '\t')) 
                ;
 
            if (idx >= s.Length) {return s;} 

            while (ch >= '0' && ch <= '9') 
            {
                status = (status<0? 0: status*10) +(ch - '0');
                if (++idx == s.Length)
                    break; 
                ch=s[idx];
            } 
 
            if (status < 0 || (idx <= s.Length && (ch != ' ' && ch != '\t'))) {return s;}
 
            while (idx < s.Length && (s[idx] == ' ' || s[idx] == '\t'))
                ++idx;

            CacheStatusDescription = s.Substring(idx); 

            CacheHttpVersion = new Version(major, minor); 
            CacheStatusCode = (HttpStatusCode)status; 
            return s;
        } 
        //
        private void CreateCacheHeaders(bool ignoreFirstString)
        {
 
            if (CacheHeaders == null)
                CacheHeaders = new WebHeaderCollection(); 
 
            if (CacheEntry.EntryMetadata == null || CacheEntry.EntryMetadata.Count == 0)
            { 
                if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_no_http_response_header));
                return;
            }
 
            string s = ParseNameValues(CacheHeaders, CacheEntry.EntryMetadata, ignoreFirstString?1:0);
            if (s != null) 
            { 
                if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_http_header_parse_error, s));
                CacheHeaders.Clear(); 
            }
        }
        //
        private void CreateSystemMeta() 
        {
            if (SystemMeta == null) 
            { 
                SystemMeta = new NameValueCollection((CacheEntry.EntryMetadata == null || CacheEntry.EntryMetadata.Count == 0? 2: CacheEntry.EntryMetadata.Count),
                                                     CaseInsensitiveAscii.StaticInstance); 
            }
            if (CacheEntry.EntryMetadata == null || CacheEntry.EntryMetadata.Count == 0)
                {return;}
 
            string s = ParseNameValues(SystemMeta, CacheEntry.SystemMetadata, 0);
            if (s != null) 
            { 
                if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_metadata_name_value_parse_error, s));
            } 
        }
        //
        // Returns null on success, otherwise the offending string.
        // 
        private string ParseNameValues(NameValueCollection cc, StringCollection sc, int start)
        { 
            WebHeaderCollection wc = cc as WebHeaderCollection; 

            string lastHeaderName = null; 
            if (sc != null)
            {
                for (int i = start; i < sc.Count; ++i)
                { 
                    string s = sc[i];
                    if (s == null || s.Length == 0) 
                    { 
                        //An empty string stands for \r\n
                        //Treat that as the end of headers and ignore the rest 
                        return null;
                    }

                    if (s[0] == ' ' || s[0] == '\t') 
                    {
                        if (lastHeaderName == null) {return s;} 
                        if (wc != null) 
                            wc.AddInternal(lastHeaderName, s);
                        else 
                            cc.Add(lastHeaderName, s);
                    }

                    int colpos = s.IndexOf(':'); 
                    if (colpos < 0)
                        {return s;} 
                    lastHeaderName = s.Substring(0, colpos); 
                    while (++colpos < s.Length && (s[colpos] == ' ' || s[colpos] == '\t'))
                        {;} 

                    try {
                        if (wc != null)
                            wc.AddInternal(lastHeaderName, s.Substring(colpos)); 
                        else
                            cc.Add(lastHeaderName, s.Substring(colpos)); 
                    } 
                    catch(Exception e) {
                        if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) 
                            throw;
                        // Otherwise the value of 's' will be used to log an error.
                        // The fact that we cannot parse headers may stand for corrupted metadata that we try to ignore
                        return s; 
                    }
                } 
            } 
            return null;
        } 
        //
        //
        //
        private void FetchHeaderValues(bool forCache) { 

            WebHeaderCollection cc = forCache? CacheHeaders: Response.Headers; 
 

            FetchCacheControl(cc.CacheControl, forCache); 

            // Parse Date Header
            string s = cc.Date;
 
            DateTime date = DateTime.MinValue;
            if (s != null && HttpDateParse.ParseHttpDate(s, out date)) { 
                date = date.ToUniversalTime(); 
            }
            if (forCache) { 
                CacheDate = date;
            }
            else {
                ResponseDate = date; 
            }
 
            // Parse Expires Header 
            s = cc.Expires;
 
            date = DateTime.MinValue;
            if (s != null && HttpDateParse.ParseHttpDate(s, out date)) {
                date = date.ToUniversalTime();
            } 
            if (forCache) {
                CacheExpires = date; 
            } 
            else {
                ResponseExpires = date; 
            }

            // Parse LastModified Header
            s = cc.LastModified; 

            date = DateTime.MinValue; 
            if (s != null && HttpDateParse.ParseHttpDate(s, out date)) { 
                date = date.ToUniversalTime();
            } 
            if (forCache) {
                CacheLastModified = date;
            }
            else { 
                ResponseLastModified = date;
            } 
 
            long totalLength = -1;
            long startRange = -1; 
            long end = -1;

            HttpWebResponse resp = Response as HttpWebResponse;
            if ((forCache? CacheStatusCode: resp.StatusCode) != HttpStatusCode.PartialContent) { 

                // Parse Content-Length Header 
                s = cc.ContentLength; 
                if (s != null && s.Length != 0) {
                    int i = 0; 
                    char ch = s[0];
                    while (i < s.Length && ch == ' ') {
                        ch = s[++i];
                    } 
                    if (i != s.Length && ch >= '0' && ch <= '9') {
                        totalLength = ch-'0'; 
                        while(++i < s.Length && (ch = s[i]) >= '0' && ch <= '9') { 
                            totalLength = totalLength*10+(ch-'0');
                        } 
                    }
                }
            }
            else { 
                //Parse Content-Range
                s = cc[HttpKnownHeaderNames.ContentRange]; 
                if(s == null || !Rfc2616.Common.GetBytesRange(s, ref startRange, ref end, ref totalLength, false)) { 
                    if(Logging.On)Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_content_range_error, (s==null ? "" : s)));
                    startRange=end=totalLength = -1; 
                }
                else if (forCache && totalLength == CacheEntry.StreamSize)
                {
                    // This is a whole response, step back to 200 
                    startRange = -1;
                    end = -1; 
                    CacheStatusCode = HttpStatusCode.OK; 
                    CacheStatusDescription = Rfc2616.Common.OkDescription;
                } 
            }

            if (forCache) {
                CacheEntityLength  = totalLength; 
                ResponseRangeStart = startRange;
                ResponseRangeEnd   = end; 
 
            }
            else { 
                ResponseEntityLength = totalLength;
                ResponseRangeStart = startRange;
                ResponseRangeEnd   = end;
            } 

            //Parse Age Header 
            TimeSpan span = TimeSpan.MinValue; 
            s = cc[HttpKnownHeaderNames.Age];
            if (s != null) { 
                int i = 0;
                int sec = 0;
                while(i < s.Length && s[i++] == ' ') {
                    ; 
                }
                while(i < s.Length && s[i] >= '0' && s[i] <= '9') { 
                    sec = sec*10 + (s[i++] - '0'); 
                }
                span = TimeSpan.FromSeconds(sec); 
            }

            if (forCache) {
                CacheAge = span; 
            }
            else { 
                ResponseAge = span; 
            }
        } 

        const long LO = 0x0020002000200020L;
        const int  LOI = 0x00200020;
        const long _prox = 'p'|('r'<<16)|((long)'o'<<32)|((long)'x'<<48); 
        const long _y_re = 'y'|('-'<<16)|((long)'r'<<32)|((long)'e'<<48);
        const long _vali = 'v'|('a'<<16)|((long)'l'<<32)|((long)'i'<<48); 
        const long _date = 'd'|('a'<<16)|((long)'t'<<32)|((long)'e'<<48); 

        const long _publ = 'p'|('u'<<16)|((long)'b'<<32)|((long)'l'<<48); 
        const int  _ic   = 'i'|('c'<<16);

        const long _priv = 'p'|('r'<<16)|((long)'i'<<32)|((long)'v'<<48);
        const int  _at   = 'a'|('t'<<16); 

        const long _no_c = 'n'|('o'<<16)|((long)'-'<<32)|((long)'c'<<48); 
        const long _ache = 'a'|('c'<<16)|((long)'h'<<32)|((long)'e'<<48); 

        const long _no_s = 'n'|('o'<<16)|((long)'-'<<32)|((long)'s'<<48); 
        const long _tore = 't'|('o'<<16)|((long)'r'<<32)|((long)'e'<<48);

        const long _must = 'm'|('u'<<16)|((long)'s'<<32)|((long)'t'<<48);
        const long __rev = '-'|('r'<<16)|((long)'e'<<32)|((long)'v'<<48); 
        const long _alid = 'a'|('l'<<16)|((long)'i'<<32)|((long)'d'<<48);
 
        const long _max_ = 'm'|('a'<<16)|((long)'x'<<32)|((long)'-'<<48); 
        const int  _ag   = 'a'|('g'<<16);
 
        const long _s_ma = 's'|('-'<<16)|((long)'m'<<32)|((long)'a'<<48);
        const long _xage = 'x'|('a'<<16)|((long)'g'<<32)|((long)'e'<<48);
        //
        // 
        //
        private unsafe void FetchCacheControl(string s, bool forCache) { 
            //Initialize it 
            ResponseCacheControl control = new ResponseCacheControl();
            if (forCache) { 
                CacheCacheControl = control;
            }
            else {
                ResponseCacheControl = control; 
            }
 
            if (s != null && s.Length != 0) { 
                fixed (char *sp = s) {
                    int len = s.Length; 
                    for (int i = 0; i < len-4; ++i) {
                        if (sp[i] < ' ' || sp[i] >= 0x7F) {
                            if(Logging.On)Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_cache_control_error, s));
                            //invalid format 
                            return;
                        } 
                        if (sp[i] == ' ' || sp[i] == ',') { 
                            continue;
                        } 

                        // These if-else are two logically identical blocks that differ only in the way of how text search is done.
                        // The text search is done differently for 32 and X-bits platforms.
                        // ATTN: You are responsible for keeping the rest of the logic in [....]. 
                        if (IntPtr.Size == 4) {
                            // We are on 32-bits platform 
 
                            long *mask = (long*)&(sp[i]);
                            //making interested chars lowercase, others are ignored anyway 
                            switch(*mask|LO) {

                                case _prox: if (i+16 > len) continue;
                                    if ((*(mask+1)|LO) != _y_re || (*(mask+2)|LO) != _vali || (*(mask+3)|LO) != _date) continue; 
                                    control.ProxyRevalidate = true;
                                    i+=15; 
                                    break; 

                                case _publ: if (i+6 > len) return; 
                                    if ((*((int*)(mask+1))|LOI) != _ic) continue;
                                    control.Public = true;
                                    i+=5;
                                    break; 

                                case _priv: if (i+7 > len) return; 
                                    if ((*((int*)(mask+1))|LOI) != _at || (sp[i+6]|0x20) != 'e') continue; 
                                    control.Private = true;
                                    i+=6; 
                                    // Check for a case: private = "name1,name2"
                                    while (i < len && sp[i] == ' ') {++i;}

                                    if (i >= len || sp[i] != '=') {--i;break;} 

                                    while (i < len && sp[++i] == ' ') {;} 
 
                                    if (i >= len || sp[i] != '\"') {--i;break;}
 
                                    System.Collections.ArrayList privateList = new System.Collections.ArrayList();
                                    ++i;
                                    while(i < len && sp[i] != '\"') {
 
                                        while (i < len && sp[i] == ' ') {++i;}
                                        int start = i; 
                                        while (i < len && sp[i] != ' ' && sp[i] != ',' && sp[i] != '\"') {++i;} 
                                        if (start != i) {
                                            privateList.Add(s.Substring(start, i-start)); 
                                        }
                                        while (i < len && sp[i] != ',' && sp[i] != '\"') {++i;}
                                    }
                                    if (privateList.Count != 0) { 
                                        control.PrivateHeaders = (string[])privateList.ToArray(typeof(string));
                                    } 
                                    break; 

                                case _no_c: if (i+8 > len) return; 
                                    if ((*(mask+1)|LOI) != _ache) continue;
                                    control.NoCache = true;
                                    i+=7;
                                    // Check for a case: no-cache = "name1,name2" 
                                    while (i < len && sp[i] == ' ') {++i;}
 
                                    if (i >= len || sp[i] != '=') {--i;break;} 

                                    while (i < len && sp[++i] == ' ') {;} 

                                    if (i >= len || sp[i] != '\"') {--i;break;}

                                    System.Collections.ArrayList nocacheList = new System.Collections.ArrayList(); 
                                    ++i;
                                    while(i < len && sp[i] != '\"') { 
 
                                        while (i < len && sp[i] == ' ') {++i;}
                                        int start = i; 
                                        while (i < len && sp[i] != ' ' && sp[i] != ',' && sp[i] != '\"') {++i;}
                                        if (start != i) {
                                            nocacheList.Add(s.Substring(start, i-start));
                                        } 
                                        while (i < len && sp[i] != ',' && sp[i] != '\"') {++i;}
                                    } 
                                    if (nocacheList.Count != 0) { 
                                        control.NoCacheHeaders = (string[])nocacheList.ToArray(typeof(string));
                                    } 
                                    break;

                                case _no_s: if (i+8 > len) return;
                                    if ((*(mask+1)|LOI) != _tore) continue; 
                                    control.NoStore = true;
                                    i+=7; 
                                    break; 

                                case _must: if (i+15 > len) continue; 

                                    if ((*(mask+1)|LO) != __rev || (*(mask+2)|LO) != _alid || (*(int*)(mask+3)|LOI) != _at || (sp[i+14]|0x20) != 'e') continue;
                                    control.MustRevalidate = true;
                                    i+=14; 
                                    break;
 
                                case _max_: if (i+7 > len) return; 
                                    if ((*((int*)(mask+1))|LOI) != _ag || (sp[i+6]|0x20) != 'e') continue;
                                    i+=7; 
                                    while (i < len && sp[i] == ' ') {
                                        ++i;
                                    }
                                    if (i == len || sp[i++] != '=') return; 
                                    while (i < len && sp[i] == ' ') {
                                        ++i; 
                                    } 
                                    if (i == len) return;
                                    control.MaxAge = 0; 
                                    while (i < len && sp[i] >= '0' && sp[i] <= '9') {
                                        control.MaxAge =control.MaxAge*10 + (sp[i++]-'0');
                                    }
                                    --i; 
                                    break;
 
                                case _s_ma: if (i+8 > len) return; 
                                    if ((*(mask+1)|LOI) != _xage) continue;
                                    i+=8; 
                                    while (i < len && sp[i] == ' ') {
                                        ++i;
                                    }
                                    if (i == len || sp[i++] != '=') return; 
                                    while (i < len && sp[i] == ' ') {
                                        ++i; 
                                    } 
                                    if (i == len) return;
                                    control.SMaxAge = 0; 
                                    while (i < len && sp[i] >= '0' && sp[i] <= '9') {
                                        control.SMaxAge = control.SMaxAge*10 + (sp[i++]-'0');
                                    }
                                    --i; 
                                    break;
                            } 
                        } 
                        else {
                            // We cannot use optimized code path due to IA-64 memory alligment problems see VSWhidbey 118967 
                            if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "proxy-revalidate")) {
                                control.ProxyRevalidate = true;
                                i+=15;
                            } 
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "public")) {
                                control.Public = true; 
                                i+=5; 
                            }
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "private")) { 
                                control.Private = true;
                                i+=6;
                                // Check for a case: private = "name1,name2"
                                while (i < len && sp[i] == ' ') {++i;} 

                                if (i >= len || sp[i] != '=') {--i;break;} 
 
                                while (i < len && sp[++i] == ' ') {;}
 
                                if (i >= len || sp[i] != '\"') {--i;break;}

                                System.Collections.ArrayList privateList = new System.Collections.ArrayList();
                                ++i; 
                                while(i < len && sp[i] != '\"') {
 
                                    while (i < len && sp[i] == ' ') {++i;} 
                                    int start = i;
                                    while (i < len && sp[i] != ' ' && sp[i] != ',' && sp[i] != '\"') {++i;} 
                                    if (start != i) {
                                        privateList.Add(s.Substring(start, i-start));
                                    }
                                    while (i < len && sp[i] != ',' && sp[i] != '\"') {++i;} 
                                }
                                if (privateList.Count != 0) { 
                                    control.PrivateHeaders = (string[])privateList.ToArray(typeof(string)); 
                                }
                            } 
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "no-cache")) {
                                control.NoCache = true;
                                i+=7;
                                // Check for a case: no-cache = "name1,name2" 
                                while (i < len && sp[i] == ' ') {++i;}
 
                                if (i >= len || sp[i] != '=') {--i;break;} 

                                while (i < len && sp[++i] == ' ') {;} 

                                if (i >= len || sp[i] != '\"') {--i;break;}

                                System.Collections.ArrayList nocacheList = new System.Collections.ArrayList(); 
                                ++i;
                                while(i < len && sp[i] != '\"') { 
 
                                    while (i < len && sp[i] == ' ') {++i;}
                                    int start = i; 
                                    while (i < len && sp[i] != ' ' && sp[i] != ',' && sp[i] != '\"') {++i;}
                                    if (start != i) {
                                        nocacheList.Add(s.Substring(start, i-start));
                                    } 
                                    while (i < len && sp[i] != ',' && sp[i] != '\"') {++i;}
                                } 
                                if (nocacheList.Count != 0) { 
                                    control.NoCacheHeaders = (string[])nocacheList.ToArray(typeof(string));
                                } 
                            }
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "no-store")) {
                                control.NoStore = true;
                                i+=7; 
                            }
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "must-revalidate")) { 
                                control.MustRevalidate = true; 
                                i+=14;
                            } 
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "max-age")) {
                                i+=7;
                                while (i < len && sp[i] == ' ') {
                                    ++i; 
                                }
                                if (i == len || sp[i++] != '=') return; 
                                while (i < len && sp[i] == ' ') { 
                                    ++i;
                                } 
                                if (i == len) return;
                                control.MaxAge = 0;
                                while (i < len && sp[i] >= '0' && sp[i] <= '9') {
                                    control.MaxAge =control.MaxAge*10 + (sp[i++]-'0'); 
                                }
                                --i; 
                            } 
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "smax-age")) {
                                i+=8; 
                                while (i < len && sp[i] == ' ') {
                                    ++i;
                                }
                                if (i == len || sp[i++] != '=') return; 
                                while (i < len && sp[i] == ' ') {
                                    ++i; 
                                } 
                                if (i == len) return;
                                control.SMaxAge = 0; 
                                while (i < len && sp[i] >= '0' && sp[i] <= '9') {
                                    control.SMaxAge = control.SMaxAge*10 + (sp[i++]-'0');
                                }
                                --i; 
                            }
                        } 
                    } 
                }
            } 
        }

        /*
          - any stored Warning headers with warn-code 1xx (see section 
        14.46) MUST be deleted from the cache entry and the forwarded
        response. 
 
          - any stored Warning headers with warn-code 2xx MUST be retained
        in the cache entry and the forwarded response. 
        */
        private void RemoveWarnings_1xx() {

            string[] warnings = CacheHeaders.GetValues(HttpKnownHeaderNames.Warning); 
            if (warnings == null) {
                return; 
            } 
            ArrayList remainingWarnings = new ArrayList();
            ParseHeaderValues(warnings, ParseWarningsCallback, remainingWarnings); 
            CacheHeaders.Remove(HttpKnownHeaderNames.Warning);
            for (int i=0; i < remainingWarnings.Count; ++i) {
                CacheHeaders.Add(HttpKnownHeaderNames.Warning, (string)remainingWarnings[i]);
            } 
        }
 
        private static readonly ParseCallback ParseWarningsCallback = new ParseCallback(ParseWarningsCallbackMethod); 
        private static void ParseWarningsCallbackMethod(string s, int start, int end, IList list) {
            if (end >= start && s[start] != '1') { 
                ParseValuesCallbackMethod(s, start, end, list);
            }
        }
        // 
        // This is used by other classes to get the list if values from a header string
        // 
        internal delegate void ParseCallback(string s, int start, int end, IList list); 
        internal static readonly ParseCallback ParseValuesCallback = new ParseCallback(ParseValuesCallbackMethod);
        private static void ParseValuesCallbackMethod(string s, int start, int end, IList list) { 

            // Deal with the cases: '' ' ' 'value' 'value   '
            while (end >= start && s[end] == ' ') {
                --end; 
            }
            if (end >= start) { 
                list.Add(s.Substring(start, end-start+1)); 
            }
        } 


        //
        // Parses header values calls a callback one value after other. 
        // Note a single string can contain multiple values and any value may have a quoted string in.
        // The parser will not cut trailing spaces when invoking a callback 
        // 
        internal static void ParseHeaderValues(string[] values, ParseCallback calback, IList list) {
 
            if (values == null) {
                return;
            }
            for (int i = 0; i < values.Length; ++i) { 
                string val = values[i];
 
                int end = 0; 
                int start = 0;
                while (end < val.Length) { 
                    //skip spaces
                    while (start < val.Length && val[start] == ' ') {
                        ++start;
                    } 

                    if (start == val.Length ) { 
                        //empty header value 
                        break;
                    } 

                    // find comma or quote
                    end = start;
                find_comma: 
                    while (end < val.Length && val[end] != ',' && val[end] != '\"') {
                        ++end; 
                    } 

                    if (end == val.Length ) { 
                        calback(val, start, end-1, list);
                        break;
                    }
 
                    if (val[end] == '\"') {
                        while (++end < val.Length && val[end] != '"') { 
                            ; 
                        }
                        if (end == val.Length ) { 
                            //warning: no closing quote, accepting
                            calback(val, start, end-1, list);
                            break;
                        } 
                        goto find_comma;
                    } 
                    else { 
                        //Comma
                        calback(val, start, end-1, list); 
                        // skip leading spaces
                        while (++end < val.Length && val[end] == ' ') {
                            ;
                        } 
                        if (end >= val.Length) {
                            break; 
                        } 
                        start = end;
                    } 
                }
            }
        }
    } 
    //
    // 
    // 
    //ATTN: The values order is importent
    internal enum HttpMethod { 
        Other   = -1,
        Head    = 0,
        Get,
        Post, 
        Put,
        Delete, 
        Options, 
        Trace,
        Connect 
    }
    //
    //
    // 
    internal class ResponseCacheControl {
        internal bool Public; 
        internal bool Private; 
        internal string[] PrivateHeaders;
        internal bool NoCache; 
        internal string[] NoCacheHeaders;
        internal bool NoStore;
        internal bool MustRevalidate;
        internal bool ProxyRevalidate; 
        internal int  MaxAge;
        internal int  SMaxAge; 
 
        internal ResponseCacheControl() {
            MaxAge = SMaxAge = -1; 
        }

        internal bool IsNotEmpty {
            get { 
                return (Public || Private || NoCache || NoStore || MustRevalidate || ProxyRevalidate || MaxAge != -1 || SMaxAge != -1);
            } 
        } 

        public override string  ToString() { 
            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            if (Public) {
                sb.Append(" public"); 
            }
            if (Private) { 
                sb.Append(" private"); 
                if (PrivateHeaders != null) {
                    sb.Append('='); 
                    for (int i = 0; i < PrivateHeaders.Length-1; ++i) {
                        sb.Append(PrivateHeaders[i]).Append(',');
                    }
                    sb.Append(PrivateHeaders[PrivateHeaders.Length-1]); 
                }
            } 
            if (NoCache) { 
                sb.Append(" no-cache");
                if (NoCacheHeaders != null) { 
                    sb.Append('=');
                    for (int i = 0; i < NoCacheHeaders.Length-1; ++i) {
                        sb.Append(NoCacheHeaders[i]).Append(',');
                    } 
                    sb.Append(NoCacheHeaders[NoCacheHeaders.Length-1]);
                } 
            } 
            if (NoStore) {
                sb.Append(" no-store"); 
            }
            if (MustRevalidate) {
                sb.Append(" must-revalidate");
            } 
            if (ProxyRevalidate) {
                sb.Append(" proxy-revalidate"); 
            } 
            if (MaxAge != -1) {
                sb.Append(" max-age=").Append(MaxAge); 
            }
            if (SMaxAge != -1) {
                sb.Append(" s-maxage=").Append(SMaxAge);
            } 
            return sb.ToString();
        } 
    } 

} 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
/*++ 
Copyright (c) Microsoft Corporation

Module Name:
 
    HttpRequestCacheValidator.cs
 
Abstract: 
    The class implements HTTP Caching validators as per RFC2616
 

Author:

    Alexei Vopilov    21-Dec-2002 

Revision History: 
 
    Jan 25 2004 - Changed the visibility of the class from public to internal.
 
--*/
namespace System.Net.Cache {
    using System;
    using System.Net; 
    using System.IO;
    using System.Collections; 
    using System.Text; 
    using System.Collections.Specialized;
    using System.Globalization; 
    using System.Threading;


    ///  The class represents an adavanced way for an application to control caching protocol  
    internal class HttpRequestCacheValidator: RequestCacheValidator {
        internal const string Warning_110 = "110 Response is stale"; 
        internal const string Warning_111 = "111 Revalidation failed"; 
        internal const string Warning_112 = "112 Disconnected operation";
        internal const string Warning_113 = "113 Heuristic expiration"; 

        private struct RequestVars {
            internal HttpMethod   Method;
            internal bool         IsCacheRange; 
            internal bool         IsUserRange;
            internal string       IfHeader1; 
            internal string       Validator1; 
            internal string       IfHeader2;
            internal string       Validator2; 
        }

        private HttpRequestCachePolicy m_HttpPolicy;
 
        private HttpStatusCode  m_StatusCode;
        private string          m_StatusDescription; 
        private Version         m_HttpVersion; 
        private WebHeaderCollection m_Headers;
        private NameValueCollection m_SystemMeta; 

        private bool            m_DontUpdateHeaders;
        private bool            m_HeuristicExpiration;
 
        private Vars            m_CacheVars;
        private Vars            m_ResponseVars; 
        private RequestVars     m_RequestVars; 

        private struct Vars { 
            internal DateTime         Date;
            internal DateTime         Expires;
            internal DateTime         LastModified;
            internal long             EntityLength; 
            internal TimeSpan         Age;
            internal TimeSpan         MaxAge; 
            internal ResponseCacheControl CacheControl; 
            internal long             RangeStart;
            internal long             RangeEnd; 

            internal void Initialize() {
                EntityLength = RangeStart = RangeEnd = -1;
                Date = DateTime.MinValue; 
                Expires = DateTime.MinValue;
                LastModified = DateTime.MinValue; 
                Age = TimeSpan.MinValue; 
                MaxAge = TimeSpan.MinValue;
            } 
        }


        //public 
        internal HttpStatusCode CacheStatusCode           {get{return m_StatusCode;}          set{m_StatusCode = value;}}
        //public 
        internal string         CacheStatusDescription    {get{return m_StatusDescription;}   set{m_StatusDescription = value;}} 
        //public
        internal Version        CacheHttpVersion          {get{return m_HttpVersion;}         set{m_HttpVersion = value;}} 

        //public
        internal WebHeaderCollection CacheHeaders         {get{return m_Headers;}             set{m_Headers = value;}}
 
        //public
        internal new HttpRequestCachePolicy   Policy      { 
                                                            get { 
                                                                if(m_HttpPolicy != null) return m_HttpPolicy;
                                                                m_HttpPolicy = base.Policy as HttpRequestCachePolicy; 
                                                                if(m_HttpPolicy != null) return m_HttpPolicy;
                                                                // promote base policy to Http one
                                                                m_HttpPolicy = new HttpRequestCachePolicy((HttpRequestCacheLevel)base.Policy.Level);
                                                                return m_HttpPolicy; 
                                                            }
                                                        } 
 
        internal NameValueCollection SystemMeta         {get{return m_SystemMeta;}                  set{m_SystemMeta = value;}}
        internal HttpMethod   RequestMethod             {get{return m_RequestVars.Method;}          set{m_RequestVars.Method = value;}} 
        internal bool         RequestRangeCache         {get{return m_RequestVars.IsCacheRange;}    set{m_RequestVars.IsCacheRange = value;}}
        internal bool         RequestRangeUser          {get{return m_RequestVars.IsUserRange;}     set{m_RequestVars.IsUserRange = value;}}
        internal string       RequestIfHeader1          {get{return m_RequestVars.IfHeader1;}       set{m_RequestVars.IfHeader1 = value;}}
        internal string       RequestValidator1         {get{return m_RequestVars.Validator1;}      set{m_RequestVars.Validator1 = value;}} 
        internal string       RequestIfHeader2          {get{return m_RequestVars.IfHeader2;}       set{m_RequestVars.IfHeader2 = value;}}
        internal string       RequestValidator2         {get{return m_RequestVars.Validator2;}      set{m_RequestVars.Validator2 = value;}} 
 
        internal bool         CacheDontUpdateHeaders    {get{return m_DontUpdateHeaders;}           set{m_DontUpdateHeaders = value;}}
 
        internal DateTime      CacheDate               {get{return m_CacheVars.Date;}              set{m_CacheVars.Date = value;}}
        internal DateTime      CacheExpires            {get{return m_CacheVars.Expires;}           set{m_CacheVars.Expires = value;}}
        internal DateTime      CacheLastModified       {get{return m_CacheVars.LastModified;}      set{m_CacheVars.LastModified = value;}}
        internal long          CacheEntityLength       {get{return m_CacheVars.EntityLength ;}     set{m_CacheVars.EntityLength  = value;}} 
        internal TimeSpan      CacheAge                {get{return m_CacheVars.Age;}               set{m_CacheVars.Age = value;}}
        internal TimeSpan      CacheMaxAge             {get{return m_CacheVars.MaxAge;}            set{m_CacheVars.MaxAge = value;}} 
        internal bool          HeuristicExpiration     {get{return m_HeuristicExpiration;}         set{m_HeuristicExpiration = value;}} 

        internal ResponseCacheControl     CacheCacheControl    {get{return m_CacheVars.CacheControl;}      set{m_CacheVars.CacheControl = value;}} 

        internal DateTime      ResponseDate            {get{return m_ResponseVars.Date;}         set{m_ResponseVars.Date = value;}}
        internal DateTime      ResponseExpires         {get{return m_ResponseVars.Expires;}      set{m_ResponseVars.Expires = value;}}
        internal DateTime      ResponseLastModified    {get{return m_ResponseVars.LastModified;} set{m_ResponseVars.LastModified = value;}} 
        internal long          ResponseEntityLength    {get{return m_ResponseVars.EntityLength ;}set{m_ResponseVars.EntityLength  = value;}}
        internal long          ResponseRangeStart      {get{return m_ResponseVars.RangeStart;}   set{m_ResponseVars.RangeStart = value;}} 
        internal long          ResponseRangeEnd        {get{return m_ResponseVars.RangeEnd;}     set{m_ResponseVars.RangeEnd = value;}} 
        internal TimeSpan      ResponseAge             {get{return m_ResponseVars.Age;}          set{m_ResponseVars.Age = value;}}
        internal ResponseCacheControl  ResponseCacheControl {get{return m_ResponseVars.CacheControl;} set{m_ResponseVars.CacheControl = value;}} 

        //
        private void ZeroPrivateVars()
        { 
            // Set default values for private members here
            m_RequestVars = new RequestVars(); 
 
            m_HttpPolicy        = null;
            m_StatusCode        = (HttpStatusCode)0; 
            m_StatusDescription = null;
            m_HttpVersion       = null;
            m_Headers           = null;
            m_SystemMeta        = null; 
            m_DontUpdateHeaders = false;
            m_HeuristicExpiration = false; 
 
            m_CacheVars   = new Vars();
            m_CacheVars.Initialize(); 

            m_ResponseVars= new Vars();
            m_ResponseVars.Initialize();
        } 

        //public 
        internal override RequestCacheValidator CreateValidator() 
        {
            return new HttpRequestCacheValidator(StrictCacheErrors, UnspecifiedMaxAge); 
        }

        /*
        //public 
        // Consider removing.
        internal HttpRequestCacheValidator(): base() 
        { 
        }
        */ 

        //public
        internal HttpRequestCacheValidator(bool strictCacheErrors, TimeSpan unspecifiedMaxAge): base(strictCacheErrors, unspecifiedMaxAge)
        { 
        }
 
        // 
        // This validation method is called first and before any Cache access is done.
        // Given the request instance the code has to decide whether the request is ever suitable for caching. 
        //
        // Returns:
        // Continue           = Proceed to the next protocol stage.
        // DoNotTakeFromCache = Don't used caches value for this request 
        // DoNotUseCache      = Cache is not used for this request and response is not cached.
        protected internal override CacheValidationStatus ValidateRequest() { 
 
            // cleanup context after previous  request
            ZeroPrivateVars(); 

            string method = Request.Method.ToUpper(CultureInfo.InvariantCulture);
            if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_request_method, method));
 
            switch (method) {
                case "GET" :    RequestMethod = HttpMethod.Get;      break; 
                case "POST":    RequestMethod = HttpMethod.Post;     break; 
                case "HEAD":    RequestMethod = HttpMethod.Head;     break;
                case "PUT" :    RequestMethod = HttpMethod.Put;      break; 
                case "DELETE":  RequestMethod = HttpMethod.Delete;   break;
                case "OPTIONS": RequestMethod = HttpMethod.Options;  break;
                case "TRACE":   RequestMethod = HttpMethod.Trace;    break;
                case "CONNECT": RequestMethod = HttpMethod.Connect;  break; 
                default:        RequestMethod = HttpMethod.Other;    break;
            } 
 
            // Apply our best knowledge of HTTP caching and return the result
            // that can be hooked up and revised by the upper level 
            return Rfc2616.OnValidateRequest(this);
        }

        // 
        // This validation method is called after caching protocol has retrieved the metadata of a cached entry.
        // Given the cached entry context, the request instance and the effective caching policy, 
        // the handler has to decide whether a cached item can be considered as fresh. 
        protected internal override CacheFreshnessStatus ValidateFreshness()
        { 

            // Transfer cache entry metadata into status line and headers.
            string s = ParseStatusLine();
 
            if(Logging.On) {
                if ((int) CacheStatusCode == 0) { 
                    Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_http_status_parse_failure, (s == null ? "null" : s))); 
                }
                else { 
                    Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_http_status_line, (CacheHttpVersion != null ? CacheHttpVersion.ToString() : "null"), (int)CacheStatusCode, CacheStatusDescription));
                }

            } 

            CreateCacheHeaders((int)CacheStatusCode != 0); 
            CreateSystemMeta(); 

            // We will need quick access to cache-control and other headers coming with the cached item 
            FetchHeaderValues(true);

            if(Logging.On) Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_cache_control, CacheCacheControl.ToString()));
 
            // Now we try to apply our best knowledge of HTTP caching and return the result
            // that can be hooked up and revised on the upper level 
            return Rfc2616.OnValidateFreshness(this); 
        }
 
        ///   This method may add headers under the "Warning" header name 
        protected internal override CacheValidationStatus ValidateCache() {

            if (this.Policy.Level != HttpRequestCacheLevel.Revalidate && base.Policy.Level >= RequestCacheLevel.Reload) 
            {
                // For those policies cache is never returned 
                GlobalLog.Assert("OnValidateCache()", "This validator should not be called for policy = " + Policy.ToString()); 
                if(Logging.On)Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_validator_invalid_for_policy, Policy.ToString()));
                return CacheValidationStatus.DoNotTakeFromCache; 
            }

            // First check is do we have a cached entry at all?
            // Also we include some very special case where cache got a 304 (NotModified) response somehow 
            if (CacheStream == Stream.Null || (int)CacheStatusCode == 0 || CacheStatusCode == HttpStatusCode.NotModified)
            { 
                if (this.Policy.Level == HttpRequestCacheLevel.CacheOnly) 
                {
                    // Throw because entry was not found and it's cache-only policy 
                    FailRequest(WebExceptionStatus.CacheEntryNotFound);
                }
                return CacheValidationStatus.DoNotTakeFromCache;
            } 

            if (RequestMethod == HttpMethod.Head) 
            { 
                // For a HEAD request we release the cache entry stream asap since we will have to suppress it anyway
                CacheStream.Close(); 
                CacheStream = new SyncMemoryStream(new byte[] {});
            }

            // Apply our best knowledge of HTTP caching and return the result 
            // that can be hooked up and revised by the upper level
 
            CacheValidationStatus result = CacheValidationStatus.DoNotTakeFromCache; 

            // 
            // Before request submission validation
            //

            // If we return from cache we should remove existing 1xx warnings 
            RemoveWarnings_1xx();
 
            // default values for a response from cache. 
            CacheStreamOffset       = 0;
            CacheStreamLength       = CacheEntry.StreamSize; 

            result = Rfc2616.OnValidateCache(this);
            if (result != CacheValidationStatus.ReturnCachedResponse && this.Policy.Level == HttpRequestCacheLevel.CacheOnly) {
                // Throw because entry was not found and it's cache-only policy 
                FailRequest(WebExceptionStatus.CacheEntryNotFound);
            } 
 
            if (result == CacheValidationStatus.ReturnCachedResponse)
            { 
                if (CacheFreshnessStatus == CacheFreshnessStatus.Stale) {
                    CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_110);
                }
                if (base.Policy.Level == RequestCacheLevel.CacheOnly) { 
                    CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_112);
                } 
                if (HeuristicExpiration && (int)CacheAge.TotalSeconds >= 24*3600) { 
                    CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_113);
                } 
            }

            if (result == CacheValidationStatus.DoNotTakeFromCache) {
                // We signal that current cache entry can be only replaced and not updated 
                CacheStatusCode = (HttpStatusCode) 0;
            } 
            else if (result == CacheValidationStatus.ReturnCachedResponse) { 
                CacheHeaders[HttpKnownHeaderNames.Age] = ((int)(CacheAge.TotalSeconds)).ToString(NumberFormatInfo.InvariantInfo);
            } 
            return result;
        }
        //
        // This is (optionally) called after receiveing a live response 
        //
        protected internal override CacheValidationStatus RevalidateCache() 
        { 
            if (this.Policy.Level != HttpRequestCacheLevel.Revalidate && base.Policy.Level >= RequestCacheLevel.Reload)
            { 
                // For those policies cache is never returned
                GlobalLog.Assert("RevalidateCache()", "This validator should not be called for policy = " + Policy.ToString());
                if(Logging.On)Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_validator_invalid_for_policy, Policy.ToString()));
                return CacheValidationStatus.DoNotTakeFromCache; 
            }
 
            // First check is do we have a cached entry at all? 
            // Also we include some very special case where cache got a 304 (NotModified) response somehow
            if (CacheStream == Stream.Null || (int)CacheStatusCode == 0 || CacheStatusCode == HttpStatusCode.NotModified) 
            {
                return CacheValidationStatus.DoNotTakeFromCache;
            }
 
            //
            // This is a second+ time validation after receiving at least one response 
            // 

            // Apply our best knowledge of HTTP caching and return the result 
            // that can be hooked up and revised by the upper level
            CacheValidationStatus result = CacheValidationStatus.DoNotTakeFromCache;

            HttpWebResponse resp = Response as HttpWebResponse; 
            if (resp == null)
            { 
                // This will result to an application error 
                return CacheValidationStatus.DoNotTakeFromCache;
            } 

            if (resp.StatusCode >= HttpStatusCode.InternalServerError) {
                // If server returned a 5XX server error
                if (Rfc2616.Common.ValidateCacheOn5XXResponse(this) == CacheValidationStatus.ReturnCachedResponse) { 
                    // We can substitute the response from cache
                    if (CacheFreshnessStatus == CacheFreshnessStatus.Stale) { 
                        CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_110); 
                    }
                    if (HeuristicExpiration && (int)CacheAge.TotalSeconds >= 24*3600) { 
                        CacheHeaders.Add(HttpKnownHeaderNames.Warning, HttpRequestCacheValidator.Warning_113);
                    }
                    // We actually failed to reach the origin server hence we don't reset the current Cache Age
                } 
            }
            else { 
 
                // if there was already one retry, then cache should not be taken into account
                if (ResponseCount > 1) { 
                    result =  CacheValidationStatus.DoNotTakeFromCache;
                }
                else {
                    /* 
                    Section 13.2.3:
                    HTTP/1.1 uses the Age response-header to convey the estimated age 
                    of the response message when obtained from a cache. 
                    The Age field value is the cache's estimate of the amount of time
                    since the response was generated or >>revalidated<< by the origin server. 
                    */
                    // Reset Cache Age to be 0 seconds
                    CacheAge = TimeSpan.Zero;
                    result = Rfc2616.Common.ValidateCacheAfterResponse(this, resp); 
                }
            } 
 
            if (result == CacheValidationStatus.ReturnCachedResponse)
            { 
                CacheHeaders[HttpKnownHeaderNames.Age] = ((int)(CacheAge.TotalSeconds)).ToString(NumberFormatInfo.InvariantInfo);
            }
            return result;
        } 

        ///  
        ///  
        /// This validation method is responsible to answer whether the live response is sufficient to make
        /// the final decision for caching protocol. 
        /// This is useful in case of possible failure or inconsistent results received from
        /// the remote cache.
        /// 
        ///  
        ///   Invalid response from this method means the request was internally modified and should be retried 
        protected internal override CacheValidationStatus ValidateResponse() { 
 
            if (this.Policy.Level != HttpRequestCacheLevel.CacheOrNextCacheOnly &&
                this.Policy.Level != HttpRequestCacheLevel.Default && 
                this.Policy.Level != HttpRequestCacheLevel.Revalidate)
            {
                // Those policy levels do not modify requests
                if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_response_valid_based_on_policy, Policy.ToString())); 
                return CacheValidationStatus.Continue;
            } 
 
            // We will need quick access to cache controls coming with the live response
            HttpWebResponse resp = Response as HttpWebResponse; 
            if (resp == null) {
                if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_null_response_failure));
                return CacheValidationStatus.Continue;
            } 

            FetchHeaderValues(false); 
            if(Logging.On) 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)
                                                      );

 
            // Apply our best knowledge of HTTP caching and return the result
            // that can be hooked up and revised by the upper level 
            return Rfc2616.OnValidateResponse(this); 
        }
 
        /// 
        /// 
        /// This action handler is responsible for making final decision on whether
        /// a received response can be cached. 
        /// 
        ///  
        ///   Invalid result from this method means the response must not be cached  
        protected internal override CacheValidationStatus UpdateCache() {
 
            if (this.Policy.Level == HttpRequestCacheLevel.NoCacheNoStore) {
                if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_removed_existing_based_on_policy, Policy.ToString()));
                return CacheValidationStatus.RemoveFromCache;
            } 
            if (this.Policy.Level == HttpRequestCacheLevel.CacheOnly) {
                if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_not_updated_based_on_policy, Policy.ToString())); 
                return CacheValidationStatus.DoNotUpdateCache; 
            }
 
            if (CacheHeaders == null)
                CacheHeaders = new WebHeaderCollection();

            if (SystemMeta == null) 
                SystemMeta = new NameValueCollection(1, CaseInsensitiveAscii.StaticInstance);
 
            if (ResponseCacheControl == null) { 
                //ValidateResponse was not invoked
                FetchHeaderValues(false); 
            }

            // Apply our best knowledge of HTTP caching and return the result
            // that can be hooked up and revised by the upper level 
            CacheValidationStatus result = Rfc2616.OnUpdateCache(this);
 
            if (result == CacheValidationStatus.UpdateResponseInformation || result == CacheValidationStatus.CacheResponse) 
            {
                FinallyUpdateCacheEntry(); 
            }
            return result;
        }
 
        //
        // 
        // 
        private void FinallyUpdateCacheEntry() {
            // Transfer the context status line back to the metadata 

            CacheEntry.EntryMetadata  = null;
            CacheEntry.SystemMetadata = null;
 
            if (CacheHeaders == null) {
                //must be an entry update without updating the headers 
                return; 
            }
 
            CacheEntry.EntryMetadata  = new StringCollection();
            CacheEntry.SystemMetadata = new StringCollection();

            if (CacheHttpVersion == null) { 
                if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_invalid_http_version));
                CacheHttpVersion = new Version(1, 0); 
            } 
            // HTTP/1.1 200 OK
            System.Text.StringBuilder sb = new System.Text.StringBuilder(CacheStatusDescription.Length + 20); 
            sb.Append("HTTP/");
            sb.Append(CacheHttpVersion.ToString(2));
            sb.Append(' ');
            sb.Append(((int)CacheStatusCode).ToString(NumberFormatInfo.InvariantInfo)); 
            sb.Append(' ');
            sb.Append(CacheStatusDescription); 
 
            // Fetch the status line into cache metadata
            CacheEntry.EntryMetadata.Add(sb.ToString()); 

            UpdateStringCollection(CacheEntry.EntryMetadata,  CacheHeaders, false);

            if (SystemMeta != null) 
            {
                UpdateStringCollection(CacheEntry.SystemMetadata, SystemMeta, true); 
            } 

            // Update other entry values 
            if (ResponseExpires != DateTime.MinValue) {
                CacheEntry.ExpiresUtc = ResponseExpires;
            }
 
            if (ResponseLastModified != DateTime.MinValue)
            { 
                CacheEntry.LastModifiedUtc = ResponseLastModified; 
            }
 
            if (this.Policy.Level == HttpRequestCacheLevel.Default)
            {
                    CacheEntry.MaxStale = this.Policy.MaxStale;
            } 

            CacheEntry.LastSynchronizedUtc = DateTime.UtcNow; 
        } 
        //
        // 
        //
        private static void UpdateStringCollection(StringCollection result, NameValueCollection cc, bool winInetCompat)
        {
            StringBuilder sb; 

            // Transfer headers 
            for (int i=0; i < cc.Count; ++i) 
            {
                    sb = new StringBuilder(40); 
                    string key   = cc.GetKey(i) as string;
                    sb.Append(key).Append(':');

                    string[] val = cc.GetValues(i); 
                    if (val.Length != 0)
                    { 
                        if (winInetCompat) 
                            {sb.Append(val[0]);}
                        else 
                            {sb.Append(' ').Append(val[0]);}
                    }

                    for (int j = 1; j < val.Length; ++j) 
                    {
                        sb.Append(key).Append(", ").Append(val[j]); 
                    } 
                    result.Add(sb.ToString());
            } 
            // Transfer last \r\n
            result.Add(string.Empty);
        }
 
        // The format is
        // HTTP/X.Y SP NUMBER SP STRING 
        // HTTP/1.1 200 OK 
        //
        private string ParseStatusLine() { 

            // This will indicate an invalid result
            CacheStatusCode = (HttpStatusCode)0;
 
            if (CacheEntry.EntryMetadata == null || CacheEntry.EntryMetadata.Count == 0)
            { 
                return null; 
            }
 
            string s = CacheEntry.EntryMetadata[0];

            if (s == null) {
                return null; 
            }
 
            int  idx = 0; 
            char ch = (char)0;
            while (++idx < s.Length && (ch=s[idx]) != '/') { 
                ;
            }

            if (idx == s.Length) {return s;} 

            int major = -1; 
            int minor = -1; 
            int status= -1;
 
            while (++idx < s.Length && (ch=s[idx]) >= '0' && ch <= '9') {
                major = (major<0? 0: major*10) +(ch - '0');
            }
 
            if (major < 0 || ch != '.') {return s;}
 
            while (++idx < s.Length && (ch=s[idx]) >= '0' && ch <= '9') { 
                minor = (minor<0? 0: minor*10) + (ch - '0');
            } 

            if (minor < 0 || (ch != ' ' && ch != '\t')) {return s;}

            while (++idx < s.Length && ((ch=s[idx]) == ' ' || ch == '\t')) 
                ;
 
            if (idx >= s.Length) {return s;} 

            while (ch >= '0' && ch <= '9') 
            {
                status = (status<0? 0: status*10) +(ch - '0');
                if (++idx == s.Length)
                    break; 
                ch=s[idx];
            } 
 
            if (status < 0 || (idx <= s.Length && (ch != ' ' && ch != '\t'))) {return s;}
 
            while (idx < s.Length && (s[idx] == ' ' || s[idx] == '\t'))
                ++idx;

            CacheStatusDescription = s.Substring(idx); 

            CacheHttpVersion = new Version(major, minor); 
            CacheStatusCode = (HttpStatusCode)status; 
            return s;
        } 
        //
        private void CreateCacheHeaders(bool ignoreFirstString)
        {
 
            if (CacheHeaders == null)
                CacheHeaders = new WebHeaderCollection(); 
 
            if (CacheEntry.EntryMetadata == null || CacheEntry.EntryMetadata.Count == 0)
            { 
                if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_no_http_response_header));
                return;
            }
 
            string s = ParseNameValues(CacheHeaders, CacheEntry.EntryMetadata, ignoreFirstString?1:0);
            if (s != null) 
            { 
                if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_http_header_parse_error, s));
                CacheHeaders.Clear(); 
            }
        }
        //
        private void CreateSystemMeta() 
        {
            if (SystemMeta == null) 
            { 
                SystemMeta = new NameValueCollection((CacheEntry.EntryMetadata == null || CacheEntry.EntryMetadata.Count == 0? 2: CacheEntry.EntryMetadata.Count),
                                                     CaseInsensitiveAscii.StaticInstance); 
            }
            if (CacheEntry.EntryMetadata == null || CacheEntry.EntryMetadata.Count == 0)
                {return;}
 
            string s = ParseNameValues(SystemMeta, CacheEntry.SystemMetadata, 0);
            if (s != null) 
            { 
                if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_metadata_name_value_parse_error, s));
            } 
        }
        //
        // Returns null on success, otherwise the offending string.
        // 
        private string ParseNameValues(NameValueCollection cc, StringCollection sc, int start)
        { 
            WebHeaderCollection wc = cc as WebHeaderCollection; 

            string lastHeaderName = null; 
            if (sc != null)
            {
                for (int i = start; i < sc.Count; ++i)
                { 
                    string s = sc[i];
                    if (s == null || s.Length == 0) 
                    { 
                        //An empty string stands for \r\n
                        //Treat that as the end of headers and ignore the rest 
                        return null;
                    }

                    if (s[0] == ' ' || s[0] == '\t') 
                    {
                        if (lastHeaderName == null) {return s;} 
                        if (wc != null) 
                            wc.AddInternal(lastHeaderName, s);
                        else 
                            cc.Add(lastHeaderName, s);
                    }

                    int colpos = s.IndexOf(':'); 
                    if (colpos < 0)
                        {return s;} 
                    lastHeaderName = s.Substring(0, colpos); 
                    while (++colpos < s.Length && (s[colpos] == ' ' || s[colpos] == '\t'))
                        {;} 

                    try {
                        if (wc != null)
                            wc.AddInternal(lastHeaderName, s.Substring(colpos)); 
                        else
                            cc.Add(lastHeaderName, s.Substring(colpos)); 
                    } 
                    catch(Exception e) {
                        if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) 
                            throw;
                        // Otherwise the value of 's' will be used to log an error.
                        // The fact that we cannot parse headers may stand for corrupted metadata that we try to ignore
                        return s; 
                    }
                } 
            } 
            return null;
        } 
        //
        //
        //
        private void FetchHeaderValues(bool forCache) { 

            WebHeaderCollection cc = forCache? CacheHeaders: Response.Headers; 
 

            FetchCacheControl(cc.CacheControl, forCache); 

            // Parse Date Header
            string s = cc.Date;
 
            DateTime date = DateTime.MinValue;
            if (s != null && HttpDateParse.ParseHttpDate(s, out date)) { 
                date = date.ToUniversalTime(); 
            }
            if (forCache) { 
                CacheDate = date;
            }
            else {
                ResponseDate = date; 
            }
 
            // Parse Expires Header 
            s = cc.Expires;
 
            date = DateTime.MinValue;
            if (s != null && HttpDateParse.ParseHttpDate(s, out date)) {
                date = date.ToUniversalTime();
            } 
            if (forCache) {
                CacheExpires = date; 
            } 
            else {
                ResponseExpires = date; 
            }

            // Parse LastModified Header
            s = cc.LastModified; 

            date = DateTime.MinValue; 
            if (s != null && HttpDateParse.ParseHttpDate(s, out date)) { 
                date = date.ToUniversalTime();
            } 
            if (forCache) {
                CacheLastModified = date;
            }
            else { 
                ResponseLastModified = date;
            } 
 
            long totalLength = -1;
            long startRange = -1; 
            long end = -1;

            HttpWebResponse resp = Response as HttpWebResponse;
            if ((forCache? CacheStatusCode: resp.StatusCode) != HttpStatusCode.PartialContent) { 

                // Parse Content-Length Header 
                s = cc.ContentLength; 
                if (s != null && s.Length != 0) {
                    int i = 0; 
                    char ch = s[0];
                    while (i < s.Length && ch == ' ') {
                        ch = s[++i];
                    } 
                    if (i != s.Length && ch >= '0' && ch <= '9') {
                        totalLength = ch-'0'; 
                        while(++i < s.Length && (ch = s[i]) >= '0' && ch <= '9') { 
                            totalLength = totalLength*10+(ch-'0');
                        } 
                    }
                }
            }
            else { 
                //Parse Content-Range
                s = cc[HttpKnownHeaderNames.ContentRange]; 
                if(s == null || !Rfc2616.Common.GetBytesRange(s, ref startRange, ref end, ref totalLength, false)) { 
                    if(Logging.On)Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_content_range_error, (s==null ? "" : s)));
                    startRange=end=totalLength = -1; 
                }
                else if (forCache && totalLength == CacheEntry.StreamSize)
                {
                    // This is a whole response, step back to 200 
                    startRange = -1;
                    end = -1; 
                    CacheStatusCode = HttpStatusCode.OK; 
                    CacheStatusDescription = Rfc2616.Common.OkDescription;
                } 
            }

            if (forCache) {
                CacheEntityLength  = totalLength; 
                ResponseRangeStart = startRange;
                ResponseRangeEnd   = end; 
 
            }
            else { 
                ResponseEntityLength = totalLength;
                ResponseRangeStart = startRange;
                ResponseRangeEnd   = end;
            } 

            //Parse Age Header 
            TimeSpan span = TimeSpan.MinValue; 
            s = cc[HttpKnownHeaderNames.Age];
            if (s != null) { 
                int i = 0;
                int sec = 0;
                while(i < s.Length && s[i++] == ' ') {
                    ; 
                }
                while(i < s.Length && s[i] >= '0' && s[i] <= '9') { 
                    sec = sec*10 + (s[i++] - '0'); 
                }
                span = TimeSpan.FromSeconds(sec); 
            }

            if (forCache) {
                CacheAge = span; 
            }
            else { 
                ResponseAge = span; 
            }
        } 

        const long LO = 0x0020002000200020L;
        const int  LOI = 0x00200020;
        const long _prox = 'p'|('r'<<16)|((long)'o'<<32)|((long)'x'<<48); 
        const long _y_re = 'y'|('-'<<16)|((long)'r'<<32)|((long)'e'<<48);
        const long _vali = 'v'|('a'<<16)|((long)'l'<<32)|((long)'i'<<48); 
        const long _date = 'd'|('a'<<16)|((long)'t'<<32)|((long)'e'<<48); 

        const long _publ = 'p'|('u'<<16)|((long)'b'<<32)|((long)'l'<<48); 
        const int  _ic   = 'i'|('c'<<16);

        const long _priv = 'p'|('r'<<16)|((long)'i'<<32)|((long)'v'<<48);
        const int  _at   = 'a'|('t'<<16); 

        const long _no_c = 'n'|('o'<<16)|((long)'-'<<32)|((long)'c'<<48); 
        const long _ache = 'a'|('c'<<16)|((long)'h'<<32)|((long)'e'<<48); 

        const long _no_s = 'n'|('o'<<16)|((long)'-'<<32)|((long)'s'<<48); 
        const long _tore = 't'|('o'<<16)|((long)'r'<<32)|((long)'e'<<48);

        const long _must = 'm'|('u'<<16)|((long)'s'<<32)|((long)'t'<<48);
        const long __rev = '-'|('r'<<16)|((long)'e'<<32)|((long)'v'<<48); 
        const long _alid = 'a'|('l'<<16)|((long)'i'<<32)|((long)'d'<<48);
 
        const long _max_ = 'm'|('a'<<16)|((long)'x'<<32)|((long)'-'<<48); 
        const int  _ag   = 'a'|('g'<<16);
 
        const long _s_ma = 's'|('-'<<16)|((long)'m'<<32)|((long)'a'<<48);
        const long _xage = 'x'|('a'<<16)|((long)'g'<<32)|((long)'e'<<48);
        //
        // 
        //
        private unsafe void FetchCacheControl(string s, bool forCache) { 
            //Initialize it 
            ResponseCacheControl control = new ResponseCacheControl();
            if (forCache) { 
                CacheCacheControl = control;
            }
            else {
                ResponseCacheControl = control; 
            }
 
            if (s != null && s.Length != 0) { 
                fixed (char *sp = s) {
                    int len = s.Length; 
                    for (int i = 0; i < len-4; ++i) {
                        if (sp[i] < ' ' || sp[i] >= 0x7F) {
                            if(Logging.On)Logging.PrintError(Logging.RequestCache, SR.GetString(SR.net_log_cache_cache_control_error, s));
                            //invalid format 
                            return;
                        } 
                        if (sp[i] == ' ' || sp[i] == ',') { 
                            continue;
                        } 

                        // These if-else are two logically identical blocks that differ only in the way of how text search is done.
                        // The text search is done differently for 32 and X-bits platforms.
                        // ATTN: You are responsible for keeping the rest of the logic in [....]. 
                        if (IntPtr.Size == 4) {
                            // We are on 32-bits platform 
 
                            long *mask = (long*)&(sp[i]);
                            //making interested chars lowercase, others are ignored anyway 
                            switch(*mask|LO) {

                                case _prox: if (i+16 > len) continue;
                                    if ((*(mask+1)|LO) != _y_re || (*(mask+2)|LO) != _vali || (*(mask+3)|LO) != _date) continue; 
                                    control.ProxyRevalidate = true;
                                    i+=15; 
                                    break; 

                                case _publ: if (i+6 > len) return; 
                                    if ((*((int*)(mask+1))|LOI) != _ic) continue;
                                    control.Public = true;
                                    i+=5;
                                    break; 

                                case _priv: if (i+7 > len) return; 
                                    if ((*((int*)(mask+1))|LOI) != _at || (sp[i+6]|0x20) != 'e') continue; 
                                    control.Private = true;
                                    i+=6; 
                                    // Check for a case: private = "name1,name2"
                                    while (i < len && sp[i] == ' ') {++i;}

                                    if (i >= len || sp[i] != '=') {--i;break;} 

                                    while (i < len && sp[++i] == ' ') {;} 
 
                                    if (i >= len || sp[i] != '\"') {--i;break;}
 
                                    System.Collections.ArrayList privateList = new System.Collections.ArrayList();
                                    ++i;
                                    while(i < len && sp[i] != '\"') {
 
                                        while (i < len && sp[i] == ' ') {++i;}
                                        int start = i; 
                                        while (i < len && sp[i] != ' ' && sp[i] != ',' && sp[i] != '\"') {++i;} 
                                        if (start != i) {
                                            privateList.Add(s.Substring(start, i-start)); 
                                        }
                                        while (i < len && sp[i] != ',' && sp[i] != '\"') {++i;}
                                    }
                                    if (privateList.Count != 0) { 
                                        control.PrivateHeaders = (string[])privateList.ToArray(typeof(string));
                                    } 
                                    break; 

                                case _no_c: if (i+8 > len) return; 
                                    if ((*(mask+1)|LOI) != _ache) continue;
                                    control.NoCache = true;
                                    i+=7;
                                    // Check for a case: no-cache = "name1,name2" 
                                    while (i < len && sp[i] == ' ') {++i;}
 
                                    if (i >= len || sp[i] != '=') {--i;break;} 

                                    while (i < len && sp[++i] == ' ') {;} 

                                    if (i >= len || sp[i] != '\"') {--i;break;}

                                    System.Collections.ArrayList nocacheList = new System.Collections.ArrayList(); 
                                    ++i;
                                    while(i < len && sp[i] != '\"') { 
 
                                        while (i < len && sp[i] == ' ') {++i;}
                                        int start = i; 
                                        while (i < len && sp[i] != ' ' && sp[i] != ',' && sp[i] != '\"') {++i;}
                                        if (start != i) {
                                            nocacheList.Add(s.Substring(start, i-start));
                                        } 
                                        while (i < len && sp[i] != ',' && sp[i] != '\"') {++i;}
                                    } 
                                    if (nocacheList.Count != 0) { 
                                        control.NoCacheHeaders = (string[])nocacheList.ToArray(typeof(string));
                                    } 
                                    break;

                                case _no_s: if (i+8 > len) return;
                                    if ((*(mask+1)|LOI) != _tore) continue; 
                                    control.NoStore = true;
                                    i+=7; 
                                    break; 

                                case _must: if (i+15 > len) continue; 

                                    if ((*(mask+1)|LO) != __rev || (*(mask+2)|LO) != _alid || (*(int*)(mask+3)|LOI) != _at || (sp[i+14]|0x20) != 'e') continue;
                                    control.MustRevalidate = true;
                                    i+=14; 
                                    break;
 
                                case _max_: if (i+7 > len) return; 
                                    if ((*((int*)(mask+1))|LOI) != _ag || (sp[i+6]|0x20) != 'e') continue;
                                    i+=7; 
                                    while (i < len && sp[i] == ' ') {
                                        ++i;
                                    }
                                    if (i == len || sp[i++] != '=') return; 
                                    while (i < len && sp[i] == ' ') {
                                        ++i; 
                                    } 
                                    if (i == len) return;
                                    control.MaxAge = 0; 
                                    while (i < len && sp[i] >= '0' && sp[i] <= '9') {
                                        control.MaxAge =control.MaxAge*10 + (sp[i++]-'0');
                                    }
                                    --i; 
                                    break;
 
                                case _s_ma: if (i+8 > len) return; 
                                    if ((*(mask+1)|LOI) != _xage) continue;
                                    i+=8; 
                                    while (i < len && sp[i] == ' ') {
                                        ++i;
                                    }
                                    if (i == len || sp[i++] != '=') return; 
                                    while (i < len && sp[i] == ' ') {
                                        ++i; 
                                    } 
                                    if (i == len) return;
                                    control.SMaxAge = 0; 
                                    while (i < len && sp[i] >= '0' && sp[i] <= '9') {
                                        control.SMaxAge = control.SMaxAge*10 + (sp[i++]-'0');
                                    }
                                    --i; 
                                    break;
                            } 
                        } 
                        else {
                            // We cannot use optimized code path due to IA-64 memory alligment problems see VSWhidbey 118967 
                            if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "proxy-revalidate")) {
                                control.ProxyRevalidate = true;
                                i+=15;
                            } 
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "public")) {
                                control.Public = true; 
                                i+=5; 
                            }
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "private")) { 
                                control.Private = true;
                                i+=6;
                                // Check for a case: private = "name1,name2"
                                while (i < len && sp[i] == ' ') {++i;} 

                                if (i >= len || sp[i] != '=') {--i;break;} 
 
                                while (i < len && sp[++i] == ' ') {;}
 
                                if (i >= len || sp[i] != '\"') {--i;break;}

                                System.Collections.ArrayList privateList = new System.Collections.ArrayList();
                                ++i; 
                                while(i < len && sp[i] != '\"') {
 
                                    while (i < len && sp[i] == ' ') {++i;} 
                                    int start = i;
                                    while (i < len && sp[i] != ' ' && sp[i] != ',' && sp[i] != '\"') {++i;} 
                                    if (start != i) {
                                        privateList.Add(s.Substring(start, i-start));
                                    }
                                    while (i < len && sp[i] != ',' && sp[i] != '\"') {++i;} 
                                }
                                if (privateList.Count != 0) { 
                                    control.PrivateHeaders = (string[])privateList.ToArray(typeof(string)); 
                                }
                            } 
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "no-cache")) {
                                control.NoCache = true;
                                i+=7;
                                // Check for a case: no-cache = "name1,name2" 
                                while (i < len && sp[i] == ' ') {++i;}
 
                                if (i >= len || sp[i] != '=') {--i;break;} 

                                while (i < len && sp[++i] == ' ') {;} 

                                if (i >= len || sp[i] != '\"') {--i;break;}

                                System.Collections.ArrayList nocacheList = new System.Collections.ArrayList(); 
                                ++i;
                                while(i < len && sp[i] != '\"') { 
 
                                    while (i < len && sp[i] == ' ') {++i;}
                                    int start = i; 
                                    while (i < len && sp[i] != ' ' && sp[i] != ',' && sp[i] != '\"') {++i;}
                                    if (start != i) {
                                        nocacheList.Add(s.Substring(start, i-start));
                                    } 
                                    while (i < len && sp[i] != ',' && sp[i] != '\"') {++i;}
                                } 
                                if (nocacheList.Count != 0) { 
                                    control.NoCacheHeaders = (string[])nocacheList.ToArray(typeof(string));
                                } 
                            }
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "no-store")) {
                                control.NoStore = true;
                                i+=7; 
                            }
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "must-revalidate")) { 
                                control.MustRevalidate = true; 
                                i+=14;
                            } 
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "max-age")) {
                                i+=7;
                                while (i < len && sp[i] == ' ') {
                                    ++i; 
                                }
                                if (i == len || sp[i++] != '=') return; 
                                while (i < len && sp[i] == ' ') { 
                                    ++i;
                                } 
                                if (i == len) return;
                                control.MaxAge = 0;
                                while (i < len && sp[i] >= '0' && sp[i] <= '9') {
                                    control.MaxAge =control.MaxAge*10 + (sp[i++]-'0'); 
                                }
                                --i; 
                            } 
                            else if (Rfc2616.Common.UnsafeAsciiLettersNoCaseEqual(sp, i, len, "smax-age")) {
                                i+=8; 
                                while (i < len && sp[i] == ' ') {
                                    ++i;
                                }
                                if (i == len || sp[i++] != '=') return; 
                                while (i < len && sp[i] == ' ') {
                                    ++i; 
                                } 
                                if (i == len) return;
                                control.SMaxAge = 0; 
                                while (i < len && sp[i] >= '0' && sp[i] <= '9') {
                                    control.SMaxAge = control.SMaxAge*10 + (sp[i++]-'0');
                                }
                                --i; 
                            }
                        } 
                    } 
                }
            } 
        }

        /*
          - any stored Warning headers with warn-code 1xx (see section 
        14.46) MUST be deleted from the cache entry and the forwarded
        response. 
 
          - any stored Warning headers with warn-code 2xx MUST be retained
        in the cache entry and the forwarded response. 
        */
        private void RemoveWarnings_1xx() {

            string[] warnings = CacheHeaders.GetValues(HttpKnownHeaderNames.Warning); 
            if (warnings == null) {
                return; 
            } 
            ArrayList remainingWarnings = new ArrayList();
            ParseHeaderValues(warnings, ParseWarningsCallback, remainingWarnings); 
            CacheHeaders.Remove(HttpKnownHeaderNames.Warning);
            for (int i=0; i < remainingWarnings.Count; ++i) {
                CacheHeaders.Add(HttpKnownHeaderNames.Warning, (string)remainingWarnings[i]);
            } 
        }
 
        private static readonly ParseCallback ParseWarningsCallback = new ParseCallback(ParseWarningsCallbackMethod); 
        private static void ParseWarningsCallbackMethod(string s, int start, int end, IList list) {
            if (end >= start && s[start] != '1') { 
                ParseValuesCallbackMethod(s, start, end, list);
            }
        }
        // 
        // This is used by other classes to get the list if values from a header string
        // 
        internal delegate void ParseCallback(string s, int start, int end, IList list); 
        internal static readonly ParseCallback ParseValuesCallback = new ParseCallback(ParseValuesCallbackMethod);
        private static void ParseValuesCallbackMethod(string s, int start, int end, IList list) { 

            // Deal with the cases: '' ' ' 'value' 'value   '
            while (end >= start && s[end] == ' ') {
                --end; 
            }
            if (end >= start) { 
                list.Add(s.Substring(start, end-start+1)); 
            }
        } 


        //
        // Parses header values calls a callback one value after other. 
        // Note a single string can contain multiple values and any value may have a quoted string in.
        // The parser will not cut trailing spaces when invoking a callback 
        // 
        internal static void ParseHeaderValues(string[] values, ParseCallback calback, IList list) {
 
            if (values == null) {
                return;
            }
            for (int i = 0; i < values.Length; ++i) { 
                string val = values[i];
 
                int end = 0; 
                int start = 0;
                while (end < val.Length) { 
                    //skip spaces
                    while (start < val.Length && val[start] == ' ') {
                        ++start;
                    } 

                    if (start == val.Length ) { 
                        //empty header value 
                        break;
                    } 

                    // find comma or quote
                    end = start;
                find_comma: 
                    while (end < val.Length && val[end] != ',' && val[end] != '\"') {
                        ++end; 
                    } 

                    if (end == val.Length ) { 
                        calback(val, start, end-1, list);
                        break;
                    }
 
                    if (val[end] == '\"') {
                        while (++end < val.Length && val[end] != '"') { 
                            ; 
                        }
                        if (end == val.Length ) { 
                            //warning: no closing quote, accepting
                            calback(val, start, end-1, list);
                            break;
                        } 
                        goto find_comma;
                    } 
                    else { 
                        //Comma
                        calback(val, start, end-1, list); 
                        // skip leading spaces
                        while (++end < val.Length && val[end] == ' ') {
                            ;
                        } 
                        if (end >= val.Length) {
                            break; 
                        } 
                        start = end;
                    } 
                }
            }
        }
    } 
    //
    // 
    // 
    //ATTN: The values order is importent
    internal enum HttpMethod { 
        Other   = -1,
        Head    = 0,
        Get,
        Post, 
        Put,
        Delete, 
        Options, 
        Trace,
        Connect 
    }
    //
    //
    // 
    internal class ResponseCacheControl {
        internal bool Public; 
        internal bool Private; 
        internal string[] PrivateHeaders;
        internal bool NoCache; 
        internal string[] NoCacheHeaders;
        internal bool NoStore;
        internal bool MustRevalidate;
        internal bool ProxyRevalidate; 
        internal int  MaxAge;
        internal int  SMaxAge; 
 
        internal ResponseCacheControl() {
            MaxAge = SMaxAge = -1; 
        }

        internal bool IsNotEmpty {
            get { 
                return (Public || Private || NoCache || NoStore || MustRevalidate || ProxyRevalidate || MaxAge != -1 || SMaxAge != -1);
            } 
        } 

        public override string  ToString() { 
            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            if (Public) {
                sb.Append(" public"); 
            }
            if (Private) { 
                sb.Append(" private"); 
                if (PrivateHeaders != null) {
                    sb.Append('='); 
                    for (int i = 0; i < PrivateHeaders.Length-1; ++i) {
                        sb.Append(PrivateHeaders[i]).Append(',');
                    }
                    sb.Append(PrivateHeaders[PrivateHeaders.Length-1]); 
                }
            } 
            if (NoCache) { 
                sb.Append(" no-cache");
                if (NoCacheHeaders != null) { 
                    sb.Append('=');
                    for (int i = 0; i < NoCacheHeaders.Length-1; ++i) {
                        sb.Append(NoCacheHeaders[i]).Append(',');
                    } 
                    sb.Append(NoCacheHeaders[NoCacheHeaders.Length-1]);
                } 
            } 
            if (NoStore) {
                sb.Append(" no-store"); 
            }
            if (MustRevalidate) {
                sb.Append(" must-revalidate");
            } 
            if (ProxyRevalidate) {
                sb.Append(" proxy-revalidate"); 
            } 
            if (MaxAge != -1) {
                sb.Append(" max-age=").Append(MaxAge); 
            }
            if (SMaxAge != -1) {
                sb.Append(" s-maxage=").Append(SMaxAge);
            } 
            return sb.ToString();
        } 
    } 

} 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK