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); } }
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); }