-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
cookie jar implementation #14
Comments
Thanks for writing this up, it's awesome! I have actually wanted to write a new cookie crate for hyper for a while now, since I imagine some of my opinions at this point would be too divisive to get into cookie-rs. For example, I would like to make the Does doing that plus the Cookie Jar / Session stuff you've done seem like a good idea? |
Say you were to make a new cookie manager from scratch, how difficult would it be? I recently came across your blog post and thought I'd try it out. So far I'm really liking the user friendly API you've built up, but the thing that's preventing me from writing any non-trivial web client stuff (as opposed to, say, Python's Requests) is the lack of cookie support. I'd be interested in sending a pull request or two, but I'm not sure how you'd want to implement cookies and integrate it with the rest of the project. What have you done towards cookies so far? |
I was hoping to push on this a bit this weekend; I'm hoping my existing code (which implements the linked rfc) can quickly be adapted for something at least functional that can be worked on for efficiency later w/o breaking back compat. |
After looking at how hyper has done cookies, @seanmonstar's idea of storing a single string and then getting slices of that using some accessor function when you retrieve a particular field sounds like the way to go. It's not really adding that much complexity and makes things a lot nicer. How did your cookie jar work? Isn't it essentially just a trie/hashmap under the hood? |
Apologies for radio silence on this; I only recently had some time to revisit. It's certainly a WIP, but in the interest of getting something rolling I've made user_agent available (but not on crates.io), updated to use reqwest as a client. Revisiting the code, I think it makes sense for this to exist as its own crate I'd be interested in feedback in terms of API, ergonomics, and as mentioned much in here may not be particularly rust-y as it was originally written a while ago. In particular, the set of traits exposed for clients to be able to be used in In terms of writing a new/reqwest/hyper-specific Cookie: |
I do agree that this could be a separate crate, I have no problem with reqwest depending on it. Looking at user_agent: I like a lot of what I see, and I see that you've thought through a lot of the concepts of storing and sending cookies, fantastic! I'd probably handle the
if let Some(cookies) = self.jar.get(&url) {
headers.set(Cookie(cookies));
}
let res = do_request(args)?;
if let Some(cookies) = res.headers.get::<SetCookie>() {
self.jar.put(res.url, cookies);
}
I'll hop over to the cooky issue to talk about that specifically. Exciting! |
Thanks for the comments. I agree that the current In regards to how you'd like to see functionality implemented within reqwest: You mention not exposing the store publicly, and that effectively each Either way, it makes sense to move the conversion logic around hyper's headers out of the |
@seanmonstar not sure if you follow the cookie-rs crate directly, but there is an interesting PR open which may make that crate more attractive for use as hyper/reqwest Cookie impl as opposed to rolling some independent |
@pfernie Saw your post from the PR. Is there something I missed in the PR that you proposed as an idea earlier (I couldn't find where you shared your ideas)? Would love to hear it if so! |
@SergioBenitez No, I actually think your PR is pretty in line with most of the ideas I had been entertaining! I haven't had a chance to dig in deeply, though. I have an un-published (on crates.io) implementation of a cookie jar which handles path/domain matching, etc. here. That crate does introduce some more specific types ( As you can tell from the age of this issue, I've been letting it get a little stagnant, but have been hoping to get back to it soon to implement ideas discussed here, add support for public suffix checking, etc. When I do so, I'll have a better idea if there are any enhancements that would make sense to push into the |
@pfernie it looks really promising! I'd also want 1 other change, which is to rip out the signing |
@seanmonstar I'll be looking into just that in the near future. |
@seanmonstar I've opened a PR that overhauls |
To update this ticket, @pfernie and I have been getting https://github.com/pfernie/user_agent back to compiling on the latest cookie and reqwest. This should help make it easier to get it feature complete, or merge with another project. |
@margolisj unfortunately I haven't had time to actively work on this. Things are at least in a building state, but it really needs some thought on where to go forward, especially in terms of how it should work w/ reqwest. In particular, I saw #155 recently; event hooks might be a sensible way for something like |
@seanmonstar Do you see this as something that can be pretty closely ported from the python requests library? I guess if it was possible, I'd really appreciate a short synthesis of how you see this fitting / if anything has changed form your previous opinions posted here. |
@margolisj I haven't really read through that part of requests, so I couldn't comment there. I don't imagine the exposed API would be the same (I wouldn't want someone to have to use My thoughts otherwise have stayed the same. If you'd like, I can find the cookie spec and list out the key points about safely storing and sending cookies to the correct urls. |
Ah very important. requests does provide another object that wraps around a Cleitn Any preference for triggering "session" mode then? Just add it as a param to the builder? The cookie spec and/or work seems to be pretty far along in their user_agent and I'd mostly like to piggy back off that. |
Yes, I'd just have a |
The existing It is alluded to in this issue, but as a NB: I threw in public suffix support via the |
@seanmonstar on second thought what I was was short sighted; for sure if you'd like to look through the cookie spec and list any key points I'm sure that would help towards getting it merged to your liking. @pfernie you meant the cookie jar part as something talk could be easily used outside of reqwest, correct? |
@margolisj correct; I mean if |
I'd like to add my two cents in favor of this, with a scenario I ran into the other night. I was making a post request to a website using My opinion on the API I'd like to see is that a |
@SergioBenitez the existence of a Any signing performed with cookies should be done on the server side on the values stored in the cookies, not on the cookies themselves and certainly not within the cookie jar on the client. Secondly, as it stands, the cookies in the As far as I can tell, there is much of the specification of cookies as far as an HTTP client is concerned missing from the Overall, the design of the |
It sounds like you've either gravely misunderstood what those types do or are pitting them against a scenario where their utility is dubious. The idea is that, on the server-side, you hold a secret key and can use it to sign/encrypt cookies; the resulting ciphertext is sent to the client. The client sends these cookies back at some point in the future. You can then cryptographically assert the confidentiality, integrity, and/or authenticity of the cookie's values. These properties are significantly stronger than memory safety. Perhaps you believe that
Great! The
I'm not sure why they wouldn't be useful for building/reading/parsing cookies on the client-side? What's more, I've used the
This feels baseless. Nothing that you've said demonstrates this. I'm also not sure which protocol you're referring to; cookies aren't a protocol. |
Glad to see this issue getting some attention; I regret that I never had the time to push on this myself. @SergioBenitez , I believe by "protocol" other users in this thread are referring to the behavior of cookie handling as described by RFC6265, which my @xurtis, I have not had a chance to review your crate, but I will say a reason I never pushed on this was I was unclear if the additional RFC functionality was appropriate for the I never dug deep, but I considered that perhaps adding functionality to All that being said, I will re-link the repo with my old implementation. The RFC is not that complicated, so perhaps it is simpler to reimplement, but it does contain implementations of most (all?) of the RFC, as well as a set of tests. And being both code from when I was first learning rust, and some niggles of the ecosystem back then (mismatched Obviously, I have not had time to make any useful progress on developing my old code into something suitable as a crate, but I continue to keep an eye on this issue, and would be interested to contribute to whatever effort is born of the discussion. |
@pfernie : I see you worked on your user_agent crate, but it's not on crates.io, so I guess, you don't consider ready for public usage, however there are some reqwest integration already there. Based on a cursory code reading, it seems pretty complete. Do you plan to finis and publish your work? It would be nice to have a working, cookie store solution for client side. |
@gzsombor user_agent is now published on crates.io. I tidied up some of my bigger objections with the crate; I split out the actual RFC6265 implementation into a separate cookie_store crate. I did not publish as
|
@pfernie that's fantastic! I'd probably as a next step try to use |
Since #480 has been merged, a little update: A cookie store is now supported (in master) and can be enabled with For now, the cookie store is private though and can not be accessed for either populating it manually or storing/saving it somewhere. This is of course desired and should be added, it just requires fleshing out good method signatures that won't need a breaking change. |
@theduke what does
mean in the 0.9.x documentation? Is it outdated? Shouldn't be enough to just do the thing below to have the cookie stored inside the client? let client = reqwest::Client::builder()
.cookie_store(true)
.build()
.unwrap();
client.get(url.as_str()).send() |
Yes that is outdated, where in the code is it? |
In the Readme.MD, 0.9.x branch.
… Il giorno 16 ott 2019, alle ore 16:08, theduke ***@***.***> ha scritto:
Yes that is outdated, where in the code is it?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub <#14?email_source=notifications&email_token=ACOOZ3C3MRAYDVQAYAUY63DQO4N65A5CNFSM4CWND4N2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEBMT32Y#issuecomment-542719467>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/ACOOZ3H7HWB4TXISKBXV3YDQO4N65ANCNFSM4CWND4NQ>.
|
remove println and use log
What else is required to close this issue ? |
I just stumbled across this. I would like to see an explicit reqwest::Session that works like Python requests.Session(). The Python code handles TLS correctly in that it doesn't create a new TLS session for each request, while reqwest is creating a separate TLS session per request. I haven't found anyway to avoid that. I am running 0.10.4 because our mirror hasn't been updated in a while, so if there is capability in 0.10.6, I cannot currently test it due to mismatched dependencies. I should add that in this case, my client is running on Windows and I am not specifying any certificates, so it is presumably using native-tls. I just tested on Ubuntu using openssl and a root CA and it has the same issue - not surprisingly. |
@crusty-dave, there should also be some way to reset the cookies in the session. Cookies can be cleared with requests.Session in Python, according to https://stackoverflow.com/a/23816320 s = requests.session()
# make a request
s.cookies.clear()
# make another request, with cookies cleared Currently, in 0.10.9, the |
So I get you can start a client with cookie_store enabled. But is there an way to pre-load(before sending the first request) cookies or access the cookie jar/store? For example if I fire up a client like
How can I then access the cookie jar and load cookies I have in a flat file. I'm guessing this feature only enables processing for cookies from responses? So I would need to build the a cookie header manually then use 'default_headers' when building the client? Like this
If that's the case, I'd love to get better support for preloading cookies. As in my use case I have a cookie flat file in this format https://curl.se/docs/http-cookies.html. While I can parse the file format easy enough, it would be great to get functions in reqwest/cookies to be able to insert the seven(correction eight, I forgot #Httponly, per https://stackoverflow.com/questions/11261069/curl-cookiejar-line-commented-out-with-httponly) attributes for cookie(s) easily. Currently unless I'm mistaken the the insert function for HeaderMap only takes KV, meaning I'd have to do a bit of wrangling which could be made much easier. |
@jlpetz I wouldn't strongly recommend it, but I will note that there is the user_agent crate, which was the source of my original implementation for That aside, after lurk-watching this issue for quite a while, I'd be interested in re-visiting the design here and perhaps bring some of the functionality asked for in this issue. Generally, my recollection is that a goal was to avoid or minimize introducing further external dependencies in the public API. I believe I explored a similar idea previously, but nevertheless I put together a rough idea of perhaps decreasing some of In regards to the current API, this is a breaking change, as This API facilitates usage such as: use cookie_store::CookieStore;
use eyre::Result;
#[tokio::main]
async fn main() -> Result<()> {
let cookie_store = CookieStore::default(); // or use `load` to deserialize an existing session off disk
let reqwest = reqwest::Client::builder()
.cookie_store(Some(cookie_store))
.build()?;
reqwest.with_cookie_store_as(|cs: Option<&CookieStore>| {
dbg!(cs);
});
// let's get us some cookies
reqwest.get("https://google.com").send().await?;
reqwest.with_cookie_store_as(|cs: Option<&CookieStore>| {
dbg!(cs);
});
reqwest.with_cookie_store_as_mut(|cs: Option<&mut CookieStore>| {
if let Some(cs) = cs { // Only `Some` if a cookie store was provided, and it can downcast to the requested type
cs.clear();
}
});
reqwest.with_cookie_store_as(|cs: Option<&CookieStore>| {
dbg!(cs);
// the CookieStore could be serialized back to disk here
});
Ok(())
} Producing:
|
@jlpetz, wouldn't that fail if the server unsets cookies, and they are still included in the default headers? More important than sessions would be read/write access to the cookie store (either in |
Probably, but I'm expecting this not to happen in my scenario. These are auth cookies I'm loading that are needed on every request I'm making from the client to the servers I will be interacting with. But it's painful as I'll need to handle expiry and such myself. Luckily I won't be hitting sites that don;t require these cookies, or I'd have to do the addition host/path checking etc. I haven't tested it yet but I'll be interested to see if the default_header cookie I initially provide gets merged correctly with any cookies the client tries to add to the cookie_store from server set_cookie responses, or if this confuses things(ie. cookie_store is out of sync and overwrites it).
Exactly, from the reply from pfernie it sounds like this isn't quite possible yet. But yes read/write access to the cookie store for saving/restoring cookies is what I'm trying to accomplish(mostly restoring, as I'm getting the cookies from an external U2F process). @pfernie, Thanks for the detailed response. Given I'm new to rust I think I'll take your advice and avoid the 'lackluster recommendation,', since that seems like a quite complex path for my skill level. I hoping that potentially hacking default headers might be enough for my use case (though likely less ideal/robust). On this comment though
Would we need to break the API to do this? I don't mind not being able to provide a preloaded cookie store and keeping the bool. But after creating the client could the cookie_store not be exposed by the client for read/write access? ie. the cookie store has insert, get, list, delete cookie functions. Surely these would already exist and are just not exposed, given a response/request can already do most of these things. Going a step further to restore/save functions would allow re-use of cookies between multiple clients and persistence to a file(like the one I'm loading from). Even if you wanted to be able to create the Client with a preloaded cookie_store, could we not provide that as a separate option to the currently available cookie_store bool, to avoid a breaking API change. ie. loaded_cookie_store or cookie_store_object. In this scenario, not providing the cookie_store_object would work as it does today. Then if you provide it, it would operate as below.
or
As I mentioned above, I'm new to rust, so more than half of what you said is likely over my head. Perhaps I'm suggesting something not workable given the goal(above) and rusts ownership (apologies if I am). I just thought I would ask here in case I was missing a way to do this which already exists and if it doesn't to hopefully get it clarified as a feature request. Sounds like it would be feature request from what you wrote. Thanks again for the fast and very detailed response. |
@jlpetz Apologies, I should have been more clear in my addressing of my comments. The first section about But, as to your comments: yes, it could be done without an API break. I was noting it more as a "I happened to do this for this implementation"; I wanted feedback on the internal approach before submitting any actual PR. Your comments sort of mention in passing the idea of a "read-only CookieStore" (don't gather cookies during client lifetime, but use the cookies initially provided). That isn't a usage I'd consider/would need; it's a bit tangential to the discussion, but would that be desirable functionality? |
I'd already been thinking that we could perhaps just define a On a meta note, I wonder if perhaps we should close this specific issue, since there is now cookie store support in reqwest, and open a new issue around the new functionality. |
Closing this issue in favor of its continuation in #1104 |
No, I don't really need a READ-ONLY cookie_store for my current usage. I was more mentioning this in case that makes doing a non breaking change easier or re-using existing codebase possible. ie. If the cookie_store_object was provided and the existing cookie_store bool was kept, what would the behaviour be? For me either A or B (below) are fine as I would always be using '1' and not '2'.
Having said that, with private browser modes, privacy cookie blocking and all that such being the rage. Perhaps someone would have a use case for wanting to provide cookies they choose but not accept (or selectively accept) cookies from responses. |
About a year ago I wrote a
user_agent
crate (not published to crates.io) for an internal project, which provides the concept of aSession
wrapping aClient
and does cookie handling per RFC6265. At the time, the only client I targeted washyper::Client
. I'd be interested in updating/adapting the code toreqwest
, but wanted to touch base on any existing plans/work here.First off, the library suffers some warts:
Cookie
class, which wraps/extends the existing cookie crate (providing aFrom
implementation to handle conversion). This was to introduce some more domain objects (CookiePath
,CookieDomain
, etc.) to enforce/aid the implementation of some RFC6265 guidelines.hyper
was on a different (0.1?) version ofcookie-rs
, whereas I was developing against a more recent version (0.2+?), meaning the library goes through some gyrations to "convert" between the two versions by stringifying-then-parsing cookies.Some of these issues can be cleaned up over time, but in terms of bigger picture I have some questions/concerns:
Cookie
seems un-necessary; submitting PRs againstcookie-rs
to bring them in line would make sense...cookie-rs
already provides aCookieJar
, implementing hierarchical jars and jar signing. In my crate,user_agent::CookieStore
is more concerned with storing/expiring/updating cookies per RFC6265, and handles storing cookies and setting outgoing cookie headers based on URLs visited during theSession
, as well as serializing the current session (to .json currently, but really to anythingserde
will serialize to) so they can be saved/restored. So, they are in some sense orthogonal currently, but feels as though they could be merged as well.Session
, although the only concrete impl isHyperSession
. This was done with the vain hope of making a library for "all the webs" (curl clients, etc.), but the realities of needing to get my project done means this was (IMO) sub-optimally implemented. I'm not sure if it makes more sense to ditch the currently not very ergonomic abstraction and focus on implementing specifically towardsreqwest
, or to fix/maintain the abstraction and publish the crate separately asuser_agent
and just implement aReqwestSession
here. At a high level, theSession
implementation is mainly concerned with defining how to get/set cookies into/out of Request/Response objects.Obviously some questions would be more easily answered with some code to review; I just wanted to get a feel for the lay of the land before updating code to be suitable for a PR.
The text was updated successfully, but these errors were encountered: