public void TryParseList_WithSomeInvalidValues_ExcludesInvalidValues() { var inputs = new[] { "", "\"tag\", tag, \"tag\"", "tag, \"tag\"", "", " \"tag ", "\r\n tag\"\r\n ", "\"tag会\"", "\"tag\", \"tag\"", "W/\"tag\"", }; Assert.True(EntityTagHeaderValue.TryParseList(inputs, out var results)); var expectedResults = new[] { new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag会\""), new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\"", true), }.ToList(); Assert.Equal(expectedResults, results); }
public void TryParseList_SetOfValidValueStrings_ParsedCorrectly() { var inputs = new[] { "", "\"tag\"", "", " \"tag\" ", "\r\n \"tag\"\r\n ", "\"tag会\"", "\"tag\",\"tag\"", "\"tag\", \"tag\"", "W/\"tag\"", }; Assert.True(EntityTagHeaderValue.TryParseList(inputs, out var results)); var expectedResults = new[] { new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag会\""), new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\"", true), }.ToList(); Assert.Equal(expectedResults, results); }
internal static bool ContentIsNotModified(ResponseCachingContext context) { var cachedResponseHeaders = context.CachedResponseHeaders; var ifNoneMatchHeader = context.HttpContext.Request.Headers[HeaderNames.IfNoneMatch]; if (!StringValues.IsNullOrEmpty(ifNoneMatchHeader)) { if (ifNoneMatchHeader.Count == 1 && StringSegment.Equals(ifNoneMatchHeader[0], EntityTagHeaderValue.Any.Tag, StringComparison.OrdinalIgnoreCase)) { context.Logger.LogNotModifiedIfNoneMatchStar(); return(true); } EntityTagHeaderValue eTag; IList <EntityTagHeaderValue> ifNoneMatchEtags; if (!StringValues.IsNullOrEmpty(cachedResponseHeaders[HeaderNames.ETag]) && EntityTagHeaderValue.TryParse(cachedResponseHeaders[HeaderNames.ETag].ToString(), out eTag) && EntityTagHeaderValue.TryParseList(ifNoneMatchHeader, out ifNoneMatchEtags)) { for (var i = 0; i < ifNoneMatchEtags.Count; i++) { var requestETag = ifNoneMatchEtags[i]; if (eTag.Compare(requestETag, useStrongComparison: false)) { context.Logger.LogNotModifiedIfNoneMatchMatched(requestETag); return(true); } } } } else { var ifModifiedSince = context.HttpContext.Request.Headers[HeaderNames.IfModifiedSince]; if (!StringValues.IsNullOrEmpty(ifModifiedSince)) { DateTimeOffset modified; if (!HeaderUtilities.TryParseDate(cachedResponseHeaders[HeaderNames.LastModified].ToString(), out modified) && !HeaderUtilities.TryParseDate(cachedResponseHeaders[HeaderNames.Date].ToString(), out modified)) { return(false); } DateTimeOffset modifiedSince; if (HeaderUtilities.TryParseDate(ifModifiedSince.ToString(), out modifiedSince) && modified <= modifiedSince) { context.Logger.LogNotModifiedIfModifiedSinceSatisfied(modified, modifiedSince); return(true); } } } return(false); }
private static bool EtagEquals(HttpRequest request, EntityTagHeaderValue currentEtag) { // CORE TODO Double check this... before I found the GetTypedHeaders() extension method, I didn't think there was a typed implementation of the IfNoneMatch header // anymore, so I reimplemented based on the response caching middleware's implementation, leaving out the * match. See // https://github.com/aspnet/ResponseCaching/blob/52e8ffefb9b22c7c591aec15845baf07ed99356c/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingMiddleware.cs#L454-L478 var ifNoneMatchHeader = request.Headers["If-None-Match"]; if (StringValues.IsNullOrEmpty(ifNoneMatchHeader)) { return(false); } return(EntityTagHeaderValue.TryParseList(ifNoneMatchHeader, out IList <Microsoft.Net.Http.Headers.EntityTagHeaderValue> ifNoneMatchEtags) && ifNoneMatchEtags.Any(t => currentEtag.Compare(t, useStrongComparison: false))); }
private static bool RequestContainsMatchingETag(IHeaderDictionary requestHeaders, EntityTagHeaderValue responseETag) { if (requestHeaders.Keys.Contains(HeaderNames.IfNoneMatch) && EntityTagHeaderValue.TryParseList(requestHeaders[HeaderNames.IfNoneMatch], out IList <EntityTagHeaderValue> requestETags)) { foreach (EntityTagHeaderValue requestETag in requestETags) { if (responseETag.Equals(requestETag)) { return(true); } } } return(false); }
public static async Task <ActionResult> GenerateActionResult(ControllerBase controller, ICacheableDataProvider provider, TimeSpan?maxAge = null) { const string CacheControlHeaderKey = "Cache-Control"; const string IfNonMatchHeaderKey = "If-None-Match"; const string IfModifiedSinceHeaderKey = "If-Modified-Since"; const string ETagHeaderKey = "ETag"; const string LastModifiedHeaderKey = "Last-Modified"; string GenerateCacheControlHeaderValue() { var cacheControlHeader = new CacheControlHeaderValue() { NoCache = true, NoStore = false, MaxAge = maxAge ?? TimeSpan.FromDays(14), Private = true, MustRevalidate = true }; return(cacheControlHeader.ToString()); } var digest = await provider.GetDigest(); var eTagValue = $"\"{digest.ETag}\""; var eTag = new EntityTagHeaderValue(eTagValue); ActionResult Generate304Result() { controller.Response.Headers.Add(ETagHeaderKey, eTagValue); controller.Response.Headers.Add(LastModifiedHeaderKey, digest.LastModified.ToString("R")); controller.Response.Headers.Add(CacheControlHeaderKey, GenerateCacheControlHeaderValue()); return(controller.StatusCode(StatusCodes.Status304NotModified, null)); } if (controller.Request.Headers.TryGetValue(IfNonMatchHeaderKey, out var ifNonMatchHeaderValue)) { if (!EntityTagHeaderValue.TryParseList(ifNonMatchHeaderValue, out var eTagList)) { return(controller.BadRequest(new CommonResponse(ErrorCodes.Common.Header.IfNonMatch_BadFormat, "Header If-None-Match is of bad format."))); } if (eTagList.FirstOrDefault(e => e.Equals(eTag)) != null) { return(Generate304Result()); } } else if (controller.Request.Headers.TryGetValue(IfModifiedSinceHeaderKey, out var ifModifiedSinceHeaderValue)) { if (!DateTime.TryParse(ifModifiedSinceHeaderValue, out var headerValue)) { return(controller.BadRequest(new CommonResponse(ErrorCodes.Common.Header.IfModifiedSince_BadFormat, "Header If-Modified-Since is of bad format."))); } if (headerValue > digest.LastModified) { return(Generate304Result()); } } var data = await provider.GetData(); controller.Response.Headers.Add(CacheControlHeaderKey, GenerateCacheControlHeaderValue()); return(controller.File(data.Data, data.ContentType, digest.LastModified, eTag)); }
public void TryParseList_NullOrEmptyArray_ReturnsFalse() { Assert.False(EntityTagHeaderValue.TryParseList(null, out var results)); Assert.False(EntityTagHeaderValue.TryParseList(new string[0], out results)); Assert.False(EntityTagHeaderValue.TryParseList(new string[] { "" }, out results)); }