Skip to content
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

Change HeaderMap extractor to clone the headers #698

Merged
merged 9 commits into from
Jan 11, 2022

Conversation

davidpdrsn
Copy link
Member

@davidpdrsn davidpdrsn commented Jan 11, 2022

We often hear from users who are hit by Headers taken by other extractor errors because they used HeaderMap as an extractor combined with Form<T>, or some other extractor that happens to use the headers.

The reason RequestParts::take_headers exists in the first place is for performance reasons. But I think I've realized that it was probably a mistake because it causes issues like this. So I think we should change it!

This PR removes RequestParts::take_headers and changes the HeaderMap extractor to clone the headers. We could also consider other workarounds such as recording which extractor removed and which requires the headers, but I think this solution is the least surprising, and worth the small perf hit.

If someone is really concerned about cloning the headers you can still write an extractor that does request_parts.headers_mut().take() which would leave an empty HeaderMap in place. I don't think axum should provide such an extractor though --- then the problem is just gonna stay.

This PR doesn't touch "extensions already taken by another extractor" which is basically the same problem. I'm not very concerned about that since it feels more niche but probably worth looking into as well.

TODO

  • Changelog
  • Document the cloning

Copy link
Member

@jplatte jplatte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the various code simplifications! I think we should be consistent and do the same thing for extensions, as well as adding some warnings about the mostly-unnecessary cloning (of headers you're not interested in) to the affected FromRequest impls.

axum/src/docs/extract.md Outdated Show resolved Hide resolved
axum-core/CHANGELOG.md Outdated Show resolved Hide resolved
@davidpdrsn davidpdrsn merged commit 5beab52 into axum-next Jan 11, 2022
@davidpdrsn davidpdrsn deleted the clone-headers-by-default branch January 11, 2022 19:39
@SabrinaJewson
Copy link
Contributor

Would there be the possibility of having RequestParts use an Arc<HeaderMap>? Then the cloning would become cheap. I don't know how useful it is to be able to mutate headers during extraction.

@davidpdrsn
Copy link
Member Author

Hm not sure if it's worth it since it's only the HeaderMap extractor that got more expensive. If you don't use that things should work the same.

@rich-murphey
Copy link

I believe most session implementations would depend on cookies or JWT obtained from the HeaderMap, and rarely if ever mutate the headers. As it stands, would this make every API call that uses sessions more expensive?

@davidpdrsn
Copy link
Member Author

As it stands, would this make every API call that uses sessions more expensive?

Like I just wrote, no it won't.

Only using HeaderMap as an extractor is more expensive.

If you need a single header for a handler you should use TypedHeader, or write your own extractor if the headers crate doesn't have the header you need.

If you need a header in your own extractor you just call request_parts.headers() which returns &HeaderMap and is free.

@rich-murphey
Copy link

Thanks for the clarification!

davidpdrsn added a commit that referenced this pull request Jan 23, 2022
* Change `HeaderMap` extractor to clone the headers

* fix docs

* changelog

* inline variable

* also add changelog item to axum

* don't list types from axum in axum-core's changelog

* document that `HeaderMap::from_request` clones the headers

* fix typo

* a few more typos
davidpdrsn added a commit that referenced this pull request Jan 23, 2022
* Change `HeaderMap` extractor to clone the headers

* fix docs

* changelog

* inline variable

* also add changelog item to axum

* don't list types from axum in axum-core's changelog

* document that `HeaderMap::from_request` clones the headers

* fix typo

* a few more typos
davidpdrsn added a commit that referenced this pull request Feb 22, 2022
Since #698 this section about
`HeaderMap` removing the headers from the request is no longer true.
davidpdrsn added a commit that referenced this pull request Feb 22, 2022
Since #698 this section about
`HeaderMap` removing the headers from the request is no longer true.
davidpdrsn added a commit that referenced this pull request Feb 22, 2022
Since #698 this section about
`HeaderMap` removing the headers from the request is no longer true.
davidpdrsn added a commit that referenced this pull request Feb 24, 2022
Since #698 this section about
`HeaderMap` removing the headers from the request is no longer true.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants