-
Notifications
You must be signed in to change notification settings - Fork 39
Conversation
_requestHeaders = HttpContext.Request.GetTypedHeaders(); | ||
_parsedResponseDate = true; | ||
DateTimeOffset date; | ||
if (DateTimeOffset.TryParse(HttpContext.Response.Headers[HeaderNames.Date], out date)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
format/culture?
@@ -0,0 +1,23 @@ | |||
using System; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copyright
@@ -0,0 +1,74 @@ | |||
using System; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copyright
{ | ||
internal class CacheControlValues | ||
{ | ||
public const string MaxAgeString = "max-age"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any chance we make these strings public in HttpAbstractions so we don't duplicated it here?
_responseDate = TypedResponseHeaders.Date; | ||
_parsedResponseSharedMaxAge = true; | ||
_responseSharedMaxAge = null; | ||
foreach (var header in HttpContext.Response.Headers[HeaderNames.CacheControl]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weren't we trying to eliminate the use of foreach
to reduce allocations for enumerators?
_responseCacheControl = TypedResponseHeaders.CacheControl ?? EmptyCacheControl; | ||
_parsedResponseExpires = true; | ||
DateTimeOffset expires; | ||
if (ParsingHelpers.TryStringToDate(HttpContext.Response.Headers[HeaderNames.Expires], out expires)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TryStringToDate
=> TryParseDate
_responseExpires = TypedResponseHeaders.Expires; | ||
_parsedResponseMaxAge = true; | ||
_responseMaxAge = null; | ||
foreach (var header in HttpContext.Response.Headers[HeaderNames.CacheControl]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks very similar to parsing ResponseSharedMaxAge
, extract into a helper method in ParsingHelpers
?
var expires = context.CachedResponseHeaders.Expires; | ||
if (responseTime >= expires) | ||
DateTimeOffset expires; | ||
if (context.CachedResponseHeaders[HeaderNames.Expires].Count > 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Combine the nested if statements.
{ | ||
// Try the various date formats in the order listed above. | ||
// We should accept a wide verity of common formats, but only output RFC 1123 style dates. | ||
if (DateTimeOffset.TryParseExact(input, DateFormats, DateTimeFormatInfo.InvariantInfo, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so return DateTimeOffset.TryParseExact(...)
? Why the if statement? In fact you can do => DateTimeOffset.TryParseExact(...)
return false; | ||
} | ||
|
||
internal static bool TryParseHeaderValue(int startIndex, string header, out int value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add tests for this.
while (endIndex < header.Length) | ||
{ | ||
var c = header[endIndex]; | ||
if ((c >= '0') && (c <= '9')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about spaces between equals and the first number? max-age = 60
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could have swore I tested that... will fix
context.Logger.LogNotModifiedIfNoneMatchMatched(tag); | ||
return true; | ||
EntityTagHeaderValue requestETag; | ||
if (EntityTagHeaderValue.TryParse(tag, out requestETag)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
combine if statements
var lastModified = cachedResponseHeaders.LastModified ?? cachedResponseHeaders.Date; | ||
if (lastModified <= ifUnmodifiedSince) | ||
DateTimeOffset modified; | ||
if (!ParsingHelpers.TryStringToDate(cachedResponseHeaders[HeaderNames.LastModified], out modified)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Combine if statements
We should probably see whether we want to do the parsing on demand each time or parse all of the cache-control values in one go, need to test the performance of the two approaches. |
@@ -0,0 +1,35 @@ | |||
using Microsoft.AspNetCore.ResponseCaching.Internal; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No copyright = copyleft and we don't like that
@@ -0,0 +1,100 @@ | |||
// Copyright (c) .NET Foundation. All rights reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file should go in the Internal folder.
internal static bool TryParseDate(string input, out DateTimeOffset result) => DateTimeOffset.TryParseExact(input, DateFormats, DateTimeFormatInfo.InvariantInfo, | ||
DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeUniversal, out result); | ||
|
||
internal static bool TryGetHeaderValue(StringValues headers, string headerName, out int value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm the method name isn't really descriptive. Maybe TryParseHeaderInt
? Should also consolidate the naming so maybe the previous method should be renamed to TryParseHeaderDate
{ | ||
context.Logger.LogResponseWithoutPublicNotCacheable(); | ||
return false; | ||
} | ||
|
||
// Check no-store | ||
if (context.RequestCacheControlHeaderValue.NoStore || context.ResponseCacheControlHeaderValue.NoStore) | ||
foreach (var header in context.HttpContext.Request.Headers[HeaderNames.CacheControl]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be extracted in a helper as well. ParsingHelper.HeaderContains(string)
|
||
namespace Microsoft.AspNetCore.ResponseCaching.Internal | ||
{ | ||
internal static class ParsingHelpers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HttpHeaderParsingHelpers
?
DateTimeOffset unmodifiedSince; | ||
if (ParsingHelpers.TryParseDate(ifUnmodifiedSince, out unmodifiedSince)) | ||
{ | ||
if (modified <= unmodifiedSince) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Combine if
{ | ||
context.Logger.LogExpirationExpiresExceeded(responseTime, expires.Value); | ||
return false; | ||
if (context.ResponseTime.Value >= expires) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
combine if
} | ||
|
||
TimeSpan? requestMaxStale = null; | ||
if (ParsingHelpers.TryGetHeaderValue(requestCacheControlHeaders, CacheControlValues.MaxStaleString, out seconds)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually this can be simplified by having a ParsingHelpers.TryParseTimeSpan
(or some consistent naming) that returns a TimeSpan.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't pass a nullable as an out parameter :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes you can
e8de495
to
54be93e
Compare
54be93e
to
aba398d
Compare
{ | ||
internal static class HttpHeaderParsingHelpers | ||
{ | ||
private static readonly string[] DateFormats = new string[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this duplicated from HttpAbstractions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is private in that repo. We are considering moving a few parts of this pr to abstractions so we'll see if this is still needed here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also taking a look at what we can move to Abstractions to eliminate duplication. Also, please review the changes in my latest commit.
{ | ||
if (cachedResponseHeaders.ETag.Compare(tag, useStrongComparison: false)) | ||
foreach (var tag in ifNoneMatchHeader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you can use a foreach
here since it's not guaranteed that each string contains only one ETag due how StringValues from HTTP headers are stored. Let's take a look at how it is parsed in Abstractions.
var lastModified = cachedResponseHeaders.LastModified ?? cachedResponseHeaders.Date; | ||
if (lastModified <= ifUnmodifiedSince) | ||
DateTimeOffset modified; | ||
if (!HttpHeaderParsingHelpers.TryParseHeaderDate(cachedResponseHeaders[HeaderNames.LastModified], out modified) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have this fixed in my clean up commit but just to note, both sides of the &&
operator are evaluated regardless of whether they are true or false. See example here: https://gist.github.com/JunTaoLuo/87f8b2e727c956333fa036f01b3275af
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Um, the first one should be TrueStatement to actually test this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, you're right. And in that case it works as you mentioned. Undid my changes. Must be confusing && with another operator we tested before.
@@ -365,20 +365,18 @@ internal static bool ContentIsNotModified(ResponseCachingContext context) | |||
return true; | |||
} | |||
|
|||
if (!StringValues.IsNullOrEmpty(cachedResponseHeaders[HeaderNames.ETag])) | |||
EntityTagHeaderValue eTag; | |||
if (!StringValues.IsNullOrEmpty(cachedResponseHeaders[HeaderNames.ETag]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous comment got collapsed. But this need to be updated to match the parsing behaviour in Abstractions.
Requires aspnet/HttpAbstractions#738 |
7322d8f
to
3163c85
Compare
b8b6c9e
to
313e047
Compare
313e047
to
c9685cc
Compare
Perf results on ResponseCachingPlaintextCached: |
🐑 🇮🇹 |
c9685cc
to
e01431f
Compare
Still a bit of cleanup needed.
Saved about
791
bytes per request.1627.6
originally836.6
afterBenchmarks has shown an average of
3.386%
increase in RPS