private async Task <bool> ConditionalGetOrHeadIsValid(HttpContext httpContext) { _logger.LogInformation("Checking for conditional GET/HEAD."); if (httpContext.Request.Method != HttpMethod.Get.ToString() && httpContext.Request.Method != HttpMethod.Head.ToString()) { _logger.LogInformation("Not valid - method isn't GET or HEAD."); return(false); } // we should check ALL If-None-Match values (can be multiple eTags) (if available), // and the If-Modified-Since date (if available AND an eTag matches). See issue #2 @Github. // So, this is a valid conditional GET/HEAD (304) if one of the ETags match and, if it's // available, the If-Modified-Since date is larger than what's saved. // if both headers are missing, we should // always return false - we don't need to check anything, and // can never return a 304 response if (!httpContext.Request.Headers.Keys.Contains(HeaderNames.IfNoneMatch) && !httpContext.Request.Headers.Keys.Contains(HeaderNames.IfModifiedSince)) { _logger.LogInformation("Not valid - no If-None-Match or If-Modified-Since headers."); return(false); } // generate the request key var storeKey = await _storeKeyGenerator.GenerateStoreKey( ConstructStoreKeyContext(httpContext.Request, _validationModelOptions)); // find the validatorValue with this key in the store var validatorValue = await _store.GetAsync(storeKey); // if there is no validation value in the store, always // return false - we have nothing to compare to, and can // never return a 304 response if (validatorValue?.ETag == null) { _logger.LogInformation("No saved response found in store."); return(false); } if (httpContext.Request.Headers.Keys.Contains(HeaderNames.IfNoneMatch)) { // check the ETags. If the Etag is present we should not check the If-Modified-Since header // see issue https://github.com/KevinDockx/HttpCacheHeaders/issues/82 // cfr: "If none of the entity tags match, then the server MAY perform the requested method as if the // If-None-Match header field did not exist, but MUST also ignore any If-Modified-Since header field(s) // in the request. That is, if no entity tags match, then the server MUST NOT return a 304(Not Modified) response." return(CheckIfNoneMatchIsValid(httpContext, validatorValue)); } _logger.LogInformation("No If-None-Match header, don't check ETag."); return(await CheckIfModifiedSinceIsValid(httpContext, validatorValue)); }
private async Task <bool> ConditionalGetOrHeadIsValid(HttpContext httpContext) { _logger.LogInformation("Checking for conditional GET/HEAD."); if (httpContext.Request.Method != HttpMethod.Get.ToString() && httpContext.Request.Method != HttpMethod.Head.ToString()) { _logger.LogInformation("Not valid - method isn't GET or HEAD."); return(false); } // we should check ALL If-None-Match values (can be multiple eTags) (if available), // and the If-Modified-Since date (if available AND an eTag matches). See issue #2 @Github. // So, this is a valid conditional GET/HEAD (304) if one of the ETags match and, if it's // available, the If-Modified-Since date is larger than what's saved. // if both headers are missing, we should // always return false - we don't need to check anything, and // can never return a 304 response if (!httpContext.Request.Headers.Keys.Contains(HeaderNames.IfNoneMatch) && !httpContext.Request.Headers.Keys.Contains(HeaderNames.IfModifiedSince)) { _logger.LogInformation("Not valid - no If-None-Match or If-Modified-Since headers."); return(false); } // generate the request key var storeKey = await _storeKeyGenerator.GenerateStoreKey( ConstructStoreKeyContext(httpContext.Request, _validationModelOptions)); // find the validatorValue with this key in the store var validatorValue = await _store.GetAsync(storeKey); // if there is no validation value in the store, always // return false - we have nothing to compare to, and can // never return a 304 response if (validatorValue?.ETag == null) { _logger.LogInformation("No saved response found in store."); return(false); } // check the ETags // return the combined result of all validators. return(CheckIfNoneMatchIsValid(httpContext, validatorValue) && await CheckIfModifiedSinceIsValid(httpContext, validatorValue)); }
// Key = generated from request URI & headers (if VaryBy is set, only use those headers) public async Task <EntityTagHeaderValue> GenerateETag(HttpContext context) { var storeKey = (await _storeKeyGenerator.GenerateStoreKey(context)).ToString(); var requestKeyAsBytes = Encoding.UTF8.GetBytes(storeKey); // combine both to generate an etag var combinedBytes = GetETagValue(requestKeyAsBytes); return(new ETag(_eTagType, combinedBytes).GetETag()); }