-
-
Notifications
You must be signed in to change notification settings - Fork 414
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
Basic support for HTTP conditional requests (RFC 7232) #811
Comments
Let's break the problem, what kind of ETag -> Handler (Either NonModified a) and we'd like API type to look like IfNoneMatch :> Get '[JSON] a That's a bit related to #732 where we want to be able to specify which error-codes the endpoint may result into, there we need to change a If some sees the great plan how to combines these, let's do it. |
I'd like to start thinking about how we'd write the handler: on a high-level view, the handler needs to be able to receive optional conditional preconditions; for my concrete use-case I need to support the following cases:
As the conditional precondition logic is a bit subtle to implement, I'd claim that the logic should be provided by a combinator or servant, rather than needing each Handler to have to reinvent it. Here's one way to model ETags: data ETagStrength = ETagStrong | ETagWeak
data ETag = ETag !ETagStrength !Text
data ETagSet = ETagAny {- `*` -} | ETagSome (NonEmpty ETag)
data Precond = Precond
{ precondIfMatch :: Maybe ETagSet
, precondIfNoneMatch :: Maybe ETagSet
} One way to model the :: Precond -> Handler (Either NonModified (ETag, a)) or instead move the logic outside, and instead do something like (as more or less suggested by @jkarni) :: Handler ([ETag], ETag -> Handler a) Where the handler returns the list of "current" ETags for a resource in order of preference (NB: these can be more than one!), and the outer logic then takes care to select one of those tags; and then either replies a 304 or instead carries on calling the nested |
Why is there a list of ETags rather than a single one? One thought is to do something like data HasETag = HasETag | NoETag
type Handler = Handler' 'NoETag
data Handler' (etag :: HasETag) a = ...
setETag :: ETag -> Handler 'HasETag ()
setETag = ... The semantics of (Possibly |
Well, because that's what the HTTP specification supports since HTTP/1.1; each resource can have more than one "current" ETag. This is particularly important for the |
An important feature of HTTP are conditional requests as specified in RFC7232.
Specifically, I'm interested in the Etag based pre-conditions, of which the most popular one IMO is the a)
if-match
and b)if-none-match
, as these allow respectively, for a) for implementing simple transactional semantics to avoid lost-updates (i.e. update/modify/delete a resource only if its state expressed as Etag matches what the client assumes it to be in), as well as b) optimise service calls to only fetch data if there's new data to fetch, especially for services returning back 100KiBs or more worth of data this is significant, as well as for simplifying client-side logic which can choose to trust the Etag to decide equality.Last year I mentioned this on
#servant
, and then I forgot about it again. So this time I'm filling an issue :-)Btw, here's what @jkarni told me about a year ago:
The text was updated successfully, but these errors were encountered: