private bool CheckIfModifiedSinceIsValid(HttpContext httpContext, ValidationValue validationValue)
        {
            if (httpContext.Request.Headers.Keys.Contains(HeaderNames.IfModifiedSince))
            {
                // if the LastModified date is smaller than the IfModifiedSince date,
                // we can return a 304 Not Modified (If there's also a matching ETag).
                // By adding an If-Modified-Since date
                // to a GET/HEAD request, the consumer is stating that (s)he only wants the resource
                // to be returned if if has been modified after that.
                var ifModifiedSinceValue = httpContext.Request.Headers[HeaderNames.IfModifiedSince].ToString();
                _logger.LogInformation($"Checking If-Modified-Since: {ifModifiedSinceValue}");

                if (DateTimeOffset.TryParseExact(
                        ifModifiedSinceValue,
                        "r",
                        CultureInfo.InvariantCulture.DateTimeFormat,
                        DateTimeStyles.AdjustToUniversal,
                        out var parsedIfModifiedSince))
                {
                    return(validationValue.LastModified.CompareTo(parsedIfModifiedSince) <= 0);
                }

                // can only check if we can parse it. Invalid values must be ignored.
                _logger.LogInformation("Cannot parse If-Modified-Since value as date, header is ignored.");
                return(true);
            }

            // if there is no IfModifiedSince header, the check is valid.
            _logger.LogInformation("No If-Modified-Since header.");
            return(true);
        }
        private bool CheckIfUnmodifiedSinceIsValid(HttpContext httpContext, ValidationValue validationValue)
        {
            // Either the ETag matches (or one of them), or there was no IfMatch header.
            // Continue with checking the IfUnModifiedSince header, if it exists.
            if (httpContext.Request.Headers.Keys.Contains(HeaderNames.IfUnmodifiedSince))
            {
                // if the LastModified date is smaller than the IfUnmodifiedSince date,
                // the precondition is valid.
                var ifUnModifiedSinceValue = httpContext.Request.Headers[HeaderNames.IfUnmodifiedSince].ToString();
                _logger.LogInformation($"Checking If-Unmodified-Since: {ifUnModifiedSinceValue}");

                if (DateTimeOffset.TryParseExact(
                        ifUnModifiedSinceValue,
                        "r",
                        CultureInfo.InvariantCulture.DateTimeFormat,
                        DateTimeStyles.AdjustToUniversal,
                        out var parsedIfUnModifiedSince))
                {
                    // If the LastModified date is smaller than the IfUnmodifiedSince date,
                    // the precondition is valid.
                    return(validationValue.LastModified.CompareTo(parsedIfUnModifiedSince) < 0);
                }

                // can only check if we can parse it. Invalid values must be ignored.
                _logger.LogInformation("Cannot parse If-Unmodified-Since value as date, header is ignored.");
                return(true);
            }

            // if there is no IfUnmodifiedSince header, the check is valid.
            _logger.LogInformation("No If-Unmodified-Since header.");
            return(true);
        }
        private bool CheckIfMatchIsValid(HttpContext httpContext, ValidationValue validationValue)
        {
            if (!httpContext.Request.Headers.Keys.Contains(HeaderNames.IfMatch))
            {
                // if there is no IfMatch header, the tag precondition is valid.
                _logger.LogInformation("No If-Match header, don't check ETag.");
                return(true);
            }

            _logger.LogInformation("Checking If-Match.");
            var ifMatchHeaderValue = httpContext.Request.Headers[HeaderNames.IfMatch].ToString().Trim();

            _logger.LogInformation($"Checking If-Match: {ifMatchHeaderValue}.");

            // if the value is *, the check is valid.
            if (ifMatchHeaderValue == "*")
            {
                return(true);
            }

            // otherwise, check the actual ETag(s)
            var eTagsFromIfMatchHeader = ifMatchHeaderValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

            // check the ETag.  If one of the ETags matches, the
            // ETag precondition is valid.

            // for concurrency checks, we use the strong
            // comparison function.
            if (eTagsFromIfMatchHeader.Any(eTag => ETagsMatch(validationValue.ETag, eTag.Trim(), true)))
            {
                _logger.LogInformation($"ETag valid: {validationValue.ETag}.");
                return(true);
            }

            // if there is an IfMatch header but none of the ETags match,
            // the precondition is already invalid.  We don't have to
            // continue checking.

            _logger.LogInformation("Not valid. No match found for ETag.");
            return(false);
        }
        private bool CheckIfNoneMatchIsValid(HttpContext httpContext, ValidationValue validationValue)
        {
            if (!httpContext.Request.Headers.Keys.Contains(HeaderNames.IfNoneMatch))
            {
                // if there is no IfNoneMatch header, the tag precondition is valid.
                _logger.LogInformation("No If-None-Match header, don't check ETag.");
                return(true);
            }

            _logger.LogInformation("Checking If-None-Match.");
            var ifNoneMatchHeaderValue = httpContext.Request.Headers[HeaderNames.IfNoneMatch].ToString().Trim();

            _logger.LogInformation($"Checking If-None-Match: {ifNoneMatchHeaderValue}.");

            // if the value is *, the check is valid.
            if (ifNoneMatchHeaderValue == "*")
            {
                return(true);
            }

            var eTagsFromIfNoneMatchHeader = ifNoneMatchHeaderValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

            // check the ETag.  If one of the ETags matches, we're good to
            // go and can return a 304 Not Modified.
            // For conditional GET/HEAD, we use weak comparison.
            if (eTagsFromIfNoneMatchHeader.Any(eTag => ETagsMatch(validationValue.ETag, eTag.Trim(), false)))
            {
                _logger.LogInformation($"ETag valid: {validationValue.ETag}.");
                return(true);
            }

            // if there is an IfNoneMatch header, but none of the eTags match, we don't take the
            // If-Modified-Since headers into account.
            //
            // 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."

            _logger.LogInformation("Not valid. No match found for ETag.");
            return(false);
        }