System.Net.Cache.Rfc2616.Common.OnUpdateCache C# (CSharp) Method

OnUpdateCache() static private method

static private OnUpdateCache ( HttpRequestCacheValidator ctx, HttpWebResponse resp ) : TriState
ctx HttpRequestCacheValidator
resp System.Net.HttpWebResponse
return TriState
            internal static TriState OnUpdateCache(HttpRequestCacheValidator ctx, HttpWebResponse resp) {
                /*
                    Quick check on supported methods.
                */
                if (ctx.RequestMethod != HttpMethod.Head &&
                    ctx.RequestMethod != HttpMethod.Get  &&
                    ctx.RequestMethod != HttpMethod.Post) {
                    if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_not_a_get_head_post));
                    return TriState.Unknown;
                }

                //If the entry did not exist ...
                if (ctx.CacheStream == Stream.Null || (int)ctx.CacheStatusCode == 0) {
                    if(resp.StatusCode == HttpStatusCode.NotModified) {
                        if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_cannot_update_cache_if_304));
                        return TriState.Unknown;
                    }
                    if (ctx.RequestMethod == HttpMethod.Head) {
                        // Protection from some caching Head response when entry does not exist.
                        if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_cannot_update_cache_with_head_resp));
                        return TriState.Unknown;
                    }
                }


                if (resp == null) {
                    if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_http_resp_is_null));
                    return TriState.Unknown;
                }

                //
                // We assume that ctx.ResponseCacheControl is already updated based on a live response
                //

                /*
                no-store
                      ... If sent in a response,
                      a cache MUST NOT store any part of either this response or the
                      request that elicited it.
                */
                if (ctx.ResponseCacheControl.NoStore) {
                    if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_resp_cache_control_is_no_store));
                    return TriState.Unknown;
                }


                /*
                If there is neither a cache validator nor an explicit expiration time
                   associated with a response, we do not expect it to be cached, but
                   certain caches MAY violate this expectation (for example, when little
                   or no network connectivity is available). A client can usually detect
                   that such a response was taken from a cache by comparing the Date
                   header to the current time.
                */

                // NOTE: If no Expire and no Validator peresnt we choose to CACHE
                //===============================================================


                /*
                    Note: a new response that has an older Date header value than
                    existing cached responses is not cacheable.
                */
                if (ctx.ResponseDate != DateTime.MinValue && ctx.CacheDate != DateTime.MinValue) {
                    if (ctx.ResponseDate < ctx.CacheDate) {
                        if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_resp_older_than_cache));
                        return TriState.Unknown;
                    }
                }

                /*
                public
                      Indicates that the response MAY be cached by any cache, even if it
                      would normally be non-cacheable or cacheable only within a non-
                      shared cache. (See also Authorization, section 14.8, for
                      additional details.)
                */
                if (ctx.ResponseCacheControl.Public) {
                    if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_resp_cache_control_is_public));
                    return TriState.Valid;
                }

                // sometiem public cache can cache a response with "private" directive, subject to other restrictions
                TriState result = TriState.Unknown;

                /*
                private
                      Indicates that all or part of the response message is intended for
                      a single user and MUST NOT be cached by a shared cache. This
                      allows an origin server to state that the specified parts of the

                      response are intended for only one user and are not a valid
                      response for requests by other users. A private (non-shared) cache
                      MAY cache the response.
                */
                if (ctx.ResponseCacheControl.Private) {
                    if (!ctx.CacheEntry.IsPrivateEntry) {
                        if (ctx.ResponseCacheControl.PrivateHeaders == null) {
                            if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_resp_cache_control_is_private));
                            return TriState.Unknown;
                        }
                        if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_resp_cache_control_is_private_plus_headers));
                        for (int i = 0; i < ctx.ResponseCacheControl.PrivateHeaders.Length; ++i) {
                            ctx.CacheHeaders.Remove(ctx.ResponseCacheControl.PrivateHeaders[i]);
                            result = TriState.Valid;
                        }
                    }
                    else {
                        result = TriState.Valid;
                    }
                }


                /*
                    The RFC is funky on no-cache directive.
                    But the bottom line is sometime you CAN cache no-cache responses.

                */
                if (ctx.ResponseCacheControl.NoCache)
                {
                        if (ctx.ResponseLastModified == DateTime.MinValue && ctx.Response.Headers.ETag == null) {
                            if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_revalidation_required));
                            return TriState.Unknown;
                        }
                        if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_needs_revalidation));
                        return TriState.Valid;
                }

                if (ctx.ResponseCacheControl.SMaxAge != -1 || ctx.ResponseCacheControl.MaxAge != -1) {
                    if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_resp_allows_caching, ctx.ResponseCacheControl.ToString()));
                    return TriState.Valid;
                }

                /*
                  When a shared cache (see section 13.7) receives a request
                  containing an Authorization field, it MUST NOT return the
                  corresponding response as a reply to any other request, unless one
                  of the following specific exceptions holds:

                  1. If the response includes the "s-maxage" cache-control

                  2. If the response includes the "must-revalidate" cache-control

                  3. If the response includes the "public" cache-control directive,
                */
                if (!ctx.CacheEntry.IsPrivateEntry && ctx.Request.Headers[HttpKnownHeaderNames.Authorization] != null) {
                    // we've already passed an opportunity to cache.
                    if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_auth_header_and_no_s_max_age));
                    return TriState.Unknown;
                }

                /*
                    POST
                    Responses to this method are not cacheable, unless the response
                    includes appropriate Cache-Control or Expires header fields.
                */
                if (ctx.RequestMethod == HttpMethod.Post && resp.Headers.Expires == null) {
                    if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_post_resp_without_cache_control_or_expires));
                    return TriState.Unknown;
                }

                /*
                 A response received with a status code of 200, 203, 206, 300, 301 or
                   410 MAY be stored by a cache and used in reply to a subsequent
                   request, subject to the expiration mechanism, unless a cache-control
                   directive prohibits caching. However, a cache that does not support
                   the Range and Content-Range headers MUST NOT cache 206 (Partial
                   Content) responses.

                   NOTE: We added 304 here which is correct
                */
                if (resp.StatusCode == HttpStatusCode.NotModified ||
                    resp.StatusCode == HttpStatusCode.OK ||
                    resp.StatusCode == HttpStatusCode.NonAuthoritativeInformation ||
                    resp.StatusCode == HttpStatusCode.PartialContent ||
                    resp.StatusCode == HttpStatusCode.MultipleChoices ||
                    resp.StatusCode == HttpStatusCode.MovedPermanently ||
                    resp.StatusCode == HttpStatusCode.Gone)
                {
                    if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_valid_based_on_status_code, (int)resp.StatusCode));
                    return TriState.Valid;
                }

                /*
                   A response received with any other status code (e.g. status codes 302
                   and 307) MUST NOT be returned in a reply to a subsequent request
                   unless there are cache-control directives or another header(s) that
                   explicitly allow it. For example, these include the following: an
                   Expires header (section 14.21); a "max-age", "s-maxage",  "must-
                   revalidate", "proxy-revalidate", "public" or "private" cache-control
                   directive (section 14.9).
                 */
                if (result != TriState.Valid) {
                    // otheriwse there was a "safe" private directive that allows caching
                    if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_resp_no_cache_control, (int)resp.StatusCode));
                }
                return result;
            }