-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Request CachePolicy isn't applied in HTTP request header #60913
Conversation
Tagging subscribers to this area: @dotnet/ncl |
I tried to do the same processing as in https://referencesource.microsoft.com/#System/net/System/Net/Cache/_Rfc2616CacheValidators.cs (Rfc2616.OnValidateRequest method) |
How does this interact with the static |
Thanks for spotting that I almost missed it ;). In .NET 4.8, there's a class called RequestCacheManager that manages global CachePolicy configuration for HTTP/HTTPS or FTP. The rule is if a DefaultCachePolicy is set, it applies to all HTTP requests unless some specific HTTP request defines its own CachePolicy, then it's overridden by the last. The problem in .NET Core, .NET 5/6 is that there's no RequestCacheManager class so there's no way to apply it by default. In this actual state, DefaultCachePolicy is useless. What do you think @scalablecory : should i go for implementing RequestCacheManager ? |
Judging by the Framework code, something like this (plus relevant tests) seems enough: HttpRequestCachePolicy cachePolicy = request.CachePolicy ?? HttpWebRequest.DefaultCachePolicy ?? WebRequest.DefaultCachePolicy |
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.
thanks. some more comments.
if (request.Headers.CacheControl == null) | ||
{ | ||
request.Headers.CacheControl = new CacheControlHeaderValue(); | ||
} |
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.
CacheControl
will always be null
here. I would:
var cacheControl = new CacheControlHeaderValue();
And accessing request.Headers.CacheControl
over and over will do non-trivial work, so I'd use this local variable in the switch body and then after setting everything:
request.Headers.CacheControl = cacheControl;
private RequestCachePolicy? GetApplicableCachePolicy() | ||
{ | ||
if (CachePolicy != null) | ||
{ | ||
return CachePolicy; | ||
} | ||
else if (IsDefaultCachePolicySet(DefaultCachePolicy)) | ||
{ | ||
return DefaultCachePolicy; | ||
} | ||
else if (IsDefaultCachePolicySet(WebRequest.DefaultCachePolicy)) | ||
{ | ||
return WebRequest.DefaultCachePolicy; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private static bool IsDefaultCachePolicySet(RequestCachePolicy? policy) => policy != null | ||
&& policy.Level != RequestCacheLevel.BypassCache; |
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 doesn't seem correct: if I explicitly want my request to bypass cache, this will end up using the default policy (which might be to cache).
I think correct behavior is to stop at the first non-null policy, and return null if that policy says bypass.
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, can you make sure BypassCache
doesn't need to do anything to the request?
The docs say it bypasses all caches "between client and server", and it's not clear if this needs to set some header related to proxies.
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 doesn't seem correct: if I explicitly want my request to bypass cache, this will end up using the default policy (which might be to cache).
I think correct behavior is to stop at the first non-null policy, and return null if that policy says bypass.
I did it because the DefaultCachePolicy is initialized with BypassCache in WebRequest and HttpWebRequest classes by default. if a user sets WebRequest.DefaultCachePolicy and didn't do it for HttpWebRequest.DefaultCachePolicy we will presume that no cache gonna be applied, which may seem misleading for the user
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 did it because the DefaultCachePolicy is initialized with BypassCache in WebRequest and HttpWebRequest classes by default. if a user sets WebRequest.DefaultCachePolicy and didn't do it for HttpWebRequest.DefaultCachePolicy we will presume that no cache gonna be applied, which may seem misleading for the user
It sounds like we need to update those properties to track "explicitly set" vs "default" state. Pull the first one that is explicitly set.
case RequestCacheLevel.CacheOnly: | ||
request.Headers.CacheControl.OnlyIfCached = true; | ||
break; |
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.
CacheOnly
is intended to throw if it's not in the local cache already. This is different from OnlyIfCached
, which is a directive to the server/proxy.
.NET no longer has a local cache, so this can always throw a WebException
. The text used in Framework is "The request was aborted: The request cache-only policy does not allow a network request and the response is not found in cache." and should be added as a resource string.
It seems that adding test cases for DefaultCachePolicy broke other tests as it is static and tests are not executed sequentially, can I drop the test cases for DefaultCachePolicy ? or there's a way around this problem? |
There's a class |
…tion on side process
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.
looking good, only some minor things.
if (httpRequestCachePolicy.MinFresh > TimeSpan.Zero) | ||
{ | ||
cacheControl = new CacheControlHeaderValue | ||
{ | ||
MinFresh = httpRequestCachePolicy.MinFresh | ||
}; | ||
} | ||
|
||
if (httpRequestCachePolicy.MaxAge != TimeSpan.MaxValue) | ||
{ | ||
cacheControl = new CacheControlHeaderValue | ||
{ | ||
MaxAge = httpRequestCachePolicy.MaxAge | ||
}; | ||
} | ||
|
||
if (httpRequestCachePolicy.MaxStale > TimeSpan.Zero) | ||
{ | ||
cacheControl = new CacheControlHeaderValue | ||
{ | ||
MaxStale = true, | ||
MaxStaleLimit = httpRequestCachePolicy.MaxStale | ||
}; | ||
} |
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.
MaxAge
and MaxStale
will both override the previous settings here. Seems like there should only be one new CacheControlHeaderValue
.
if (policy != null && policy.Level != RequestCacheLevel.BypassCache) | ||
{ | ||
CacheControlHeaderValue? cacheControl = null; | ||
var pragmaHeaders = request.Headers.Pragma; |
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.
We only var
if the right hand side is new T
. This makes sure the code is easy to read outside of an IDE.
{ | ||
return DefaultCachePolicy; | ||
} | ||
else if (WebRequest.DefaultCachePolicy != null) |
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.
else if (WebRequest.DefaultCachePolicy != null) | |
else |
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.
looks good, thanks for the PR!
Fixes #31320