H/2: discard Host header when :authority is present#30005
H/2: discard Host header when :authority is present#30005yanavlasov merged 4 commits intoenvoyproxy:mainfrom
Conversation
Signed-off-by: Yan Avlasov <yavlasov@google.com>
adisuissa
left a comment
There was a problem hiding this comment.
Code looks good!
I've got a high-level question about the desired behavior.
| // Check if there is already the :authority header | ||
| auto result = stream->headers().get(Http::Headers::get().Host); | ||
| if (!result.empty()) { | ||
| // Discard the host header value |
There was a problem hiding this comment.
My understanding of https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1 is that discarding host is not necessary, but it must have the same value as :authority. Should the discarding be done only if the host differs from :authority?
There was a problem hiding this comment.
Here are the statements related to the server:
The recipient of an HTTP/2 request MUST NOT use the Host header field to determine the target URI if ":authority" is present.- A server SHOULD treat a request as malformed if it contains a Host header field that identifies an entity that differs from the entity in the ":authority" pseudo-header field. (Envoy does not need to reject request with Host different from :authority)
- An intermediary that forwards a request over HTTP/2 MAY retain any Host header field. (Envoy does not need to retain the Host if it was present).
Note that Envoy can not have both :authority and Host headers in the header map, they are treated as one and the same. As a result discarding the Host header is compliant with RFC.
There was a problem hiding this comment.
You got me convinced. I agree it makes sense to remove the header and protect it with a runtime flag.
| StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id); | ||
| if (stream && name == static_cast<absl::string_view>(Http::Headers::get().HostLegacy)) { | ||
| // Check if there is already the :authority header | ||
| auto result = stream->headers().get(Http::Headers::get().Host); |
|
I think this deserves a cross-company review |
| if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_discard_host_header")) { | ||
| StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id); | ||
| if (stream && name == static_cast<absl::string_view>(Http::Headers::get().HostLegacy)) { | ||
| // Check if there is already the :authority header |
There was a problem hiding this comment.
The http2 spec says that :authority must come before regular header fields and that reversing the order should be considered a malformed request. As long as the h2 parser actually rejects the reverse order, this looks good.
There was a problem hiding this comment.
This is correct. I have added a test to validate this.
| ASSERT(frame->headers.cat == NGHTTP2_HCAT_REQUEST || frame->headers.cat == NGHTTP2_HCAT_HEADERS); | ||
| if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_discard_host_header")) { | ||
| StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id); | ||
| if (stream && name == static_cast<absl::string_view>(Http::Headers::get().HostLegacy)) { |
There was a problem hiding this comment.
nit, maybe move this check above the line 2131
envoy/source/common/http/http2/codec_impl.cc
Lines 1474 to 1484 in 518c582
although not sure that check still available for now.
There was a problem hiding this comment.
saveHeader is called from two places. I would need to duplicate this check. Maybe it is is better to keep it in one place only.
|
/wait on comments and main merge |
|
This pull request has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions! |
|
This pull request has been automatically closed because it has not had activity in the last 37 days. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions! |
Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Yan Avlasov <yavlasov@google.com>
|
@jmarantz @zuercher @soulxu -- quick question as I try to understand the flow of envoy releases. I see this PR is merged but is not associated with a milestone. Am I correct in assuming that this would go out with 1.28.1 and possibly be back-ported to 1.27.3? Apologies for tagging you all; I looked through the various docs but if there was anything about release flow & cadence, I missed it. |
@SeanKilleen this is going to 1.29 release. If you want this in 1.28 or 1.27, you need to backport it. |
|
Per @zirain at the Istio project, the Istio team would appreciate this change being back-ported to v1.28.x.
CCing @yanavlasov in case this is something they'd be able to take on as well. I would typically be willing to attempt it myself but I'm out of my depth from a language perspective. Sorry for the time gap on this -- a miscommunication elsewhere gave me the impression it already had been backported, despite @soulxu's comments above to the contrary. Apologies! |
|
@SeanKilleen can you raise PRs against the other supported branches - ie:
it should be backported to all supported (unless they dont have the issue it addresses) /backport |
|
@phlax sure, I can try. I have no experience in this codebase or C++; I just happened to stumble upon an issue which was luckily fixed in this PR by @yanavlasov. But I'll check the contributing docs etc. and do my best. |
|
great thanks @SeanKilleen dont worry about c++ skills (mine are rudimentary at best) the steps are basically this ... $ git checkout release/v1.28
$ git checkout -b 1.28-discard-fix
$ git cherry-pick $COMMIT_ID_FROM_MAIN
$ git push --set-upstream origin 1.28-discard-fixthis assumes you have a remote the ie when you create the PRs make sure the target branch is set to the relevant branch - in the example case we should probs add this to docs somewhere |
|
you will most likely have to resolve some conflict - if you are lucky just the changelog |
|
@phlax ah, no problem. I'll add it to the docs too :) |
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1 This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false. --------- Signed-off-by: Yan Avlasov <yavlasov@google.com> Signed-off-by: Sean Killeen <SeanKilleen@gmail.com>
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1 This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false. --------- Signed-off-by: Yan Avlasov <yavlasov@google.com> Signed-off-by: Sean Killeen <SeanKilleen@gmail.com>
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1 This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false. --------- Signed-off-by: Yan Avlasov <yavlasov@google.com>
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1 This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false. --------- Signed-off-by: Yan Avlasov <yavlasov@google.com> Signed-off-by: Sean Killeen <SeanKilleen@gmail.com>
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1 This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false. --------- Signed-off-by: Yan Avlasov <yavlasov@google.com> Signed-off-by: Sean Killeen <SeanKilleen@gmail.com>
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1 This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false. --------- Signed-off-by: Yan Avlasov <yavlasov@google.com> Signed-off-by: Sean Killeen <SeanKilleen@gmail.com>
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1 This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false. --------- Signed-off-by: Yan Avlasov <yavlasov@google.com> Signed-off-by: Sean Killeen <SeanKilleen@gmail.com>
Commit Message:
Discard the
Hostheader if the:authorityheader was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1This behavioral change can be reverted by setting runtime flag
envoy.reloadable_features.http2_discard_host_headerto false.Risk Level: Low
Testing: Unit Tests
Fixes #31118
Docs Changes: N/A
Release Notes: Yes
Platform Specific Features: N/A
Runtime guard: envoy.reloadable_features.http2_discard_host_header