http3: Add support for HTTP/3 METADATA#32568
Conversation
|
CC @envoyproxy/api-shepherds: Your approval is needed for changes made to |
|
Sorry! I keep forgetting that Envoy does automatic assignment. I think the code in this PR is basically ready for review, but it depends on some un-commited QUICHE changes that I manually passed in so it won't yet pass CI |
e68f58b to
ccddb41
Compare
Signed-off-by: Ryan Hamilton <rch@google.com> Cleanup Signed-off-by: Ryan Hamilton <rch@google.com> Reduce code duplication Signed-off-by: Ryan Hamilton <rch@google.com> Changelog Signed-off-by: Ryan Hamilton <rch@google.com> Better release notes Signed-off-by: Ryan Hamilton <rch@google.com> More cleanup Signed-off-by: Ryan Hamilton <rch@google.com> Even more cleanup Signed-off-by: Ryan Hamilton <rch@google.com> Cleanup! Cleanup Signed-off-by: Ryan Hamilton <rch@google.com>
Signed-off-by: Ryan Hamilton <rch@google.com>
ccddb41 to
71c28a9
Compare
|
CC @envoyproxy/dependency-shepherds: Your approval is needed for changes made to |
|
/assign @danzh2010 Ok, this is ready for review, I hope. There's a QUICHE update in this as well, but I'll land that separately once #32642 lands. |
Signed-off-by: Ryan Hamilton <rch@google.com>
Signed-off-by: Ryan Hamilton <rch@google.com>
Signed-off-by: Ryan Hamilton <rch@google.com>
Signed-off-by: Ryan Hamilton <rch@google.com>
|
Now that #32650 has landed, this PR does not change deps, so I'll remove that label. |
| #ifdef ENVOY_ENABLE_QUIC | ||
| EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_http3_metadata_decoding)); | ||
| #else | ||
| EXCLUDE_DOWNSTREAM_HTTP3; // The HTTP/3 client has no "bad frame" equivalent. |
There was a problem hiding this comment.
The comment seems irrelevant.
There was a problem hiding this comment.
Whoops. Thanks.
| result = quic_stream_.WriteMemSlices(metadata_frame, /*end_stream=*/false); | ||
| } | ||
| // QUIC stream must take all. | ||
| if (result.bytes_consumed == 0) { |
There was a problem hiding this comment.
Can this be true if the metadata passed in is empty?
There was a problem hiding this comment.
No, because the METADATA frame header will always be present.
Signed-off-by: Ryan Hamilton <rch@google.com>
alyssawilk
left a comment
There was a problem hiding this comment.
Nice addition!
/wait on comments
| return dynamic_cast<QuicFilterManagerConnectionImpl*>(session()); | ||
| } | ||
|
|
||
| void EnvoyQuicClientStream::OnMetadataComplete(size_t /*frame_len*/, |
There was a problem hiding this comment.
in H3, can metadata arrive before headers? I think it's an invariant of Envoy that headers arrive first and I'm not sure if it's an invariant of the quiche libraries.
There was a problem hiding this comment.
METADATA is an HTTP/3 frame which is delivered on the QUIC stream, so it's guaranteed to be delivered in the order it was sent. I think this is true for HTTP/2 as well. But if a peer sent a METADATA frame before sending HEADERS I think QUICHE will expose it in that order. I think that's also the same for HTTP/2. But we could make this an error of some here, if you prefer?
There was a problem hiding this comment.
I think we should either make it an error, or add an e2e test it doesn't cause problems in Envoy.
There was a problem hiding this comment.
Oh! I think we have a test for this already! ProxyMetadataInResponse does:
// Sends metadata before response header.
const std::string key = "key";
std::string value = std::string(80 * 1024, '1');
Http::MetadataMap metadata_map = {{key, value}};
Http::MetadataMapPtr metadata_map_ptr = std::make_unique<Http::MetadataMap>(metadata_map);
Http::MetadataMapVector metadata_map_vector;
metadata_map_vector.push_back(std::move(metadata_map_ptr));
upstream_request_->encodeMetadata(metadata_map_vector);
upstream_request_->encodeHeaders(default_response_headers_, false);
upstream_request_->encodeData(12, true);
|
|
||
| void EnvoyQuicStream::encodeMetadata(const Http::MetadataMapVector& metadata_map_vector) { | ||
| if (!http3_options_.allow_metadata()) { | ||
| // Metadata Frame is not supported in QUICHE. |
There was a problem hiding this comment.
update comment and log: not supported by config
There was a problem hiding this comment.
Good point. Done.
| "bytes in send buffer. Current write was rejected.", | ||
| quic_stream_.write_side_closed() ? "closed" : "open", | ||
| quic_stream_.BufferedDataBytes())); | ||
| quic_stream_.Reset(quic::QUIC_BAD_APPLICATION_PAYLOAD); |
There was a problem hiding this comment.
is bad application payload the best? feels like an invariant failure but idk if ther's a better code.
There was a problem hiding this comment.
Fair enough. How about QUIC_ERROR_PROCESSING_STREAM which seems to be the closest thing we have.
|
|
||
| void EnvoyQuicServerStream::OnMetadataComplete(size_t /*frame_len*/, | ||
| const quic::QuicHeaderList& header_list) { | ||
| request_decoder_->decodeMetadata(metadataMapFromHeaderList(header_list)); |
There was a problem hiding this comment.
do we need to do any validation steps with or without UHV? I honestly don't recall sorry
There was a problem hiding this comment.
I don't see anything like that for HTTP/2. I think the payload of METADATA is "key/value pairs" not "HTTP headers" so normal header validation does not apply, as I understand it.
There was a problem hiding this comment.
Ok I didn't know if we had concerns about character validation, since I think Envoy has some assumptions there, but maybe not for metadata.
cc @yanavlasov
There was a problem hiding this comment.
No restrictions on metadata character set, since it is opt-in and the strings are length prefixed.
| EXPECT_EQ(count * size + added_decoded_data_size * 2, response->body().size()); | ||
| } | ||
|
|
||
| class MetadataIntegrationTest : public HttpProtocolIntegrationTest { |
There was a problem hiding this comment.
can we change Http2MetadataIntegrationTest to be MetadataIntegrationTest and run all the tests for H2/H3?
Signed-off-by: Ryan Hamilton <rch@google.com>
Signed-off-by: Ryan Hamilton <rch@google.com>
Signed-off-by: Ryan Hamilton <rch@google.com>
Signed-off-by: Ryan Hamilton <rch@google.com>
https://github.com/google/quiche/compare/cc9724b90..70bc4dfde ``` $ git log cc9724b90..70bc4dfde --date=short --no-merges --format="%ad %al %s" 2024-03-13 rch Return false from QuicSpdyStream::OnMetadataFrameEnd if the stream has been reset. 2024-03-13 martinduke Update MOQT to draft-03 wire image. Does not add ANNOUNCE_CANCEL because it does not have a code point. 2024-03-13 martinduke Rename kGenericError to kInternalError. ``` Signed-off-by: Ryan Hamilton <rch@google.com>
Signed-off-by: Ryan Hamilton <rch@google.com>
|
@alyssawilk PTAL. The QUICHE update which the size limit tests require will be landed in #32892. In the meantime, I've patched that .bzl file change into this PR so I'll need a main merge once that lands. But it's ready for review, I think! |
|
/retest |
1 similar comment
|
/retest |
alyssawilk
left a comment
There was a problem hiding this comment.
looks like test/integration/protocol_integration_test.cc has some Metadata tests as well as http2_flood_integration_test.cc
Given it's hidden and we're the only users I'm OK with a TODO to turn those up and landing to kick off import - your call if that's helpful or if you want to just land the right proper fix :-)
| return dynamic_cast<QuicFilterManagerConnectionImpl*>(session()); | ||
| } | ||
|
|
||
| void EnvoyQuicClientStream::OnMetadataComplete(size_t /*frame_len*/, |
There was a problem hiding this comment.
I think we should either make it an error, or add an e2e test it doesn't cause problems in Envoy.
|
|
||
| void EnvoyQuicServerStream::OnMetadataComplete(size_t /*frame_len*/, | ||
| const quic::QuicHeaderList& header_list) { | ||
| request_decoder_->decodeMetadata(metadataMapFromHeaderList(header_list)); |
There was a problem hiding this comment.
Ok I didn't know if we had concerns about character validation, since I think Envoy has some assumptions there, but maybe not for metadata.
cc @yanavlasov
| @@ -571,13 +588,13 @@ TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { | |||
| EXPECT_EQ(4, response->metadataMapsDecodedCount()); | |||
|
|
|||
| // Upstream responds with headers, 100-continue and data. | |||
There was a problem hiding this comment.
optional, switch to adding expect 100continue to default_request_headers_
| @@ -884,14 +911,14 @@ void Http2MetadataIntegrationTest::runHeaderOnlyTest(bool send_request_body, siz | |||
| Http::TestRequestHeaderMapImpl{{":method", "POST"}, | |||
There was a problem hiding this comment.
default request headers here and below?
| [&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { | ||
| RELEASE_ASSERT(bootstrap.mutable_static_resources()->clusters_size() >= 1, ""); | ||
| ConfigHelper::HttpProtocolOptions protocol_options; | ||
| protocol_options.mutable_explicit_http_config() |
There was a problem hiding this comment.
Http2MetadataIntegrationTest -> MetadataIntegraionTest?
There was a problem hiding this comment.
Whoops, done!
Signed-off-by: Ryan Hamilton <rch@google.com>
Thanks! Yeah, if it's ok with you, I'd love to address this in a follow up. Hopefully it's straightforward :) |
Signed-off-by: Ryan Hamilton <rch@google.com>
RyanTheOptimist
left a comment
There was a problem hiding this comment.
Thanks for the comments! The QUICHE merge landed so this PR no longer includes it.
| [&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { | ||
| RELEASE_ASSERT(bootstrap.mutable_static_resources()->clusters_size() >= 1, ""); | ||
| ConfigHelper::HttpProtocolOptions protocol_options; | ||
| protocol_options.mutable_explicit_http_config() |
There was a problem hiding this comment.
Whoops, done!
| @@ -884,14 +911,14 @@ void Http2MetadataIntegrationTest::runHeaderOnlyTest(bool send_request_body, siz | |||
| Http::TestRequestHeaderMapImpl{{":method", "POST"}, | |||
| @@ -571,13 +588,13 @@ TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { | |||
| EXPECT_EQ(4, response->metadataMapsDecodedCount()); | |||
|
|
|||
| // Upstream responds with headers, 100-continue and data. | |||
| return dynamic_cast<QuicFilterManagerConnectionImpl*>(session()); | ||
| } | ||
|
|
||
| void EnvoyQuicClientStream::OnMetadataComplete(size_t /*frame_len*/, |
There was a problem hiding this comment.
Oh! I think we have a test for this already! ProxyMetadataInResponse does:
// Sends metadata before response header.
const std::string key = "key";
std::string value = std::string(80 * 1024, '1');
Http::MetadataMap metadata_map = {{key, value}};
Http::MetadataMapPtr metadata_map_ptr = std::make_unique<Http::MetadataMap>(metadata_map);
Http::MetadataMapVector metadata_map_vector;
metadata_map_vector.push_back(std::move(metadata_map_ptr));
upstream_request_->encodeMetadata(metadata_map_vector);
upstream_request_->encodeHeaders(default_response_headers_, false);
upstream_request_->encodeData(12, true);
|
|
||
| void EnvoyQuicServerStream::OnMetadataComplete(size_t /*frame_len*/, | ||
| const quic::QuicHeaderList& header_list) { | ||
| request_decoder_->decodeMetadata(metadataMapFromHeaderList(header_list)); |
alyssawilk
left a comment
There was a problem hiding this comment.
mildly prefer an explicit TODO for the rest of tests but realistically I know you're good for it =P
http3: Add support for HTTP/3 METADATA
Adds a new
allow_metadataoption to Http3ProtocolOptions.Risk Level: Low, protected by new config option
Testing: New integration tests
Docs Changes: N/A
Release Notes: Updated