
Well, this is embarrassing. Please disregard my previous email. I should
learn to read the RFC *before* submitting proposals.
--Myles
On Tue, Jan 31, 2012 at 6:37 PM, Myles C. Maxfield wrote: Here are my initial ideas about supporting cookies. Note that I'm using
Chrome for ideas since it's open source. - Network/HTTP/Conduit/Cookies.hs file
- Exporting the following symbols:
- type StuffedCookie = SetCookie
- A regular SetCookie can have Nothing for its Domain and Path
attributes. A StuffedCookie has to have these fields set.
- type CookieJar = [StuffedCookie]
- Chrome's cookie jar is implemented as (the C++ equivalent of)
Map W.Ascii StuffedCookie. The key is the "eTLD+1" of the domain, so
lookups for all cookies for a given domain are fast.
- I think I'll stay with just a list of StuffedCookies just to
keep it simple. Perhaps a later revision can implement the faster map.
- getRelevantCookies :: Request m -> CookieJar -> UTCTime ->
(CookieJar, Cookies)
- Gets all the cookies from the cookie jar that should be set
for the given Request.
- The time argument is whatever "now" is (it's pulled out of the
function so the function can remain pure and easily testable)
- The function will also remove expired cookies from the cookie
jar (given what "now" is) and return the filtered cookie jar
- putRelevantCookies :: Request m -> CookieJar -> [StuffedCookie]
-> CookieJar
- Insert cookies from a server response into the cookie jar.
- The first argument is only used for checking to see which
cookies are valid (which cookies match the requested domain, etc, so
site1.com can't set a cookie for site2.com)
- stuffCookie :: Request m -> SetCookie -> StuffedCookie
- If the SetCookie's fields are Nothing, fill them in given the
Request from which it originated
- getCookies :: Response a -> ([SetCookie], Response a)
- Pull cookies out of a server response. Return the response
with the Set-Cookie headers filtered out
- putCookies :: Request a -> Cookies -> Request a
- A wrapper around renderCookies. Inserts some cookies into a
request.
- Doesn't overwrite cookies that are already set in the request
- These functions will be exported from Network.HTTP.Conduit as
well, so callers can use them to re-implement redirection chains
- I won't implement a cookie filtering function (like what
Network.Browser has)
- If you want to have arbitrary handling of cookies, re-implement
redirection following. It's not very difficult if you use the API provided,
and the 'http' function is open source so you can use that as a reference.
- I will implement the functions according to RFC 6265
- I will also need to write the following functions. Should they also
be exported?
- canonicalizeDomain :: W.Ascii -> W.Ascii
- turns "..a.b.c..d.com..." to "a.b.c.d.com"
- Technically necessary for domain matching (Chrome does it)
- Perhaps unnecessary for a first pass? Perhaps we can trust
users for now?
- domainMatches :: W.Ascii -> W.Ascii -> Maybe W.Ascii
- Does the first domain match against the second domain?
- If so, return the prefix of the first that isn't in the second
- pathMatches :: W.Ascii -> W.Ascii -> Bool
- Do the paths match?
- In order to implement domain matching, I have to have knowledge
of the Public Suffix Listhttp://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_name... so
I know that sub1.sub2.pvt.k12.wy.us can set a cookie for
sub2.pvt.k12.wy.us but not for k12.wy.us (because pvt.k12.wy.us is a
"suffix"). There are a variety of ways to implement this.
- As far as I can tell, Chrome does it by using a script (which a
human periodically runs) which parses the list at creates a .cc file that
is included in the build.
- I might be wrong about the execution of the script; it might
be a build step. If it is a build step, however, it is suspicious that a
build target would try to download a file...
- Any more elegant ideas? Feedback on any/all of the above would be very helpful before I go off
into the weeds on this project. Thanks,
Myles C. Maxfield On Sat, Jan 28, 2012 at 8:17 PM, Michael Snoyman Thanks, looks great! I've merged it into the Github tree. On Sat, Jan 28, 2012 at 8:36 PM, Myles C. Maxfield
Ah, yes, you're completely right. I completely agree that moving the
function into the Maybe monad increases readability. This kind of
function
is what the Maybe monad was designed for. Here is a revised patch. On Sat, Jan 28, 2012 at 8:28 AM, Michael Snoyman On Sat, Jan 28, 2012 at 1:20 AM, Myles C. Maxfield
the fromJust should never fail, beceause of the guard statement: | 300 <= code && code < 400 && isJust l'' && isJust l' = Just $ req Because of the order of the && operators, it will only evaluate fromJust after it makes sure that the argument isJust. That function in
particular
shouldn't throw any exceptions - it should only return Nothing. Knowing that, I don't quite think I understand what your concern is.
Can
you
elaborate? You're right, but I had to squint really hard to prove to myself that
you're right. That's the kind of code that could easily be broken in
future updates by an unwitting maintainer (e.g., me). To protect the
world from me, I'd prefer if the code didn't have the fromJust. This
might be a good place to leverage the Monad instance of Maybe. Michael