-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Consider using http::HeaderMap
internally
#1498
Comments
|
I'm not sure I follow. As far as I can tell, there is no way to get |
Sorry - I conflated that point with the situation in both |
I've tried implementing a new HeaderMap which wraps As for the issues raised above:
True, but this is slightly mitigated by taking ownership of Hyper's headers as suggested above. For header names, it's not much waste anyway, since the internal representation is either an owned string (which will be initialized just once during processing), or a "known" header, which is represented efficiently.
Exposing the actual HeaderValues would make these tests slightly more tolerable, and push the eventual bytes->string handling onto the user. The only other way to deal with this is to ensure that only values stored in the map are always valid UTF-8, but that's not spec compliant.
We have those issues already on Finally, how should we insert and lookups by |
Thanks for taking a look at this!
Are you saying you've done something like: pub struct HeaderMap<'h> {
hyper: http::HeaderMap
headers: IndexMap<Uncased<'h>, Vec<Cow<'h, str>>>
} Or something else? If you haven't done the former, this is the approach that I think would be a good fit in the interim.
I don't think the user should have to deal with such a common task themselves. At worst, we should have two methods, one which returns bytes and the other a string, caching the result of the latter for later retrieval.
The issue is with adding new headers that borrow from the request. Unless the approach I've suggested above is taken, the issue isn't at all mitigated.
I think a header fn get(&self name: &str) -> impl Iterator<Item = &str> {
self.headers.get(name)
.or_else(self.hyper.get(name))
.filter_map(|h| h.as_str())
} That is, try to get from
That's a good point, though ideally this wouldn't be the case. In all, I really think |
You're welcome. I really like Rocket's approach, and I'd like to help it towards a nice 0.5 release on stable Rust.
No, I have just one map, the
Using two maps would help with the duplication of incoming request headers, but not the overhead around outgoing response headers (nor the problem with converting values and header names to and from strings).
I don't get this. Would you just "panic" on illegal header names or values, or "sanitize" them by dropping the offending bytes? Perhaps I am focusing too much on edge cases? ;-)
But the results are eventually allocated and put into http::HeaderMap before being returned to hyper (in
Yes, that's probably the right place to address both the case-preservation and the borrowing issue, if fixable. Anyway, I've thrown my work in progress in a branch on my fork: jespersm@fcb5981 (note: it has multiple issues, TODOs, and the tests don't compile.) |
Excellent points regarding allocations! I would be in favor of a change to use
Perhaps the correct surface API to change here is that of |
Right. This does require adding lifetimes for the borrowed values inside the It also requires either adding silent value dropping, or panic'ing on bad values, when getting and setting values.
Exactly. I have a PoC of this in my branch already (for let vals: Vec<_> = req.headers().get(header_name!("My-Header")).collect(); (Changing |
I'm suggesting "lying": keep Rocket's API (except for
Very cool! |
Noted. There's also the question of "bad" values, such as newlines, in a header value. Today, the Rocket accepts them but "silently" crashes when pivoting the response. We have the option of either silently sanitizing the value, or dropping it altogether on insertion, if it contains illegal ASCII values. This should probably be logged in either case, so the unsuspecting developer can realize their mistake. By the way, I've posted a draft PR for |
Just a note: if/when rust-lang/rust#51999 lands, we can make |
At present, Rocket spends significantly more time than necessary converting an
http::HeaderMap
and its internal values into arocket::http::HeaderMap
. More importantly, the conversion is directly in the request processing path, so it effects the performance of every request:https://github.com/SergioBenitez/Rocket/blob/9671115796e42865eaebd020ac27542802027b02/core/lib/src/request/request.rs#L887-L893
Instead of our entirely custom
HeaderMap
type, we should consider leveraging the work inhttp
by using anhttp::HeaderMap
internally, wrapping it in a custom type to preserve our documentation and interoperability with the rest of Rocket's types. Thehttp::HeaderMap
allows customization of the internal value; forhyper
, this ishttp::header::HeaderValue
. Unfortunately, unlike Rocket's header map, this does not allow borrowing header values of a particular lifetime; all values are heap-allocated. The same issue occurs with header names. What's more, because values are stored as bytes inHeaderMap
(a good thing), retrieving them as strings requires a UTF-8 check (a bad thing). The map also does not preserve case which is known to cause issues downstream.In summary, there are at least the following road-blocks to using an
http::HeaderMap
internally:The text was updated successfully, but these errors were encountered: