-
Notifications
You must be signed in to change notification settings - Fork 5.3k
http3: adding connect support #17877
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
Changes from all commits
64cb5a6
3924a76
3679898
fa2c3a8
e40d6b4
65e8a4f
9297851
5988a7e
ec4bfa4
8c06cdd
49ad7fa
3f4ee6c
6b3bb58
ed3cbff
fe54c45
ba1234c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -473,6 +473,7 @@ message GrpcProtocolOptions { | |
| } | ||
|
|
||
| // A message which allows using HTTP/3. | ||
| // [#next-free-field: 6] | ||
| message Http3ProtocolOptions { | ||
| QuicProtocolOptions quic_protocol_options = 1; | ||
|
|
||
|
|
@@ -483,6 +484,14 @@ message Http3ProtocolOptions { | |
| // If set, this overrides any HCM :ref:`stream_error_on_invalid_http_messaging | ||
| // <envoy_v3_api_field_extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.stream_error_on_invalid_http_message>`. | ||
| google.protobuf.BoolValue override_stream_error_on_invalid_http_message = 2; | ||
|
|
||
| // Allows proxying Websocket and other upgrades over HTTP/3 CONNECT using | ||
| // the header mechanisms from the `HTTP/2 extended connect RFC | ||
| // <https://datatracker.ietf.org/doc/html/rfc8441>`_ | ||
| // and settings `proposed for HTTP/3 | ||
| // <https://datatracker.ietf.org/doc/draft-ietf-httpbis-h3-websockets/>`_ | ||
| // [#alpha:] as HTTP/3 CONNECT is not yet an RFC. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to eventually convert this into an annotation. I assume that for a primitive type, if it's set to something other than default, I can determine that the annotation is set in that case but not if it's left to the default? Basically I'm wondering if we need to make this a WKT vs. a primitive or not for fields. cc @envoyproxy/api-shepherds who know more about proto.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd think it'd be fine if for no other reason than setting it false is the same as not-present and doesn't need an alpha tag. it's really only the behavior when this is explicitly set true that may change depending on the RFC
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK sounds good. I will see if this case works OK when I implement the feature.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think we do need in general to make WIP annotated things WKT/optional/messages when the default value is material, but if their absence is a no-op then we can skip. |
||
| bool allow_extended_connect = 5; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When we had discussed this last, I was under the impression that we wanted an HTTP/3 option to enable CONNECT altogether. Is that no longer the case? Are we allowing non-extended CONNECT by default?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alyssa can correct me, but if I'm remembering our conversation correctly, I believe vanilla CONNECT is controlled by an HCM knob and we did not need a per-protocol vanilla CONNECT knob. (The HTTP/2 option, while named "allow_connect" actually enables extended connect so this HTTP/3 is parallel to the HTTP/3 option but with a more accurate name)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, we've gone over that a couple of times now :-) Old-style CONNECT gets rejected in the HCM if there's not connect matchers, and so we only need to control extended connect in the protoco-specific options. I tried to also make sure the documentation pretty clear on this point (https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/http/upgrades#connect-support) but please let me know if you have ideas of how I could improve it!
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for clarifying! |
||
| } | ||
|
|
||
| // A message to control transformations to the :scheme header | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -339,6 +339,18 @@ Http::Status HeaderUtility::checkRequiredRequestHeaders(const Http::RequestHeade | |
| return absl::InvalidArgumentError( | ||
| absl::StrCat("missing required header: ", Envoy::Http::Headers::get().Host.get())); | ||
| } | ||
| if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.validate_connect")) { | ||
| if (headers.Path() && !headers.Protocol()) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As per the spec, when
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :scheme is required for all non-CONNECT requests and is supposed to be done prior to this point as we expect core protocol invariant to be enforced by the codec (this is pre-RFC and extension so less so for this). If QUICHE isn't doing that enforcement, I'd argue it probably should so thanks for volunteering to pick it up below. |
||
| // Path and Protocol header should only be present for CONNECT for upgrade style CONNECT. | ||
| return absl::InvalidArgumentError( | ||
| absl::StrCat("missing required header: ", Envoy::Http::Headers::get().Protocol.get())); | ||
| } | ||
| if (!headers.Path() && headers.Protocol()) { | ||
| // Path and Protocol header should only be present for CONNECT for upgrade style CONNECT. | ||
| return absl::InvalidArgumentError( | ||
| absl::StrCat("missing required header: ", Envoy::Http::Headers::get().Path.get())); | ||
| } | ||
| } | ||
| } else { | ||
| if (!headers.Path()) { | ||
| // :path header must be present for non-CONNECT requests. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,6 +46,8 @@ EnvoyQuicServerStream::EnvoyQuicServerStream( | |
| headers_with_underscores_action_(headers_with_underscores_action) { | ||
| ASSERT(static_cast<uint32_t>(GetReceiveWindow().value()) > 8 * 1024, | ||
| "Send buffer limit should be larger than 8KB."); | ||
| // TODO(alyssawilk, danzh) if http3_options_.allow_extended_connect() is true, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW I'm planning on adding support for this SETTING in QUICHE for Privacy Proxy, I plan on always sending it and validating the presence of pseudo-headers in QUICHE
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for taking this on. We should perhaps discuss this on the QUICHE PR/CL, but I'm not convinced that always sending the setting is the right choice. But let's discuss there.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @DavidSchinazi Actually I'm already working on a QUICHE change to add this setting per previous discussion. I can take this feature if you don't mind?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @danzh2010 that's fine by me! From Alyssa's other comment I think QUICHE should validate that the correct headers are present. Please add me as a reviewer on the change |
||
| // send the correct SETTINGS. | ||
| } | ||
|
|
||
| void EnvoyQuicServerStream::encode100ContinueHeaders(const Http::ResponseHeaderMap& headers) { | ||
|
|
@@ -167,7 +169,9 @@ void EnvoyQuicServerStream::OnInitialHeadersComplete(bool fin, size_t frame_len, | |
| onStreamError(close_connection_upon_invalid_header_, rst); | ||
| return; | ||
| } | ||
| if (Http::HeaderUtility::requestHeadersValid(*headers) != absl::nullopt) { | ||
| if (Http::HeaderUtility::requestHeadersValid(*headers) != absl::nullopt || | ||
| Http::HeaderUtility::checkRequiredRequestHeaders(*headers) != Http::okStatus() || | ||
| (headers->Protocol() && !http3_options_.allow_extended_connect())) { | ||
| details_ = Http3ResponseCodeDetailValues::invalid_http_header; | ||
| onStreamError(absl::nullopt); | ||
| return; | ||
|
|
@@ -392,7 +396,7 @@ void EnvoyQuicServerStream::onStreamError(absl::optional<bool> should_close_conn | |
| !http3_options_.override_stream_error_on_invalid_http_message().value(); | ||
| } | ||
| if (close_connection_upon_invalid_header) { | ||
| stream_delegate()->OnStreamError(quic::QUIC_HTTP_FRAME_ERROR, "Invalid headers"); | ||
| stream_delegate()->OnStreamError(quic::QUIC_HTTP_FRAME_ERROR, std::string(details_)); | ||
| } else { | ||
| Reset(rst); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -95,6 +95,7 @@ constexpr const char* runtime_features[] = { | |
| "envoy.reloadable_features.unquote_log_string_values", | ||
| "envoy.reloadable_features.upstream_host_weight_change_causes_rebuild", | ||
| "envoy.reloadable_features.use_observable_cluster_name", | ||
| "envoy.reloadable_features.validate_connect", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be documented somewhere? |
||
| "envoy.reloadable_features.vhds_heartbeats", | ||
| "envoy.reloadable_features.wasm_cluster_name_envoy_grpc", | ||
| "envoy.reloadable_features.upstream_http2_flood_checks", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -257,6 +257,7 @@ IntegrationCodecClientPtr HttpIntegrationTest::makeRawHttpConnection( | |
| } else { | ||
| cluster->http3_options_ = ConfigHelper::http2ToHttp3ProtocolOptions( | ||
| http2_options.value(), quic::kStreamReceiveWindowLimit); | ||
| cluster->http3_options_.set_allow_extended_connect(true); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this config on client stream has any effect?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume it'll result in sending the SETTINGS when we have it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about this one?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought the ENABLE_CONNECT_PROTOCOL setting was supposed to be sent by server. So the the knob should only be used by server code. No?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd imagine that on the client we'd use this option to fail the extended connect before it's sent? Or is it a no-op on the client? |
||
| #endif | ||
| } | ||
| cluster->http2_options_ = http2_options.value(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.