private PreconditionResult ExecutePrecondition(ActionContext actionContext, IPreconditionInformation information)
        {
            var preconditionBuilder = new HttpPreconditionBuilder(actionContext.HttpContext);
            var getResult           = preconditionBuilder.BuildPrecondition(actionContext.HttpContext.Request, this.ActionOptions.MissingPreconditionResult);

            this.Log(TraceEventType.Verbose, "Executing precondition.");

            return(getResult(information));
        }
        private static bool TryBuildLastModified(IPreconditionInformation preconditionInformation, out DateTimeOffset headerValue)
        {
            if (preconditionInformation.ModifiedOn > DateTimeOffset.MinValue)
            {
                headerValue = preconditionInformation.ModifiedOn;

                return(true);
            }
            else
            {
                headerValue = DateTimeOffset.MinValue;

                return(false);
            }
        }
        private static bool TryBuildEntityTagHeader(IPreconditionInformation preconditionInformation, out EntityTagHeaderValue headerValue)
        {
            string eTag;

            if (ETagUtility.TryCreate(preconditionInformation, out eTag))
            {
                headerValue = new EntityTagHeaderValue(ETagUtility.FormatStandard(eTag));

                return(true);
            }
            else
            {
                headerValue = null;

                return(false);
            }
        }
示例#4
0
        public static bool TryCreate(IPreconditionInformation info, out string eTag)
        {
            if (info == null)
            {
                throw new ArgumentNullException("info");
            }


            eTag = null;
            if (info.RowVersion != null && info.RowVersion.Length > 0)
            {
                eTag = BitHelper.ConvertToHex(info.RowVersion);
            }
            else
            {
                eTag = BitHelper.ConvertToHex(info.ModifiedOn);
            }
            return(eTag != null);
        }
        /// <summary>
        /// Gets the result for a HTTP method that doesn't change server side state.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="defaultResult">The default result.</param>
        /// <param name="localInformation">The local information.</param>
        /// <returns>PreconditionResult.</returns>
        protected virtual PreconditionResult GetResultForNonMutating(HttpRequest request, PreconditionResult defaultResult, IPreconditionInformation localInformation)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (defaultResult == null)
            {
                throw new ArgumentNullException(nameof(defaultResult));
            }

            if (localInformation == null)
            {
                throw new ArgumentNullException(nameof(localInformation));
            }

            var httpRequestHeaders = request.GetTypedHeaders();

            // Predicate code.

            string localETag;

            if (ETagUtility.TryCreate(localInformation, out localETag))
            {
                localETag = ETagUtility.FormatStandard(localETag);

                // Favours eTags over timestamps.
                //
                if (httpRequestHeaders.IfNoneMatch != null && httpRequestHeaders.IfNoneMatch.Any())
                {
                    foreach (var eTag in httpRequestHeaders.IfNoneMatch)
                    {
                        _logger?.LogTrace($"Precondition If-None-Match: client {eTag} vs local {localETag}");

                        if (eTag.Tag == localETag)
                        {
                            _logger?.LogInformation("Precondition If-None-Match: false");

                            return(PreconditionResult.PreconditionFailedNotModified); // As soon as one matches, we've failed (if *none* match).
                        }
                    }

                    _logger?.LogInformation("Precondition If-None-Match: true");

                    return(PreconditionResult.PreconditionPassed);
                }

                // Falls through to try modified stamps.
                //
                if (httpRequestHeaders.IfModifiedSince.HasValue && localInformation.ModifiedOn > DateTimeOffset.MinValue)
                {
                    DateTimeOffset clientTimestamp = httpRequestHeaders.IfModifiedSince.Value;

                    _logger?.LogTrace($"Precondition If-None-Match: client {clientTimestamp} vs local {localInformation.ModifiedOn}");

                    if (IsTimeSignificantlyGreaterThan(clientTimestamp, localInformation.ModifiedOn))
                    {
                        _logger?.LogInformation("Precondition If-Modified-Since: true");

                        return(PreconditionResult.PreconditionPassed);
                    }
                    else
                    {
                        _logger?.LogInformation("Precondition If-Modified-Since: false");

                        return(PreconditionResult.PreconditionFailedNotModified);
                    }
                }

                _logger?.LogInformation("Precondition headers not found: " + defaultResult.ToString());
            }
            else
            {
                _logger?.LogInformation("Local version data not found: " + defaultResult.ToString());
            }

            _logger?.LogInformation("The information required to determine precondition is not present. Defaulting to HTTP " + defaultResult.StatusCode);

            return(defaultResult);
        }
        /// <summary>
        /// Executes the precondition and returns the result.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="defaultResult">The default result.</param>
        /// <param name="localInformation">The local information.</param>
        /// <returns>PreconditionResult.</returns>
        public PreconditionResult GetResult(HttpRequest request, PreconditionResult defaultResult, IPreconditionInformation localInformation)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (defaultResult == null)
            {
                throw new ArgumentNullException(nameof(defaultResult));
            }

            if (localInformation == null)
            {
                throw new ArgumentNullException(nameof(localInformation));
            }

            if (HttpPreconditionBuilder.IsMutatingMethod(request.Method))
            {
                return(this.GetResultForMutating(request, defaultResult, localInformation));
            }
            else
            {
                return(this.GetResultForNonMutating(request, defaultResult, localInformation));
            }
        }
        /// <summary>
        /// Gets the result for a HTTP method that changes server side state.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="defaultResult">The default result.</param>
        /// <param name="localInformation">The server side information.</param>
        /// <returns>PreconditionResult.</returns>
        /// <exception cref="System.NotSupportedException">
        /// The wildcard ETag conditional PUT|DELETE is not supported.
        /// or
        /// The wildcard ETag conditional PUT|DELETE is not supported.
        /// </exception>
        protected virtual PreconditionResult GetResultForMutating(HttpRequest request, PreconditionResult defaultResult, IPreconditionInformation localInformation)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            if (defaultResult == null)
            {
                throw new ArgumentNullException(nameof(defaultResult));
            }

            if (localInformation == null)
            {
                throw new ArgumentNullException(nameof(localInformation));
            }

            var httpRequestHeaders = request.GetTypedHeaders();

            // Predicate code.

            string localETag;

            if (ETagUtility.TryCreate(localInformation, out localETag))
            {
                localETag = ETagUtility.FormatStandard(localETag);

                // Favours eTags over timestamps.
                //
                if (httpRequestHeaders.IfMatch != null && httpRequestHeaders.IfMatch.Any())
                {
                    foreach (var eTag in httpRequestHeaders.IfMatch)
                    {
                        _logger?.LogTrace($"Precondition If-None-Match: client {eTag} vs local {localETag}");

                        if (eTag.Tag == "*")
                        {
                            throw new NotSupportedException("The wildcard ETag conditional PUT|DELETE is not supported.");
                        }

                        if (eTag.Tag == localETag)
                        {
                            _logger?.LogInformation("Precondition If-Match: true");

                            return(PreconditionResult.PreconditionPassed);
                        }
                    }

                    _logger?.LogInformation("Precondition If-Match: false");

                    return(PreconditionResult.PreconditionFailed);
                }

                if (httpRequestHeaders.IfNoneMatch != null && httpRequestHeaders.IfNoneMatch.Any())
                {
                    foreach (var eTag in httpRequestHeaders.IfNoneMatch)
                    {
                        _logger?.LogTrace($"Precondition If-None-Match: client {eTag} vs local {localETag}");

                        if (eTag.Tag == "*")
                        {
                            throw new NotSupportedException("The wildcard ETag conditional PUT|DELETE is not supported.");
                        }

                        if (eTag.Tag == localETag)
                        {
                            _logger?.LogInformation("Precondition If-None-Match: false");

                            return(PreconditionResult.PreconditionFailed); // As soon as one matches, we've failed (if *none* match).
                        }
                    }

                    _logger?.LogInformation("Precondition If-None-Match: true");

                    return(PreconditionResult.PreconditionPassed);
                }

                // Falls through to try modified stamps, note: *Un*modified since.
                //
                if (httpRequestHeaders.IfUnmodifiedSince.HasValue && localInformation.ModifiedOn > DateTimeOffset.MinValue)
                {
                    DateTimeOffset clientTimestamp = httpRequestHeaders.IfUnmodifiedSince.Value;

                    _logger?.LogTrace($"Precondition If-Unmodified-Since: client {clientTimestamp} vs local {localInformation.ModifiedOn}");

                    // Pass, only if the server resource has not been modified since the specified date/time, i.e. if the timestamp
                    // on the server's copy matches that which the client has.
                    //
                    if (AreTimesAlmostEqual(clientTimestamp, localInformation.ModifiedOn))
                    {
                        _logger?.LogInformation("Precondition If-Unmodified-Since: true");

                        return(PreconditionResult.PreconditionPassed);
                    }
                    else
                    {
                        _logger?.LogInformation("Precondition If-Unmodified-Since: false");

                        return(PreconditionResult.PreconditionFailed);
                    }
                }

                _logger?.LogInformation("Precondition headers not found: " + defaultResult.ToString());
            }
            else
            {
                _logger?.LogInformation("Local version data not found: " + defaultResult.ToString());
            }

            _logger?.LogInformation("The information required to determine precondition is not present. Defaulting to HTTP " + defaultResult.StatusCode);

            return(defaultResult);
        }
        /// <summary>
        /// Applies version headers based on information in the supplied content.
        /// </summary>
        public static ActionContext ApplyVersionHeaders(this ActionContext actionContext, IPreconditionInformation versionableContent)
        {
            var headers = actionContext.HttpContext.Response.GetTypedHeaders();

            headers.Date = DateTimeOffset.Now;

            EntityTagHeaderValue eTagHeader;

            if (TryBuildEntityTagHeader(versionableContent, out eTagHeader))
            {
                headers.ETag = eTagHeader;
            }

            DateTimeOffset modified;

            if (TryBuildLastModified(versionableContent, out modified))
            {
                headers.LastModified = modified;
            }

            return(actionContext);
        }