Closed Vidarls closed 8 years ago
Note: I am very much not an expert of the http protocol, so I really want Freya to push me into the pit-of-success (:tm:)
You're right, machine at the moment doesn't really help much with cache control. It could, now that I think about it (at least in terms of helping with the response handler, taking that away from the end user). I'm not sure there's time to get that in to 3.0, although I'll take a look. If not, it'll definitely be in 4.0.
The same, I think, applies to the other cache control items - I think I need to have a session with the RFCs and work out whether they make sense as part of the processing flow. It may well be that it's something Freya can bring to the party which is quite new.
Your wish in terms of pit-of-success is absolutely reasonable, and very much what I want with Freya - these are both really good points, and I'm going to make sure Freya does as much as possible to help, whether in the very soon 3.0 release, or the "not quite as soon, but definitely not too long" 4.0 release.
One dependency needed is fuller support for those headers in Arachne as well, so I'll make sure that ends up on the slate. At the moment, you could probably make sure some of those headers are set directly from the handler in machine, but it isn't as nice as it could/should be.
I can set the headers directly in the handler, and the machine would respect those?
Can you give a pointer to how to do this with lenses?
It shouldn't touch them from that point onward. The machine isn't going to use them in any way as it stands, it just lets you take as much control in the handler as you'd like. So although it isn't as helpful as I'd like, it should "keep out of the way"!
I'll try and put a quick example together shortly :smile:
I think - and this is effectively from memory/looking at code - that cache control would look something like this (this assumes Freya 2.x)
let handler =
freya {
do! Freya.Lens.setPartial Response.Headers.CacheControl_ (CacheControl [ NoStore; Private ])
... }
That uses the CacheControl type from Arachne (https://github.com/freya-fs/arachne/blob/master/src/Arachne.Http/Http.fs#L1626) with the lens to the appropriate response header. If you were to add that to your handlers (and add more directives to cache control if relevant) then the responses should include the appropriate header. I haven't tried it though, so let me know if it doesn't!
For reference also - in 3.x+ the same would look like this:
let handler =
freya {
do! Freya.Optic.set Response.Headers.cacheControl_ (Some (CacheControl [ NoStore; Private ]))
... }
while waiting I did this to try to put all caching concerns in the same place:
(note: I want to enable client side caching, as performance is a concern, but I do not want intermediary caching as security is also a concern. (And yes, all is 100% https))
let setPrivateCache maxAge sMaxAge eTag =
freya {
do! (Freya.Lens.setPartial Freya.Lenses.Http.Lenses.Response.Headers.CacheControl_
(CacheControl(
[CacheDirective.Private;
CacheDirective.SMaxAge(System.TimeSpan.FromSeconds(sMaxAge));
CacheDirective.MaxAge(System.TimeSpan.FromSeconds(maxAge))])))
do! (Freya.Lens.setPartial Freya.Lenses.Http.Lenses.Response.Headers.Expires_
(Expires(System.DateTime.UtcNow.AddSeconds(maxAge))))
do! (Freya.Lens.setPartial Freya.Lenses.Http.Lenses.Response.Headers.ETag_
(ETag(Weak(eTag))))
}
Looks sensible?
edit: Replaced |> ignore
with do!
Yes that should probably do it. If you open Freya.Lenses.Http
and Arachne.Http
then you should be able to shorten a lot of those long type references as well. Response.Headers.CacheControl_
is what I'd normally expect, etc.
Those Freya.Lens.setPartial
calls should also use do!
notation, so do! Freya.Lens.setPartial ...
etc. with no need for |> ignore
.
Hope that's helpful!
Will setting expired
and ETag
out of band like this disturb how the machine responds to requests?
:( Yes it will, I get 304 not modified always when e-tag is not set in machine, and I can not set e-tag in machine due to #176.
Is the prerelease of v3 somewhat stable?
No it shouldn't, the only change when not telling the machine about etags and last modified is that the machine won't do any precondition checking (so it won't be able to handle the conditional request logic and will effectively skip it). However, that may not be a big problem, and anything you set in the handler should have no impact - invoking the handler is basically the last thing in a machine.
Hmmm. That's interesting, it definitely looks like there's a bug with etags. That certainly needs fixing. v3 is relatively stable (it has various changes and so will result in quite a lot of deprecation warnings, and a few breaking changes to functions, but they're not too surprising). Aside from this current question around lastModified/etags there's nothing major I'm aware of stopping the release, it's purely documentation.
Just for clarity: This bug is fixed in v3?
if not: Is there any way to to a manual handler for precondition checking?
Also on caching:
Any time a request is received with headers that may cause different representations, the vary
header field should be used to ensure any caches does not cache the wrong representation.
This could be media type, language, but also authentication.
Even when cache-control :private
is used, multiple users on a shared computer could get a representation from cache that is not intended for them if the logged in user changes. (As cache is keyed by url only by default.
ref: https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44
and: https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6
If it isn't already fixed in v3, it will be over the next couple of days! A manual handler would be "kind of" possible, but is going to happen at a less than optimal point in the process (you'd have to write it in to the handler, with all of the logic and responses going there, which isn't great).
I'll have a look in to the variation specs (RFCs 7230-7235 are the current ones on this) and make sure we're doing what we can in terms of helping with these scenarios.
And it is not super straight forward, more goodness from the RFC:
https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4
I'll try to use the pre-release.
Currently doing a rather big rewrite / refactoring from Proof-of-concept-that-is-currently-in-production to something we can build a production grade solution from, not expecting to put any of this into production until some time next week.
A little note on my project: Biggest challenge I believe will be creating a stack I can easily teach my C# centric team, and making sure the resulting code is fast.
Yes, variability isn't completely straight forward at the best of times! I'm going to try and take some time over the next couple of days to make sure that 3.0 is free of known issues and ready to be released at the beginning of next week.
So, that was the fastest major version migration I've done ever I think :+1: Clear compiler errors and obsolete warnings
But bug is still present in v3:
'Unable to cast object of type myMachine@81-13' to type 'Microsoft.FSharp.Core.FSharpFunc
2[Freya.Core.Types+FreyaState,Microsoft.FSharp.Control.FSharpAsync
1[System.Tuple`2[Arachne.Http+EntityTag,Freya.Core.Types+FreyaState]]]'.
Right, I'll see if I can repro and get a fix done!
Quick Q on v3, are there any notes anywhere on subtle breaking changes?
It looks to me like the specs param of handleOk is now removed, may cause some strangeness?
Current caching solution, works as intended:
let handleWithPrivateCache maxAge eTag handler =
let cachingHandler handler =
freya {
do! (Freya.Optic.set Response.Headers.cacheControl_
(Some(CacheControl(
[CacheDirective.Private;
CacheDirective.MaxAge(System.TimeSpan.FromSeconds(maxAge))]))))
do! (Freya.Optic.set Response.Headers.vary_ (Some("Accept Authorization")))
return! handler
}
freyaMachine {
expires (DateTime.UtcNow.AddSeconds(maxAge))
//TODO, etag proper when bug fixed
//etag (ETag(Weak(eTag)))
handleOk (cachingHandler handler)
}
usage:
freyaMachine {
including
(handleWithPrivateCache 30.0 (System.Guid.NewGuid().ToString())
myHandler)}
Composition is fun :-)
I have a solution now that mostly works (some OPTIONS issues #186 ) and a bit of ceremony required, but it works. Feel free to close , and maybe open a new issue for a more thorough solution to all things caching if you feel that is wanted.
I'll close this one and open a new issue around the possibility of more declarative setting of cache information.
Currently working on getting my api to play nicely with caching clients, and I struggle a bit.
Cache-control
I can't find any trace of cache control directives using FreyaMachine.
When handling an authorized request, at least
cache-control: private
header should be present.Also for a close-to-metal stack like Freya it would seems natural to provide a lot more control here, (of course with sensible default behaviour)
Expiry
Freya only seems to support the
expired
timestamp method, none of the more modern lifetime / shared lifetime, as cache-control does not seem to be implemented.