http3: support Http3Options for downstream #15753
http3: support Http3Options for downstream #15753alyssawilk merged 20 commits intoenvoyproxy:mainfrom
Conversation
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
alyssawilk
left a comment
There was a problem hiding this comment.
Nice change!
Once you sort out docs CI, let's check coverage on this one and make sure we've got corner cases tested.
| } | ||
|
|
||
| HeaderValidationResult | ||
| EnvoyQuicServerStream::checkHeaderNameForUnderscores(const std::string& header_name) { |
There was a problem hiding this comment.
can we factor the Http2 code into HeaderUtility or not enough overlap? or maybe at least stick it in the utility directory, unit terst, and we can use when we switch codec libraries.
There was a problem hiding this comment.
I moved this method to HeaderUtility, but using it in h2 codec doesn't save many lines, so I didn't use it there.
| return HeaderValidationResult::ACCEPT; | ||
| } | ||
|
|
||
| HeaderValidationResult validateContentLength(absl::string_view header_value, |
There was a problem hiding this comment.
Ditto, consider moving to utility
|
|
||
| // Additional HTTP/3 settings that are passed directly to the HTTP/3 codec. | ||
| config.core.v3.Http3ProtocolOptions http3_protocol_options = 44 | ||
| [(udpa.annotations.security).configure_for_untrusted_downstream = true]; |
There was a problem hiding this comment.
@htuch The doc CI fails because this field is annotated with udpa.annotations.security, but doesn't have description in docs/protodoc_manifest.yaml. Do I need to document override_stream_error_on_invalid_http_message, the only field of Http3ProtocolOptions, there? http2_protocol_options doesn't have documentation about its similar fields in protodoc_manifest.yaml.
There was a problem hiding this comment.
If there is anything security related, e.g. buffering, update the example manifest YAML as per offline discussion. If not, feel free to drop the security annotation.
|
Try hiding the field and see if that fixes docs build - it's probably trying to generate docs for the (not hidden) field refering to the (hidden) proto and having problems. |
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
|
/retest |
|
Retrying Azure Pipelines: |
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
alyssawilk
left a comment
There was a problem hiding this comment.
Cool, just a few more nits and I think this one's good to go
| Http::HeaderUtility::HeaderValidationResult | ||
| validateHeader(const std::string& header_name, absl::string_view header_value) override { | ||
| bool override_stream_error_on_invalid_http_message = | ||
| http3_options_.has_override_stream_error_on_invalid_http_message() && |
There was a problem hiding this comment.
Given we're cloning this, what do you think of just always setting it in initializeAndValidate?
There was a problem hiding this comment.
Makes sense, then we don't need to check http3_options_.has_override_stream_error_on_invalid_http_message() here.
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
|
/retest |
|
Retrying Azure Pipelines: |
mattklein123
left a comment
There was a problem hiding this comment.
Thanks really awesome to see this all coming together. A few comments from a quick pass.
/wait
| // closed. | ||
| static constexpr absl::string_view invalid_http_header = "http3.invalid_header_field"; | ||
| // The size of headers (or trailers) exceeded the configured limits. | ||
| static constexpr absl::string_view headers_too_large = "http3.headers.too.large"; |
There was a problem hiding this comment.
nit: it's kind of strange that this one is uses dot separation and the others are using underscores? Can we be consistent? Is this copied from elsewhere?
There was a problem hiding this comment.
I saw http2.invalid.header.field and thought any error from external library should use ., no?
There was a problem hiding this comment.
I think this is probably just a typo. IMO I would make it consistent with the others.
include/envoy/upstream/upstream.h
Outdated
| virtual Http::Http2::CodecStats& http2CodecStats() const PURE; | ||
|
|
||
| /** | ||
| * @return the Http2 Codec Stats. |
There was a problem hiding this comment.
| * @return the Http2 Codec Stats. | |
| * @return the Http3 Codec Stats. |
| ENVOY_CONN_LOG(error, "Attempt to create stream {} before HCM filter is initialized.", *this, | ||
| id); | ||
| return nullptr; |
There was a problem hiding this comment.
Can this actually happen? If it can, can this log spam?
There was a problem hiding this comment.
It will be an implementation error if this happens.
There was a problem hiding this comment.
OK then please use either ASSERT or ENVOY_BUG depending on what you want to do in terms of providing testing. I don't think kind of error handling makes sense.
| !absl::EqualsIgnoreCase(header, Headers::get().HostLegacy.get())); | ||
| } | ||
|
|
||
| HeaderUtility::HeaderValidationResult HeaderUtility::checkHeaderNameForUnderscores( |
There was a problem hiding this comment.
It kills me that this code is now copied into a third place when we already effectively have it in both the H1 and H2 codec. This is really scary and security sensitive stuff and I feel we shouldn't be making the problem worse (it's in the opposite direction of #10646). Is there any work we can do to share some of this code now? Maybe we can do a different PR to add this helper and call it somehow from both other codecs?
There was a problem hiding this comment.
I don't want to touch any code in production in this PR because that needs feature protection. Once this PR lands, the other two codec can switch to this utility function.
There was a problem hiding this comment.
OK fair enough. Can you at least add comments and put TODOs in the other codec places which reference this code and the tracking issue for merging?
There was a problem hiding this comment.
cc @yanavlasov might be a good starter project for one of the envoy-sec folks
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
|
I refactored the stream error handling a bit. PTAL |
mattklein123
left a comment
There was a problem hiding this comment.
Thanks LGTM, will defer to @alyssawilk to review the most recent set of changes and merge. Thank you!
| if (!codec_stats_.has_value() || !http3_options_.has_value()) { | ||
| ENVOY_CONN_LOG(error, "Attempt to create stream {} before HCM filter is initialized.", *this, | ||
| id); | ||
| ENVOY_BUG(false, |
There was a problem hiding this comment.
Do you have a test for this ENVOY_BUG? If not just make it an assert.
There was a problem hiding this comment.
As long as our implementation is correct, it shouldn't be reached. So I don't know how to trigger it in a meaningful test right now. But I'd like to keep it in production, in case we miss some QUICHE behavior change that could occassionally lead to this in the future.
There was a problem hiding this comment.
But how will you know the error checking will work if we reach it in production? :) In general we require testing for ENVOY_BUG type logic, so I would still prefer that you switch this to assert if you can't reasonably test it.
There was a problem hiding this comment.
Okay, I added a unit test in envoy_quic_server_session_test to hit this ENVOY_BUG just to check the ENVOY_BUG works as intended in debug and release build.
|
/retest |
|
Retrying Azure Pipelines: |
alyssawilk
left a comment
There was a problem hiding this comment.
Nice! just one more nit and good to go.
| !absl::EqualsIgnoreCase(header, Headers::get().HostLegacy.get())); | ||
| } | ||
|
|
||
| HeaderUtility::HeaderValidationResult HeaderUtility::checkHeaderNameForUnderscores( |
There was a problem hiding this comment.
cc @yanavlasov might be a good starter project for one of the envoy-sec folks
| if (Http::HeaderUtility::requestHeadersValid(*headers) != absl::nullopt) { | ||
| stream_delegate()->OnStreamError(quic::QUIC_HTTP_FRAME_ERROR, "Invalid headers"); | ||
| details_ = Http3ResponseCodeDetailValues::invalid_http_header; | ||
| onStreamError(!http3_options_.override_stream_error_on_invalid_http_message().value()); |
There was a problem hiding this comment.
what do you think of making this a default argument? If you'd prefer to be explicit I'd mildly prefer a helper function for brevity.
There was a problem hiding this comment.
I'd prefer having explicit argument. I changed the function to take an optional bool which if absent means checking override_stream_error_on_invalid_http_message from h3 options.
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
| // Either reset the stream or close the connection according to | ||
| // close_connection_upon_invalid_header. | ||
| void onStreamError(bool close_connection_upon_invalid_header); | ||
| void onStreamError(absl::optional<bool> should_close_connection); |
|
API hasn't changed since the LGTM so I'm going to go ahead and merge. |
Commit Message: allow HCM to config Http3Options and use it with other HCM configs, i.e. max_request_headers_kb and headers_with_underscores_action, to setup QuicHttpServerConnectionImpl. And support these configurations in QUIC. Currently the only Http3Options configuration is override_stream_error_on_invalid_http_message.
Additional Description: added Http3 codec stats. Pass it along with Http3Options and other HCM configs.
Risk Level: low
Testing: enable related tests in quic_protocol_integration_test
Docs Changes: updated docs/root/configuration/http/http_conn_man/response_code_details.rst
Part of #12930 #2557