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

ValidateCacheAfterResponse() public static method

public static ValidateCacheAfterResponse ( HttpRequestCacheValidator ctx, HttpWebResponse resp ) : CacheValidationStatus
ctx HttpRequestCacheValidator
resp System.Net.HttpWebResponse
return CacheValidationStatus
            public static CacheValidationStatus ValidateCacheAfterResponse(HttpRequestCacheValidator ctx, HttpWebResponse resp) {

                if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_after_validation));

                if ((ctx.CacheStream == Stream.Null || (int)ctx.CacheStatusCode == 0) && resp.StatusCode == HttpStatusCode.NotModified) {
                    if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_resp_status_304));
                    return CacheValidationStatus.DoNotTakeFromCache;
                }

                if (ctx.RequestMethod == HttpMethod.Head) {
                    /*
                           The response to a HEAD request MAY be cacheable in the sense that the
                           information contained in the response MAY be used to update a
                           previously cached entity from that resource. If the new field values
                           indicate that the cached entity differs from the current entity (as
                           would be indicated by a change in Content-Length, Content-MD5, ETag
                           or Last-Modified), then the cache MUST treat the cache entry as
                           stale.
                    */
                    bool invalidate = false;

                    if (ctx.ResponseEntityLength != -1 && ctx.ResponseEntityLength != ctx.CacheEntityLength) {
                        if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_head_resp_has_different_content_length));
                        invalidate = true;
                    }
                    if (resp.Headers[HttpKnownHeaderNames.ContentMD5] != ctx.CacheHeaders[HttpKnownHeaderNames.ContentMD5]) {
                        if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_head_resp_has_different_content_md5));
                        invalidate = true;
                    }
                    if (resp.Headers.ETag != ctx.CacheHeaders.ETag) {
                        if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_head_resp_has_different_etag));
                        invalidate = true;
                    }
                    if (resp.StatusCode != HttpStatusCode.NotModified && resp.Headers.LastModified != ctx.CacheHeaders.LastModified)
                    {
                        if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_304_head_resp_has_different_last_modified));
                        invalidate = true;
                    }
                    if (invalidate) {
                        if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_existing_entry_has_to_be_discarded));
                        return CacheValidationStatus.RemoveFromCache;
                    }
                }

                // If server has returned 206 partial content
                if (resp.StatusCode == HttpStatusCode.PartialContent) {
                    /*
                           A cache MUST NOT combine a 206 response with other previously cached
                           content if the ETag or Last-Modified headers do not match exactly,
                           see 13.5.4.
                    */

                    // Sometime if ETag has been used the server won't include Last-Modified, which seems to be OK
                    if (ctx.CacheHeaders.ETag != ctx.Response.Headers.ETag ||
                        (ctx.CacheHeaders.LastModified != ctx.Response.Headers.LastModified
                         && (ctx.Response.Headers.LastModified != null || ctx.Response.Headers.ETag == null)))
                    {
                        if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_206_resp_non_matching_entry));
                        if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_existing_entry_should_be_discarded));
                        return CacheValidationStatus.RemoveFromCache;
                    }


                    // check does the live stream fit exactly into our cache tail
                    if (ctx.CacheEntry.StreamSize != ctx.ResponseRangeStart) {
                        if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_206_resp_starting_position_not_adjusted));
                        return CacheValidationStatus.DoNotTakeFromCache;
                    }

                    Common.ReplaceOrUpdateCacheHeaders(ctx, resp);
                    if (ctx.RequestRangeUser) {
                        // This happens when a response is being downloaded page by page

                        // We request combining the streams
                        // A user will see data starting CacheStreamOffset of a combined stream
                        ctx.CacheStreamOffset       = ctx.CacheEntry.StreamSize;
                        // This is a user response content length
                        ctx.CacheStreamLength       = ctx.ResponseRangeEnd - ctx.ResponseRangeStart + 1;
                        // This is a new cache stream size
                        ctx.CacheEntityLength       = ctx.ResponseEntityLength;

                        ctx.CacheStatusCode         = resp.StatusCode;
                        ctx.CacheStatusDescription  = resp.StatusDescription;
                        ctx.CacheHttpVersion        = resp.ProtocolVersion;
                    }
                    else {
                        // This happens when previous response was downloaded partly

                        ctx.CacheStreamOffset       = 0;
                        ctx.CacheStreamLength       = ctx.ResponseEntityLength;
                        ctx.CacheEntityLength       = ctx.ResponseEntityLength;

                        ctx.CacheStatusCode         = HttpStatusCode.OK;
                        ctx.CacheStatusDescription  = Common.OkDescription;
                        ctx.CacheHttpVersion        = resp.ProtocolVersion;
                        ctx.CacheHeaders.Remove(HttpKnownHeaderNames.ContentRange);

                        if (ctx.CacheStreamLength == -1)
                            {ctx.CacheHeaders.Remove(HttpKnownHeaderNames.ContentLength);}
                        else
                            {ctx.CacheHeaders[HttpKnownHeaderNames.ContentLength] = ctx.CacheStreamLength.ToString(NumberFormatInfo.InvariantInfo);}

                    }
                    // At this point the protocol should create a combined stream made up of the cached and live streams
                    if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_combined_resp_requested));
                    return CacheValidationStatus.CombineCachedAndServerResponse;
                }

                /*
                304 Not Modified
                    The response MUST include the following header fields:

                      - Date, unless its omission is required by section 14.18.1

                   If a clockless origin server obeys these rules, and proxies and
                   clients add their own Date to any response received without one (as
                   already specified by [RFC 2068], section 14.19), caches will operate
                   correctly.

                      - ETag and/or Content-Location, if the header would have been sent
                        in a 200 response to the same request

                      - Expires, Cache-Control, and/or Vary, if the field-value might
                        differ from that sent in any previous response for the same
                        variant
                */

                if (resp.StatusCode == HttpStatusCode.NotModified) {


                    WebHeaderCollection cc = resp.Headers;

                    string  location = null;
                    string  etag = null;

                    if ((ctx.CacheExpires != ctx.ResponseExpires) ||
                        (ctx.CacheLastModified != ctx.ResponseLastModified) ||
                        (ctx.CacheDate != ctx.ResponseDate) ||
                        (ctx.ResponseCacheControl.IsNotEmpty) ||
                        ((location=cc[HttpKnownHeaderNames.ContentLocation]) != null && location != ctx.CacheHeaders[HttpKnownHeaderNames.ContentLocation]) ||
                        ((etag=cc.ETag) != null && etag != ctx.CacheHeaders.ETag)) {
                        // Headers have to be updated
                        // Note that would allow a new E-Tag header to come in without changing the content.
                        if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_updating_headers_on_304));
                        Common.ReplaceOrUpdateCacheHeaders(ctx, resp);
                        return CacheValidationStatus.ReturnCachedResponse;
                    }

                    //Try to not update headers if they are invariant or the same
                    int ignoredHeaders = 0;
                    if (etag != null) {
                        ++ignoredHeaders;
                    }
                    if (location != null) {
                        ++ignoredHeaders;
                    }
                    if (ctx.ResponseAge != TimeSpan.MinValue) {
                        ++ignoredHeaders;
                    }
                    if (ctx.ResponseLastModified != DateTime.MinValue) {
                        ++ignoredHeaders;
                    }
                    if (ctx.ResponseExpires != DateTime.MinValue) {
                        ++ignoredHeaders;
                    }
                    if (ctx.ResponseDate != DateTime.MinValue) {
                        ++ignoredHeaders;
                    }
                    if (cc.Via != null) {
                        ++ignoredHeaders;
                    }
                    if (cc[HttpKnownHeaderNames.Connection] != null) {
                        ++ignoredHeaders;
                    }
                    if (cc[HttpKnownHeaderNames.KeepAlive] != null) {
                        ++ignoredHeaders;
                    }
                    if (cc.ProxyAuthenticate != null) {
                        ++ignoredHeaders;
                    }
                    if (cc[HttpKnownHeaderNames.ProxyAuthorization] != null) {
                        ++ignoredHeaders;
                    }
                    if (cc[HttpKnownHeaderNames.TE] != null) {
                        ++ignoredHeaders;
                    }
                    if (cc[HttpKnownHeaderNames.TransferEncoding] != null) {
                        ++ignoredHeaders;
                    }
                    if (cc[HttpKnownHeaderNames.Trailer] != null) {
                        ++ignoredHeaders;
                    }
                    if (cc[HttpKnownHeaderNames.Upgrade] != null) {
                        ++ignoredHeaders;
                    }

                    if (resp.Headers.Count <= ignoredHeaders) {
                        if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_suppressing_headers_update_on_304));
                        ctx.CacheDontUpdateHeaders = true;
                    }
                    else {
                        Common.ReplaceOrUpdateCacheHeaders(ctx, resp);
                    }
                    return CacheValidationStatus.ReturnCachedResponse;
                }

                // Any other response
                if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_status_code_not_304_206));
                return CacheValidationStatus.DoNotTakeFromCache;
            }