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

http: add HTTP/1.1 case preservation #15619

Merged
merged 14 commits into from
Mar 28, 2021
Merged

http: add HTTP/1.1 case preservation #15619

merged 14 commits into from
Mar 28, 2021

Conversation

mattklein123
Copy link
Member

  1. Add new stateful header formatter extension point
  2. Add preserve case formatter extension

Fixes #14363

Risk Level: Low
Testing: New integration test
Docs Changes: Added
Release Notes: Added
Platform Specific Features: N/A

1) Add new stateful header formatter extension point
2) Add preserve case formatter extension

Fixes #14363

Signed-off-by: Matt Klein <[email protected]>
@repokitteh-read-only
Copy link

CC @envoyproxy/api-shepherds: Your approval is needed for changes made to api/envoy/.
API shepherd assignee is @lizan
CC @envoyproxy/api-watchers: FYI only for changes made to api/envoy/.

🐱

Caused by: #15619 was opened by mattklein123.

see: more, trace.

@mattklein123 mattklein123 assigned jmarantz, snowp and alyssawilk and unassigned lizan Mar 23, 2021
Signed-off-by: Matt Klein <[email protected]>
@mattklein123
Copy link
Member Author

@asraa @alyssawilk @jmarantz @snowp PTAL. I'm pretty happy with how this turned out. It's quite clean and the perf overhead when not in use should be negligible.

Copy link
Contributor

@jmarantz jmarantz left a comment

Choose a reason for hiding this comment

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

Epic! Looks great overall. A few minor comments so far.

api/envoy/config/core/v3/protocol.proto Outdated Show resolved Hide resolved
include/envoy/http/codec.h Outdated Show resolved Hide resolved
include/envoy/http/header_map.h Show resolved Hide resolved
api/envoy/config/core/v3/protocol.proto Outdated Show resolved Hide resolved
include/envoy/http/codec.h Outdated Show resolved Hide resolved
@mattklein123
Copy link
Member Author

@asraa @jmarantz @snowp I think this is ready for another pass.

Copy link
Contributor

@snowp snowp left a comment

Choose a reason for hiding this comment

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

This looks great, just a couple small comments

Copy link
Contributor

@jmarantz jmarantz left a comment

Choose a reason for hiding this comment

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

looks great; just a few nits.

jtway
jtway previously approved these changes Mar 24, 2021
Copy link
Contributor

@jtway jtway left a comment

Choose a reason for hiding this comment

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

This seems great overall. Couple minor comments.

Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

Awesome, I'm sure loads of users are going to love this one.

Two drive by comment from the "what could possibly go wrong" department.

My first thought was "What about T-E header which we add and remove?" Looks like you handle that case, but mind adding it to the integration test?

From a user experience perspective I suspect folks will probably want snow's proper casing along with your preserve casing, so if there's a header addition (say we gunzip and go from content length to transfer encoding) or header creation (send local reply with content length) we "create" the right one. Do we expect them to play well together? If so I'd suggest having the canonical config use both, update docs explaining why users who want preserve probably want create, and integration test the transformation example.

@mattklein123
Copy link
Member Author

From a user experience perspective I suspect folks will probably want snow's proper casing along with your preserve casing, so if there's a header addition (say we gunzip and go from content length to transfer encoding) or header creation (send local reply with content length) we "create" the right one. Do we expect them to play well together? If so I'd suggest having the canonical config use both, update docs explaining why users who want preserve probably want create, and integration test the transformation example.

This is interesting. I didn't think about them being stacked like this. This is not how it works today. What does everyone think about this? Right now it's actually possible in the proto to configure this. I can potentially deprecate and make it repeated? That would be a pretty big change and I'm not sure it's worth it? Thoughts?

@alyssawilk
Copy link
Contributor

We're adding this case preservation system to handle clients/servers who do case sensitive compares.
If regular Envoy tweaking of headers results in lower case content-length etc. I think they're still going to have a problem, so I think we have to make sure we have a way to do preservation and proper casing to really solve that problem.

@mattklein123
Copy link
Member Author

OK this should be ready for another pass, thanks.

@@ -439,11 +440,16 @@ struct Http1Settings {
// Performs proper casing of header keys: the first and all alpha characters following a
// non-alphanumeric character is capitalized.
ProperCase,
// A stateful formatter extension has been configured.
StatefulFormatter,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think given @alyssawilk insight that we might want to generate mixed-case results for synthesized headers while preserving the case of others, maybe we want to avoid checking in the API that has these choices as exclusive?

I think from a data structure perspective we'd need to actually eliminate StatefulFormatter as a choice here and just allow users to configure both the enum and a stateful formatter (or repeated stateful formatters if we think that might be useful in the future). Does it make sense to do that now before the initial checkin?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm less concerned about the code and more about the API. My understanding from the thread above is that we decided if we need this in the future we can implement a nested formatter inside the preserve case formatter for unknown headers? Because of that I think we decide we can punt for now until someone asks for it?

Copy link
Contributor

Choose a reason for hiding this comment

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

Personally, I would think that any other than nested or another extension (one that may not even be in the core repo) would need to handle prioritization. For instance if you defined a stateful (preserve case), and stateless (proper case), in the current form the stateless would just end up proper casing the header keys that the stateful formatter was looking to preserve. Am I missing something @jmarantz, as it stands I'd agree 💯 with @mattklein123.

Copy link
Contributor

@snowp snowp left a comment

Choose a reason for hiding this comment

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

Looks good besides the one comment.

I think we might not be propagating the casing in situation where we copy the downstream headers (e.g. request shadowing), but it might not be worth trying to solve this now.

@@ -65,7 +65,9 @@
# perform temporary registrations.
REGISTER_FACTORY_TEST_ALLOWLIST = (
"./test/common/config/registry_test.cc", "./test/integration/clusters/",
"./test/integration/filters/")
"./test/integration/filters/",
"./test/extensions/http/header_formatters/preserve_case/preserve_case_formatter_integration_test.cc"
Copy link
Contributor

Choose a reason for hiding this comment

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

The comment above suggests you should be using InjectFactory instead of of REGISTER_FACTORY, thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

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

This isn't possible for integration tests. This is here for the same reason test/integration/filters is here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Huh I thought I'd seen InjectFactory used in integration tests, but I could be wrong here

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah OK it looks like it can. Sure I can fix that.

@mattklein123
Copy link
Member Author

I think we might not be propagating the casing in situation where we copy the downstream headers (e.g. request shadowing), but it might not be worth trying to solve this now.

Yes, it won't work in that case. I think it's not very hard to fix. We could add a clone() method to the formatter, and then clone it during header map copy. I didn't do this because I doubt anyone will notice and I didn't want to use shared_ptr. wdyt?

@snowp
Copy link
Contributor

snowp commented Mar 26, 2021

I didn't do this because I doubt anyone will notice and I didn't want to use shared_ptr. wdyt?

I think if it's limited to request shadowing it's probably fine to leave for now, I imagine the main use case will be to format response headers, so the shadowing case is probably not important. I wasn't sure if there were other header copying cases that we needed to cover

@mattklein123
Copy link
Member Author

Any copying case can be fixed relatively easily by allowing formatter cloning, but like I said, I would rather wait until someone complains, because I doubt they will. It won't be hard to fix at that point.

@mattklein123
Copy link
Member Author

@snowp updated per comments

Copy link
Contributor

@snowp snowp left a comment

Choose a reason for hiding this comment

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

Thanks!

@dalegaspi
Copy link

is this going to be available on the latest release only or is it going to be back-ported to some older releases?

@mattklein123 mattklein123 merged commit 2a4d97c into main Mar 28, 2021
@mattklein123 mattklein123 deleted the header_casing_take_2 branch March 28, 2021 16:51
# HTTP header formatters
#

"envoy.http.stateful_header_formatters.preserve_case": "//source/extensions/http/header_formatters/preserve_case:preserve_case_formatter"
Copy link
Contributor

Choose a reason for hiding this comment

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

missing , at the end of the line.

Copy link
Member Author

Choose a reason for hiding this comment

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

Will fix this in my current PR

@noah8713
Copy link
Contributor

@mattklein123 Is there an optimal option/improvement/suggestion for skipping this for http2 clusters when in case like istio, ingress gateway needs to allow both http1/2 traffic. We ran into istio/istio#36299 when using auto_config for case preserve leveraging envoy to auto detect http1.1/2 depending on upstream config but it didn't pick up as expected.

@mattklein123
Copy link
Member Author

@mattklein123 Is there an optimal option/improvement/suggestion for skipping this for http2 clusters when in case like istio, ingress gateway needs to allow both http1/2 traffic. We ran into istio/istio#36299 when using auto_config for case preserve leveraging envoy to auto detect http1.1/2 depending on upstream config but it didn't pick up as expected.

This only takes effect for H1. If something else is happening I would open a new issue with repro instructions, etc.

@noah8713
Copy link
Contributor

Thanks @mattklein123 : I have mentioned details of reproducing in istio/istio#36299 as it happens while using auto_config for case preserve in httpprotocol options vs explicit_http_config as mentioned in the doc. I am not sure if I should open a new issue here too or continue on the one on istio side.

@noah8713
Copy link
Contributor

noah8713 commented Dec 1, 2021

@mattklein123 Is there an optimal option/improvement/suggestion for skipping this for http2 clusters when in case like istio, ingress gateway needs to allow both http1/2 traffic. We ran into istio/istio#36299 when using auto_config for case preserve leveraging envoy to auto detect http1.1/2 depending on upstream config but it didn't pick up as expected.

This only takes effect for H1. If something else is happening I would open a new issue with repro instructions, etc.

Filed new issue for the same #19149

jiangshantao-dbg pushed a commit to istio-mt/envoy that referenced this pull request Mar 30, 2022
1) Add new stateful header formatter extension point; 2) Add preserve case formatter extension;
Fixes envoyproxy#14363

Signed-off-by: jiangshantao <[email protected]>
jiangshantao-dbg added a commit to istio-mt/envoy that referenced this pull request Mar 31, 2022
* feat: http: add HTTP/1.1 case preservation (envoyproxy#15619)

1) Add new stateful header formatter extension point; 2) Add preserve case formatter extension;
Fixes envoyproxy#14363

Signed-off-by: jiangshantao <[email protected]>

* fix: fix Error envoy_cc_library() got unexpected keyword argument: category

Signed-off-by: jiangshantao <[email protected]>

* fix: source/common/http/http1/settings.cc:39:39: error: unused parameter 'validate_scheme'

Signed-off-by: jiangshantao <[email protected]>

* feat: preserve case with proper case config

Signed-off-by: jiangshantao <[email protected]>

Co-authored-by: jiangshantao <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Envoy should be able to preserve header casing in HTTP/1.1