From 24ab288add1a64b8ed3dc02a2a64916fe99a9754 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Nov 2021 04:32:01 +0000 Subject: [PATCH 001/110] build(deps): bump wrapt from 1.13.2 to 1.13.3 in /tools/dependency (#18851) Bumps [wrapt](https://github.com/GrahamDumpleton/wrapt) from 1.13.2 to 1.13.3. - [Release notes](https://github.com/GrahamDumpleton/wrapt/releases) - [Changelog](https://github.com/GrahamDumpleton/wrapt/blob/develop/docs/changes.rst) - [Commits](https://github.com/GrahamDumpleton/wrapt/compare/1.13.2...1.13.3) --- updated-dependencies: - dependency-name: wrapt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/dependency/requirements.txt | 97 +++++++++++++++++-------------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/tools/dependency/requirements.txt b/tools/dependency/requirements.txt index b155ec330df65..53527b4a6d55d 100644 --- a/tools/dependency/requirements.txt +++ b/tools/dependency/requirements.txt @@ -132,49 +132,56 @@ urllib3==1.26.7 \ --hash=sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844 \ --hash=sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece # via requests -wrapt==1.13.2 \ - --hash=sha256:3de7b4d3066cc610054e7aa2c005645e308df2f92be730aae3a47d42e910566a \ - --hash=sha256:8164069f775c698d15582bf6320a4f308c50d048c1c10cf7d7a341feaccf5df7 \ - --hash=sha256:9adee1891253670575028279de8365c3a02d3489a74a66d774c321472939a0b1 \ - --hash=sha256:a70d876c9aba12d3bd7f8f1b05b419322c6789beb717044eea2c8690d35cb91b \ - --hash=sha256:3f87042623530bcffea038f824b63084180513c21e2e977291a9a7e65a66f13b \ - --hash=sha256:e634136f700a21e1fcead0c137f433dde928979538c14907640607d43537d468 \ - --hash=sha256:3e33c138d1e3620b1e0cc6fd21e46c266393ed5dae0d595b7ed5a6b73ed57aa0 \ - --hash=sha256:283e402e5357e104ac1e3fba5791220648e9af6fb14ad7d9cc059091af2b31d2 \ - --hash=sha256:ccb34ce599cab7f36a4c90318697ead18312c67a9a76327b3f4f902af8f68ea1 \ - --hash=sha256:fbad5ba74c46517e6488149514b2e2348d40df88cd6b52a83855b7a8bf04723f \ - --hash=sha256:724ed2bc9c91a2b9026e5adce310fa60c6e7c8760b03391445730b9789b9d108 \ - --hash=sha256:83f2793ec6f3ef513ad8d5b9586f5ee6081cad132e6eae2ecb7eac1cc3decae0 \ - --hash=sha256:0473d1558b93e314e84313cc611f6c86be779369f9d3734302bf185a4d2625b1 \ - --hash=sha256:15eee0e6fd07f48af2f66d0e6f2ff1916ffe9732d464d5e2390695296872cad9 \ - --hash=sha256:bc85d17d90201afd88e3d25421da805e4e135012b5d1f149e4de2981394b2a52 \ - --hash=sha256:c6ee5f8734820c21b9b8bf705e99faba87f21566d20626568eeb0d62cbeaf23c \ - --hash=sha256:53c6706a1bcfb6436f1625511b95b812798a6d2ccc51359cd791e33722b5ea32 \ - --hash=sha256:fbe6aebc9559fed7ea27de51c2bf5c25ba2a4156cf0017556f72883f2496ee9a \ - --hash=sha256:0582180566e7a13030f896c2f1ac6a56134ab5f3c3f4c5538086f758b1caf3f2 \ - --hash=sha256:bff0a59387a0a2951cb869251257b6553663329a1b5525b5226cab8c88dcbe7e \ - --hash=sha256:df3eae297a5f1594d1feb790338120f717dac1fa7d6feed7b411f87e0f2401c7 \ - --hash=sha256:1eb657ed84f4d3e6ad648483c8a80a0cf0a78922ef94caa87d327e2e1ad49b48 \ - --hash=sha256:a0cdedf681db878416c05e1831ec69691b0e6577ac7dca9d4f815632e3549580 \ - --hash=sha256:87ee3c73bdfb4367b26c57259995935501829f00c7b3eed373e2ad19ec21e4e4 \ - --hash=sha256:3e0d16eedc242d01a6f8cf0623e9cdc3b869329da3f97a15961d8864111d8cf0 \ - --hash=sha256:8318088860968c07e741537030b1abdd8908ee2c71fbe4facdaade624a09e006 \ - --hash=sha256:d90520616fce71c05dedeac3a0fe9991605f0acacd276e5f821842e454485a70 \ - --hash=sha256:22142afab65daffc95863d78effcbd31c19a8003eca73de59f321ee77f73cadb \ - --hash=sha256:d0d717e10f952df7ea41200c507cc7e24458f4c45b56c36ad418d2e79dacd1d4 \ - --hash=sha256:593cb049ce1c391e0288523b30426c4430b26e74c7e6f6e2844bd99ac7ecc831 \ - --hash=sha256:8860c8011a6961a651b1b9f46fdbc589ab63b0a50d645f7d92659618a3655867 \ - --hash=sha256:ada5e29e59e2feb710589ca1c79fd989b1dd94d27079dc1d199ec954a6ecc724 \ - --hash=sha256:fdede980273aeca591ad354608778365a3a310e0ecdd7a3587b38bc5be9b1808 \ - --hash=sha256:af9480de8e63c5f959a092047aaf3d7077422ded84695b3398f5d49254af3e90 \ - --hash=sha256:c65e623ea7556e39c4f0818200a046cbba7575a6b570ff36122c276fdd30ab0a \ - --hash=sha256:b20703356cae1799080d0ad15085dc3213c1ac3f45e95afb9f12769b98231528 \ - --hash=sha256:1c5c4cf188b5643a97e87e2110bbd4f5bc491d54a5b90633837b34d5df6a03fe \ - --hash=sha256:82223f72eba6f63eafca87a0f614495ae5aa0126fe54947e2b8c023969e9f2d7 \ - --hash=sha256:81a4cf257263b299263472d669692785f9c647e7dca01c18286b8f116dbf6b38 \ - --hash=sha256:728e2d9b7a99dd955d3426f237b940fc74017c4a39b125fec913f575619ddfe9 \ - --hash=sha256:7574de567dcd4858a2ffdf403088d6df8738b0e1eabea220553abf7c9048f59e \ - --hash=sha256:c7ac2c7a8e34bd06710605b21dd1f3576764443d68e069d2afba9b116014d072 \ - --hash=sha256:6e6d1a8eeef415d7fb29fe017de0e48f45e45efd2d1bfda28fc50b7b330859ef \ - --hash=sha256:dca56cc5963a5fd7c2aa8607017753f534ee514e09103a6c55d2db70b50e7447 +wrapt==1.13.3 \ + --hash=sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a \ + --hash=sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489 \ + --hash=sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909 \ + --hash=sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229 \ + --hash=sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af \ + --hash=sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de \ + --hash=sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb \ + --hash=sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80 \ + --hash=sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca \ + --hash=sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44 \ + --hash=sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056 \ + --hash=sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785 \ + --hash=sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096 \ + --hash=sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33 \ + --hash=sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f \ + --hash=sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e \ + --hash=sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d \ + --hash=sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179 \ + --hash=sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3 \ + --hash=sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755 \ + --hash=sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851 \ + --hash=sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13 \ + --hash=sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918 \ + --hash=sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade \ + --hash=sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc \ + --hash=sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf \ + --hash=sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125 \ + --hash=sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36 \ + --hash=sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10 \ + --hash=sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068 \ + --hash=sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709 \ + --hash=sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df \ + --hash=sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2 \ + --hash=sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b \ + --hash=sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829 \ + --hash=sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea \ + --hash=sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9 \ + --hash=sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554 \ + --hash=sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c \ + --hash=sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b \ + --hash=sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce \ + --hash=sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79 \ + --hash=sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb \ + --hash=sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb \ + --hash=sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32 \ + --hash=sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7 \ + --hash=sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e \ + --hash=sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640 \ + --hash=sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374 \ + --hash=sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb \ + --hash=sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185 # via deprecated From dbb1f50ef340e565c5a495a17aac0afff5d9a4f9 Mon Sep 17 00:00:00 2001 From: Peter Jausovec Date: Mon, 1 Nov 2021 22:05:12 -0700 Subject: [PATCH 002/110] Fix typo (remove extra word) (#18855) Docs Changes: Removes an extra word Signed-off-by: Peter Jausovec --- api/envoy/extensions/formatter/metadata/v3/metadata.proto | 2 +- docs/root/configuration/observability/access_log/usage.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/envoy/extensions/formatter/metadata/v3/metadata.proto b/api/envoy/extensions/formatter/metadata/v3/metadata.proto index 9b110a4893812..86b5053b1fdde 100644 --- a/api/envoy/extensions/formatter/metadata/v3/metadata.proto +++ b/api/envoy/extensions/formatter/metadata/v3/metadata.proto @@ -26,7 +26,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // :ref:`Metadata ` info, // where TYPE is type of metadata (see above for supported types), // NAMESPACE is the filter namespace used when setting the metadata, KEY is an optional -// lookup up key in the namespace with the option of specifying nested keys separated by ':', +// lookup key in the namespace with the option of specifying nested keys separated by ':', // and Z is an optional parameter denoting string truncation up to Z characters long. // The data will be logged as a JSON string. For example, for the following ROUTE metadata: // diff --git a/docs/root/configuration/observability/access_log/usage.rst b/docs/root/configuration/observability/access_log/usage.rst index 383e6e12b1a84..1b6ba004befef 100644 --- a/docs/root/configuration/observability/access_log/usage.rst +++ b/docs/root/configuration/observability/access_log/usage.rst @@ -506,7 +506,7 @@ The following command operators are supported: HTTP :ref:`Dynamic Metadata ` info, where NAMESPACE is the filter namespace used when setting the metadata, KEY is an optional - lookup up key in the namespace with the option of specifying nested keys separated by ':', + lookup key in the namespace with the option of specifying nested keys separated by ':', and Z is an optional parameter denoting string truncation up to Z characters long. Dynamic Metadata can be set by filters using the :repo:`StreamInfo ` API: *setDynamicMetadata*. The data will be logged as a JSON string. For example, for the following dynamic metadata: @@ -541,7 +541,7 @@ The following command operators are supported: HTTP :ref:`Upstream cluster Metadata ` info, where NAMESPACE is the filter namespace used when setting the metadata, KEY is an optional - lookup up key in the namespace with the option of specifying nested keys separated by ':', + lookup key in the namespace with the option of specifying nested keys separated by ':', and Z is an optional parameter denoting string truncation up to Z characters long. The data will be logged as a JSON string. For example, for the following dynamic metadata: From 8b8ece8adb8c9282cbd9405151993fa1bc5cebee Mon Sep 17 00:00:00 2001 From: Zhiyong Yang Date: Tue, 2 Nov 2021 13:09:06 +0800 Subject: [PATCH 003/110] [Bandwidth-Limit] Add response trailers for bandwidth delay (#18267) - Add response trailers for the bandwidth limit filter delays. - Add new metric request_enforced and response_enforced. - Change the following metrics type from Gauge to Counter (request_incoming_size, response_incoming_size, request_allowed_size, response_allowed_size) to better calculate the network bytes per second. This is useful when downstream want to understand how much delays that is caused by bandwidth limit filter. Risk Level: Low Testing: UT added. Docs Changes: Updated. Release Notes: Added. Signed-off-by: gayang --- .../bandwidth_limit/v3/bandwidth_limit.proto | 17 ++++- .../http_filters/bandwidth_limit_filter.rst | 2 + docs/root/version_history/current.rst | 2 + .../http/bandwidth_limit/bandwidth_limit.cc | 66 +++++++++++++++++-- .../http/bandwidth_limit/bandwidth_limit.h | 10 +++ .../http/common/stream_rate_limiter.cc | 14 +++- .../filters/http/common/stream_rate_limiter.h | 10 +-- .../filters/http/fault/fault_filter.cc | 2 +- .../http/bandwidth_limit/config_test.cc | 15 ++++- .../http/bandwidth_limit/filter_test.cc | 48 +++++++++++++- .../http/common/stream_rate_limiter_test.cc | 4 +- 11 files changed, 168 insertions(+), 22 deletions(-) diff --git a/api/envoy/extensions/filters/http/bandwidth_limit/v3/bandwidth_limit.proto b/api/envoy/extensions/filters/http/bandwidth_limit/v3/bandwidth_limit.proto index c512d541aaefc..8c4d1b7b208ca 100644 --- a/api/envoy/extensions/filters/http/bandwidth_limit/v3/bandwidth_limit.proto +++ b/api/envoy/extensions/filters/http/bandwidth_limit/v3/bandwidth_limit.proto @@ -19,7 +19,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // Bandwidth limit :ref:`configuration overview `. // [#extension: envoy.filters.http.bandwidth_limit] -// [#next-free-field: 6] +// [#next-free-field: 8] message BandwidthLimit { // Defines the mode for the bandwidth limit filter. // Values represent bitmask. @@ -66,4 +66,19 @@ message BandwidthLimit { // Runtime flag that controls whether the filter is enabled or not. If not specified, defaults // to enabled. config.core.v3.RuntimeFeatureFlag runtime_enabled = 5; + + // Enable response trailers. + // + // .. note:: + // + // * If set true, the response trailers *bandwidth-request-delay-ms* and *bandwidth-response-delay-ms* will be added, prefixed by *response_trailer_prefix*. + // * bandwidth-request-delay-ms: delay time in milliseconds it took for the request stream transfer. + // * bandwidth-response-delay-ms: delay time in milliseconds it took for the response stream transfer. + // * If :ref:`enable_mode ` is DISABLED or REQUEST, the trailers will not be set. + // * If both the request and response delay time is 0, the trailers will not be set. + // + bool enable_response_trailers = 6; + + // Optional The prefix for the response trailers. + string response_trailer_prefix = 7; } diff --git a/docs/root/configuration/http/http_filters/bandwidth_limit_filter.rst b/docs/root/configuration/http/http_filters/bandwidth_limit_filter.rst index fa0b6d27b96c6..57072ac10a8de 100644 --- a/docs/root/configuration/http/http_filters/bandwidth_limit_filter.rst +++ b/docs/root/configuration/http/http_filters/bandwidth_limit_filter.rst @@ -42,11 +42,13 @@ The HTTP bandwidth limit filter outputs statistics in the ``.http_b :widths: 1, 1, 2 request_enabled, Counter, Total number of request streams for which the bandwidth limiter was consulted + request_enforced, Counter, Total number of request streams for which the bandwidth limiter was enforced request_pending, GAUGE, Number of request streams which are currently pending transfer in bandwidth limiter request_incoming_size, GAUGE, Size in bytes of incoming request data to bandwidth limiter request_allowed_size, GAUGE, Size in bytes of outgoing request data from bandwidth limiter request_transfer_duration, HISTOGRAM, Total time (including added delay) it took for the request stream transfer response_enabled, Counter, Total number of response streams for which the bandwidth limiter was consulted + response_enforced, Counter, Total number of response streams for which the bandwidth limiter was enforced response_pending, GAUGE, Number of response streams which are currently pending transfer in bandwidth limiter response_incoming_size, GAUGE, Size in bytes of incoming response data to bandwidth limiter response_allowed_size, GAUGE, Size in bytes of outgoing response data from bandwidth limiter diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 16fed757edc2c..dfb3386834b69 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -11,6 +11,8 @@ Minor Behavior Changes ---------------------- *Changes that may cause incompatibilities for some users, but should not for most* +* bandwidth_limit: added :ref:`response trailers ` when request or response delay are enforced. +* bandwidth_limit: added :ref:`bandwidth limit stats ` *request_enforced* and *response_enforced*. * config: the log message for "gRPC config stream closed" now uses the most recent error message, and reports seconds instead of milliseconds for how long the most recent status has been received. * dns: now respecting the returned DNS TTL for resolved hosts, rather than always relying on the hard-coded :ref:`dns_refresh_rate. ` This behavior can be temporarily reverted by setting the runtime guard ``envoy.reloadable_features.use_dns_ttl`` to false. * listener: destroy per network filter chain stats when a network filter chain is removed during the listener in place update. diff --git a/source/extensions/filters/http/bandwidth_limit/bandwidth_limit.cc b/source/extensions/filters/http/bandwidth_limit/bandwidth_limit.cc index 7da4185786293..2e7178d5a26ab 100644 --- a/source/extensions/filters/http/bandwidth_limit/bandwidth_limit.cc +++ b/source/extensions/filters/http/bandwidth_limit/bandwidth_limit.cc @@ -16,6 +16,14 @@ namespace Extensions { namespace HttpFilters { namespace BandwidthLimitFilter { +namespace { +const Http::LowerCaseString DefaultRequestDelayTrailer = + Http::LowerCaseString("bandwidth-request-delay-ms"); +const Http::LowerCaseString DefaultResponseDelayTrailer = + Http::LowerCaseString("bandwidth-response-delay-ms"); +const std::chrono::milliseconds ZeroMilliseconds = std::chrono::milliseconds(0); +} // namespace + FilterConfig::FilterConfig(const BandwidthLimit& config, Stats::Scope& scope, Runtime::Loader& runtime, TimeSource& time_source, bool per_route) : runtime_(runtime), time_source_(time_source), enable_mode_(config.enable_mode()), @@ -23,7 +31,18 @@ FilterConfig::FilterConfig(const BandwidthLimit& config, Stats::Scope& scope, fill_interval_(std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT( config, fill_interval, StreamRateLimiter::DefaultFillInterval.count()))), enabled_(config.runtime_enabled(), runtime), - stats_(generateStats(config.stat_prefix(), scope)) { + stats_(generateStats(config.stat_prefix(), scope)), + request_delay_trailer_( + config.response_trailer_prefix().empty() + ? DefaultRequestDelayTrailer + : Http::LowerCaseString(absl::StrCat(config.response_trailer_prefix(), "-", + DefaultRequestDelayTrailer.get()))), + response_delay_trailer_( + config.response_trailer_prefix().empty() + ? DefaultResponseDelayTrailer + : Http::LowerCaseString(absl::StrCat(config.response_trailer_prefix(), "-", + DefaultResponseDelayTrailer.get()))), + enable_response_trailers_(config.enable_response_trailers()) { if (per_route && !config.has_limit_kbps()) { throw EnvoyException("bandwidthlimitfilter: limit must be set for per route filter config"); } @@ -64,7 +83,12 @@ Http::FilterHeadersStatus BandwidthLimiter::decodeHeaders(Http::RequestHeaderMap updateStatsOnDecodeFinish(); decoder_callbacks_->continueDecoding(); }, - [config](uint64_t len) { config.stats().request_allowed_size_.set(len); }, + [&config](uint64_t len, bool limit_enforced) { + config.stats().request_allowed_size_.set(len); + if (limit_enforced) { + config.stats().request_enforced_.inc(); + } + }, const_cast(&config)->timeSource(), decoder_callbacks_->dispatcher(), decoder_callbacks_->scope(), config.tokenBucket(), config.fillInterval()); } @@ -123,7 +147,12 @@ Http::FilterHeadersStatus BandwidthLimiter::encodeHeaders(Http::ResponseHeaderMa updateStatsOnEncodeFinish(); encoder_callbacks_->continueEncoding(); }, - [config](uint64_t len) { config.stats().response_allowed_size_.set(len); }, + [&config](uint64_t len, bool limit_enforced) { + config.stats().response_allowed_size_.set(len); + if (limit_enforced) { + config.stats().response_enforced_.inc(); + } + }, const_cast(&config)->timeSource(), encoder_callbacks_->dispatcher(), encoder_callbacks_->scope(), config.tokenBucket(), config.fillInterval()); } @@ -135,6 +164,14 @@ Http::FilterDataStatus BandwidthLimiter::encodeData(Buffer::Instance& data, bool if (response_limiter_ != nullptr) { const auto& config = getConfig(); + // Adds encoded trailers. May only be called in encodeData when end_stream is set to true. + // If upstream has trailers, addEncodedTrailers won't be called + bool trailer_added = false; + if (end_stream && config.enableResponseTrailers()) { + trailers_ = &encoder_callbacks_->addEncodedTrailers(); + trailer_added = true; + } + if (!response_latency_) { response_latency_ = std::make_unique( config.stats().response_transfer_duration_, @@ -143,15 +180,18 @@ Http::FilterDataStatus BandwidthLimiter::encodeData(Buffer::Instance& data, bool } config.stats().response_incoming_size_.set(data.length()); - response_limiter_->writeData(data, end_stream); + response_limiter_->writeData(data, end_stream, trailer_added); return Http::FilterDataStatus::StopIterationNoBuffer; } ENVOY_LOG(debug, "BandwidthLimiter : response_limiter not set"); return Http::FilterDataStatus::Continue; } -Http::FilterTrailersStatus BandwidthLimiter::encodeTrailers(Http::ResponseTrailerMap&) { +Http::FilterTrailersStatus +BandwidthLimiter::encodeTrailers(Http::ResponseTrailerMap& response_trailers) { if (response_limiter_ != nullptr) { + trailers_ = &response_trailers; + if (response_limiter_->onTrailers()) { return Http::FilterTrailersStatus::StopIteration; } else { @@ -164,6 +204,7 @@ Http::FilterTrailersStatus BandwidthLimiter::encodeTrailers(Http::ResponseTraile void BandwidthLimiter::updateStatsOnDecodeFinish() { if (request_latency_) { + request_duration_ = request_latency_.get()->elapsed(); request_latency_->complete(); request_latency_.reset(); getConfig().stats().request_pending_.dec(); @@ -172,9 +213,22 @@ void BandwidthLimiter::updateStatsOnDecodeFinish() { void BandwidthLimiter::updateStatsOnEncodeFinish() { if (response_latency_) { + const auto& config = getConfig(); + + if (config.enableResponseTrailers() && trailers_ != nullptr) { + auto response_duration = response_latency_.get()->elapsed(); + if (request_duration_ > ZeroMilliseconds) { + trailers_->setCopy(config.requestDelayTrailer(), std::to_string(request_duration_.count())); + } + if (response_duration > ZeroMilliseconds) { + trailers_->setCopy(config.responseDelayTrailer(), + std::to_string(response_duration.count())); + } + } + response_latency_->complete(); response_latency_.reset(); - getConfig().stats().response_pending_.dec(); + config.stats().response_pending_.dec(); } } diff --git a/source/extensions/filters/http/bandwidth_limit/bandwidth_limit.h b/source/extensions/filters/http/bandwidth_limit/bandwidth_limit.h index 876ef673e39f8..c7c2242a0258c 100644 --- a/source/extensions/filters/http/bandwidth_limit/bandwidth_limit.h +++ b/source/extensions/filters/http/bandwidth_limit/bandwidth_limit.h @@ -32,6 +32,8 @@ namespace BandwidthLimitFilter { #define ALL_BANDWIDTH_LIMIT_STATS(COUNTER, GAUGE, HISTOGRAM) \ COUNTER(request_enabled) \ COUNTER(response_enabled) \ + COUNTER(request_enforced) \ + COUNTER(response_enforced) \ GAUGE(request_pending, Accumulate) \ GAUGE(response_pending, Accumulate) \ GAUGE(request_incoming_size, Accumulate) \ @@ -70,6 +72,9 @@ class FilterConfig : public ::Envoy::Router::RouteSpecificFilterConfig { EnableMode enableMode() const { return enable_mode_; }; const std::shared_ptr tokenBucket() const { return token_bucket_; } std::chrono::milliseconds fillInterval() const { return fill_interval_; } + const Http::LowerCaseString& requestDelayTrailer() const { return request_delay_trailer_; } + const Http::LowerCaseString& responseDelayTrailer() const { return response_delay_trailer_; } + bool enableResponseTrailers() const { return enable_response_trailers_; } private: friend class FilterTest; @@ -85,6 +90,9 @@ class FilterConfig : public ::Envoy::Router::RouteSpecificFilterConfig { mutable BandwidthLimitStats stats_; // Filter chain's shared token bucket std::shared_ptr token_bucket_; + const Http::LowerCaseString request_delay_trailer_; + const Http::LowerCaseString response_delay_trailer_; + const bool enable_response_trailers_; }; using FilterConfigSharedPtr = std::shared_ptr; @@ -141,6 +149,8 @@ class BandwidthLimiter : public Http::StreamFilter, Logger::Loggable response_limiter_; Stats::TimespanPtr request_latency_; Stats::TimespanPtr response_latency_; + std::chrono::milliseconds request_duration_; + Http::ResponseTrailerMap* trailers_; }; } // namespace BandwidthLimitFilter diff --git a/source/extensions/filters/http/common/stream_rate_limiter.cc b/source/extensions/filters/http/common/stream_rate_limiter.cc index 6763adbeb2414..a4e215a5f1d03 100644 --- a/source/extensions/filters/http/common/stream_rate_limiter.cc +++ b/source/extensions/filters/http/common/stream_rate_limiter.cc @@ -17,7 +17,7 @@ StreamRateLimiter::StreamRateLimiter( uint64_t max_kbps, uint64_t max_buffered_data, std::function pause_data_cb, std::function resume_data_cb, std::function write_data_cb, std::function continue_cb, - std::function write_stats_cb, TimeSource& time_source, + std::function write_stats_cb, TimeSource& time_source, Event::Dispatcher& dispatcher, const ScopeTrackedObject& scope, std::shared_ptr token_bucket, std::chrono::milliseconds fill_interval) : fill_interval_(std::move(fill_interval)), write_data_cb_(write_data_cb), @@ -63,7 +63,7 @@ void StreamRateLimiter::onTokenTimer() { // Move the data to write into the output buffer with as little copying as possible. // NOTE: This might be moving zero bytes, but that should work fine. data_to_write.move(buffer_, bytes_to_write); - write_stats_cb_(bytes_to_write); + write_stats_cb_(bytes_to_write, buffer_.length() > 0); // If the buffer still contains data in it, we couldn't get enough tokens, so schedule the next // token available time. @@ -88,10 +88,18 @@ void StreamRateLimiter::onTokenTimer() { } } -void StreamRateLimiter::writeData(Buffer::Instance& incoming_buffer, bool end_stream) { +void StreamRateLimiter::writeData(Buffer::Instance& incoming_buffer, bool end_stream, + bool trailer_added) { auto len = incoming_buffer.length(); buffer_.move(incoming_buffer); saw_end_stream_ = end_stream; + // If trailer_added is true, set saw_trailers_ to true to continue encode trailers, added + // after buffer_.move to ensure buffer has data and won't invoke continue_cb_ before + // processing the data in last data frame. + if (trailer_added) { + saw_trailers_ = true; + } + ENVOY_LOG(debug, "StreamRateLimiter : got new {} bytes of data. token " "timer {} scheduled.", diff --git a/source/extensions/filters/http/common/stream_rate_limiter.h b/source/extensions/filters/http/common/stream_rate_limiter.h index b8aed9ac8a72a..42b6c21015f91 100644 --- a/source/extensions/filters/http/common/stream_rate_limiter.h +++ b/source/extensions/filters/http/common/stream_rate_limiter.h @@ -49,9 +49,9 @@ class StreamRateLimiter : Logger::Loggable { StreamRateLimiter(uint64_t max_kbps, uint64_t max_buffered_data, std::function pause_data_cb, std::function resume_data_cb, std::function write_data_cb, - std::function continue_cb, std::function write_stats_cb, - TimeSource& time_source, Event::Dispatcher& dispatcher, - const ScopeTrackedObject& scope, + std::function continue_cb, + std::function write_stats_cb, TimeSource& time_source, + Event::Dispatcher& dispatcher, const ScopeTrackedObject& scope, std::shared_ptr token_bucket = nullptr, std::chrono::milliseconds fill_interval = DefaultFillInterval); @@ -59,7 +59,7 @@ class StreamRateLimiter : Logger::Loggable { * Called by the stream to write data. All data writes happen asynchronously, the stream should * be stopped after this call (all data will be drained from incoming_buffer). */ - void writeData(Buffer::Instance& incoming_buffer, bool end_stream); + void writeData(Buffer::Instance& incoming_buffer, bool end_stream, bool trailer_added = false); /** * Called if the stream receives trailers. @@ -83,7 +83,7 @@ class StreamRateLimiter : Logger::Loggable { const std::chrono::milliseconds fill_interval_; const std::function write_data_cb_; const std::function continue_cb_; - const std::function write_stats_cb_; + const std::function write_stats_cb_; const ScopeTrackedObject& scope_; std::shared_ptr token_bucket_; Event::TimerPtr token_timer_; diff --git a/source/extensions/filters/http/fault/fault_filter.cc b/source/extensions/filters/http/fault/fault_filter.cc index 7499e47dd5b8c..809b396b2b0fc 100644 --- a/source/extensions/filters/http/fault/fault_filter.cc +++ b/source/extensions/filters/http/fault/fault_filter.cc @@ -212,7 +212,7 @@ void FaultFilter::maybeSetupResponseRateLimit(const Http::RequestHeaderMap& requ encoder_callbacks_->injectEncodedDataToFilterChain(data, end_stream); }, [this] { encoder_callbacks_->continueEncoding(); }, - [](uint64_t) { + [](uint64_t, bool) { // write stats callback. }, config_->timeSource(), decoder_callbacks_->dispatcher(), decoder_callbacks_->scope()); diff --git a/test/extensions/filters/http/bandwidth_limit/config_test.cc b/test/extensions/filters/http/bandwidth_limit/config_test.cc index 853a3ca8896ac..c7b66374d8f9b 100644 --- a/test/extensions/filters/http/bandwidth_limit/config_test.cc +++ b/test/extensions/filters/http/bandwidth_limit/config_test.cc @@ -37,6 +37,8 @@ TEST(Factory, RouteSpecificFilterConfig) { enable_mode: REQUEST_AND_RESPONSE limit_kbps: 10 fill_interval: 0.1s + enable_response_trailers: true + response_trailer_prefix: test )"; BandwidthLimitFilterConfig factory; @@ -53,6 +55,11 @@ TEST(Factory, RouteSpecificFilterConfig) { EXPECT_EQ(config->fillInterval().count(), 100); EXPECT_EQ(config->enableMode(), EnableMode::BandwidthLimit_EnableMode_REQUEST_AND_RESPONSE); EXPECT_FALSE(config->tokenBucket() == nullptr); + EXPECT_EQ(config->enableResponseTrailers(), true); + EXPECT_EQ(const_cast(config)->requestDelayTrailer(), + Http::LowerCaseString("test-bandwidth-request-delay-ms")); + EXPECT_EQ(const_cast(config)->responseDelayTrailer(), + Http::LowerCaseString("test-bandwidth-response-delay-ms")); } TEST(Factory, RouteSpecificFilterConfigDisabledByDefault) { @@ -77,7 +84,7 @@ TEST(Factory, RouteSpecificFilterConfigDisabledByDefault) { EXPECT_EQ(config->fillInterval().count(), 100); } -TEST(Factory, RouteSpecificFilterConfigDefaultFillInterval) { +TEST(Factory, RouteSpecificFilterConfigDefault) { const std::string config_yaml = R"( stat_prefix: test enable_mode: REQUEST_AND_RESPONSE @@ -96,6 +103,12 @@ TEST(Factory, RouteSpecificFilterConfigDefaultFillInterval) { const auto* config = dynamic_cast(route_config.get()); EXPECT_EQ(config->limit(), 10); EXPECT_EQ(config->fillInterval().count(), 50); + // default trailers + EXPECT_EQ(config->enableResponseTrailers(), false); + EXPECT_EQ(const_cast(config)->requestDelayTrailer(), + Http::LowerCaseString("bandwidth-request-delay-ms")); + EXPECT_EQ(const_cast(config)->responseDelayTrailer(), + Http::LowerCaseString("bandwidth-response-delay-ms")); } TEST(Factory, PerRouteConfigNoLimits) { diff --git a/test/extensions/filters/http/bandwidth_limit/filter_test.cc b/test/extensions/filters/http/bandwidth_limit/filter_test.cc index 5d052318111cf..338c53ffa6c40 100644 --- a/test/extensions/filters/http/bandwidth_limit/filter_test.cc +++ b/test/extensions/filters/http/bandwidth_limit/filter_test.cc @@ -11,6 +11,7 @@ using testing::_; using testing::AnyNumber; using testing::NiceMock; using testing::Return; +using testing::ReturnRef; namespace Envoy { namespace Extensions { @@ -57,6 +58,7 @@ class FilterTest : public testing::Test { Http::TestResponseTrailerMapImpl response_trailers_; Buffer::OwnedImpl data_; Event::SimulatedTimeSystem time_system_; + Http::TestResponseTrailerMapImpl trailers_; }; TEST_F(FilterTest, Disabled) { @@ -68,6 +70,7 @@ TEST_F(FilterTest, Disabled) { enable_mode: DISABLED limit_kbps: 10 fill_interval: 1s + enable_response_trailers: true )"; setup(fmt::format(config_yaml, "1")); @@ -75,11 +78,14 @@ TEST_F(FilterTest, Disabled) { EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); EXPECT_EQ(0U, findCounter("test.http_bandwidth_limit.request_enabled")); + EXPECT_EQ(0U, findCounter("test.http_bandwidth_limit.request_enforced")); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); EXPECT_EQ(0U, findCounter("test.http_bandwidth_limit.response_enabled")); + EXPECT_EQ(false, response_trailers_.has("bandwidth-request-delay-ms")); + EXPECT_EQ(false, response_trailers_.has("bandwidth-response-delay-ms")); } TEST_F(FilterTest, LimitOnDecode) { @@ -90,6 +96,8 @@ TEST_F(FilterTest, LimitOnDecode) { runtime_key: foo_key enable_mode: REQUEST limit_kbps: 1 + enable_response_trailers: true + response_trailer_prefix: test )"; setup(fmt::format(config_yaml, "1")); @@ -111,6 +119,7 @@ TEST_F(FilterTest, LimitOnDecode) { EXPECT_CALL(decoder_filter_callbacks_, injectDecodedDataToFilterChain(BufferStringEqual("hello"), false)); token_timer->invokeCallback(); + EXPECT_EQ(0, findCounter("test.http_bandwidth_limit.request_enforced")); EXPECT_EQ(5, findGauge("test.http_bandwidth_limit.request_allowed_size")); // Advance time by 1s which should refill all tokens. @@ -129,6 +138,7 @@ TEST_F(FilterTest, LimitOnDecode) { EXPECT_CALL(decoder_filter_callbacks_, injectDecodedDataToFilterChain(BufferStringEqual(std::string(1024, 'a')), false)); token_timer->invokeCallback(); + EXPECT_EQ(1, findCounter("test.http_bandwidth_limit.request_enforced")); EXPECT_EQ(1024, findGauge("test.http_bandwidth_limit.request_allowed_size")); EXPECT_EQ(1126, findGauge("test.http_bandwidth_limit.request_incoming_size")); @@ -138,6 +148,7 @@ TEST_F(FilterTest, LimitOnDecode) { EXPECT_CALL(decoder_filter_callbacks_, injectDecodedDataToFilterChain(BufferStringEqual(std::string(51, 'a')), false)); token_timer->invokeCallback(); + EXPECT_EQ(2, findCounter("test.http_bandwidth_limit.request_enforced")); EXPECT_EQ(51, findGauge("test.http_bandwidth_limit.request_allowed_size")); EXPECT_EQ(1126, findGauge("test.http_bandwidth_limit.request_incoming_size")); @@ -153,6 +164,7 @@ TEST_F(FilterTest, LimitOnDecode) { EXPECT_CALL(decoder_filter_callbacks_, injectDecodedDataToFilterChain(BufferStringEqual(std::string(51, 'a')), false)); token_timer->invokeCallback(); + EXPECT_EQ(3, findCounter("test.http_bandwidth_limit.request_enforced")); EXPECT_EQ(51, findGauge("test.http_bandwidth_limit.request_allowed_size")); // Fire timer, also advance time. No timer enable because there is nothing @@ -161,6 +173,7 @@ TEST_F(FilterTest, LimitOnDecode) { EXPECT_CALL(decoder_filter_callbacks_, injectDecodedDataToFilterChain(BufferStringEqual(std::string(51, 'b')), false)); token_timer->invokeCallback(); + EXPECT_EQ(3, findCounter("test.http_bandwidth_limit.request_enforced")); EXPECT_EQ(51, findGauge("test.http_bandwidth_limit.request_allowed_size")); // Advance time by 1s for a full refill. @@ -175,8 +188,11 @@ TEST_F(FilterTest, LimitOnDecode) { EXPECT_CALL(decoder_filter_callbacks_, injectDecodedDataToFilterChain(BufferStringEqual(std::string(1024, 'c')), true)); token_timer->invokeCallback(); + EXPECT_EQ(3, findCounter("test.http_bandwidth_limit.request_enforced")); EXPECT_EQ(1024, findGauge("test.http_bandwidth_limit.request_allowed_size")); EXPECT_EQ(0, findGauge("test.http_bandwidth_limit.request_pending")); + EXPECT_EQ(false, response_trailers_.has("test-bandwidth-request-delay-ms")); + EXPECT_EQ(false, response_trailers_.has("test-bandwidth-response-delay-ms")); filter_->onDestroy(); } @@ -189,10 +205,13 @@ TEST_F(FilterTest, LimitOnEncode) { runtime_key: foo_key enable_mode: RESPONSE limit_kbps: 1 + enable_response_trailers: true + response_trailer_prefix: test )"; setup(fmt::format(config_yaml, "1")); ON_CALL(encoder_filter_callbacks_, encoderBufferLimit()).WillByDefault(Return(1100)); + ON_CALL(encoder_filter_callbacks_, addEncodedTrailers()).WillByDefault(ReturnRef(trailers_)); Event::MockTimer* token_timer = new NiceMock(&encoder_filter_callbacks_.dispatcher_); @@ -215,6 +234,7 @@ TEST_F(FilterTest, LimitOnEncode) { EXPECT_CALL(encoder_filter_callbacks_, injectEncodedDataToFilterChain(BufferStringEqual("hello"), false)); token_timer->invokeCallback(); + EXPECT_EQ(0, findCounter("test.http_bandwidth_limit.response_enforced")); EXPECT_EQ(5, findGauge("test.http_bandwidth_limit.response_allowed_size")); // Advance time by 1s which should refill all tokens. @@ -233,6 +253,7 @@ TEST_F(FilterTest, LimitOnEncode) { injectEncodedDataToFilterChain(BufferStringEqual(std::string(1024, 'a')), false)); token_timer->invokeCallback(); EXPECT_EQ(1, findGauge("test.http_bandwidth_limit.response_pending")); + EXPECT_EQ(1, findCounter("test.http_bandwidth_limit.response_enforced")); EXPECT_EQ(1126, findGauge("test.http_bandwidth_limit.response_incoming_size")); EXPECT_EQ(1024, findGauge("test.http_bandwidth_limit.response_allowed_size")); @@ -242,6 +263,7 @@ TEST_F(FilterTest, LimitOnEncode) { EXPECT_CALL(encoder_filter_callbacks_, injectEncodedDataToFilterChain(BufferStringEqual(std::string(51, 'a')), false)); token_timer->invokeCallback(); + EXPECT_EQ(2, findCounter("test.http_bandwidth_limit.response_enforced")); EXPECT_EQ(51, findGauge("test.http_bandwidth_limit.response_allowed_size")); // Get new data with current data buffered, not end_stream. @@ -254,6 +276,7 @@ TEST_F(FilterTest, LimitOnEncode) { EXPECT_CALL(encoder_filter_callbacks_, injectEncodedDataToFilterChain(BufferStringEqual(std::string(51, 'a')), false)); token_timer->invokeCallback(); + EXPECT_EQ(3, findCounter("test.http_bandwidth_limit.response_enforced")); EXPECT_EQ(51, findGauge("test.http_bandwidth_limit.response_allowed_size")); // Fire timer, also advance time. No time enable because there is nothing @@ -262,6 +285,7 @@ TEST_F(FilterTest, LimitOnEncode) { EXPECT_CALL(encoder_filter_callbacks_, injectEncodedDataToFilterChain(BufferStringEqual(std::string(51, 'b')), false)); token_timer->invokeCallback(); + EXPECT_EQ(3, findCounter("test.http_bandwidth_limit.response_enforced")); EXPECT_EQ(51, findGauge("test.http_bandwidth_limit.response_allowed_size")); // Advance time by 1s for a full refill. @@ -274,11 +298,15 @@ TEST_F(FilterTest, LimitOnEncode) { EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, filter_->encodeData(data4, true)); EXPECT_EQ(1024, findGauge("test.http_bandwidth_limit.response_incoming_size")); EXPECT_CALL(encoder_filter_callbacks_, - injectEncodedDataToFilterChain(BufferStringEqual(std::string(1024, 'c')), true)); + injectEncodedDataToFilterChain(BufferStringEqual(std::string(1024, 'c')), false)); token_timer->invokeCallback(); EXPECT_EQ(0, findGauge("test.http_bandwidth_limit.response_pending")); + EXPECT_EQ(3, findCounter("test.http_bandwidth_limit.response_enforced")); EXPECT_EQ(1024, findGauge("test.http_bandwidth_limit.response_allowed_size")); + EXPECT_EQ(false, response_trailers_.has("test-bandwidth-request-delay-ms")); + EXPECT_EQ("2150", trailers_.get_("test-bandwidth-response-delay-ms")); + filter_->onDestroy(); } @@ -290,11 +318,14 @@ TEST_F(FilterTest, LimitOnDecodeAndEncode) { runtime_key: foo_key enable_mode: REQUEST_AND_RESPONSE limit_kbps: 1 + enable_response_trailers: true + response_trailer_prefix: test )"; setup(fmt::format(config_yaml, "1")); ON_CALL(decoder_filter_callbacks_, decoderBufferLimit()).WillByDefault(Return(1050)); ON_CALL(encoder_filter_callbacks_, encoderBufferLimit()).WillByDefault(Return(1100)); + ON_CALL(encoder_filter_callbacks_, addEncodedTrailers()).WillByDefault(ReturnRef(trailers_)); Event::MockTimer* request_timer = new NiceMock(&decoder_filter_callbacks_.dispatcher_); Event::MockTimer* response_timer = @@ -403,9 +434,13 @@ TEST_F(FilterTest, LimitOnDecodeAndEncode) { EXPECT_CALL(decoder_filter_callbacks_, injectDecodedDataToFilterChain(BufferStringEqual(std::string(51, 'd')), true)); EXPECT_CALL(encoder_filter_callbacks_, - injectEncodedDataToFilterChain(BufferStringEqual(std::string(960, 'e')), true)); - response_timer->invokeCallback(); + injectEncodedDataToFilterChain(BufferStringEqual(std::string(960, 'e')), false)); + EXPECT_CALL(encoder_filter_callbacks_, continueEncoding()); + request_timer->invokeCallback(); + response_timer->invokeCallback(); + EXPECT_EQ("2200", trailers_.get_("test-bandwidth-request-delay-ms")); + EXPECT_EQ("2200", trailers_.get_("test-bandwidth-response-delay-ms")); filter_->onDestroy(); } @@ -418,6 +453,7 @@ TEST_F(FilterTest, WithTrailers) { runtime_key: foo_key enable_mode: REQUEST_AND_RESPONSE limit_kbps: 1 + response_trailer_prefix: test )"; setup(fmt::format(config_yaml, "1")); @@ -479,6 +515,8 @@ TEST_F(FilterTest, WithTrailers) { injectEncodedDataToFilterChain(BufferStringEqual(std::string(5, 'e')), false)); response_timer->invokeCallback(); EXPECT_EQ(0, findGauge("test.http_bandwidth_limit.response_pending")); + EXPECT_EQ(false, response_trailers_.has("test-bandwidth-request-delay-ms")); + EXPECT_EQ(false, response_trailers_.has("test-bandwidth-response-delay-ms")); } TEST_F(FilterTest, WithTrailersNoEndStream) { @@ -489,6 +527,7 @@ TEST_F(FilterTest, WithTrailersNoEndStream) { runtime_key: foo_key enable_mode: REQUEST_AND_RESPONSE limit_kbps: 1 + enable_response_trailers: true )"; setup(fmt::format(config_yaml, "1")); @@ -550,6 +589,9 @@ TEST_F(FilterTest, WithTrailersNoEndStream) { EXPECT_EQ(1, findGauge("test.http_bandwidth_limit.response_pending")); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); EXPECT_EQ(0, findGauge("test.http_bandwidth_limit.response_pending")); + + EXPECT_EQ("50", response_trailers_.get_("bandwidth-request-delay-ms")); + EXPECT_EQ("150", response_trailers_.get_("bandwidth-response-delay-ms")); } } // namespace BandwidthLimitFilter diff --git a/test/extensions/filters/http/common/stream_rate_limiter_test.cc b/test/extensions/filters/http/common/stream_rate_limiter_test.cc index 97f18f5957ca1..7fc0aeca1c66a 100644 --- a/test/extensions/filters/http/common/stream_rate_limiter_test.cc +++ b/test/extensions/filters/http/common/stream_rate_limiter_test.cc @@ -40,7 +40,7 @@ class StreamRateLimiterTest : public testing::Test { decoder_callbacks_.injectDecodedDataToFilterChain(data, end_stream); }, [this] { decoder_callbacks_.continueDecoding(); }, - [](uint64_t /*len*/) { + [](uint64_t /*len*/, bool) { // config->stats().decode_allowed_size_.set(len); }, time_system_, decoder_callbacks_.dispatcher_, decoder_callbacks_.scope(), token_bucket, @@ -59,7 +59,7 @@ class StreamRateLimiterTest : public testing::Test { decoder_callbacks_.injectDecodedDataToFilterChain(data, end_stream); }, [this] { decoder_callbacks_.continueDecoding(); }, - [](uint64_t /*len*/) { + [](uint64_t /*len*/, bool) { // config->stats().decode_allowed_size_.set(len); }, time_system_, decoder_callbacks_.dispatcher_, decoder_callbacks_.scope()); From 676ec2ada2bfb70d27537bfb469a7ed9a2e2e5a5 Mon Sep 17 00:00:00 2001 From: Snow Pettersen Date: Tue, 2 Nov 2021 08:43:19 -0400 Subject: [PATCH 004/110] http: remove runtime flag preventing usage of the new matching api (#18609) Signed-off-by: Snow Pettersen --- .../http/http_filters/_include/composite.yaml | 8 ---- .../matching/_include/complicated.yaml | 8 ---- .../matching/_include/request_response.yaml | 8 ---- .../advanced/matching/_include/simple.yaml | 8 ---- docs/root/version_history/current.rst | 1 + source/common/http/match_wrapper/config.cc | 4 -- source/common/runtime/runtime_features.cc | 2 - test/common/http/match_wrapper/config_test.cc | 47 ------------------- test/config_test/config_test.cc | 7 --- .../composite_filter_integration_test.cc | 2 - .../extension_discovery_integration_test.cc | 2 - test/integration/integration_test.cc | 4 -- 12 files changed, 1 insertion(+), 100 deletions(-) diff --git a/docs/root/configuration/http/http_filters/_include/composite.yaml b/docs/root/configuration/http/http_filters/_include/composite.yaml index f7c4c4f0dbaea..42949658dfaa3 100644 --- a/docs/root/configuration/http/http_filters/_include/composite.yaml +++ b/docs/root/configuration/http/http_filters/_include/composite.yaml @@ -90,11 +90,3 @@ static_resources: socket_address: address: 127.0.0.1 port_value: 50051 - -layered_runtime: - layers: - - name: static-layer - static_layer: - envoy: - reloadable_features: - experimental_matching_api: true diff --git a/docs/root/intro/arch_overview/advanced/matching/_include/complicated.yaml b/docs/root/intro/arch_overview/advanced/matching/_include/complicated.yaml index 6111adfd23eea..22df44b44a741 100644 --- a/docs/root/intro/arch_overview/advanced/matching/_include/complicated.yaml +++ b/docs/root/intro/arch_overview/advanced/matching/_include/complicated.yaml @@ -90,11 +90,3 @@ static_resources: socket_address: address: 127.0.0.1 port_value: 8080 - -layered_runtime: - layers: - - name: static-layer - static_layer: - envoy: - reloadable_features: - experimental_matching_api: true diff --git a/docs/root/intro/arch_overview/advanced/matching/_include/request_response.yaml b/docs/root/intro/arch_overview/advanced/matching/_include/request_response.yaml index 5fc3a5c3e8ecb..195419353b63b 100644 --- a/docs/root/intro/arch_overview/advanced/matching/_include/request_response.yaml +++ b/docs/root/intro/arch_overview/advanced/matching/_include/request_response.yaml @@ -76,11 +76,3 @@ static_resources: socket_address: address: 127.0.0.1 port_value: 8080 - -layered_runtime: - layers: - - name: static-layer - static_layer: - envoy: - reloadable_features: - experimental_matching_api: true diff --git a/docs/root/intro/arch_overview/advanced/matching/_include/simple.yaml b/docs/root/intro/arch_overview/advanced/matching/_include/simple.yaml index 1433fa75d1089..11276dcf7d57b 100644 --- a/docs/root/intro/arch_overview/advanced/matching/_include/simple.yaml +++ b/docs/root/intro/arch_overview/advanced/matching/_include/simple.yaml @@ -64,11 +64,3 @@ static_resources: socket_address: address: 127.0.0.1 port_value: 8080 - -layered_runtime: - layers: - - name: static-layer - static_layer: - envoy: - reloadable_features: - experimental_matching_api: true diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index dfb3386834b69..51da2dda37cf6 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -15,6 +15,7 @@ Minor Behavior Changes * bandwidth_limit: added :ref:`bandwidth limit stats ` *request_enforced* and *response_enforced*. * config: the log message for "gRPC config stream closed" now uses the most recent error message, and reports seconds instead of milliseconds for how long the most recent status has been received. * dns: now respecting the returned DNS TTL for resolved hosts, rather than always relying on the hard-coded :ref:`dns_refresh_rate. ` This behavior can be temporarily reverted by setting the runtime guard ``envoy.reloadable_features.use_dns_ttl`` to false. +* http: usage of the experimental matching API is no longer guarded behind a feature flag, as the corresponding protobuf fields have been marked as WIP. * listener: destroy per network filter chain stats when a network filter chain is removed during the listener in place update. * quic: add back the support for IETF draft 29 which is guarded via ``envoy.reloadable_features.FLAGS_quic_reloadable_flag_quic_disable_version_draft_29``. It is off by default so Envoy only supports RFCv1 without flipping this runtime guard explicitly. Draft 29 is not recommended for use. diff --git a/source/common/http/match_wrapper/config.cc b/source/common/http/match_wrapper/config.cc index 2292ca4e2d6ff..f727a18072c03 100644 --- a/source/common/http/match_wrapper/config.cc +++ b/source/common/http/match_wrapper/config.cc @@ -90,10 +90,6 @@ Envoy::Http::FilterFactoryCb MatchWrapperConfig::createFilterFactoryFromProtoTyp const envoy::extensions::common::matching::v3::ExtensionWithMatcher& proto_config, const std::string& prefix, Server::Configuration::FactoryContext& context) { - if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.experimental_matching_api")) { - throw EnvoyException("Experimental matching API is not enabled"); - } - ASSERT(proto_config.has_extension_config()); auto& factory = Config::Utility::getAndCheckFactory( diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 4b060373c4e5b..5ffe167627d75 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -111,8 +111,6 @@ constexpr const char* disabled_runtime_features[] = { "envoy.reloadable_features.remove_legacy_json", // Sentinel and test flag. "envoy.reloadable_features.test_feature_false", - // Allows the use of ExtensionWithMatcher to wrap a HTTP filter with a match tree. - "envoy.reloadable_features.experimental_matching_api", // When the runtime is flipped to true, use shared cache in getOrCreateRawAsyncClient method if // CacheOption is CacheWhenRuntimeEnabled. // Caller that use AlwaysCache option will always cache, unaffected by this runtime. diff --git a/test/common/http/match_wrapper/config_test.cc b/test/common/http/match_wrapper/config_test.cc index 8287d9e5fd2d0..79fca551a4132 100644 --- a/test/common/http/match_wrapper/config_test.cc +++ b/test/common/http/match_wrapper/config_test.cc @@ -48,42 +48,7 @@ struct TestFactory : public Envoy::Server::Configuration::NamedHttpFilterConfigF } }; -TEST(MatchWrapper, DisabledByDefault) { - NiceMock factory_context; - - const auto config = - TestUtility::parseYaml(R"EOF( -extension_config: - name: test - typed_config: - "@type": type.googleapis.com/google.protobuf.StringValue -xds_matcher: - matcher_tree: - input: - name: request-headers - typed_config: - "@type": type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput - header_name: default-matcher-header - exact_match_map: - map: - match: - action: - name: skip - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.common.matcher.action.v3.SkipFilter -)EOF"); - - MatchWrapperConfig match_wrapper_config; - EXPECT_THROW_WITH_MESSAGE( - match_wrapper_config.createFilterFactoryFromProto(config, "", factory_context), - EnvoyException, "Experimental matching API is not enabled"); -} - TEST(MatchWrapper, WithMatcher) { - TestScopedRuntime scoped_runtime; - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.experimental_matching_api", "true"}}); - TestFactory test_factory; Envoy::Registry::InjectFactory inject_factory(test_factory); @@ -131,10 +96,6 @@ TEST(MatchWrapper, WithMatcher) { } TEST(MatchWrapper, DEPRECATED_FEATURE_TEST(WithDeprecatedMatcher)) { - TestScopedRuntime scoped_runtime; - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.experimental_matching_api", "true"}}); - TestFactory test_factory; Envoy::Registry::InjectFactory inject_factory(test_factory); @@ -182,10 +143,6 @@ TEST(MatchWrapper, DEPRECATED_FEATURE_TEST(WithDeprecatedMatcher)) { } TEST(MatchWrapper, WithNoMatcher) { - TestScopedRuntime scoped_runtime; - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.experimental_matching_api", "true"}}); - TestFactory test_factory; Envoy::Registry::InjectFactory inject_factory(test_factory); @@ -207,10 +164,6 @@ TEST(MatchWrapper, WithNoMatcher) { } TEST(MatchWrapper, WithMatcherInvalidDataInput) { - TestScopedRuntime scoped_runtime; - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.experimental_matching_api", "true"}}); - TestFactory test_factory; Envoy::Registry::InjectFactory inject_factory(test_factory); diff --git a/test/config_test/config_test.cc b/test/config_test/config_test.cc index d6f8d800f9b66..b323a1938f8c0 100644 --- a/test/config_test/config_test.cc +++ b/test/config_test/config_test.cc @@ -86,13 +86,6 @@ class ConfigTest { ON_CALL(server_.runtime_loader_.snapshot_, deprecatedFeatureEnabled(_, _)) .WillByDefault(Invoke([](absl::string_view, bool default_value) { return default_value; })); - // TODO(snowp): There's no way to override runtime flags per example file (since we mock out the - // runtime loader), so temporarily enable this flag explicitly here until we flip the default. - // This should allow the existing configuration examples to continue working despite the feature - // being disabled by default. - ON_CALL(*snapshot_, - runtimeFeatureEnabled("envoy.reloadable_features.experimental_matching_api")) - .WillByDefault(Return(true)); ON_CALL(server_.runtime_loader_, threadsafeSnapshot()).WillByDefault(Invoke([this]() { return snapshot_; })); diff --git a/test/extensions/filters/http/composite/composite_filter_integration_test.cc b/test/extensions/filters/http/composite/composite_filter_integration_test.cc index 551112736898c..30311497e3075 100644 --- a/test/extensions/filters/http/composite/composite_filter_integration_test.cc +++ b/test/extensions/filters/http/composite/composite_filter_integration_test.cc @@ -14,8 +14,6 @@ class CompositeFilterIntegrationTest : public testing::TestWithParam Date: Tue, 2 Nov 2021 08:27:24 -0700 Subject: [PATCH 005/110] Windows specific `io_handle` (#18738) In preparation for #18305, I split the implementation of io_socket_handle on different platforms to make the code more readable and separated. The new Windows class leverages the common implementation and now is responsible for registering the events. This will allow us to cleanly implement MSG_PEEK by read in a consequent PR in a self-contained way. Signed-off-by: Sotiris Nanopoulos --- source/common/network/BUILD | 2 + .../common/network/io_socket_handle_impl.cc | 94 +----- .../common/network/socket_interface_impl.cc | 11 +- source/common/network/socket_interface_impl.h | 3 + .../network/win32_socket_handle_impl.cc | 88 +++++ .../common/network/win32_socket_handle_impl.h | 49 +++ test/common/buffer/owned_impl_test.cc | 310 +++++++++--------- test/common/network/connection_impl_test.cc | 45 ++- .../common/network/listen_socket_impl_test.cc | 4 +- test/integration/filters/BUILD | 1 + .../filters/test_socket_interface.cc | 2 +- .../filters/test_socket_interface.h | 10 +- test/test_common/network_utility.h | 8 + 13 files changed, 371 insertions(+), 256 deletions(-) create mode 100644 source/common/network/win32_socket_handle_impl.cc create mode 100644 source/common/network/win32_socket_handle_impl.h diff --git a/source/common/network/BUILD b/source/common/network/BUILD index b75b299dc7c8f..98af3afd269a2 100644 --- a/source/common/network/BUILD +++ b/source/common/network/BUILD @@ -184,10 +184,12 @@ envoy_cc_library( srcs = [ "io_socket_handle_impl.cc", "socket_interface_impl.cc", + "win32_socket_handle_impl.cc", ], hdrs = [ "io_socket_handle_impl.h", "socket_interface_impl.h", + "win32_socket_handle_impl.h", ], deps = [ ":address_lib", diff --git a/source/common/network/io_socket_handle_impl.cc b/source/common/network/io_socket_handle_impl.cc index 452f57711b5e4..020bd4b55c474 100644 --- a/source/common/network/io_socket_handle_impl.cc +++ b/source/common/network/io_socket_handle_impl.cc @@ -6,6 +6,7 @@ #include "source/common/common/utility.h" #include "source/common/event/file_event_impl.h" #include "source/common/network/address_impl.h" +#include "source/common/network/socket_interface_impl.h" #include "absl/container/fixed_array.h" #include "absl/types/optional.h" @@ -96,15 +97,6 @@ Api::IoCallUint64Result IoSocketHandleImpl::readv(uint64_t max_length, Buffer::R ASSERT(num_bytes_to_read <= max_length); auto result = sysCallResultToIoCallResult(Api::OsSysCallsSingleton::get().readv( fd_, iov.begin(), static_cast(num_slices_to_read))); - - // Emulated edge events need to registered if the socket operation did not complete - // because the socket would block. - if constexpr (Event::PlatformDefaultTriggerType == Event::FileTriggerType::EmulatedEdge) { - // Some tests try to read without initializing the file_event. - if (result.wouldBlock() && file_event_) { - file_event_->registerEventIfEmulatedEdge(Event::FileReadyType::Read); - } - } return result; } @@ -120,15 +112,6 @@ Api::IoCallUint64Result IoSocketHandleImpl::read(Buffer::Instance& buffer, uint64_t bytes_to_commit = result.ok() ? result.return_value_ : 0; ASSERT(bytes_to_commit <= max_length); reservation.commit(bytes_to_commit); - - // Emulated edge events need to registered if the socket operation did not complete - // because the socket would block. - if constexpr (Event::PlatformDefaultTriggerType == Event::FileTriggerType::EmulatedEdge) { - // Some tests try to read without initializing the file_event. - if (result.wouldBlock() && file_event_) { - file_event_->registerEventIfEmulatedEdge(Event::FileReadyType::Read); - } - } return result; } @@ -148,15 +131,6 @@ Api::IoCallUint64Result IoSocketHandleImpl::writev(const Buffer::RawSlice* slice } auto result = sysCallResultToIoCallResult( Api::OsSysCallsSingleton::get().writev(fd_, iov.begin(), num_slices_to_write)); - - // Emulated edge events need to registered if the socket operation did not complete - // because the socket would block. - if constexpr (Event::PlatformDefaultTriggerType == Event::FileTriggerType::EmulatedEdge) { - // Some tests try to write without initializing the file_event. - if (result.wouldBlock() && file_event_) { - file_event_->registerEventIfEmulatedEdge(Event::FileReadyType::Write); - } - } return result; } @@ -167,15 +141,6 @@ Api::IoCallUint64Result IoSocketHandleImpl::write(Buffer::Instance& buffer) { if (result.ok() && result.return_value_ > 0) { buffer.drain(static_cast(result.return_value_)); } - - // Emulated edge events need to registered if the socket operation did not complete - // because the socket would block. - if constexpr (Event::PlatformDefaultTriggerType == Event::FileTriggerType::EmulatedEdge) { - // Some tests try to read without initializing the file_event. - if (result.wouldBlock() && file_event_) { - file_event_->registerEventIfEmulatedEdge(Event::FileReadyType::Write); - } - } return result; } @@ -213,15 +178,7 @@ Api::IoCallUint64Result IoSocketHandleImpl::sendmsg(const Buffer::RawSlice* slic message.msg_control = nullptr; message.msg_controllen = 0; const Api::SysCallSizeResult result = os_syscalls.sendmsg(fd_, &message, flags); - auto io_result = sysCallResultToIoCallResult(result); - // Emulated edge events need to registered if the socket operation did not complete - // because the socket would block. - if constexpr (Event::PlatformDefaultTriggerType == Event::FileTriggerType::EmulatedEdge) { - if (io_result.wouldBlock() && file_event_) { - file_event_->registerEventIfEmulatedEdge(Event::FileReadyType::Write); - } - } - return io_result; + return sysCallResultToIoCallResult(result); } else { const size_t space_v6 = CMSG_SPACE(sizeof(in6_pktinfo)); const size_t space_v4 = CMSG_SPACE(sizeof(in_pktinfo)); @@ -263,15 +220,7 @@ Api::IoCallUint64Result IoSocketHandleImpl::sendmsg(const Buffer::RawSlice* slic *(reinterpret_cast(pktinfo->ipi6_addr.s6_addr)) = self_ip->ipv6()->address(); } const Api::SysCallSizeResult result = os_syscalls.sendmsg(fd_, &message, flags); - auto io_result = sysCallResultToIoCallResult(result); - // Emulated edge events need to registered if the socket operation did not complete - // because the socket would block. - if constexpr (Event::PlatformDefaultTriggerType == Event::FileTriggerType::EmulatedEdge) { - if (io_result.wouldBlock() && file_event_) { - file_event_->registerEventIfEmulatedEdge(Event::FileReadyType::Write); - } - } - return io_result; + return sysCallResultToIoCallResult(result); } } @@ -343,15 +292,7 @@ Api::IoCallUint64Result IoSocketHandleImpl::recvmsg(Buffer::RawSlice* slices, Api::SysCallSizeResult result = Api::OsSysCallsSingleton::get().recvmsg(fd_, &hdr, messageTruncatedOption()); if (result.return_value_ < 0) { - auto io_result = sysCallResultToIoCallResult(result); - // Emulated edge events need to registered if the socket operation did not complete - // because the socket would block. - if constexpr (Event::PlatformDefaultTriggerType == Event::FileTriggerType::EmulatedEdge) { - if (io_result.wouldBlock() && file_event_) { - file_event_->registerEventIfEmulatedEdge(Event::FileReadyType::Read); - } - } - return io_result; + return sysCallResultToIoCallResult(result); } if ((hdr.msg_flags & MSG_TRUNC) != 0) { ENVOY_LOG_MISC(debug, "Dropping truncated UDP packet with size: {}.", result.return_value_); @@ -442,15 +383,7 @@ Api::IoCallUint64Result IoSocketHandleImpl::recvmmsg(RawSliceArrays& slices, uin messageTruncatedOption() | MSG_WAITFORONE, nullptr); if (result.return_value_ <= 0) { - auto io_result = sysCallResultToIoCallResult(result); - // Emulated edge events need to registered if the socket operation did not complete - // because the socket would block. - if constexpr (Event::PlatformDefaultTriggerType == Event::FileTriggerType::EmulatedEdge) { - if (io_result.wouldBlock() && file_event_) { - file_event_->registerEventIfEmulatedEdge(Event::FileReadyType::Read); - } - } - return io_result; + return sysCallResultToIoCallResult(result); } int num_packets_read = result.return_value_; @@ -504,15 +437,7 @@ Api::IoCallUint64Result IoSocketHandleImpl::recvmmsg(RawSliceArrays& slices, uin Api::IoCallUint64Result IoSocketHandleImpl::recv(void* buffer, size_t length, int flags) { const Api::SysCallSizeResult result = Api::OsSysCallsSingleton::get().recv(fd_, buffer, length, flags); - auto io_result = sysCallResultToIoCallResult(result); - // Emulated edge events need to registered if the socket operation did not complete - // because the socket would block. - if constexpr (Event::PlatformDefaultTriggerType == Event::FileTriggerType::EmulatedEdge) { - if (io_result.wouldBlock() && file_event_) { - file_event_->registerEventIfEmulatedEdge(Event::FileReadyType::Read); - } - } - return io_result; + return sysCallResultToIoCallResult(result); } bool IoSocketHandleImpl::supportsMmsg() const { @@ -536,8 +461,8 @@ IoHandlePtr IoSocketHandleImpl::accept(struct sockaddr* addr, socklen_t* addrlen if (SOCKET_INVALID(result.return_value_)) { return nullptr; } - - return std::make_unique(result.return_value_, socket_v6only_, domain_); + return SocketInterfaceImpl::makePlatformSpecificSocket(result.return_value_, socket_v6only_, + domain_); } Api::SysCallIntResult IoSocketHandleImpl::connect(Address::InstanceConstSharedPtr address) { @@ -571,7 +496,8 @@ IoHandlePtr IoSocketHandleImpl::duplicate() { RELEASE_ASSERT(result.return_value_ != -1, fmt::format("duplicate failed for '{}': ({}) {}", fd_, result.errno_, errorDetails(result.errno_))); - return std::make_unique(result.return_value_, socket_v6only_, domain_); + return SocketInterfaceImpl::makePlatformSpecificSocket(result.return_value_, socket_v6only_, + domain_); } absl::optional IoSocketHandleImpl::domain() { return domain_; } diff --git a/source/common/network/socket_interface_impl.cc b/source/common/network/socket_interface_impl.cc index f851e69791126..5b46cb1eaf666 100644 --- a/source/common/network/socket_interface_impl.cc +++ b/source/common/network/socket_interface_impl.cc @@ -7,13 +7,22 @@ #include "source/common/common/assert.h" #include "source/common/common/utility.h" #include "source/common/network/io_socket_handle_impl.h" +#include "source/common/network/win32_socket_handle_impl.h" namespace Envoy { namespace Network { +IoHandlePtr SocketInterfaceImpl::makePlatformSpecificSocket(int socket_fd, bool socket_v6only, + absl::optional domain) { + if constexpr (Event::PlatformDefaultTriggerType == Event::FileTriggerType::EmulatedEdge) { + return std::make_unique(socket_fd, socket_v6only, domain); + } + return std::make_unique(socket_fd, socket_v6only, domain); +} + IoHandlePtr SocketInterfaceImpl::makeSocket(int socket_fd, bool socket_v6only, absl::optional domain) const { - return std::make_unique(socket_fd, socket_v6only, domain); + return makePlatformSpecificSocket(socket_fd, socket_v6only, domain); } IoHandlePtr SocketInterfaceImpl::socket(Socket::Type socket_type, Address::Type addr_type, diff --git a/source/common/network/socket_interface_impl.h b/source/common/network/socket_interface_impl.h index 6759d9f9a5132..780c0dc97b71d 100644 --- a/source/common/network/socket_interface_impl.h +++ b/source/common/network/socket_interface_impl.h @@ -26,6 +26,9 @@ class SocketInterfaceImpl : public SocketInterfaceBase { return "envoy.extensions.network.socket_interface.default_socket_interface"; }; + static IoHandlePtr makePlatformSpecificSocket(int socket_fd, bool socket_v6only, + absl::optional domain); + protected: virtual IoHandlePtr makeSocket(int socket_fd, bool socket_v6only, absl::optional domain) const; diff --git a/source/common/network/win32_socket_handle_impl.cc b/source/common/network/win32_socket_handle_impl.cc new file mode 100644 index 0000000000000..c74f5144691b7 --- /dev/null +++ b/source/common/network/win32_socket_handle_impl.cc @@ -0,0 +1,88 @@ +#include "source/common/network/win32_socket_handle_impl.h" + +#include "envoy/buffer/buffer.h" + +#include "source/common/api/os_sys_calls_impl.h" +#include "source/common/common/utility.h" +#include "source/common/event/file_event_impl.h" +#include "source/common/network/address_impl.h" + +#include "absl/container/fixed_array.h" +#include "absl/types/optional.h" + +using Envoy::Api::SysCallIntResult; +using Envoy::Api::SysCallSizeResult; + +namespace Envoy { +namespace Network { + +Api::IoCallUint64Result Win32SocketHandleImpl::readv(uint64_t max_length, Buffer::RawSlice* slices, + uint64_t num_slice) { + auto result = IoSocketHandleImpl::readv(max_length, slices, num_slice); + reEnableEventBasedOnIOResult(result, Event::FileReadyType::Read); + return result; +} + +Api::IoCallUint64Result Win32SocketHandleImpl::read(Buffer::Instance& buffer, + absl::optional max_length_opt) { + auto result = IoSocketHandleImpl::read(buffer, max_length_opt); + reEnableEventBasedOnIOResult(result, Event::FileReadyType::Read); + return result; +} + +Api::IoCallUint64Result Win32SocketHandleImpl::writev(const Buffer::RawSlice* slices, + uint64_t num_slice) { + auto result = IoSocketHandleImpl::writev(slices, num_slice); + reEnableEventBasedOnIOResult(result, Event::FileReadyType::Write); + return result; +} + +Api::IoCallUint64Result Win32SocketHandleImpl::write(Buffer::Instance& buffer) { + Api::IoCallUint64Result result = IoSocketHandleImpl::write(buffer); + reEnableEventBasedOnIOResult(result, Event::FileReadyType::Write); + return result; +} + +Api::IoCallUint64Result Win32SocketHandleImpl::sendmsg(const Buffer::RawSlice* slices, + uint64_t num_slice, int flags, + const Address::Ip* self_ip, + const Address::Instance& peer_address) { + + Api::IoCallUint64Result result = + IoSocketHandleImpl::sendmsg(slices, num_slice, flags, self_ip, peer_address); + reEnableEventBasedOnIOResult(result, Event::FileReadyType::Write); + return result; +} + +Api::IoCallUint64Result Win32SocketHandleImpl::recvmsg(Buffer::RawSlice* slices, + const uint64_t num_slice, uint32_t self_port, + RecvMsgOutput& output) { + Api::IoCallUint64Result result = + IoSocketHandleImpl::recvmsg(slices, num_slice, self_port, output); + reEnableEventBasedOnIOResult(result, Event::FileReadyType::Read); + return result; +} + +Api::IoCallUint64Result Win32SocketHandleImpl::recvmmsg(RawSliceArrays& slices, uint32_t self_port, + RecvMsgOutput& output) { + Api::IoCallUint64Result result = IoSocketHandleImpl::recvmmsg(slices, self_port, output); + reEnableEventBasedOnIOResult(result, Event::FileReadyType::Read); + return result; +} + +Api::IoCallUint64Result Win32SocketHandleImpl::recv(void* buffer, size_t length, int flags) { + + Api::IoCallUint64Result result = IoSocketHandleImpl::recv(buffer, length, flags); + reEnableEventBasedOnIOResult(result, Event::FileReadyType::Read); + return result; +} + +void Win32SocketHandleImpl::reEnableEventBasedOnIOResult(const Api::IoCallUint64Result& result, + uint32_t event) { + if (result.wouldBlock() && file_event_) { + file_event_->registerEventIfEmulatedEdge(event); + } +} + +} // namespace Network +} // namespace Envoy diff --git a/source/common/network/win32_socket_handle_impl.h b/source/common/network/win32_socket_handle_impl.h new file mode 100644 index 0000000000000..b9465f71db544 --- /dev/null +++ b/source/common/network/win32_socket_handle_impl.h @@ -0,0 +1,49 @@ +#pragma once + +#include "envoy/api/io_error.h" +#include "envoy/api/os_sys_calls.h" +#include "envoy/common/platform.h" +#include "envoy/event/dispatcher.h" +#include "envoy/network/io_handle.h" + +#include "source/common/common/logger.h" +#include "source/common/network/io_socket_error_impl.h" +#include "source/common/network/io_socket_handle_impl.h" + +namespace Envoy { +namespace Network { + +/** + * IoHandle derivative for win32 emulated edge sockets. + */ +class Win32SocketHandleImpl : public IoSocketHandleImpl { +public: + explicit Win32SocketHandleImpl(os_fd_t fd = INVALID_SOCKET, bool socket_v6only = false, + absl::optional domain = absl::nullopt) + : IoSocketHandleImpl(fd, socket_v6only, domain) {} + + Api::IoCallUint64Result readv(uint64_t max_length, Buffer::RawSlice* slices, + uint64_t num_slice) override; + Api::IoCallUint64Result read(Buffer::Instance& buffer, + absl::optional max_length) override; + + Api::IoCallUint64Result writev(const Buffer::RawSlice* slices, uint64_t num_slice) override; + + Api::IoCallUint64Result write(Buffer::Instance& buffer) override; + + Api::IoCallUint64Result sendmsg(const Buffer::RawSlice* slices, uint64_t num_slice, int flags, + const Address::Ip* self_ip, + const Address::Instance& peer_address) override; + + Api::IoCallUint64Result recvmsg(Buffer::RawSlice* slices, const uint64_t num_slice, + uint32_t self_port, RecvMsgOutput& output) override; + + Api::IoCallUint64Result recvmmsg(RawSliceArrays& slices, uint32_t self_port, + RecvMsgOutput& output) override; + Api::IoCallUint64Result recv(void* buffer, size_t length, int flags) override; + +private: + void reEnableEventBasedOnIOResult(const Api::IoCallUint64Result& result, uint32_t event); +}; +} // namespace Network +} // namespace Envoy diff --git a/test/common/buffer/owned_impl_test.cc b/test/common/buffer/owned_impl_test.cc index 854be0f4dd5e1..7a4adc7d3058c 100644 --- a/test/common/buffer/owned_impl_test.cc +++ b/test/common/buffer/owned_impl_test.cc @@ -4,6 +4,7 @@ #include "source/common/buffer/buffer_impl.h" #include "source/common/network/io_socket_handle_impl.h" +#include "source/common/network/win32_socket_handle_impl.h" #include "test/common/buffer/utility.h" #include "test/mocks/api/mocks.h" @@ -253,92 +254,6 @@ TEST_F(OwnedImplTest, PrependBuffer) { EXPECT_EQ(0, prefixBuffer.length()); } -TEST_F(OwnedImplTest, Write) { - Api::MockOsSysCalls os_sys_calls; - TestThreadsafeSingletonInjector os_calls(&os_sys_calls); - - Buffer::OwnedImpl buffer; - Network::IoSocketHandleImpl io_handle; - buffer.add("example"); - EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{7, 0})); - Api::IoCallUint64Result result = io_handle.write(buffer); - EXPECT_TRUE(result.ok()); - EXPECT_EQ(7, result.return_value_); - EXPECT_EQ(0, buffer.length()); - - buffer.add("example"); - EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{6, 0})); - result = io_handle.write(buffer); - EXPECT_TRUE(result.ok()); - EXPECT_EQ(6, result.return_value_); - EXPECT_EQ(1, buffer.length()); - - EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{0, 0})); - result = io_handle.write(buffer); - EXPECT_TRUE(result.ok()); - EXPECT_EQ(0, result.return_value_); - EXPECT_EQ(1, buffer.length()); - - EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{-1, 0})); - result = io_handle.write(buffer); - EXPECT_EQ(Api::IoError::IoErrorCode::UnknownError, result.err_->getErrorCode()); - EXPECT_EQ(0, result.return_value_); - EXPECT_EQ(1, buffer.length()); - - EXPECT_CALL(os_sys_calls, writev(_, _, _)) - .WillOnce(Return(Api::SysCallSizeResult{-1, SOCKET_ERROR_AGAIN})); - result = io_handle.write(buffer); - EXPECT_EQ(Api::IoError::IoErrorCode::Again, result.err_->getErrorCode()); - EXPECT_EQ(0, result.return_value_); - EXPECT_EQ(1, buffer.length()); - - EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{1, 0})); - result = io_handle.write(buffer); - EXPECT_TRUE(result.ok()); - EXPECT_EQ(1, result.return_value_); - EXPECT_EQ(0, buffer.length()); - - EXPECT_CALL(os_sys_calls, writev(_, _, _)).Times(0); - result = io_handle.write(buffer); - EXPECT_EQ(0, result.return_value_); - EXPECT_EQ(0, buffer.length()); -} - -TEST_F(OwnedImplTest, Read) { - Api::MockOsSysCalls os_sys_calls; - TestThreadsafeSingletonInjector os_calls(&os_sys_calls); - - Buffer::OwnedImpl buffer; - Network::IoSocketHandleImpl io_handle; - EXPECT_CALL(os_sys_calls, readv(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{0, 0})); - Api::IoCallUint64Result result = io_handle.read(buffer, 100); - EXPECT_TRUE(result.ok()); - EXPECT_EQ(0, result.return_value_); - EXPECT_EQ(0, buffer.length()); - EXPECT_THAT(buffer.describeSlicesForTest(), testing::IsEmpty()); - - EXPECT_CALL(os_sys_calls, readv(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{-1, 0})); - result = io_handle.read(buffer, 100); - EXPECT_EQ(Api::IoError::IoErrorCode::UnknownError, result.err_->getErrorCode()); - EXPECT_EQ(0, result.return_value_); - EXPECT_EQ(0, buffer.length()); - EXPECT_THAT(buffer.describeSlicesForTest(), testing::IsEmpty()); - - EXPECT_CALL(os_sys_calls, readv(_, _, _)) - .WillOnce(Return(Api::SysCallSizeResult{-1, SOCKET_ERROR_AGAIN})); - result = io_handle.read(buffer, 100); - EXPECT_EQ(Api::IoError::IoErrorCode::Again, result.err_->getErrorCode()); - EXPECT_EQ(0, result.return_value_); - EXPECT_EQ(0, buffer.length()); - EXPECT_THAT(buffer.describeSlicesForTest(), testing::IsEmpty()); - - EXPECT_CALL(os_sys_calls, readv(_, _, _)).Times(0); - result = io_handle.read(buffer, 0); - EXPECT_EQ(0, result.return_value_); - EXPECT_EQ(0, buffer.length()); - EXPECT_THAT(buffer.describeSlicesForTest(), testing::IsEmpty()); -} - TEST_F(OwnedImplTest, ExtractOwnedSlice) { // Create a buffer with two owned slices. Buffer::OwnedImpl buffer; @@ -1136,68 +1051,6 @@ TEST_F(OwnedImplTest, PrependEmpty) { EXPECT_EQ(0, buf.length()); } -// Regression test for oss-fuzz issues -// https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=14466, empty commit -// following a reserve resulted in a corrupted libevent internal state. -TEST_F(OwnedImplTest, ReserveZeroCommit) { - BufferFragmentImpl frag("", 0, nullptr); - Buffer::OwnedImpl buf; - buf.addBufferFragment(frag); - buf.prepend("bbbbb"); - buf.add(""); - expectSlices({{5, 0, 4096}, {0, 0, 0}}, buf); - { auto reservation = buf.reserveSingleSlice(1280); } - expectSlices({{5, 0, 4096}}, buf); - os_fd_t pipe_fds[2] = {0, 0}; - auto& os_sys_calls = Api::OsSysCallsSingleton::get(); -#ifdef WIN32 - ASSERT_EQ(os_sys_calls.socketpair(AF_INET, SOCK_STREAM, 0, pipe_fds).return_value_, 0); -#else - ASSERT_EQ(pipe(pipe_fds), 0); -#endif - Network::IoSocketHandleImpl io_handle(pipe_fds[0]); - ASSERT_EQ(os_sys_calls.setsocketblocking(pipe_fds[0], false).return_value_, 0); - ASSERT_EQ(os_sys_calls.setsocketblocking(pipe_fds[1], false).return_value_, 0); - const uint32_t max_length = 1953; - std::string data(max_length, 'e'); - const ssize_t rc = os_sys_calls.write(pipe_fds[1], data.data(), max_length).return_value_; - ASSERT_GT(rc, 0); - const uint32_t previous_length = buf.length(); - Api::IoCallUint64Result result = io_handle.read(buf, max_length); - ASSERT_EQ(result.return_value_, static_cast(rc)); - ASSERT_EQ(os_sys_calls.close(pipe_fds[1]).return_value_, 0); - ASSERT_EQ(previous_length, buf.search(data.data(), rc, previous_length, 0)); - EXPECT_EQ("bbbbb", buf.toString().substr(0, 5)); - expectSlices({{5, 0, 4096}, {1953, 14431, 16384}}, buf); -} - -TEST_F(OwnedImplTest, ReadReserveAndCommit) { - BufferFragmentImpl frag("", 0, nullptr); - Buffer::OwnedImpl buf; - buf.add("bbbbb"); - - os_fd_t pipe_fds[2] = {0, 0}; - auto& os_sys_calls = Api::OsSysCallsSingleton::get(); -#ifdef WIN32 - ASSERT_EQ(os_sys_calls.socketpair(AF_INET, SOCK_STREAM, 0, pipe_fds).return_value_, 0); -#else - ASSERT_EQ(pipe(pipe_fds), 0); -#endif - Network::IoSocketHandleImpl io_handle(pipe_fds[0]); - ASSERT_EQ(os_sys_calls.setsocketblocking(pipe_fds[0], false).return_value_, 0); - ASSERT_EQ(os_sys_calls.setsocketblocking(pipe_fds[1], false).return_value_, 0); - - const uint32_t read_length = 32768; - std::string data = "e"; - const ssize_t rc = os_sys_calls.write(pipe_fds[1], data.data(), data.size()).return_value_; - ASSERT_GT(rc, 0); - Api::IoCallUint64Result result = io_handle.read(buf, read_length); - ASSERT_EQ(result.return_value_, static_cast(rc)); - ASSERT_EQ(os_sys_calls.close(pipe_fds[1]).return_value_, 0); - EXPECT_EQ("bbbbbe", buf.toString()); - expectSlices({{6, 4090, 4096}}, buf); -} - TEST(OverflowDetectingUInt64, Arithmetic) { OverflowDetectingUInt64 length; length += 1; @@ -1265,6 +1118,167 @@ TEST_F(OwnedImplTest, FrontSlice) { EXPECT_EQ(1, buffer.frontSlice().len_); } +template struct OwnedImplTypedTest : public OwnedImplTest { + using IoSocketHandleTestType = T; +}; + +using IoSocketHandleTypes = + testing::Types; + +TYPED_TEST_CASE(OwnedImplTypedTest, IoSocketHandleTypes); + +TYPED_TEST(OwnedImplTypedTest, Write) { + Api::MockOsSysCalls os_sys_calls; + TestThreadsafeSingletonInjector os_calls(&os_sys_calls); + + Buffer::OwnedImpl buffer; + using IoSocketHandleType = typename TestFixture::IoSocketHandleTestType; + IoSocketHandleType io_handle; + buffer.add("example"); + EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{7, 0})); + Api::IoCallUint64Result result = io_handle.write(buffer); + EXPECT_TRUE(result.ok()); + EXPECT_EQ(7, result.return_value_); + EXPECT_EQ(0, buffer.length()); + + buffer.add("example"); + EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{6, 0})); + result = io_handle.write(buffer); + EXPECT_TRUE(result.ok()); + EXPECT_EQ(6, result.return_value_); + EXPECT_EQ(1, buffer.length()); + + EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{0, 0})); + result = io_handle.write(buffer); + EXPECT_TRUE(result.ok()); + EXPECT_EQ(0, result.return_value_); + EXPECT_EQ(1, buffer.length()); + + EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{-1, 0})); + result = io_handle.write(buffer); + EXPECT_EQ(Api::IoError::IoErrorCode::UnknownError, result.err_->getErrorCode()); + EXPECT_EQ(0, result.return_value_); + EXPECT_EQ(1, buffer.length()); + + EXPECT_CALL(os_sys_calls, writev(_, _, _)) + .WillOnce(Return(Api::SysCallSizeResult{-1, SOCKET_ERROR_AGAIN})); + result = io_handle.write(buffer); + EXPECT_EQ(Api::IoError::IoErrorCode::Again, result.err_->getErrorCode()); + EXPECT_EQ(0, result.return_value_); + EXPECT_EQ(1, buffer.length()); + + EXPECT_CALL(os_sys_calls, writev(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{1, 0})); + result = io_handle.write(buffer); + EXPECT_TRUE(result.ok()); + EXPECT_EQ(1, result.return_value_); + EXPECT_EQ(0, buffer.length()); + + EXPECT_CALL(os_sys_calls, writev(_, _, _)).Times(0); + result = io_handle.write(buffer); + EXPECT_EQ(0, result.return_value_); + EXPECT_EQ(0, buffer.length()); +} + +TYPED_TEST(OwnedImplTypedTest, Read) { + Api::MockOsSysCalls os_sys_calls; + TestThreadsafeSingletonInjector os_calls(&os_sys_calls); + + Buffer::OwnedImpl buffer; + using IoSocketHandleType = typename TestFixture::IoSocketHandleTestType; + IoSocketHandleType io_handle; + EXPECT_CALL(os_sys_calls, readv(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{0, 0})); + Api::IoCallUint64Result result = io_handle.read(buffer, 100); + EXPECT_TRUE(result.ok()); + EXPECT_EQ(0, result.return_value_); + EXPECT_EQ(0, buffer.length()); + EXPECT_THAT(buffer.describeSlicesForTest(), testing::IsEmpty()); + + EXPECT_CALL(os_sys_calls, readv(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{-1, 0})); + result = io_handle.read(buffer, 100); + EXPECT_EQ(Api::IoError::IoErrorCode::UnknownError, result.err_->getErrorCode()); + EXPECT_EQ(0, result.return_value_); + EXPECT_EQ(0, buffer.length()); + EXPECT_THAT(buffer.describeSlicesForTest(), testing::IsEmpty()); + + EXPECT_CALL(os_sys_calls, readv(_, _, _)) + .WillOnce(Return(Api::SysCallSizeResult{-1, SOCKET_ERROR_AGAIN})); + result = io_handle.read(buffer, 100); + EXPECT_EQ(Api::IoError::IoErrorCode::Again, result.err_->getErrorCode()); + EXPECT_EQ(0, result.return_value_); + EXPECT_EQ(0, buffer.length()); + EXPECT_THAT(buffer.describeSlicesForTest(), testing::IsEmpty()); + + EXPECT_CALL(os_sys_calls, readv(_, _, _)).Times(0); + result = io_handle.read(buffer, 0); + EXPECT_EQ(0, result.return_value_); + EXPECT_EQ(0, buffer.length()); + EXPECT_THAT(buffer.describeSlicesForTest(), testing::IsEmpty()); +} + +// Regression test for oss-fuzz issues +// https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=14466, empty commit +// following a reserve resulted in a corrupted libevent internal state. +TYPED_TEST(OwnedImplTypedTest, ReserveZeroCommit) { + BufferFragmentImpl frag("", 0, nullptr); + Buffer::OwnedImpl buf; + buf.addBufferFragment(frag); + buf.prepend("bbbbb"); + buf.add(""); + OwnedImplTest::expectSlices({{5, 0, 4096}, {0, 0, 0}}, buf); + { auto reservation = buf.reserveSingleSlice(1280); } + OwnedImplTest::expectSlices({{5, 0, 4096}}, buf); + os_fd_t pipe_fds[2] = {0, 0}; + auto& os_sys_calls = Api::OsSysCallsSingleton::get(); +#ifdef WIN32 + ASSERT_EQ(os_sys_calls.socketpair(AF_INET, SOCK_STREAM, 0, pipe_fds).return_value_, 0); +#else + ASSERT_EQ(pipe(pipe_fds), 0); +#endif + using IoSocketHandleType = typename TestFixture::IoSocketHandleTestType; + IoSocketHandleType io_handle(pipe_fds[0]); + ASSERT_EQ(os_sys_calls.setsocketblocking(pipe_fds[0], false).return_value_, 0); + ASSERT_EQ(os_sys_calls.setsocketblocking(pipe_fds[1], false).return_value_, 0); + const uint32_t max_length = 1953; + std::string data(max_length, 'e'); + const ssize_t rc = os_sys_calls.write(pipe_fds[1], data.data(), max_length).return_value_; + ASSERT_GT(rc, 0); + const uint32_t previous_length = buf.length(); + Api::IoCallUint64Result result = io_handle.read(buf, max_length); + ASSERT_EQ(result.return_value_, static_cast(rc)); + ASSERT_EQ(os_sys_calls.close(pipe_fds[1]).return_value_, 0); + ASSERT_EQ(previous_length, buf.search(data.data(), rc, previous_length, 0)); + EXPECT_EQ("bbbbb", buf.toString().substr(0, 5)); + OwnedImplTest::expectSlices({{5, 0, 4096}, {1953, 14431, 16384}}, buf); +} + +TYPED_TEST(OwnedImplTypedTest, ReadReserveAndCommit) { + BufferFragmentImpl frag("", 0, nullptr); + Buffer::OwnedImpl buf; + buf.add("bbbbb"); + + os_fd_t pipe_fds[2] = {0, 0}; + auto& os_sys_calls = Api::OsSysCallsSingleton::get(); +#ifdef WIN32 + ASSERT_EQ(os_sys_calls.socketpair(AF_INET, SOCK_STREAM, 0, pipe_fds).return_value_, 0); +#else + ASSERT_EQ(pipe(pipe_fds), 0); +#endif + using IoSocketHandleType = typename TestFixture::IoSocketHandleTestType; + IoSocketHandleType io_handle(pipe_fds[0]); + ASSERT_EQ(os_sys_calls.setsocketblocking(pipe_fds[0], false).return_value_, 0); + ASSERT_EQ(os_sys_calls.setsocketblocking(pipe_fds[1], false).return_value_, 0); + + const uint32_t read_length = 32768; + std::string data = "e"; + const ssize_t rc = os_sys_calls.write(pipe_fds[1], data.data(), data.size()).return_value_; + ASSERT_GT(rc, 0); + Api::IoCallUint64Result result = io_handle.read(buf, read_length); + ASSERT_EQ(result.return_value_, static_cast(rc)); + ASSERT_EQ(os_sys_calls.close(pipe_fds[1]).return_value_, 0); + EXPECT_EQ("bbbbbe", buf.toString()); + OwnedImplTest::expectSlices({{6, 4090, 4096}}, buf); +} + } // namespace } // namespace Buffer } // namespace Envoy diff --git a/test/common/network/connection_impl_test.cc b/test/common/network/connection_impl_test.cc index b666ded243ef4..7d2e600272484 100644 --- a/test/common/network/connection_impl_test.cc +++ b/test/common/network/connection_impl_test.cc @@ -111,7 +111,7 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, ConnectionImplDeathTest, TEST_P(ConnectionImplDeathTest, BadFd) { Api::ApiPtr api = Api::createApiForTest(); Event::DispatcherPtr dispatcher(api->allocateDispatcher("test_thread")); - IoHandlePtr io_handle = std::make_unique(); + IoHandlePtr io_handle = std::make_unique(); StreamInfo::StreamInfoImpl stream_info(dispatcher->timeSource(), nullptr); EXPECT_DEATH( ConnectionImpl(*dispatcher, @@ -131,6 +131,13 @@ class ConnectionImplTest : public testing::TestWithParam { ConnectionImplTest() : api_(Api::createApiForTest(time_system_)), stream_info_(time_system_, nullptr) {} + ~ConnectionImplTest() override { + EXPECT_TRUE(timer_destroyed_ || timer_ == nullptr); + if (!timer_destroyed_) { + delete timer_; + } + } + void setUpBasicConnection() { if (dispatcher_ == nullptr) { dispatcher_ = api_->allocateDispatcher("test_thread"); @@ -232,11 +239,11 @@ class ConnectionImplTest : public testing::TestWithParam { return new Buffer::WatermarkBuffer(below_low, above_high, above_overflow); })); - Event::MockTimer* timer = nullptr; if (create_timer) { // This timer will be returned (transferring ownership) to the ConnectionImpl when // createTimer() is called to allocate the delayed close timer. - timer = new Event::MockTimer(dispatcher.get()); + timer_ = new Event::MockTimer(dispatcher.get()); + timer_->timer_destroyed_ = &timer_destroyed_; } NiceMock* file_event = new NiceMock; @@ -246,13 +253,15 @@ class ConnectionImplTest : public testing::TestWithParam { auto transport_socket = std::make_unique>(); EXPECT_CALL(*transport_socket, canFlushClose()).WillRepeatedly(Return(true)); - return ConnectionMocks{std::move(dispatcher), timer, std::move(transport_socket), file_event, + return ConnectionMocks{std::move(dispatcher), timer_, std::move(transport_socket), file_event, &file_ready_cb_}; } Network::TestClientConnectionImpl* testClientConnection() { return dynamic_cast(client_connection_.get()); } + Event::MockTimer* timer_{nullptr}; + bool timer_destroyed_{false}; Event::FileReadyCb file_ready_cb_; Event::SimulatedTimeSystem time_system_; Api::ApiPtr api_; @@ -400,7 +409,7 @@ TEST_P(ConnectionImplTest, ImmediateConnectError) { TEST_P(ConnectionImplTest, SetServerTransportSocketTimeout) { ConnectionMocks mocks = createConnectionMocks(false); MockTransportSocket* transport_socket = mocks.transport_socket_.get(); - IoHandlePtr io_handle = std::make_unique(0); + IoHandlePtr io_handle = std::make_unique(0); auto* mock_timer = new NiceMock(); EXPECT_CALL(*mocks.dispatcher_, @@ -425,7 +434,7 @@ TEST_P(ConnectionImplTest, SetServerTransportSocketTimeout) { TEST_P(ConnectionImplTest, SetServerTransportSocketTimeoutAfterConnect) { ConnectionMocks mocks = createConnectionMocks(false); MockTransportSocket* transport_socket = mocks.transport_socket_.get(); - IoHandlePtr io_handle = std::make_unique(0); + IoHandlePtr io_handle = std::make_unique(0); auto server_connection = std::make_unique( *mocks.dispatcher_, @@ -445,7 +454,7 @@ TEST_P(ConnectionImplTest, SetServerTransportSocketTimeoutAfterConnect) { TEST_P(ConnectionImplTest, ServerTransportSocketTimeoutDisabledOnConnect) { ConnectionMocks mocks = createConnectionMocks(false); MockTransportSocket* transport_socket = mocks.transport_socket_.get(); - IoHandlePtr io_handle = std::make_unique(0); + IoHandlePtr io_handle = std::make_unique(0); auto* mock_timer = new NiceMock(); EXPECT_CALL(*mocks.dispatcher_, @@ -466,7 +475,9 @@ TEST_P(ConnectionImplTest, ServerTransportSocketTimeoutDisabledOnConnect) { transport_socket->callbacks_->raiseEvent(ConnectionEvent::Connected); EXPECT_TRUE(timer_destroyed); - + if (!timer_destroyed) { + delete mock_timer; + } server_connection->close(ConnectionCloseType::NoFlush); } @@ -676,7 +687,7 @@ TEST_P(ConnectionImplTest, ConnectionStats) { // against actual enabling twice in a row. TEST_P(ConnectionImplTest, ReadDisable) { ConnectionMocks mocks = createConnectionMocks(false); - IoHandlePtr io_handle = std::make_unique(0); + IoHandlePtr io_handle = std::make_unique(0); auto connection = std::make_unique( *mocks.dispatcher_, std::make_unique(std::move(io_handle), nullptr, nullptr), @@ -1636,7 +1647,7 @@ TEST_P(ConnectionImplTest, FlushWriteAndDelayConfigDisabledTest) { std::function above_overflow) -> Buffer::Instance* { return new Buffer::WatermarkBuffer(below_low, above_high, above_overflow); })); - IoHandlePtr io_handle = std::make_unique(0); + IoHandlePtr io_handle = std::make_unique(0); std::unique_ptr server_connection(new Network::ConnectionImpl( dispatcher, std::make_unique(std::move(io_handle), nullptr, nullptr), std::make_unique>(), stream_info_, true)); @@ -1667,7 +1678,7 @@ TEST_P(ConnectionImplTest, FlushWriteAndDelayConfigDisabledTest) { TEST_P(ConnectionImplTest, DelayedCloseTimerResetWithPendingWriteBufferFlushes) { ConnectionMocks mocks = createConnectionMocks(); MockTransportSocket* transport_socket = mocks.transport_socket_.get(); - IoHandlePtr io_handle = std::make_unique(0); + IoHandlePtr io_handle = std::make_unique(0); auto server_connection = std::make_unique( *mocks.dispatcher_, std::make_unique(std::move(io_handle), nullptr, nullptr), @@ -1728,7 +1739,7 @@ TEST_P(ConnectionImplTest, DelayedCloseTimerResetWithPendingWriteBufferFlushes) TEST_P(ConnectionImplTest, IgnoreSpuriousFdWriteEventsDuringFlushWriteAndDelay) { ConnectionMocks mocks = createConnectionMocks(); MockTransportSocket* transport_socket = mocks.transport_socket_.get(); - IoHandlePtr io_handle = std::make_unique(0); + IoHandlePtr io_handle = std::make_unique(0); auto server_connection = std::make_unique( *mocks.dispatcher_, std::make_unique(std::move(io_handle), nullptr, nullptr), @@ -1820,7 +1831,7 @@ TEST_P(ConnectionImplTest, IgnoreSpuriousFdWriteEventsDuringFlushWriteAndDelay) TEST_P(ConnectionImplTest, DelayedCloseTimeoutDisableOnSocketClose) { ConnectionMocks mocks = createConnectionMocks(); MockTransportSocket* transport_socket = mocks.transport_socket_.get(); - IoHandlePtr io_handle = std::make_unique(0); + IoHandlePtr io_handle = std::make_unique(0); auto server_connection = std::make_unique( *mocks.dispatcher_, std::make_unique(std::move(io_handle), nullptr, nullptr), @@ -1856,7 +1867,7 @@ TEST_P(ConnectionImplTest, DelayedCloseTimeoutDisableOnSocketClose) { TEST_P(ConnectionImplTest, DelayedCloseTimeoutNullStats) { ConnectionMocks mocks = createConnectionMocks(); MockTransportSocket* transport_socket = mocks.transport_socket_.get(); - IoHandlePtr io_handle = std::make_unique(0); + IoHandlePtr io_handle = std::make_unique(0); auto server_connection = std::make_unique( *mocks.dispatcher_, std::make_unique(std::move(io_handle), nullptr, nullptr), @@ -1896,7 +1907,7 @@ TEST_P(ConnectionImplTest, DelayedCloseTimeoutNullStats) { TEST_P(ConnectionImplTest, NetworkSocketDumpsWithoutAllocatingMemory) { std::array buffer; OutputBufferStream ostream{buffer.data(), buffer.size()}; - IoHandlePtr io_handle = std::make_unique(0); + IoHandlePtr io_handle = std::make_unique(0); Address::InstanceConstSharedPtr server_addr; Address::InstanceConstSharedPtr local_addr; if (GetParam() == Network::Address::IpVersion::v4) { @@ -1943,7 +1954,7 @@ TEST_P(ConnectionImplTest, NetworkConnectionDumpsWithoutAllocatingMemory) { std::array buffer; OutputBufferStream ostream{buffer.data(), buffer.size()}; ConnectionMocks mocks = createConnectionMocks(false); - IoHandlePtr io_handle = std::make_unique(0); + IoHandlePtr io_handle = std::make_unique(0); auto server_connection = std::make_unique( *mocks.dispatcher_, @@ -2008,7 +2019,7 @@ class MockTransportConnectionImplTest : public testing::Test { .WillOnce(Invoke([this](TransportSocketCallbacks& callbacks) { transport_socket_callbacks_ = &callbacks; })); - IoHandlePtr io_handle = std::make_unique(0); + IoHandlePtr io_handle = std::make_unique(0); connection_ = std::make_unique( dispatcher_, std::make_unique(std::move(io_handle), nullptr, nullptr), TransportSocketPtr(transport_socket_), stream_info_, true); diff --git a/test/common/network/listen_socket_impl_test.cc b/test/common/network/listen_socket_impl_test.cc index 949054479cb9b..6a17c33b7d8f4 100644 --- a/test/common/network/listen_socket_impl_test.cc +++ b/test/common/network/listen_socket_impl_test.cc @@ -119,7 +119,7 @@ class ListenSocketImplTest : public testing::TestWithParam { auto socket_result = os_sys_calls.socket(domain, SOCK_STREAM, 0); EXPECT_TRUE(SOCKET_VALID(socket_result.return_value_)); Network::IoHandlePtr io_handle = - std::make_unique(socket_result.return_value_); + std::make_unique(socket_result.return_value_); auto socket3 = createListenSocketPtr(std::move(io_handle), addr, nullptr); EXPECT_EQ(socket3->connectionInfoProvider().localAddress()->asString(), addr->asString()); @@ -173,7 +173,7 @@ TEST_P(ListenSocketImplTestTcp, BindSpecificPort) { testBindSpecificPort(); } class TestListenSocket : public ListenSocketImpl { public: TestListenSocket(Address::InstanceConstSharedPtr address) - : ListenSocketImpl(std::make_unique(), address) {} + : ListenSocketImpl(std::make_unique(), address) {} TestListenSocket(Address::IpVersion ip_version) : ListenSocketImpl(/*io_handle=*/nullptr, ip_version == Address::IpVersion::v4 diff --git a/test/integration/filters/BUILD b/test/integration/filters/BUILD index 95d9735eadbd8..1011e3c74776e 100644 --- a/test/integration/filters/BUILD +++ b/test/integration/filters/BUILD @@ -528,6 +528,7 @@ envoy_cc_test_library( "//source/common/common:utility_lib", "//source/common/network:address_lib", "//source/common/network:default_socket_interface_lib", + "//test/test_common:network_utility_lib", "@com_google_absl//absl/types:optional", "@envoy_api//envoy/extensions/network/socket_interface/v3:pkg_cc_proto", ], diff --git a/test/integration/filters/test_socket_interface.cc b/test/integration/filters/test_socket_interface.cc index e32d5ace315ec..5991e7a948708 100644 --- a/test/integration/filters/test_socket_interface.cc +++ b/test/integration/filters/test_socket_interface.cc @@ -21,7 +21,7 @@ Api::IoCallUint64Result TestIoSocketHandle::writev(const Buffer::RawSlice* slice return std::move(result).value(); } } - return IoSocketHandleImpl::writev(slices, num_slice); + return Test::IoSocketHandlePlatformImpl::writev(slices, num_slice); } IoHandlePtr TestIoSocketHandle::accept(struct sockaddr* addr, socklen_t* addrlen) { diff --git a/test/integration/filters/test_socket_interface.h b/test/integration/filters/test_socket_interface.h index 905b227dae254..61da3e86fa105 100644 --- a/test/integration/filters/test_socket_interface.h +++ b/test/integration/filters/test_socket_interface.h @@ -7,6 +7,9 @@ #include "source/common/network/io_socket_handle_impl.h" #include "source/common/network/socket_interface_impl.h" +#include "source/common/network/win32_socket_handle_impl.h" + +#include "test/test_common/network_utility.h" #include "absl/types/optional.h" @@ -16,7 +19,7 @@ namespace Envoy { namespace Network { -class TestIoSocketHandle : public IoSocketHandleImpl { +class TestIoSocketHandle : public Test::IoSocketHandlePlatformImpl { public: using WritevOverrideType = absl::optional(TestIoSocketHandle* io_handle, const Buffer::RawSlice* slices, @@ -25,13 +28,14 @@ class TestIoSocketHandle : public IoSocketHandleImpl { TestIoSocketHandle(WritevOverrideProc writev_override_proc, os_fd_t fd = INVALID_SOCKET, bool socket_v6only = false, absl::optional domain = absl::nullopt) - : IoSocketHandleImpl(fd, socket_v6only, domain), writev_override_(writev_override_proc) {} + : Test::IoSocketHandlePlatformImpl(fd, socket_v6only, domain), + writev_override_(writev_override_proc) {} void initializeFileEvent(Event::Dispatcher& dispatcher, Event::FileReadyCb cb, Event::FileTriggerType trigger, uint32_t events) override { absl::MutexLock lock(&mutex_); dispatcher_ = &dispatcher; - IoSocketHandleImpl::initializeFileEvent(dispatcher, cb, trigger, events); + Test::IoSocketHandlePlatformImpl::initializeFileEvent(dispatcher, cb, trigger, events); } // Schedule resumption on the IoHandle by posting a callback to the IoHandle's dispatcher. Note diff --git a/test/test_common/network_utility.h b/test/test_common/network_utility.h index c7a9b7b85b672..58e40374b0316 100644 --- a/test/test_common/network_utility.h +++ b/test/test_common/network_utility.h @@ -8,8 +8,10 @@ #include "envoy/network/io_handle.h" #include "envoy/network/transport_socket.h" +#include "source/common/network/io_socket_handle_impl.h" #include "source/common/network/listen_socket_impl.h" #include "source/common/network/utility.h" +#include "source/common/network/win32_socket_handle_impl.h" #include "gtest/gtest.h" @@ -17,6 +19,12 @@ namespace Envoy { namespace Network { namespace Test { +#if defined(WIN32) || defined(FORCE_LEVEL_EVENTS) +using IoSocketHandlePlatformImpl = Win32SocketHandleImpl; +#else +using IoSocketHandlePlatformImpl = IoSocketHandleImpl; +#endif + /** * Determines if the passed in address and port is available for binding. If the port is zero, * the OS should pick an unused port for the supplied address (e.g. for the loopback address). From f393ff14d4dd07650c4090138430b711396ba27a Mon Sep 17 00:00:00 2001 From: Jojy George Varghese Date: Tue, 2 Nov 2021 08:31:09 -0700 Subject: [PATCH 006/110] [rbac]: Fix extension name. (#18848) Fixing extension name in multiple places. Signed-off-by: Jojy George Varghese --- .../filters/common/rbac/matchers/upstream_ip_port.h | 2 +- .../filters/http/rbac/rbac_filter_integration_test.cc | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/extensions/filters/common/rbac/matchers/upstream_ip_port.h b/source/extensions/filters/common/rbac/matchers/upstream_ip_port.h index 3a01a8a7afab3..27dcbb004e164 100644 --- a/source/extensions/filters/common/rbac/matchers/upstream_ip_port.h +++ b/source/extensions/filters/common/rbac/matchers/upstream_ip_port.h @@ -36,7 +36,7 @@ class UpstreamIpPortMatcherFactory UpstreamIpPortMatcher, envoy::extensions::rbac::matchers::upstream_ip_port::v3::UpstreamIpPortMatcher> { public: - std::string name() const override { return "envoy.rbac.matchers.upstream.upstream_ip_port"; } + std::string name() const override { return "envoy.rbac.matchers.upstream_ip_port"; } }; } // namespace Matchers diff --git a/test/extensions/filters/http/rbac/rbac_filter_integration_test.cc b/test/extensions/filters/http/rbac/rbac_filter_integration_test.cc index 8894afc1303a9..3aed76982535a 100644 --- a/test/extensions/filters/http/rbac/rbac_filter_integration_test.cc +++ b/test/extensions/filters/http/rbac/rbac_filter_integration_test.cc @@ -636,7 +636,7 @@ name: rbac - or_rules: rules: - matcher: - name: envoy.filters.http.rbac.matchers.upstream_ip_port + name: envoy.rbac.matchers.upstream_ip_port typed_config: "@type": type.googleapis.com/envoy.extensions.rbac.matchers.upstream_ip_port.v3.UpstreamIpPortMatcher upstream_ip: @@ -679,21 +679,21 @@ name: rbac - or_rules: rules: - matcher: - name: envoy.filters.http.rbac.matchers.upstream_ip_port + name: envoy.rbac.matchers.upstream_ip_port typed_config: "@type": type.googleapis.com/envoy.extensions.rbac.matchers.upstream_ip_port.v3.UpstreamIpPortMatcher upstream_ip: address_prefix: 127.2.1.1 prefix_len: 24 - matcher: - name: envoy.filters.http.rbac.matchers.upstream_ip_port + name: envoy.rbac.matchers.upstream_ip_port typed_config: "@type": type.googleapis.com/envoy.extensions.rbac.matchers.upstream_ip_port.v3.UpstreamIpPortMatcher upstream_ip: address_prefix: 127.0.0.1 prefix_len: 24 - matcher: - name: envoy.filters.http.rbac.matchers.upstream_ip_port + name: envoy.rbac.matchers.upstream_ip_port typed_config: "@type": type.googleapis.com/envoy.extensions.rbac.matchers.upstream_ip_port.v3.UpstreamIpPortMatcher upstream_ip: From ca13cce45b10717338df4fd43ce56d5f286db203 Mon Sep 17 00:00:00 2001 From: Kevin Baichoo Date: Tue, 2 Nov 2021 13:28:00 -0700 Subject: [PATCH 007/110] Request copy assignment operator. (#18865) The copy assignment operator is being invoked for SocketOptionName implicitly. Explicitly request it. This fixes compiling with clang-14. Signed-off-by: Kevin Baichoo --- envoy/network/socket.h | 1 + 1 file changed, 1 insertion(+) diff --git a/envoy/network/socket.h b/envoy/network/socket.h index e57899b7f9eac..0d492c0e7a888 100644 --- a/envoy/network/socket.h +++ b/envoy/network/socket.h @@ -24,6 +24,7 @@ namespace Network { struct SocketOptionName { SocketOptionName() = default; SocketOptionName(const SocketOptionName&) = default; + SocketOptionName& operator=(const SocketOptionName&) = default; SocketOptionName(int level, int option, const std::string& name) : value_(std::make_tuple(level, option, name)) {} From 56e8c45b1b340c4a4f8f02ec2488354c31806d59 Mon Sep 17 00:00:00 2001 From: Rei Shimizu Date: Wed, 3 Nov 2021 06:01:52 +0900 Subject: [PATCH 008/110] tls: allow cert validation by only leaf trusted CA's CRL (#18289) Commit Message: Allow cert validation by only leaf trusted CAs CRL Additional Description: Close #18268. In the previous implementation, we don't have availability to validate certs when all trusted CAs don't have their own CRLs if any trusted CAs have that. This feature allows validating even if all trusted CAs don't have CRLs. Risk Level: Low Testing: Unit Docs Changes: Required Release Notes: Required Signed-off-by: Shikugawa --- .../transport_sockets/tls/v3/common.proto | 13 ++- docs/root/version_history/current.rst | 1 + .../certificate_validation_context_config.h | 5 ++ ...tificate_validation_context_config_impl.cc | 2 +- ...rtificate_validation_context_config_impl.h | 3 + .../tls/cert_validator/default_validator.cc | 9 ++- .../tls/cert_validator/test_common.h | 1 + .../transport_sockets/tls/ssl_socket_test.cc | 79 +++++++++++++++++++ test/mocks/ssl/mocks.h | 1 + 9 files changed, 108 insertions(+), 6 deletions(-) diff --git a/api/envoy/extensions/transport_sockets/tls/v3/common.proto b/api/envoy/extensions/transport_sockets/tls/v3/common.proto index 82dcb37cd7ca0..ab88abcbe2ce6 100644 --- a/api/envoy/extensions/transport_sockets/tls/v3/common.proto +++ b/api/envoy/extensions/transport_sockets/tls/v3/common.proto @@ -253,7 +253,7 @@ message CertificateProviderPluginInstance { string certificate_name = 2; } -// [#next-free-field: 14] +// [#next-free-field: 15] message CertificateValidationContext { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.auth.CertificateValidationContext"; @@ -292,6 +292,9 @@ message CertificateValidationContext { // that if a CRL is provided for any certificate authority in a trust chain, a CRL must be // provided for all certificate authorities in that chain. Failure to do so will result in // verification failure for both revoked and unrevoked certificates from that chain. + // The behavior of requiring all certificates to contain CRLs if any do can be altered by + // setting :ref:`only_verify_leaf_cert_crl ` + // true. If set to true, only the final certificate in the chain undergoes CRL verification. // // See :ref:`the TLS overview ` for a list of common // system CA locations. @@ -417,7 +420,9 @@ message CertificateValidationContext { // for any certificate authority in a trust chain, a CRL must be provided // for all certificate authorities in that chain. Failure to do so will // result in verification failure for both revoked and unrevoked certificates - // from that chain. + // from that chain. This default behavior can be altered by setting + // :ref:`only_verify_leaf_cert_crl ` to + // true. config.core.v3.DataSource crl = 7; // If specified, Envoy will not reject expired certificates. @@ -433,4 +438,8 @@ message CertificateValidationContext { // Refer to the documentation for the specified validator. If you do not want a custom validation algorithm, do not set this field. // [#extension-category: envoy.tls.cert_validator] config.core.v3.TypedExtensionConfig custom_validator_config = 12; + + // If this option is set to true, only the certificate at the end of the + // certificate chain will be subject to validation by :ref:`CRL `. + bool only_verify_leaf_cert_crl = 14; } diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 51da2dda37cf6..354ef9637ae3d 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -67,6 +67,7 @@ New Features * thrift_proxy: add upstream metrics to show decoding errors and whether exception is from local or remote, e.g. ``cluster.cluster_name.thrift.upstream_resp_exception_remote``. * thrift_proxy: add host level success/error metrics where success is a reply of type success and error is any other response to a call. * thrift_proxy: support subset lb when using request or route metadata. +* tls: added support for only verifying the leaf CRL in the certificate chain with :ref:`only_verify_leaf_cert_crl `. * transport_socket: added :ref:`envoy.transport_sockets.tcp_stats ` which generates additional statistics gathered from the OS TCP stack. * udp: add support for multiple listener filters. * upstream: added the ability to :ref:`configure max connection duration ` for upstream clusters. diff --git a/envoy/ssl/certificate_validation_context_config.h b/envoy/ssl/certificate_validation_context_config.h index 544215a73daea..d8a1bf6b0af20 100644 --- a/envoy/ssl/certificate_validation_context_config.h +++ b/envoy/ssl/certificate_validation_context_config.h @@ -78,6 +78,11 @@ class CertificateValidationContextConfig { * @return a reference to the api object. */ virtual Api::Api& api() const PURE; + + /** + * @return whether to validate certificate chain with all CRL or not. + */ + virtual bool onlyVerifyLeafCertificateCrl() const PURE; }; using CertificateValidationContextConfigPtr = std::unique_ptr; diff --git a/source/common/ssl/certificate_validation_context_config_impl.cc b/source/common/ssl/certificate_validation_context_config_impl.cc index 40dc20f6ef3a3..564325c3fb662 100644 --- a/source/common/ssl/certificate_validation_context_config_impl.cc +++ b/source/common/ssl/certificate_validation_context_config_impl.cc @@ -35,7 +35,7 @@ CertificateValidationContextConfigImpl::CertificateValidationContextConfigImpl( ? absl::make_optional( config.custom_validator_config()) : absl::nullopt), - api_(api) { + api_(api), only_verify_leaf_cert_crl_(config.only_verify_leaf_cert_crl()) { if (ca_cert_.empty() && custom_validator_config_ == absl::nullopt) { if (!certificate_revocation_list_.empty()) { throw EnvoyException(fmt::format("Failed to load CRL from {} without trusted CA", diff --git a/source/common/ssl/certificate_validation_context_config_impl.h b/source/common/ssl/certificate_validation_context_config_impl.h index 7cf045a351841..038abe08b1e96 100644 --- a/source/common/ssl/certificate_validation_context_config_impl.h +++ b/source/common/ssl/certificate_validation_context_config_impl.h @@ -48,6 +48,8 @@ class CertificateValidationContextConfigImpl : public CertificateValidationConte Api::Api& api() const override { return api_; } + bool onlyVerifyLeafCertificateCrl() const override { return only_verify_leaf_cert_crl_; } + private: const std::string ca_cert_; const std::string ca_cert_path_; @@ -61,6 +63,7 @@ class CertificateValidationContextConfigImpl : public CertificateValidationConte TrustChainVerification trust_chain_verification_; const absl::optional custom_validator_config_; Api::Api& api_; + const bool only_verify_leaf_cert_crl_; }; } // namespace Ssl diff --git a/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc b/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc index 691b5018189d2..e638b98b42841 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc +++ b/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc @@ -100,7 +100,9 @@ int DefaultCertValidator::initializeSslContexts(std::vector contexts, absl::StrCat("Failed to load trusted CA certificates from ", config_->caCertPath())); } if (has_crl) { - X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); + X509_STORE_set_flags(store, config_->onlyVerifyLeafCertificateCrl() + ? X509_V_FLAG_CRL_CHECK + : X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); } verify_mode = SSL_VERIFY_PEER; verify_trusted_ca_ = true; @@ -136,8 +138,9 @@ int DefaultCertValidator::initializeSslContexts(std::vector contexts, X509_STORE_add_crl(store, item->crl); } } - - X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); + X509_STORE_set_flags(store, config_->onlyVerifyLeafCertificateCrl() + ? X509_V_FLAG_CRL_CHECK + : X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); } } diff --git a/test/extensions/transport_sockets/tls/cert_validator/test_common.h b/test/extensions/transport_sockets/tls/cert_validator/test_common.h index b958f17272075..11d4022cf0436 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/test_common.h +++ b/test/extensions/transport_sockets/tls/cert_validator/test_common.h @@ -72,6 +72,7 @@ class TestCertificateValidationContextConfig } Api::Api& api() const override { return *api_; } + bool onlyVerifyLeafCertificateCrl() const override { return false; } private: bool allow_expired_certificate_{false}; diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index 24b1952640a5b..4685f03829a4f 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -4610,6 +4610,85 @@ TEST_P(SslSocketTest, RevokedIntermediateCertificateCRLInTrustedCA) { testUtil(complete_unrevoked_test_options.setExpectedSerialNumber(TEST_SAN_DNS4_CERT_SERIAL)); } +TEST_P(SslSocketTest, NotRevokedLeafCertificateOnlyLeafCRLValidation) { + // The test checks that revoked certificate will makes the validation success even if we set + // only_verify_leaf_cert_crl to true. + // + // Trust chain contains: + // - Root authority certificate (i.e., ca_cert.pem) + // - Intermediate authority certificate (i.e., intermediate_ca_cert.pem) + // - Intermediate authority certificate revocation list (i.e., intermediate_ca_cert.crl) + // + // Trust chain omits (But this test will succeed): + // - Root authority certificate revocation list (i.e., ca_cert.crl) + const std::string incomplete_server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/unittest_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/unittest_key.pem" + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/intermediate_ca_cert_chain_with_crl.pem" + only_verify_leaf_cert_crl: true +)EOF"; + + // This should succeed, since the certificate has not been revoked. + const std::string unrevoked_client_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns4_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns4_key.pem" +)EOF"; + + TestUtilOptions complete_unrevoked_test_options(unrevoked_client_ctx_yaml, + incomplete_server_ctx_yaml, true, GetParam()); + testUtil(complete_unrevoked_test_options.setExpectedSerialNumber(TEST_SAN_DNS4_CERT_SERIAL)); +} + +TEST_P(SslSocketTest, RevokedLeafCertificateOnlyLeafCRLValidation) { + // The test checks that revoked certificate will makes the validation fails even if we set + // only_verify_leaf_cert_crl to true. + // + // Trust chain contains: + // - Root authority certificate (i.e., ca_cert.pem) + // - Intermediate authority certificate (i.e., intermediate_ca_cert.pem) + // - Intermediate authority certificate revocation list (i.e., intermediate_ca_cert.crl) + // + // Trust chain omits (But this test will succeed): + // - Root authority certificate revocation list (i.e., ca_cert.crl) + const std::string incomplete_server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/unittest_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/unittest_key.pem" + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/intermediate_ca_cert_chain_with_crl.pem" + only_verify_leaf_cert_crl: true +)EOF"; + + // This should fail, since the certificate has been revoked. + const std::string revoked_client_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns3_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns3_key.pem" +)EOF"; + + TestUtilOptions complete_revoked_test_options(revoked_client_ctx_yaml, incomplete_server_ctx_yaml, + false, GetParam()); + testUtil(complete_revoked_test_options.setExpectedServerStats("ssl.fail_verify_error") + .setExpectedVerifyErrorCode(X509_V_ERR_CERT_REVOKED)); +} + TEST_P(SslSocketTest, GetRequestedServerName) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index 43ad275fce277..ae545d126d55f 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -167,6 +167,7 @@ class MockCertificateValidationContextConfig : public CertificateValidationConte MOCK_METHOD(envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext:: TrustChainVerification, trustChainVerification, (), (const)); + MOCK_METHOD(bool, onlyVerifyLeafCertificateCrl, (), (const)); }; class MockPrivateKeyMethodManager : public PrivateKeyMethodManager { From c06971509137490f80f2204a9c7c374ccb7959c1 Mon Sep 17 00:00:00 2001 From: Yuchen Dai Date: Tue, 2 Nov 2021 16:38:40 -0700 Subject: [PATCH 009/110] tcp_proxy: add support to evaluate downstream info at tunnel setting (#18858) This allows to generate tcp proxy's upstream CONNECT header values using downstream info. Examples include modifying the host/:authority headers Or emulate the proxy protocol via CONNECT as in the test cases. Use with caution: a RFC non-compliant upstream header can be introduced because stream info may carry arbitrary bytes. Signed-off-by: Yuchen Dai --- configs/encapsulate_in_http2_connect.yaml | 4 ++ docs/root/version_history/current.rst | 1 + source/common/tcp_proxy/upstream.cc | 27 +++++---- source/common/tcp_proxy/upstream.h | 11 +++- test/common/tcp_proxy/upstream_test.cc | 58 +++++++++++++++++-- .../upstreams/tcp/generic/config_test.cc | 20 +++++-- .../tcp_tunneling_integration_test.cc | 49 ++++++++++++++++ 7 files changed, 145 insertions(+), 25 deletions(-) diff --git a/configs/encapsulate_in_http2_connect.yaml b/configs/encapsulate_in_http2_connect.yaml index 1f985457ab2dd..55629ef0df2aa 100644 --- a/configs/encapsulate_in_http2_connect.yaml +++ b/configs/encapsulate_in_http2_connect.yaml @@ -26,6 +26,10 @@ static_resources: cluster: "cluster_0" tunneling_config: hostname: host.com:443 + headers_to_add: + - header: + key: original_dst_port + value: "%DOWNSTREAM_LOCAL_PORT%" clusters: - name: cluster_0 connect_timeout: 5s diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 354ef9637ae3d..30648e1bb1c41 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -63,6 +63,7 @@ New Features * listener: added support for :ref:`MPTCP ` (multipath TCP). * oauth filter: added :ref:`cookie_names ` to allow overriding (default) cookie names (``BearerToken``, ``OauthHMAC``, and ``OauthExpires``) set by the filter. * tcp: added a :ref:`FilterState ` :ref:`hash policy `, used by :ref:`TCP proxy ` to allow hashing load balancer algorithms to hash on objects in filter state. +* tcp_proxy: added support to populate upstream http connect header values from stream info. * thrift_proxy: add upstream response zone metrics in the form ``cluster.cluster_name.zone.local_zone.upstream_zone.thrift.upstream_resp_success``. * thrift_proxy: add upstream metrics to show decoding errors and whether exception is from local or remote, e.g. ``cluster.cluster_name.thrift.upstream_resp_exception_remote``. * thrift_proxy: add host level success/error metrics where success is a reply of type success and error is any other response to a call. diff --git a/source/common/tcp_proxy/upstream.cc b/source/common/tcp_proxy/upstream.cc index 8982729206000..4fdb838540cda 100644 --- a/source/common/tcp_proxy/upstream.cc +++ b/source/common/tcp_proxy/upstream.cc @@ -58,8 +58,10 @@ TcpUpstream::onDownstreamEvent(Network::ConnectionEvent event) { } HttpUpstream::HttpUpstream(Tcp::ConnectionPool::UpstreamCallbacks& callbacks, - const TunnelingConfig& config) - : config_(config), response_decoder_(*this), upstream_callbacks_(callbacks) { + const TunnelingConfig& config, + const StreamInfo::StreamInfo& downstream_info) + : config_(config), downstream_info_(downstream_info), response_decoder_(*this), + upstream_callbacks_(callbacks) { header_parser_ = Envoy::Router::HeaderParser::configure(config_.headers_to_add()); } @@ -195,7 +197,8 @@ HttpConnPool::HttpConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, Upstream::LoadBalancerContext* context, const TunnelingConfig& config, Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks, Http::CodecType type) - : config_(config), type_(type), upstream_callbacks_(upstream_callbacks) { + : config_(config), type_(type), upstream_callbacks_(upstream_callbacks), + downstream_info_(context->downstreamConnection()->streamInfo()) { absl::optional protocol; if (type_ == Http::CodecType::HTTP3) { protocol = Http::Protocol::Http3; @@ -217,9 +220,9 @@ HttpConnPool::~HttpConnPool() { void HttpConnPool::newStream(GenericConnectionPoolCallbacks& callbacks) { callbacks_ = &callbacks; if (type_ == Http::CodecType::HTTP1) { - upstream_ = std::make_unique(upstream_callbacks_, config_); + upstream_ = std::make_unique(upstream_callbacks_, config_, downstream_info_); } else { - upstream_ = std::make_unique(upstream_callbacks_, config_); + upstream_ = std::make_unique(upstream_callbacks_, config_, downstream_info_); } Tcp::ConnectionPool::Cancellable* handle = conn_pool_data_.value().newStream(upstream_->responseDecoder(), *this); @@ -251,8 +254,9 @@ void HttpConnPool::onGenericPoolReady(Upstream::HostDescriptionConstSharedPtr& h } Http2Upstream::Http2Upstream(Tcp::ConnectionPool::UpstreamCallbacks& callbacks, - const TunnelingConfig& config) - : HttpUpstream(callbacks, config) {} + const TunnelingConfig& config, + const StreamInfo::StreamInfo& downstream_info) + : HttpUpstream(callbacks, config, downstream_info) {} bool Http2Upstream::isValidResponse(const Http::ResponseHeaderMap& headers) { if (Http::Utility::getResponseStatus(headers) != 200) { @@ -279,15 +283,16 @@ void Http2Upstream::setRequestEncoder(Http::RequestEncoder& request_encoder, boo Http::Headers::get().ProtocolValues.Bytestream); } - header_parser_->evaluateHeaders(*headers, nullptr /*stream_info*/); + header_parser_->evaluateHeaders(*headers, downstream_info_); const auto status = request_encoder_->encodeHeaders(*headers, false); // Encoding can only fail on missing required request headers. ASSERT(status.ok()); } Http1Upstream::Http1Upstream(Tcp::ConnectionPool::UpstreamCallbacks& callbacks, - const TunnelingConfig& config) - : HttpUpstream(callbacks, config) {} + const TunnelingConfig& config, + const StreamInfo::StreamInfo& downstream_info) + : HttpUpstream(callbacks, config, downstream_info) {} void Http1Upstream::setRequestEncoder(Http::RequestEncoder& request_encoder, bool) { request_encoder_ = &request_encoder; @@ -305,7 +310,7 @@ void Http1Upstream::setRequestEncoder(Http::RequestEncoder& request_encoder, boo headers->addReference(Http::Headers::get().Path, "/"); } - header_parser_->evaluateHeaders(*headers, nullptr /*stream_info*/); + header_parser_->evaluateHeaders(*headers, downstream_info_); const auto status = request_encoder_->encodeHeaders(*headers, false); // Encoding can only fail on missing required request headers. ASSERT(status.ok()); diff --git a/source/common/tcp_proxy/upstream.h b/source/common/tcp_proxy/upstream.h index 017257b5bcaa0..19c10cbb3694e 100644 --- a/source/common/tcp_proxy/upstream.h +++ b/source/common/tcp_proxy/upstream.h @@ -103,6 +103,7 @@ class HttpConnPool : public GenericConnPool, public Http::ConnectionPool::Callba GenericConnectionPoolCallbacks* callbacks_{}; Tcp::ConnectionPool::UpstreamCallbacks& upstream_callbacks_; std::unique_ptr upstream_; + const StreamInfo::StreamInfo& downstream_info_; }; class TcpUpstream : public GenericUpstream { @@ -150,12 +151,14 @@ class HttpUpstream : public GenericUpstream, protected Http::StreamCallbacks { } protected: - HttpUpstream(Tcp::ConnectionPool::UpstreamCallbacks& callbacks, const TunnelingConfig& config); + HttpUpstream(Tcp::ConnectionPool::UpstreamCallbacks& callbacks, const TunnelingConfig& config, + const StreamInfo::StreamInfo& downstream_info); void resetEncoder(Network::ConnectionEvent event, bool inform_downstream = true); Http::RequestEncoder* request_encoder_{}; const TunnelingConfig config_; std::unique_ptr header_parser_; + const StreamInfo::StreamInfo& downstream_info_; private: class DecoderShim : public Http::ResponseDecoder { @@ -198,7 +201,8 @@ class HttpUpstream : public GenericUpstream, protected Http::StreamCallbacks { class Http1Upstream : public HttpUpstream { public: - Http1Upstream(Tcp::ConnectionPool::UpstreamCallbacks& callbacks, const TunnelingConfig& config); + Http1Upstream(Tcp::ConnectionPool::UpstreamCallbacks& callbacks, const TunnelingConfig& config, + const StreamInfo::StreamInfo& downstream_info); void encodeData(Buffer::Instance& data, bool end_stream) override; void setRequestEncoder(Http::RequestEncoder& request_encoder, bool is_ssl) override; @@ -207,7 +211,8 @@ class Http1Upstream : public HttpUpstream { class Http2Upstream : public HttpUpstream { public: - Http2Upstream(Tcp::ConnectionPool::UpstreamCallbacks& callbacks, const TunnelingConfig& config); + Http2Upstream(Tcp::ConnectionPool::UpstreamCallbacks& callbacks, const TunnelingConfig& config, + const StreamInfo::StreamInfo& downstream_info); void setRequestEncoder(Http::RequestEncoder& request_encoder, bool is_ssl) override; bool isValidResponse(const Http::ResponseHeaderMap& headers) override; diff --git a/test/common/tcp_proxy/upstream_test.cc b/test/common/tcp_proxy/upstream_test.cc index ce4184067be55..e5970fafa7531 100644 --- a/test/common/tcp_proxy/upstream_test.cc +++ b/test/common/tcp_proxy/upstream_test.cc @@ -6,6 +6,8 @@ #include "test/mocks/http/mocks.h" #include "test/mocks/http/stream_encoder.h" #include "test/mocks/tcp/mocks.h" +#include "test/test_common/environment.h" +#include "test/test_common/network_utility.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -33,10 +35,10 @@ template class HttpUpstreamTest : public testing::Test { } EXPECT_CALL(stream_encoder_options_, enableHalfClose()).Times(AnyNumber()); config_.set_hostname("default.host.com:443"); - upstream_ = std::make_unique(callbacks_, config_); + upstream_ = std::make_unique(callbacks_, config_, downstream_stream_info_); upstream_->setRequestEncoder(encoder_, true); } - + NiceMock downstream_stream_info_; Http::MockRequestEncoder encoder_; Http::MockHttp1StreamEncoderOptions stream_encoder_options_; NiceMock callbacks_; @@ -60,7 +62,8 @@ TYPED_TEST(HttpUpstreamTest, WriteUpstream) { this->upstream_->encodeData(buffer2, true); // New upstream with no encoder - this->upstream_ = std::make_unique(this->callbacks_, this->config_); + this->upstream_ = + std::make_unique(this->callbacks_, this->config_, this->downstream_stream_info_); this->upstream_->encodeData(buffer2, true); } @@ -94,7 +97,8 @@ TYPED_TEST(HttpUpstreamTest, ReadDisable) { EXPECT_TRUE(this->upstream_->readDisable(false)); // New upstream with no encoder - this->upstream_ = std::make_unique(this->callbacks_, this->config_); + this->upstream_ = + std::make_unique(this->callbacks_, this->config_, this->downstream_stream_info_); EXPECT_FALSE(this->upstream_->readDisable(true)); } @@ -182,8 +186,11 @@ template class HttpUpstreamRequestEncoderTest : public testing::Tes config_.set_hostname("default.host.com:443"); } - void setupUpstream() { upstream_ = std::make_unique(callbacks_, config_); } + void setupUpstream() { + upstream_ = std::make_unique(callbacks_, config_, this->downstream_stream_info_); + } + NiceMock downstream_stream_info_; Http::MockRequestEncoder encoder_; Http::MockHttp1StreamEncoderOptions stream_encoder_options_; NiceMock callbacks_; @@ -275,6 +282,47 @@ TYPED_TEST(HttpUpstreamRequestEncoderTest, RequestEncoderHeaders) { EXPECT_CALL(this->encoder_, encodeHeaders(HeaderMapEqualRef(expected_headers.get()), false)); this->upstream_->setRequestEncoder(this->encoder_, false); } + +TYPED_TEST(HttpUpstreamRequestEncoderTest, RequestEncoderHeadersWithDownstreamInfo) { + auto* header = this->config_.add_headers_to_add(); + auto* hdr = header->mutable_header(); + hdr->set_key("header0"); + hdr->set_value("value0"); + + header = this->config_.add_headers_to_add(); + hdr = header->mutable_header(); + hdr->set_key("downstream_local_port"); + hdr->set_value("%DOWNSTREAM_LOCAL_PORT%"); + header->mutable_append()->set_value(true); + + this->setupUpstream(); + std::unique_ptr expected_headers; + expected_headers = Http::createHeaderMap({ + {Http::Headers::get().Method, "CONNECT"}, + {Http::Headers::get().Host, this->config_.hostname()}, + }); + + if (this->is_http2_) { + expected_headers->setReferenceKey(Http::Headers::get().Path, "/"); + expected_headers->setReferenceKey(Http::Headers::get().Scheme, + Http::Headers::get().SchemeValues.Http); + expected_headers->setReferenceKey(Http::Headers::get().Protocol, + Http::Headers::get().ProtocolValues.Bytestream); + } + + expected_headers->setCopy(Http::LowerCaseString("header0"), "value0"); + expected_headers->addCopy(Http::LowerCaseString("downstream_local_port"), "80"); + auto ip_versions = TestEnvironment::getIpVersionsForTest(); + ASSERT_FALSE(ip_versions.empty()); + + auto ip_port = Network::Utility::getAddressWithPort( + *Network::Test::getCanonicalLoopbackAddress(ip_versions[0]), 80); + Network::ConnectionInfoSetterImpl connection_info(ip_port, ip_port); + EXPECT_CALL(this->downstream_stream_info_, downstreamAddressProvider) + .WillOnce(testing::ReturnRef(connection_info)); + EXPECT_CALL(this->encoder_, encodeHeaders(HeaderMapEqualRef(expected_headers.get()), false)); + this->upstream_->setRequestEncoder(this->encoder_, false); +} } // namespace } // namespace TcpProxy } // namespace Envoy diff --git a/test/extensions/upstreams/tcp/generic/config_test.cc b/test/extensions/upstreams/tcp/generic/config_test.cc index dd222bedcc0cc..1054b6fadfccb 100644 --- a/test/extensions/upstreams/tcp/generic/config_test.cc +++ b/test/extensions/upstreams/tcp/generic/config_test.cc @@ -2,6 +2,7 @@ #include "test/mocks/tcp/mocks.h" #include "test/mocks/upstream/cluster_manager.h" +#include "test/mocks/upstream/load_balancer_context.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -19,17 +20,24 @@ namespace Generic { class TcpConnPoolTest : public ::testing::Test { public: + TcpConnPoolTest() { + EXPECT_CALL(connection_, streamInfo()).WillRepeatedly(ReturnRef(downstream_stream_info_)); + EXPECT_CALL(lb_context_, downstreamConnection()).WillRepeatedly(Return(&connection_)); + } NiceMock thread_local_cluster_; GenericConnPoolFactory factory_; NiceMock callbacks_; + NiceMock downstream_stream_info_; + NiceMock connection_; + Upstream::MockLoadBalancerContext lb_context_; }; TEST_F(TcpConnPoolTest, TestNoConnPool) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config; config.set_hostname("host"); EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); - EXPECT_EQ(nullptr, - factory_.createGenericConnPool(thread_local_cluster_, config, nullptr, callbacks_)); + EXPECT_EQ(nullptr, factory_.createGenericConnPool(thread_local_cluster_, config, &lb_context_, + callbacks_)); } TEST_F(TcpConnPoolTest, Http2Config) { @@ -39,8 +47,8 @@ TEST_F(TcpConnPoolTest, Http2Config) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config; config.set_hostname("host"); EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); - EXPECT_EQ(nullptr, - factory_.createGenericConnPool(thread_local_cluster_, config, nullptr, callbacks_)); + EXPECT_EQ(nullptr, factory_.createGenericConnPool(thread_local_cluster_, config, &lb_context_, + callbacks_)); } TEST_F(TcpConnPoolTest, Http3Config) { @@ -52,8 +60,8 @@ TEST_F(TcpConnPoolTest, Http3Config) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config; config.set_hostname("host"); EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); - EXPECT_EQ(nullptr, - factory_.createGenericConnPool(thread_local_cluster_, config, nullptr, callbacks_)); + EXPECT_EQ(nullptr, factory_.createGenericConnPool(thread_local_cluster_, config, &lb_context_, + callbacks_)); } } // namespace Generic diff --git a/test/integration/tcp_tunneling_integration_test.cc b/test/integration/tcp_tunneling_integration_test.cc index b98c290348823..7c59ac5e98f12 100644 --- a/test/integration/tcp_tunneling_integration_test.cc +++ b/test/integration/tcp_tunneling_integration_test.cc @@ -605,6 +605,55 @@ TEST_P(TcpTunnelingIntegrationTest, BasicUsePost) { closeConnection(fake_upstream_connection_); } +TEST_P(TcpTunnelingIntegrationTest, BasicHeaderEvaluationTunnelingConfig) { + // Set the "downstream-local-ip" header in the CONNECT request. + config_helper_.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { + envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy proxy_config; + proxy_config.set_stat_prefix("tcp_stats"); + proxy_config.set_cluster("cluster_0"); + proxy_config.mutable_tunneling_config()->set_hostname("host.com:80"); + auto new_header = proxy_config.mutable_tunneling_config()->mutable_headers_to_add()->Add(); + new_header->mutable_header()->set_key("downstream-local-ip"); + new_header->mutable_header()->set_value("%DOWNSTREAM_LOCAL_ADDRESS_WITHOUT_PORT%"); + + auto* listeners = bootstrap.mutable_static_resources()->mutable_listeners(); + for (auto& listener : *listeners) { + if (listener.name() != "tcp_proxy") { + continue; + } + auto* filter_chain = listener.mutable_filter_chains(0); + auto* filter = filter_chain->mutable_filters(0); + filter->mutable_typed_config()->PackFrom(proxy_config); + break; + } + }); + + initialize(); + + // Start a connection, and verify the upgrade headers are received upstream. + tcp_client_ = makeTcpConnection(lookupPort("tcp_proxy")); + ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); + ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); + ASSERT_TRUE(upstream_request_->waitForHeadersComplete()); + EXPECT_EQ(upstream_request_->headers().getMethodValue(), "CONNECT"); + + // Verify that the connect request has a "downstream-local-ip" header and its value is the + // loopback address. + EXPECT_EQ( + upstream_request_->headers().get(Envoy::Http::LowerCaseString("downstream-local-ip")).size(), + 1); + EXPECT_EQ(upstream_request_->headers() + .get(Envoy::Http::LowerCaseString("downstream-local-ip"))[0] + ->value() + .getStringView(), + Network::Test::getLoopbackAddressString(version_)); + + // Send upgrade headers downstream, fully establishing the connection. + upstream_request_->encodeHeaders(default_response_headers_, false); + sendBidiData(fake_upstream_connection_); + closeConnection(fake_upstream_connection_); +} + TEST_P(TcpTunnelingIntegrationTest, Goaway) { if (upstreamProtocol() == Http::CodecType::HTTP1) { return; From 19a31679f8dc211f6ef69b027a1a3a01769daa4c Mon Sep 17 00:00:00 2001 From: phlax Date: Wed, 3 Nov 2021 02:43:36 +0000 Subject: [PATCH 010/110] dependabot: Updates (#18770) * build(deps): bump frozendict from 2.0.6 to 2.0.7 in /tools/base Bumps [frozendict](https://github.com/Marco-Sulla/python-frozendict) from 2.0.6 to 2.0.7. - [Release notes](https://github.com/Marco-Sulla/python-frozendict/releases) - [Commits](https://github.com/Marco-Sulla/python-frozendict/commits) --- updated-dependencies: - dependency-name: frozendict dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: Ryan Northey * build(deps): bump setuptools from 58.2.0 to 58.3.0 in /tools/base Bumps [setuptools](https://github.com/pypa/setuptools) from 58.2.0 to 58.3.0. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/CHANGES.rst) - [Commits](https://github.com/pypa/setuptools/compare/v58.2.0...v58.3.0) --- updated-dependencies: - dependency-name: setuptools dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: Ryan Northey * build(deps): bump pyparsing from 2.4.7 to 3.0.1 in /tools/dependency Bumps [pyparsing](https://github.com/pyparsing/pyparsing) from 2.4.7 to 3.0.1. - [Release notes](https://github.com/pyparsing/pyparsing/releases) - [Changelog](https://github.com/pyparsing/pyparsing/blob/master/CHANGES) - [Commits](https://github.com/pyparsing/pyparsing/compare/pyparsing_2.4.7...pyparsing_3.0.1) --- updated-dependencies: - dependency-name: pyparsing dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: Ryan Northey * build(deps): bump protobuf in /examples/grpc-bridge/client Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 3.18.0 to 3.19.0. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/master/generate_changelog.py) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.18.0...v3.19.0) --- updated-dependencies: - dependency-name: protobuf dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: Ryan Northey * build(deps): bump pyjwt from 2.1.0 to 2.3.0 in /tools/dependency Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.1.0 to 2.3.0. - [Release notes](https://github.com/jpadilla/pyjwt/releases) - [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst) - [Commits](https://github.com/jpadilla/pyjwt/compare/2.1.0...2.3.0) --- updated-dependencies: - dependency-name: pyjwt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: Ryan Northey Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/grpc-bridge/client/requirements.txt | 47 +++++++++++--------- tools/base/requirements.txt | 12 ++--- tools/dependency/requirements.txt | 12 ++--- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/examples/grpc-bridge/client/requirements.txt b/examples/grpc-bridge/client/requirements.txt index 0c58c1ec1b48b..9294fee3a139a 100644 --- a/examples/grpc-bridge/client/requirements.txt +++ b/examples/grpc-bridge/client/requirements.txt @@ -110,28 +110,31 @@ idna==3.2 \ --hash=sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a \ --hash=sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3 # via requests -protobuf==3.18.1 \ - --hash=sha256:0851b5b89191e1976d34fa2e8eb8659829dfb45252053224cf9df857fb5f6a45 \ - --hash=sha256:09d9268f6f9da81b7657adcf2fb397524c82f20cdf9e0db3ff4e7567977abd67 \ - --hash=sha256:10544fc7ace885a882623083c24da5b14148c77563acddc3c58d66f6153c09cd \ - --hash=sha256:1c9bb40503751087300dd12ce2e90899d68628977905c76effc48e66d089391e \ - --hash=sha256:387f621bf7295a331f8c8a6962d097ceddeb85356792888cfa6a5c6bfc6886a4 \ - --hash=sha256:3c1644f8a7f19b45c7a4c32278e2a55ae9e7e2f9e5f02d960a61f04a4890d3e6 \ - --hash=sha256:4d19c9cb805fd2be1d59eee39e152367ee92a30167e77bd06c8819f8f0009a4c \ - --hash=sha256:61ca58e14033ca0dfa484a31d57237c1be3b6013454c7f53876a20fc88dd69b1 \ - --hash=sha256:6f714f5de9d40b3bec90ede4a688cce52f637ccdc5403afcda1f67598f4fdcd7 \ - --hash=sha256:7a7be937c319146cc9f2626f0181e6809062c353e1fe449ecd0df374ba1036b2 \ - --hash=sha256:7e2f0677d68ecdd1cfda2abea65873f5bc7c3f5aae199404a3f5c1d1198c1a63 \ - --hash=sha256:8c1c5d3966c856f60a9d8d62f4455d70c31026422acdd5c228edf22b65b16c38 \ - --hash=sha256:93bad12895d8b0ebc66b605c2ef1802311595f881aef032d9f13282b7550e6b2 \ - --hash=sha256:c0e2790c580070cff2921b93d562539ae027064340151c50db6aaf94c33048cd \ - --hash=sha256:c492c217d3f69f4d2d5619571e52ab98538edbf53caf67e53ea92bd0a3b5670f \ - --hash=sha256:d6d927774c0ec746fed15a4faff5f44aad0b7a3421fadb6f3ae5ca1f2f8ae26e \ - --hash=sha256:d76201380f41a2d83fb613a4683059d1fcafbe969518b3e409e279a8788fde2f \ - --hash=sha256:e2ee8b11e3eb2ed38f12137c3c132270a0b1dd509e317228ac47b67f21a583f1 \ - --hash=sha256:e9ac691f7b24e4371dcd3980e4f5d6c840a2010da37986203053fee995786ec5 \ - --hash=sha256:f20f803892f2135e8b96dc58c9a0c6a7ad8436794bf8784af229498d939b4c77 \ - --hash=sha256:fa6d1049d5315566f55c04d0b50c0033415144f96a9d25c820dc542fe2bb7f45 +protobuf==3.19.0 \ + --hash=sha256:01a0645ef3acddfbc90237e1cdfae1086130fc7cb480b5874656193afd657083 \ + --hash=sha256:2f6046b9e2feee0dce994493186e8715b4392ed5f50f356280ad9c2f9f93080a \ + --hash=sha256:34a77b8fafdeb8f89fee2b7108ae60d8958d72e33478680cc1e05517892ecc46 \ + --hash=sha256:36bf292f44966c67080e535321501717f4f1eba30faef8f2cd4b0c745a027211 \ + --hash=sha256:3fea09aa04ef2f8b01fcc9bb87f19509934f8a35d177c865b8f9ee5c32b60c1b \ + --hash=sha256:4f93e0f6af796ddd1502225ff8ea25340ced186ca05b601c44d5c88b45ba80a0 \ + --hash=sha256:6a1dc6584d24ef86f5b104bcad64fa0fe06ed36e5687f426e0445d363a041d18 \ + --hash=sha256:6f16925f5c977dd7787973a50c242e60c22b1d1182aba6bec7bd02862579c10f \ + --hash=sha256:708d04394a63ee9bdc797938b6e15ed5bf24a1cb37743eb3886fd74a5a67a234 \ + --hash=sha256:7b3867795708ac88fde8d6f34f0d9a50af56087e41f624bdb2e9ff808ea5dda7 \ + --hash=sha256:8488c2276f14f294e890cc1260ab342a13e90cd20dcc03319d2eea258f1fd321 \ + --hash=sha256:942dd6bc8bd2a3c6a156d8ab0f80bd45313f22b78e1176283270054dcc8ca4c2 \ + --hash=sha256:9a8a880593015ef2c83f7af797fa4fbf583b2c98b4bd94e46c5b61fee319d84b \ + --hash=sha256:a74432e9d28a6072a2359a0f49f81eb14dd718e7dbbfb6c0789b456c49e1f130 \ + --hash=sha256:ac2f8ec942d414609aba0331952ae12bb823e8f424bbb6b8c422f1cef32dc842 \ + --hash=sha256:b64be5d7270cf5e76375bac049846e8a9543a2d4368b69afe78ab725380a7487 \ + --hash=sha256:c96e94d3e523a82caa3e5f74b35dd1c4884199358d01c950d95c341255ff48bc \ + --hash=sha256:c99af73ae34c93e0e2ace57ea2e70243f34fc015c8c23fd39ee93652e726f7e7 \ + --hash=sha256:d1f4277d321f60456845ca9b882c4845736f1f5c1c69eb778eba22a97977d8af \ + --hash=sha256:d3861c9721a90ba83ee0936a9cfcc4fa1c4b4144ac9658fb6f6343b38558e9b4 \ + --hash=sha256:d4ca5f0c7bc8d2e6966ca3bbd85e9ebe7191b6e21f067896d4af6b28ecff29fe \ + --hash=sha256:ee4d07d596357f51316b6ecf1cc1927660e9d5e418385bb1c51fd2496cd9bee7 \ + --hash=sha256:f7a031cf8e2fc14acc0ba694f6dff0a01e06b70d817eba6edc72ee6cc20517ac \ + --hash=sha256:f9097327d277b0aa4a3224e61cd6850aef3269172397715299bcffc9f90293c9 # via # -r requirements.in # grpcio-tools diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 24cd2cd19536b..455b54209ca59 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -332,9 +332,9 @@ flake8-polyfill==1.0.2 \ --hash=sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9 \ --hash=sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda # via pep8-naming -frozendict==2.0.6 \ - --hash=sha256:3f00de72805cf4c9e81b334f3f04809278b967d2fed84552313a0fcce511beb1 \ - --hash=sha256:5d3f75832c35d4df041f0e19c268964cbef29c1eb34cd3517cf883f1c2d089b9 +frozendict==2.0.7 \ + --hash=sha256:a68f609d1af67da80b45519fdcfca2d60249c0a8c96e68279c1b6ddd92128204 \ + --hash=sha256:d650f9cf3d2c5438b1631488a975a49b3bdd12c7a97eec59b85e57821eebf28a # via # -r requirements.in # envoy.base.runner @@ -799,9 +799,9 @@ yarl==1.6.3 \ # via aiohttp # The following packages are considered to be unsafe in a requirements file: -setuptools==58.2.0 \ - --hash=sha256:2551203ae6955b9876741a26ab3e767bb3242dafe86a32a749ea0d78b6792f11 \ - --hash=sha256:2c55bdb85d5bb460bd2e3b12052b677879cffcf46c0c688f2e5bf51d36001145 +setuptools==58.3.0 \ + --hash=sha256:1a24f0e5c14b91ad6810745a7242721fd5011ed164553136f946f768b06559f7 \ + --hash=sha256:b0c2461641b58fe30e11d4c3dfba316c513bdf9ec85f9fed0c871c678447205e # via # -r requirements.in # sphinx diff --git a/tools/dependency/requirements.txt b/tools/dependency/requirements.txt index 53527b4a6d55d..e1505daa443be 100644 --- a/tools/dependency/requirements.txt +++ b/tools/dependency/requirements.txt @@ -88,9 +88,9 @@ pygithub==1.55 \ --hash=sha256:1bbfff9372047ff3f21d5cd8e07720f3dbfdaf6462fcaed9d815f528f1ba7283 \ --hash=sha256:2caf0054ea079b71e539741ae56c5a95e073b81fa472ce222e81667381b9601b # via -r requirements.in -pyjwt==2.2.0 \ - --hash=sha256:b0ed5824c8ecc5362e540c65dc6247567db130c4226670bf7699aec92fb4dae1 \ - --hash=sha256:a0b9a3b4e5ca5517cac9f1a6e9cd30bf1aa80be74fcdf4e28eded582ecfcfbae +pyjwt==2.3.0 \ + --hash=sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f \ + --hash=sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41 # via pygithub pynacl==1.4.0 \ --hash=sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4 \ @@ -112,9 +112,9 @@ pynacl==1.4.0 \ --hash=sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff \ --hash=sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80 # via pygithub -pyparsing==2.4.7 \ - --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \ - --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b +pyparsing==3.0.1 \ + --hash=sha256:fd93fc45c47893c300bd98f5dd1b41c0e783eaeb727e7cea210dcc09d64ce7c3 \ + --hash=sha256:84196357aa3566d64ad123d7a3c67b0e597a115c4934b097580e5ce220b91531 # via packaging pytz==2021.3 \ --hash=sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c \ From 936ab8ff7af72a05d03473a10ee709eaf0b998ec Mon Sep 17 00:00:00 2001 From: phlax Date: Wed, 3 Nov 2021 02:45:36 +0000 Subject: [PATCH 011/110] deps: Bump `rules_python` -> 0.5.0 (#18766) Fix #18764 Signed-off-by: Ryan Northey --- bazel/repository_locations.bzl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 3ad59c2111809..1a82cb8f970b4 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -688,9 +688,9 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Python rules for Bazel", project_desc = "Bazel rules for the Python language", project_url = "https://github.com/bazelbuild/rules_python", - version = "0.4.0", - sha256 = "954aa89b491be4a083304a2cb838019c8b8c3720a7abb9c4cb81ac7a24230cea", - release_date = "2021-09-12", + version = "0.5.0", + sha256 = "cd6730ed53a002c56ce4e2f396ba3b3be262fd7cb68339f0377a45e8227fe332", + release_date = "2021-10-26", urls = ["https://github.com/bazelbuild/rules_python/releases/download/{version}/rules_python-{version}.tar.gz"], use_category = ["build"], ), From 82261f5a401418df13626ca3fa52fa65fea10c81 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 3 Nov 2021 10:27:26 -0400 Subject: [PATCH 012/110] Update QUICHE from 1e04d1e30 to 0b75841d5 (#18862) https://github.com/google/quiche/compare/1e04d1e30..0b75841d5 2021-11-01 dschinazi Internal change 2021-11-01 dschinazi Platformize AsciiUrlDecode 2021-11-01 wub Pass ParsedClientHello to QuicDispatcher::CreateQuicSession. This makes it easier to pass CHLO information into CreateQuicSession, which is responsible for creating a QUIC session. 2021-11-01 wub Deprecate --gfe2_reloadable_flag_quic_tls_disable_resumption_refactor. 2021-11-01 haoyuewang Internal change Risk Level: low Testing: n/a Docs Changes: n/a Release Notes: n/a Signed-off-by: Alyssa Wilk --- bazel/external/quiche.BUILD | 1 + bazel/repository_locations.bzl | 6 +++--- source/common/quic/envoy_quic_dispatcher.cc | 4 ++-- source/common/quic/envoy_quic_dispatcher.h | 10 ++++------ 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/bazel/external/quiche.BUILD b/bazel/external/quiche.BUILD index 18b1c2275e213..8c6415833a678 100644 --- a/bazel/external/quiche.BUILD +++ b/bazel/external/quiche.BUILD @@ -3493,6 +3493,7 @@ envoy_cc_library( ":quic_core_time_lib", ":quic_platform_base", ":quiche_common_endian_lib", + ":quiche_common_print_elements_lib", ], ) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 1a82cb8f970b4..bc3efdd3cc735 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -838,12 +838,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "QUICHE", project_desc = "QUICHE (QUIC, HTTP/2, Etc) is Google‘s implementation of QUIC and related protocols", project_url = "https://github.com/google/quiche", - version = "1e04d1e300be1425e2098c4f7ea0e92b697f90a8", - sha256 = "c10fccecbfae316d27ee18bc52af78bac1da863b9892d1e00d54be8c084331fc", + version = "0b75841d5b5844c53f4399a41d64de7250c204d8", + sha256 = "a00b0671180fc79952baf754148e65364bfca9d35b988710594752fb7f9bf6a1", urls = ["https://github.com/google/quiche/archive/{version}.tar.gz"], strip_prefix = "quiche-{version}", use_category = ["dataplane_core"], - release_date = "2021-10-29", + release_date = "2021-11-02", cpe = "N/A", ), com_googlesource_googleurl = dict( diff --git a/source/common/quic/envoy_quic_dispatcher.cc b/source/common/quic/envoy_quic_dispatcher.cc index 37fd7472c9a6a..bcc5c4eb91e72 100644 --- a/source/common/quic/envoy_quic_dispatcher.cc +++ b/source/common/quic/envoy_quic_dispatcher.cc @@ -61,12 +61,12 @@ void EnvoyQuicDispatcher::OnConnectionClosed(quic::QuicConnectionId connection_i std::unique_ptr EnvoyQuicDispatcher::CreateQuicSession( quic::QuicConnectionId server_connection_id, const quic::QuicSocketAddress& self_address, const quic::QuicSocketAddress& peer_address, absl::string_view /*alpn*/, - const quic::ParsedQuicVersion& version, absl::string_view sni) { + const quic::ParsedQuicVersion& version, const quic::ParsedClientHello& parsed_chlo) { quic::QuicConfig quic_config = config(); // TODO(danzh) use passed-in ALPN instead of hard-coded h3 after proof source interfaces takes in // ALPN. Network::ConnectionSocketPtr connection_socket = createServerConnectionSocket( - listen_socket_.ioHandle(), self_address, peer_address, std::string(sni), "h3"); + listen_socket_.ioHandle(), self_address, peer_address, std::string(parsed_chlo.sni), "h3"); const Network::FilterChain* filter_chain = listener_config_->filterChainManager().findFilterChain(*connection_socket); diff --git a/source/common/quic/envoy_quic_dispatcher.h b/source/common/quic/envoy_quic_dispatcher.h index 97d04adb048d2..a022c224f21d9 100644 --- a/source/common/quic/envoy_quic_dispatcher.h +++ b/source/common/quic/envoy_quic_dispatcher.h @@ -51,12 +51,10 @@ class EnvoyQuicDispatcher : public quic::QuicDispatcher { protected: // quic::QuicDispatcher - std::unique_ptr CreateQuicSession(quic::QuicConnectionId server_connection_id, - const quic::QuicSocketAddress& self_address, - const quic::QuicSocketAddress& peer_address, - absl::string_view alpn, - const quic::ParsedQuicVersion& version, - absl::string_view sni) override; + std::unique_ptr CreateQuicSession( + quic::QuicConnectionId server_connection_id, const quic::QuicSocketAddress& self_address, + const quic::QuicSocketAddress& peer_address, absl::string_view alpn, + const quic::ParsedQuicVersion& version, const quic::ParsedClientHello& parsed_chlo) override; // Overridden to restore the first 4 bytes of the connection ID because our BPF filter only looks // at the first 4 bytes. This ensures that the replacement routes to the same quic dispatcher. quic::QuicConnectionId From 0cafc30be7235405614a5c73ef2bf0800c87426c Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 3 Nov 2021 11:34:20 -0400 Subject: [PATCH 013/110] docs: updating backports docs to reflect recent process and new setec folks (#18872) Signed-off-by: Alyssa Wilk --- OWNERS.md | 2 ++ RELEASES.md | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/OWNERS.md b/OWNERS.md index 66fb84068b2cb..70c524ad79969 100644 --- a/OWNERS.md +++ b/OWNERS.md @@ -67,6 +67,8 @@ without further review. * Tony Allen ([tonya11en](https://github.com/tonya11en)) (tony@allen.gg) * Yan Avlasov ([yanavlasov](https://github.com/yanavlasov)) (yavlasov@google.com) * William A Rowe Jr ([wrowe](https://github.com/wrowe)) (wrowe@vmware.com) +* Otto van der Schaaf ([oschaaf](https://github.com/oschaaf)) (oschaaf@redhat.com) +* Tim Walsh ([twghu](https://github.com/twghu)) (walsh@redhat.com) # Emeritus maintainers diff --git a/RELEASES.md b/RELEASES.md index 1619b0d22d729..4f086eb33ef0b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -26,7 +26,7 @@ to execute tests on it. ### Security releases Critical security fixes are owned by the Envoy security team, which provides fixes for the -`main` branch, and the latest release branch. Once those fixes are ready, the maintainers +`main` branch. Once those fixes are ready, the maintainers of stable releases backport them to the remaining supported stable releases. ### Backports @@ -55,6 +55,7 @@ stable releases and sending announcements about them. This role is rotating on a | 2021 Q1 | Rei Shimizu ([Shikugawa](https://github.com/Shikugawa)) | | 2021 Q2 | Dmitri Dolguikh ([dmitri-d](https://github.com/dmitri-d)) | | 2021 Q3 | Takeshi Yoneda ([mathetake](https://github.com/mathetake)) | +| 2021 Q4 | Otto van der Schaaf ([oschaaf](https://github.com/oschaaf)) | ## Release schedule From c41808220dda74de37af6f21988fae4d6975b60c Mon Sep 17 00:00:00 2001 From: dongpo Date: Thu, 4 Nov 2021 00:31:08 +0800 Subject: [PATCH 014/110] thrift_proxy: support header flags (#18806) Risk Level: Low Testing: unit test Docs Changes: N/A Release Notes: added Platform Specific Features: n.a Signed-off-by: Zhangdong Ma --- docs/root/version_history/current.rst | 1 + .../network/thrift_proxy/header_transport_impl.cc | 11 +++++++++-- .../filters/network/thrift_proxy/metadata.h | 9 +++++++++ .../thrift_proxy/header_transport_impl_test.cc | 9 ++++++--- .../filters/network/thrift_proxy/metadata_test.cc | 6 ++++++ .../extensions/filters/network/thrift_proxy/utility.h | 1 + 6 files changed, 32 insertions(+), 5 deletions(-) diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 30648e1bb1c41..3648d52837cc4 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -67,6 +67,7 @@ New Features * thrift_proxy: add upstream response zone metrics in the form ``cluster.cluster_name.zone.local_zone.upstream_zone.thrift.upstream_resp_success``. * thrift_proxy: add upstream metrics to show decoding errors and whether exception is from local or remote, e.g. ``cluster.cluster_name.thrift.upstream_resp_exception_remote``. * thrift_proxy: add host level success/error metrics where success is a reply of type success and error is any other response to a call. +* thrift_proxy: support header flags. * thrift_proxy: support subset lb when using request or route metadata. * tls: added support for only verifying the leaf CRL in the certificate chain with :ref:`only_verify_leaf_cert_crl `. * transport_socket: added :ref:`envoy.transport_sockets.tcp_stats ` which generates additional statistics gathered from the OS TCP stack. diff --git a/source/extensions/filters/network/thrift_proxy/header_transport_impl.cc b/source/extensions/filters/network/thrift_proxy/header_transport_impl.cc index cbfb42b0dc08b..978b7fb9c00ae 100644 --- a/source/extensions/filters/network/thrift_proxy/header_transport_impl.cc +++ b/source/extensions/filters/network/thrift_proxy/header_transport_impl.cc @@ -63,7 +63,9 @@ bool HeaderTransportImpl::decodeFrameStart(Buffer::Instance& buffer, MessageMeta throw EnvoyException(fmt::format("invalid thrift header transport magic {:04x}", magic)); } - // offset 6: 16 bit flags field, unused + // offset 6: 16 bit flags field + int16_t header_flags = buffer.peekBEInt(6); + // offset 8: 32 bit sequence number field int32_t seq_id = buffer.peekBEInt(8); @@ -92,6 +94,7 @@ bool HeaderTransportImpl::decodeFrameStart(Buffer::Instance& buffer, MessageMeta // (header_size). metadata.setFrameSize( static_cast(frame_size - header_size - MinFrameStartSizeNoHeaders)); + metadata.setHeaderFlags(header_flags); metadata.setSequenceId(seq_id); ProtocolType proto = ProtocolType::Auto; @@ -237,10 +240,14 @@ void HeaderTransportImpl::encodeFrame(Buffer::Instance& buffer, const MessageMet if (metadata.hasSequenceId()) { seq_id = metadata.sequenceId(); } + int16_t header_flags = 0; + if (metadata.hasHeaderFlags()) { + header_flags = metadata.headerFlags(); + } buffer.writeBEInt(static_cast(size)); buffer.writeBEInt(Magic); - buffer.writeBEInt(0); // flags + buffer.writeBEInt(header_flags); // flags buffer.writeBEInt(seq_id); buffer.writeBEInt(static_cast(header_size / 4)); diff --git a/source/extensions/filters/network/thrift_proxy/metadata.h b/source/extensions/filters/network/thrift_proxy/metadata.h index de44db1948b2d..9e8b7ae3f26e8 100644 --- a/source/extensions/filters/network/thrift_proxy/metadata.h +++ b/source/extensions/filters/network/thrift_proxy/metadata.h @@ -45,6 +45,10 @@ class MessageMetadata { copy->setMethodName(methodName()); } + if (hasHeaderFlags()) { + copy->setHeaderFlags(headerFlags()); + } + if (hasSequenceId()) { copy->setSequenceId(sequenceId()); } @@ -111,6 +115,10 @@ class MessageMetadata { const std::string& methodName() const { return method_name_.value(); } void setMethodName(const std::string& method_name) { method_name_ = method_name; } + bool hasHeaderFlags() const { return header_flags_.has_value(); } + int16_t headerFlags() const { return header_flags_.value(); } + void setHeaderFlags(int16_t header_flags) { header_flags_ = header_flags; } + bool hasSequenceId() const { return seq_id_.has_value(); } int32_t sequenceId() const { return seq_id_.value(); } void setSequenceId(int32_t seq_id) { seq_id_ = seq_id; } @@ -174,6 +182,7 @@ class MessageMetadata { absl::optional frame_size_{}; absl::optional proto_{}; absl::optional method_name_{}; + absl::optional header_flags_{}; absl::optional seq_id_{}; absl::optional msg_type_{}; absl::optional reply_type_{}; diff --git a/test/extensions/filters/network/thrift_proxy/header_transport_impl_test.cc b/test/extensions/filters/network/thrift_proxy/header_transport_impl_test.cc index 33cc7e7fb4f6c..24b603149c1f1 100644 --- a/test/extensions/filters/network/thrift_proxy/header_transport_impl_test.cc +++ b/test/extensions/filters/network/thrift_proxy/header_transport_impl_test.cc @@ -243,13 +243,14 @@ TEST(HeaderTransportTest, NoTransformsOrInfo) { buffer.writeBEInt(100); buffer.writeBEInt(0x0FFF); - buffer.writeBEInt(0); + buffer.writeBEInt(1); // header flags buffer.writeBEInt(1); // sequence number buffer.writeBEInt(1); // size 4 addSeq(buffer, {0, 0, 0, 0}); // 0 = binary proto, 0 = num transforms, pad, pad EXPECT_TRUE(transport.decodeFrameStart(buffer, metadata)); EXPECT_THAT(metadata, HasFrameSize(86U)); EXPECT_THAT(metadata, HasProtocol(ProtocolType::Binary)); + EXPECT_THAT(metadata, HasHeaderFlags(1)); EXPECT_THAT(metadata, HasSequenceId(1)); EXPECT_THAT(metadata, HasNoHeaders()); EXPECT_EQ(buffer.length(), 0); @@ -261,13 +262,14 @@ TEST(HeaderTransportTest, NoTransformsOrInfo) { buffer.writeBEInt(101); buffer.writeBEInt(0x0FFF); - buffer.writeBEInt(0); + buffer.writeBEInt(2); // header flags buffer.writeBEInt(2); // sequence number buffer.writeBEInt(1); // size 4 addSeq(buffer, {2, 0, 0, 0}); // 2 = compact proto, 0 = num transforms, pad, pad EXPECT_TRUE(transport.decodeFrameStart(buffer, metadata)); EXPECT_THAT(metadata, HasFrameSize(87U)); EXPECT_THAT(metadata, HasProtocol(ProtocolType::Compact)); + EXPECT_THAT(metadata, HasHeaderFlags(2)); EXPECT_THAT(metadata, HasSequenceId(2)); EXPECT_THAT(metadata, HasNoHeaders()); } @@ -341,7 +343,7 @@ TEST(HeaderTransportTest, InvalidInfoBlock) { buffer.writeBEInt(100); buffer.writeBEInt(0x0FFF); - buffer.writeBEInt(0); + buffer.writeBEInt(1); // header flags buffer.writeBEInt(1); // sequence number buffer.writeBEInt(1); // size 4 addSeq(buffer, {0, 0, 2, 0}); // 0 = binary proto, 0 = num transforms, 2 = unknown info id, pad @@ -350,6 +352,7 @@ TEST(HeaderTransportTest, InvalidInfoBlock) { EXPECT_TRUE(transport.decodeFrameStart(buffer, metadata)); EXPECT_THAT(metadata, HasFrameSize(86U)); EXPECT_THAT(metadata, HasProtocol(ProtocolType::Binary)); + EXPECT_THAT(metadata, HasHeaderFlags(1)); EXPECT_THAT(metadata, HasSequenceId(1)); EXPECT_THAT(metadata, HasNoHeaders()); EXPECT_EQ(buffer.length(), 0); diff --git a/test/extensions/filters/network/thrift_proxy/metadata_test.cc b/test/extensions/filters/network/thrift_proxy/metadata_test.cc index 6ca84143e6dce..1f1ff8c2ae8c3 100644 --- a/test/extensions/filters/network/thrift_proxy/metadata_test.cc +++ b/test/extensions/filters/network/thrift_proxy/metadata_test.cc @@ -37,6 +37,12 @@ TEST(MessageMetadataTest, Fields) { EXPECT_TRUE(metadata.hasMessageType()); EXPECT_EQ(MessageType::Call, metadata.messageType()); + EXPECT_FALSE(metadata.hasHeaderFlags()); + EXPECT_THROW(metadata.headerFlags(), absl::bad_optional_access); + metadata.setHeaderFlags(11); + EXPECT_TRUE(metadata.hasHeaderFlags()); + EXPECT_EQ(11, metadata.headerFlags()); + EXPECT_FALSE(metadata.hasSequenceId()); EXPECT_THROW(metadata.sequenceId(), absl::bad_optional_access); metadata.setSequenceId(101); diff --git a/test/extensions/filters/network/thrift_proxy/utility.h b/test/extensions/filters/network/thrift_proxy/utility.h index a19f9ef1b057e..a6edf0721bf94 100644 --- a/test/extensions/filters/network/thrift_proxy/utility.h +++ b/test/extensions/filters/network/thrift_proxy/utility.h @@ -149,6 +149,7 @@ MATCHER_P(HasFrameSize, n, "") { MATCHER_P(HasProtocol, p, "") { return arg.hasProtocol() && arg.protocol() == p; } MATCHER_P(HasSequenceId, id, "") { return arg.hasSequenceId() && arg.sequenceId() == id; } +MATCHER_P(HasHeaderFlags, flags, "") { return arg.hasHeaderFlags() && arg.headerFlags() == flags; } MATCHER(HasNoHeaders, "") { return arg.headers().size() == 0; } MATCHER_P2(HasAppException, t, m, "") { From 0b53a6bb612e197ada5b04931a0b7ddd4cab0dd9 Mon Sep 17 00:00:00 2001 From: asraa Date: Wed, 3 Nov 2021 16:50:25 -0500 Subject: [PATCH 015/110] [ci] disable multplexed integration test for coverage temporarily (#18883) Signed-off-by: Asra Ali --- test/integration/multiplexed_integration_test.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/integration/multiplexed_integration_test.cc b/test/integration/multiplexed_integration_test.cc index 730639e261171..e8b614867f04a 100644 --- a/test/integration/multiplexed_integration_test.cc +++ b/test/integration/multiplexed_integration_test.cc @@ -1859,6 +1859,8 @@ TEST_P(Http2IntegrationTest, OnLocalReply) { } } +// Disabled for coverage temporarily see #18881 +#if !defined(ENVOY_CONFIG_COVERAGE) TEST_P(Http2IntegrationTest, InvalidTrailers) { useAccessLog("%RESPONSE_CODE_DETAILS%"); autonomous_upstream_ = true; @@ -1878,6 +1880,7 @@ TEST_P(Http2IntegrationTest, InvalidTrailers) { // http2.invalid.header.field or http3.invalid_header_field EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("invalid")); } +#endif TEST_P(Http2IntegrationTest, InconsistentContentLength) { useAccessLog("%RESPONSE_CODE_DETAILS%"); From 6af9f9c5debcdf72c27e55d1b2d3c18184a05558 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Thu, 4 Nov 2021 05:29:02 -0700 Subject: [PATCH 016/110] =?UTF-8?q?test:=20Inline=20multiplexed=5Fupstream?= =?UTF-8?q?=5Fintegration=5Ftest.h=20into=20multipl=E2=80=A6=20(#18890)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit upstream: Inline multiplexed_upstream_integration_test.h into multiplexed_upstream_integration_test.cc Also rename Http2UpstreamIntegrationTest to MultiplexedUpstreamIntegrationTest since the tests run on HTTP/3 as well as HTTP/2. Risk Level: N/A Testing: N/A Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A Signed-off-by: Ryan Hamilton --- test/integration/BUILD | 1 - .../multiplexed_upstream_integration_test.cc | 101 +++++++++++------- .../multiplexed_upstream_integration_test.h | 27 ----- 3 files changed, 60 insertions(+), 69 deletions(-) delete mode 100644 test/integration/multiplexed_upstream_integration_test.h diff --git a/test/integration/BUILD b/test/integration/BUILD index 7861e4f63a6c5..e969701fa6f35 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -538,7 +538,6 @@ envoy_cc_test( name = "multiplexed_upstream_integration_test", srcs = [ "multiplexed_upstream_integration_test.cc", - "multiplexed_upstream_integration_test.h", ], shard_count = 2, deps = [ diff --git a/test/integration/multiplexed_upstream_integration_test.cc b/test/integration/multiplexed_upstream_integration_test.cc index 4acf747d777ce..1a30fda97d9db 100644 --- a/test/integration/multiplexed_upstream_integration_test.cc +++ b/test/integration/multiplexed_upstream_integration_test.cc @@ -1,5 +1,3 @@ -#include "test/integration/multiplexed_upstream_integration_test.h" - #include #include "envoy/config/bootstrap/v3/bootstrap.pb.h" @@ -8,6 +6,7 @@ #include "source/common/http/header_map_impl.h" #include "test/integration/autonomous_upstream.h" +#include "test/integration/http_protocol_integration.h" #include "test/test_common/printers.h" #include "test/test_common/utility.h" @@ -15,33 +14,53 @@ namespace Envoy { -INSTANTIATE_TEST_SUITE_P(Protocols, Http2UpstreamIntegrationTest, +class MultiplexedUpstreamIntegrationTest : public HttpProtocolIntegrationTest { +public: + void initialize() override { + upstream_tls_ = true; + config_helper_.configureUpstreamTls(use_alpn_, upstreamProtocol() == Http::CodecType::HTTP3); + HttpProtocolIntegrationTest::initialize(); + } + + void bidirectionalStreaming(uint32_t bytes); + void manySimultaneousRequests(uint32_t request_bytes, uint32_t max_response_bytes, + uint32_t num_streams = 50); + + bool use_alpn_{false}; + + uint64_t upstreamRxResetCounterValue(); + uint64_t upstreamTxResetCounterValue(); + uint64_t downstreamRxResetCounterValue(); + uint64_t downstreamTxResetCounterValue(); +}; + +INSTANTIATE_TEST_SUITE_P(Protocols, MultiplexedUpstreamIntegrationTest, testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( {Http::CodecType::HTTP2}, {Http::CodecType::HTTP2, Http::CodecType::HTTP3})), HttpProtocolIntegrationTest::protocolTestParamsToString); -TEST_P(Http2UpstreamIntegrationTest, RouterRequestAndResponseWithBodyNoBuffer) { +TEST_P(MultiplexedUpstreamIntegrationTest, RouterRequestAndResponseWithBodyNoBuffer) { testRouterRequestAndResponseWithBody(1024, 512, false); } -TEST_P(Http2UpstreamIntegrationTest, RouterRequestAndResponseWithZeroByteBodyNoBuffer) { +TEST_P(MultiplexedUpstreamIntegrationTest, RouterRequestAndResponseWithZeroByteBodyNoBuffer) { testRouterRequestAndResponseWithBody(0, 0, false); } -TEST_P(Http2UpstreamIntegrationTest, RouterHeaderOnlyRequestAndResponseNoBuffer) { +TEST_P(MultiplexedUpstreamIntegrationTest, RouterHeaderOnlyRequestAndResponseNoBuffer) { testRouterHeaderOnlyRequestAndResponse(); } -TEST_P(Http2UpstreamIntegrationTest, RouterUpstreamDisconnectBeforeRequestcomplete) { +TEST_P(MultiplexedUpstreamIntegrationTest, RouterUpstreamDisconnectBeforeRequestcomplete) { testRouterUpstreamDisconnectBeforeRequestComplete(); } -TEST_P(Http2UpstreamIntegrationTest, RouterUpstreamDisconnectBeforeResponseComplete) { +TEST_P(MultiplexedUpstreamIntegrationTest, RouterUpstreamDisconnectBeforeResponseComplete) { testRouterUpstreamDisconnectBeforeResponseComplete(); } -TEST_P(Http2UpstreamIntegrationTest, RouterDownstreamDisconnectBeforeRequestComplete) { +TEST_P(MultiplexedUpstreamIntegrationTest, RouterDownstreamDisconnectBeforeRequestComplete) { testRouterDownstreamDisconnectBeforeRequestComplete(); // Given the downstream disconnect, Envoy will reset the upstream stream. @@ -49,21 +68,21 @@ TEST_P(Http2UpstreamIntegrationTest, RouterDownstreamDisconnectBeforeRequestComp EXPECT_EQ(0, upstreamRxResetCounterValue()); } -TEST_P(Http2UpstreamIntegrationTest, RouterDownstreamDisconnectBeforeResponseComplete) { +TEST_P(MultiplexedUpstreamIntegrationTest, RouterDownstreamDisconnectBeforeResponseComplete) { testRouterDownstreamDisconnectBeforeResponseComplete(); } -TEST_P(Http2UpstreamIntegrationTest, RouterUpstreamResponseBeforeRequestComplete) { +TEST_P(MultiplexedUpstreamIntegrationTest, RouterUpstreamResponseBeforeRequestComplete) { testRouterUpstreamResponseBeforeRequestComplete(); } -TEST_P(Http2UpstreamIntegrationTest, Retry) { testRetry(); } +TEST_P(MultiplexedUpstreamIntegrationTest, Retry) { testRetry(); } -TEST_P(Http2UpstreamIntegrationTest, GrpcRetry) { testGrpcRetry(); } +TEST_P(MultiplexedUpstreamIntegrationTest, GrpcRetry) { testGrpcRetry(); } -TEST_P(Http2UpstreamIntegrationTest, Trailers) { testTrailers(1024, 2048, true, true); } +TEST_P(MultiplexedUpstreamIntegrationTest, Trailers) { testTrailers(1024, 2048, true, true); } -TEST_P(Http2UpstreamIntegrationTest, TestSchemeAndXFP) { +TEST_P(MultiplexedUpstreamIntegrationTest, TestSchemeAndXFP) { autonomous_upstream_ = true; initialize(); @@ -91,7 +110,7 @@ TEST_P(Http2UpstreamIntegrationTest, TestSchemeAndXFP) { } // Ensure Envoy handles streaming requests and responses simultaneously. -void Http2UpstreamIntegrationTest::bidirectionalStreaming(uint32_t bytes) { +void MultiplexedUpstreamIntegrationTest::bidirectionalStreaming(uint32_t bytes) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -126,32 +145,32 @@ void Http2UpstreamIntegrationTest::bidirectionalStreaming(uint32_t bytes) { EXPECT_TRUE(response->complete()); } -TEST_P(Http2UpstreamIntegrationTest, BidirectionalStreaming) { bidirectionalStreaming(1024); } +TEST_P(MultiplexedUpstreamIntegrationTest, BidirectionalStreaming) { bidirectionalStreaming(1024); } -TEST_P(Http2UpstreamIntegrationTest, LargeBidirectionalStreamingWithBufferLimits) { +TEST_P(MultiplexedUpstreamIntegrationTest, LargeBidirectionalStreamingWithBufferLimits) { config_helper_.setBufferLimits(1024, 1024); // Set buffer limits upstream and downstream. bidirectionalStreaming(1024 * 32); } -uint64_t Http2UpstreamIntegrationTest::upstreamRxResetCounterValue() { +uint64_t MultiplexedUpstreamIntegrationTest::upstreamRxResetCounterValue() { return test_server_ ->counter(absl::StrCat("cluster.cluster_0.", upstreamProtocolStatsRoot(), ".rx_reset")) ->value(); } -uint64_t Http2UpstreamIntegrationTest::upstreamTxResetCounterValue() { +uint64_t MultiplexedUpstreamIntegrationTest::upstreamTxResetCounterValue() { return test_server_ ->counter(absl::StrCat("cluster.cluster_0.", upstreamProtocolStatsRoot(), ".tx_reset")) ->value(); } -uint64_t Http2UpstreamIntegrationTest::downstreamRxResetCounterValue() { +uint64_t MultiplexedUpstreamIntegrationTest::downstreamRxResetCounterValue() { return test_server_->counter(absl::StrCat(downstreamProtocolStatsRoot(), ".rx_reset"))->value(); } -uint64_t Http2UpstreamIntegrationTest::downstreamTxResetCounterValue() { +uint64_t MultiplexedUpstreamIntegrationTest::downstreamTxResetCounterValue() { return test_server_->counter(absl::StrCat(downstreamProtocolStatsRoot(), ".tx_reset"))->value(); } -TEST_P(Http2UpstreamIntegrationTest, BidirectionalStreamingReset) { +TEST_P(MultiplexedUpstreamIntegrationTest, BidirectionalStreamingReset) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -193,18 +212,18 @@ TEST_P(Http2UpstreamIntegrationTest, BidirectionalStreamingReset) { EXPECT_EQ(1, downstreamTxResetCounterValue()); } -TEST_P(Http2UpstreamIntegrationTest, SimultaneousRequest) { +TEST_P(MultiplexedUpstreamIntegrationTest, SimultaneousRequest) { simultaneousRequest(1024, 512, 1023, 513); } -TEST_P(Http2UpstreamIntegrationTest, LargeSimultaneousRequestWithBufferLimits) { +TEST_P(MultiplexedUpstreamIntegrationTest, LargeSimultaneousRequestWithBufferLimits) { config_helper_.setBufferLimits(1024, 1024); // Set buffer limits upstream and downstream. simultaneousRequest(1024 * 20, 1024 * 14 + 2, 1024 * 10 + 5, 1024 * 16); } -void Http2UpstreamIntegrationTest::manySimultaneousRequests(uint32_t request_bytes, - uint32_t max_response_bytes, - uint32_t num_requests) { +void MultiplexedUpstreamIntegrationTest::manySimultaneousRequests(uint32_t request_bytes, + uint32_t max_response_bytes, + uint32_t num_requests) { TestRandomGenerator rand; std::vector encoders; std::vector responses; @@ -247,15 +266,15 @@ void Http2UpstreamIntegrationTest::manySimultaneousRequests(uint32_t request_byt EXPECT_EQ(0, test_server_->gauge("http2.pending_send_bytes")->value()); } -TEST_P(Http2UpstreamIntegrationTest, ManySimultaneousRequest) { +TEST_P(MultiplexedUpstreamIntegrationTest, ManySimultaneousRequest) { manySimultaneousRequests(1024, 1024, 100); } -TEST_P(Http2UpstreamIntegrationTest, TooManySimultaneousRequests) { +TEST_P(MultiplexedUpstreamIntegrationTest, TooManySimultaneousRequests) { manySimultaneousRequests(1024, 1024, 200); } -TEST_P(Http2UpstreamIntegrationTest, ManySimultaneousRequestsTightUpstreamLimits) { +TEST_P(MultiplexedUpstreamIntegrationTest, ManySimultaneousRequestsTightUpstreamLimits) { if (upstreamProtocol() == Http::CodecType::HTTP2) { return; } @@ -269,12 +288,12 @@ TEST_P(Http2UpstreamIntegrationTest, ManySimultaneousRequestsTightUpstreamLimits manySimultaneousRequests(1024, 1024, 10); } -TEST_P(Http2UpstreamIntegrationTest, ManyLargeSimultaneousRequestWithBufferLimits) { +TEST_P(MultiplexedUpstreamIntegrationTest, ManyLargeSimultaneousRequestWithBufferLimits) { config_helper_.setBufferLimits(1024, 1024); // Set buffer limits upstream and downstream. manySimultaneousRequests(1024 * 20, 1024 * 20); } -TEST_P(Http2UpstreamIntegrationTest, ManyLargeSimultaneousRequestWithRandomBackup) { +TEST_P(MultiplexedUpstreamIntegrationTest, ManyLargeSimultaneousRequestWithRandomBackup) { if (upstreamProtocol() == Http::CodecType::HTTP3 && downstreamProtocol() == Http::CodecType::HTTP2) { // This test depends on fragile preconditions. @@ -293,7 +312,7 @@ TEST_P(Http2UpstreamIntegrationTest, ManyLargeSimultaneousRequestWithRandomBacku manySimultaneousRequests(1024 * 20, 1024 * 20); } -TEST_P(Http2UpstreamIntegrationTest, UpstreamConnectionCloseWithManyStreams) { +TEST_P(MultiplexedUpstreamIntegrationTest, UpstreamConnectionCloseWithManyStreams) { config_helper_.setBufferLimits(1024, 1024); // Set buffer limits upstream and downstream. const uint32_t num_requests = 20; std::vector encoders; @@ -355,7 +374,7 @@ TEST_P(Http2UpstreamIntegrationTest, UpstreamConnectionCloseWithManyStreams) { } // Regression test for https://github.com/envoyproxy/envoy/issues/6744 -TEST_P(Http2UpstreamIntegrationTest, HittingEncoderFilterLimitForGrpc) { +TEST_P(MultiplexedUpstreamIntegrationTest, HittingEncoderFilterLimitForGrpc) { config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { @@ -423,7 +442,7 @@ name: router // Tests the default limit for the number of response headers is 100. Results in a stream reset if // exceeds. -TEST_P(Http2UpstreamIntegrationTest, TestManyResponseHeadersRejected) { +TEST_P(MultiplexedUpstreamIntegrationTest, TestManyResponseHeadersRejected) { // Default limit for response headers is 100. initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -442,7 +461,7 @@ TEST_P(Http2UpstreamIntegrationTest, TestManyResponseHeadersRejected) { } // Tests bootstrap configuration of max response headers. -TEST_P(Http2UpstreamIntegrationTest, ManyResponseHeadersAccepted) { +TEST_P(MultiplexedUpstreamIntegrationTest, ManyResponseHeadersAccepted) { // Set max response header count to 200. config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { ConfigHelper::HttpProtocolOptions protocol_options; @@ -470,7 +489,7 @@ TEST_P(Http2UpstreamIntegrationTest, ManyResponseHeadersAccepted) { } // Tests that HTTP/2 response headers over 60 kB are rejected and result in a stream reset. -TEST_P(Http2UpstreamIntegrationTest, LargeResponseHeadersRejected) { +TEST_P(MultiplexedUpstreamIntegrationTest, LargeResponseHeadersRejected) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -488,7 +507,7 @@ TEST_P(Http2UpstreamIntegrationTest, LargeResponseHeadersRejected) { // Regression test to make sure that configuring upstream logs over gRPC will not crash Envoy. // TODO(asraa): Test output of the upstream logs. // See https://github.com/envoyproxy/envoy/issues/8828. -TEST_P(Http2UpstreamIntegrationTest, ConfigureHttpOverGrpcLogs) { +TEST_P(MultiplexedUpstreamIntegrationTest, ConfigureHttpOverGrpcLogs) { config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { @@ -529,7 +548,7 @@ name: router } // Regression test for https://github.com/envoyproxy/envoy/issues/13933 -TEST_P(Http2UpstreamIntegrationTest, MultipleRequestsLowStreamLimit) { +TEST_P(MultiplexedUpstreamIntegrationTest, MultipleRequestsLowStreamLimit) { autonomous_upstream_ = true; envoy::config::core::v3::Http2ProtocolOptions config; config.mutable_max_concurrent_streams()->set_value(1); @@ -560,7 +579,7 @@ TEST_P(Http2UpstreamIntegrationTest, MultipleRequestsLowStreamLimit) { } // Regression test for https://github.com/envoyproxy/envoy/issues/13933 -TEST_P(Http2UpstreamIntegrationTest, UpstreamGoaway) { +TEST_P(MultiplexedUpstreamIntegrationTest, UpstreamGoaway) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); diff --git a/test/integration/multiplexed_upstream_integration_test.h b/test/integration/multiplexed_upstream_integration_test.h deleted file mode 100644 index 14aeb56a49c0b..0000000000000 --- a/test/integration/multiplexed_upstream_integration_test.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "test/integration/http_protocol_integration.h" - -#include "gtest/gtest.h" - -namespace Envoy { -class Http2UpstreamIntegrationTest : public HttpProtocolIntegrationTest { -public: - void initialize() override { - upstream_tls_ = true; - config_helper_.configureUpstreamTls(use_alpn_, upstreamProtocol() == Http::CodecType::HTTP3); - HttpProtocolIntegrationTest::initialize(); - } - - void bidirectionalStreaming(uint32_t bytes); - void manySimultaneousRequests(uint32_t request_bytes, uint32_t max_response_bytes, - uint32_t num_streams = 50); - - bool use_alpn_{false}; - - uint64_t upstreamRxResetCounterValue(); - uint64_t upstreamTxResetCounterValue(); - uint64_t downstreamRxResetCounterValue(); - uint64_t downstreamTxResetCounterValue(); -}; -} // namespace Envoy From 93754dd70c42fa6bb85ec674ab65661b70f25e70 Mon Sep 17 00:00:00 2001 From: Alexcei Date: Thu, 4 Nov 2021 17:11:04 +0300 Subject: [PATCH 017/110] oauth2 filter: saving id_token and refresh_token in the cookie if they are provided (#18353) Signed-off-by: Shmakov Aleksey --- .../http/http_filters/oauth2_filter.rst | 1 + docs/root/version_history/current.rst | 1 + .../extensions/filters/http/oauth2/filter.cc | 29 +++++++++++++++++-- .../extensions/filters/http/oauth2/filter.h | 7 ++++- source/extensions/filters/http/oauth2/oauth.h | 3 +- .../filters/http/oauth2/oauth_client.cc | 7 ++++- .../filters/http/oauth2/oauth_response.proto | 2 ++ .../filters/http/oauth2/filter_test.cc | 4 +++ .../filters/http/oauth2/oauth_test.cc | 5 ++-- 9 files changed, 51 insertions(+), 8 deletions(-) diff --git a/docs/root/configuration/http/http_filters/oauth2_filter.rst b/docs/root/configuration/http/http_filters/oauth2_filter.rst index d28789ca88899..c919c2af526a5 100644 --- a/docs/root/configuration/http/http_filters/oauth2_filter.rst +++ b/docs/root/configuration/http/http_filters/oauth2_filter.rst @@ -28,6 +28,7 @@ The OAuth filter's flow involves: :ref:`hmac_secret ` to assist in encoding. * The filter calls continueDecoding() to unblock the filter chain. +* The filter sets `IdToken` and `RefreshToken` cookies if they are provided by Identity provider along with `AccessToken`. When the authn server validates the client and returns an authorization token back to the OAuth filter, no matter what format that token is, if diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 3648d52837cc4..3712b2463d80a 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -62,6 +62,7 @@ New Features * listener: added API for extensions to access :ref:`typed_filter_metadata ` configured in the listener's :ref:`metadata ` field. * listener: added support for :ref:`MPTCP ` (multipath TCP). * oauth filter: added :ref:`cookie_names ` to allow overriding (default) cookie names (``BearerToken``, ``OauthHMAC``, and ``OauthExpires``) set by the filter. +* oauth filter: setting IdToken and RefreshToken cookies if they are provided by Identity provider along with AccessToken. * tcp: added a :ref:`FilterState ` :ref:`hash policy `, used by :ref:`TCP proxy ` to allow hashing load balancer algorithms to hash on objects in filter state. * tcp_proxy: added support to populate upstream http connect header values from stream info. * thrift_proxy: add upstream response zone metrics in the form ``cluster.cluster_name.zone.local_zone.upstream_zone.thrift.upstream_resp_success``. diff --git a/source/extensions/filters/http/oauth2/filter.cc b/source/extensions/filters/http/oauth2/filter.cc index 4f1def409668c..446e3394de779 100644 --- a/source/extensions/filters/http/oauth2/filter.cc +++ b/source/extensions/filters/http/oauth2/filter.cc @@ -44,6 +44,12 @@ constexpr const char* SignoutCookieValue = constexpr const char* SignoutBearerTokenValue = "{}=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"; +constexpr absl::string_view SignoutIdTokenValue = + "IdToken=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"; + +constexpr absl::string_view SignoutRefreshTokenValue = + "RefreshToken=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"; + constexpr const char* CookieTailFormatString = ";version=1;path=/;Max-Age={};secure"; constexpr const char* CookieTailHttpOnlyFormatString = @@ -149,11 +155,13 @@ void OAuth2CookieValidator::setParams(const Http::RequestHeaderMap& headers, const std::string& secret) { const auto& cookies = Http::Utility::parseCookies(headers, [this](absl::string_view key) -> bool { return key == cookie_names_.oauth_expires_ || key == cookie_names_.bearer_token_ || - key == cookie_names_.oauth_hmac_; + key == cookie_names_.oauth_hmac_ || key == "IdToken" || key == "RefreshToken"; }); expires_ = findValue(cookies, cookie_names_.oauth_expires_); token_ = findValue(cookies, cookie_names_.bearer_token_); + id_token_ = findValue(cookies, "IdToken"); + refresh_token_ = findValue(cookies, "RefreshToken"); hmac_ = findValue(cookies, cookie_names_.oauth_hmac_); host_ = headers.Host()->value().getStringView(); @@ -162,7 +170,7 @@ void OAuth2CookieValidator::setParams(const Http::RequestHeaderMap& headers, bool OAuth2CookieValidator::hmacIsValid() const { auto& crypto_util = Envoy::Common::Crypto::UtilitySingleton::get(); - const auto hmac_payload = absl::StrCat(host_, expires_, token_); + const auto hmac_payload = absl::StrCat(host_, expires_, token_, id_token_, refresh_token_); const auto pre_encoded_hmac = Hex::encode(crypto_util.getSha256Hmac(secret_, hmac_payload)); std::string encoded_hmac; absl::Base64Escape(pre_encoded_hmac, &encoded_hmac); @@ -407,6 +415,8 @@ Http::FilterHeadersStatus OAuth2Filter::signOutUser(const Http::RequestHeaderMap response_headers->addReferenceKey( Http::Headers::get().SetCookie, fmt::format(SignoutBearerTokenValue, config_->cookieNames().bearer_token_)); + response_headers->addReferenceKey(Http::Headers::get().SetCookie, SignoutIdTokenValue); + response_headers->addReferenceKey(Http::Headers::get().SetCookie, SignoutRefreshTokenValue); response_headers->setLocation(new_path); decoder_callbacks_->encodeHeaders(std::move(response_headers), true, SIGN_OUT); @@ -414,8 +424,12 @@ Http::FilterHeadersStatus OAuth2Filter::signOutUser(const Http::RequestHeaderMap } void OAuth2Filter::onGetAccessTokenSuccess(const std::string& access_code, + const std::string& id_token, + const std::string& refresh_token, std::chrono::seconds expires_in) { access_token_ = access_code; + id_token_ = id_token; + refresh_token_ = refresh_token; const auto new_epoch = time_source_.systemTime() + expires_in; new_expires_ = std::to_string( @@ -439,7 +453,7 @@ void OAuth2Filter::finishFlow() { std::string token_payload; if (config_->forwardBearerToken()) { - token_payload = absl::StrCat(host_, new_expires_, access_token_); + token_payload = absl::StrCat(host_, new_expires_, access_token_, id_token_, refresh_token_); } else { token_payload = absl::StrCat(host_, new_expires_); } @@ -480,6 +494,15 @@ void OAuth2Filter::finishFlow() { response_headers->addReferenceKey( Http::Headers::get().SetCookie, absl::StrCat(cookie_names.bearer_token_, "=", access_token_, cookie_tail)); + if (id_token_ != EMPTY_STRING) { + response_headers->addReferenceKey(Http::Headers::get().SetCookie, + absl::StrCat("IdToken=", id_token_, cookie_tail)); + } + + if (refresh_token_ != EMPTY_STRING) { + response_headers->addReferenceKey(Http::Headers::get().SetCookie, + absl::StrCat("RefreshToken=", refresh_token_, cookie_tail)); + } } response_headers->setLocation(state_); diff --git a/source/extensions/filters/http/oauth2/filter.h b/source/extensions/filters/http/oauth2/filter.h index 5b26f62b6446a..06838a5b65fff 100644 --- a/source/extensions/filters/http/oauth2/filter.h +++ b/source/extensions/filters/http/oauth2/filter.h @@ -197,6 +197,8 @@ class OAuth2CookieValidator : public CookieValidator { private: std::string token_; + std::string id_token_; + std::string refresh_token_; std::string expires_; std::string hmac_; std::vector secret_; @@ -219,7 +221,8 @@ class OAuth2Filter : public Http::PassThroughDecoderFilter, public FilterCallbac Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& headers, bool) override; // FilterCallbacks - void onGetAccessTokenSuccess(const std::string& access_code, + void onGetAccessTokenSuccess(const std::string& access_code, const std::string& id_token, + const std::string& refresh_token, std::chrono::seconds expires_in) override; // a catch-all function used for request failures. we don't retry, as a user can simply refresh // the page in the case of a network blip. @@ -235,6 +238,8 @@ class OAuth2Filter : public Http::PassThroughDecoderFilter, public FilterCallbac // wrap up some of these in a UserData struct or something... std::string auth_code_; std::string access_token_; // TODO - see if we can avoid this being a member variable + std::string id_token_; + std::string refresh_token_; std::string new_expires_; absl::string_view host_; std::string state_; diff --git a/source/extensions/filters/http/oauth2/oauth.h b/source/extensions/filters/http/oauth2/oauth.h index 709668c903447..f93087b9dcc6c 100644 --- a/source/extensions/filters/http/oauth2/oauth.h +++ b/source/extensions/filters/http/oauth2/oauth.h @@ -19,7 +19,8 @@ class FilterCallbacks { public: virtual ~FilterCallbacks() = default; - virtual void onGetAccessTokenSuccess(const std::string& access_token, + virtual void onGetAccessTokenSuccess(const std::string& access_token, const std::string& id_token, + const std::string& refresh_token, std::chrono::seconds expires_in) PURE; virtual void sendUnauthorizedResponse() PURE; diff --git a/source/extensions/filters/http/oauth2/oauth_client.cc b/source/extensions/filters/http/oauth2/oauth_client.cc index 24335ff6c3909..c597e1f1dd347 100644 --- a/source/extensions/filters/http/oauth2/oauth_client.cc +++ b/source/extensions/filters/http/oauth2/oauth_client.cc @@ -6,6 +6,7 @@ #include "envoy/http/message.h" #include "envoy/upstream/cluster_manager.h" +#include "source/common/common/empty_string.h" #include "source/common/common/fmt.h" #include "source/common/common/logger.h" #include "source/common/http/message_impl.h" @@ -95,8 +96,12 @@ void OAuth2ClientImpl::onSuccess(const Http::AsyncClient::Request&, } const std::string access_token{PROTOBUF_GET_WRAPPED_REQUIRED(response, access_token)}; + const std::string id_token{PROTOBUF_GET_WRAPPED_OR_DEFAULT(response, id_token, EMPTY_STRING)}; + const std::string refresh_token{ + PROTOBUF_GET_WRAPPED_OR_DEFAULT(response, refresh_token, EMPTY_STRING)}; const std::chrono::seconds expires_in{PROTOBUF_GET_WRAPPED_REQUIRED(response, expires_in)}; - parent_->onGetAccessTokenSuccess(access_token, expires_in); + + parent_->onGetAccessTokenSuccess(access_token, id_token, refresh_token, expires_in); } void OAuth2ClientImpl::onFailure(const Http::AsyncClient::Request&, diff --git a/source/extensions/filters/http/oauth2/oauth_response.proto b/source/extensions/filters/http/oauth2/oauth_response.proto index 09d305dcbef48..48645dce7b99c 100644 --- a/source/extensions/filters/http/oauth2/oauth_response.proto +++ b/source/extensions/filters/http/oauth2/oauth_response.proto @@ -7,4 +7,6 @@ import "google/protobuf/wrappers.proto"; message OAuthResponse { google.protobuf.StringValue access_token = 1; google.protobuf.UInt64Value expires_in = 2; + google.protobuf.StringValue id_token = 3; + google.protobuf.StringValue refresh_token = 4; } diff --git a/test/extensions/filters/http/oauth2/filter_test.cc b/test/extensions/filters/http/oauth2/filter_test.cc index 52b2e81b97002..80445e0effdcd 100644 --- a/test/extensions/filters/http/oauth2/filter_test.cc +++ b/test/extensions/filters/http/oauth2/filter_test.cc @@ -367,6 +367,10 @@ TEST_F(OAuth2Test, RequestSignout) { "OauthHMAC=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"}, {Http::Headers::get().SetCookie.get(), "BearerToken=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"}, + {Http::Headers::get().SetCookie.get(), + "IdToken=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"}, {Http::Headers::get().Location.get(), "https://traffic.example.com/"}, }; EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); diff --git a/test/extensions/filters/http/oauth2/oauth_test.cc b/test/extensions/filters/http/oauth2/oauth_test.cc index d7f9a29d443b8..7f9ea40d86636 100644 --- a/test/extensions/filters/http/oauth2/oauth_test.cc +++ b/test/extensions/filters/http/oauth2/oauth_test.cc @@ -28,7 +28,8 @@ using testing::Return; class MockCallbacks : public FilterCallbacks { public: MOCK_METHOD(void, sendUnauthorizedResponse, ()); - MOCK_METHOD(void, onGetAccessTokenSuccess, (const std::string&, std::chrono::seconds)); + MOCK_METHOD(void, onGetAccessTokenSuccess, + (const std::string&, const std::string&, const std::string&, std::chrono::seconds)); }; class OAuth2ClientTest : public testing::Test { @@ -96,7 +97,7 @@ TEST_F(OAuth2ClientTest, RequestAccessTokenSuccess) { client_->setCallbacks(*mock_callbacks_); client_->asyncGetAccessToken("a", "b", "c", "d"); EXPECT_EQ(1, callbacks_.size()); - EXPECT_CALL(*mock_callbacks_, onGetAccessTokenSuccess(_, _)); + EXPECT_CALL(*mock_callbacks_, onGetAccessTokenSuccess(_, _, _, _)); Http::MockAsyncClientRequest request(&cm_.thread_local_cluster_.async_client_); ASSERT_TRUE(popPendingCallback( [&](auto* callback) { callback->onSuccess(request, std::move(mock_response)); })); From 97c8d437121a24ebeab0d2ceb9c07c7760a1755b Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Thu, 4 Nov 2021 10:17:44 -0400 Subject: [PATCH 018/110] dfp: fixing test for systems without v4 support (#18882) Signed-off-by: Alyssa Wilk --- .../dynamic_forward_proxy/proxy_filter_integration_test.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_integration_test.cc b/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_integration_test.cc index c138d28fbb59b..b8d03fa681b98 100644 --- a/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_integration_test.cc +++ b/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_integration_test.cc @@ -429,7 +429,11 @@ TEST_P(ProxyFilterIntegrationTest, UseCacheFileAndTestHappyEyeballs) { "true"); use_cache_file_ = true; // Prepend a bad address - cache_file_value_contents_ = "99.99.99.99:1|1000000|0\n"; + if (GetParam() == Network::Address::IpVersion::v4) { + cache_file_value_contents_ = "99.99.99.99:1|1000000|0\n"; + } else { + cache_file_value_contents_ = "[::99]:1|1000000|0\n"; + } initializeWithArgs(); codec_client_ = makeHttpConnection(lookupPort("http")); From 5317f7c5b9cfc5eb2e81bdc85ee07a4fb310e2eb Mon Sep 17 00:00:00 2001 From: theidexisted Date: Thu, 4 Nov 2021 22:32:20 +0800 Subject: [PATCH 019/110] rocketmq_proxy: Simplify the convertion for absl::string_view (#18811) Signed-off-by: theidexisted --- contrib/rocketmq_proxy/filters/network/source/protocol.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contrib/rocketmq_proxy/filters/network/source/protocol.h b/contrib/rocketmq_proxy/filters/network/source/protocol.h index 03082f3398c3f..2de55a04130fa 100644 --- a/contrib/rocketmq_proxy/filters/network/source/protocol.h +++ b/contrib/rocketmq_proxy/filters/network/source/protocol.h @@ -614,7 +614,7 @@ class GetConsumerListByGroupResponseBody { void encode(ProtobufWkt::Struct& root); void add(absl::string_view consumer_id) { - consumer_id_list_.emplace_back(std::string(consumer_id.data(), consumer_id.length())); + consumer_id_list_.emplace_back(consumer_id.data(), consumer_id.length()); } private: @@ -657,8 +657,7 @@ class MetadataHelper { struct AckMessageDirective { AckMessageDirective(absl::string_view broker_name, int32_t broker_id, MonotonicTime create_time) - : broker_name_(broker_name.data(), broker_name.length()), broker_id_(broker_id), - creation_time_(create_time) {} + : broker_name_(broker_name), broker_id_(broker_id), creation_time_(create_time) {} std::string broker_name_; int32_t broker_id_; From c75c1410c8682cb44c9136ce4ad01e6a58e16e8e Mon Sep 17 00:00:00 2001 From: Sotiris Nanopoulos Date: Thu, 4 Nov 2021 07:36:43 -0700 Subject: [PATCH 020/110] stats: Add universal stats tag from CLI (#18668) Fixes #1975 Adds a CLI parameter that can be repeated that acts as universal tags for all stats Additional Description: Risk Level: Low, additional parameter Testing: Unit + integration + manual Docs Changes: Provided Release Notes: Pending Platform Specific Features: N/A Signed-off-by: Sotiris Nanopoulos --- api/envoy/admin/v3/server_info.proto | 5 ++- docs/root/operations/cli.rst | 6 ++++ envoy/server/options.h | 7 ++++ source/common/config/utility.cc | 5 +-- source/common/config/utility.h | 4 ++- source/common/config/well_known_names.cc | 37 +++++++++++++------ source/common/config/well_known_names.h | 2 ++ source/common/stats/BUILD | 1 + source/common/stats/tag_producer_impl.cc | 13 ++++++- source/common/stats/tag_producer_impl.h | 4 +++ source/common/stats/tag_utility.cc | 17 +++++++++ source/common/stats/tag_utility.h | 5 +++ source/server/BUILD | 1 + source/server/config_validation/server.cc | 2 +- source/server/options_impl.cc | 40 +++++++++++++++++++++ source/server/options_impl.h | 4 +++ source/server/server.cc | 2 +- test/common/config/utility_test.cc | 16 +++++++-- test/common/stats/tag_producer_impl_test.cc | 21 ++++++++--- test/mocks/server/options.cc | 1 + test/mocks/server/options.h | 2 ++ test/server/options_impl_test.cc | 39 ++++++++++++++++++++ 22 files changed, 208 insertions(+), 26 deletions(-) diff --git a/api/envoy/admin/v3/server_info.proto b/api/envoy/admin/v3/server_info.proto index 15b2c74e88310..ac1688fa10f18 100644 --- a/api/envoy/admin/v3/server_info.proto +++ b/api/envoy/admin/v3/server_info.proto @@ -58,7 +58,7 @@ message ServerInfo { config.core.v3.Node node = 7; } -// [#next-free-field: 38] +// [#next-free-field: 39] message CommandLineOptions { option (udpa.annotations.versioning).previous_message_type = "envoy.admin.v2alpha.CommandLineOptions"; @@ -189,4 +189,7 @@ message CommandLineOptions { // See :option:`--enable-core-dump` for details. bool enable_core_dump = 37; + + // See :option:`--stats-tag` for details. + repeated string stats_tag = 38; } diff --git a/docs/root/operations/cli.rst b/docs/root/operations/cli.rst index 11eb0480fff0e..2e3acf908827f 100644 --- a/docs/root/operations/cli.rst +++ b/docs/root/operations/cli.rst @@ -359,3 +359,9 @@ following are the command line options that Envoy supports. It enables core dumps by invoking `prctl `_ using the PR_SET_DUMPABLE option. This is useful for container environments when using capabilities, given that when Envoy has more capabilities than its base environment core dumping will be disabled by the kernel. + +.. option:: --stats-tag + + *(optional)* This flag provides a universal tag for all stats generated by Envoy. The format is ``tag:value``. Only + alphanumeric values are allowed for tag names. For tag values all characters are permitted except for '.' (dot). + This flag can be repeated multiple times to set multiple universal tags. Multiple values for the same tag name are not allowed. diff --git a/envoy/server/options.h b/envoy/server/options.h index 3968b7f88ca34..f029a54d1335c 100644 --- a/envoy/server/options.h +++ b/envoy/server/options.h @@ -8,6 +8,7 @@ #include "envoy/common/pure.h" #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/network/address.h" +#include "envoy/stats/tag.h" #include "absl/types/optional.h" #include "spdlog/spdlog.h" @@ -259,6 +260,12 @@ class Options { * @return the mode of socket file. */ virtual mode_t socketMode() const PURE; + + /** + * @return the stats tags provided by the cli. Tags may contain duplicates. It is the + * responsibility of the caller to handle the duplicates. + */ + virtual const Stats::TagVector& statsTags() const PURE; }; } // namespace Server diff --git a/source/common/config/utility.cc b/source/common/config/utility.cc index 3c97063d91fa0..aaaa0816abecf 100644 --- a/source/common/config/utility.cc +++ b/source/common/config/utility.cc @@ -215,8 +215,9 @@ Utility::parseRateLimitSettings(const envoy::config::core::v3::ApiConfigSource& } Stats::TagProducerPtr -Utility::createTagProducer(const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - return std::make_unique(bootstrap.stats_config()); +Utility::createTagProducer(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, + const Stats::TagVector& cli_tags) { + return std::make_unique(bootstrap.stats_config(), cli_tags); } Stats::StatsMatcherPtr diff --git a/source/common/config/utility.h b/source/common/config/utility.h index 45e415d2c7024..80bbdc69e587c 100644 --- a/source/common/config/utility.h +++ b/source/common/config/utility.h @@ -422,10 +422,12 @@ class Utility { * Create TagProducer instance. Check all tag names for conflicts to avoid * unexpected tag name overwriting. * @param bootstrap bootstrap proto. + * @param cli_tags tags that are provided by the cli * @throws EnvoyException when the conflict of tag names is found. */ static Stats::TagProducerPtr - createTagProducer(const envoy::config::bootstrap::v3::Bootstrap& bootstrap); + createTagProducer(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, + const Stats::TagVector& cli_tags); /** * Create StatsMatcher instance. diff --git a/source/common/config/well_known_names.cc b/source/common/config/well_known_names.cc index 71a03839b1a7b..09eaa6532422a 100644 --- a/source/common/config/well_known_names.cc +++ b/source/common/config/well_known_names.cc @@ -7,6 +7,8 @@ namespace Config { namespace { +const absl::string_view TAG_VALUE_REGEX = R"([^\.]+)"; + // To allow for more readable regular expressions to be declared below, and to // reduce duplication, define a few common pattern substitutions for regex // segments. @@ -18,14 +20,23 @@ std::string expandRegex(const std::string& regex) { // underscores. {"", R"([\w-]+)"}, // A generic name can contain any character except dots. - {"", R"([^\.]+)"}, + {"", TAG_VALUE_REGEX}, // Route names may contain dots in addition to alphanumerics and // dashes with underscores. {"", R"([\w-\.]+)"}}); } +const Regex::CompiledGoogleReMatcher& validTagValueRegex() { + CONSTRUCT_ON_FIRST_USE(Regex::CompiledGoogleReMatcher, absl::StrCat("^", TAG_VALUE_REGEX, "$"), + false); +} + } // namespace +bool doesTagNameValueMatchInvalidCharRegex(absl::string_view name) { + return validTagValueRegex().match(name); +} + TagNameValues::TagNameValues() { // Note: the default regexes are defined below in the order that they will typically be matched // (see the TagExtractor class definition for an explanation of the iterative matching process). @@ -53,22 +64,25 @@ TagNameValues::TagNameValues() { addRe2(RESPONSE_CODE_CLASS, R"(_rq_((\d))xx$)", "_rq_"); // http.[.]dynamodb.table.[.]capacity.[.](__partition_id=) - addRe2(DYNAMO_PARTITION_ID, - R"(^http\.\.dynamodb\.table\.\.capacity\.(\.__partition_id=(\w{7}))$)", - ".dynamodb.table."); + addRe2( + DYNAMO_PARTITION_ID, + R"(^http\.\.dynamodb\.table\.\.capacity\.(\.__partition_id=(\w{7}))$)", + ".dynamodb.table."); // http.[.]dynamodb.operation.(.)* or // http.[.]dynamodb.table.[.]capacity.(.)[] - addRe2(DYNAMO_OPERATION, - R"(^http\.\.dynamodb.(?:operation|table\.\.capacity)(\.())(?:\.|$))", - ".dynamodb."); + addRe2( + DYNAMO_OPERATION, + R"(^http\.\.dynamodb.(?:operation|table\.\.capacity)(\.())(?:\.|$))", + ".dynamodb."); // mongo.[.]collection.[.]callsite.(.)query.* addTokenized(MONGO_CALLSITE, "mongo.*.collection.*.callsite.$.query.**"); // http.[.]dynamodb.table.(.)* or // http.[.]dynamodb.error.(.)* - addRe2(DYNAMO_TABLE, R"(^http\.\.dynamodb.(?:table|error)\.(()\.))", ".dynamodb."); + addRe2(DYNAMO_TABLE, R"(^http\.\.dynamodb.(?:table|error)\.(()\.))", + ".dynamodb."); // mongo.[.]collection.(.)query.* addTokenized(MONGO_COLLECTION, "mongo.*.collection.$.**.query.*"); @@ -92,7 +106,8 @@ TagNameValues::TagNameValues() { addRe2(SSL_CIPHER, R"(^listener\..*?\.ssl\.cipher(\.())$)"); // cluster.[.]ssl.ciphers.() - addRe2(SSL_CIPHER_SUITE, R"(^cluster\.\.ssl\.ciphers(\.())$)", ".ssl.ciphers."); + addRe2(SSL_CIPHER_SUITE, R"(^cluster\.\.ssl\.ciphers(\.())$)", + ".ssl.ciphers."); // cluster.[.]grpc.(.)* addTokenized(GRPC_BRIDGE_SERVICE, "cluster.*.grpc.$.**"); @@ -115,7 +130,7 @@ TagNameValues::TagNameValues() { // listener.[
.]http.(.)* // The
part can be anything here (.*?) for the sake of a simpler // internal state of the regex which performs better. - addRe2(HTTP_CONN_MANAGER_PREFIX, R"(^listener\..*?\.http\.(()\.))", ".http."); + addRe2(HTTP_CONN_MANAGER_PREFIX, R"(^listener\..*?\.http\.(()\.))", ".http."); // http.(.)* addTokenized(HTTP_CONN_MANAGER_PREFIX, "http.$.**"); @@ -132,7 +147,7 @@ TagNameValues::TagNameValues() { // http.[.]rds.(.) // Note: can contain dots thus we have to maintain full // match. - addRe2(RDS_ROUTE_CONFIG, R"(^http\.\.rds\.(()\.)\w+?$)", ".rds."); + addRe2(RDS_ROUTE_CONFIG, R"(^http\.\.rds\.(()\.)\w+?$)", ".rds."); // listener_manager.(worker_.)* addRe2(WORKER_ID, R"(^listener_manager\.((worker_\d+)\.))", "listener_manager.worker_"); diff --git a/source/common/config/well_known_names.h b/source/common/config/well_known_names.h index 0b694392a4aeb..dd69131e8e24f 100644 --- a/source/common/config/well_known_names.h +++ b/source/common/config/well_known_names.h @@ -12,6 +12,8 @@ namespace Envoy { namespace Config { +bool doesTagNameValueMatchInvalidCharRegex(absl::string_view name); + /** * Well-known address resolver names. */ diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index 7e3ef609fba5b..122d5d66ca4a2 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -90,6 +90,7 @@ envoy_cc_library( ":symbol_table_lib", "//envoy/stats:stats_interface", "//envoy/stats:symbol_table_interface", + "//source/common/config:well_known_names", ], ) diff --git a/source/common/stats/tag_producer_impl.cc b/source/common/stats/tag_producer_impl.cc index 85384e7eb2ec2..b21f5895d4ae0 100644 --- a/source/common/stats/tag_producer_impl.cc +++ b/source/common/stats/tag_producer_impl.cc @@ -11,11 +11,22 @@ namespace Envoy { namespace Stats { -TagProducerImpl::TagProducerImpl(const envoy::config::metrics::v3::StatsConfig& config) { +TagProducerImpl::TagProducerImpl(const envoy::config::metrics::v3::StatsConfig& config) + : TagProducerImpl(config, {}) {} + +TagProducerImpl::TagProducerImpl(const envoy::config::metrics::v3::StatsConfig& config, + const Stats::TagVector& cli_tags) { // To check name conflict. reserveResources(config); absl::node_hash_set names = addDefaultExtractors(config); + for (const auto& cli_tag : cli_tags) { + if (!names.emplace(cli_tag.name_).second) { + throw EnvoyException(fmt::format("Tag name '{}' specified twice.", cli_tag.name_)); + } + default_tags_.emplace_back(cli_tag); + } + for (const auto& tag_specifier : config.stats_tags()) { const std::string& name = tag_specifier.tag_name(); if (!names.emplace(name).second) { diff --git a/source/common/stats/tag_producer_impl.h b/source/common/stats/tag_producer_impl.h index ff5dc3d34fb10..1c05c2083d24e 100644 --- a/source/common/stats/tag_producer_impl.h +++ b/source/common/stats/tag_producer_impl.h @@ -28,7 +28,11 @@ namespace Stats { */ class TagProducerImpl : public TagProducer { public: + TagProducerImpl(const envoy::config::metrics::v3::StatsConfig& config, + const Stats::TagVector& cli_tags); + TagProducerImpl(const envoy::config::metrics::v3::StatsConfig& config); + TagProducerImpl() = default; /** diff --git a/source/common/stats/tag_utility.cc b/source/common/stats/tag_utility.cc index b73ff3d81fe20..550bdf81180bf 100644 --- a/source/common/stats/tag_utility.cc +++ b/source/common/stats/tag_utility.cc @@ -1,5 +1,8 @@ #include "source/common/stats/tag_utility.h" +#include + +#include "source/common/config/well_known_names.h" #include "source/common/stats/symbol_table_impl.h" namespace Envoy { @@ -48,6 +51,20 @@ SymbolTable::StoragePtr TagStatNameJoiner::joinNameAndTags(StatName name, return symbol_table.join(stat_names); } + +bool isTagValueValid(absl::string_view name) { + return Config::doesTagNameValueMatchInvalidCharRegex(name); +} + +bool isTagNameValid(absl::string_view value) { + for (const auto& token : value) { + if (!absl::ascii_isalnum(token)) { + return false; + } + } + return true; +} + } // namespace TagUtility } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/tag_utility.h b/source/common/stats/tag_utility.h index b3e56e7737b3d..d0961cf0f3e82 100644 --- a/source/common/stats/tag_utility.h +++ b/source/common/stats/tag_utility.h @@ -54,6 +54,11 @@ class TagStatNameJoiner { SymbolTable::StoragePtr joinNameAndTags(StatName name, const StatNameTagVector& stat_name_tags, SymbolTable& symbol_table); }; + +bool isTagNameValid(absl::string_view name); + +bool isTagValueValid(absl::string_view value); + } // namespace TagUtility } // namespace Stats } // namespace Envoy diff --git a/source/server/BUILD b/source/server/BUILD index fe5c9056f6558..12886dfd10aad 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -388,6 +388,7 @@ envoy_cc_library( "//source/common/common:macros", "//source/common/protobuf:utility_lib", "//source/common/stats:stats_lib", + "//source/common/stats:tag_utility_lib", "//source/common/version:version_lib", ], ) diff --git a/source/server/config_validation/server.cc b/source/server/config_validation/server.cc index 57eba26cabcbb..7dcf1596703cd 100644 --- a/source/server/config_validation/server.cc +++ b/source/server/config_validation/server.cc @@ -82,7 +82,7 @@ void ValidationInstance::initialize(const Options& options, InstanceUtil::loadBootstrapConfig(bootstrap_, options, messageValidationContext().staticValidationVisitor(), *api_); - Config::Utility::createTagProducer(bootstrap_); + Config::Utility::createTagProducer(bootstrap_, options_.statsTags()); if (!bootstrap_.node().user_agent_build_version().has_version()) { *bootstrap_.mutable_node()->mutable_user_agent_build_version() = VersionInfo::buildVersion(); } diff --git a/source/server/options_impl.cc b/source/server/options_impl.cc index 7ef794c2098fb..7551c23f6141f 100644 --- a/source/server/options_impl.cc +++ b/source/server/options_impl.cc @@ -11,6 +11,7 @@ #include "source/common/common/logger.h" #include "source/common/common/macros.h" #include "source/common/protobuf/utility.h" +#include "source/common/stats/tag_utility.h" #include "source/common/version/version.h" #include "source/server/options_impl_platform.h" @@ -151,6 +152,14 @@ OptionsImpl::OptionsImpl(std::vector args, "600", "string", cmd); TCLAP::SwitchArg enable_core_dump("", "enable-core-dump", "Enable core dumps", cmd, false); + TCLAP::MultiArg stats_tag( + "", "stats-tag", + "This flag provides a universal tag for all stats generated by Envoy. The format is " + "``tag:value``. Only alphanumeric values are allowed for tag names. For tag values all " + "characters are permitted except for '.' (dot). This flag can be repeated multiple times to " + "set multiple universal tags. Multiple values for the same tag name are not allowed.", + false, "string", cmd); + cmd.setExceptionHandling(false); TRY_ASSERT_MAIN_THREAD { cmd.parse(args); @@ -282,6 +291,34 @@ OptionsImpl::OptionsImpl(std::vector args, if (!disable_extensions.getValue().empty()) { disabled_extensions_ = absl::StrSplit(disable_extensions.getValue(), ','); } + + if (!stats_tag.getValue().empty()) { + for (const auto& cli_tag_pair : stats_tag.getValue()) { + + std::vector cli_tag_pair_tokens = + absl::StrSplit(cli_tag_pair, absl::MaxSplits(':', 1)); + if (cli_tag_pair_tokens.size() != 2) { + throw MalformedArgvException( + fmt::format("error: misformatted stats-tag '{}'", cli_tag_pair)); + } + + auto name = cli_tag_pair_tokens[0]; + if (!Stats::TagUtility::isTagNameValid(name)) { + throw MalformedArgvException( + fmt::format("error: misformatted stats-tag '{}' contains invalid char in '{}'", + cli_tag_pair, name)); + } + + auto value = cli_tag_pair_tokens[1]; + if (!Stats::TagUtility::isTagValueValid(value)) { + throw MalformedArgvException( + fmt::format("error: misformatted stats-tag '{}' contains invalid char in '{}'", + cli_tag_pair, value)); + } + + stats_tags_.emplace_back(Stats::Tag{std::string(name), std::string(value)}); + } + } } spdlog::level::level_enum OptionsImpl::parseAndValidateLogLevel(absl::string_view log_level) { @@ -396,6 +433,9 @@ Server::CommandLineOptionsPtr OptionsImpl::toCommandLineOptions() const { } command_line_options->set_socket_path(socketPath()); command_line_options->set_socket_mode(socketMode()); + for (const auto& tag : statsTags()) { + command_line_options->add_stats_tag(fmt::format("{}:{}", tag.name_, tag.value_)); + } return command_line_options; } diff --git a/source/server/options_impl.h b/source/server/options_impl.h index 697dee2440f15..3c4426ade7aef 100644 --- a/source/server/options_impl.h +++ b/source/server/options_impl.h @@ -104,6 +104,8 @@ class OptionsImpl : public Server::Options, protected Logger::Loggable disabled_extensions_; + Stats::TagVector stats_tags_; uint32_t count_{0}; // Initialization added here to avoid integration_admin_test failure caused by uninitialized diff --git a/source/server/server.cc b/source/server/server.cc index 9bb2a226a3e31..6879298a5a941 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -397,7 +397,7 @@ void InstanceImpl::initialize(Network::Address::InstanceConstSharedPtr local_add // Needs to happen as early as possible in the instantiation to preempt the objects that require // stats. - stats_store_.setTagProducer(Config::Utility::createTagProducer(bootstrap_)); + stats_store_.setTagProducer(Config::Utility::createTagProducer(bootstrap_, options_.statsTags())); stats_store_.setStatsMatcher( Config::Utility::createStatsMatcher(bootstrap_, stats_store_.symbolTable())); stats_store_.setHistogramSettings(Config::Utility::createHistogramSettings(bootstrap_)); diff --git a/test/common/config/utility_test.cc b/test/common/config/utility_test.cc index f822f37e8cc80..c55a580295dbc 100644 --- a/test/common/config/utility_test.cc +++ b/test/common/config/utility_test.cc @@ -98,14 +98,24 @@ TEST(UtilityTest, TranslateApiConfigSource) { TEST(UtilityTest, createTagProducer) { envoy::config::bootstrap::v3::Bootstrap bootstrap; - auto producer = Utility::createTagProducer(bootstrap); - ASSERT(producer != nullptr); - std::vector tags; + auto producer = Utility::createTagProducer(bootstrap, {}); + ASSERT_TRUE(producer != nullptr); + Stats::TagVector tags; auto extracted_name = producer->produceTags("http.config_test.rq_total", tags); ASSERT_EQ(extracted_name, "http.rq_total"); ASSERT_EQ(tags.size(), 1); } +TEST(UtilityTest, createTagProducerWithDefaultTgs) { + envoy::config::bootstrap::v3::Bootstrap bootstrap; + auto producer = Utility::createTagProducer(bootstrap, {{"foo", "bar"}}); + ASSERT_TRUE(producer != nullptr); + Stats::TagVector tags; + auto extracted_name = producer->produceTags("http.config_test.rq_total", tags); + EXPECT_EQ(extracted_name, "http.rq_total"); + EXPECT_EQ(tags.size(), 2); +} + TEST(UtilityTest, CheckFilesystemSubscriptionBackingPath) { Api::ApiPtr api = Api::createApiForTest(); diff --git a/test/common/stats/tag_producer_impl_test.cc b/test/common/stats/tag_producer_impl_test.cc index db7be62da02f5..c00074c9b58a1 100644 --- a/test/common/stats/tag_producer_impl_test.cc +++ b/test/common/stats/tag_producer_impl_test.cc @@ -17,22 +17,33 @@ TEST(TagProducerTest, CheckConstructor) { auto& tag_specifier1 = *stats_config.mutable_stats_tags()->Add(); tag_specifier1.set_tag_name("test.x"); tag_specifier1.set_fixed_value("xxx"); - EXPECT_NO_THROW(TagProducerImpl{stats_config}); + EXPECT_NO_THROW(TagProducerImpl(stats_config, {})); + EXPECT_NO_THROW(TagProducerImpl(stats_config, {{"test.y", "yyy"}})); + + // Should raise an error when duplicate tag names between cli and config. + EXPECT_THROW_WITH_MESSAGE(TagProducerImpl(stats_config, {{"test.x", "yyy"}}), EnvoyException, + fmt::format("Tag name '{}' specified twice.", "test.x")); // Should raise an error when duplicate tag names are specified. auto& tag_specifier2 = *stats_config.mutable_stats_tags()->Add(); tag_specifier2.set_tag_name("test.x"); tag_specifier2.set_fixed_value("yyy"); - EXPECT_THROW_WITH_MESSAGE(TagProducerImpl{stats_config}, EnvoyException, + EXPECT_THROW_WITH_MESSAGE(TagProducerImpl(stats_config, {{"test.y", "yyy"}}), EnvoyException, fmt::format("Tag name '{}' specified twice.", "test.x")); + // Should raise an error when a cli tag names conflicts with Envoy's default tag names. + EXPECT_THROW_WITH_MESSAGE( + TagProducerImpl(stats_config, {{Config::TagNames::get().CLUSTER_NAME, "yyy"}}), + EnvoyException, + fmt::format("Tag name '{}' specified twice.", Config::TagNames::get().CLUSTER_NAME)); + // Also should raise an error when user defined tag name conflicts with Envoy's default tag names. stats_config.clear_stats_tags(); stats_config.mutable_use_all_default_tags()->set_value(true); auto& custom_tag_extractor = *stats_config.mutable_stats_tags()->Add(); custom_tag_extractor.set_tag_name(Config::TagNames::get().CLUSTER_NAME); EXPECT_THROW_WITH_MESSAGE( - TagProducerImpl{stats_config}, EnvoyException, + TagProducerImpl(stats_config, {}), EnvoyException, fmt::format("Tag name '{}' specified twice.", Config::TagNames::get().CLUSTER_NAME)); // Non-default custom name without regex should throw @@ -41,7 +52,7 @@ TEST(TagProducerTest, CheckConstructor) { custom_tag_extractor = *stats_config.mutable_stats_tags()->Add(); custom_tag_extractor.set_tag_name("test_extractor"); EXPECT_THROW_WITH_MESSAGE( - TagProducerImpl{stats_config}, EnvoyException, + TagProducerImpl(stats_config, {}), EnvoyException, "No regex specified for tag specifier and no default regex for name: 'test_extractor'"); // Also empty regex should throw @@ -51,7 +62,7 @@ TEST(TagProducerTest, CheckConstructor) { custom_tag_extractor.set_tag_name("test_extractor"); custom_tag_extractor.set_regex(""); EXPECT_THROW_WITH_MESSAGE( - TagProducerImpl{stats_config}, EnvoyException, + TagProducerImpl(stats_config, {}), EnvoyException, "No regex specified for tag specifier and no default regex for name: 'test_extractor'"); } diff --git a/test/mocks/server/options.cc b/test/mocks/server/options.cc index 8c48e026f81dd..d947cccf1c9e5 100644 --- a/test/mocks/server/options.cc +++ b/test/mocks/server/options.cc @@ -45,6 +45,7 @@ MockOptions::MockOptions(const std::string& config_path) : config_path_(config_p })); ON_CALL(*this, socketPath()).WillByDefault(ReturnRef(socket_path_)); ON_CALL(*this, socketMode()).WillByDefault(ReturnPointee(&socket_mode_)); + ON_CALL(*this, statsTags()).WillByDefault(ReturnRef(stats_tags_)); } MockOptions::~MockOptions() = default; diff --git a/test/mocks/server/options.h b/test/mocks/server/options.h index 35bc8ff5c26aa..fb64f4f1b6642 100644 --- a/test/mocks/server/options.h +++ b/test/mocks/server/options.h @@ -52,6 +52,7 @@ class MockOptions : public Options { MOCK_METHOD(Server::CommandLineOptionsPtr, toCommandLineOptions, (), (const)); MOCK_METHOD(const std::string&, socketPath, (), (const)); MOCK_METHOD(mode_t, socketMode, (), (const)); + MOCK_METHOD((const Stats::TagVector&), statsTags, (), (const)); std::string config_path_; envoy::config::bootstrap::v3::Bootstrap config_proto_; @@ -76,6 +77,7 @@ class MockOptions : public Options { std::vector disabled_extensions_; std::string socket_path_; mode_t socket_mode_; + Stats::TagVector stats_tags_; }; } // namespace Server } // namespace Envoy diff --git a/test/server/options_impl_test.cc b/test/server/options_impl_test.cc index 916b68b19f377..06b4b4a02f7df 100644 --- a/test/server/options_impl_test.cc +++ b/test/server/options_impl_test.cc @@ -93,6 +93,7 @@ TEST_F(OptionsImplTest, All) { "--disable-hot-restart --cpuset-threads --allow-unknown-static-fields " "--reject-unknown-dynamic-fields --base-id 5 " "--use-dynamic-base-id --base-id-path /foo/baz " + "--stats-tag foo:bar --stats-tag baz:bar " "--socket-path /foo/envoy_domain_socket --socket-mode 644"); EXPECT_EQ(Server::Mode::Validate, options->mode()); EXPECT_EQ(2U, options->concurrency()); @@ -120,6 +121,7 @@ TEST_F(OptionsImplTest, All) { EXPECT_EQ("/foo/baz", options->baseIdPath()); EXPECT_EQ("/foo/envoy_domain_socket", options->socketPath()); EXPECT_EQ(0644, options->socketMode()); + EXPECT_EQ(2U, options->statsTags().size()); options = createOptionsImpl("envoy --mode init_only"); EXPECT_EQ(Server::Mode::InitOnly, options->mode()); @@ -181,6 +183,7 @@ TEST_F(OptionsImplTest, SetAll) { options->setRejectUnknownFieldsDynamic(true); options->setSocketPath("/foo/envoy_domain_socket"); options->setSocketMode(0644); + options->setStatsTags({{"foo", "bar"}}); EXPECT_EQ(109876, options->baseId()); EXPECT_EQ(true, options->useDynamicBaseId()); @@ -244,6 +247,8 @@ TEST_F(OptionsImplTest, SetAll) { EXPECT_EQ(options->cpusetThreadsEnabled(), command_line_options->cpuset_threads()); EXPECT_EQ(options->socketPath(), command_line_options->socket_path()); EXPECT_EQ(options->socketMode(), command_line_options->socket_mode()); + EXPECT_EQ(1U, command_line_options->stats_tag().size()); + EXPECT_EQ("foo:bar", command_line_options->stats_tag(0)); } TEST_F(OptionsImplTest, DefaultParams) { @@ -257,6 +262,7 @@ TEST_F(OptionsImplTest, DefaultParams) { EXPECT_EQ(spdlog::level::warn, options->logLevel()); EXPECT_EQ("@envoy_domain_socket", options->socketPath()); EXPECT_EQ(0, options->socketMode()); + EXPECT_EQ(0U, options->statsTags().size()); EXPECT_FALSE(options->hotRestartDisabled()); EXPECT_FALSE(options->cpusetThreadsEnabled()); @@ -275,6 +281,7 @@ TEST_F(OptionsImplTest, DefaultParams) { EXPECT_FALSE(command_line_options->cpuset_threads()); EXPECT_FALSE(command_line_options->allow_unknown_static_fields()); EXPECT_FALSE(command_line_options->reject_unknown_dynamic_fields()); + EXPECT_EQ(0, options->statsTags().size()); } // Validates that the server_info proto is in sync with the options. @@ -392,6 +399,38 @@ TEST_F(OptionsImplTest, AllowedLogLevels) { OptionsImpl::allowedLogLevels()); } +TEST_F(OptionsImplTest, InvalidStatsTags) { + EXPECT_THROW_WITH_REGEX(createOptionsImpl("envoy --stats-tag foo"), MalformedArgvException, + "error: misformatted stats-tag 'foo'"); +} + +TEST_F(OptionsImplTest, InvalidCharsInStatsTags) { + EXPECT_THROW_WITH_REGEX( + createOptionsImpl("envoy --stats-tag foo:b.ar"), MalformedArgvException, + "error: misformatted stats-tag 'foo:b.ar' contains invalid char in 'b.ar'"); + EXPECT_THROW_WITH_REGEX( + createOptionsImpl("envoy --stats-tag foo:b.a.r"), MalformedArgvException, + "error: misformatted stats-tag 'foo:b.a.r' contains invalid char in 'b.a.r'"); + EXPECT_THROW_WITH_REGEX(createOptionsImpl("envoy --stats-tag f_o:bar"), MalformedArgvException, + "error: misformatted stats-tag 'f_o:bar' contains invalid char in 'f_o'"); +} + +TEST_F(OptionsImplTest, ValidStatsTagsCharacters) { + Stats::TagVector test_tags{ + Stats::Tag{"foo", "bar"}, + Stats::Tag{"foo", "b:ar"}, + Stats::Tag{"foo", "b_-ar"}, + }; + + for (const auto& tag : test_tags) { + std::unique_ptr options = + createOptionsImpl(fmt::format("envoy --stats-tag {}:{}", tag.name_, tag.value_)); + EXPECT_EQ(options->statsTags().size(), 1); + EXPECT_EQ(options->statsTags()[0].name_, tag.name_); + EXPECT_EQ(options->statsTags()[0].value_, tag.value_); + } +} + // Test that the test constructor comes up with the same default values as the main constructor. TEST_F(OptionsImplTest, SaneTestConstructor) { std::unique_ptr regular_options_impl(createOptionsImpl("envoy")); From 0f69c11b06d5a65e27f2ec2c1d86baf8c050b9a5 Mon Sep 17 00:00:00 2001 From: Snow Pettersen Date: Thu, 4 Nov 2021 12:17:41 -0400 Subject: [PATCH 021/110] logging: include spdlog log_msg in log to SinkDelegate (#18854) This allows sinks to inspect the log line details without parsing the formatted message. Signed-off-by: Snow Pettersen --- source/common/common/logger.cc | 8 ++-- source/common/common/logger.h | 21 ++++++++++- source/common/common/logger_delegates.cc | 2 +- source/common/common/logger_delegates.h | 2 +- test/common/common/logger_test.cc | 47 +++++++++++++++++------- test/test_common/logging.cc | 4 +- test/test_common/logging.h | 2 +- 7 files changed, 61 insertions(+), 25 deletions(-) diff --git a/source/common/common/logger.cc b/source/common/common/logger.cc index ec0e4a99a5c63..a418cc046e02b 100644 --- a/source/common/common/logger.cc +++ b/source/common/common/logger.cc @@ -66,7 +66,7 @@ StderrSinkDelegate::StderrSinkDelegate(DelegatingLogSinkSharedPtr log_sink) StderrSinkDelegate::~StderrSinkDelegate() { restoreDelegate(); } -void StderrSinkDelegate::log(absl::string_view msg) { +void StderrSinkDelegate::log(absl::string_view msg, const spdlog::details::log_msg&) { Thread::OptionalLockGuard guard(lock_); std::cerr << msg; } @@ -94,11 +94,11 @@ void DelegatingLogSink::log(const spdlog::details::log_msg& msg) { } lock.Release(); - auto log_to_sink = [this, msg_view](SinkDelegate& sink) { + auto log_to_sink = [this, msg_view, msg](SinkDelegate& sink) { if (should_escape_) { - sink.log(escapeLogLine(msg_view)); + sink.log(escapeLogLine(msg_view), msg); } else { - sink.log(msg_view); + sink.log(msg_view, msg); } }; auto* tls_sink = tlsDelegate(); diff --git a/source/common/common/logger.h b/source/common/common/logger.h index 2dd45c7e8daaa..0d55c68fa5573 100644 --- a/source/common/common/logger.h +++ b/source/common/common/logger.h @@ -109,9 +109,26 @@ class SinkDelegate : NonCopyable { explicit SinkDelegate(DelegatingLogSinkSharedPtr log_sink); virtual ~SinkDelegate(); - virtual void log(absl::string_view msg) PURE; + /** + * Called to log a single log line. + * @param formatted_msg The final, formatted message. + * @param the original log message, including additional metadata. + */ + virtual void log(absl::string_view msg, const spdlog::details::log_msg& log_msg) PURE; + + /** + * Called to log a single log line with a stable name. + * @param stable_name stable name of this log line. + * @param level the string representation of the log level for this log line. + * @param component the component this log was logged via. + * @param msg the log line to log. + */ virtual void logWithStableName(absl::string_view stable_name, absl::string_view level, absl::string_view component, absl::string_view msg); + + /** + * Called to flush the log sink. + */ virtual void flush() PURE; protected: @@ -151,7 +168,7 @@ class StderrSinkDelegate : public SinkDelegate { ~StderrSinkDelegate() override; // SinkDelegate - void log(absl::string_view msg) override; + void log(absl::string_view msg, const spdlog::details::log_msg& log_msg) override; void flush() override; bool hasLock() const { return lock_ != nullptr; } diff --git a/source/common/common/logger_delegates.cc b/source/common/common/logger_delegates.cc index 8f008b296fb7e..46371a5de2e1e 100644 --- a/source/common/common/logger_delegates.cc +++ b/source/common/common/logger_delegates.cc @@ -19,7 +19,7 @@ FileSinkDelegate::FileSinkDelegate(const std::string& log_path, FileSinkDelegate::~FileSinkDelegate() { restoreDelegate(); } -void FileSinkDelegate::log(absl::string_view msg) { +void FileSinkDelegate::log(absl::string_view msg, const spdlog::details::log_msg&) { // Log files have internal locking to ensure serial, non-interleaved // writes, so no additional locking needed here. log_file_->write(msg); diff --git a/source/common/common/logger_delegates.h b/source/common/common/logger_delegates.h index f9e1d7da69cc1..c85d0b1b46a0f 100644 --- a/source/common/common/logger_delegates.h +++ b/source/common/common/logger_delegates.h @@ -24,7 +24,7 @@ class FileSinkDelegate : public SinkDelegate { ~FileSinkDelegate() override; // SinkDelegate - void log(absl::string_view msg) override; + void log(absl::string_view msg, const spdlog::details::log_msg& log_msg) override; void flush() override; private: diff --git a/test/common/common/logger_test.cc b/test/common/common/logger_test.cc index d1ecf385ba95a..faeb939f2d211 100644 --- a/test/common/common/logger_test.cc +++ b/test/common/common/logger_test.cc @@ -10,6 +10,7 @@ #include "gtest/gtest.h" using testing::_; +using testing::HasSubstr; using testing::Invoke; namespace Envoy { @@ -154,11 +155,11 @@ TEST_F(LoggerCustomFlagsTest, LogMessageAsJsonStringEscaped) { "\\\"transport: Error while dialing dial tcp [::1]:15012: connect: connection refused\\\""); } -struct NamedLogSink : SinkDelegate { - NamedLogSink(DelegatingLogSinkSharedPtr log_sink) : SinkDelegate(log_sink) { setDelegate(); } - ~NamedLogSink() override { restoreDelegate(); } +struct MockLogSink : SinkDelegate { + MockLogSink(DelegatingLogSinkSharedPtr log_sink) : SinkDelegate(log_sink) { setDelegate(); } + ~MockLogSink() override { restoreDelegate(); } - MOCK_METHOD(void, log, (absl::string_view)); + MOCK_METHOD(void, log, (absl::string_view, const spdlog::details::log_msg&)); MOCK_METHOD(void, logWithStableName, (absl::string_view, absl::string_view, absl::string_view, absl::string_view)); void flush() override {} @@ -167,7 +168,7 @@ struct NamedLogSink : SinkDelegate { class NamedLogTest : public Loggable, public testing::Test {}; TEST_F(NamedLogTest, NamedLogsAreSentToSink) { - NamedLogSink sink(Envoy::Logger::Registry::getSink()); + MockLogSink sink(Envoy::Logger::Registry::getSink()); Envoy::Logger::Registry::setLogLevel(spdlog::level::info); // Log level is above debug, so we shouldn't get any logs. @@ -175,12 +176,14 @@ TEST_F(NamedLogTest, NamedLogsAreSentToSink) { Envoy::Logger::Registry::setLogLevel(spdlog::level::debug); - EXPECT_CALL(sink, log(_)); + EXPECT_CALL(sink, log(_, _)); EXPECT_CALL(sink, logWithStableName("test_event", "debug", "assert", "test log 1")); ENVOY_LOG_EVENT(debug, "test_event", "test {} {}", "log", 1); // Verify that ENVOY_LOG_EVENT_TO_LOGGER does the right thing. - EXPECT_CALL(sink, log(_)).WillOnce(Invoke([](auto log) { EXPECT_TRUE(log.find("[misc]")); })); + EXPECT_CALL(sink, log(_, _)).WillOnce(Invoke([](auto log, const auto&) { + EXPECT_TRUE(log.find("[misc]")); + })); EXPECT_CALL(sink, logWithStableName("misc_event", "debug", "misc", "log")); ENVOY_LOG_EVENT_TO_LOGGER(Registry::getLog(Id::misc), debug, "misc_event", "log"); } @@ -189,31 +192,32 @@ struct TlsLogSink : SinkDelegate { TlsLogSink(DelegatingLogSinkSharedPtr log_sink) : SinkDelegate(log_sink) { setTlsDelegate(); } ~TlsLogSink() override { restoreTlsDelegate(); } - MOCK_METHOD(void, log, (absl::string_view)); + MOCK_METHOD(void, log, (absl::string_view, const spdlog::details::log_msg&)); MOCK_METHOD(void, logWithStableName, (absl::string_view, absl::string_view, absl::string_view, absl::string_view)); MOCK_METHOD(void, flush, ()); }; -TEST(NamedLogtest, OverrideSink) { - NamedLogSink global_sink(Envoy::Logger::Registry::getSink()); +// Verifies that we can register a thread local sink override. +TEST(TlsLoggingOverrideTest, OverrideSink) { + MockLogSink global_sink(Envoy::Logger::Registry::getSink()); testing::InSequence s; { TlsLogSink tls_sink(Envoy::Logger::Registry::getSink()); // Calls on the current thread goes to the TLS sink. - EXPECT_CALL(tls_sink, log(_)); + EXPECT_CALL(tls_sink, log(_, _)); ENVOY_LOG_MISC(info, "hello tls"); // Calls on other threads should use the global sink. std::thread([&]() { - EXPECT_CALL(global_sink, log(_)); + EXPECT_CALL(global_sink, log(_, _)); ENVOY_LOG_MISC(info, "hello global"); }).join(); // Sanity checking that we're still using the TLS sink. - EXPECT_CALL(tls_sink, log(_)); + EXPECT_CALL(tls_sink, log(_, _)); ENVOY_LOG_MISC(info, "hello tls"); // All the logging functions should be delegated to the TLS override. @@ -226,8 +230,23 @@ TEST(NamedLogtest, OverrideSink) { // Now that the TLS sink is out of scope, log calls on this thread should use the global sink // again. - EXPECT_CALL(global_sink, log(_)); + EXPECT_CALL(global_sink, log(_, _)); ENVOY_LOG_MISC(info, "hello global 2"); } + +TEST(LoggerTest, LogWithLogDetails) { + Envoy::Logger::Registry::setLogLevel(spdlog::level::info); + + MockLogSink sink(Envoy::Logger::Registry::getSink()); + + EXPECT_CALL(sink, log(_, _)).WillOnce(Invoke([](auto msg, auto& log) { + EXPECT_THAT(msg, HasSubstr("[info]")); + EXPECT_THAT(msg, HasSubstr("hello")); + + EXPECT_EQ(log.logger_name, "misc"); + })); + ENVOY_LOG_MISC(info, "hello"); +} + } // namespace Logger } // namespace Envoy diff --git a/test/test_common/logging.cc b/test/test_common/logging.cc index 3663feb0c83dd..ecfe04dbf9cf1 100644 --- a/test/test_common/logging.cc +++ b/test/test_common/logging.cc @@ -41,8 +41,8 @@ LogRecordingSink::LogRecordingSink(Logger::DelegatingLogSinkSharedPtr log_sink) LogRecordingSink::~LogRecordingSink() { restoreDelegate(); } -void LogRecordingSink::log(absl::string_view msg) { - previousDelegate()->log(msg); +void LogRecordingSink::log(absl::string_view msg, const spdlog::details::log_msg& log_msg) { + previousDelegate()->log(msg, log_msg); absl::MutexLock ml(&mtx_); messages_.push_back(std::string(msg)); diff --git a/test/test_common/logging.h b/test/test_common/logging.h index 2d6252b2c68ec..09cb5cdb5fa35 100644 --- a/test/test_common/logging.h +++ b/test/test_common/logging.h @@ -54,7 +54,7 @@ class LogRecordingSink : public Logger::SinkDelegate { ~LogRecordingSink() override; // Logger::SinkDelegate - void log(absl::string_view msg) override; + void log(absl::string_view msg, const spdlog::details::log_msg& log_msg) override; void flush() override; const std::vector messages() const { From 361fd533b6d739e343cc46270e33ede3292b85f1 Mon Sep 17 00:00:00 2001 From: Yao Zengzeng Date: Fri, 5 Nov 2021 05:19:18 +0800 Subject: [PATCH 022/110] quic: fast-fail if secrets not loaded when create quic connection (#18705) Signed-off-by: YaoZengzeng --- source/common/http/http3/conn_pool.cc | 13 +++-- .../quic/quic_transport_socket_factory.h | 2 +- test/common/http/http3/conn_pool_test.cc | 54 +++++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/source/common/http/http3/conn_pool.cc b/source/common/http/http3/conn_pool.cc index c4a53797c747e..2795587f1b5d0 100644 --- a/source/common/http/http3/conn_pool.cc +++ b/source/common/http/http3/conn_pool.cc @@ -89,9 +89,10 @@ allocateConnPool(Event::Dispatcher& dispatcher, Random::RandomGenerator& random_ auto factory = &pool->host()->transportSocketFactory(); ASSERT(dynamic_cast(factory) != nullptr); if (static_cast(factory)->sslCtx() == nullptr) { - ENVOY_LOG_TO_LOGGER(Envoy::Logger::Registry::getLog(Envoy::Logger::Id::pool), warn, - "Failed to create Http/3 client. Transport socket " - "factory is not configured correctly."); + ENVOY_LOG_EVERY_POW_2_TO_LOGGER(Envoy::Logger::Registry::getLog(Envoy::Logger::Id::pool), + warn, + "Failed to create Http/3 client. Transport socket " + "factory is not configured correctly."); return nullptr; } Http3ConnPoolImpl* h3_pool = reinterpret_cast(pool); @@ -105,6 +106,12 @@ allocateConnPool(Event::Dispatcher& dispatcher, Random::RandomGenerator& random_ data.connection_ = Quic::createQuicNetworkConnection(h3_pool->quicInfo(), pool->dispatcher(), host_address, source_address, quic_stat_names, scope); + if (data.connection_ == nullptr) { + ENVOY_LOG_EVERY_POW_2_TO_LOGGER( + Envoy::Logger::Registry::getLog(Envoy::Logger::Id::pool), warn, + "Failed to create Http/3 client. Failed to create quic network connection."); + return nullptr; + } // Store a handle to connection as it will be moved during client construction. Network::Connection& connection = *data.connection_; auto client = std::make_unique(*pool, data); diff --git a/source/common/quic/quic_transport_socket_factory.h b/source/common/quic/quic_transport_socket_factory.h index 7c98e1ef29e69..c2328324a6b36 100644 --- a/source/common/quic/quic_transport_socket_factory.h +++ b/source/common/quic/quic_transport_socket_factory.h @@ -108,7 +108,7 @@ class QuicClientTransportSocketFactory : public QuicTransportSocketFactoryBase { return fallback_factory_->createTransportSocket(options); } - Envoy::Ssl::ClientContextSharedPtr sslCtx() { return fallback_factory_->sslCtx(); } + virtual Envoy::Ssl::ClientContextSharedPtr sslCtx() { return fallback_factory_->sslCtx(); } const Ssl::ClientContextConfig& clientContextConfig() const { return fallback_factory_->config(); diff --git a/test/common/http/http3/conn_pool_test.cc b/test/common/http/http3/conn_pool_test.cc index ba4c3124cb38c..b60676533c1d8 100644 --- a/test/common/http/http3/conn_pool_test.cc +++ b/test/common/http/http3/conn_pool_test.cc @@ -68,6 +68,60 @@ class Http3ConnPoolImplTest : public Event::TestUsingSimulatedTime, public testi ConnectionPool::InstancePtr pool_; }; +class MockQuicClientTransportSocketFactory : public Quic::QuicClientTransportSocketFactory { +public: + MockQuicClientTransportSocketFactory( + Ssl::ClientContextConfigPtr config, + Server::Configuration::TransportSocketFactoryContext& factory_context) + : Quic::QuicClientTransportSocketFactory(move(config), factory_context) {} + + MOCK_METHOD(Envoy::Ssl::ClientContextSharedPtr, sslCtx, ()); +}; + +TEST_F(Http3ConnPoolImplTest, FastFailWithoutSecretsLoaded) { + MockQuicClientTransportSocketFactory factory{ + std::unique_ptr(new NiceMock), + context_}; + + EXPECT_CALL(factory, sslCtx()).WillRepeatedly(Return(nullptr)); + + EXPECT_CALL(mockHost(), address()).WillRepeatedly(Return(test_address_)); + EXPECT_CALL(mockHost(), transportSocketFactory()).WillRepeatedly(testing::ReturnRef(factory)); + // The unique pointer of this object will be returned in createSchedulableCallback_ of + // dispatcher_, so there is no risk of object leak. + new Event::MockSchedulableCallback(&dispatcher_); + Network::ConnectionSocket::OptionsSharedPtr options; + Network::TransportSocketOptionsConstSharedPtr transport_options; + ConnectionPool::InstancePtr pool = + allocateConnPool(dispatcher_, random_, host_, Upstream::ResourcePriority::Default, options, + transport_options, state_, simTime(), quic_stat_names_, store_); + + EXPECT_EQ(static_cast(pool.get())->instantiateActiveClient(), nullptr); +} + +TEST_F(Http3ConnPoolImplTest, FailWithSecretsBecomeEmpty) { + MockQuicClientTransportSocketFactory factory{ + std::unique_ptr(new NiceMock), + context_}; + + Ssl::ClientContextSharedPtr ssl_context(new Ssl::MockClientContext()); + EXPECT_CALL(factory, sslCtx()) + .WillOnce(Return(ssl_context)) + .WillOnce(Return(nullptr)) + .WillRepeatedly(Return(ssl_context)); + + EXPECT_CALL(mockHost(), address()).WillRepeatedly(Return(test_address_)); + EXPECT_CALL(mockHost(), transportSocketFactory()).WillRepeatedly(testing::ReturnRef(factory)); + new Event::MockSchedulableCallback(&dispatcher_); + Network::ConnectionSocket::OptionsSharedPtr options; + Network::TransportSocketOptionsConstSharedPtr transport_options; + ConnectionPool::InstancePtr pool = + allocateConnPool(dispatcher_, random_, host_, Upstream::ResourcePriority::Default, options, + transport_options, state_, simTime(), quic_stat_names_, store_); + + EXPECT_EQ(static_cast(pool.get())->instantiateActiveClient(), nullptr); +} + TEST_F(Http3ConnPoolImplTest, CreationWithBufferLimits) { EXPECT_CALL(mockHost().cluster_, perConnectionBufferLimitBytes); initialize(); From 19a87ee62f9dc3bde09b918f6c75b34ce45de5ac Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Thu, 4 Nov 2021 17:47:31 -0400 Subject: [PATCH 023/110] quic: post-import TODO cleanup (#18898) moving to an accessor now that it exists. Risk Level: low Testing: n/a (existing tests apply) Docs Changes: n/a Release Notes: n/a Signed-off-by: Alyssa Wilk --- .../common/quic/envoy_quic_client_session.cc | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/source/common/quic/envoy_quic_client_session.cc b/source/common/quic/envoy_quic_client_session.cc index 4cf14c31db3b9..a1c5372a5654d 100644 --- a/source/common/quic/envoy_quic_client_session.cc +++ b/source/common/quic/envoy_quic_client_session.cc @@ -5,21 +5,6 @@ #include "quic_filter_manager_connection_impl.h" -namespace quic { -namespace test { - -// TODO(alyssawilk) add the necessary accessors to quiche and remove this. -class QuicSessionPeer { -public: - static quic::QuicStreamIdManager& - getStreamIdManager(Envoy::Quic::EnvoyQuicClientSession* session) { - return session->ietf_streamid_manager_.bidirectional_stream_id_manager_; - } -}; - -} // namespace test -} // namespace quic - namespace Envoy { namespace Quic { @@ -135,9 +120,11 @@ quic::QuicConnection* EnvoyQuicClientSession::quicConnection() { } uint64_t EnvoyQuicClientSession::streamsAvailable() { - quic::QuicStreamIdManager& manager = quic::test::QuicSessionPeer::getStreamIdManager(this); - ASSERT(manager.outgoing_max_streams() >= manager.outgoing_stream_count()); - uint32_t streams_available = manager.outgoing_max_streams() - manager.outgoing_stream_count(); + const quic::UberQuicStreamIdManager& manager = ietf_streamid_manager(); + ASSERT(manager.max_outgoing_bidirectional_streams() >= + manager.outgoing_bidirectional_stream_count()); + uint32_t streams_available = + manager.max_outgoing_bidirectional_streams() - manager.outgoing_bidirectional_stream_count(); return streams_available; } From c81d4452db4769679e53ae64da9fb41fe291c0f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Nov 2021 16:50:25 -0600 Subject: [PATCH 024/110] build(deps): bump setuptools from 58.3.0 to 58.5.2 in /tools/base (#18901) Bumps [setuptools](https://github.com/pypa/setuptools) from 58.3.0 to 58.5.2. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/CHANGES.rst) - [Commits](https://github.com/pypa/setuptools/compare/v58.3.0...v58.5.2) --- updated-dependencies: - dependency-name: setuptools dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 455b54209ca59..ccbf5a8d684f1 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -732,7 +732,6 @@ typing-extensions==3.10.0.2 \ # via # aiodocker # aiohttp - # gitpython uritemplate==3.0.1 \ --hash=sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f \ --hash=sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae @@ -799,9 +798,9 @@ yarl==1.6.3 \ # via aiohttp # The following packages are considered to be unsafe in a requirements file: -setuptools==58.3.0 \ - --hash=sha256:1a24f0e5c14b91ad6810745a7242721fd5011ed164553136f946f768b06559f7 \ - --hash=sha256:b0c2461641b58fe30e11d4c3dfba316c513bdf9ec85f9fed0c871c678447205e +setuptools==58.5.2 \ + --hash=sha256:132856b317080c4688b664e40164ad27fa210636258ee7c25cf2eb49b982ee51 \ + --hash=sha256:3a99bc3975c1eb3cca138d3f178aedaca87f75e9372e40cd361056b82d630659 # via # -r requirements.in # sphinx From 3a5f7958afb161efd62dada8e6bbb77f96c4dd04 Mon Sep 17 00:00:00 2001 From: Rei Shimizu Date: Fri, 5 Nov 2021 12:17:07 +0900 Subject: [PATCH 025/110] access logging: gRPC logger retry to establish underlying stream connection (#17469) Add a retry mechanism to the grpc access logger. This retry mechanism currently only supports a simple retry count. Also, at the moment, retries are only fired when the gRPC stream fails to be established, and nothing happens if the stream is successfully established once and a reset is issued. Risk Level: Low Testing: Unit Signed-off-by: Shikugawa --- .../access_loggers/grpc/v3/als.proto | 12 ++- docs/root/version_history/current.rst | 1 + .../access_loggers/grpc/v3/als.proto | 98 +++++++++++++++++++ source/common/grpc/async_client_impl.cc | 2 +- .../common/grpc_access_logger.h | 30 ++++-- .../grpc/grpc_access_log_impl.cc | 14 +-- .../grpc/grpc_access_log_impl.h | 9 +- .../open_telemetry/grpc_access_log_impl.cc | 14 +-- .../open_telemetry/grpc_access_log_impl.h | 9 +- .../common/grpc_access_logger_test.cc | 46 ++++++--- .../grpc/grpc_access_log_impl_test.cc | 8 +- .../grpc_access_log_impl_test.cc | 8 +- 12 files changed, 206 insertions(+), 45 deletions(-) create mode 100644 generated_api_shadow/envoy/extensions/access_loggers/grpc/v3/als.proto diff --git a/api/envoy/extensions/access_loggers/grpc/v3/als.proto b/api/envoy/extensions/access_loggers/grpc/v3/als.proto index fa0a9f0f820d5..a671873580f31 100644 --- a/api/envoy/extensions/access_loggers/grpc/v3/als.proto +++ b/api/envoy/extensions/access_loggers/grpc/v3/als.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package envoy.extensions.access_loggers.grpc.v3; +import "envoy/config/core/v3/base.proto"; import "envoy/config/core/v3/config_source.proto"; import "envoy/config/core/v3/grpc_service.proto"; @@ -54,7 +55,7 @@ message TcpGrpcAccessLogConfig { } // Common configuration for gRPC access logs. -// [#next-free-field: 7] +// [#next-free-field: 8] message CommonGrpcAccessLogConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.config.accesslog.v2.CommonGrpcAccessLogConfig"; @@ -86,4 +87,13 @@ message CommonGrpcAccessLogConfig { // `. // Logger will call `FilterState::Object::serializeAsProto` to serialize the filter state object. repeated string filter_state_objects_to_log = 5; + + // Sets the retry policy when the establishment of a gRPC stream fails. + // If the stream succeeds once in establishing If the stream succeeds + // at least once in establishing itself, no retry will be performed + // no matter what gRPC status is received. Note that only + // :ref:`num_retries ` + // will be used in this configuration. This feature is used only when you are using + // :ref:`Envoy gRPC client `. + config.core.v3.RetryPolicy grpc_stream_retry_policy = 7; } diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 3712b2463d80a..0ccdb71c4b8a5 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -48,6 +48,7 @@ Removed Config or Runtime New Features ------------ +* access log: added :ref:`grpc_stream_retry_policy ` to the gRPC logger to reconnect when a connection fails to be established. * api: added support for *xds.type.v3.TypedStruct* in addition to the now-deprecated *udpa.type.v1.TypedStruct* proto message, which is a wrapper proto used to encode typed JSON data in a *google.protobuf.Any* field. * bootstrap: added :ref:`typed_dns_resolver_config ` in the bootstrap to support DNS resolver as an extension. * cluster: added :ref:`typed_dns_resolver_config ` in the cluster to support DNS resolver as an extension. diff --git a/generated_api_shadow/envoy/extensions/access_loggers/grpc/v3/als.proto b/generated_api_shadow/envoy/extensions/access_loggers/grpc/v3/als.proto new file mode 100644 index 0000000000000..d8f2175dc8898 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/access_loggers/grpc/v3/als.proto @@ -0,0 +1,98 @@ +syntax = "proto3"; + +package envoy.extensions.access_loggers.grpc.v3; + +import "envoy/config/core/v3/base.proto"; +import "envoy/config/core/v3/config_source.proto"; +import "envoy/config/core/v3/grpc_service.proto"; + +import "google/protobuf/duration.proto"; +import "google/protobuf/wrappers.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.access_loggers.grpc.v3"; +option java_outer_classname = "AlsProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: gRPC Access Log Service (ALS)] + +// Configuration for the built-in *envoy.access_loggers.http_grpc* +// :ref:`AccessLog `. This configuration will +// populate :ref:`StreamAccessLogsMessage.http_logs +// `. +// [#extension: envoy.access_loggers.http_grpc] +message HttpGrpcAccessLogConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.accesslog.v2.HttpGrpcAccessLogConfig"; + + CommonGrpcAccessLogConfig common_config = 1 [(validate.rules).message = {required: true}]; + + // Additional request headers to log in :ref:`HTTPRequestProperties.request_headers + // `. + repeated string additional_request_headers_to_log = 2; + + // Additional response headers to log in :ref:`HTTPResponseProperties.response_headers + // `. + repeated string additional_response_headers_to_log = 3; + + // Additional response trailers to log in :ref:`HTTPResponseProperties.response_trailers + // `. + repeated string additional_response_trailers_to_log = 4; +} + +// Configuration for the built-in *envoy.access_loggers.tcp_grpc* type. This configuration will +// populate *StreamAccessLogsMessage.tcp_logs*. +// [#extension: envoy.access_loggers.tcp_grpc] +message TcpGrpcAccessLogConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.accesslog.v2.TcpGrpcAccessLogConfig"; + + CommonGrpcAccessLogConfig common_config = 1 [(validate.rules).message = {required: true}]; +} + +// Common configuration for gRPC access logs. +// [#next-free-field: 8] +message CommonGrpcAccessLogConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.accesslog.v2.CommonGrpcAccessLogConfig"; + + // The friendly name of the access log to be returned in :ref:`StreamAccessLogsMessage.Identifier + // `. This allows the + // access log server to differentiate between different access logs coming from the same Envoy. + string log_name = 1 [(validate.rules).string = {min_len: 1}]; + + // The gRPC service for the access log service. + config.core.v3.GrpcService grpc_service = 2 [(validate.rules).message = {required: true}]; + + // API version for access logs service transport protocol. This describes the access logs service + // gRPC endpoint and version of messages used on the wire. + config.core.v3.ApiVersion transport_api_version = 6 + [(validate.rules).enum = {defined_only: true}]; + + // Interval for flushing access logs to the gRPC stream. Logger will flush requests every time + // this interval is elapsed, or when batch size limit is hit, whichever comes first. Defaults to + // 1 second. + google.protobuf.Duration buffer_flush_interval = 3 [(validate.rules).duration = {gt {}}]; + + // Soft size limit in bytes for access log entries buffer. Logger will buffer requests until + // this limit it hit, or every time flush interval is elapsed, whichever comes first. Setting it + // to zero effectively disables the batching. Defaults to 16384. + google.protobuf.UInt32Value buffer_size_bytes = 4; + + // Additional filter state objects to log in :ref:`filter_state_objects + // `. + // Logger will call `FilterState::Object::serializeAsProto` to serialize the filter state object. + repeated string filter_state_objects_to_log = 5; + + // Sets the retry policy when the establishment of a gRPC stream fails. + // If the stream succeeds once in establishing If the stream succeeds + // at least once in establishing itself, no retry will be performed + // no matter what gRPC status is received. Note that only + // :ref:`num_retries ` + // will be used in this configuration. + config.core.v3.RetryPolicy grpc_stream_retry_policy = 7; +} diff --git a/source/common/grpc/async_client_impl.cc b/source/common/grpc/async_client_impl.cc index 9bb41f34f8508..f1da95844a407 100644 --- a/source/common/grpc/async_client_impl.cc +++ b/source/common/grpc/async_client_impl.cc @@ -56,7 +56,7 @@ RawAsyncStream* AsyncClientImpl::startRaw(absl::string_view service_full_name, auto grpc_stream = std::make_unique(*this, service_full_name, method_name, callbacks, options); - grpc_stream->initialize(false); + grpc_stream->initialize(options.buffer_body_for_retry); if (grpc_stream->hasResetStream()) { return nullptr; } diff --git a/source/extensions/access_loggers/common/grpc_access_logger.h b/source/extensions/access_loggers/common/grpc_access_logger.h index 921186875fbe5..446dd2698f67a 100644 --- a/source/extensions/access_loggers/common/grpc_access_logger.h +++ b/source/extensions/access_loggers/common/grpc_access_logger.h @@ -11,6 +11,7 @@ #include "source/common/common/assert.h" #include "source/common/grpc/typed_async_client.h" +#include "source/common/http/utility.h" #include "source/common/protobuf/utility.h" #include "absl/container/flat_hash_map.h" @@ -75,8 +76,9 @@ template class GrpcAccessLogge template class GrpcAccessLogClient { public: GrpcAccessLogClient(const Grpc::RawAsyncClientSharedPtr& client, - const Protobuf::MethodDescriptor& service_method) - : client_(client), service_method_(service_method) {} + const Protobuf::MethodDescriptor& service_method, + const envoy::config::core::v3::RetryPolicy& retry_policy) + : client_(client), service_method_(service_method), grpc_stream_retry_policy_(retry_policy) {} public: struct LocalStream : public Grpc::AsyncStreamCallbacks { @@ -108,8 +110,7 @@ template class GrpcAccessLogClient { } if (stream_->stream_ == nullptr) { - stream_->stream_ = - client_->start(service_method_, *stream_, Http::AsyncClient::StreamOptions()); + stream_->stream_ = client_->start(service_method_, *stream_, createStreamOptionsForRetry()); } if (stream_->stream_ != nullptr) { @@ -124,9 +125,24 @@ template class GrpcAccessLogClient { return true; } + Http::AsyncClient::StreamOptions createStreamOptionsForRetry() { + auto opt = Http::AsyncClient::StreamOptions(); + + if (!grpc_stream_retry_policy_) { + return opt; + } + + const auto retry_policy = + Http::Utility::convertCoreToRouteRetryPolicy(*grpc_stream_retry_policy_, "connect-failure"); + opt.setBufferBodyForRetry(true); + opt.setRetryPolicy(retry_policy); + return opt; + } + Grpc::AsyncClient client_; std::unique_ptr stream_; const Protobuf::MethodDescriptor& service_method_; + const absl::optional grpc_stream_retry_policy_; }; } // namespace Detail @@ -160,8 +176,10 @@ class GrpcAccessLogger : public Detail::GrpcAccessLoggerenableTimer(buffer_flush_interval_msec_); diff --git a/source/extensions/access_loggers/grpc/grpc_access_log_impl.cc b/source/extensions/access_loggers/grpc/grpc_access_log_impl.cc index ca45d2c5acaf5..bc94345b938e3 100644 --- a/source/extensions/access_loggers/grpc/grpc_access_log_impl.cc +++ b/source/extensions/access_loggers/grpc/grpc_access_log_impl.cc @@ -16,14 +16,16 @@ namespace AccessLoggers { namespace GrpcCommon { GrpcAccessLoggerImpl::GrpcAccessLoggerImpl( - const Grpc::RawAsyncClientSharedPtr& client, std::string log_name, + const Grpc::RawAsyncClientSharedPtr& client, + const envoy::extensions::access_loggers::grpc::v3::CommonGrpcAccessLogConfig& config, std::chrono::milliseconds buffer_flush_interval_msec, uint64_t max_buffer_size_bytes, Event::Dispatcher& dispatcher, const LocalInfo::LocalInfo& local_info, Stats::Scope& scope) : GrpcAccessLogger(std::move(client), buffer_flush_interval_msec, max_buffer_size_bytes, dispatcher, scope, GRPC_LOG_STATS_PREFIX, *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( - "envoy.service.accesslog.v3.AccessLogService.StreamAccessLogs")), - log_name_(log_name), local_info_(local_info) {} + "envoy.service.accesslog.v3.AccessLogService.StreamAccessLogs"), + config.grpc_stream_retry_policy()), + log_name_(config.log_name()), local_info_(local_info) {} void GrpcAccessLoggerImpl::addEntry(envoy::data::accesslog::v3::HTTPAccessLogEntry&& entry) { message_.mutable_http_logs()->mutable_log_entry()->Add(std::move(entry)); @@ -54,9 +56,9 @@ GrpcAccessLoggerImpl::SharedPtr GrpcAccessLoggerCacheImpl::createLogger( const Grpc::RawAsyncClientSharedPtr& client, std::chrono::milliseconds buffer_flush_interval_msec, uint64_t max_buffer_size_bytes, Event::Dispatcher& dispatcher) { - return std::make_shared(client, config.log_name(), - buffer_flush_interval_msec, max_buffer_size_bytes, - dispatcher, local_info_, scope_); + return std::make_shared(client, config, buffer_flush_interval_msec, + max_buffer_size_bytes, dispatcher, local_info_, + scope_); } } // namespace GrpcCommon diff --git a/source/extensions/access_loggers/grpc/grpc_access_log_impl.h b/source/extensions/access_loggers/grpc/grpc_access_log_impl.h index c502f4365d891..34aab4cf6edc9 100644 --- a/source/extensions/access_loggers/grpc/grpc_access_log_impl.h +++ b/source/extensions/access_loggers/grpc/grpc_access_log_impl.h @@ -23,10 +23,11 @@ class GrpcAccessLoggerImpl envoy::service::accesslog::v3::StreamAccessLogsMessage, envoy::service::accesslog::v3::StreamAccessLogsResponse> { public: - GrpcAccessLoggerImpl(const Grpc::RawAsyncClientSharedPtr& client, std::string log_name, - std::chrono::milliseconds buffer_flush_interval_msec, - uint64_t max_buffer_size_bytes, Event::Dispatcher& dispatcher, - const LocalInfo::LocalInfo& local_info, Stats::Scope& scope); + GrpcAccessLoggerImpl( + const Grpc::RawAsyncClientSharedPtr& client, + const envoy::extensions::access_loggers::grpc::v3::CommonGrpcAccessLogConfig& config, + std::chrono::milliseconds buffer_flush_interval_msec, uint64_t max_buffer_size_bytes, + Event::Dispatcher& dispatcher, const LocalInfo::LocalInfo& local_info, Stats::Scope& scope); private: // Extensions::AccessLoggers::GrpcCommon::GrpcAccessLogger diff --git a/source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.cc b/source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.cc index 38d9616922a91..0c08ce1194e11 100644 --- a/source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.cc +++ b/source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.cc @@ -21,14 +21,16 @@ namespace AccessLoggers { namespace OpenTelemetry { GrpcAccessLoggerImpl::GrpcAccessLoggerImpl( - const Grpc::RawAsyncClientSharedPtr& client, std::string log_name, + const Grpc::RawAsyncClientSharedPtr& client, + const envoy::extensions::access_loggers::grpc::v3::CommonGrpcAccessLogConfig& config, std::chrono::milliseconds buffer_flush_interval_msec, uint64_t max_buffer_size_bytes, Event::Dispatcher& dispatcher, const LocalInfo::LocalInfo& local_info, Stats::Scope& scope) : GrpcAccessLogger(client, buffer_flush_interval_msec, max_buffer_size_bytes, dispatcher, scope, GRPC_LOG_STATS_PREFIX, *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( - "opentelemetry.proto.collector.logs.v1.LogsService.Export")) { - initMessageRoot(log_name, local_info); + "opentelemetry.proto.collector.logs.v1.LogsService.Export"), + config.grpc_stream_retry_policy()) { + initMessageRoot(config.log_name(), local_info); } namespace { @@ -77,9 +79,9 @@ GrpcAccessLoggerImpl::SharedPtr GrpcAccessLoggerCacheImpl::createLogger( const Grpc::RawAsyncClientSharedPtr& client, std::chrono::milliseconds buffer_flush_interval_msec, uint64_t max_buffer_size_bytes, Event::Dispatcher& dispatcher) { - return std::make_shared(client, config.log_name(), - buffer_flush_interval_msec, max_buffer_size_bytes, - dispatcher, local_info_, scope_); + return std::make_shared(client, config, buffer_flush_interval_msec, + max_buffer_size_bytes, dispatcher, local_info_, + scope_); } } // namespace OpenTelemetry diff --git a/source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.h b/source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.h index 85aa0ad8d6943..0fa389d75b1db 100644 --- a/source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.h +++ b/source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.h @@ -35,10 +35,11 @@ class GrpcAccessLoggerImpl ProtobufWkt::Empty, opentelemetry::proto::collector::logs::v1::ExportLogsServiceRequest, opentelemetry::proto::collector::logs::v1::ExportLogsServiceResponse> { public: - GrpcAccessLoggerImpl(const Grpc::RawAsyncClientSharedPtr& client, std::string log_name, - std::chrono::milliseconds buffer_flush_interval_msec, - uint64_t max_buffer_size_bytes, Event::Dispatcher& dispatcher, - const LocalInfo::LocalInfo& local_info, Stats::Scope& scope); + GrpcAccessLoggerImpl( + const Grpc::RawAsyncClientSharedPtr& client, + const envoy::extensions::access_loggers::grpc::v3::CommonGrpcAccessLogConfig& config, + std::chrono::milliseconds buffer_flush_interval_msec, uint64_t max_buffer_size_bytes, + Event::Dispatcher& dispatcher, const LocalInfo::LocalInfo& local_info, Stats::Scope& scope); private: void initMessageRoot(const std::string& log_name, const LocalInfo::LocalInfo& local_info); diff --git a/test/extensions/access_loggers/common/grpc_access_logger_test.cc b/test/extensions/access_loggers/common/grpc_access_logger_test.cc index ec6e35ab635e5..0a8bd0d819946 100644 --- a/test/extensions/access_loggers/common/grpc_access_logger_test.cc +++ b/test/extensions/access_loggers/common/grpc_access_logger_test.cc @@ -48,13 +48,15 @@ class MockGrpcAccessLoggerImpl : public Common::GrpcAccessLogger { public: - MockGrpcAccessLoggerImpl(const Grpc::RawAsyncClientSharedPtr& client, - std::chrono::milliseconds buffer_flush_interval_msec, - uint64_t max_buffer_size_bytes, Event::Dispatcher& dispatcher, - Stats::Scope& scope, std::string access_log_prefix, - const Protobuf::MethodDescriptor& service_method) + MockGrpcAccessLoggerImpl( + const Grpc::RawAsyncClientSharedPtr& client, + const envoy::extensions::access_loggers::grpc::v3::CommonGrpcAccessLogConfig& config, + std::chrono::milliseconds buffer_flush_interval_msec, uint64_t max_buffer_size_bytes, + Event::Dispatcher& dispatcher, Stats::Scope& scope, std::string access_log_prefix, + const Protobuf::MethodDescriptor& service_method) : GrpcAccessLogger(std::move(client), buffer_flush_interval_msec, max_buffer_size_bytes, - dispatcher, scope, access_log_prefix, service_method) {} + dispatcher, scope, access_log_prefix, service_method, + config.grpc_stream_retry_policy()) {} int numInits() const { return num_inits_; } @@ -116,8 +118,9 @@ class GrpcAccessLogTest : public testing::Test { timer_ = new Event::MockTimer(&dispatcher_); EXPECT_CALL(*timer_, enableTimer(buffer_flush_interval_msec, _)); logger_ = std::make_unique( - Grpc::RawAsyncClientPtr{async_client_}, buffer_flush_interval_msec, buffer_size_bytes, - dispatcher_, stats_store_, "mock_access_log_prefix.", mockMethodDescriptor()); + Grpc::RawAsyncClientPtr{async_client_}, config_, buffer_flush_interval_msec, + buffer_size_bytes, dispatcher_, stats_store_, "mock_access_log_prefix.", + mockMethodDescriptor()); } void expectStreamStart(MockAccessLogStream& stream, AccessLogCallbacks** callbacks_to_set) { @@ -148,6 +151,7 @@ class GrpcAccessLogTest : public testing::Test { Event::MockDispatcher dispatcher_; Grpc::MockAsyncClient* async_client_{new Grpc::MockAsyncClient}; std::unique_ptr logger_; + envoy::extensions::access_loggers::grpc::v3::CommonGrpcAccessLogConfig config_; }; // Test basic stream logging flow. @@ -256,6 +260,26 @@ TEST_F(GrpcAccessLogTest, StreamFailure) { EXPECT_EQ(1, logger_->numInits()); } +TEST_F(GrpcAccessLogTest, StreamFailureAndRetry) { + config_.mutable_grpc_stream_retry_policy()->mutable_num_retries()->set_value(2); + config_.mutable_grpc_stream_retry_policy() + ->mutable_retry_back_off() + ->mutable_base_interval() + ->set_seconds(1); + initLogger(FlushInterval, 1); + + EXPECT_CALL(*async_client_, startRaw(_, _, _, _)) + .WillOnce( + Invoke([](absl::string_view, absl::string_view, Grpc::RawAsyncStreamCallbacks&, + const Http::AsyncClient::StreamOptions& options) -> Grpc::RawAsyncStream* { + EXPECT_TRUE(options.retry_policy.has_value()); + EXPECT_TRUE(options.retry_policy.value().has_num_retries()); + EXPECT_EQ(PROTOBUF_GET_WRAPPED_REQUIRED(options.retry_policy.value(), num_retries), 2); + return nullptr; + })); + logger_->log(mockHttpEntry()); +} + // Test that log entries are batched. TEST_F(GrpcAccessLogTest, Batching) { // The approximate log size for buffering is calculated based on each entry's byte size. @@ -319,13 +343,13 @@ class MockGrpcAccessLoggerCache private: // Common::GrpcAccessLoggerCache MockGrpcAccessLoggerImpl::SharedPtr - createLogger(const envoy::extensions::access_loggers::grpc::v3::CommonGrpcAccessLogConfig&, + createLogger(const envoy::extensions::access_loggers::grpc::v3::CommonGrpcAccessLogConfig& config, const Grpc::RawAsyncClientSharedPtr& client, std::chrono::milliseconds buffer_flush_interval_msec, uint64_t max_buffer_size_bytes, Event::Dispatcher& dispatcher) override { return std::make_shared( - std::move(client), buffer_flush_interval_msec, max_buffer_size_bytes, dispatcher, scope_, - "mock_access_log_prefix.", mockMethodDescriptor()); + std::move(client), config, buffer_flush_interval_msec, max_buffer_size_bytes, dispatcher, + scope_, "mock_access_log_prefix.", mockMethodDescriptor()); } }; diff --git a/test/extensions/access_loggers/grpc/grpc_access_log_impl_test.cc b/test/extensions/access_loggers/grpc/grpc_access_log_impl_test.cc index 3ea77be37f64a..44f2d4f6f4d10 100644 --- a/test/extensions/access_loggers/grpc/grpc_access_log_impl_test.cc +++ b/test/extensions/access_loggers/grpc/grpc_access_log_impl_test.cc @@ -70,9 +70,10 @@ class GrpcAccessLoggerImplTest : public testing::Test { : async_client_(new Grpc::MockAsyncClient), timer_(new Event::MockTimer(&dispatcher_)), grpc_access_logger_impl_test_helper_(local_info_, async_client_) { EXPECT_CALL(*timer_, enableTimer(_, _)); - logger_ = std::make_unique( - Grpc::RawAsyncClientPtr{async_client_}, "test_log_name", FlushInterval, BUFFER_SIZE_BYTES, - dispatcher_, local_info_, stats_store_); + *config_.mutable_log_name() = "test_log_name"; + logger_ = std::make_unique(Grpc::RawAsyncClientPtr{async_client_}, + config_, FlushInterval, BUFFER_SIZE_BYTES, + dispatcher_, local_info_, stats_store_); } Grpc::MockAsyncClient* async_client_; @@ -82,6 +83,7 @@ class GrpcAccessLoggerImplTest : public testing::Test { Event::MockTimer* timer_; std::unique_ptr logger_; GrpcAccessLoggerImplTestHelper grpc_access_logger_impl_test_helper_; + envoy::extensions::access_loggers::grpc::v3::CommonGrpcAccessLogConfig config_; }; TEST_F(GrpcAccessLoggerImplTest, LogHttp) { diff --git a/test/extensions/access_loggers/open_telemetry/grpc_access_log_impl_test.cc b/test/extensions/access_loggers/open_telemetry/grpc_access_log_impl_test.cc index b40e82c47236c..6fa166cdf5c3f 100644 --- a/test/extensions/access_loggers/open_telemetry/grpc_access_log_impl_test.cc +++ b/test/extensions/access_loggers/open_telemetry/grpc_access_log_impl_test.cc @@ -79,9 +79,10 @@ class GrpcAccessLoggerImplTest : public testing::Test { : async_client_(new Grpc::MockAsyncClient), timer_(new Event::MockTimer(&dispatcher_)), grpc_access_logger_impl_test_helper_(local_info_, async_client_) { EXPECT_CALL(*timer_, enableTimer(_, _)); - logger_ = std::make_unique( - Grpc::RawAsyncClientPtr{async_client_}, "test_log_name", FlushInterval, BUFFER_SIZE_BYTES, - dispatcher_, local_info_, stats_store_); + *config_.mutable_log_name() = "test_log_name"; + logger_ = std::make_unique(Grpc::RawAsyncClientPtr{async_client_}, + config_, FlushInterval, BUFFER_SIZE_BYTES, + dispatcher_, local_info_, stats_store_); } Grpc::MockAsyncClient* async_client_; @@ -91,6 +92,7 @@ class GrpcAccessLoggerImplTest : public testing::Test { Event::MockTimer* timer_; std::unique_ptr logger_; GrpcAccessLoggerImplTestHelper grpc_access_logger_impl_test_helper_; + envoy::extensions::access_loggers::grpc::v3::CommonGrpcAccessLogConfig config_; }; TEST_F(GrpcAccessLoggerImplTest, LogHttp) { From 759064c983f37a54c2ce836a8f37d1d60352444f Mon Sep 17 00:00:00 2001 From: "Adi (Suissa) Peleg" Date: Fri, 5 Nov 2021 10:33:16 -0400 Subject: [PATCH 026/110] Minor: adding copy assignment operator for AsyncStream (#18896) Signed-off-by: Adi Suissa-Peleg --- source/common/grpc/typed_async_client.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source/common/grpc/typed_async_client.h b/source/common/grpc/typed_async_client.h index af9e8df9492aa..f41a7cc7de733 100644 --- a/source/common/grpc/typed_async_client.h +++ b/source/common/grpc/typed_async_client.h @@ -36,6 +36,7 @@ template class AsyncStream /* : public RawAsyncStream */ { AsyncStream() = default; AsyncStream(RawAsyncStream* stream) : stream_(stream) {} AsyncStream(const AsyncStream& other) = default; + AsyncStream& operator=(const AsyncStream&) = default; void sendMessage(const Protobuf::Message& request, bool end_stream) { Internal::sendMessageUntyped(stream_, std::move(request), end_stream); } From fd0d4032ed4dd6e233dbf24e69ab49b9e24e40ee Mon Sep 17 00:00:00 2001 From: Wayne Zhang Date: Fri, 5 Nov 2021 07:33:42 -0700 Subject: [PATCH 027/110] Update grpc-httpjson-transcoding (#18907) Signed-off-by: Wayne Zhang --- bazel/repository_locations.bzl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index bc3efdd3cc735..1de14053a2e88 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -638,13 +638,13 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "grpc-httpjson-transcoding", project_desc = "Library that supports transcoding so that HTTP/JSON can be converted to gRPC", project_url = "https://github.com/grpc-ecosystem/grpc-httpjson-transcoding", - version = "3127eeaf889d48b5d2cd870fd910f1ae3e7abca4", - sha256 = "f98da3fe9b2539c9fc9b3884e01baa8d2e19ed016bc5f41bed2998781c96ac63", + version = "6acdde98c94b70453b39a81fe7bf59b847188fc3", + sha256 = "a076ca60fca2719b505c49dc1175fd27485dc76deaaef21581e6bd37c84da890", strip_prefix = "grpc-httpjson-transcoding-{version}", urls = ["https://github.com/grpc-ecosystem/grpc-httpjson-transcoding/archive/{version}.tar.gz"], use_category = ["dataplane_ext"], extensions = ["envoy.filters.http.grpc_json_transcoder"], - release_date = "2021-09-22", + release_date = "2021-11-04", cpe = "N/A", ), io_bazel_rules_go = dict( From c23e473ad35e801e8d965e58e3e2c5e2fc62f833 Mon Sep 17 00:00:00 2001 From: Peter Jausovec Date: Fri, 5 Nov 2021 07:34:10 -0700 Subject: [PATCH 028/110] Remove an extra word (typo) (#18908) Signed-off-by: Peter Jausovec --- docs/root/configuration/observability/access_log/usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/root/configuration/observability/access_log/usage.rst b/docs/root/configuration/observability/access_log/usage.rst index 1b6ba004befef..c00a5a5c35b47 100644 --- a/docs/root/configuration/observability/access_log/usage.rst +++ b/docs/root/configuration/observability/access_log/usage.rst @@ -378,7 +378,7 @@ The following command operators are supported: * **SI**: Stream idle timeout in addition to 408 response code. * **DPE**: The downstream request had an HTTP protocol error. * **UPE**: The upstream response had an HTTP protocol error. - * **UMSDR**: The upstream request reached to max stream duration. + * **UMSDR**: The upstream request reached max stream duration. * **OM**: Overload Manager terminated the request. %ROUTE_NAME% From 26da59944339363e7b6e90664f55b9f73b5923b5 Mon Sep 17 00:00:00 2001 From: Rei Shimizu Date: Sat, 6 Nov 2021 02:02:02 +0900 Subject: [PATCH 029/110] remove generate_api_shadow by mistake (#18911) Signed-off-by: Shikugawa --- .../access_loggers/grpc/v3/als.proto | 98 ------------------- 1 file changed, 98 deletions(-) delete mode 100644 generated_api_shadow/envoy/extensions/access_loggers/grpc/v3/als.proto diff --git a/generated_api_shadow/envoy/extensions/access_loggers/grpc/v3/als.proto b/generated_api_shadow/envoy/extensions/access_loggers/grpc/v3/als.proto deleted file mode 100644 index d8f2175dc8898..0000000000000 --- a/generated_api_shadow/envoy/extensions/access_loggers/grpc/v3/als.proto +++ /dev/null @@ -1,98 +0,0 @@ -syntax = "proto3"; - -package envoy.extensions.access_loggers.grpc.v3; - -import "envoy/config/core/v3/base.proto"; -import "envoy/config/core/v3/config_source.proto"; -import "envoy/config/core/v3/grpc_service.proto"; - -import "google/protobuf/duration.proto"; -import "google/protobuf/wrappers.proto"; - -import "udpa/annotations/status.proto"; -import "udpa/annotations/versioning.proto"; -import "validate/validate.proto"; - -option java_package = "io.envoyproxy.envoy.extensions.access_loggers.grpc.v3"; -option java_outer_classname = "AlsProto"; -option java_multiple_files = true; -option (udpa.annotations.file_status).package_version_status = ACTIVE; - -// [#protodoc-title: gRPC Access Log Service (ALS)] - -// Configuration for the built-in *envoy.access_loggers.http_grpc* -// :ref:`AccessLog `. This configuration will -// populate :ref:`StreamAccessLogsMessage.http_logs -// `. -// [#extension: envoy.access_loggers.http_grpc] -message HttpGrpcAccessLogConfig { - option (udpa.annotations.versioning).previous_message_type = - "envoy.config.accesslog.v2.HttpGrpcAccessLogConfig"; - - CommonGrpcAccessLogConfig common_config = 1 [(validate.rules).message = {required: true}]; - - // Additional request headers to log in :ref:`HTTPRequestProperties.request_headers - // `. - repeated string additional_request_headers_to_log = 2; - - // Additional response headers to log in :ref:`HTTPResponseProperties.response_headers - // `. - repeated string additional_response_headers_to_log = 3; - - // Additional response trailers to log in :ref:`HTTPResponseProperties.response_trailers - // `. - repeated string additional_response_trailers_to_log = 4; -} - -// Configuration for the built-in *envoy.access_loggers.tcp_grpc* type. This configuration will -// populate *StreamAccessLogsMessage.tcp_logs*. -// [#extension: envoy.access_loggers.tcp_grpc] -message TcpGrpcAccessLogConfig { - option (udpa.annotations.versioning).previous_message_type = - "envoy.config.accesslog.v2.TcpGrpcAccessLogConfig"; - - CommonGrpcAccessLogConfig common_config = 1 [(validate.rules).message = {required: true}]; -} - -// Common configuration for gRPC access logs. -// [#next-free-field: 8] -message CommonGrpcAccessLogConfig { - option (udpa.annotations.versioning).previous_message_type = - "envoy.config.accesslog.v2.CommonGrpcAccessLogConfig"; - - // The friendly name of the access log to be returned in :ref:`StreamAccessLogsMessage.Identifier - // `. This allows the - // access log server to differentiate between different access logs coming from the same Envoy. - string log_name = 1 [(validate.rules).string = {min_len: 1}]; - - // The gRPC service for the access log service. - config.core.v3.GrpcService grpc_service = 2 [(validate.rules).message = {required: true}]; - - // API version for access logs service transport protocol. This describes the access logs service - // gRPC endpoint and version of messages used on the wire. - config.core.v3.ApiVersion transport_api_version = 6 - [(validate.rules).enum = {defined_only: true}]; - - // Interval for flushing access logs to the gRPC stream. Logger will flush requests every time - // this interval is elapsed, or when batch size limit is hit, whichever comes first. Defaults to - // 1 second. - google.protobuf.Duration buffer_flush_interval = 3 [(validate.rules).duration = {gt {}}]; - - // Soft size limit in bytes for access log entries buffer. Logger will buffer requests until - // this limit it hit, or every time flush interval is elapsed, whichever comes first. Setting it - // to zero effectively disables the batching. Defaults to 16384. - google.protobuf.UInt32Value buffer_size_bytes = 4; - - // Additional filter state objects to log in :ref:`filter_state_objects - // `. - // Logger will call `FilterState::Object::serializeAsProto` to serialize the filter state object. - repeated string filter_state_objects_to_log = 5; - - // Sets the retry policy when the establishment of a gRPC stream fails. - // If the stream succeeds once in establishing If the stream succeeds - // at least once in establishing itself, no retry will be performed - // no matter what gRPC status is received. Note that only - // :ref:`num_retries ` - // will be used in this configuration. - config.core.v3.RetryPolicy grpc_stream_retry_policy = 7; -} From 0264041ec7a271c0291aa2d761856045c90aef7e Mon Sep 17 00:00:00 2001 From: Taylor Barrella Date: Fri, 5 Nov 2021 10:06:14 -0700 Subject: [PATCH 030/110] ecds: templatize FilterConfigProviderManagerImpl (#18832) Part of https://github.com/envoyproxy/envoy/issues/14696#issuecomment-897855672. The refactoring that enables this is mainly within `FilterConfigSubscription::onConfigUpdate`. The rest is adding and removing template parameters. Risk Level: Low Testing: Existing (refactoring) Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A #14696 Signed-off-by: Taylor Barrella --- envoy/config/extension_config_provider.h | 6 +- envoy/filter/BUILD | 1 - envoy/filter/config_provider_manager.h | 24 ++-- source/common/filter/config_discovery_impl.cc | 69 +++--------- source/common/filter/config_discovery_impl.h | 105 +++++++++++++----- .../network/http_connection_manager/config.h | 4 +- .../filter/config_discovery_impl_test.cc | 11 +- 7 files changed, 118 insertions(+), 102 deletions(-) diff --git a/envoy/config/extension_config_provider.h b/envoy/config/extension_config_provider.h index e55ca63543f66..ba8761f045fa2 100644 --- a/envoy/config/extension_config_provider.h +++ b/envoy/config/extension_config_provider.h @@ -16,7 +16,7 @@ using ConfigAppliedCb = std::function; * the extension configuration discovery service. Dynamically updated extension * configurations may share subscriptions across extension config providers. */ -template class ExtensionConfigProvider { +template class ExtensionConfigProvider { public: virtual ~ExtensionConfigProvider() = default; @@ -63,9 +63,9 @@ class DynamicExtensionConfigProviderBase { virtual void applyDefaultConfiguration() PURE; }; -template +template class DynamicExtensionConfigProvider : public DynamicExtensionConfigProviderBase, - public ExtensionConfigProvider {}; + public ExtensionConfigProvider {}; } // namespace Config } // namespace Envoy diff --git a/envoy/filter/BUILD b/envoy/filter/BUILD index 02e267213684f..9a6697e0ee631 100644 --- a/envoy/filter/BUILD +++ b/envoy/filter/BUILD @@ -13,7 +13,6 @@ envoy_cc_library( hdrs = ["config_provider_manager.h"], deps = [ "//envoy/config:extension_config_provider_interface", - "//envoy/http:filter_interface", "//envoy/init:manager_interface", "//envoy/server:filter_config_interface", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", diff --git a/envoy/filter/config_provider_manager.h b/envoy/filter/config_provider_manager.h index 824c36e73dea5..cd1affc579977 100644 --- a/envoy/filter/config_provider_manager.h +++ b/envoy/filter/config_provider_manager.h @@ -2,7 +2,6 @@ #include "envoy/config/core/v3/config_source.pb.h" #include "envoy/config/extension_config_provider.h" -#include "envoy/http/filter.h" #include "envoy/init/manager.h" #include "envoy/server/filter_config.h" @@ -11,19 +10,20 @@ namespace Envoy { namespace Filter { -using FilterConfigProvider = - Envoy::Config::ExtensionConfigProvider; -using FilterConfigProviderPtr = std::unique_ptr; -using DynamicFilterConfigProvider = Envoy::Config::DynamicExtensionConfigProvider< - Server::Configuration::NamedHttpFilterConfigFactory, Envoy::Http::FilterFactoryCb>; -using DynamicFilterConfigProviderPtr = std::unique_ptr; +template +using FilterConfigProvider = Envoy::Config::ExtensionConfigProvider; +template +using FilterConfigProviderPtr = std::unique_ptr>; +template +using DynamicFilterConfigProvider = Envoy::Config::DynamicExtensionConfigProvider; +template +using DynamicFilterConfigProviderPtr = std::unique_ptr>; /** * The FilterConfigProviderManager exposes the ability to get an FilterConfigProvider * for both static and dynamic filter config providers. */ -class FilterConfigProviderManager { +template class FilterConfigProviderManager { public: virtual ~FilterConfigProviderManager() = default; @@ -38,7 +38,7 @@ class FilterConfigProviderManager { * configured chain * @param filter_chain_type is the filter chain type */ - virtual DynamicFilterConfigProviderPtr createDynamicFilterConfigProvider( + virtual DynamicFilterConfigProviderPtr createDynamicFilterConfigProvider( const envoy::config::core::v3::ExtensionConfigSource& config_source, const std::string& filter_config_name, Server::Configuration::FactoryContext& factory_context, const std::string& stat_prefix, bool last_filter_in_filter_chain, @@ -49,8 +49,8 @@ class FilterConfigProviderManager { * @param config is a fully resolved filter instantiation factory. * @param filter_config_name is the name of the filter configuration resource. */ - virtual FilterConfigProviderPtr - createStaticFilterConfigProvider(const Envoy::Http::FilterFactoryCb& config, + virtual FilterConfigProviderPtr + createStaticFilterConfigProvider(const FactoryCb& config, const std::string& filter_config_name) PURE; }; diff --git a/source/common/filter/config_discovery_impl.cc b/source/common/filter/config_discovery_impl.cc index 5b694d63b427e..7ff56f26471fe 100644 --- a/source/common/filter/config_discovery_impl.cc +++ b/source/common/filter/config_discovery_impl.cc @@ -73,7 +73,6 @@ FilterConfigSubscription::FilterConfigSubscription( : Config::SubscriptionBase( factory_context.messageValidationContext().dynamicValidationVisitor(), "name"), filter_config_name_(filter_config_name), factory_context_(factory_context), - validator_(factory_context.messageValidationContext().dynamicValidationVisitor()), init_target_(fmt::format("FilterConfigSubscription init {}", filter_config_name_), [this]() { start(); }), scope_(factory_context.scope().createScope(stat_prefix + "extension_config_discovery." + @@ -116,9 +115,6 @@ void FilterConfigSubscription::onConfigUpdate( if (new_hash == last_config_hash_) { return; } - auto& factory = - Config::Utility::getAndCheckFactory( - filter_config); // Ensure that the filter config is valid in the filter chain context once the proto is processed. // Validation happens before updating to prevent a partial update application. It might be // possible that the providers have distinct type URL constraints. @@ -126,18 +122,17 @@ void FilterConfigSubscription::onConfigUpdate( for (auto* provider : filter_config_providers_) { provider->validateTypeUrl(type_url); } - ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig( - filter_config.typed_config(), validator_, factory); - bool is_terminal_filter = factory.isTerminalFilterByProto(*message, factory_context_); + auto [message, factory_name, is_terminal_filter] = + filter_config_provider_manager_.getMessage(filter_config, factory_context_); for (auto* provider : filter_config_providers_) { - provider->validateTerminalFilter(filter_config_name_, factory.name(), is_terminal_filter); + provider->validateTerminalFilter(filter_config_name_, factory_name, is_terminal_filter); } ENVOY_LOG(debug, "Updating filter config {}", filter_config_name_); Common::applyToAllWithCleanup( filter_config_providers_, - [&message, &version_info](DynamicFilterConfigProviderImplBase* provider, - std::shared_ptr cleanup) { + [&message = message, &version_info](DynamicFilterConfigProviderImplBase* provider, + std::shared_ptr cleanup) { provider->onConfigUpdate(*message, version_info, [cleanup] {}); }, [this]() { stats_.config_reload_.inc(); }); @@ -145,7 +140,7 @@ void FilterConfigSubscription::onConfigUpdate( last_config_ = std::move(message); last_type_url_ = type_url; last_version_info_ = version_info; - last_filter_name_ = factory.name(); + last_filter_name_ = factory_name; last_filter_is_terminal_ = is_terminal_filter; } @@ -244,46 +239,18 @@ void FilterConfigProviderManagerImplBase::applyLastOrDefaultConfig( } } -DynamicFilterConfigProviderPtr FilterConfigProviderManagerImpl::createDynamicFilterConfigProvider( - const envoy::config::core::v3::ExtensionConfigSource& config_source, - const std::string& filter_config_name, Server::Configuration::FactoryContext& factory_context, - const std::string& stat_prefix, bool last_filter_in_filter_chain, - const std::string& filter_chain_type) { - auto subscription = getSubscription(config_source.config_source(), filter_config_name, - factory_context, stat_prefix); - // For warming, wait until the subscription receives the first response to indicate readiness. - // Otherwise, mark ready immediately and start the subscription on initialization. A default - // config is expected in the latter case. - if (!config_source.apply_default_config_without_warming()) { - factory_context.initManager().add(subscription->initTarget()); - } - absl::flat_hash_set require_type_urls; - for (const auto& type_url : config_source.type_urls()) { - auto factory_type_url = TypeUtil::typeUrlToDescriptorFullName(type_url); - require_type_urls.emplace(factory_type_url); - } - - ProtobufTypes::MessagePtr default_config; - if (config_source.has_default_config()) { - default_config = - getDefaultConfig(config_source.default_config(), filter_config_name, factory_context, - last_filter_in_filter_chain, filter_chain_type, require_type_urls); - } - - auto provider = std::make_unique( - subscription, require_type_urls, factory_context, std::move(default_config), - last_filter_in_filter_chain, filter_chain_type, - [this, stat_prefix, - &factory_context](const Protobuf::Message& message) -> Envoy::Http::FilterFactoryCb { - return instantiateFilterFactory(message, stat_prefix, factory_context); - }); - - // Ensure the subscription starts if it has not already. - if (config_source.apply_default_config_without_warming()) { - factory_context.initManager().add(provider->initTarget()); - } - applyLastOrDefaultConfig(subscription, *provider, filter_config_name); - return provider; +std::tuple +HttpFilterConfigProviderManagerImpl::getMessage( + const envoy::config::core::v3::TypedExtensionConfig& filter_config, + Server::Configuration::FactoryContext& factory_context) const { + auto& factory = + Config::Utility::getAndCheckFactory( + filter_config); + ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig( + filter_config.typed_config(), + factory_context.messageValidationContext().dynamicValidationVisitor(), factory); + bool is_terminal_filter = factory.isTerminalFilterByProto(*message, factory_context); + return {std::move(message), factory.name(), is_terminal_filter}; } ProtobufTypes::MessagePtr HttpFilterConfigProviderManagerImpl::getDefaultConfig( diff --git a/source/common/filter/config_discovery_impl.h b/source/common/filter/config_discovery_impl.h index 4183ffe41fbbf..3d708adaa75a4 100644 --- a/source/common/filter/config_discovery_impl.h +++ b/source/common/filter/config_discovery_impl.h @@ -62,16 +62,17 @@ class DynamicFilterConfigProviderImplBase : public Config::DynamicExtensionConfi /** * Implementation of a filter config provider using discovery subscriptions. **/ +template class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBase, - public DynamicFilterConfigProvider { + public DynamicFilterConfigProvider { public: - DynamicFilterConfigProviderImpl( - FilterConfigSubscriptionSharedPtr& subscription, - const absl::flat_hash_set& require_type_urls, - Server::Configuration::FactoryContext& factory_context, - ProtobufTypes::MessagePtr&& default_config, bool last_filter_in_filter_chain, - const std::string& filter_chain_type, - std::function factory_cb_fn) + DynamicFilterConfigProviderImpl(FilterConfigSubscriptionSharedPtr& subscription, + const absl::flat_hash_set& require_type_urls, + Server::Configuration::FactoryContext& factory_context, + ProtobufTypes::MessagePtr&& default_config, + bool last_filter_in_filter_chain, + const std::string& filter_chain_type, + std::function factory_cb_fn) : DynamicFilterConfigProviderImplBase(subscription, require_type_urls, last_filter_in_filter_chain, filter_chain_type), default_configuration_(std::move(default_config)), tls_(factory_context.threadLocal()), @@ -81,12 +82,12 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa // Config::ExtensionConfigProvider const std::string& name() override { return DynamicFilterConfigProviderImplBase::name(); } - absl::optional config() override { return tls_->config_; } + absl::optional config() override { return tls_->config_; } // Config::DynamicExtensionConfigProviderBase void onConfigUpdate(const Protobuf::Message& message, const std::string&, Config::ConfigAppliedCb cb) override { - const Envoy::Http::FilterFactoryCb config = factory_cb_fn_(message); + const FactoryCb config = factory_cb_fn_(message); tls_.runOnAllThreads( [config, cb](OptRef tls) { tls->config_ = config; @@ -102,7 +103,7 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa } void onConfigRemoved(Config::ConfigAppliedCb applied_on_all_threads) override { - const absl::optional default_config = + const absl::optional default_config = default_configuration_ ? absl::make_optional(factory_cb_fn_(*default_configuration_)) : absl::nullopt; tls_.runOnAllThreads( @@ -126,15 +127,15 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa private: struct ThreadLocalConfig : public ThreadLocal::ThreadLocalObject { ThreadLocalConfig() : config_{absl::nullopt} {} - absl::optional config_{}; + absl::optional config_{}; }; // Currently applied configuration to ensure that the main thread deletes the last reference to // it. - absl::optional current_config_{absl::nullopt}; + absl::optional current_config_{absl::nullopt}; const ProtobufTypes::MessagePtr default_configuration_; ThreadLocal::TypedSlot tls_; - const std::function factory_cb_fn_; + const std::function factory_cb_fn_; }; /** @@ -199,7 +200,6 @@ class FilterConfigSubscription std::string last_filter_name_; bool last_filter_is_terminal_; Server::Configuration::FactoryContext& factory_context_; - ProtobufMessage::ValidationVisitor& validator_; Init::SharedTargetImpl init_target_; bool started_{false}; @@ -222,18 +222,18 @@ class FilterConfigSubscription /** * Provider implementation of a static filter config. **/ -class StaticFilterConfigProviderImpl : public FilterConfigProvider { +template +class StaticFilterConfigProviderImpl : public FilterConfigProvider { public: - StaticFilterConfigProviderImpl(const Envoy::Http::FilterFactoryCb& config, - const std::string filter_config_name) + StaticFilterConfigProviderImpl(const FactoryCb& config, const std::string filter_config_name) : config_(config), filter_config_name_(filter_config_name) {} // Config::ExtensionConfigProvider const std::string& name() override { return filter_config_name_; } - absl::optional config() override { return config_; } + absl::optional config() override { return config_; } private: - Envoy::Http::FilterFactoryCb config_; + FactoryCb config_; const std::string filter_config_name_; }; @@ -241,6 +241,13 @@ class StaticFilterConfigProviderImpl : public FilterConfigProvider { * Base class for a FilterConfigProviderManager. */ class FilterConfigProviderManagerImplBase : Logger::Loggable { +public: + virtual ~FilterConfigProviderManagerImplBase() = default; + + virtual std::tuple + getMessage(const envoy::config::core::v3::TypedExtensionConfig& filter_config, + Server::Configuration::FactoryContext& factory_context) const PURE; + protected: std::shared_ptr getSubscription(const envoy::config::core::v3::ConfigSource& config_source, @@ -258,20 +265,56 @@ class FilterConfigProviderManagerImplBase : Logger::Loggable /** * An implementation of FilterConfigProviderManager. */ +template class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBase, - public FilterConfigProviderManager, + public FilterConfigProviderManager, public Singleton::Instance { public: - DynamicFilterConfigProviderPtr createDynamicFilterConfigProvider( + DynamicFilterConfigProviderPtr createDynamicFilterConfigProvider( const envoy::config::core::v3::ExtensionConfigSource& config_source, const std::string& filter_config_name, Server::Configuration::FactoryContext& factory_context, const std::string& stat_prefix, bool last_filter_in_filter_chain, - const std::string& filter_chain_type) override; + const std::string& filter_chain_type) override { + auto subscription = getSubscription(config_source.config_source(), filter_config_name, + factory_context, stat_prefix); + // For warming, wait until the subscription receives the first response to indicate readiness. + // Otherwise, mark ready immediately and start the subscription on initialization. A default + // config is expected in the latter case. + if (!config_source.apply_default_config_without_warming()) { + factory_context.initManager().add(subscription->initTarget()); + } + absl::flat_hash_set require_type_urls; + for (const auto& type_url : config_source.type_urls()) { + auto factory_type_url = TypeUtil::typeUrlToDescriptorFullName(type_url); + require_type_urls.emplace(factory_type_url); + } + + ProtobufTypes::MessagePtr default_config; + if (config_source.has_default_config()) { + default_config = + getDefaultConfig(config_source.default_config(), filter_config_name, factory_context, + last_filter_in_filter_chain, filter_chain_type, require_type_urls); + } - FilterConfigProviderPtr - createStaticFilterConfigProvider(const Envoy::Http::FilterFactoryCb& config, + auto provider = std::make_unique>( + subscription, require_type_urls, factory_context, std::move(default_config), + last_filter_in_filter_chain, filter_chain_type, + [this, stat_prefix, &factory_context](const Protobuf::Message& message) -> FactoryCb { + return instantiateFilterFactory(message, stat_prefix, factory_context); + }); + + // Ensure the subscription starts if it has not already. + if (config_source.apply_default_config_without_warming()) { + factory_context.initManager().add(provider->initTarget()); + } + applyLastOrDefaultConfig(subscription, *provider, filter_config_name); + return provider; + } + + FilterConfigProviderPtr + createStaticFilterConfigProvider(const FactoryCb& config, const std::string& filter_config_name) override { - return std::make_unique(config, filter_config_name); + return std::make_unique>(config, filter_config_name); } protected: @@ -281,12 +324,18 @@ class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBa bool last_filter_in_filter_chain, const std::string& filter_chain_type, const absl::flat_hash_set& require_type_urls) const PURE; - virtual Http::FilterFactoryCb + virtual FactoryCb instantiateFilterFactory(const Protobuf::Message& message, const std::string& stat_prefix, Server::Configuration::FactoryContext& factory_context) const PURE; }; -class HttpFilterConfigProviderManagerImpl : public FilterConfigProviderManagerImpl { +class HttpFilterConfigProviderManagerImpl + : public FilterConfigProviderManagerImpl { +public: + std::tuple + getMessage(const envoy::config::core::v3::TypedExtensionConfig& filter_config, + Server::Configuration::FactoryContext& factory_context) const override; + protected: ProtobufTypes::MessagePtr getDefaultConfig(const ProtobufWkt::Any& proto_config, const std::string& filter_config_name, diff --git a/source/extensions/filters/network/http_connection_manager/config.h b/source/extensions/filters/network/http_connection_manager/config.h index e480599fefd98..3464659a14c0b 100644 --- a/source/extensions/filters/network/http_connection_manager/config.h +++ b/source/extensions/filters/network/http_connection_manager/config.h @@ -39,7 +39,7 @@ namespace Extensions { namespace NetworkFilters { namespace HttpConnectionManager { -using FilterConfigProviderManager = Filter::FilterConfigProviderManager; +using FilterConfigProviderManager = Filter::FilterConfigProviderManager; /** * Config registration for the HTTP connection manager filter. @see NamedNetworkFilterConfigFactory. @@ -125,7 +125,7 @@ class HttpConnectionManagerConfig : Logger::Loggable, // Http::FilterChainFactory void createFilterChain(Http::FilterChainFactoryCallbacks& callbacks) override; - using FilterFactoriesList = std::list; + using FilterFactoriesList = std::list>; struct FilterConfig { std::unique_ptr filter_factories; bool allow_upgrade; diff --git a/test/common/filter/config_discovery_impl_test.cc b/test/common/filter/config_discovery_impl_test.cc index 7eebb2b237a02..4db6a00108664 100644 --- a/test/common/filter/config_discovery_impl_test.cc +++ b/test/common/filter/config_discovery_impl_test.cc @@ -76,9 +76,9 @@ class FilterConfigDiscoveryImplTest : public FilterConfigDiscoveryTestBase { } ~FilterConfigDiscoveryImplTest() override { factory_context_.thread_local_.shutdownThread(); } - DynamicFilterConfigProviderPtr createProvider(std::string name, bool warm, - bool default_configuration, - bool last_filter_config = true) { + DynamicFilterConfigProviderPtr + createProvider(std::string name, bool warm, bool default_configuration, + bool last_filter_config = true) { EXPECT_CALL(init_manager_, add(_)); envoy::config::core::v3::ExtensionConfigSource config_source; @@ -121,8 +121,9 @@ config_source: { ads: {} } init_manager_.initialize(init_watcher_); } - std::unique_ptr filter_config_provider_manager_; - DynamicFilterConfigProviderPtr provider_; + std::unique_ptr> + filter_config_provider_manager_; + DynamicFilterConfigProviderPtr provider_; Config::SubscriptionCallbacks* callbacks_{}; }; From 2ce532577a650c0fe1a731d13ddd86574197a20e Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Sun, 7 Nov 2021 10:42:10 -0800 Subject: [PATCH 031/110] bazel: disable lld on macOS (#18922) I'm not sure what changed here but it seems chromium attempts to use lld on macOS even though it doesn't ship with Xcode. This could be intentional on their part if they require folks to use an llvm toolchain instead, but this likely isn't the right move for envoy unless we vendor that toolchain ourselves. For now we can safely disable this. https://github.com/envoyproxy/envoy/issues/16482#issuecomment-962372629 Signed-off-by: Keith Smiley --- bazel/external/wee8.genrule_cmd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bazel/external/wee8.genrule_cmd b/bazel/external/wee8.genrule_cmd index 8c92818102fec..e54777fd3c0df 100644 --- a/bazel/external/wee8.genrule_cmd +++ b/bazel/external/wee8.genrule_cmd @@ -100,6 +100,11 @@ WEE8_BUILD_ARGS+=" v8_use_external_startup_data=false" # TODO(PiotrSikora): remove when fixed upstream. WEE8_BUILD_ARGS+=" v8_enable_shared_ro_heap=false" +# Disable lld on Darwin since it's not vendored with Xcode +if [[ $${SYSTEM} == Darwin ]]; then + WEE8_BUILD_ARGS+=" use_lld=false" +fi + # Set target architecture. if [[ $${ARCH} == "x86_64" ]]; then WEE8_BUILD_ARGS+=" target_cpu=\"x64\"" From a13fb858be09069e04a7a49eede8a8d2d09b4ee3 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Sun, 7 Nov 2021 10:42:51 -0800 Subject: [PATCH 032/110] bazel: use mac ninja on arm64 (#18921) I don't know gn well enough to know how this is used, but it seems like both macOS architectures should point to this. Signed-off-by: Keith Smiley --- bazel/external/wee8.genrule_cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bazel/external/wee8.genrule_cmd b/bazel/external/wee8.genrule_cmd index e54777fd3c0df..505f33085a23d 100644 --- a/bazel/external/wee8.genrule_cmd +++ b/bazel/external/wee8.genrule_cmd @@ -136,7 +136,7 @@ else fi # Select ninja tool for the current platform. -if [[ $${PLATFORM} == "Darwin-x86_64" ]]; then +if [[ $${SYSTEM} == "Darwin" ]]; then ninja=third_party/depot_tools/ninja-mac elif [[ $${PLATFORM} == "Linux-x86_64" ]]; then ninja=third_party/depot_tools/ninja-linux64 From ae43490333294cec61750ab92cb40dda474737aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Nov 2021 13:49:55 +0000 Subject: [PATCH 033/110] build(deps): bump actions/checkout from 2 to 2.4.0 (#18885) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 2.4.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...ec3a7ce113134d7a93b817d10a8272cb61118579) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-daily.yml | 2 +- .github/workflows/codeql-push.yml | 2 +- .github/workflows/pr_notifier.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-daily.yml b/.github/workflows/codeql-daily.yml index 358695c84f61c..920108ff42eeb 100644 --- a/.github/workflows/codeql-daily.yml +++ b/.github/workflows/codeql-daily.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 + uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.3.4 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/codeql-push.yml b/.github/workflows/codeql-push.yml index d79e7a011d347..26b66486367b3 100644 --- a/.github/workflows/codeql-push.yml +++ b/.github/workflows/codeql-push.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 + uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.3.4 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/pr_notifier.yml b/.github/workflows/pr_notifier.yml index 88759f9f14141..fee32f3c49e83 100644 --- a/.github/workflows/pr_notifier.yml +++ b/.github/workflows/pr_notifier.yml @@ -10,7 +10,7 @@ jobs: if: github.repository_owner == 'envoyproxy' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 - name: Set up Python 3.8 uses: actions/setup-python@v2 with: From 6fe10e67adc0f902ec7572e5465ba80c8bbd5305 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Mon, 8 Nov 2021 09:21:24 -0500 Subject: [PATCH 034/110] http3: capacity fixes (#18879) Fixing a bug where the new quic limits weren't respected on connection creation, because accounting used effectiveConcurrentStreamLimit() which wasn't aware of quic stream limitations. Risk Level: Medium Testing: new integration test Docs Changes: n/a Release Notes: n/a Signed-off-by: Alyssa Wilk --- source/common/conn_pool/conn_pool_base.h | 12 ++++----- source/common/http/http3/conn_pool.h | 21 ++++++++++++--- .../multiplexed_upstream_integration_test.cc | 26 +++++++++++++++++++ 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/source/common/conn_pool/conn_pool_base.h b/source/common/conn_pool/conn_pool_base.h index 29a714410f8cb..802cff1d639c2 100644 --- a/source/common/conn_pool/conn_pool_base.h +++ b/source/common/conn_pool/conn_pool_base.h @@ -49,7 +49,7 @@ class ActiveClient : public LinkedObject, // Returns the concurrent stream limit, accounting for if the total stream limit // is less than the concurrent stream limit. - uint32_t effectiveConcurrentStreamLimit() const { + virtual uint32_t effectiveConcurrentStreamLimit() const { return std::min(remaining_streams_, concurrent_stream_limit_); } @@ -267,6 +267,11 @@ class ConnPoolImplBase : protected Logger::Loggable { connecting_stream_capacity_ -= delta; } + void incrConnectingAndConnectedStreamCapacity(uint32_t delta) { + state_.incrConnectingAndConnectedStreamCapacity(delta); + connecting_stream_capacity_ += delta; + } + // Called when an upstream is ready to serve pending streams. void onUpstreamReady(); @@ -309,11 +314,6 @@ class ConnPoolImplBase : protected Logger::Loggable { bool hasActiveStreams() const { return num_active_streams_ > 0; } - void incrConnectingAndConnectedStreamCapacity(uint32_t delta) { - state_.incrConnectingAndConnectedStreamCapacity(delta); - connecting_stream_capacity_ += delta; - } - Upstream::ClusterConnectivityState& state_; const Upstream::HostConstSharedPtr host_; diff --git a/source/common/http/http3/conn_pool.h b/source/common/http/http3/conn_pool.h index 2db0426630d67..d89c5fa69ab06 100644 --- a/source/common/http/http3/conn_pool.h +++ b/source/common/http/http3/conn_pool.h @@ -35,6 +35,11 @@ class ActiveClient : public MultiplexedActiveClientBase { return MultiplexedActiveClientBase::newStreamEncoder(response_decoder); } + uint32_t effectiveConcurrentStreamLimit() const override { + return std::min(MultiplexedActiveClientBase::effectiveConcurrentStreamLimit(), + quiche_capacity_); + } + // Overload the default capacity calculations to return the quic capacity // (modified by any stream limits in Envoy config) int64_t currentUnusedCapacity() const override { @@ -54,10 +59,18 @@ class ActiveClient : public MultiplexedActiveClientBase { quiche_capacity_ = new_quiche_capacity; uint64_t new_capacity = currentUnusedCapacity(); - if (new_capacity < old_capacity) { - parent_.decrClusterStreamCapacity(old_capacity - new_capacity); - } else if (old_capacity < new_capacity) { - parent_.incrClusterStreamCapacity(new_capacity - old_capacity); + if (connect_timer_) { + if (new_capacity < old_capacity) { + parent_.decrConnectingAndConnectedStreamCapacity(old_capacity - new_capacity); + } else if (old_capacity < new_capacity) { + parent_.incrConnectingAndConnectedStreamCapacity(new_capacity - old_capacity); + } + } else { + if (new_capacity < old_capacity) { + parent_.decrClusterStreamCapacity(old_capacity - new_capacity); + } else if (old_capacity < new_capacity) { + parent_.incrClusterStreamCapacity(new_capacity - old_capacity); + } } } diff --git a/test/integration/multiplexed_upstream_integration_test.cc b/test/integration/multiplexed_upstream_integration_test.cc index 1a30fda97d9db..ae1e3c8166fb0 100644 --- a/test/integration/multiplexed_upstream_integration_test.cc +++ b/test/integration/multiplexed_upstream_integration_test.cc @@ -288,6 +288,32 @@ TEST_P(MultiplexedUpstreamIntegrationTest, ManySimultaneousRequestsTightUpstream manySimultaneousRequests(1024, 1024, 10); } +TEST_P(MultiplexedUpstreamIntegrationTest, ManySimultaneousRequestsLaxUpstreamLimits) { + envoy::config::core::v3::Http2ProtocolOptions config; + config.mutable_max_concurrent_streams()->set_value(10000); + mergeOptions(config); + envoy::config::listener::v3::QuicProtocolOptions options; + options.mutable_quic_protocol_options()->mutable_max_concurrent_streams()->set_value(10000); + mergeOptions(options); + + if (upstreamProtocol() == Http::CodecType::HTTP3) { + config_helper_.addConfigModifier( + [&](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() + ->mutable_http3_protocol_options() + ->mutable_quic_protocol_options() + ->mutable_max_concurrent_streams() + ->set_value(10000); + ConfigHelper::setProtocolOptions( + *bootstrap.mutable_static_resources()->mutable_clusters(0), protocol_options); + }); + } + + manySimultaneousRequests(1024, 1024, 10); +} + TEST_P(MultiplexedUpstreamIntegrationTest, ManyLargeSimultaneousRequestWithBufferLimits) { config_helper_.setBufferLimits(1024, 1024); // Set buffer limits upstream and downstream. manySimultaneousRequests(1024 * 20, 1024 * 20); From 3d31be3f6a29c6f448e7c58fad3a58a5a85379e9 Mon Sep 17 00:00:00 2001 From: danzh Date: Mon, 8 Nov 2021 09:42:29 -0500 Subject: [PATCH 035/110] http: add early-data upon receiving 0-RRT request (#18803) Commit Message: Make HCM to add Early-Data:1 header to a request sent as early data according to https://datatracker.ietf.org/doc/html/rfc8470 Risk Level: low Testing: modified quic integration tests to test the presence of that header in upstream. Docs Changes: none Release Notes: none Platform Specific Features: none Part of #18802 Fixes #18868 Signed-off-by: Dan Zhang --- source/common/http/conn_manager_utility.cc | 7 +++ source/common/http/headers.h | 1 + test/integration/http_integration.cc | 19 ++++++-- test/integration/http_integration.h | 8 +++- .../integration/quic_http_integration_test.cc | 47 ++++++++++++++++++- 5 files changed, 75 insertions(+), 7 deletions(-) diff --git a/source/common/http/conn_manager_utility.cc b/source/common/http/conn_manager_utility.cc index c75401a9ede5c..6c07a7dab1049 100644 --- a/source/common/http/conn_manager_utility.cc +++ b/source/common/http/conn_manager_utility.cc @@ -258,6 +258,13 @@ ConnectionManagerUtility::MutateRequestHeadersResult ConnectionManagerUtility::m rid_extension->set(request_headers, force_set); } + if (connection.connecting() && request_headers.get(Headers::get().EarlyData).empty()) { + // Add an Early-Data header to indicate that this is a 0-RTT request according to + // https://datatracker.ietf.org/doc/html/rfc8470#section-5.1. + HeaderString value; + value.setCopy("1"); + request_headers.addViaMove(HeaderString(Headers::get().EarlyData), std::move(value)); + } mutateXfccRequestHeader(request_headers, connection, config); return {final_remote_address, absl::nullopt}; diff --git a/source/common/http/headers.h b/source/common/http/headers.h index ccae31bf706c3..75b574599ef7f 100644 --- a/source/common/http/headers.h +++ b/source/common/http/headers.h @@ -218,6 +218,7 @@ class HeaderValues { const LowerCaseString WWWAuthenticate{"www-authenticate"}; const LowerCaseString XContentTypeOptions{"x-content-type-options"}; const LowerCaseString XSquashDebug{"x-squash-debug"}; + const LowerCaseString EarlyData{"early-data"}; struct { const std::string Close{"close"}; diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index 711ca5d1bddf7..61418440f839e 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -77,11 +77,20 @@ IntegrationCodecClient::IntegrationCodecClient( Event::Dispatcher& dispatcher, Random::RandomGenerator& random, Network::ClientConnectionPtr&& conn, Upstream::HostDescriptionConstSharedPtr host_description, Http::CodecType type) + : IntegrationCodecClient(dispatcher, random, std::move(conn), std::move(host_description), type, + true) {} + +IntegrationCodecClient::IntegrationCodecClient( + Event::Dispatcher& dispatcher, Random::RandomGenerator& random, + Network::ClientConnectionPtr&& conn, Upstream::HostDescriptionConstSharedPtr host_description, + Http::CodecType type, bool wait_till_connected) : CodecClientProd(type, std::move(conn), host_description, dispatcher, random), - dispatcher_(dispatcher), callbacks_(*this), codec_callbacks_(*this) { + dispatcher_(dispatcher), callbacks_(*this, wait_till_connected), codec_callbacks_(*this) { connection_->addConnectionCallbacks(callbacks_); setCodecConnectionCallbacks(codec_callbacks_); - dispatcher.run(Event::Dispatcher::RunType::Block); + if (wait_till_connected) { + dispatcher.run(Event::Dispatcher::RunType::Block); + } } void IntegrationCodecClient::flushWrite() { @@ -203,12 +212,14 @@ void IntegrationCodecClient::ConnectionCallbacks::onEvent(Network::ConnectionEve parent_.last_connection_event_ = event; if (event == Network::ConnectionEvent::Connected) { parent_.connected_ = true; - parent_.connection_->dispatcher().exit(); + if (block_till_connected_) { + parent_.connection_->dispatcher().exit(); + } } else if (event == Network::ConnectionEvent::RemoteClose) { parent_.disconnected_ = true; parent_.connection_->dispatcher().exit(); } else { - if (parent_.type() == Http::CodecType::HTTP3 && !parent_.connected_) { + if (parent_.type() == Http::CodecType::HTTP3 && !parent_.connected_ && block_till_connected_) { // Before handshake gets established, any connection failure should exit the loop. I.e. a QUIC // connection may fail of INVALID_VERSION if both this client doesn't support any of the // versions the server advertised before handshake established. In this case the connection is diff --git a/test/integration/http_integration.h b/test/integration/http_integration.h index 01327b015a922..07b2a67891e62 100644 --- a/test/integration/http_integration.h +++ b/test/integration/http_integration.h @@ -26,6 +26,10 @@ class IntegrationCodecClient : public Http::CodecClientProd { Network::ClientConnectionPtr&& conn, Upstream::HostDescriptionConstSharedPtr host_description, Http::CodecType type); + IntegrationCodecClient(Event::Dispatcher& dispatcher, Random::RandomGenerator& random, + Network::ClientConnectionPtr&& conn, + Upstream::HostDescriptionConstSharedPtr host_description, + Http::CodecType type, bool wait_till_connected); IntegrationStreamDecoderPtr makeHeaderOnlyRequest(const Http::RequestHeaderMap& headers); IntegrationStreamDecoderPtr makeRequestWithBody(const Http::RequestHeaderMap& headers, @@ -52,7 +56,8 @@ class IntegrationCodecClient : public Http::CodecClientProd { private: struct ConnectionCallbacks : public Network::ConnectionCallbacks { - ConnectionCallbacks(IntegrationCodecClient& parent) : parent_(parent) {} + ConnectionCallbacks(IntegrationCodecClient& parent, bool block_till_connected) + : parent_(parent), block_till_connected_(block_till_connected) {} // Network::ConnectionCallbacks void onEvent(Network::ConnectionEvent event) override; @@ -60,6 +65,7 @@ class IntegrationCodecClient : public Http::CodecClientProd { void onBelowWriteBufferLowWatermark() override {} IntegrationCodecClient& parent_; + bool block_till_connected_; }; struct CodecCallbacks : public Http::ConnectionCallbacks { diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index 843d4c1b945ca..472ec639e272a 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -204,6 +204,14 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, IntegrationCodecClientPtr makeRawHttpConnection( Network::ClientConnectionPtr&& conn, absl::optional http2_options) override { + return makeRawHttp3Connection(std::move(conn), http2_options, true); + } + + // Create Http3 codec client with the option not to wait for 1-RTT key establishment. + IntegrationCodecClientPtr makeRawHttp3Connection( + Network::ClientConnectionPtr&& conn, + absl::optional http2_options, + bool wait_for_1rtt_key) { std::shared_ptr cluster{new NiceMock()}; cluster->max_response_headers_count_ = 200; if (http2_options.has_value()) { @@ -218,7 +226,8 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, // This call may fail in QUICHE because of INVALID_VERSION. QUIC connection doesn't support // in-connection version negotiation. auto codec = std::make_unique(*dispatcher_, random_, std::move(conn), - host_description, downstream_protocol_); + host_description, downstream_protocol_, + wait_for_1rtt_key); if (codec->disconnected()) { // Connection may get closed during version negotiation or handshake. // TODO(#8479) QUIC connection doesn't support in-connection version negotiationPropagate @@ -386,10 +395,12 @@ TEST_P(QuicHttpIntegrationTest, ZeroRtt) { // Close the first connection. codec_client_->close(); // Start a second connection. - codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http")))); + codec_client_ = makeRawHttp3Connection(makeClientConnection((lookupPort("http"))), absl::nullopt, + /*wait_for_1rtt_key*/ false); // Send a complete request on the second connection. auto response2 = codec_client_->makeHeaderOnlyRequest(default_request_headers_); waitForNextUpstreamRequest(0); + EXPECT_THAT(upstream_request_->headers(), HeaderValueOf(Http::Headers::get().EarlyData, "1")); upstream_request_->encodeHeaders(default_response_headers_, true); ASSERT_TRUE(response2->waitForEndStream()); // Ensure 0-RTT was used by second connection. @@ -414,6 +425,38 @@ TEST_P(QuicHttpIntegrationTest, ZeroRtt) { } test_server_->waitForCounterEq("http3.quic_version_rfc_v1", 2u); + + // Start the third connection. + codec_client_ = makeRawHttp3Connection(makeClientConnection((lookupPort("http"))), absl::nullopt, + /*wait_for_1rtt_key*/ false); + auto response3 = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(0); + EXPECT_THAT(upstream_request_->headers(), HeaderValueOf(Http::Headers::get().EarlyData, "1")); + const Http::TestResponseHeaderMapImpl response_headers{{":status", "425"}}; + upstream_request_->encodeHeaders(response_headers, true); + ASSERT_TRUE(response3->waitForEndStream()); + // Without retry, 425 should be forwarded back to the client. + EXPECT_EQ("425", response3->headers().getStatusValue()); + codec_client_->close(); + + // Start the fourth connection. + codec_client_ = makeRawHttp3Connection(makeClientConnection((lookupPort("http"))), absl::nullopt, + /*wait_for_1rtt_key*/ false); + Http::TestRequestHeaderMapImpl request{{":method", "GET"}, + {":path", "/test/long/url"}, + {":scheme", "http"}, + {":authority", "host"}, + {"Early-Data", "2"}}; + auto response4 = codec_client_->makeHeaderOnlyRequest(request); + waitForNextUpstreamRequest(0); + // If the request already has Early-Data header, no additional Early-Data header should be added + // and the header should be forwarded as is. + EXPECT_THAT(upstream_request_->headers(), HeaderValueOf(Http::Headers::get().EarlyData, "2")); + upstream_request_->encodeHeaders(response_headers, true); + ASSERT_TRUE(response3->waitForEndStream()); + // 425 response should be forwarded back to the client. + EXPECT_EQ("425", response3->headers().getStatusValue()); + codec_client_->close(); } // Ensure multiple quic connections work, regardless of platform BPF support From b29e478f08b5d623932dae59237ed0d66eb89e12 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Mon, 8 Nov 2021 10:22:20 -0500 Subject: [PATCH 036/110] runtime: flipping envoy.reloadable_features.remove_legacy_json (#18861) We added the code to switch json libraries over in #14467 The removal is tracked by #4705 and smoke tested (#18451) Risk Level: Medium for folks using json Testing: n/a Docs Changes: n/a Release Notes: inline Fixes #18451 Signed-off-by: Alyssa Wilk --- docs/root/version_history/current.rst | 1 + source/common/runtime/runtime_features.cc | 3 +- test/common/json/BUILD | 2 +- test/common/json/json_fuzz_test.cc | 5 --- test/common/router/header_formatter_test.cc | 41 ++++++++++++--------- test/per_file_coverage.sh | 2 +- 6 files changed, 28 insertions(+), 26 deletions(-) diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 0ccdb71c4b8a5..271c72da8e64a 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -16,6 +16,7 @@ Minor Behavior Changes * config: the log message for "gRPC config stream closed" now uses the most recent error message, and reports seconds instead of milliseconds for how long the most recent status has been received. * dns: now respecting the returned DNS TTL for resolved hosts, rather than always relying on the hard-coded :ref:`dns_refresh_rate. ` This behavior can be temporarily reverted by setting the runtime guard ``envoy.reloadable_features.use_dns_ttl`` to false. * http: usage of the experimental matching API is no longer guarded behind a feature flag, as the corresponding protobuf fields have been marked as WIP. +* json: switching from rapidjson to nlohmann/json. This behavioral change can be temporarily reverted by setting runtime guard ``envoy.reloadable_features.remove_legacy_json`` to false. * listener: destroy per network filter chain stats when a network filter chain is removed during the listener in place update. * quic: add back the support for IETF draft 29 which is guarded via ``envoy.reloadable_features.FLAGS_quic_reloadable_flag_quic_disable_version_draft_29``. It is off by default so Envoy only supports RFCv1 without flipping this runtime guard explicitly. Draft 29 is not recommended for use. diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 5ffe167627d75..b62c35bfc2496 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -75,6 +75,7 @@ constexpr const char* runtime_features[] = { "envoy.reloadable_features.new_tcp_connection_pool", "envoy.reloadable_features.no_chunked_encoding_header_for_304", "envoy.reloadable_features.preserve_downstream_scheme", + "envoy.reloadable_features.remove_legacy_json", "envoy.reloadable_features.require_strict_1xx_and_204_response_headers", "envoy.reloadable_features.send_strict_1xx_and_204_response_headers", "envoy.reloadable_features.strip_port_from_connect", @@ -107,8 +108,6 @@ constexpr const char* runtime_features[] = { constexpr const char* disabled_runtime_features[] = { // TODO(alyssawilk, junr03) flip (and add release notes + docs) these after Lyft tests "envoy.reloadable_features.allow_multiple_dns_addresses", - // TODO(asraa) flip to true in a separate PR to enable the new JSON by default. - "envoy.reloadable_features.remove_legacy_json", // Sentinel and test flag. "envoy.reloadable_features.test_feature_false", // When the runtime is flipped to true, use shared cache in getOrCreateRawAsyncClient method if diff --git a/test/common/json/BUILD b/test/common/json/BUILD index b77f0b53d88a0..e68b7f30bad6b 100644 --- a/test/common/json/BUILD +++ b/test/common/json/BUILD @@ -17,7 +17,6 @@ envoy_cc_fuzz_test( "//source/common/protobuf", "//source/common/protobuf:utility_lib", "//test/fuzz:utility_lib", - "//test/test_common:test_runtime_lib", "//test/test_common:utility_lib", ], ) @@ -38,5 +37,6 @@ envoy_cc_test( envoy_cc_test( name = "json_loader_legacy_test", srcs = ["json_loader_test.cc"], + args = ["--runtime-feature-disable-for-tests=envoy.reloadable_features.remove_legacy_json"], deps = JSON_TEST_DEPS, ) diff --git a/test/common/json/json_fuzz_test.cc b/test/common/json/json_fuzz_test.cc index 7e2f40b89c6f5..925afbef64136 100644 --- a/test/common/json/json_fuzz_test.cc +++ b/test/common/json/json_fuzz_test.cc @@ -3,7 +3,6 @@ #include "test/fuzz/fuzz_runner.h" #include "test/fuzz/utility.h" -#include "test/test_common/test_runtime.h" #include "test/test_common/utility.h" namespace Envoy { @@ -13,10 +12,6 @@ namespace Fuzz { // We fuzz nlohmann/JSON and protobuf and compare their results, since RapidJSON is deprecated and // has known limitations. See https://github.com/envoyproxy/envoy/issues/4705. DEFINE_FUZZER(const uint8_t* buf, size_t len) { - TestScopedRuntime runtime; - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.remove_legacy_json", "true"}}); - std::string json_string{reinterpret_cast(buf), len}; // Load via Protobuf JSON parsing, if we can. diff --git a/test/common/router/header_formatter_test.cc b/test/common/router/header_formatter_test.cc index 02a466020d291..d1c262c284341 100644 --- a/test/common/router/header_formatter_test.cc +++ b/test/common/router/header_formatter_test.cc @@ -733,11 +733,13 @@ TEST_F(StreamInfoHeaderFormatterTest, UnknownVariable) { testInvalidFormat("INVA TEST_F(StreamInfoHeaderFormatterTest, WrongFormatOnUpstreamMetadataVariable) { // Invalid JSON. - EXPECT_THROW_WITH_MESSAGE( - StreamInfoHeaderFormatter("UPSTREAM_METADATA(abcd)", false), EnvoyException, - "Invalid header configuration. Expected format UPSTREAM_METADATA([\"namespace\", \"k\", " - "...]), actual format UPSTREAM_METADATA(abcd), because JSON supplied is not valid. " - "Error(offset 0, line 1): Invalid value.\n"); + EXPECT_THROW_WITH_MESSAGE(StreamInfoHeaderFormatter("UPSTREAM_METADATA(abcd)", false), + EnvoyException, + "Invalid header configuration. Expected format " + "UPSTREAM_METADATA([\"namespace\", \"k\", ...]), actual format " + "UPSTREAM_METADATA(abcd), because JSON supplied is not valid. " + "Error(line 1, column 1, token a): syntax error while parsing value - " + "invalid literal; last read: 'a'\n"); // No parameters. EXPECT_THROW_WITH_MESSAGE(StreamInfoHeaderFormatter("UPSTREAM_METADATA", false), EnvoyException, @@ -747,9 +749,10 @@ TEST_F(StreamInfoHeaderFormatterTest, WrongFormatOnUpstreamMetadataVariable) { EXPECT_THROW_WITH_MESSAGE( StreamInfoHeaderFormatter("UPSTREAM_METADATA()", false), EnvoyException, - "Invalid header configuration. Expected format UPSTREAM_METADATA([\"namespace\", \"k\", " - "...]), actual format UPSTREAM_METADATA(), because JSON supplied is not valid. Error(offset " - "0, line 1): The document is empty.\n"); + "Invalid header configuration. Expected format " + "UPSTREAM_METADATA([\"namespace\", \"k\", ...]), actual format UPSTREAM_METADATA(), " + "because JSON supplied is not valid. Error(line 1, column 1, token ): syntax error while " + "parsing value - unexpected end of input; expected '[', '{', or a literal\n"); // One parameter. EXPECT_THROW_WITH_MESSAGE(StreamInfoHeaderFormatter("UPSTREAM_METADATA([\"ns\"])", false), @@ -787,9 +790,10 @@ TEST_F(StreamInfoHeaderFormatterTest, WrongFormatOnUpstreamMetadataVariable) { // Invalid string elements. EXPECT_THROW_WITH_MESSAGE( StreamInfoHeaderFormatter("UPSTREAM_METADATA([\"a\", \"\\unothex\"])", false), EnvoyException, - "Invalid header configuration. Expected format UPSTREAM_METADATA([\"namespace\", \"k\", " - "...]), actual format UPSTREAM_METADATA([\"a\", \"\\unothex\"]), because JSON supplied is " - "not valid. Error(offset 7, line 1): Incorrect hex digit after \\u escape in string.\n"); + "Invalid header configuration. Expected format UPSTREAM_METADATA([\"namespace\", " + "\"k\", ...]), actual format UPSTREAM_METADATA([\"a\", \"\\unothex\"]), because JSON " + "supplied is not valid. Error(line 1, column 10, token \"\\un): syntax error while parsing " + "value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\un'\n"); // Non-array parameters. EXPECT_THROW_WITH_MESSAGE( @@ -868,14 +872,17 @@ TEST(HeaderParserTest, TestParseInternal) { // Parsing errors in variable expressions that take a JSON-array parameter. {"%UPSTREAM_METADATA(no array)%", {}, - {"Invalid header configuration. Expected format UPSTREAM_METADATA([\"namespace\", \"k\", " - "...]), actual format UPSTREAM_METADATA(no array), because JSON supplied is not valid. " - "Error(offset 1, line 1): Invalid value.\n"}}, + {"Invalid header configuration. Expected format " + "UPSTREAM_METADATA([\"namespace\", \"k\", ...]), actual format " + "UPSTREAM_METADATA(no array), because JSON supplied is not valid. Error(line 1, " + "column 2, token no): syntax error while parsing value - invalid literal; last read: " + "'no'\n"}}, {"%UPSTREAM_METADATA( no array)%", {}, - {"Invalid header configuration. Expected format UPSTREAM_METADATA([\"namespace\", \"k\", " - "...]), actual format UPSTREAM_METADATA( no array), because JSON supplied is not valid. " - "Error(offset 2, line 1): Invalid value.\n"}}, + {"Invalid header configuration. Expected format " + "UPSTREAM_METADATA([\"namespace\", \"k\", ...]), actual format UPSTREAM_METADATA( " + "no array), because JSON supplied is not valid. Error(line 1, column 3, token no): " + "syntax error while parsing value - invalid literal; last read: ' no'\n"}}, {"%UPSTREAM_METADATA([\"unterminated array\")%", {}, {"Invalid header configuration. Expecting ',', ']', or whitespace after " diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index ec4c43e4f43c5..184a5485da260 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -13,7 +13,7 @@ declare -a KNOWN_LOW_COVERAGE=( "source/common/filesystem/posix:95.5" "source/common/http:96.3" "source/common/http/http2:96.4" -"source/common/json:90.1" +"source/common/json:89.8" "source/common/matcher:94.0" "source/common/network:94.4" # Flaky, `activateFileEvents`, `startSecureTransport` and `ioctl`, listener_socket do not always report LCOV "source/common/network/dns_resolver:90.7" # A few lines of MacOS code not tested in linux scripts. Tested in MacOS scripts From bfecc0025bd4b195a2b240a24ecc3c7330c823ec Mon Sep 17 00:00:00 2001 From: phlax Date: Mon, 8 Nov 2021 16:41:44 +0000 Subject: [PATCH 037/110] dependabot: Python updates (#18929) * build(deps): bump protobuf in /examples/grpc-bridge/client Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 3.18.1 to 3.19.1. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/master/generate_changelog.py) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.18.1...v3.19.1) --- updated-dependencies: - dependency-name: protobuf dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: Ryan Northey * build(deps): bump packaging from 21.0 to 21.2 in /tools/dependency Bumps [packaging](https://github.com/pypa/packaging) from 21.0 to 21.2. - [Release notes](https://github.com/pypa/packaging/releases) - [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pypa/packaging/compare/21.0...21.2) --- updated-dependencies: - dependency-name: packaging dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: Ryan Northey * build(deps): bump grpcio-tools in /examples/grpc-bridge/client Bumps [grpcio-tools](https://github.com/grpc/grpc) from 1.41.0 to 1.41.1. - [Release notes](https://github.com/grpc/grpc/releases) - [Changelog](https://github.com/grpc/grpc/blob/master/doc/grpc_release_schedule.md) - [Commits](https://github.com/grpc/grpc/compare/v1.41.0...v1.41.1) --- updated-dependencies: - dependency-name: grpcio-tools dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: Ryan Northey * build(deps): bump setuptools from 58.5.2 to 58.5.3 in /tools/base Bumps [setuptools](https://github.com/pypa/setuptools) from 58.5.2 to 58.5.3. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/CHANGES.rst) - [Commits](https://github.com/pypa/setuptools/compare/v58.5.2...v58.5.3) --- updated-dependencies: - dependency-name: setuptools dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: Ryan Northey * build(deps): bump pyparsing from 2.4.7 to 3.0.4 in /tools/dependency Bumps [pyparsing](https://github.com/pyparsing/pyparsing) from 2.4.7 to 3.0.4. - [Release notes](https://github.com/pyparsing/pyparsing/releases) - [Changelog](https://github.com/pyparsing/pyparsing/blob/master/CHANGES) - [Commits](https://github.com/pyparsing/pyparsing/compare/pyparsing_2.4.7...pyparsing_3.0.4) --- updated-dependencies: - dependency-name: pyparsing dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: Ryan Northey Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/grpc-bridge/client/requirements.txt | 230 +++++++++---------- tools/base/requirements.txt | 6 +- tools/dependency/requirements.txt | 12 +- 3 files changed, 124 insertions(+), 124 deletions(-) diff --git a/examples/grpc-bridge/client/requirements.txt b/examples/grpc-bridge/client/requirements.txt index 9294fee3a139a..87771cf2fb87c 100644 --- a/examples/grpc-bridge/client/requirements.txt +++ b/examples/grpc-bridge/client/requirements.txt @@ -12,129 +12,129 @@ charset-normalizer==2.0.6 \ --hash=sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6 \ --hash=sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f # via requests -grpcio==1.41.0 \ - --hash=sha256:056806e83eaa09d0af0e452dd353db8f7c90aa2dedcce1112a2d21592550f6b1 \ - --hash=sha256:07594e585a5ba25cf331ddb63095ca51010c34e328a822cb772ffbd5daa62cb5 \ - --hash=sha256:0abd56d90dff3ed566807520de1385126dded21e62d3490a34c180a91f94c1f4 \ - --hash=sha256:15c04d695833c739dbb25c88eaf6abd9a461ec0dbd32f44bc8769335a495cf5a \ - --hash=sha256:1820845e7e6410240eff97742e9f76cd5bf10ca01d36a322e86c0bd5340ac25b \ - --hash=sha256:1bcbeac764bbae329bc2cc9e95d0f4d3b0fb456b92cf12e7e06e3e860a4b31cf \ - --hash=sha256:2410000eb57cf76b05b37d2aee270b686f0a7876710850a2bba92b4ed133e026 \ - --hash=sha256:2882b62f74de8c8a4f7b2be066f6230ecc46f4edc8f42db1fb7358200abe3b25 \ - --hash=sha256:297ee755d3c6cd7e7d3770f298f4d4d4b000665943ae6d2888f7407418a9a510 \ - --hash=sha256:39ce785f0cbd07966a9019386b7a054615b2da63da3c7727f371304d000a1890 \ - --hash=sha256:3a92e4df5330cd384984e04804104ae34f521345917813aa86fc0930101a3697 \ - --hash=sha256:3bbeee115b05b22f6a9fa9bc78f9ab8d9d6bb8c16fdfc60401fc8658beae1099 \ - --hash=sha256:4537bb9e35af62c5189493792a8c34d127275a6d175c8ad48b6314cacba4021e \ - --hash=sha256:462178987f0e5c60d6d1b79e4e95803a4cd789db961d6b3f087245906bb5ae04 \ - --hash=sha256:5292a627b44b6d3065de4a364ead23bab3c9d7a7c05416a9de0c0624d0fe03f4 \ - --hash=sha256:5502832b7cec670a880764f51a335a19b10ff5ab2e940e1ded67f39b88aa02b1 \ - --hash=sha256:585847ed190ea9cb4d632eb0ebf58f1d299bbca5e03284bc3d0fa08bab6ea365 \ - --hash=sha256:59645b2d9f19b5ff30cb46ddbcaa09c398f9cd81e4e476b21c7c55ae1e942807 \ - --hash=sha256:5d4b30d068b022e412adcf9b14c0d9bcbc872e9745b91467edc0a4c700a8bba6 \ - --hash=sha256:7033199706526e7ee06a362e38476dfdf2ddbad625c19b67ed30411d1bb25a18 \ - --hash=sha256:7b07cbbd4eea56738e995fcbba3b60e41fd9aa9dac937fb7985c5dcbc7626260 \ - --hash=sha256:7da3f6f6b857399c9ad85bcbffc83189e547a0a1a777ab68f5385154f8bc1ed4 \ - --hash=sha256:83c1e731c2b76f26689ad88534cafefe105dcf385567bead08f5857cb308246b \ - --hash=sha256:9674a9d3f23702e35a89e22504f41b467893cf704f627cc9cdd118cf1dcc8e26 \ - --hash=sha256:9ecd0fc34aa46eeac24f4d20e67bafaf72ca914f99690bf2898674905eaddaf9 \ - --hash=sha256:a0c4bdd1d646365d10ba1468bcf234ea5ad46e8ce2b115983e8563248614910a \ - --hash=sha256:a144f6cecbb61aace12e5920840338a3d246123a41d795e316e2792e9775ad15 \ - --hash=sha256:a3cd7f945d3e3b82ebd2a4c9862eb9891a5ac87f84a7db336acbeafd86e6c402 \ - --hash=sha256:a614224719579044bd7950554d3b4c1793bb5715cbf0f0399b1f21d283c40ef6 \ - --hash=sha256:ace080a9c3c673c42adfd2116875a63fec9613797be01a6105acf7721ed0c693 \ - --hash=sha256:b2de4e7b5a930be04a4d05c9f5fce7e9191217ccdc174b026c2a7928770dca9f \ - --hash=sha256:b6b68c444abbaf4a2b944a61cf35726ab9645f45d416bcc7cf4addc4b2f2d53d \ - --hash=sha256:be3c6ac822edb509aeef41361ca9c8c5ee52cb9e4973e1977d2bb7d6a460fd97 \ - --hash=sha256:c07acd49541f5f6f9984fe0adf162d77bf70e0f58e77f9960c6f571314ff63a4 \ - --hash=sha256:c1e0a4c86d4cbd93059d5eeceed6e1c2e3e1494e1bf40be9b8ab14302c576162 \ - --hash=sha256:c8c5bc498f6506b6041c30afb7a55c57a9fd535d1a0ac7cdba9b5fd791a85633 \ - --hash=sha256:c95dd6e60e059ff770a2ac9f5a202b75dd64d76b0cd0c48f27d58907e43ed6a6 \ - --hash=sha256:ccd2f1cf11768d1f6fbe4e13e8b8fb0ccfe9914ceeff55a367d5571e82eeb543 \ - --hash=sha256:d0cc0393744ce3ce1b237ae773635cc928470ff46fb0d3f677e337a38e5ed4f6 \ - --hash=sha256:d539ebd05a2bbfbf897d41738d37d162d5c3d9f2b1f8ddf2c4f75e2c9cf59907 \ - --hash=sha256:d71aa430b2ac40e18e388504ac34cc91d49d811855ca507c463a21059bf364f0 \ - --hash=sha256:dcb5f324712a104aca4a459e524e535f205f36deb8005feb4f9d3ff0a22b5177 \ - --hash=sha256:e516124010ef60d5fc2e0de0f1f987599249dc55fd529001f17f776a4145767f \ - --hash=sha256:fb64abf0d92134cb0ba4496a3b7ab918588eee42de20e5b3507fe6ee16db97ee +grpcio==1.41.1 \ + --hash=sha256:0aa1af3e1480b6dd3092ee67c4b67b1ea88d638fcdc4d1a611ae11e311800b34 \ + --hash=sha256:0c075616d5e86fb65fd4784d5a87d6e5a1882d277dce5c33d9b67cfc71d79899 \ + --hash=sha256:133fb9a3cf4519543e4e41eb18b5dac0da26941aeabca8122dbcf3decbad2d21 \ + --hash=sha256:23a3f03e1d9ac429ff78d23d2ab07756d3728ee1a68b5f244d8435006608b6aa \ + --hash=sha256:2a34c8979de10b04a44d2cad07d41d83643e65e49f84a05b1adf150aeb41c95f \ + --hash=sha256:2eb8180a6d9e47fc865a4e92a2678f3202145021ef2c1bccf165fa5744f6ec95 \ + --hash=sha256:2f2ee78a6ae88d668ceda56fa4a18d8a38b34c2f2e1332083dd1da1a92870703 \ + --hash=sha256:31a47af7356fb5ed3120636dd75c5efb571ecf15737484119e31286687f0e52a \ + --hash=sha256:3213dfe3abfc3fda7f30e86aa5967dce0c2eb4cc90a0504f95434091bf6b219a \ + --hash=sha256:32b7ca83f1a6929217098aaaac89fc49879ae714c95501d40df41a0e7506164c \ + --hash=sha256:3713e3918da6ae10812a64e75620a172f01af2ff0a1c99d6481c910e1d4a9053 \ + --hash=sha256:3b4b7c1ab18283eb64af5648d20eabef9237a2aec09e30a805f18adc9497258d \ + --hash=sha256:3f0b70cf8632028714a8341b841b011a47900b1c163bf5fababb4ab3888c9b6c \ + --hash=sha256:61aa02f4505c5bbbaeba80fef1bd6871d1aef05a8778a707ab91303ee0865ad0 \ + --hash=sha256:6ca497ccecaa8727f14c4ccc9ffb70a19c6413fe1d4650500c90a7febd662860 \ + --hash=sha256:71d9ed5a732a54b9c87764609f2fd2bc4ae72fa85e271038eb132ea723222209 \ + --hash=sha256:72d0bdc3605dc8f4187b302e1180643963896e3f2917a52becb51afb54448e3e \ + --hash=sha256:734690b3f35468f8ed4003ec7622d2d47567f1881f5fcdca34f1e52551c2ef55 \ + --hash=sha256:740f5b21a7108a8c08bf522434752dc1d306274d47ca8b4d51af5588a16b6113 \ + --hash=sha256:766f1b943abc3e27842b72fba6e28fb9f57c9b84029fd7e91146e4c37034d937 \ + --hash=sha256:788154b32bf712e9711d001df024af5f7b2522117876c129bb27b9ad6e5461fb \ + --hash=sha256:7a22a7378ea59ad1e6f2e79f9da6862eb9e1f6586253aee784d419a49e3f4bd9 \ + --hash=sha256:8487fb0649ebebc9c5dca1a6dc4eb7fddf701183426b3eefeb3584639d223d43 \ + --hash=sha256:8824b36e6b0e45fefe0b4eac5ad460830e0cbc856a0c794f711289b4b8933d53 \ + --hash=sha256:888d8519709652dd39415de5f79abd50257201b345dd4f40151feffc3dad3232 \ + --hash=sha256:9170b5d2082fc00c057c6ccd6b893033c1ade05717fcec1d63557c3bc7afdb1b \ + --hash=sha256:9b751271b029432a526a4970dc9b70d93eb6f0963b6a841b574f780b72651969 \ + --hash=sha256:9d1be99f216b18f8a9dbdfbdbcc9a6caee504d0d27295fdbb5c8da35f5254a69 \ + --hash=sha256:9e403d07d77ed4495ad3c18994191525b11274693e72e464241c9139e2f9cd7c \ + --hash=sha256:a3bb4302389b23f2006ecaaea5eb4a39cc80ea98d1964159e59c1c20ef39a483 \ + --hash=sha256:a5ac91db3c588296366554b2d91116fc3a9f05bae516cafae07220e1f05bfef7 \ + --hash=sha256:b1232c5efc8a9e4b7a13db235c51135412beb9e62e618a2a89dd0463edb3d929 \ + --hash=sha256:b8dd1b6456c6fb3681affe0f81dff4b3bc46f825fc05e086d64216545da9ad92 \ + --hash=sha256:c32c470e077b34a52e87e7de26644ad0f9e9ff89a785ff7e6466870869659e05 \ + --hash=sha256:c35b847bc6bd3c3a118a13277d91a772e7dd163ce7dd2791239f9941b6eaafe3 \ + --hash=sha256:c3a446b6a1f8077cc03d0d496fc1cecdd3d0b66860c0c5b65cc92d0549117840 \ + --hash=sha256:d1461672b2eaef9affb60a71014ebd2f789deea7c9acb1d4bd163de92dd8e044 \ + --hash=sha256:e156ea12adb7a7ca8d8280c9df850c15510b790c785fc26c9a3fb928cd221fd4 \ + --hash=sha256:ead9885b53777bed4b0694ff0baea9d2c519ff774b17b177bde43d73e2b4aa38 \ + --hash=sha256:ebbe9582ef06559a2358827a588ab4b92a2639517de8fe428288772820ab03b5 \ + --hash=sha256:f68aa98f5970eccb6c94456f3447a99916c42fbddae1971256bc4e7c40a6593b \ + --hash=sha256:fc2eadfb5ec956c556c138fab0dfc1d2395c57ae0bfea047edae1976a26b250c \ + --hash=sha256:fd11995e3402af0f838844194707da8b3235f1719bcac961493f0138f1325893 \ + --hash=sha256:fd570720871dc84d2adc8430ce287319c9238d1e2f70c140f9bc54c690fabd1b # via # -r requirements.in # grpcio-tools -grpcio-tools==1.41.0 \ - --hash=sha256:022ea466300fd8eee03375795c764b8d01aee7ba614c1d7ba198eef9eaebc07a \ - --hash=sha256:05730f1acd3fa70e63a62fe37377297774db7f4794fb6ae3e43f64aa354460f8 \ - --hash=sha256:08654c9f723fa644be52cc8f975c01bb93a99808ab02c2e64a20e9c9e92c9a3b \ - --hash=sha256:0d6489ed1310250f152d6170ee539e84bfc364bbfdffbbe98e8ce9297c4a1550 \ - --hash=sha256:17a759203f627b941086a65a0c3f39c5da41f11d11dc8ca5883e844c055876dd \ - --hash=sha256:2d48309bbbb2d7144117748718ca52eb60f10dd86a0cb8a0a5f952ee08575bee \ - --hash=sha256:3891b1df82369acbc8451d4952cd20755f49a82398dce62437511ad17b47290e \ - --hash=sha256:3c7f6c8559ac6bea6029b8c5d188d24509d30a28816de02c723659f56e862b98 \ - --hash=sha256:3f6c2bff12e2015bd69c600710fb427720446034ed9a237cd6edf7e2452cf826 \ - --hash=sha256:3f860f8a804f6ef6ea545483c1506d184f9bba40f635c6886d79791822c679e3 \ - --hash=sha256:4b48c13dbbf96d36a41e45fd011eeabc1541ec8705f2d533fa4c20634f750885 \ - --hash=sha256:50a9f66502e4868c20bc0b8c1c7d3b21e6b6b2578a7aef6ce7c28294b9eba911 \ - --hash=sha256:51bdc4bd088592d5f52b5cb6d3be072bf0d847a7af92e544f9885acdf5de1252 \ - --hash=sha256:55915c61baae316b607be6ff5be72614efc067e50dfffd389bde95c240a5416e \ - --hash=sha256:57f35fd71366f1eecd4c08b9d8eda1007d371827f092ae916b4235744e9175a6 \ - --hash=sha256:5b1edfcfa4f21c210bfe66534af9fa5ca37374bb0e0d1754018e0d92c8fe4c8e \ - --hash=sha256:5d15f5dd0c01f914ab15e921484b71aff0eff8aa123b22d76e71c76be8d81efc \ - --hash=sha256:5f52f7d8841372a047493ee9722810856a4adfa38330b4a688a1421dd3460518 \ - --hash=sha256:5f85be3053486cc53b41fe888957f61e98d6aab74b0726a54cf35e4a685f2b96 \ - --hash=sha256:602b7dd5e52924794f19f637ec042bc141b7d9dd127ddc662b28c42f8db08e95 \ - --hash=sha256:609f6e4cad800f0b2caa0b46baefbb30444bddfc94d1429b9add02d5e6759001 \ - --hash=sha256:6622feec0a3f326fb86cf01bf1bcbfec23548ae4d80706d88b296d792d816f0e \ - --hash=sha256:7145e9243718bd8a4792547efb1443846cebb3d36d49dca52d5f9edfb81aa256 \ - --hash=sha256:7242b39d16970319b11c13832f3474d09be53cbc88bc05c54140f5394a247184 \ - --hash=sha256:731c78b612ca672af0f4682e68d331d304a3eccd1836f0b89402c332aa653815 \ - --hash=sha256:7f3bf213d7b182628bdfb10854cc7b19d4882e1916786fc3a14f724555a7e824 \ - --hash=sha256:85b4cd4a77c27df984dce5b14eafa29c54abd134335230b59fa8d096c995b877 \ - --hash=sha256:898b032ddcd25a051c6c8892b76779b8821e073fc363e6105dc08efd95857bcd \ - --hash=sha256:8cf6ab58c14b7bd4cf5b4d652e2bfafc6543d38210d68332ccccff4733bcc615 \ - --hash=sha256:8f7cd5b8eeae570743cfd0ece36f62b32424b995ee3862697cfe94bc9c4fa5fe \ - --hash=sha256:98d9e581bc9ad154697af40c0109221926628d57fab2a52a1fa2cfed401349d5 \ - --hash=sha256:9ff9fdef6df6b3d1e4395158f4bd2bfab58867370bd4b4ed81a1a2ab20de085b \ - --hash=sha256:a111af9732c1ac85b35b894c4b6150127c52349ca220c0708d241d4bb8ee4622 \ - --hash=sha256:a1e2db4c90cb07d6b8f1526346df65da85dce995e7aa7c4db76bcc2a99dcbf43 \ - --hash=sha256:a4e08366f780b439499645fbb0b7788cccd978c06158b19e915726bfbe420031 \ - --hash=sha256:b78a3225302b60e59a922d909413b2c0de2ba19f4dc79273411dfad560e21418 \ - --hash=sha256:b8e9181327b94886f6214cfe2147721c6b60138c111d78313b9070f4068020b5 \ - --hash=sha256:c13b6a37fe3619be603265a14a614f86fa97a95934e6447de2bc9e66f9a35590 \ - --hash=sha256:c93137598d5f2b4d163aff571197be92d3c691a5d82dabb29b1ef467e3c29db6 \ - --hash=sha256:ceefaa88c066c9c779f15e8d58d57d3763efef3d0dbec483be99bc75ae0e2d70 \ - --hash=sha256:db64aa08ae500cb20c9f377e41a66e493c4cba27ab99710852340ef81c7d0e30 \ - --hash=sha256:dc65beee944735d4cb42c8c43e284ff711512d1f7a029bdbaeb0729243f3a702 \ - --hash=sha256:e1814b98a955aad08107eb4c4f068b1cd147cc923a2480bc2fae51007bb7866b \ - --hash=sha256:f4c03f312877e57b47beda2e9db5a39bc3af65ee22b38e85b4c0f94b3b9c26af +grpcio-tools==1.41.1 \ + --hash=sha256:082ec573f9f1303b6adb7700df6c381871bba890d1a6e3238d12496181b88188 \ + --hash=sha256:0b79dcba5bdc17cb9e98aac8d1ecff301cf8cb3ff793f5cb637d27d7cde12ed6 \ + --hash=sha256:0e8934968475b895b42439247d2a95683756c452dff95c57812af9c78065b061 \ + --hash=sha256:0ef02428d0bd87c333e02ed5c43f15ecb5625ac93f14280d63359e8abb8f08a8 \ + --hash=sha256:1135a65aa2c5347a3ec3614e6831adf94215eaa22cb92e828bd22e7652bd6fbb \ + --hash=sha256:19dfe6eab4924a934425993d2fe33cad60e3bc55a0e79b9ce4fd2e3e4399911d \ + --hash=sha256:1a4a58df69bcf9bf2792bc49aa7d74b24d044a56d09e529e6cf393095decd48c \ + --hash=sha256:1b9042ed70bdecfcdf1f2ff9b7e53bbf6404d47f90747954e3c18b506eb2f31c \ + --hash=sha256:1cbe254c7e9cf9a4b5d3be13384aafa69cb78c98d3b4bfead21cbea1c1b4f131 \ + --hash=sha256:1ccd0c5af4560e39cdf32c106cb9065fb5ab1a839c0f411ce342bb462cf74f65 \ + --hash=sha256:1db24ac6b50ef747f650c40c2d7049a303fd57872b1c43e43582f989f9140eee \ + --hash=sha256:2149bb2c936bc83b24e0ed4e74bf6660d6e91f85492c65044956b25a2e1d8609 \ + --hash=sha256:252cea08d5a9d8947e92e0a783e0a41690af8f7913f037e9af4b4fde16d567be \ + --hash=sha256:2c6b36881c4b6d4199604fb42253d2e1722d8e5c0e504d9e721e0a51ac5c5cbd \ + --hash=sha256:32ffdab1fdce423a3db489112a24a1ade5bab3d22f337dc63e9a0441e1dab1d7 \ + --hash=sha256:36168c5a00bc020f4030758e60d4a3bc2418279b0425025eebb8d9fe0310bb61 \ + --hash=sha256:39070f66cd789bdd6743e10813d4360cb13390ab21161bfa878d1679a2b8fd1e \ + --hash=sha256:4a7dd864f2ccb840fe6fd07f0e81a94e17a5510b86ab8edb8506088552e3cd67 \ + --hash=sha256:4b9dff509b80319123fd5e3de3dfcdb862ca91b7a180639ba476a1594c363a69 \ + --hash=sha256:4cbe0589e462dd69139c9f3752c76b3466ae243eae81959c77965efc1fd925c8 \ + --hash=sha256:550360617fbf2deb41e95d64b644fcb373ccaa7daed51b273ef9a10c6a5a2bea \ + --hash=sha256:5c7961c2c1f47faabd1be79326fa604ba7acbf2387105b0d514a2ccd16179302 \ + --hash=sha256:5d32f0371cb8d372c937ba0003a125aedbc702081de4dc8dac4984e6d2c0f6b6 \ + --hash=sha256:6dce70d332c6b9c95d60ce5049421602073914ee5770c559edce1ce9550400ad \ + --hash=sha256:780db6b0cfdc0b02965eba126f1a5edaff50bcc0f7e96bb0e3f93658fe1a0b66 \ + --hash=sha256:794dd3f2a4af440e11b551bf14516f961490a4a35bca1a0ea9f1d3b0275cff1a \ + --hash=sha256:846d558f06405eeac403182c5f2bb800526268e3eedc9a9848bb9ea898a30ae4 \ + --hash=sha256:8bee8a1004389e70596407fd616f28cba4e8354a0e002011a7fc5e91d29572a0 \ + --hash=sha256:91250fb70b57018796a22d9a27ea3819ef8573a3c6a960271cead310de606c57 \ + --hash=sha256:a1f097c62ba60f0dd9e047bef919a0f24f79829d23b9116dbc6c5e3aa4dc055e \ + --hash=sha256:a549fbba1e4aa1cb13e45025ea5dc15cbc89e50d94b3a558462f1c4378102444 \ + --hash=sha256:bce9cb7159897974cbac8c48146d04a4b13d1e38e5c9f61745b8b6404f8c551e \ + --hash=sha256:c13eb6c2a39079815ea1adad5a6bfeef5740955ff3eb765ba4aac1022010b426 \ + --hash=sha256:c664ffa4acc74c5d2a7d548a86167a9fd81b70d8dd4b0b9edd1e994889e44df0 \ + --hash=sha256:c6c1014acb907dba2faefc7ad0d79a2ac7b6ecebb4c6f327928e8d98ac44b544 \ + --hash=sha256:cdcb9ca63423c1d88485d1870e27c4393dea9563604f3b1c30f6cb7aa8c60258 \ + --hash=sha256:cebf44af95c6218e42ba9677dcf9be27d68904ee4c74881a787dd04fbeef3e48 \ + --hash=sha256:d8aad0c3a4a9b16b4d7be989938a05a96495296e0aedae50e3f3e9131517c08c \ + --hash=sha256:eb6748aed8cf77280591570b71d8694360690b467937fbbdbedba788903b7301 \ + --hash=sha256:f16e4c63996ca8fe0af1eb9c4a07e5207874c4a69f890ccb824cd858521d981f \ + --hash=sha256:f418c6dd993de1d7a4d279745f3593f1dd5ceab6d733e259b87c7e6beeb951de \ + --hash=sha256:f59929960221a52f54ec1837b90365b2e7b6227443dc250ce620a7e8e4b46ef0 \ + --hash=sha256:f6bec85639fc8d23470fb224368dd0cf0fce3ae250cf392edba6d79d68e1c4ba \ + --hash=sha256:fd2e2d99af0ae333707417c2a0b7ef90dd37486071a59ff7854d8ff23d5bac45 # via -r requirements.in idna==3.2 \ --hash=sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a \ --hash=sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3 # via requests -protobuf==3.19.0 \ - --hash=sha256:01a0645ef3acddfbc90237e1cdfae1086130fc7cb480b5874656193afd657083 \ - --hash=sha256:2f6046b9e2feee0dce994493186e8715b4392ed5f50f356280ad9c2f9f93080a \ - --hash=sha256:34a77b8fafdeb8f89fee2b7108ae60d8958d72e33478680cc1e05517892ecc46 \ - --hash=sha256:36bf292f44966c67080e535321501717f4f1eba30faef8f2cd4b0c745a027211 \ - --hash=sha256:3fea09aa04ef2f8b01fcc9bb87f19509934f8a35d177c865b8f9ee5c32b60c1b \ - --hash=sha256:4f93e0f6af796ddd1502225ff8ea25340ced186ca05b601c44d5c88b45ba80a0 \ - --hash=sha256:6a1dc6584d24ef86f5b104bcad64fa0fe06ed36e5687f426e0445d363a041d18 \ - --hash=sha256:6f16925f5c977dd7787973a50c242e60c22b1d1182aba6bec7bd02862579c10f \ - --hash=sha256:708d04394a63ee9bdc797938b6e15ed5bf24a1cb37743eb3886fd74a5a67a234 \ - --hash=sha256:7b3867795708ac88fde8d6f34f0d9a50af56087e41f624bdb2e9ff808ea5dda7 \ - --hash=sha256:8488c2276f14f294e890cc1260ab342a13e90cd20dcc03319d2eea258f1fd321 \ - --hash=sha256:942dd6bc8bd2a3c6a156d8ab0f80bd45313f22b78e1176283270054dcc8ca4c2 \ - --hash=sha256:9a8a880593015ef2c83f7af797fa4fbf583b2c98b4bd94e46c5b61fee319d84b \ - --hash=sha256:a74432e9d28a6072a2359a0f49f81eb14dd718e7dbbfb6c0789b456c49e1f130 \ - --hash=sha256:ac2f8ec942d414609aba0331952ae12bb823e8f424bbb6b8c422f1cef32dc842 \ - --hash=sha256:b64be5d7270cf5e76375bac049846e8a9543a2d4368b69afe78ab725380a7487 \ - --hash=sha256:c96e94d3e523a82caa3e5f74b35dd1c4884199358d01c950d95c341255ff48bc \ - --hash=sha256:c99af73ae34c93e0e2ace57ea2e70243f34fc015c8c23fd39ee93652e726f7e7 \ - --hash=sha256:d1f4277d321f60456845ca9b882c4845736f1f5c1c69eb778eba22a97977d8af \ - --hash=sha256:d3861c9721a90ba83ee0936a9cfcc4fa1c4b4144ac9658fb6f6343b38558e9b4 \ - --hash=sha256:d4ca5f0c7bc8d2e6966ca3bbd85e9ebe7191b6e21f067896d4af6b28ecff29fe \ - --hash=sha256:ee4d07d596357f51316b6ecf1cc1927660e9d5e418385bb1c51fd2496cd9bee7 \ - --hash=sha256:f7a031cf8e2fc14acc0ba694f6dff0a01e06b70d817eba6edc72ee6cc20517ac \ - --hash=sha256:f9097327d277b0aa4a3224e61cd6850aef3269172397715299bcffc9f90293c9 +protobuf==3.19.1 \ + --hash=sha256:038daf4fa38a7e818dd61f51f22588d61755160a98db087a046f80d66b855942 \ + --hash=sha256:28ccea56d4dc38d35cd70c43c2da2f40ac0be0a355ef882242e8586c6d66666f \ + --hash=sha256:36d90676d6f426718463fe382ec6274909337ca6319d375eebd2044e6c6ac560 \ + --hash=sha256:3cd0458870ea7d1c58e948ac8078f6ba8a7ecc44a57e03032ed066c5bb318089 \ + --hash=sha256:5935c8ce02e3d89c7900140a8a42b35bc037ec07a6aeb61cc108be8d3c9438a6 \ + --hash=sha256:615b426a177780ce381ecd212edc1e0f70db8557ed72560b82096bd36b01bc04 \ + --hash=sha256:62a8e4baa9cb9e064eb62d1002eca820857ab2138440cb4b3ea4243830f94ca7 \ + --hash=sha256:655264ed0d0efe47a523e2255fc1106a22f6faab7cc46cfe99b5bae085c2a13e \ + --hash=sha256:6e8ea9173403219239cdfd8d946ed101f2ab6ecc025b0fda0c6c713c35c9981d \ + --hash=sha256:71b0250b0cfb738442d60cab68abc166de43411f2a4f791d31378590bfb71bd7 \ + --hash=sha256:74f33edeb4f3b7ed13d567881da8e5a92a72b36495d57d696c2ea1ae0cfee80c \ + --hash=sha256:77d2fadcf369b3f22859ab25bd12bb8e98fb11e05d9ff9b7cd45b711c719c002 \ + --hash=sha256:8b30a7de128c46b5ecb343917d9fa737612a6e8280f440874e5cc2ba0d79b8f6 \ + --hash=sha256:8e51561d72efd5bd5c91490af1f13e32bcba8dab4643761eb7de3ce18e64a853 \ + --hash=sha256:a529e7df52204565bcd33738a7a5f288f3d2d37d86caa5d78c458fa5fabbd54d \ + --hash=sha256:b691d996c6d0984947c4cf8b7ae2fe372d99b32821d0584f0b90277aa36982d3 \ + --hash=sha256:d80f80eb175bf5f1169139c2e0c5ada98b1c098e2b3c3736667f28cbbea39fc8 \ + --hash=sha256:d83e1ef8cb74009bebee3e61cc84b1c9cd04935b72bca0cbc83217d140424995 \ + --hash=sha256:d8919368410110633717c406ab5c97e8df5ce93020cfcf3012834f28b1fab1ea \ + --hash=sha256:db3532d9f7a6ebbe2392041350437953b6d7a792de10e629c1e4f5a6b1fe1ac6 \ + --hash=sha256:e7b24c11df36ee8e0c085e5b0dc560289e4b58804746fb487287dda51410f1e2 \ + --hash=sha256:e7e8d2c20921f8da0dea277dfefc6abac05903ceac8e72839b2da519db69206b \ + --hash=sha256:e813b1c9006b6399308e917ac5d298f345d95bb31f46f02b60cd92970a9afa17 \ + --hash=sha256:fd390367fc211cc0ffcf3a9e149dfeca78fecc62adb911371db0cec5c8b7472d # via # -r requirements.in # grpcio-tools diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index ccbf5a8d684f1..c26dedabb09fd 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -798,9 +798,9 @@ yarl==1.6.3 \ # via aiohttp # The following packages are considered to be unsafe in a requirements file: -setuptools==58.5.2 \ - --hash=sha256:132856b317080c4688b664e40164ad27fa210636258ee7c25cf2eb49b982ee51 \ - --hash=sha256:3a99bc3975c1eb3cca138d3f178aedaca87f75e9372e40cd361056b82d630659 +setuptools==58.5.3 \ + --hash=sha256:a481fbc56b33f5d8f6b33dce41482e64c68b668be44ff42922903b03872590bf \ + --hash=sha256:dae6b934a965c8a59d6d230d3867ec408bb95e73bd538ff77e71fedf1eaca729 # via # -r requirements.in # sphinx diff --git a/tools/dependency/requirements.txt b/tools/dependency/requirements.txt index e1505daa443be..59c01a6605ae1 100644 --- a/tools/dependency/requirements.txt +++ b/tools/dependency/requirements.txt @@ -76,9 +76,9 @@ idna==3.3 \ --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d # via requests -packaging==21.0 \ - --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 \ - --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14 +packaging==21.2 \ + --hash=sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0 \ + --hash=sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966 # via -r requirements.in pycparser==2.20 \ --hash=sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0 \ @@ -112,9 +112,9 @@ pynacl==1.4.0 \ --hash=sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff \ --hash=sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80 # via pygithub -pyparsing==3.0.1 \ - --hash=sha256:fd93fc45c47893c300bd98f5dd1b41c0e783eaeb727e7cea210dcc09d64ce7c3 \ - --hash=sha256:84196357aa3566d64ad123d7a3c67b0e597a115c4934b097580e5ce220b91531 +pyparsing==3.0.4 \ + --hash=sha256:c0a7dfcd26825bd4453574c4e7ad04aa095975ce54d04f738fe3a8350fbd223a \ + --hash=sha256:e0df773d7fa2240322daae7805626dfc5f2d5effb34e1a7be2702c99cfb9f6b1 # via packaging pytz==2021.3 \ --hash=sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c \ From 41d7be65863ef4f83cbd35eded2ff2fa798006ca Mon Sep 17 00:00:00 2001 From: RenjieTang Date: Mon, 8 Nov 2021 09:37:26 -0800 Subject: [PATCH 038/110] quic: allow port migration to config the number of timeouts needed to trigger path degrading (#18801) Signed-off-by: Renjie Tang --- api/envoy/config/core/v3/protocol.proto | 7 ++----- source/common/quic/envoy_quic_client_connection.cc | 8 ++++++++ source/common/quic/envoy_quic_client_connection.h | 4 +--- source/common/quic/envoy_quic_client_session.cc | 2 +- test/integration/quic_http_integration_test.cc | 7 +++++++ 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/api/envoy/config/core/v3/protocol.proto b/api/envoy/config/core/v3/protocol.proto index 49ee04c3aed82..89f488130969a 100644 --- a/api/envoy/config/core/v3/protocol.proto +++ b/api/envoy/config/core/v3/protocol.proto @@ -83,15 +83,12 @@ message QuicProtocolOptions { google.protobuf.UInt32Value initial_connection_window_size = 3 [(validate.rules).uint32 = {lte: 25165824 gte: 1}]; - // [#not-implemented-hide:] Hiding until timeout config is supported. // The number of timeouts that can occur before port migration is triggered for QUIC clients. - // This defaults to 1. If sets to 0, port migration will not occur. + // This defaults to 1. If set to 0, port migration will not occur on path degrading. // Timeout here refers to QUIC internal path degrading timeout mechanism, such as PTO. // This has no effect on server sessions. - // Currently the value can only be 0 or 1. - // TODO(renjietang): Plumb through quiche to make this config able to adjust the amount of timeouts needed to trigger port migration. google.protobuf.UInt32Value num_timeouts_to_trigger_port_migration = 4 - [(validate.rules).uint32 = {lte: 1 gte: 0}]; + [(validate.rules).uint32 = {lte: 5 gte: 0}]; // Probes the peer at the configured interval to solicit traffic, i.e. ACK or PATH_RESPONSE, from the peer to push back connection idle timeout. // If absent, use the default keepalive behavior of which a client connection sends PINGs every 15s, and a server connection doesn't do anything. diff --git a/source/common/quic/envoy_quic_client_connection.cc b/source/common/quic/envoy_quic_client_connection.cc index 0a50d81b11bfd..31670c62606e3 100644 --- a/source/common/quic/envoy_quic_client_connection.cc +++ b/source/common/quic/envoy_quic_client_connection.cc @@ -217,6 +217,14 @@ void EnvoyQuicClientConnection::onFileEvent(uint32_t events, } } +void EnvoyQuicClientConnection::setNumPtosForPortMigration(uint32_t num_ptos_for_path_degrading) { + if (num_ptos_for_path_degrading < 1) { + return; + } + migrate_port_on_path_degrading_ = true; + sent_packet_manager().set_num_ptos_for_path_degrading(num_ptos_for_path_degrading); +} + EnvoyQuicClientConnection::EnvoyQuicPathValidationContext::EnvoyQuicPathValidationContext( quic::QuicSocketAddress& self_address, quic::QuicSocketAddress& peer_address, std::unique_ptr writer, diff --git a/source/common/quic/envoy_quic_client_connection.h b/source/common/quic/envoy_quic_client_connection.h index f24a67ce067cd..fc11311619b85 100644 --- a/source/common/quic/envoy_quic_client_connection.h +++ b/source/common/quic/envoy_quic_client_connection.h @@ -76,9 +76,7 @@ class EnvoyQuicClientConnection : public quic::QuicConnection, // and be destructed. void onPathValidationFailure(std::unique_ptr context); - void setMigratePortOnPathDegrading(bool migrate_port_on_path_degrading) { - migrate_port_on_path_degrading_ = migrate_port_on_path_degrading; - } + void setNumPtosForPortMigration(uint32_t num_ptos_for_path_degrading); private: // Holds all components needed for a QUIC connection probing/migration. diff --git a/source/common/quic/envoy_quic_client_session.cc b/source/common/quic/envoy_quic_client_session.cc index a1c5372a5654d..0982f71c4bc49 100644 --- a/source/common/quic/envoy_quic_client_session.cc +++ b/source/common/quic/envoy_quic_client_session.cc @@ -152,7 +152,7 @@ void EnvoyQuicClientSession::setHttp3Options( return; } static_cast(connection()) - ->setMigratePortOnPathDegrading(PROTOBUF_GET_WRAPPED_OR_DEFAULT( + ->setNumPtosForPortMigration(PROTOBUF_GET_WRAPPED_OR_DEFAULT( http3_options.quic_protocol_options(), num_timeouts_to_trigger_port_migration, 1)); if (http3_options_->quic_protocol_options().has_connection_keepalive()) { diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index 472ec639e272a..2d64721013305 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -23,6 +23,7 @@ #include "quiche/quic/core/quic_utils.h" #include "quiche/quic/test_tools/quic_test_utils.h" #include "quiche/quic/test_tools/quic_session_peer.h" +#include "quiche/quic/test_tools/quic_sent_packet_manager_peer.h" #if defined(__GNUC__) #pragma GCC diagnostic pop @@ -545,8 +546,14 @@ TEST_P(QuicHttpIntegrationTest, PortMigration) { TEST_P(QuicHttpIntegrationTest, PortMigrationOnPathDegrading) { concurrency_ = 2; initialize(); + client_quic_options_.mutable_num_timeouts_to_trigger_port_migration()->set_value(2); uint32_t old_port = lookupPort("http"); codec_client_ = makeHttpConnection(old_port); + + // Make sure that the port migration config is plumbed through. + EXPECT_EQ(2u, quic::test::QuicSentPacketManagerPeer::GetNumPtosForPathDegrading( + &quic_connection_->sent_packet_manager())); + auto encoder_decoder = codec_client_->startRequest(Http::TestRequestHeaderMapImpl{{":method", "POST"}, {":path", "/test/long/url"}, From 4796df4a2bcdcff4491b7718b52f9610c8c0c2ae Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Mon, 8 Nov 2021 11:46:39 -0800 Subject: [PATCH 039/110] test: Cleanup multiplexed_integration_test.cc (#18892) Inline multiplexed_integration_test.h into multiplexed_integration_test.cc Rename Http2IntegrationTest to MultiplexedIntegrationTest and rename Http2RingHashIntegrationTest to MultiplexedRingHashIntegrationTest and make both also cover HTTP/3. Reorder the tests so that the various tests are contiguous instead of interspersed through the file. Signed-off-by: Ryan Hamilton rch@google.com --- test/integration/BUILD | 1 - .../multiplexed_integration_test.cc | 175 ++++++++++++------ .../multiplexed_integration_test.h | 69 ------- 3 files changed, 118 insertions(+), 127 deletions(-) delete mode 100644 test/integration/multiplexed_integration_test.h diff --git a/test/integration/BUILD b/test/integration/BUILD index e969701fa6f35..33a6b71217d40 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -366,7 +366,6 @@ envoy_cc_test( name = "multiplexed_integration_test", srcs = [ "multiplexed_integration_test.cc", - "multiplexed_integration_test.h", ], shard_count = 8, deps = [ diff --git a/test/integration/multiplexed_integration_test.cc b/test/integration/multiplexed_integration_test.cc index e8b614867f04a..98f92eb686d6b 100644 --- a/test/integration/multiplexed_integration_test.cc +++ b/test/integration/multiplexed_integration_test.cc @@ -1,12 +1,13 @@ -#include "test/integration/multiplexed_integration_test.h" - #include +#include #include #ifdef ENVOY_ENABLE_QUIC #include "source/common/quic/client_connection_factory_impl.h" #endif +#include "absl/synchronization/mutex.h" + #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/config/cluster/v3/cluster.pb.h" #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" @@ -16,6 +17,7 @@ #include "source/common/http/header_map_impl.h" #include "test/integration/filters/stop_and_continue_filter_config.pb.h" +#include "test/integration/http_protocol_integration.h" #include "test/integration/utility.h" #include "test/mocks/http/mocks.h" #include "test/test_common/network_utility.h" @@ -34,30 +36,35 @@ namespace Envoy { return; \ } -INSTANTIATE_TEST_SUITE_P(IpVersions, Http2IntegrationTest, +class MultiplexedIntegrationTest : public HttpProtocolIntegrationTest { +public: + void simultaneousRequest(int32_t request1_bytes, int32_t request2_bytes); +}; + +INSTANTIATE_TEST_SUITE_P(IpVersions, MultiplexedIntegrationTest, testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( {Http::CodecType::HTTP2, Http::CodecType::HTTP3}, {Http::CodecType::HTTP1})), HttpProtocolIntegrationTest::protocolTestParamsToString); -TEST_P(Http2IntegrationTest, RouterRequestAndResponseWithBodyNoBuffer) { +TEST_P(MultiplexedIntegrationTest, RouterRequestAndResponseWithBodyNoBuffer) { testRouterRequestAndResponseWithBody(1024, 512, false, false); } -TEST_P(Http2IntegrationTest, RouterRequestAndResponseWithGiantBodyNoBuffer) { +TEST_P(MultiplexedIntegrationTest, RouterRequestAndResponseWithGiantBodyNoBuffer) { config_helper_.addConfigModifier(ConfigHelper::adjustUpstreamTimeoutForTsan); testRouterRequestAndResponseWithBody(10 * 1024 * 1024, 10 * 1024 * 1024, false, false, nullptr, TSAN_TIMEOUT_FACTOR * TestUtility::DefaultTimeout); } -TEST_P(Http2IntegrationTest, FlowControlOnAndGiantBody) { +TEST_P(MultiplexedIntegrationTest, FlowControlOnAndGiantBody) { config_helper_.addConfigModifier(ConfigHelper::adjustUpstreamTimeoutForTsan); config_helper_.setBufferLimits(1024, 1024); // Set buffer limits upstream and downstream. testRouterRequestAndResponseWithBody(10 * 1024 * 1024, 10 * 1024 * 1024, false, false, nullptr, TSAN_TIMEOUT_FACTOR * TestUtility::DefaultTimeout); } -TEST_P(Http2IntegrationTest, LargeFlowControlOnAndGiantBody) { +TEST_P(MultiplexedIntegrationTest, LargeFlowControlOnAndGiantBody) { config_helper_.addConfigModifier(ConfigHelper::adjustUpstreamTimeoutForTsan); config_helper_.setBufferLimits(128 * 1024, 128 * 1024); // Set buffer limits upstream and downstream. @@ -65,24 +72,24 @@ TEST_P(Http2IntegrationTest, LargeFlowControlOnAndGiantBody) { TSAN_TIMEOUT_FACTOR * TestUtility::DefaultTimeout); } -TEST_P(Http2IntegrationTest, RouterRequestAndResponseWithBodyAndContentLengthNoBuffer) { +TEST_P(MultiplexedIntegrationTest, RouterRequestAndResponseWithBodyAndContentLengthNoBuffer) { testRouterRequestAndResponseWithBody(1024, 512, false, true); } -TEST_P(Http2IntegrationTest, RouterRequestAndResponseWithGiantBodyAndContentLengthNoBuffer) { +TEST_P(MultiplexedIntegrationTest, RouterRequestAndResponseWithGiantBodyAndContentLengthNoBuffer) { config_helper_.addConfigModifier(ConfigHelper::adjustUpstreamTimeoutForTsan); testRouterRequestAndResponseWithBody(10 * 1024 * 1024, 10 * 1024 * 1024, false, true, nullptr, TSAN_TIMEOUT_FACTOR * TestUtility::DefaultTimeout); } -TEST_P(Http2IntegrationTest, FlowControlOnAndGiantBodyWithContentLength) { +TEST_P(MultiplexedIntegrationTest, FlowControlOnAndGiantBodyWithContentLength) { config_helper_.addConfigModifier(ConfigHelper::adjustUpstreamTimeoutForTsan); config_helper_.setBufferLimits(1024, 1024); // Set buffer limits upstream and downstream. testRouterRequestAndResponseWithBody(10 * 1024 * 1024, 10 * 1024 * 1024, false, true, nullptr, TSAN_TIMEOUT_FACTOR * TestUtility::DefaultTimeout); } -TEST_P(Http2IntegrationTest, LargeFlowControlOnAndGiantBodyWithContentLength) { +TEST_P(MultiplexedIntegrationTest, LargeFlowControlOnAndGiantBodyWithContentLength) { config_helper_.addConfigModifier(ConfigHelper::adjustUpstreamTimeoutForTsan); config_helper_.setBufferLimits(128 * 1024, 128 * 1024); // Set buffer limits upstream and downstream. @@ -90,42 +97,44 @@ TEST_P(Http2IntegrationTest, LargeFlowControlOnAndGiantBodyWithContentLength) { TSAN_TIMEOUT_FACTOR * TestUtility::DefaultTimeout); } -TEST_P(Http2IntegrationTest, RouterHeaderOnlyRequestAndResponseNoBuffer) { +TEST_P(MultiplexedIntegrationTest, RouterHeaderOnlyRequestAndResponseNoBuffer) { testRouterHeaderOnlyRequestAndResponse(); } -TEST_P(Http2IntegrationTest, RouterRequestAndResponseLargeHeaderNoBuffer) { +TEST_P(MultiplexedIntegrationTest, RouterRequestAndResponseLargeHeaderNoBuffer) { testRouterRequestAndResponseWithBody(1024, 512, true); } -TEST_P(Http2IntegrationTest, RouterUpstreamDisconnectBeforeRequestcomplete) { +TEST_P(MultiplexedIntegrationTest, RouterUpstreamDisconnectBeforeRequestcomplete) { testRouterUpstreamDisconnectBeforeRequestComplete(); } -TEST_P(Http2IntegrationTest, RouterUpstreamDisconnectBeforeResponseComplete) { +TEST_P(MultiplexedIntegrationTest, RouterUpstreamDisconnectBeforeResponseComplete) { testRouterUpstreamDisconnectBeforeResponseComplete(); } -TEST_P(Http2IntegrationTest, RouterDownstreamDisconnectBeforeRequestComplete) { +TEST_P(MultiplexedIntegrationTest, RouterDownstreamDisconnectBeforeRequestComplete) { testRouterDownstreamDisconnectBeforeRequestComplete(); } -TEST_P(Http2IntegrationTest, RouterDownstreamDisconnectBeforeResponseComplete) { +TEST_P(MultiplexedIntegrationTest, RouterDownstreamDisconnectBeforeResponseComplete) { testRouterDownstreamDisconnectBeforeResponseComplete(); } -TEST_P(Http2IntegrationTest, RouterUpstreamResponseBeforeRequestComplete) { +TEST_P(MultiplexedIntegrationTest, RouterUpstreamResponseBeforeRequestComplete) { testRouterUpstreamResponseBeforeRequestComplete(); } -TEST_P(Http2IntegrationTest, Retry) { testRetry(); } +TEST_P(MultiplexedIntegrationTest, Retry) { testRetry(); } -TEST_P(Http2IntegrationTest, RetryAttemptCount) { testRetryAttemptCountHeader(); } +TEST_P(MultiplexedIntegrationTest, RetryAttemptCount) { testRetryAttemptCountHeader(); } -TEST_P(Http2IntegrationTest, LargeRequestTrailersRejected) { testLargeRequestTrailers(66, 60); } +TEST_P(MultiplexedIntegrationTest, LargeRequestTrailersRejected) { + testLargeRequestTrailers(66, 60); +} // Verify downstream codec stream flush timeout. -TEST_P(Http2IntegrationTest, CodecStreamIdleTimeout) { +TEST_P(MultiplexedIntegrationTest, CodecStreamIdleTimeout) { config_helper_.setBufferLimits(1024, 1024); config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& @@ -161,7 +170,7 @@ TEST_P(Http2IntegrationTest, CodecStreamIdleTimeout) { ASSERT_TRUE(response->waitForReset()); } -TEST_P(Http2IntegrationTest, Http2DownstreamKeepalive) { +TEST_P(MultiplexedIntegrationTest, Http2DownstreamKeepalive) { EXCLUDE_DOWNSTREAM_HTTP3; // Http3 keepalive doesn't timeout and close connection. constexpr uint64_t interval_ms = 1; constexpr uint64_t timeout_ms = 250; @@ -196,6 +205,41 @@ name: response-metadata-filter "@type": type.googleapis.com/google.protobuf.Empty )EOF"; +class Http2MetadataIntegrationTest : public HttpProtocolIntegrationTest { +public: + void SetUp() override { + HttpProtocolIntegrationTest::SetUp(); + config_helper_.addConfigModifier( + [&](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() + ->mutable_http2_protocol_options() + ->set_allow_metadata(true); + ConfigHelper::setProtocolOptions( + *bootstrap.mutable_static_resources()->mutable_clusters(0), protocol_options); + }); + config_helper_.addConfigModifier( + [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) -> void { hcm.mutable_http2_protocol_options()->set_allow_metadata(true); }); + } + + void testRequestMetadataWithStopAllFilter(); + + void verifyHeadersOnlyTest(); + + void runHeaderOnlyTest(bool send_request_body, size_t body_size); + +protected: + // Utility function to prepend filters. Note that the filters + // are added in reverse order. + void prependFilters(std::vector filters) { + for (const auto& filter : filters) { + config_helper_.prependFilter(filter); + } + } +}; + // Verifies metadata can be sent at different locations of the responses. TEST_P(Http2MetadataIntegrationTest, ProxyMetadataInResponse) { initialize(); @@ -887,7 +931,7 @@ name: encode-headers-return-stop-all-filter EXPECT_EQ(count * size + added_decoded_data_size * 2, response->body().size()); } -TEST_P(Http2IntegrationTest, GrpcRouterNotFound) { +TEST_P(MultiplexedIntegrationTest, GrpcRouterNotFound) { config_helper_.setDefaultHostAndRoute("foo.com", "/found"); initialize(); @@ -900,10 +944,10 @@ TEST_P(Http2IntegrationTest, GrpcRouterNotFound) { EXPECT_EQ("12", response->headers().getGrpcStatusValue()); } -TEST_P(Http2IntegrationTest, GrpcRetry) { testGrpcRetry(); } +TEST_P(MultiplexedIntegrationTest, GrpcRetry) { testGrpcRetry(); } // Verify the case where there is an HTTP/2 codec/protocol error with an active stream. -TEST_P(Http2IntegrationTest, CodecErrorAfterStreamStart) { +TEST_P(MultiplexedIntegrationTest, CodecErrorAfterStreamStart) { EXCLUDE_DOWNSTREAM_HTTP3; // The HTTP/3 client has no "bad frame" equivalent. initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -920,7 +964,7 @@ TEST_P(Http2IntegrationTest, CodecErrorAfterStreamStart) { ASSERT_TRUE(response->waitForEndStream()); } -TEST_P(Http2IntegrationTest, Http2BadMagic) { +TEST_P(MultiplexedIntegrationTest, Http2BadMagic) { if (downstreamProtocol() == Http::CodecType::HTTP3) { // The "magic" payload is an HTTP/2 specific thing. return; @@ -936,7 +980,7 @@ TEST_P(Http2IntegrationTest, Http2BadMagic) { EXPECT_EQ("", response); } -TEST_P(Http2IntegrationTest, BadFrame) { +TEST_P(MultiplexedIntegrationTest, BadFrame) { EXCLUDE_DOWNSTREAM_HTTP3; // The HTTP/3 client has no "bad frame" equivalent. initialize(); @@ -952,7 +996,7 @@ TEST_P(Http2IntegrationTest, BadFrame) { // Send client headers, a GoAway and then a body and ensure the full request and // response are received. -TEST_P(Http2IntegrationTest, GoAway) { +TEST_P(MultiplexedIntegrationTest, GoAway) { autonomous_upstream_ = true; initialize(); @@ -970,14 +1014,14 @@ TEST_P(Http2IntegrationTest, GoAway) { EXPECT_EQ("200", response->headers().getStatusValue()); } -TEST_P(Http2IntegrationTest, Trailers) { testTrailers(1024, 2048, false, false); } +TEST_P(MultiplexedIntegrationTest, Trailers) { testTrailers(1024, 2048, false, false); } -TEST_P(Http2IntegrationTest, TrailersGiantBody) { +TEST_P(MultiplexedIntegrationTest, TrailersGiantBody) { testTrailers(1024 * 1024, 1024 * 1024, false, false); } // Ensure if new timeouts are set, legacy timeouts do not apply. -TEST_P(Http2IntegrationTest, DEPRECATED_FEATURE_TEST(GrpcRequestTimeoutMixedLegacy)) { +TEST_P(MultiplexedIntegrationTest, DEPRECATED_FEATURE_TEST(GrpcRequestTimeoutMixedLegacy)) { config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { @@ -1010,7 +1054,7 @@ TEST_P(Http2IntegrationTest, DEPRECATED_FEATURE_TEST(GrpcRequestTimeoutMixedLega EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("via_upstream\n")); } -TEST_P(Http2IntegrationTest, GrpcRequestTimeout) { +TEST_P(MultiplexedIntegrationTest, GrpcRequestTimeout) { config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { @@ -1043,7 +1087,7 @@ TEST_P(Http2IntegrationTest, GrpcRequestTimeout) { } // Interleave two requests and responses and make sure that idle timeout is handled correctly. -TEST_P(Http2IntegrationTest, IdleTimeoutWithSimultaneousRequests) { +TEST_P(MultiplexedIntegrationTest, IdleTimeoutWithSimultaneousRequests) { FakeHttpConnectionPtr fake_upstream_connection1; FakeHttpConnectionPtr fake_upstream_connection2; Http::RequestEncoder* encoder1; @@ -1132,7 +1176,7 @@ TEST_P(Http2IntegrationTest, IdleTimeoutWithSimultaneousRequests) { } // Test request mirroring / shadowing with an HTTP/2 downstream and a request with a body. -TEST_P(Http2IntegrationTest, RequestMirrorWithBody) { +TEST_P(MultiplexedIntegrationTest, RequestMirrorWithBody) { config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { @@ -1180,7 +1224,8 @@ TEST_P(Http2IntegrationTest, RequestMirrorWithBody) { } // Interleave two requests and responses and make sure the HTTP2 stack handles this correctly. -void Http2IntegrationTest::simultaneousRequest(int32_t request1_bytes, int32_t request2_bytes) { +void MultiplexedIntegrationTest::simultaneousRequest(int32_t request1_bytes, + int32_t request2_bytes) { FakeHttpConnectionPtr fake_upstream_connection1; FakeHttpConnectionPtr fake_upstream_connection2; Http::RequestEncoder* encoder1; @@ -1249,15 +1294,15 @@ void Http2IntegrationTest::simultaneousRequest(int32_t request1_bytes, int32_t r codec_client_->close(); } -TEST_P(Http2IntegrationTest, SimultaneousRequest) { simultaneousRequest(1024, 512); } +TEST_P(MultiplexedIntegrationTest, SimultaneousRequest) { simultaneousRequest(1024, 512); } -TEST_P(Http2IntegrationTest, SimultaneousRequestWithBufferLimits) { +TEST_P(MultiplexedIntegrationTest, SimultaneousRequestWithBufferLimits) { config_helper_.setBufferLimits(1024, 1024); // Set buffer limits upstream and downstream. simultaneousRequest(1024 * 32, 1024 * 16); } // Test downstream connection delayed close processing. -TEST_P(Http2IntegrationTest, DelayedCloseAfterBadFrame) { +TEST_P(MultiplexedIntegrationTest, DelayedCloseAfterBadFrame) { EXCLUDE_DOWNSTREAM_HTTP3; // Needs HTTP/3 "bad frame" equivalent. config_helper_.addConfigModifier( [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& @@ -1287,7 +1332,7 @@ TEST_P(Http2IntegrationTest, DelayedCloseAfterBadFrame) { } // Test disablement of delayed close processing on downstream connections. -TEST_P(Http2IntegrationTest, DelayedCloseDisabled) { +TEST_P(MultiplexedIntegrationTest, DelayedCloseDisabled) { EXCLUDE_DOWNSTREAM_HTTP3; // Needs HTTP/3 "bad frame" equivalent. config_helper_.addConfigModifier( [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& @@ -1314,7 +1359,7 @@ TEST_P(Http2IntegrationTest, DelayedCloseDisabled) { 0); } -TEST_P(Http2IntegrationTest, PauseAndResume) { +TEST_P(MultiplexedIntegrationTest, PauseAndResume) { config_helper_.prependFilter(R"EOF( name: stop-iteration-and-continue-filter typed_config: @@ -1344,7 +1389,7 @@ TEST_P(Http2IntegrationTest, PauseAndResume) { ASSERT_TRUE(response->complete()); } -TEST_P(Http2IntegrationTest, PauseAndResumeHeadersOnly) { +TEST_P(MultiplexedIntegrationTest, PauseAndResumeHeadersOnly) { config_helper_.prependFilter(R"EOF( name: stop-iteration-and-continue-filter typed_config: @@ -1367,7 +1412,7 @@ TEST_P(Http2IntegrationTest, PauseAndResumeHeadersOnly) { // Verify the case when we have large pending data with empty trailers. It should not introduce // stack-overflow (on ASan build). This is a regression test for // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=24714. -TEST_P(Http2IntegrationTest, EmptyTrailers) { +TEST_P(MultiplexedIntegrationTest, EmptyTrailers) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -1385,7 +1430,22 @@ TEST_P(Http2IntegrationTest, EmptyTrailers) { ASSERT_TRUE(response->complete()); } -Http2RingHashIntegrationTest::Http2RingHashIntegrationTest() { +class MultiplexedRingHashIntegrationTest : public HttpProtocolIntegrationTest { +public: + MultiplexedRingHashIntegrationTest(); + + ~MultiplexedRingHashIntegrationTest() override; + + void createUpstreams() override; + + void sendMultipleRequests(int request_bytes, Http::TestRequestHeaderMapImpl headers, + std::function cb); + + std::vector fake_upstream_connections_; + int num_upstreams_ = 5; +}; + +MultiplexedRingHashIntegrationTest::MultiplexedRingHashIntegrationTest() { config_helper_.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { auto* cluster = bootstrap.mutable_static_resources()->mutable_clusters(0); cluster->clear_load_assignment(); @@ -1404,7 +1464,7 @@ Http2RingHashIntegrationTest::Http2RingHashIntegrationTest() { }); } -Http2RingHashIntegrationTest::~Http2RingHashIntegrationTest() { +MultiplexedRingHashIntegrationTest::~MultiplexedRingHashIntegrationTest() { if (codec_client_) { codec_client_->close(); codec_client_ = nullptr; @@ -1417,15 +1477,16 @@ Http2RingHashIntegrationTest::~Http2RingHashIntegrationTest() { } } -void Http2RingHashIntegrationTest::createUpstreams() { +void MultiplexedRingHashIntegrationTest::createUpstreams() { for (int i = 0; i < num_upstreams_; i++) { addFakeUpstream(Http::CodecType::HTTP1); } } -INSTANTIATE_TEST_SUITE_P(IpVersions, Http2RingHashIntegrationTest, +INSTANTIATE_TEST_SUITE_P(IpVersions, MultiplexedRingHashIntegrationTest, testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( - {Http::CodecType::HTTP2}, {Http::CodecType::HTTP1})), + {Http::CodecType::HTTP2, Http::CodecType::HTTP3}, + {Http::CodecType::HTTP1})), HttpProtocolIntegrationTest::protocolTestParamsToString); INSTANTIATE_TEST_SUITE_P(IpVersions, Http2MetadataIntegrationTest, @@ -1433,7 +1494,7 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, Http2MetadataIntegrationTest, {Http::CodecType::HTTP2}, {Http::CodecType::HTTP2})), HttpProtocolIntegrationTest::protocolTestParamsToString); -void Http2RingHashIntegrationTest::sendMultipleRequests( +void MultiplexedRingHashIntegrationTest::sendMultipleRequests( int request_bytes, Http::TestRequestHeaderMapImpl headers, std::function cb) { TestRandomGenerator rand; @@ -1480,7 +1541,7 @@ void Http2RingHashIntegrationTest::sendMultipleRequests( } } -TEST_P(Http2RingHashIntegrationTest, CookieRoutingNoCookieNoTtl) { +TEST_P(MultiplexedRingHashIntegrationTest, CookieRoutingNoCookieNoTtl) { config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { @@ -1514,7 +1575,7 @@ TEST_P(Http2RingHashIntegrationTest, CookieRoutingNoCookieNoTtl) { EXPECT_EQ(served_by.size(), num_upstreams_); } -TEST_P(Http2RingHashIntegrationTest, CookieRoutingNoCookieWithNonzeroTtlSet) { +TEST_P(MultiplexedRingHashIntegrationTest, CookieRoutingNoCookieWithNonzeroTtlSet) { config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { @@ -1545,7 +1606,7 @@ TEST_P(Http2RingHashIntegrationTest, CookieRoutingNoCookieWithNonzeroTtlSet) { EXPECT_EQ(set_cookies.size(), 1); } -TEST_P(Http2RingHashIntegrationTest, CookieRoutingNoCookieWithZeroTtlSet) { +TEST_P(MultiplexedRingHashIntegrationTest, CookieRoutingNoCookieWithZeroTtlSet) { config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { @@ -1576,7 +1637,7 @@ TEST_P(Http2RingHashIntegrationTest, CookieRoutingNoCookieWithZeroTtlSet) { EXPECT_EQ(set_cookies.size(), 1); } -TEST_P(Http2RingHashIntegrationTest, CookieRoutingWithCookieNoTtl) { +TEST_P(MultiplexedRingHashIntegrationTest, CookieRoutingWithCookieNoTtl) { config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { @@ -1608,7 +1669,7 @@ TEST_P(Http2RingHashIntegrationTest, CookieRoutingWithCookieNoTtl) { EXPECT_EQ(served_by.size(), 1); } -TEST_P(Http2RingHashIntegrationTest, CookieRoutingWithCookieWithTtlSet) { +TEST_P(MultiplexedRingHashIntegrationTest, CookieRoutingWithCookieWithTtlSet) { config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { @@ -1825,7 +1886,7 @@ name: on-local-reply-filter "@type": type.googleapis.com/google.protobuf.Empty )EOF"; -TEST_P(Http2IntegrationTest, OnLocalReply) { +TEST_P(MultiplexedIntegrationTest, OnLocalReply) { config_helper_.prependFilter(on_local_reply_filter); initialize(); @@ -1861,7 +1922,7 @@ TEST_P(Http2IntegrationTest, OnLocalReply) { // Disabled for coverage temporarily see #18881 #if !defined(ENVOY_CONFIG_COVERAGE) -TEST_P(Http2IntegrationTest, InvalidTrailers) { +TEST_P(MultiplexedIntegrationTest, InvalidTrailers) { useAccessLog("%RESPONSE_CODE_DETAILS%"); autonomous_upstream_ = true; initialize(); @@ -1882,7 +1943,7 @@ TEST_P(Http2IntegrationTest, InvalidTrailers) { } #endif -TEST_P(Http2IntegrationTest, InconsistentContentLength) { +TEST_P(MultiplexedIntegrationTest, InconsistentContentLength) { useAccessLog("%RESPONSE_CODE_DETAILS%"); initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); diff --git a/test/integration/multiplexed_integration_test.h b/test/integration/multiplexed_integration_test.h deleted file mode 100644 index 81876304b44c6..0000000000000 --- a/test/integration/multiplexed_integration_test.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include - -#include "envoy/config/bootstrap/v3/bootstrap.pb.h" -#include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" - -#include "test/integration/http_protocol_integration.h" - -#include "absl/synchronization/mutex.h" -#include "gtest/gtest.h" - -namespace Envoy { -class Http2IntegrationTest : public HttpProtocolIntegrationTest { -public: - void simultaneousRequest(int32_t request1_bytes, int32_t request2_bytes); - -protected: - // Utility function to prepend filters. Note that the filters - // are added in reverse order. - void prependFilters(std::vector filters) { - for (const auto& filter : filters) { - config_helper_.prependFilter(filter); - } - } -}; - -class Http2RingHashIntegrationTest : public Http2IntegrationTest { -public: - Http2RingHashIntegrationTest(); - - ~Http2RingHashIntegrationTest() override; - - void createUpstreams() override; - - void sendMultipleRequests(int request_bytes, Http::TestRequestHeaderMapImpl headers, - std::function cb); - - std::vector fake_upstream_connections_; - int num_upstreams_ = 5; -}; - -class Http2MetadataIntegrationTest : public Http2IntegrationTest { -public: - void SetUp() override { - HttpProtocolIntegrationTest::SetUp(); - config_helper_.addConfigModifier( - [&](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() - ->mutable_http2_protocol_options() - ->set_allow_metadata(true); - ConfigHelper::setProtocolOptions( - *bootstrap.mutable_static_resources()->mutable_clusters(0), protocol_options); - }); - config_helper_.addConfigModifier( - [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& - hcm) -> void { hcm.mutable_http2_protocol_options()->set_allow_metadata(true); }); - } - - void testRequestMetadataWithStopAllFilter(); - - void verifyHeadersOnlyTest(); - - void runHeaderOnlyTest(bool send_request_body, size_t body_size); -}; - -} // namespace Envoy From a5a4251492a38c512cea974d318ed77a3f2a7b0b Mon Sep 17 00:00:00 2001 From: Otto van der Schaaf Date: Mon, 8 Nov 2021 23:07:12 +0100 Subject: [PATCH 040/110] test: parameterize ipversion for the weighted cluster integration tests (#18679) Right now it hard-codes the ip-version to ipv6, which causes trouble in certain environments. Signed-off-by: Otto van der Schaaf --- .../weighted_cluster_integration_test.cc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/integration/weighted_cluster_integration_test.cc b/test/integration/weighted_cluster_integration_test.cc index e43d784419b70..268bf0a215c04 100644 --- a/test/integration/weighted_cluster_integration_test.cc +++ b/test/integration/weighted_cluster_integration_test.cc @@ -14,10 +14,11 @@ namespace Envoy { namespace { -class WeightedClusterIntegrationTest : public testing::Test, public HttpIntegrationTest { +class WeightedClusterIntegrationTest : public testing::TestWithParam, + public HttpIntegrationTest { public: WeightedClusterIntegrationTest() - : HttpIntegrationTest(Http::CodecClient::Type::HTTP2, Network::Address::IpVersion::v6) {} + : HttpIntegrationTest(Http::CodecClient::Type::HTTP2, GetParam()) {} void createUpstreams() override { setUpstreamProtocol(FakeHttpConnection::Type::HTTP2); @@ -101,8 +102,12 @@ class WeightedClusterIntegrationTest : public testing::Test, public HttpIntegrat std::vector default_weights_ = {20, 30}; }; +INSTANTIATE_TEST_SUITE_P(IpVersions, WeightedClusterIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + // Steer the traffic (i.e. send the request) to the weighted cluster with `name` specified. -TEST_F(WeightedClusterIntegrationTest, SteerTrafficToOneClusterWithName) { +TEST_P(WeightedClusterIntegrationTest, SteerTrafficToOneClusterWithName) { setDeterministicValue(); initializeConfig(getDefaultWeights()); @@ -116,7 +121,7 @@ TEST_F(WeightedClusterIntegrationTest, SteerTrafficToOneClusterWithName) { // Steer the traffic (i.e. send the request) to the weighted cluster with `cluster_header` // specified. -TEST_F(WeightedClusterIntegrationTest, SteerTrafficToOneClusterWithHeader) { +TEST_P(WeightedClusterIntegrationTest, SteerTrafficToOneClusterWithHeader) { const std::vector& default_weights = getDefaultWeights(); // The index of the cluster with `cluster_header` specified is 1. @@ -139,7 +144,7 @@ TEST_F(WeightedClusterIntegrationTest, SteerTrafficToOneClusterWithHeader) { } // Steer the traffic (i.e. send the request) to the weighted clusters randomly based on weight. -TEST_F(WeightedClusterIntegrationTest, SplitTrafficRandomly) { +TEST_P(WeightedClusterIntegrationTest, SplitTrafficRandomly) { std::vector weights = {50, 50}; int upstream_count = weights.size(); initializeConfig(weights); From 7351734f6076152ccdad2c8630e3c3760b7ec38e Mon Sep 17 00:00:00 2001 From: Jose Ulises Nino Rivera Date: Mon, 8 Nov 2021 18:45:14 -0800 Subject: [PATCH 041/110] c-ares dns: add log on failure (#18918) Signed-off-by: Jose Nino --- source/extensions/network/dns_resolver/cares/dns_impl.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/extensions/network/dns_resolver/cares/dns_impl.cc b/source/extensions/network/dns_resolver/cares/dns_impl.cc index 38650568adce3..5b12a65a63613 100644 --- a/source/extensions/network/dns_resolver/cares/dns_impl.cc +++ b/source/extensions/network/dns_resolver/cares/dns_impl.cc @@ -100,6 +100,11 @@ void DnsResolverImpl::initializeChannel(ares_options* options, int optmask) { void DnsResolverImpl::PendingResolution::onAresGetAddrInfoCallback(int status, int timeouts, ares_addrinfo* addrinfo) { + if (status != ARES_SUCCESS) { + ENVOY_LOG_EVENT(debug, "cares_resolution_failure", + "dns resolution for {} failed with c-ares status {}", dns_name_, status); + } + // We receive ARES_EDESTRUCTION when destructing with pending queries. if (status == ARES_EDESTRUCTION) { ASSERT(owned_); From 6acc5d20cba6706cdba1c9ade2303f06061c88d9 Mon Sep 17 00:00:00 2001 From: James Fish Date: Mon, 8 Nov 2021 20:10:46 -0800 Subject: [PATCH 042/110] thrift_proxy: Add thrift header to metadata filter (#18637) Signed-off-by: James Fish --- api/BUILD | 1 + .../filters/header_to_metadata/v3/BUILD | 12 + .../v3/header_to_metadata.proto | 109 ++++ api/versioning/BUILD | 1 + .../header_to_metadata_filter.rst | 90 ++++ .../thrift_filters/thrift_filters.rst | 1 + docs/root/version_history/current.rst | 1 + source/extensions/extensions_build_config.bzl | 1 + source/extensions/extensions_metadata.yaml | 5 + .../filters/header_to_metadata/BUILD | 33 ++ .../filters/header_to_metadata/config.cc | 35 ++ .../filters/header_to_metadata/config.h | 33 ++ .../header_to_metadata_filter.cc | 184 +++++++ .../header_to_metadata_filter.h | 104 ++++ .../filters/header_to_metadata/BUILD | 37 ++ .../filters/header_to_metadata/config_test.cc | 127 +++++ .../header_to_metadata_filter_test.cc | 502 ++++++++++++++++++ 17 files changed, 1276 insertions(+) create mode 100644 api/envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/BUILD create mode 100644 api/envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/header_to_metadata.proto create mode 100644 docs/root/configuration/other_protocols/thrift_filters/header_to_metadata_filter.rst create mode 100644 source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/BUILD create mode 100644 source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config.cc create mode 100644 source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config.h create mode 100644 source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter.cc create mode 100644 source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter.h create mode 100644 test/extensions/filters/network/thrift_proxy/filters/header_to_metadata/BUILD create mode 100644 test/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config_test.cc create mode 100644 test/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter_test.cc diff --git a/api/BUILD b/api/BUILD index 11eb3d4bdb3ce..ddd1f98b36cb7 100644 --- a/api/BUILD +++ b/api/BUILD @@ -193,6 +193,7 @@ proto_library( "//envoy/extensions/filters/network/sni_cluster/v3:pkg", "//envoy/extensions/filters/network/sni_dynamic_forward_proxy/v3:pkg", "//envoy/extensions/filters/network/tcp_proxy/v3:pkg", + "//envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3:pkg", "//envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3:pkg", "//envoy/extensions/filters/network/thrift_proxy/router/v3:pkg", "//envoy/extensions/filters/network/thrift_proxy/v3:pkg", diff --git a/api/envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/BUILD b/api/envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/BUILD new file mode 100644 index 0000000000000..693f0b92ff34d --- /dev/null +++ b/api/envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/BUILD @@ -0,0 +1,12 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/type/matcher/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/header_to_metadata.proto b/api/envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/header_to_metadata.proto new file mode 100644 index 0000000000000..f0b0ec963b4fb --- /dev/null +++ b/api/envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/header_to_metadata.proto @@ -0,0 +1,109 @@ +syntax = "proto3"; + +package envoy.extensions.filters.network.thrift_proxy.filters.header_to_metadata.v3; + +import "envoy/type/matcher/v3/regex.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.network.thrift_proxy.filters.header_to_metadata.v3"; +option java_outer_classname = "HeaderToMetadataProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Header-To-Metadata Filter] +// +// The configuration for transforming headers into metadata. This is useful +// for matching load balancer subsets, logging, etc. +// +// Header to Metadata :ref:`configuration overview `. +// [#extension: envoy.filters.thrift.header_to_metadata] + +message HeaderToMetadata { + enum ValueType { + STRING = 0; + + NUMBER = 1; + + // The value is a serialized `protobuf.Value + // `_. + PROTOBUF_VALUE = 2; + } + + // ValueEncode defines the encoding algorithm. + enum ValueEncode { + // The value is not encoded. + NONE = 0; + + // The value is encoded in `Base64 `_. + // Note: this is mostly used for STRING and PROTOBUF_VALUE to escape the + // non-ASCII characters in the header. + BASE64 = 1; + } + + // [#next-free-field: 7] + message KeyValuePair { + // The namespace — if this is empty, the filter's namespace will be used. + string metadata_namespace = 1; + + // The key to use within the namespace. + string key = 2 [(validate.rules).string = {min_len: 1}]; + + oneof value_type { + // The value to pair with the given key. + // + // When used for on_present case, if value is non-empty it'll be used instead + // of the header value. If both are empty, no metadata is added. + // + // When used for on_missing case, a non-empty value must be provided otherwise + // no metadata is added. + string value = 3; + + // If present, the header's value will be matched and substituted with this. + // If there is no match or substitution, the header value + // is used as-is. + // + // This is only used for on_present. + // + // Note: if the `value` field is non-empty this field should be empty. + type.matcher.v3.RegexMatchAndSubstitute regex_value_rewrite = 4; + } + + // The value's type — defaults to string. + ValueType type = 5 [(validate.rules).enum = {defined_only: true}]; + + // How is the value encoded, default is NONE (not encoded). + // The value will be decoded accordingly before storing to metadata. + ValueEncode encode = 6; + } + + // A Rule defines what metadata to apply when a header is present or missing. + message Rule { + // Specifies that a match will be performed on the value of a header. + // + // The header to be extracted. + string header = 1 + [(validate.rules).string = {min_len: 1 well_known_regex: HTTP_HEADER_NAME strict: false}]; + + // If the header is present, apply this metadata KeyValuePair. + // + // If the value in the KeyValuePair is non-empty, it'll be used instead + // of the header value. + KeyValuePair on_present = 2; + + // If the header is not present, apply this metadata KeyValuePair. + // + // The value in the KeyValuePair must be set, since it'll be used in lieu + // of the missing header value. + KeyValuePair on_missing = 3; + + // Whether or not to remove the header after a rule is applied. + // + // This prevents headers from leaking. + bool remove = 4; + } + + // The list of rules to apply to requests. + repeated Rule request_rules = 1 [(validate.rules).repeated = {min_items: 1}]; +} diff --git a/api/versioning/BUILD b/api/versioning/BUILD index 8eeb536f66c84..24195d8d680dd 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -130,6 +130,7 @@ proto_library( "//envoy/extensions/filters/network/sni_cluster/v3:pkg", "//envoy/extensions/filters/network/sni_dynamic_forward_proxy/v3:pkg", "//envoy/extensions/filters/network/tcp_proxy/v3:pkg", + "//envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3:pkg", "//envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3:pkg", "//envoy/extensions/filters/network/thrift_proxy/router/v3:pkg", "//envoy/extensions/filters/network/thrift_proxy/v3:pkg", diff --git a/docs/root/configuration/other_protocols/thrift_filters/header_to_metadata_filter.rst b/docs/root/configuration/other_protocols/thrift_filters/header_to_metadata_filter.rst new file mode 100644 index 0000000000000..611142166349d --- /dev/null +++ b/docs/root/configuration/other_protocols/thrift_filters/header_to_metadata_filter.rst @@ -0,0 +1,90 @@ +.. _config_thrift_filters_header_to_metadata: + +Envoy Header-To-Metadata Filter +=============================== +* :ref:`v3 API reference ` +* This filter should be configured with the name *envoy.filters.http.header_to_metadata*. + +This filter is configured with rules that will be matched against requests. +Each rule has either a header and can be triggered either when the header is present or missing. + +When a rule is triggered, dynamic metadata will be added based on the configuration of the rule. +If the header is present, it's value is extracted and used along with the specified +key as metadata. If the header is missing, on missing case is triggered and the value +specified is used for adding metadata. + +The metadata can then be used for load balancing decisions, consumed from logs, etc. + +A typical use case for this filter is to dynamically match requests with load balancer +subsets. For this, a given header's value would be extracted and attached to the request +as dynamic metadata which would then be used to match a subset of endpoints. + +Example +------- + +A sample filter configuration to route traffic to endpoints based on the presence or +absence of a version header could be: + +.. code-block:: yaml + + thrift_filters: + - name: envoy.filters.thrift.header_to_metadata + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.thrift.header_to_metadata.v3.HeaderToMetadata + request_rules: + - header: x-version + on_header_present: + metadata_namespace: envoy.lb + key: version + type: STRING + on_header_missing: + metadata_namespace: envoy.lb + key: default + value: 'true' + type: STRING + remove: false + +A corresponding upstream cluster configuration could be: + +.. code-block:: yaml + + clusters: + - name: versioned-cluster + type: EDS + lb_policy: ROUND_ROBIN + lb_subset_config: + fallback_policy: ANY_ENDPOINT + subset_selectors: + - keys: + - default + - keys: + - version + +This would then allow requests with the ``x-version`` header set to be matched against +endpoints with the corresponding version. Whereas requests with that header missing +would be matched with the default endpoints. + +If the header's value needs to be transformed before it's added to the request as +dynamic metadata, this filter supports regex matching and substitution: + +.. code-block:: yaml + + thrift_filters: + - name: envoy.filters.thrift.header_to_metadata + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.thrift.header_to_metadata.v3.HeaderToMetadata + request_rules: + - header: ":path" + on_header_present: + metadata_namespace: envoy.lb + key: cluster + regex_value_rewrite: + pattern: + google_re2: {} + regex: "^/(cluster[\\d\\w-]+)/?.*$" + substitution: "\\1" + +Statistics +---------- + +Currently, this filter generates no statistics. diff --git a/docs/root/configuration/other_protocols/thrift_filters/thrift_filters.rst b/docs/root/configuration/other_protocols/thrift_filters/thrift_filters.rst index 5dadb3c5f8d40..3dd28c6523324 100644 --- a/docs/root/configuration/other_protocols/thrift_filters/thrift_filters.rst +++ b/docs/root/configuration/other_protocols/thrift_filters/thrift_filters.rst @@ -8,5 +8,6 @@ Envoy has the following builtin Thrift filters. .. toctree:: :maxdepth: 2 + header_to_metadata_filter rate_limit_filter router_filter diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 271c72da8e64a..0d9dcd8fa1d65 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -67,6 +67,7 @@ New Features * oauth filter: setting IdToken and RefreshToken cookies if they are provided by Identity provider along with AccessToken. * tcp: added a :ref:`FilterState ` :ref:`hash policy `, used by :ref:`TCP proxy ` to allow hashing load balancer algorithms to hash on objects in filter state. * tcp_proxy: added support to populate upstream http connect header values from stream info. +* thrift_proxy: add header to metadata filter for turning headers into dynamic metadata. * thrift_proxy: add upstream response zone metrics in the form ``cluster.cluster_name.zone.local_zone.upstream_zone.thrift.upstream_resp_success``. * thrift_proxy: add upstream metrics to show decoding errors and whether exception is from local or remote, e.g. ``cluster.cluster_name.thrift.upstream_resp_exception_remote``. * thrift_proxy: add host level success/error metrics where success is a reply of type success and error is any other response to a call. diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index f9acb724d9642..cd6a62d3d5c42 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -173,6 +173,7 @@ EXTENSIONS = { # "envoy.filters.thrift.router": "//source/extensions/filters/network/thrift_proxy/router:config", + "envoy.filters.thrift.header_to_metadata": "//source/extensions/filters/network/thrift_proxy/filters/header_to_metadata:config", "envoy.filters.thrift.ratelimit": "//source/extensions/filters/network/thrift_proxy/filters/ratelimit:config", # diff --git a/source/extensions/extensions_metadata.yaml b/source/extensions/extensions_metadata.yaml index 4df99902fad6d..6a4a403220fdb 100644 --- a/source/extensions/extensions_metadata.yaml +++ b/source/extensions/extensions_metadata.yaml @@ -393,6 +393,11 @@ envoy.filters.network.zookeeper_proxy: - envoy.filters.network security_posture: requires_trusted_downstream_and_upstream status: alpha +envoy.filters.thrift.header_to_metadata: + categories: + - envoy.thrift_proxy.filters + security_posture: requires_trusted_downstream_and_upstream + status: alpha envoy.filters.thrift.ratelimit: categories: - envoy.thrift_proxy.filters diff --git a/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/BUILD b/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/BUILD new file mode 100644 index 0000000000000..810944a4ac04d --- /dev/null +++ b/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/BUILD @@ -0,0 +1,33 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + deps = [ + ":header_to_metadata_filter_lib", + "//envoy/registry", + "//source/extensions/filters/network/thrift_proxy/filters:factory_base_lib", + "@envoy_api//envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3:pkg_cc_proto", + ], +) + +envoy_cc_library( + name = "header_to_metadata_filter_lib", + srcs = ["header_to_metadata_filter.cc"], + hdrs = ["header_to_metadata_filter.h"], + deps = [ + "//envoy/server:filter_config_interface", + "//source/extensions/filters/network/thrift_proxy/filters:pass_through_filter_lib", + "@envoy_api//envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3:pkg_cc_proto", + ], +) diff --git a/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config.cc b/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config.cc new file mode 100644 index 0000000000000..f7fde92031706 --- /dev/null +++ b/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config.cc @@ -0,0 +1,35 @@ +#include "source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config.h" + +#include + +#include "source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter.h" + +namespace Envoy { +namespace Extensions { +namespace ThriftFilters { +namespace HeaderToMetadataFilter { + +using namespace Envoy::Extensions::NetworkFilters; + +ThriftProxy::ThriftFilters::FilterFactoryCb +HeaderToMetadataFilterConfig::createFilterFactoryFromProtoTyped( + const envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata::v3:: + HeaderToMetadata& proto_config, + const std::string&, Server::Configuration::FactoryContext&) { + ConfigSharedPtr filter_config(std::make_shared(proto_config)); + return + [filter_config](ThriftProxy::ThriftFilters::FilterChainFactoryCallbacks& callbacks) -> void { + callbacks.addDecoderFilter(std::make_shared(filter_config)); + }; +} + +/** + * Static registration for the header to metadata filter. @see RegisterFactory. + */ +REGISTER_FACTORY(HeaderToMetadataFilterConfig, + ThriftProxy::ThriftFilters::NamedThriftFilterConfigFactory); + +} // namespace HeaderToMetadataFilter +} // namespace ThriftFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config.h b/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config.h new file mode 100644 index 0000000000000..08777ae395a13 --- /dev/null +++ b/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config.h @@ -0,0 +1,33 @@ +#pragma once + +#include "envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/header_to_metadata.pb.h" +#include "envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/header_to_metadata.pb.validate.h" + +#include "source/extensions/filters/network/thrift_proxy/filters/factory_base.h" +#include "source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter.h" + +namespace Envoy { +namespace Extensions { +namespace ThriftFilters { +namespace HeaderToMetadataFilter { + +/** + * Config registration for the header to metadata filter. @see NamedThriftFilterConfigFactory. + */ +class HeaderToMetadataFilterConfig : public ThriftProxy::ThriftFilters::FactoryBase< + envoy::extensions::filters::network::thrift_proxy:: + filters::header_to_metadata::v3::HeaderToMetadata> { +public: + HeaderToMetadataFilterConfig() : FactoryBase("envoy.filters.thrift.header_to_metadata") {} + +private: + ThriftProxy::ThriftFilters::FilterFactoryCb createFilterFactoryFromProtoTyped( + const envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata::v3:: + HeaderToMetadata& proto_config, + const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; +}; + +} // namespace HeaderToMetadataFilter +} // namespace ThriftFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter.cc b/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter.cc new file mode 100644 index 0000000000000..bf2b3409a7c40 --- /dev/null +++ b/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter.cc @@ -0,0 +1,184 @@ +#include "source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter.h" + +#include "source/common/common/base64.h" +#include "source/common/common/regex.h" +#include "source/common/http/headers.h" +#include "source/common/network/utility.h" + +namespace Envoy { +namespace Extensions { +namespace ThriftFilters { +namespace HeaderToMetadataFilter { + +using namespace Envoy::Extensions::NetworkFilters; + +// Extract the value of the header. +absl::optional HeaderValueSelector::extract(Http::HeaderMap& map) const { + const auto header_entry = map.get(header_); + if (header_entry.empty()) { + return absl::nullopt; + } + // Catch the value in the header before removing. + absl::optional value = std::string(header_entry[0]->value().getStringView()); + if (remove_) { + map.remove(header_); + } + return value; +} + +Rule::Rule(const ProtoRule& rule) : rule_(rule) { + selector_ = + std::make_shared(Http::LowerCaseString(rule.header()), rule.remove()); + + // Rule must have at least one of the `on_*` fields set. + if (!rule.has_on_present() && !rule.has_on_missing()) { + const auto& error = fmt::format("header to metadata filter: rule for {} has neither " + "`on_present` nor `on_missing` set", + selector_->toString()); + throw EnvoyException(error); + } + + if (rule.has_on_missing() && rule.on_missing().value().empty()) { + throw EnvoyException("Cannot specify on_missing rule without non-empty value"); + } + + if (rule.has_on_present() && rule.on_present().has_regex_value_rewrite()) { + const auto& rewrite_spec = rule.on_present().regex_value_rewrite(); + regex_rewrite_ = Regex::Utility::parseRegex(rewrite_spec.pattern()); + regex_rewrite_substitution_ = rewrite_spec.substitution(); + } +} + +Config::Config(const envoy::extensions::filters::network::thrift_proxy::filters:: + header_to_metadata::v3::HeaderToMetadata& config) { + for (const auto& entry : config.request_rules()) { + request_rules_.emplace_back(entry); + } +} + +HeaderToMetadataFilter::HeaderToMetadataFilter(const ConfigSharedPtr config) : config_(config) {} + +ThriftProxy::FilterStatus +HeaderToMetadataFilter::transportBegin(ThriftProxy::MessageMetadataSharedPtr metadata) { + auto& headers = metadata->headers(); + + writeHeaderToMetadata(headers, config_->requestRules(), *decoder_callbacks_); + + return ThriftProxy::FilterStatus::Continue; +} + +const std::string& HeaderToMetadataFilter::decideNamespace(const std::string& nspace) const { + static const std::string& headerToMetadata = "envoy.filters.thrift.header_to_metadata"; + return nspace.empty() ? headerToMetadata : nspace; +} + +bool HeaderToMetadataFilter::addMetadata(StructMap& map, const std::string& meta_namespace, + const std::string& key, std::string value, ValueType type, + ValueEncode encode) const { + ProtobufWkt::Value val; + + ASSERT(!value.empty()); + + if (value.size() >= MAX_HEADER_VALUE_LEN) { + // Too long, go away. + ENVOY_LOG(debug, "metadata value is too long"); + return false; + } + + if (encode == envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata::v3:: + HeaderToMetadata::BASE64) { + value = Base64::decodeWithoutPadding(value); + if (value.empty()) { + ENVOY_LOG(debug, "Base64 decode failed"); + return false; + } + } + + // Sane enough, add the key/value. + switch (type) { + case envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata::v3:: + HeaderToMetadata::STRING: + val.set_string_value(std::move(value)); + break; + case envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata::v3:: + HeaderToMetadata::NUMBER: { + double dval; + if (absl::SimpleAtod(StringUtil::trim(value), &dval)) { + val.set_number_value(dval); + } else { + ENVOY_LOG(debug, "value to number conversion failed"); + return false; + } + break; + } + case envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata::v3:: + HeaderToMetadata::PROTOBUF_VALUE: { + if (!val.ParseFromString(value)) { + ENVOY_LOG(debug, "parse from decoded string failed"); + return false; + } + break; + } + default: + NOT_REACHED_GCOVR_EXCL_LINE; + } + + // Have we seen this namespace before? + auto namespace_iter = map.find(meta_namespace); + if (namespace_iter == map.end()) { + map[meta_namespace] = ProtobufWkt::Struct(); + namespace_iter = map.find(meta_namespace); + } + + auto& keyval = namespace_iter->second; + (*keyval.mutable_fields())[key] = val; + + return true; +} + +// add metadata['key']= value depending on header present or missing case +void HeaderToMetadataFilter::applyKeyValue(std::string&& value, const Rule& rule, + const KeyValuePair& keyval, StructMap& np) const { + if (keyval.has_regex_value_rewrite()) { + const auto& matcher = rule.regexRewrite(); + value = matcher->replaceAll(value, rule.regexSubstitution()); + } else if (!keyval.value().empty()) { + value = keyval.value(); + } + if (!value.empty()) { + const auto& nspace = decideNamespace(keyval.metadata_namespace()); + addMetadata(np, nspace, keyval.key(), value, keyval.type(), keyval.encode()); + } else { + ENVOY_LOG(debug, "value is empty, not adding metadata"); + } +} + +void HeaderToMetadataFilter::writeHeaderToMetadata( + Http::HeaderMap& headers, const HeaderToMetadataRules& rules, + ThriftProxy::ThriftFilters::DecoderFilterCallbacks& callbacks) const { + StructMap structs_by_namespace; + + for (const auto& rule : rules) { + const auto& proto_rule = rule.rule(); + absl::optional value = rule.selector_->extract(headers); + + if (value && proto_rule.has_on_present()) { + applyKeyValue(std::move(value).value_or(""), rule, proto_rule.on_present(), + structs_by_namespace); + } else if (!value && proto_rule.has_on_missing()) { + applyKeyValue(std::move(value).value_or(""), rule, proto_rule.on_missing(), + structs_by_namespace); + } + } + // Any matching rules? + if (!structs_by_namespace.empty()) { + for (auto const& entry : structs_by_namespace) { + callbacks.streamInfo().setDynamicMetadata(entry.first, entry.second); + } + } +} + +} // namespace HeaderToMetadataFilter +} // namespace ThriftFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter.h b/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter.h new file mode 100644 index 0000000000000..3578943d52fbd --- /dev/null +++ b/source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter.h @@ -0,0 +1,104 @@ +#pragma once + +#include +#include +#include + +#include "envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/header_to_metadata.pb.h" + +#include "source/common/common/logger.h" +#include "source/common/common/matchers.h" +#include "source/extensions/filters/network/thrift_proxy/filters/pass_through_filter.h" + +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Extensions { +namespace ThriftFilters { +namespace HeaderToMetadataFilter { + +using namespace Envoy::Extensions::NetworkFilters; +using ProtoRule = envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata:: + v3::HeaderToMetadata::Rule; +using KeyValuePair = envoy::extensions::filters::network::thrift_proxy::filters:: + header_to_metadata::v3::HeaderToMetadata::KeyValuePair; +using ValueType = envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata:: + v3::HeaderToMetadata::ValueType; +using ValueEncode = envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata:: + v3::HeaderToMetadata::ValueEncode; + +// Get value from a header. +class HeaderValueSelector { +public: + explicit HeaderValueSelector(Http::LowerCaseString header, bool remove) + : header_(std::move(header)), remove_(std::move(remove)) {} + absl::optional extract(Http::HeaderMap& map) const; + std::string toString() const { return fmt::format("header '{}'", header_.get()); } + +private: + const Http::LowerCaseString header_; + const bool remove_; +}; + +class Rule { +public: + Rule(const ProtoRule& rule); + const ProtoRule& rule() const { return rule_; } + const Regex::CompiledMatcherPtr& regexRewrite() const { return regex_rewrite_; } + const std::string& regexSubstitution() const { return regex_rewrite_substitution_; } + std::shared_ptr selector_; + +private: + const ProtoRule rule_; + Regex::CompiledMatcherPtr regex_rewrite_{}; + std::string regex_rewrite_substitution_{}; +}; + +using HeaderToMetadataRules = std::vector; + +const uint32_t MAX_HEADER_VALUE_LEN = 8 * 1024; + +class Config { +public: + Config(const envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata::v3:: + HeaderToMetadata& config); + const HeaderToMetadataRules& requestRules() const { return request_rules_; } + +private: + HeaderToMetadataRules request_rules_; +}; + +using ConfigSharedPtr = std::shared_ptr; + +class HeaderToMetadataFilter : public ThriftProxy::ThriftFilters::PassThroughDecoderFilter, + protected Logger::Loggable { +public: + HeaderToMetadataFilter(const ConfigSharedPtr config); + + ThriftProxy::FilterStatus + transportBegin(Extensions::NetworkFilters::ThriftProxy::MessageMetadataSharedPtr) override; + +private: + using ProtobufRepeatedRule = Protobuf::RepeatedPtrField; + using StructMap = std::map; + + /** + * writeHeaderToMetadata encapsulates (1) searching for the header and (2) writing it to the + * request metadata. + * @param headers the map of key-value headers to look through. These are request headers. + * @param rules the header-to-metadata mapping set in configuration. + */ + void writeHeaderToMetadata(Http::HeaderMap& headers, const HeaderToMetadataRules& rules, + ThriftProxy::ThriftFilters::DecoderFilterCallbacks& callbacks) const; + bool addMetadata(StructMap&, const std::string&, const std::string&, std::string, ValueType, + ValueEncode) const; + void applyKeyValue(std::string&&, const Rule&, const KeyValuePair&, StructMap&) const; + const std::string& decideNamespace(const std::string& nspace) const; + + const ConfigSharedPtr config_; +}; + +} // namespace HeaderToMetadataFilter +} // namespace ThriftFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/network/thrift_proxy/filters/header_to_metadata/BUILD b/test/extensions/filters/network/thrift_proxy/filters/header_to_metadata/BUILD new file mode 100644 index 0000000000000..45f0eec6b570a --- /dev/null +++ b/test/extensions/filters/network/thrift_proxy/filters/header_to_metadata/BUILD @@ -0,0 +1,37 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_package", +) +load( + "//test/extensions:extensions_build_system.bzl", + "envoy_extension_cc_test", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_extension_cc_test( + name = "config_test", + srcs = ["config_test.cc"], + extension_names = ["envoy.filters.thrift.header_to_metadata"], + deps = [ + "//source/extensions/filters/network/thrift_proxy/filters/header_to_metadata:config", + "//source/extensions/filters/network/thrift_proxy/filters/header_to_metadata:header_to_metadata_filter_lib", + "//test/extensions/filters/network/thrift_proxy:mocks", + "//test/mocks/server:server_mocks", + "@envoy_api//envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3:pkg_cc_proto", + ], +) + +envoy_extension_cc_test( + name = "header_to_metadata_filter_test", + srcs = ["header_to_metadata_filter_test.cc"], + extension_names = ["envoy.filters.thrift.header_to_metadata"], + deps = [ + "//source/extensions/filters/network/thrift_proxy/filters/header_to_metadata:header_to_metadata_filter_lib", + "//test/extensions/filters/network/thrift_proxy:mocks", + "//test/mocks/server:server_mocks", + "//test/mocks/ssl:ssl_mocks", + ], +) diff --git a/test/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config_test.cc b/test/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config_test.cc new file mode 100644 index 0000000000000..e3dadf9eedf25 --- /dev/null +++ b/test/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config_test.cc @@ -0,0 +1,127 @@ +#include + +#include "envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/header_to_metadata.pb.h" +#include "envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/header_to_metadata.pb.validate.h" + +#include "source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/config.h" +#include "source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter.h" + +#include "test/extensions/filters/network/thrift_proxy/mocks.h" +#include "test/mocks/server/factory_context.h" +#include "test/mocks/server/instance.h" +#include "test/test_common/utility.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace ThriftFilters { +namespace HeaderToMetadataFilter { + +using HeaderToMetadataProtoConfig = envoy::extensions::filters::network::thrift_proxy::filters:: + header_to_metadata::v3::HeaderToMetadata; + +void testForbiddenConfig(const std::string& yaml, const std::string& message) { + HeaderToMetadataProtoConfig proto_config; + TestUtility::loadFromYamlAndValidate(yaml, proto_config); + + testing::NiceMock context; + HeaderToMetadataFilterConfig factory; + + EXPECT_THROW_WITH_MESSAGE(factory.createFilterFactoryFromProto(proto_config, "stats", context), + EnvoyException, message); +} + +// Tests that empty (metadata) keys are rejected. +TEST(HeaderToMetadataFilterConfigTest, InvalidEmptyKey) { + const std::string yaml = R"EOF( +request_rules: + - header: x-version + on_present: + metadata_namespace: envoy.lb + key: "" + type: STRING + )EOF"; + + HeaderToMetadataProtoConfig proto_config; + EXPECT_THROW(TestUtility::loadFromYamlAndValidate(yaml, proto_config), ProtoValidationException); +} + +// Tests that a valid config with header is properly consumed. +TEST(HeaderToMetadataFilterConfigTest, SimpleConfig) { + const std::string yaml = R"EOF( +request_rules: + - header: x-version + on_present: + metadata_namespace: envoy.lb + key: version + type: STRING + on_missing: + metadata_namespace: envoy.lb + key: default + value: 'true' + type: STRING + )EOF"; + + HeaderToMetadataProtoConfig proto_config; + TestUtility::loadFromYamlAndValidate(yaml, proto_config); + + testing::NiceMock context; + HeaderToMetadataFilterConfig factory; + + auto cb = factory.createFilterFactoryFromProto(proto_config, "stats", context); + NetworkFilters::ThriftProxy::ThriftFilters::MockFilterChainFactoryCallbacks filter_callbacks; + EXPECT_CALL(filter_callbacks, addDecoderFilter(_)); + cb(filter_callbacks); +} + +// Tests that configuration does not allow value and regex_value_rewrite in the same rule. +TEST(HeaderToMetadataFilterConfigTest, ValueAndRegex) { + const std::string yaml = R"EOF( +request_rules: + - header: x-version + on_present: + metadata_namespace: envoy.lb + key: cluster + value: foo + regex_value_rewrite: + pattern: + google_re2: {} + regex: "^/(cluster[\\d\\w-]+)/?.*$" + substitution: "\\1" + )EOF"; + + HeaderToMetadataProtoConfig proto_config; + EXPECT_THROW(TestUtility::loadFromYamlAndValidate(yaml, proto_config), EnvoyException); +} + +// Tests that configuration does not allow rule without either on_present or on_missing. +TEST(HeaderToMetadataFilterConfigTest, InvalidEmptyRule) { + const std::string yaml = R"EOF( +request_rules: + - header: x-no-exist + )EOF"; + + testForbiddenConfig(yaml, "header to metadata filter: rule for header 'x-no-exist' has neither " + "`on_present` nor `on_missing` set"); +} + +// Tests that on_missing rules don't allow an empty value. +TEST(HeaderToMetadataFilterConfigTest, OnHeaderMissingEmptyValue) { + const std::string yaml = R"EOF( +request_rules: + - header: x-version + on_missing: + metadata_namespace: envoy.lb + key: "foo" + type: STRING + )EOF"; + + testForbiddenConfig(yaml, "Cannot specify on_missing rule without non-empty value"); +} + +} // namespace HeaderToMetadataFilter +} // namespace ThriftFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter_test.cc b/test/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter_test.cc new file mode 100644 index 0000000000000..587bef6389dc3 --- /dev/null +++ b/test/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter_test.cc @@ -0,0 +1,502 @@ +#include + +#include "source/common/common/base64.h" +#include "source/extensions/filters/network/thrift_proxy/filters/header_to_metadata/header_to_metadata_filter.h" + +#include "test/extensions/filters/network/thrift_proxy/mocks.h" +#include "test/mocks/network/mocks.h" +#include "test/mocks/server/mocks.h" +#include "test/mocks/stream_info/mocks.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace ThriftFilters { +namespace HeaderToMetadataFilter { + +namespace { + +MATCHER_P(MapEq, rhs, "") { + const ProtobufWkt::Struct& obj = arg; + EXPECT_TRUE(!rhs.empty()); + for (auto const& entry : rhs) { + EXPECT_EQ(obj.fields().at(entry.first).string_value(), entry.second); + } + return true; +} + +MATCHER_P(MapEqNum, rhs, "") { + const ProtobufWkt::Struct& obj = arg; + EXPECT_TRUE(!rhs.empty()); + for (auto const& entry : rhs) { + EXPECT_EQ(obj.fields().at(entry.first).number_value(), entry.second); + } + return true; +} + +MATCHER_P(MapEqValue, rhs, "") { + const ProtobufWkt::Struct& obj = arg; + EXPECT_TRUE(!rhs.empty()); + for (auto const& entry : rhs) { + EXPECT_TRUE(TestUtility::protoEqual(obj.fields().at(entry.first), entry.second)); + } + return true; +} + +} // namespace + +using namespace Envoy::Extensions::NetworkFilters; + +class HeaderToMetadataTest : public testing::Test { +public: + void initializeFilter(const std::string& yaml) { + envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata::v3:: + HeaderToMetadata proto_config; + TestUtility::loadFromYaml(yaml, proto_config); + const auto& filter_config = std::make_shared(proto_config); + filter_ = std::make_shared(filter_config); + filter_->setDecoderFilterCallbacks(decoder_callbacks_); + } + + NiceMock decoder_callbacks_; + NiceMock req_info_; + std::shared_ptr filter_; +}; + +TEST_F(HeaderToMetadataTest, BasicRequestTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-version + on_present: + metadata_namespace: envoy.lb + key: version +)EOF"; + initializeFilter(request_config_yaml); + std::map expected = {{"version", "0xdeadbeef"}}; + EXPECT_CALL(req_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-VERSION"), "0xdeadbeef"); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +TEST_F(HeaderToMetadataTest, DefaultNamespaceTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-version + on_present: + key: version +)EOF"; + initializeFilter(request_config_yaml); + std::map expected = {{"version", "0xdeadbeef"}}; + EXPECT_CALL(req_info_, + setDynamicMetadata("envoy.filters.thrift.header_to_metadata", MapEq(expected))); + + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-VERSION"), "0xdeadbeef"); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +TEST_F(HeaderToMetadataTest, ReplaceValueTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-replace + on_present: + metadata_namespace: envoy.lb + key: replace + value: world +)EOF"; + initializeFilter(request_config_yaml); + std::map expected = {{"replace", "world"}}; + EXPECT_CALL(req_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-replace"), "hello"); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +TEST_F(HeaderToMetadataTest, SubstituteValueTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-sub + on_present: + metadata_namespace: envoy.lb + key: subbed + regex_value_rewrite: + pattern: + google_re2: {} + regex: "^hello (\\w+)?.*$" + substitution: "\\1" +)EOF"; + initializeFilter(request_config_yaml); + std::map expected = {{"subbed", "world"}}; + EXPECT_CALL(req_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-sub"), "hello world!!!!!"); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +TEST_F(HeaderToMetadataTest, NoMatchSubstituteValueTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-sub + on_present: + metadata_namespace: envoy.lb + key: subbed + regex_value_rewrite: + pattern: + google_re2: {} + regex: "^hello (\\w+)?.*$" + substitution: "\\1" +)EOF"; + initializeFilter(request_config_yaml); + std::map expected = {{"subbed", "does not match"}}; + EXPECT_CALL(req_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-sub"), "does not match"); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +/** + * Test empty value doesn't get written to metadata. + */ +TEST_F(HeaderToMetadataTest, SubstituteEmptyValueTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-sub + on_present: + metadata_namespace: envoy.lb + key: subbed + regex_value_rewrite: + pattern: + google_re2: {} + regex: "^hello (\\w+)?.*$" + substitution: "\\1" +)EOF"; + initializeFilter(request_config_yaml); + EXPECT_CALL(req_info_, setDynamicMetadata(_, _)).Times(0); + + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-sub"), "hello !!!!!"); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +/** + * Test the value gets written as a number. + */ +TEST_F(HeaderToMetadataTest, NumberTypeTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-number + on_present: + metadata_namespace: envoy.lb + key: number + type: NUMBER +)EOF"; + initializeFilter(request_config_yaml); + std::map expected = {{"number", 1}}; + EXPECT_CALL(req_info_, setDynamicMetadata("envoy.lb", MapEqNum(expected))); + + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-Number"), "1"); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +/** + * Test the value gets written as a number. + */ +TEST_F(HeaderToMetadataTest, BadNumberTypeTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-number + on_present: + metadata_namespace: envoy.lb + key: number + type: NUMBER +)EOF"; + initializeFilter(request_config_yaml); + EXPECT_CALL(req_info_, setDynamicMetadata(_, _)).Times(0); + + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-Number"), "invalid"); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +/** + * Test the Base64 encoded value gets written as a string. + */ +TEST_F(HeaderToMetadataTest, StringTypeInBase64UrlTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-base64 + on_present: + metadata_namespace: envoy.lb + key: base64_key + type: STRING + encode: BASE64 +)EOF"; + initializeFilter(request_config_yaml); + std::string data = "Non-ascii-characters"; + std::map expected = {{"base64_key", data}}; + EXPECT_CALL(req_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + + const auto encoded = Base64::encode(data.c_str(), data.size()); + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-Base64"), encoded); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +/** + * Test the Base64 encoded protobuf value gets written as a protobuf value. + */ +TEST_F(HeaderToMetadataTest, ProtobufValueTypeInBase64UrlTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-proto-base64 + on_present: + metadata_namespace: envoy.lb + key: proto_key + type: PROTOBUF_VALUE + encode: BASE64 +)EOF"; + initializeFilter(request_config_yaml); + + ProtobufWkt::Value value; + auto* s = value.mutable_struct_value(); + + ProtobufWkt::Value v; + v.set_string_value("blafoo"); + (*s->mutable_fields())["k1"] = v; + v.set_number_value(2019.07); + (*s->mutable_fields())["k2"] = v; + v.set_bool_value(true); + (*s->mutable_fields())["k3"] = v; + + std::map expected = {{"proto_key", value}}; + EXPECT_CALL(req_info_, setDynamicMetadata("envoy.lb", MapEqValue(expected))); + + std::string data; + ASSERT_TRUE(value.SerializeToString(&data)); + const auto encoded = Base64::encode(data.c_str(), data.size()); + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-Proto-Base64"), encoded); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +/** + * Test bad Base64 encoding is not written. + */ +TEST_F(HeaderToMetadataTest, ProtobufValueTypeInBadBase64UrlTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-bad-base64 + on_present: + key: proto_key + type: PROTOBUF_VALUE + encode: BASE64 +)EOF"; + initializeFilter(request_config_yaml); + EXPECT_CALL(req_info_, setDynamicMetadata(_, _)).Times(0); + + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-Bad-Base64"), "invalid"); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +/** + * Test the bad protobuf value is not written. + */ +TEST_F(HeaderToMetadataTest, BadProtobufValueTypeInBase64UrlTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-bad-proto + on_present: + key: proto_key + type: PROTOBUF_VALUE + encode: BASE64 +)EOF"; + initializeFilter(request_config_yaml); + EXPECT_CALL(req_info_, setDynamicMetadata(_, _)).Times(0); + + std::string data = "invalid"; + const auto encoded = Base64::encode(data.c_str(), data.size()); + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-Bad-Proto"), encoded); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +/* + * Set configured value when header is missing. + */ +TEST_F(HeaderToMetadataTest, SetMissingValueTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-no-exist + on_missing: + metadata_namespace: envoy.lb + key: set + value: hi +)EOF"; + initializeFilter(request_config_yaml); + std::map expected = {{"set", "hi"}}; + EXPECT_CALL(req_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + + auto metadata = std::make_shared(); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +/** + * Missing case is not executed when header is present. + */ +TEST_F(HeaderToMetadataTest, NoMissingWhenHeaderIsPresent) { + const std::string config = R"EOF( +request_rules: + - header: x-exist + on_missing: + metadata_namespace: envoy.lb + key: version + value: hi +)EOF"; + initializeFilter(config); + EXPECT_CALL(req_info_, setDynamicMetadata(_, _)).Times(0); + + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-Exist"), "hello"); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +TEST_F(HeaderToMetadataTest, RemoveHeaderTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-remove + on_present: + metadata_namespace: envoy.lb + key: remove + value: hello + remove: true + - header: x-keep + on_present: + metadata_namespace: envoy.lb + key: keep + value: world +)EOF"; + initializeFilter(request_config_yaml); + std::map expected = {{"remove", "hello"}, {"keep", "world"}}; + EXPECT_CALL(req_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-REMOVE"), + "replaced in metadata then removed from headers"); + metadata->headers().setCopy(Http::LowerCaseString("X-KEEP"), "remains"); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + Http::TestRequestHeaderMapImpl headers{metadata->headers()}; + EXPECT_EQ("", headers.get_(Http::LowerCaseString("X-REMOVE"))); + EXPECT_EQ("remains", headers.get_(Http::LowerCaseString("X-KEEP"))); + filter_->onDestroy(); +} + +/** + * No header value does not set any metadata. + */ +TEST_F(HeaderToMetadataTest, EmptyHeaderValue) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-version + on_present: + metadata_namespace: envoy.lb + key: version +)EOF"; + initializeFilter(request_config_yaml); + EXPECT_CALL(req_info_, setDynamicMetadata(_, _)).Times(0); + + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-VERSION"), ""); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +/** + * Header value too long does not set header value as metadata. + */ +TEST_F(HeaderToMetadataTest, HeaderValueTooLong) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-version + on_present: + metadata_namespace: envoy.lb + key: version +)EOF"; + initializeFilter(request_config_yaml); + EXPECT_CALL(req_info_, setDynamicMetadata(_, _)).Times(0); + + auto metadata = std::make_shared(); + auto length = MAX_HEADER_VALUE_LEN + 1; + metadata->headers().setCopy(Http::LowerCaseString("X-VERSION"), std::string(length, 'x')); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +TEST_F(HeaderToMetadataTest, MultipleRulesTest) { + const std::string request_config_yaml = R"EOF( +request_rules: + - header: x-no-exist + on_missing: + metadata_namespace: envoy.lb + key: set + value: hello + - header: x-replace + on_present: + metadata_namespace: envoy.lb + key: replace + value: world +)EOF"; + initializeFilter(request_config_yaml); + std::map expected = {{"set", "hello"}, {"replace", "world"}}; + EXPECT_CALL(req_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + + auto metadata = std::make_shared(); + metadata->headers().setCopy(Http::LowerCaseString("X-REPLACE"), "should be replaced"); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(req_info_)); + EXPECT_EQ(ThriftProxy::FilterStatus::Continue, filter_->transportBegin(metadata)); + filter_->onDestroy(); +} + +} // namespace HeaderToMetadataFilter +} // namespace ThriftFilters +} // namespace Extensions +} // namespace Envoy From 8fc53a00ebc085024d88fddd93e9051413355a0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Nov 2021 21:11:31 -0700 Subject: [PATCH 043/110] build(deps): bump pyparsing from 3.0.4 to 3.0.5 in /tools/dependency (#18937) Bumps [pyparsing](https://github.com/pyparsing/pyparsing) from 3.0.4 to 3.0.5. - [Release notes](https://github.com/pyparsing/pyparsing/releases) - [Changelog](https://github.com/pyparsing/pyparsing/blob/master/CHANGES) - [Commits](https://github.com/pyparsing/pyparsing/compare/pyparsing_3.0.4...pyparsing_3.0.5) --- updated-dependencies: - dependency-name: pyparsing dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/dependency/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/dependency/requirements.txt b/tools/dependency/requirements.txt index 59c01a6605ae1..0ab51192aefe4 100644 --- a/tools/dependency/requirements.txt +++ b/tools/dependency/requirements.txt @@ -112,9 +112,9 @@ pynacl==1.4.0 \ --hash=sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff \ --hash=sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80 # via pygithub -pyparsing==3.0.4 \ - --hash=sha256:c0a7dfcd26825bd4453574c4e7ad04aa095975ce54d04f738fe3a8350fbd223a \ - --hash=sha256:e0df773d7fa2240322daae7805626dfc5f2d5effb34e1a7be2702c99cfb9f6b1 +pyparsing==3.0.5 \ + --hash=sha256:4881e3d2979f27b41a3a2421b10be9cbfa7ce2baa6c7117952222f8bbea6650c \ + --hash=sha256:9329d1c1b51f0f76371c4ded42c5ec4cc0be18456b22193e0570c2da98ed288b # via packaging pytz==2021.3 \ --hash=sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c \ From 14b16bf2377c955437c338f420f5c7fe1155a910 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 9 Nov 2021 09:03:51 -0500 Subject: [PATCH 044/110] test: moving proxy proto (#18939) Risk Level: n/a Testing: n/a Docs Changes: n/a Release Notes: n/a Part of #9953 Signed-off-by: Alyssa Wilk --- .../filters/listener/proxy_protocol/BUILD | 4 ---- .../filters/listener/proxy_protocol/BUILD | 22 +++++++++++++++++++ .../proxy_proto_integration_test.cc | 2 +- .../proxy_proto_integration_test.h | 0 test/integration/BUILD | 21 ------------------ 5 files changed, 23 insertions(+), 26 deletions(-) rename test/{integration => extensions/filters/listener/proxy_protocol}/proxy_proto_integration_test.cc (99%) rename test/{integration => extensions/filters/listener/proxy_protocol}/proxy_proto_integration_test.h (100%) diff --git a/source/extensions/filters/listener/proxy_protocol/BUILD b/source/extensions/filters/listener/proxy_protocol/BUILD index 404e111186766..eaca916f34c30 100644 --- a/source/extensions/filters/listener/proxy_protocol/BUILD +++ b/source/extensions/filters/listener/proxy_protocol/BUILD @@ -39,10 +39,6 @@ envoy_cc_library( envoy_cc_extension( name = "config", srcs = ["config.cc"], - # TODO(#9953) clean up. - extra_visibility = [ - "//test/integration:__subpackages__", - ], deps = [ "//envoy/registry", "//envoy/server:filter_config_interface", diff --git a/test/extensions/filters/listener/proxy_protocol/BUILD b/test/extensions/filters/listener/proxy_protocol/BUILD index d9149b598de1d..fc156709f7b3d 100644 --- a/test/extensions/filters/listener/proxy_protocol/BUILD +++ b/test/extensions/filters/listener/proxy_protocol/BUILD @@ -59,3 +59,25 @@ envoy_cc_fuzz_test( "//test/extensions/filters/listener/common/fuzz:listener_filter_fuzzer_lib", ], ) + +envoy_extension_cc_test( + name = "proxy_proto_integration_test", + srcs = [ + "proxy_proto_integration_test.cc", + "proxy_proto_integration_test.h", + ], + extension_names = ["envoy.filters.listener.proxy_protocol"], + deps = [ + "//source/common/buffer:buffer_lib", + "//source/common/http:codec_client_lib", + "//source/extensions/access_loggers/file:config", + "//source/extensions/filters/listener/proxy_protocol:config", + "//source/extensions/filters/network/tcp_proxy:config", + "//test/integration:http_integration_lib", + "//test/test_common:utility_lib", + "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", + "@envoy_api//envoy/config/cluster/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/access_loggers/file/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/network/tcp_proxy/v3:pkg_cc_proto", + ], +) diff --git a/test/integration/proxy_proto_integration_test.cc b/test/extensions/filters/listener/proxy_protocol/proxy_proto_integration_test.cc similarity index 99% rename from test/integration/proxy_proto_integration_test.cc rename to test/extensions/filters/listener/proxy_protocol/proxy_proto_integration_test.cc index 4d48a8aee68ed..9aaf9fee88d26 100644 --- a/test/integration/proxy_proto_integration_test.cc +++ b/test/extensions/filters/listener/proxy_protocol/proxy_proto_integration_test.cc @@ -1,4 +1,4 @@ -#include "test/integration/proxy_proto_integration_test.h" +#include "test/extensions/filters/listener/proxy_protocol/proxy_proto_integration_test.h" #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/config/cluster/v3/cluster.pb.h" diff --git a/test/integration/proxy_proto_integration_test.h b/test/extensions/filters/listener/proxy_protocol/proxy_proto_integration_test.h similarity index 100% rename from test/integration/proxy_proto_integration_test.h rename to test/extensions/filters/listener/proxy_protocol/proxy_proto_integration_test.h diff --git a/test/integration/BUILD b/test/integration/BUILD index 33a6b71217d40..de692a7345185 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -1145,27 +1145,6 @@ envoy_cc_test( ], ) -envoy_cc_test( - name = "proxy_proto_integration_test", - srcs = [ - "proxy_proto_integration_test.cc", - "proxy_proto_integration_test.h", - ], - deps = [ - ":http_integration_lib", - "//source/common/buffer:buffer_lib", - "//source/common/http:codec_client_lib", - "//source/extensions/access_loggers/file:config", - "//source/extensions/filters/listener/proxy_protocol:config", - "//source/extensions/filters/network/tcp_proxy:config", - "//test/test_common:utility_lib", - "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", - "@envoy_api//envoy/config/cluster/v3:pkg_cc_proto", - "@envoy_api//envoy/extensions/access_loggers/file/v3:pkg_cc_proto", - "@envoy_api//envoy/extensions/filters/network/tcp_proxy/v3:pkg_cc_proto", - ], -) - envoy_cc_test( name = "rtds_integration_test", srcs = ["rtds_integration_test.cc"], From f1ed59b416acd31652d4b9b8b8ff65a3790d4138 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 9 Nov 2021 09:09:33 -0500 Subject: [PATCH 045/110] quic: supporting connections with zero initial available streams (#18775) Risk Level: Medium (conn pool refactors) Testing: unit, integration Docs Changes: n/a Release Notes: n/a Signed-off-by: Alyssa Wilk --- source/common/conn_pool/conn_pool_base.cc | 8 ++- source/common/http/http3/conn_pool.cc | 3 + source/common/quic/codec_impl.h | 2 + .../common/quic/envoy_quic_client_session.cc | 15 ++--- test/common/conn_pool/conn_pool_base_test.cc | 27 +++++++++ test/integration/BUILD | 1 + test/integration/fake_upstream.cc | 16 ++++++ test/integration/fake_upstream.h | 4 ++ .../integration/quic_http_integration_test.cc | 56 +++++++++++++++++++ 9 files changed, 121 insertions(+), 11 deletions(-) diff --git a/source/common/conn_pool/conn_pool_base.cc b/source/common/conn_pool/conn_pool_base.cc index 096f27a206833..de35a059d3ad5 100644 --- a/source/common/conn_pool/conn_pool_base.cc +++ b/source/common/conn_pool/conn_pool_base.cc @@ -492,7 +492,9 @@ void ConnPoolImplBase::onConnectionEvent(ActiveClient& client, absl::string_view client.conn_connect_ms_->complete(); client.conn_connect_ms_.reset(); ASSERT(client.state() == ActiveClient::State::CONNECTING); - transitionActiveClientState(client, ActiveClient::State::READY); + bool streams_available = client.currentUnusedCapacity() > 0; + transitionActiveClientState(client, streams_available ? ActiveClient::State::READY + : ActiveClient::State::BUSY); // Now that the active client is ready, set up a timer for max connection duration. const absl::optional max_connection_duration = @@ -506,7 +508,9 @@ void ConnPoolImplBase::onConnectionEvent(ActiveClient& client, absl::string_view // At this point, for the mixed ALPN pool, the client may be deleted. Do not // refer to client after this point. onConnected(client); - onUpstreamReady(); + if (streams_available) { + onUpstreamReady(); + } checkForIdleAndCloseIdleConnsIfDraining(); } } diff --git a/source/common/http/http3/conn_pool.cc b/source/common/http/http3/conn_pool.cc index 2795587f1b5d0..059806bc002bf 100644 --- a/source/common/http/http3/conn_pool.cc +++ b/source/common/http/http3/conn_pool.cc @@ -36,6 +36,9 @@ void ActiveClient::onMaxStreamsChanged(uint32_t num_streams) { parent_.transitionActiveClientState(*this, ActiveClient::State::READY); // If there's waiting streams, make sure the pool will now serve them. parent_.onUpstreamReady(); + } else if (currentUnusedCapacity() == 0 && state() == ActiveClient::State::READY) { + // With HTTP/3 this can only happen during a rejected 0-RTT handshake. + parent_.transitionActiveClientState(*this, ActiveClient::State::BUSY); } } diff --git a/source/common/quic/codec_impl.h b/source/common/quic/codec_impl.h index b5136bb1b6313..399cf5dde889f 100644 --- a/source/common/quic/codec_impl.h +++ b/source/common/quic/codec_impl.h @@ -53,6 +53,8 @@ class QuicHttpServerConnectionImpl : public QuicHttpConnectionImplBase, void onUnderlyingConnectionAboveWriteBufferHighWatermark() override; void onUnderlyingConnectionBelowWriteBufferLowWatermark() override; + EnvoyQuicServerSession& quicServerSession() { return quic_server_session_; } + private: EnvoyQuicServerSession& quic_server_session_; }; diff --git a/source/common/quic/envoy_quic_client_session.cc b/source/common/quic/envoy_quic_client_session.cc index 0982f71c4bc49..21ceba761eef7 100644 --- a/source/common/quic/envoy_quic_client_session.cc +++ b/source/common/quic/envoy_quic_client_session.cc @@ -86,9 +86,7 @@ void EnvoyQuicClientSession::OnCanCreateNewOutgoingStream(bool unidirectional) { return; } uint32_t streams_available = streamsAvailable(); - if (streams_available > 0) { - http_connection_callbacks_->onMaxStreamsChanged(streams_available); - } + http_connection_callbacks_->onMaxStreamsChanged(streams_available); } std::unique_ptr EnvoyQuicClientSession::CreateClientStream() { @@ -131,12 +129,11 @@ uint64_t EnvoyQuicClientSession::streamsAvailable() { void EnvoyQuicClientSession::OnTlsHandshakeComplete() { quic::QuicSpdyClientSession::OnTlsHandshakeComplete(); - // TODO(alyssawilk) support the case where a connection starts with 0 max streams. - ASSERT(streamsAvailable()); - if (streamsAvailable() > 0) { - OnCanCreateNewOutgoingStream(false); - raiseConnectionEvent(Network::ConnectionEvent::Connected); - } + // Fake this to make sure we set the connection pool stream limit correctly + // before use. This may result in OnCanCreateNewOutgoingStream with zero + // available streams. + OnCanCreateNewOutgoingStream(false); + raiseConnectionEvent(Network::ConnectionEvent::Connected); } std::unique_ptr EnvoyQuicClientSession::CreateQuicCryptoStream() { diff --git a/test/common/conn_pool/conn_pool_base_test.cc b/test/common/conn_pool/conn_pool_base_test.cc index 90ec7ceb64569..ba0f4a39f883d 100644 --- a/test/common/conn_pool/conn_pool_base_test.cc +++ b/test/common/conn_pool/conn_pool_base_test.cc @@ -35,8 +35,15 @@ class TestActiveClient : public ActiveClient { ASSERT_TRUE(testClient != nullptr); testClient->active_streams_++; } + int64_t currentUnusedCapacity() const override { + if (capacity_override_.has_value()) { + return capacity_override_.value(); + } + return ActiveClient::currentUnusedCapacity(); + } uint32_t active_streams_{}; + absl::optional capacity_override_; }; class TestPendingStream : public PendingStream { @@ -417,6 +424,26 @@ TEST_F(ConnPoolImplDispatcherBaseTest, MaxConnectionDurationCallbackWhileConnect pool_.destructAllConnections(); } +// Test the behavior of a client created with 0 zero streams available. +TEST_F(ConnPoolImplDispatcherBaseTest, NoAvailableStreams) { + // Start with a concurrent stream limit of 0. + stream_limit_ = 1; + newConnectingClient(); + clients_.back()->capacity_override_ = 0; + pool_.decrClusterStreamCapacity(stream_limit_); + + // Make sure that when the connected event is raised, there is no call to + // onPoolReady, and the client is marked as busy. + EXPECT_CALL(pool_, onPoolReady).Times(0); + clients_.back()->onEvent(Network::ConnectionEvent::Connected); + EXPECT_EQ(ActiveClient::State::BUSY, clients_.back()->state()); + + // Clean up. + EXPECT_CALL(pool_, instantiateActiveClient); + EXPECT_CALL(pool_, onPoolFailure); + pool_.destructAllConnections(); +} + // Remote close simulates the peer closing the connection. TEST_F(ConnPoolImplBaseTest, PoolIdleCallbackTriggeredRemoteClose) { EXPECT_CALL(dispatcher_, createTimer_(_)).Times(AnyNumber()); diff --git a/test/integration/BUILD b/test/integration/BUILD index de692a7345185..205733f731dc2 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -740,6 +740,7 @@ envoy_cc_test_library( ] + envoy_select_enable_http3([ "//source/common/quic:active_quic_listener_lib", "//source/common/quic:quic_factory_lib", + "@com_github_google_quiche//:quic_test_tools_session_peer_lib", ]), ) diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index 2f33f3c3f519c..e72b03a52fe75 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -17,6 +17,7 @@ #ifdef ENVOY_ENABLE_QUIC #include "source/common/quic/codec_impl.h" +#include "quiche/quic/test_tools/quic_session_peer.h" #endif #include "source/server/connection_handler_impl.h" @@ -396,6 +397,21 @@ void FakeHttpConnection::encodeGoAway() { postToConnectionThread([this]() { codec_->goAway(); }); } +void FakeHttpConnection::updateConcurrentStreams(uint64_t max_streams) { + ASSERT(type_ >= Http::CodecType::HTTP3); + +#ifdef ENVOY_ENABLE_QUIC + postToConnectionThread([this, max_streams]() { + auto codec = dynamic_cast(codec_.get()); + quic::test::QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams(&codec->quicServerSession(), + max_streams); + codec->quicServerSession().SendMaxStreams(1, false); + }); +#else + UNREFERENCED_PARAMETER(max_streams); +#endif +} + void FakeHttpConnection::encodeProtocolError() { ASSERT(type_ >= Http::CodecType::HTTP2); diff --git a/test/integration/fake_upstream.h b/test/integration/fake_upstream.h index f667bcb2a2258..c282ad207ee0a 100644 --- a/test/integration/fake_upstream.h +++ b/test/integration/fake_upstream.h @@ -462,6 +462,10 @@ class FakeHttpConnection : public Http::ServerConnectionCallbacks, public FakeCo // Should only be called for HTTP2 or above, sends a GOAWAY frame with ENHANCE_YOUR_CALM. void encodeProtocolError(); + // Update the maximum number of concurrent streams. This is currently only + // supported for HTTP/3 + void updateConcurrentStreams(uint64_t max_streams); + private: struct ReadFilter : public Network::ReadFilterBaseImpl { ReadFilter(FakeHttpConnection& parent) : parent_(parent) {} diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index 2d64721013305..43f00246bd0aa 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -837,6 +837,62 @@ TEST_P(QuicHttpIntegrationTest, Http3DownstreamKeepalive) { ASSERT_TRUE(response->complete()); } +TEST_P(QuicHttpIntegrationTest, NoInitialStreams) { + // Set the fake upstream to start with 0 streams available. + setUpstreamProtocol(Http::CodecType::HTTP3); + envoy::config::listener::v3::QuicProtocolOptions options; + options.mutable_quic_protocol_options()->mutable_max_concurrent_streams()->set_value(0); + mergeOptions(options); + initialize(); + + // Create the client connection and send a request. + codec_client_ = makeRawHttpConnection(makeClientConnection(lookupPort("http")), absl::nullopt); + IntegrationStreamDecoderPtr response = + codec_client_->makeHeaderOnlyRequest(default_request_headers_); + + // There should now be an upstream connection, but no upstream stream. + ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); + ASSERT_FALSE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_, + std::chrono::milliseconds(100))); + + // Update the upstream to have 1 stream available. Now Envoy should ship the + // original request upstream. + fake_upstream_connection_->updateConcurrentStreams(1); + ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); + + // Make sure the standard request/response pipeline works as expected. + upstream_request_->encodeHeaders(default_response_headers_, true); + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); +} + +TEST_P(QuicHttpIntegrationTest, NoStreams) { + // Tighten the stream idle timeout, as it defaults to 5m + config_helper_.addConfigModifier( + [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) -> void { + hcm.mutable_stream_idle_timeout()->set_seconds(0); + hcm.mutable_stream_idle_timeout()->set_nanos(400 * 1000 * 1000); + }); + + // Set the fake upstream to start with 0 streams available. + setUpstreamProtocol(Http::CodecType::HTTP3); + envoy::config::listener::v3::QuicProtocolOptions options; + options.mutable_quic_protocol_options()->mutable_max_concurrent_streams()->set_value(0); + mergeOptions(options); + initialize(); + + // Create the client connection and send a request. + codec_client_ = makeRawHttpConnection(makeClientConnection(lookupPort("http")), absl::nullopt); + IntegrationStreamDecoderPtr response = + codec_client_->makeHeaderOnlyRequest(default_request_headers_); + + // Make sure the time out closes the stream. + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_TRUE(response->complete()); +} + class QuicInplaceLdsIntegrationTest : public QuicHttpIntegrationTest { public: void inplaceInitialize(bool add_default_filter_chain = false) { From 965df56786567f3a279404f9ae218b8c5895dc8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Nov 2021 09:23:39 -0500 Subject: [PATCH 046/110] build(deps): bump pycparser from 2.20 to 2.21 in /tools/dependency (#18936) Bumps [pycparser](https://github.com/eliben/pycparser) from 2.20 to 2.21. - [Release notes](https://github.com/eliben/pycparser/releases) - [Changelog](https://github.com/eliben/pycparser/blob/master/CHANGES) - [Commits](https://github.com/eliben/pycparser/compare/release_v2.20...release_v2.21) --- updated-dependencies: - dependency-name: pycparser dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/dependency/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/dependency/requirements.txt b/tools/dependency/requirements.txt index 0ab51192aefe4..5885d13aa20b8 100644 --- a/tools/dependency/requirements.txt +++ b/tools/dependency/requirements.txt @@ -80,9 +80,9 @@ packaging==21.2 \ --hash=sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0 \ --hash=sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966 # via -r requirements.in -pycparser==2.20 \ - --hash=sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0 \ - --hash=sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705 +pycparser==2.21 \ + --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \ + --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206 # via cffi pygithub==1.55 \ --hash=sha256:1bbfff9372047ff3f21d5cd8e07720f3dbfdaf6462fcaed9d815f528f1ba7283 \ From 5d0dee8378468893b810fec82f88f4af684553e7 Mon Sep 17 00:00:00 2001 From: phlax Date: Tue, 9 Nov 2021 14:24:12 +0000 Subject: [PATCH 047/110] deps: Bump `com_github_bazelbuild_buildtools` -> 4.2.3 (#18931) Fix #18793 Signed-off-by: Ryan Northey --- api/bazel/repository_locations.bzl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/bazel/repository_locations.bzl b/api/bazel/repository_locations.bzl index 3adc1245a8790..b861d2819ef86 100644 --- a/api/bazel/repository_locations.bzl +++ b/api/bazel/repository_locations.bzl @@ -32,9 +32,9 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Bazel build tools", project_desc = "Developer tools for working with Google's bazel buildtool.", project_url = "https://github.com/bazelbuild/buildtools", - version = "4.2.2", - sha256 = "ae34c344514e08c23e90da0e2d6cb700fcd28e80c02e23e4d5715dddcb42f7b3", - release_date = "2021-10-07", + version = "4.2.3", + sha256 = "614c84128ddb86aab4e1f25ba2e027d32fd5c6da302ae30685b9d7973b13da1b", + release_date = "2021-10-26", strip_prefix = "buildtools-{version}", urls = ["https://github.com/bazelbuild/buildtools/archive/{version}.tar.gz"], use_category = ["api"], From c7446324808304dd8c7974b26f8c5c9a2415bd99 Mon Sep 17 00:00:00 2001 From: phlax Date: Tue, 9 Nov 2021 14:24:39 +0000 Subject: [PATCH 048/110] deps: Bump `build_bazel_rules_apple` -> 0.32.0 (#18932) Fix #18839 Signed-off-by: Ryan Northey --- bazel/repository_locations.bzl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 1de14053a2e88..f1119eb359229 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -39,10 +39,10 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Apple Rules for Bazel", project_desc = "Bazel rules for Apple platforms", project_url = "https://github.com/bazelbuild/rules_apple", - version = "0.31.3", - sha256 = "0052d452af7742c8f3a4e0929763388a66403de363775db7e90adecb2ba4944b", + version = "0.32.0", + sha256 = "77e8bf6fda706f420a55874ae6ee4df0c9d95da6c7838228b26910fc82eea5a2", urls = ["https://github.com/bazelbuild/rules_apple/releases/download/{version}/rules_apple.{version}.tar.gz"], - release_date = "2021-08-08", + release_date = "2021-10-29", use_category = ["build"], ), rules_fuzzing = dict( From b8307b43cfe57ad28aca2a7b2a069467266fab49 Mon Sep 17 00:00:00 2001 From: phlax Date: Tue, 9 Nov 2021 16:29:13 +0000 Subject: [PATCH 049/110] deps: Revert pyparsing bump (#18946) This reverts the bump to pyparsing in PRs: build(deps): bump pyparsing from 3.0.4 to 3.0.5 in /tools/dependency #18937 dependabot: Python updates #18929 the pyparsing version is not compatible with the current version of packaging the issue is fixed upstream pypa/packaging#482 but the fix is not yet included in any release this issue is currently causing the github deps checker action to fail Signed-off-by: Ryan Northey --- tools/dependency/requirements.txt | 198 +++++++++++++++--------------- 1 file changed, 100 insertions(+), 98 deletions(-) diff --git a/tools/dependency/requirements.txt b/tools/dependency/requirements.txt index 5885d13aa20b8..9eb48be5c543d 100644 --- a/tools/dependency/requirements.txt +++ b/tools/dependency/requirements.txt @@ -1,68 +1,68 @@ # -# This file is autogenerated by pip-compile +# This file is autogenerated by pip-compile with python 3.9 # To update, run: # # pip-compile --allow-unsafe --generate-hashes requirements.in # certifi==2021.10.8 \ - --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 \ - --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 + --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ + --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 # via requests cffi==1.15.0 \ - --hash=sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962 \ - --hash=sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0 \ - --hash=sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14 \ - --hash=sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27 \ - --hash=sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023 \ - --hash=sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474 \ - --hash=sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6 \ - --hash=sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2 \ - --hash=sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e \ - --hash=sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7 \ --hash=sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3 \ - --hash=sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c \ - --hash=sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962 \ - --hash=sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382 \ - --hash=sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55 \ - --hash=sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0 \ - --hash=sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e \ - --hash=sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39 \ - --hash=sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc \ - --hash=sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032 \ - --hash=sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8 \ - --hash=sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605 \ - --hash=sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e \ - --hash=sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc \ + --hash=sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2 \ --hash=sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636 \ - --hash=sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4 \ - --hash=sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997 \ - --hash=sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b \ - --hash=sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2 \ - --hash=sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7 \ - --hash=sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66 \ - --hash=sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029 \ - --hash=sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880 \ --hash=sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20 \ - --hash=sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024 \ - --hash=sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e \ --hash=sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728 \ - --hash=sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6 \ - --hash=sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c \ + --hash=sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27 \ + --hash=sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66 \ --hash=sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443 \ - --hash=sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a \ + --hash=sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0 \ + --hash=sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7 \ + --hash=sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39 \ + --hash=sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605 \ + --hash=sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a \ --hash=sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37 \ + --hash=sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029 \ + --hash=sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139 \ + --hash=sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc \ + --hash=sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df \ + --hash=sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14 \ + --hash=sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880 \ + --hash=sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2 \ + --hash=sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a \ + --hash=sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e \ + --hash=sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474 \ + --hash=sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024 \ + --hash=sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8 \ + --hash=sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0 \ + --hash=sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e \ --hash=sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a \ --hash=sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e \ - --hash=sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796 \ - --hash=sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df \ + --hash=sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032 \ + --hash=sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6 \ + --hash=sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e \ + --hash=sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b \ + --hash=sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e \ + --hash=sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954 \ + --hash=sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962 \ + --hash=sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c \ + --hash=sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4 \ + --hash=sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55 \ + --hash=sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962 \ + --hash=sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023 \ + --hash=sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c \ + --hash=sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6 \ --hash=sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8 \ - --hash=sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a \ - --hash=sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139 \ - --hash=sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954 + --hash=sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382 \ + --hash=sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7 \ + --hash=sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc \ + --hash=sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997 \ + --hash=sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796 # via pynacl charset-normalizer==2.0.7 \ - --hash=sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b \ - --hash=sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0 + --hash=sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0 \ + --hash=sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b # via requests colorama==0.4.4 \ --hash=sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b \ @@ -77,8 +77,8 @@ idna==3.3 \ --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d # via requests packaging==21.2 \ - --hash=sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0 \ - --hash=sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966 + --hash=sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966 \ + --hash=sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0 # via -r requirements.in pycparser==2.21 \ --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \ @@ -89,8 +89,8 @@ pygithub==1.55 \ --hash=sha256:2caf0054ea079b71e539741ae56c5a95e073b81fa472ce222e81667381b9601b # via -r requirements.in pyjwt==2.3.0 \ - --hash=sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f \ - --hash=sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41 + --hash=sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41 \ + --hash=sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f # via pygithub pynacl==1.4.0 \ --hash=sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4 \ @@ -112,10 +112,12 @@ pynacl==1.4.0 \ --hash=sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff \ --hash=sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80 # via pygithub -pyparsing==3.0.5 \ - --hash=sha256:4881e3d2979f27b41a3a2421b10be9cbfa7ce2baa6c7117952222f8bbea6650c \ - --hash=sha256:9329d1c1b51f0f76371c4ded42c5ec4cc0be18456b22193e0570c2da98ed288b - # via packaging +pyparsing==2.4.7 \ + --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \ + --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b + # via + # -r requirements.in + # packaging pytz==2021.3 \ --hash=sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c \ --hash=sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326 @@ -129,59 +131,59 @@ six==1.16.0 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 # via pynacl urllib3==1.26.7 \ - --hash=sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844 \ - --hash=sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece + --hash=sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece \ + --hash=sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844 # via requests wrapt==1.13.3 \ - --hash=sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a \ - --hash=sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489 \ + --hash=sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179 \ + --hash=sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096 \ + --hash=sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374 \ + --hash=sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df \ + --hash=sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185 \ + --hash=sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785 \ + --hash=sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7 \ --hash=sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909 \ - --hash=sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229 \ + --hash=sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918 \ + --hash=sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33 \ + --hash=sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068 \ + --hash=sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829 \ --hash=sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af \ - --hash=sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de \ - --hash=sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb \ - --hash=sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80 \ + --hash=sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79 \ + --hash=sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce \ + --hash=sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc \ + --hash=sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36 \ + --hash=sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade \ --hash=sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca \ - --hash=sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44 \ - --hash=sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056 \ - --hash=sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785 \ - --hash=sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096 \ - --hash=sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33 \ + --hash=sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32 \ + --hash=sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125 \ + --hash=sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e \ + --hash=sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709 \ --hash=sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f \ - --hash=sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e \ - --hash=sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d \ - --hash=sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179 \ - --hash=sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3 \ - --hash=sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755 \ + --hash=sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b \ + --hash=sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb \ + --hash=sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb \ + --hash=sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489 \ + --hash=sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640 \ + --hash=sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb \ --hash=sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851 \ + --hash=sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d \ + --hash=sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44 \ --hash=sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13 \ - --hash=sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918 \ - --hash=sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade \ - --hash=sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc \ - --hash=sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf \ - --hash=sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125 \ - --hash=sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36 \ - --hash=sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10 \ - --hash=sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068 \ - --hash=sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709 \ - --hash=sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df \ --hash=sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2 \ - --hash=sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b \ - --hash=sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829 \ - --hash=sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea \ + --hash=sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb \ + --hash=sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b \ --hash=sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9 \ - --hash=sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554 \ + --hash=sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755 \ --hash=sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c \ - --hash=sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b \ - --hash=sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce \ - --hash=sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79 \ - --hash=sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb \ - --hash=sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb \ - --hash=sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32 \ - --hash=sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7 \ - --hash=sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e \ - --hash=sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640 \ - --hash=sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374 \ - --hash=sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb \ - --hash=sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185 + --hash=sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a \ + --hash=sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf \ + --hash=sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3 \ + --hash=sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229 \ + --hash=sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e \ + --hash=sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de \ + --hash=sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554 \ + --hash=sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10 \ + --hash=sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80 \ + --hash=sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056 \ + --hash=sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea # via deprecated From 1f084f9101b59dbd1d6859ccba952ba7355aad9b Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 9 Nov 2021 12:01:23 -0500 Subject: [PATCH 050/110] test: fixing a test flake (#18899) Signed-off-by: Alyssa Wilk --- test/integration/multiplexed_integration_test.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/integration/multiplexed_integration_test.cc b/test/integration/multiplexed_integration_test.cc index 98f92eb686d6b..fbde3f043ace9 100644 --- a/test/integration/multiplexed_integration_test.cc +++ b/test/integration/multiplexed_integration_test.cc @@ -1920,9 +1920,8 @@ TEST_P(MultiplexedIntegrationTest, OnLocalReply) { } } -// Disabled for coverage temporarily see #18881 -#if !defined(ENVOY_CONFIG_COVERAGE) TEST_P(MultiplexedIntegrationTest, InvalidTrailers) { + autonomous_allow_incomplete_streams_ = true; useAccessLog("%RESPONSE_CODE_DETAILS%"); autonomous_upstream_ = true; initialize(); @@ -1941,7 +1940,6 @@ TEST_P(MultiplexedIntegrationTest, InvalidTrailers) { // http2.invalid.header.field or http3.invalid_header_field EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("invalid")); } -#endif TEST_P(MultiplexedIntegrationTest, InconsistentContentLength) { useAccessLog("%RESPONSE_CODE_DETAILS%"); From 47ebcd5485ede051db0013862baff384338b48d1 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 9 Nov 2021 14:04:02 -0500 Subject: [PATCH 051/110] test: moving echo test (#18938) I did a pass at trying to move all tests using the echo filter to something else, and decided that echo filter is a reasonable filter to use for testing. We could copy paste a clone into test if we want pure separation for #9953 but I'm inclined to just remove the TODO. Risk Level: n/a (test only) Testing: n/a Docs Changes: n/a Release Notes: n/a Part of #9953 Signed-off-by: Alyssa Wilk --- source/extensions/filters/network/echo/BUILD | 2 +- test/extensions/filters/network/echo/BUILD | 27 +++++++++++++++++++ .../network/echo}/echo_integration_test.cc | 0 test/integration/BUILD | 18 ------------- 4 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 test/extensions/filters/network/echo/BUILD rename test/{integration => extensions/filters/network/echo}/echo_integration_test.cc (100%) diff --git a/source/extensions/filters/network/echo/BUILD b/source/extensions/filters/network/echo/BUILD index e0a91ea30b2c9..67db7f18a9673 100644 --- a/source/extensions/filters/network/echo/BUILD +++ b/source/extensions/filters/network/echo/BUILD @@ -28,7 +28,7 @@ envoy_cc_library( envoy_cc_extension( name = "config", srcs = ["config.cc"], - # TODO(#9953) move echo integration test to extensions. + # The echo filter is used in integration tests which don't need an upstream. extra_visibility = [ "//test/integration:__subpackages__", ], diff --git a/test/extensions/filters/network/echo/BUILD b/test/extensions/filters/network/echo/BUILD new file mode 100644 index 0000000000000..d84aeebaac91b --- /dev/null +++ b/test/extensions/filters/network/echo/BUILD @@ -0,0 +1,27 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_test( + name = "echo_integration_test", + srcs = [ + "echo_integration_test.cc", + ], + tags = [ + # Uncomment this line to run this test repeatedly in exclusive mode if not using docker-sandbox, + # or RBE, see comments in AddRemoveListener. + # "exclusive", + ], + deps = [ + "//source/extensions/filters/network/echo:config", + "//test/integration:integration_lib", + "//test/server:utility_lib", + "//test/test_common:utility_lib", + ], +) diff --git a/test/integration/echo_integration_test.cc b/test/extensions/filters/network/echo/echo_integration_test.cc similarity index 100% rename from test/integration/echo_integration_test.cc rename to test/extensions/filters/network/echo/echo_integration_test.cc diff --git a/test/integration/BUILD b/test/integration/BUILD index 205733f731dc2..d8c451a7220e5 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -1001,24 +1001,6 @@ envoy_cc_test( ], ) -envoy_cc_test( - name = "echo_integration_test", - srcs = [ - "echo_integration_test.cc", - ], - tags = [ - # Uncomment this line to run this test repeatedly in exclusive mode if not using docker-sandbox, - # or RBE, see comments in AddRemoveListener. - # "exclusive", - ], - deps = [ - ":integration_lib", - "//source/extensions/filters/network/echo:config", - "//test/server:utility_lib", - "//test/test_common:utility_lib", - ], -) - envoy_cc_test( name = "socket_interface_integration_test", srcs = ["socket_interface_integration_test.cc"], From e9203e9845611bedfabbce26ed7d72cd8a6c8c32 Mon Sep 17 00:00:00 2001 From: code Date: Wed, 10 Nov 2021 03:24:04 +0800 Subject: [PATCH 052/110] remove unnecessary file level not unimplemented hide annotation (#18924) Remove the unnecessary file level 'not-implemented-hide' annotation. I found these annotations during the development of PR #18923 . But it seems that they should not have this annotation. If this is a misunderstanding on my part, please close this PR. Risk Level: Doc Only. Testing: N/A. Docs Changes: N/A. Release Notes: N/A. Signed-off-by: wbpcode --- api/envoy/service/discovery/v2/ads.proto | 2 +- api/envoy/service/discovery/v3/ads.proto | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/envoy/service/discovery/v2/ads.proto b/api/envoy/service/discovery/v2/ads.proto index d70e0cdc8e149..e981d5502cc27 100644 --- a/api/envoy/service/discovery/v2/ads.proto +++ b/api/envoy/service/discovery/v2/ads.proto @@ -14,7 +14,7 @@ option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Aggregated Discovery Service (ADS)] -// [#not-implemented-hide:] Discovery services for endpoints, clusters, routes, +// Discovery services for endpoints, clusters, routes, // and listeners are retained in the package `envoy.api.v2` for backwards // compatibility with existing management servers. New development in discovery // services should proceed in the package `envoy.service.discovery.v2`. diff --git a/api/envoy/service/discovery/v3/ads.proto b/api/envoy/service/discovery/v3/ads.proto index 03021559ab669..b57b46e459ef0 100644 --- a/api/envoy/service/discovery/v3/ads.proto +++ b/api/envoy/service/discovery/v3/ads.proto @@ -15,7 +15,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Aggregated Discovery Service (ADS)] -// [#not-implemented-hide:] Discovery services for endpoints, clusters, routes, +// Discovery services for endpoints, clusters, routes, // and listeners are retained in the package `envoy.api.v2` for backwards // compatibility with existing management servers. New development in discovery // services should proceed in the package `envoy.service.discovery.v2`. From 2b0991af7f429071e330ffd2b740773b9021705d Mon Sep 17 00:00:00 2001 From: phlax Date: Wed, 10 Nov 2021 03:01:13 +0000 Subject: [PATCH 053/110] cvescan: Move cvescan data to yaml (#18947) Signed-off-by: Ryan Northey --- tools/dependency/BUILD | 3 ++ tools/dependency/cve.yaml | 83 +++++++++++++++++++++++++++++++ tools/dependency/cve_scan.py | 96 ++++-------------------------------- 3 files changed, 95 insertions(+), 87 deletions(-) create mode 100644 tools/dependency/cve.yaml diff --git a/tools/dependency/BUILD b/tools/dependency/BUILD index b1e0af18c2aea..c2cfa4e8e17e4 100644 --- a/tools/dependency/BUILD +++ b/tools/dependency/BUILD @@ -28,8 +28,11 @@ py_binary( "cve_scan.py", "utils.py", ], + args = ["$(location :cve.yaml)"], data = [ + ":cve.yaml", ":exports", + requirement("envoy.base.utils"), ], ) diff --git a/tools/dependency/cve.yaml b/tools/dependency/cve.yaml new file mode 100644 index 0000000000000..25640f1decd94 --- /dev/null +++ b/tools/dependency/cve.yaml @@ -0,0 +1,83 @@ + +# We only look back a few years, since we shouldn't have any ancient deps. +start_year: 2018 + +ndist_url: https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-{year}.json.gz + +# These CVEs are false positives for the match heuristics. An explanation is +# required when adding a new entry to this list as a comment. +ignore: +# Node.js issue unrelated to http-parser (napi_ API implementation). +- CVE-2020-8174 +# Node.js HTTP desync attack. Request smuggling due to CR and hyphen +# conflation in llhttp +# (https://github.com/nodejs/llhttp/commit/9d9da1d0f18599ceddd8f484df5a5ad694d23361). +# This was a result of using llparses toLowerUnsafe() for header keys. +# http-parser uses a TOKEN method that doesnt have the same issue for +# header fields. +- CVE-2020-8201 +# Node.js issue unrelated to http-parser. This is a DoS due to a lack of +# request/connection timeouts, see +# https://github.com/nodejs/node/commit/753f3b247a. +- CVE-2020-8251 +# Node.js issue unrelated to http-parser (libuv). +- CVE-2020-8252 +# Fixed via the nghttp2 1.41.0 bump in Envoy 8b6ea4. +- CVE-2020-11080 +# Node.js issue rooted in a c-ares bug. Does not appear to affect +# http-parser or our use of c-ares, c-ares has been bumped regardless. +- CVE-2020-8277 +# gRPC issue that only affects Javascript bindings. +- CVE-2020-7768 +# Node.js issue unrelated to http-parser, see +# https://github.com/mhart/StringStream/issues/7. +- CVE-2018-21270 +# These should not affect Curl 7.74.0, but we see false positives due to the +# relative release date and CPE wildcard. +- CVE-2020-8169 +- CVE-2020-8177 +- CVE-2020-8284 +# Low severity Curl issue with incorrect re-use of connections due to case +# in/sensitivity +- CVE-2021-22924 +# Node.js issue unrelated to http-parser (Node TLS). +- CVE-2020-8265 +# Node.js request smuggling. +# https://github.com/envoyproxy/envoy/pull/14686 validates that this does +# not apply to Envoy. +- CVE-2020-8287 +# Envoy is operating post Brotli 1.0.9 release, so not affected by this. +- CVE-2020-8927 +# Node.js issue unrelated to http-parser (*). +- CVE-2021-22883 +- CVE-2021-22884 +# False positive on the match heuristic, fixed in Curl 7.76.0. +- CVE-2021-22876 +- CVE-2021-22890 +# Node.js issues unrelated to http-parser. +# See https://nvd.nist.gov/vuln/detail/CVE-2021-22918 +# See https://nvd.nist.gov/vuln/detail/CVE-2021-22921 +# See https://nvd.nist.gov/vuln/detail/CVE-2021-22931 +# See https://nvd.nist.gov/vuln/detail/CVE-2021-22939 +# See https://nvd.nist.gov/vuln/detail/CVE-2021-22940 +- CVE-2021-22918 +- CVE-2021-22921 +- CVE-2021-22930 +- CVE-2021-22931 +- CVE-2021-22939 +- CVE-2021-22940 +# +# Currently, cvescan does not respect/understand versions (see #18354). +# +# The following CVEs target versions that are not currently used in the Envoy repo. +# +# libcurl +- CVE-2021-22945 +# +# kafka +- CVE-2021-38153 +# +# wasmtime +- CVE-2021-39216 +- CVE-2021-39218 +- CVE-2021-39219 diff --git a/tools/dependency/cve_scan.py b/tools/dependency/cve_scan.py index af4e70aa7cd0a..315780ae33010 100755 --- a/tools/dependency/cve_scan.py +++ b/tools/dependency/cve_scan.py @@ -12,86 +12,9 @@ import textwrap import urllib.request -import utils as dep_utils +from envoy.base import utils -# These CVEs are false positives for the match heuristics. An explanation is -# required when adding a new entry to this list as a comment. -IGNORES_CVES = set([ - # Node.js issue unrelated to http-parser (napi_ API implementation). - 'CVE-2020-8174', - # Node.js HTTP desync attack. Request smuggling due to CR and hyphen - # conflation in llhttp - # (https://github.com/nodejs/llhttp/commit/9d9da1d0f18599ceddd8f484df5a5ad694d23361). - # This was a result of using llparse's toLowerUnsafe() for header keys. - # http-parser uses a TOKEN method that doesn't have the same issue for - # header fields. - 'CVE-2020-8201', - # Node.js issue unrelated to http-parser. This is a DoS due to a lack of - # request/connection timeouts, see - # https://github.com/nodejs/node/commit/753f3b247a. - 'CVE-2020-8251', - # Node.js issue unrelated to http-parser (libuv). - 'CVE-2020-8252', - # Fixed via the nghttp2 1.41.0 bump in Envoy 8b6ea4. - 'CVE-2020-11080', - # Node.js issue rooted in a c-ares bug. Does not appear to affect - # http-parser or our use of c-ares, c-ares has been bumped regardless. - 'CVE-2020-8277', - # gRPC issue that only affects Javascript bindings. - 'CVE-2020-7768', - # Node.js issue unrelated to http-parser, see - # https://github.com/mhart/StringStream/issues/7. - 'CVE-2018-21270', - # These should not affect Curl 7.74.0, but we see false positives due to the - # relative release date and CPE wildcard. - 'CVE-2020-8169', - 'CVE-2020-8177', - 'CVE-2020-8284', - # Low severity Curl issue with incorrect re-use of connections due to case - # in/sensitivity - 'CVE-2021-22924', - # Node.js issue unrelated to http-parser (Node TLS). - 'CVE-2020-8265', - # Node.js request smuggling. - # https://github.com/envoyproxy/envoy/pull/14686 validates that this does - # not apply to Envoy. - 'CVE-2020-8287', - # Envoy is operating post Brotli 1.0.9 release, so not affected by this. - 'CVE-2020-8927', - # Node.js issue unrelated to http-parser (*). - 'CVE-2021-22883', - 'CVE-2021-22884', - # False positive on the match heuristic, fixed in Curl 7.76.0. - 'CVE-2021-22876', - 'CVE-2021-22890', - # Node.js issues unrelated to http-parser. - # See https://nvd.nist.gov/vuln/detail/CVE-2021-22918 - # See https://nvd.nist.gov/vuln/detail/CVE-2021-22921 - # See https://nvd.nist.gov/vuln/detail/CVE-2021-22931 - # See https://nvd.nist.gov/vuln/detail/CVE-2021-22939 - # See https://nvd.nist.gov/vuln/detail/CVE-2021-22940 - 'CVE-2021-22918', - 'CVE-2021-22921', - 'CVE-2021-22930', - 'CVE-2021-22931', - 'CVE-2021-22939', - 'CVE-2021-22940', - # - # Currently, cvescan does not respect/understand versions (see #18354). - # - # The following CVEs target versions that are not currently used in the Envoy repo. - # - # libcurl - "CVE-2021-22945", - # - # kafka - 'CVE-2021-38153', - # - # wasmtime - "CVE-2021-39216", - "CVE-2021-39218", - "CVE-2021-39219", -]) +import utils as dep_utils # Subset of CVE fields that are useful below. Cve = namedtuple( @@ -332,19 +255,18 @@ def cve_scan(cves, cpe_revmap, cve_allowlist, repository_locations): if __name__ == '__main__': + cve_config = utils.from_yaml(sys.argv[1]) + # Allow local overrides for NIST CVE database URLs via args. - urls = sys.argv[1:] + urls = sys.argv[2:] if not urls: - # We only look back a few years, since we shouldn't have any ancient deps. current_year = dt.datetime.now().year - scan_years = range(2018, current_year + 1) - urls = [ - f'https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-{year}.json.gz' - for year in scan_years - ] + scan_years = range(cve_config["start_year"], current_year + 1) + urls = [cve_config["ndist_url"].format(year=year) for year in scan_years] cves, cpe_revmap = download_cve_data(urls) + possible_cves, cve_deps = cve_scan( - cves, cpe_revmap, IGNORES_CVES, dep_utils.repository_locations()) + cves, cpe_revmap, cve_config["ignore"], dep_utils.repository_locations()) if possible_cves: print( '\nBased on heuristic matching with the NIST CVE database, Envoy may be vulnerable to:') From b790c7bfb5384be0f2cbc42e54d18050f64ada91 Mon Sep 17 00:00:00 2001 From: phlax Date: Wed, 10 Nov 2021 03:05:35 +0000 Subject: [PATCH 054/110] deps: Bump `com_googlesource_code_re2` -> 2021-11-01 (#18933) Signed-off-by: Ryan Northey --- bazel/repository_locations.bzl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index f1119eb359229..02ca22d05d1e5 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -909,12 +909,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "RE2", project_desc = "RE2, a regular expression library", project_url = "https://github.com/google/re2", - version = "2021-09-01", - sha256 = "42a2e1d56b5de252f5d418dc1cc0848e9e52ca22b056453988b18c6195ec7f8d", + version = "2021-11-01", + sha256 = "8c45f7fba029ab41f2a7e6545058d9eec94eef97ce70df58e92d85cfc08b4669", strip_prefix = "re2-{version}", urls = ["https://github.com/google/re2/archive/{version}.tar.gz"], use_category = ["controlplane", "dataplane_core"], - release_date = "2021-09-01", + release_date = "2021-11-01", cpe = "N/A", ), # Included to access FuzzedDataProvider.h. This is compiler agnostic but From a22f9c08b5b1146943fdeba34d92770fd18f8a68 Mon Sep 17 00:00:00 2001 From: phlax Date: Wed, 10 Nov 2021 03:06:19 +0000 Subject: [PATCH 055/110] deps: Bump `com_google_protobuf` -> 3.19.1 (#18930) Signed-off-by: Ryan Northey --- bazel/protobuf.patch | 4 ++-- bazel/repository_locations.bzl | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bazel/protobuf.patch b/bazel/protobuf.patch index a8c5e959ddbe8..6c68b2d8aedc3 100644 --- a/bazel/protobuf.patch +++ b/bazel/protobuf.patch @@ -12,13 +12,13 @@ index 1690d4219..8a7f1bf14 100644 ################################################################################ # Protobuf Runtime Library diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py -index 68087e550..0a2d63e73 100644 +index 52101b6fe..60f06aba2 100644 --- a/python/google/protobuf/__init__.py +++ b/python/google/protobuf/__init__.py @@ -31,3 +31,9 @@ # Copyright 2007 Google Inc. All Rights Reserved. - __version__ = '3.19.0' + __version__ = '3.19.1' + +if __name__ != '__main__': + try: diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 02ca22d05d1e5..a88b25bfeb2c6 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -626,12 +626,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Protocol Buffers", project_desc = "Language-neutral, platform-neutral extensible mechanism for serializing structured data", project_url = "https://developers.google.com/protocol-buffers", - version = "3.19.0", - sha256 = "7b8d3ac3d6591ce9d25f90faba80da78d0ef620fda711702367f61a40ba98429", + version = "3.19.1", + sha256 = "80631d5a18d51daa3a1336e340001ad4937e926762f21144c62d26fe2a8d71fe", strip_prefix = "protobuf-{version}", urls = ["https://github.com/protocolbuffers/protobuf/releases/download/v{version}/protobuf-all-{version}.tar.gz"], use_category = ["dataplane_core", "controlplane"], - release_date = "2021-10-20", + release_date = "2021-10-29", cpe = "cpe:2.3:a:google:protobuf:*", ), grpc_httpjson_transcoding = dict( From 493fa15f98865ddb4a196c068235e2bdb3b34f0f Mon Sep 17 00:00:00 2001 From: tkovacs-2 <70200421+tkovacs-2@users.noreply.github.com> Date: Wed, 10 Nov 2021 11:07:56 +0800 Subject: [PATCH 056/110] Test for FilterConfigPerRoute dtor called on worker thread. (#18927) Signed-off-by: Tamas Kovacs --- .../filters/http/lua/lua_filter_test.cc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/extensions/filters/http/lua/lua_filter_test.cc b/test/extensions/filters/http/lua/lua_filter_test.cc index ce401a0c5cd72..9c34c4e081dee 100644 --- a/test/extensions/filters/http/lua/lua_filter_test.cc +++ b/test/extensions/filters/http/lua/lua_filter_test.cc @@ -2449,6 +2449,22 @@ TEST_F(LuaHttpFilterTest, LogTableInsteadOfString) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true)); } +TEST_F(LuaHttpFilterTest, DestructFilterConfigPerRoute) { + setupFilter(); + envoy::extensions::filters::http::lua::v3::LuaPerRoute proto; + proto.mutable_source_code()->set_inline_string(HEADER_ONLY_SCRIPT); + per_route_config_ = std::make_shared(proto, server_factory_context_); + + InSequence s; + EXPECT_CALL(server_factory_context_.dispatcher_, isThreadSafe()).WillOnce(Return(false)); + EXPECT_CALL(server_factory_context_.dispatcher_, post(_)); + EXPECT_CALL(server_factory_context_.dispatcher_, isThreadSafe()).WillOnce(Return(true)); + EXPECT_CALL(server_factory_context_.dispatcher_, post(_)).Times(0); + + per_route_config_ = std::make_shared(proto, server_factory_context_); + per_route_config_.reset(); +} + } // namespace } // namespace Lua } // namespace HttpFilters From e660cb8048155b1e0ae994d946906b6842b8a766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Luk=C5=A1a?= Date: Wed, 10 Nov 2021 04:25:23 +0100 Subject: [PATCH 057/110] tls: unit test: spiffe signed by intermediate cert (#18914) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marko Lukša --- .../spiffe/spiffe_validator_test.cc | 31 +++++++++++++++++++ .../transport_sockets/tls/test_data/certs.sh | 5 +++ ...spiffe_san_signed_by_intermediate_cert.pem | 26 ++++++++++++++++ ...ffe_san_signed_by_intermediate_cert_info.h | 12 +++++++ .../spiffe_san_signed_by_intermediate_key.pem | 27 ++++++++++++++++ 5 files changed, 101 insertions(+) create mode 100644 test/extensions/transport_sockets/tls/test_data/spiffe_san_signed_by_intermediate_cert.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/spiffe_san_signed_by_intermediate_cert_info.h create mode 100644 test/extensions/transport_sockets/tls/test_data/spiffe_san_signed_by_intermediate_key.pem diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc index cf77bdf3737a8..07206e156a72b 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc @@ -374,6 +374,37 @@ name: envoy.tls.cert_validator.spiffe } } +TEST_F(TestSPIFFEValidator, TestDoVerifyCertChainIntermediateCerts) { + initialize(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: example.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + )EOF")); + + X509StorePtr ssl_ctx = X509_STORE_new(); + + // Chain contains workload, intermediate, and ca cert, so it should be accepted. + auto cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/" + "spiffe_san_signed_by_intermediate_cert.pem")); + auto intermediate_ca_cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/" + "intermediate_ca_cert.pem")); + + STACK_OF(X509)* intermediates = sk_X509_new_null(); + sk_X509_push(intermediates, intermediate_ca_cert.release()); + + X509StoreContextPtr store_ctx = X509_STORE_CTX_new(); + EXPECT_TRUE(X509_STORE_CTX_init(store_ctx.get(), ssl_ctx.get(), cert.get(), intermediates)); + EXPECT_TRUE(validator().doVerifyCertChain(store_ctx.get(), nullptr, *cert, nullptr)); + + sk_X509_pop_free(intermediates, X509_free); +} + void addIA5StringGenNameExt(X509* cert, int type, const std::string name) { GeneralNamesPtr gens = sk_GENERAL_NAME_new_null(); GENERAL_NAME* gen = GENERAL_NAME_new(); // ownership taken by "gens" diff --git a/test/extensions/transport_sockets/tls/test_data/certs.sh b/test/extensions/transport_sockets/tls/test_data/certs.sh index 4afb702255f75..fc45baa58ca39 100755 --- a/test/extensions/transport_sockets/tls/test_data/certs.sh +++ b/test/extensions/transport_sockets/tls/test_data/certs.sh @@ -277,3 +277,8 @@ cp -f spiffe_san_cert.cfg expired_spiffe_san_cert.cfg generate_rsa_key expired_spiffe_san generate_x509_cert expired_spiffe_san ca -365 rm -f expired_spiffe_san_cert.cfg + +cp -f spiffe_san_cert.cfg spiffe_san_signed_by_intermediate_cert.cfg +generate_rsa_key spiffe_san_signed_by_intermediate +generate_x509_cert spiffe_san_signed_by_intermediate intermediate_ca +rm -f spiffe_san_signed_by_intermediate_cert.cfg diff --git a/test/extensions/transport_sockets/tls/test_data/spiffe_san_signed_by_intermediate_cert.pem b/test/extensions/transport_sockets/tls/test_data/spiffe_san_signed_by_intermediate_cert.pem new file mode 100644 index 0000000000000..81a786bc61aa6 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/spiffe_san_signed_by_intermediate_cert.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEUjCCAzqgAwIBAgIUTXzlcveB7pdkyzbQUqaTsAROFXswDQYJKoZIhvcNAQEL +BQAwgYMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKDARMeWZ0MRkwFwYDVQQLDBBMeWZ0IEVu +Z2luZWVyaW5nMR0wGwYDVQQDDBRUZXN0IEludGVybWVkaWF0ZSBDQTAeFw0yMTEx +MDUxNDQxNDlaFw0yMzExMDUxNDQxNDlaMHoxCzAJBgNVBAYTAlVTMRMwEQYDVQQI +DApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKDARM +eWZ0MRkwFwYDVQQLDBBMeWZ0IEVuZ2luZWVyaW5nMRQwEgYDVQQDDAtUZXN0IFNl +cnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMUT5l1GdPh2XJD6 +xnr4FAJi0krqtnbSGk9DdtCCckpRXJrs8qXU1ksQG1FTHRlfbKhOs9LVqQSj8jQu +haeG+M7Lr4gT2twZCOAo/mzCfvUGGWghtZDZj9ksbpE2Y1BxawpzOpjAjrQ7nNIw +BDTxBv0ySOvJnfx6CnUQAwjj6ovtqWLHfmeSYiQMQLfHWFZiMh0GGUkyf1tm2INS +cI1LQX4XfLb4u99m4mw1OOILbrF5PQWxHSg94jxFUMBmB7B+C87T7qZdfzZAOxJ6 +weeQ/6B0V3K7+XIZPG12FfVevX2PvTJq801me9Eto0e1rcdP05ckYFTyXkPSLorg +owvbDNMCAwEAAaOBxTCBwjAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIF4DAdBgNV +HSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwRgYDVR0RBD8wPYIJZW52b3kuY29t +hh1zcGlmZmU6Ly9leGFtcGxlLmNvbS93b3JrbG9hZIERZW52b3lAZXhhbXBsZS5j +b20wHQYDVR0OBBYEFMo38WSUt+hgmycQhiFjugeebBApMB8GA1UdIwQYMBaAFKbQ +dxTWui6GjBedEeOPwmCUdTCwMA0GCSqGSIb3DQEBCwUAA4IBAQB/no6yxvq/joiE +JYFQH7eDIpF6HB30SqMAYQMi4QQ6dP7FOmiHa1jV7NM/+iNq71/H5AFg+h1veVT/ +gAcg2hIuL6wk16MUqEzHng8nLI6Vy1pAHOE6YlFCOI5jgTkm9gfWWmGDQl4+7TZ1 +NRpfaogAsSxCTFnauR9Lau6HoOQEUknv1yERcB3c8JsjRGT5SQrpiVOxbXts2gTL +lnYWogZeNzchEeq0tgiljG/hSdrGar/irfU3LMLSP4i1H1kvdZQ+Htdt0OXh4H4A +cdXEN6ltFeN7DQbiXHNTjqbwZYGXcjYcFjoTNaAIDNVweYipUMQcMMC1ufhgcth1 +k00XU3J9 +-----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/spiffe_san_signed_by_intermediate_cert_info.h b/test/extensions/transport_sockets/tls/test_data/spiffe_san_signed_by_intermediate_cert_info.h new file mode 100644 index 0000000000000..7d9a92c283a5c --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/spiffe_san_signed_by_intermediate_cert_info.h @@ -0,0 +1,12 @@ +// NOLINT(namespace-envoy) +constexpr char TEST_SPIFFE_SAN_SIGNED_BY_INTERMEDIATE_CERT_256_HASH[] = + "dbe6287d60a13301a0029545571416209be7d07d9a3b7a024e0e50c62dc9c196"; +constexpr char TEST_SPIFFE_SAN_SIGNED_BY_INTERMEDIATE_CERT_1_HASH[] = + "301c86cf68eae1fed88dff935d5425a33acac6cd"; +constexpr char TEST_SPIFFE_SAN_SIGNED_BY_INTERMEDIATE_CERT_SPKI[] = + "7HyQL+bBrylQPcFkicayv3jTPp6DEnZzQfpvxchaQMA="; +constexpr char TEST_SPIFFE_SAN_SIGNED_BY_INTERMEDIATE_CERT_SERIAL[] = + "4d7ce572f781ee9764cb36d052a693b0044e157b"; +constexpr char TEST_SPIFFE_SAN_SIGNED_BY_INTERMEDIATE_CERT_NOT_BEFORE[] = + "Nov 5 14:41:49 2021 GMT"; +constexpr char TEST_SPIFFE_SAN_SIGNED_BY_INTERMEDIATE_CERT_NOT_AFTER[] = "Nov 5 14:41:49 2023 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/spiffe_san_signed_by_intermediate_key.pem b/test/extensions/transport_sockets/tls/test_data/spiffe_san_signed_by_intermediate_key.pem new file mode 100644 index 0000000000000..ad5b35d7c7ab7 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/spiffe_san_signed_by_intermediate_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAxRPmXUZ0+HZckPrGevgUAmLSSuq2dtIaT0N20IJySlFcmuzy +pdTWSxAbUVMdGV9sqE6z0tWpBKPyNC6Fp4b4zsuviBPa3BkI4Cj+bMJ+9QYZaCG1 +kNmP2SxukTZjUHFrCnM6mMCOtDuc0jAENPEG/TJI68md/HoKdRADCOPqi+2pYsd+ +Z5JiJAxAt8dYVmIyHQYZSTJ/W2bYg1JwjUtBfhd8tvi732bibDU44gtusXk9BbEd +KD3iPEVQwGYHsH4LztPupl1/NkA7EnrB55D/oHRXcrv5chk8bXYV9V69fY+9Mmrz +TWZ70S2jR7Wtx0/TlyRgVPJeQ9IuiuCjC9sM0wIDAQABAoIBACUWOprBAJAlTgQm +fSV0++b7C9H3W4D+xt61tm1ErxdXOlMZVgxpAi68CDgEqQw2Te9aaDK77IOoCpNR +UeuV1cqswAqemegjee0dKcvzygp4LF3RQibRGmXnG6OOFaB0x4z+5D8MtY4rTbas +PI5t8T/Cr8BXf7icis0+xyNsKJ5ONPucYUMYel2AeuLDx0Hkg3CIptyy06Ai7rBX +SqTzoXZ3t+TBwFQvPZj8P8QDtBJiAg7TUFPJfbaYx8lsU8cvWTeCBIytphCh2YB5 +ILVdJXvql4dlET0W8pFEi29dhRkl0yGIcyJqYtkzCuKf4HuyJ1PahR+nHUXN/b+d +GOefGgECgYEA/6WkoQeulNsSXlpUP6osLGlC2d99O4rPECe0HFRxCKVvtBLF59x/ +XL0V+DK+hSp2D9grYIJ2sMzCz88+I+SLRu8umeahXQz2bAgJUBz13sREhHzaKEgy +/9pfO2CFB+YxxqG9UHaSMMntOEUbHFYuoIkxPGJMWsIe7VieQLfAkdMCgYEAxVmO +SVghy2A07kZP6YonLIR0cdiPAJ/H5tg+7bz3kHX9+zzlkVHFNwJXYrim+fckS8cO +e6Iw27S+9jd1X2qn+NpZC/WYlNxCeofzF0eBebiLf/kWTJVbK3H/8aUKxR/pCAkP +A/x45KQd8Dd/lNWPpgzFD0UcxJJ3dk0/PS3DuQECgYAn8rlsFGg6iJUxO0pI/I2U +jwpMQ3ktUb6TlrC1cJiNMlTnPbvBRJp+YmnJdByDcKQsS6pTlW94pzaWBJuAPllp +Rzzv/bMfeEQVk5fo9e2R1veiAGSSwN1/T59sBuQi3NzQXjvYE/86MoOoNFxNLEZy +/Z09A1tNH2J30k5AbLZh0wKBgQCvQxtj84r/rM8VFQh/JRwpIvCu8l39dej4D+/C +/mD1wHPwnWJbLj1w3vlwSQCxWVS4n20zSxUM6XX1/8aTGItYK8GNJ218Nigr3XR7 +phtMWCI7YqD1Hmc7LCDbH3FzIyW25ySYq61JkJ6t6Pu61/acxxZyuzQTNug0/eE9 +mdkKAQKBgQCGS8VAgnFfgOSRUH2gk+tbedyGSwqDZIDfC0Ky+vZQuW7NAeBh7xh0 +46IoCsL2rAdEOq9GrjFrWwb29U3MVVJnNb0BKC4UsIuelNFTuULHGKFq8uG4pTp4 +Lc8UKDyavNZ8IQV8LLCsbAYnWGjjQAXtnBano+syQsJuYs2k68wrow== +-----END RSA PRIVATE KEY----- From 8a6388df7a4ca2e5b55c2bcf5134675ab7fb77a1 Mon Sep 17 00:00:00 2001 From: theidexisted Date: Wed, 10 Nov 2021 11:27:58 +0800 Subject: [PATCH 058/110] rocketmq_proxy: Improvement for map find (#18909) Signed-off-by: theidexisted --- contrib/rocketmq_proxy/filters/network/source/conn_manager.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/rocketmq_proxy/filters/network/source/conn_manager.cc b/contrib/rocketmq_proxy/filters/network/source/conn_manager.cc index 7dfb039736436..f19ae099f64dd 100644 --- a/contrib/rocketmq_proxy/filters/network/source/conn_manager.cc +++ b/contrib/rocketmq_proxy/filters/network/source/conn_manager.cc @@ -198,7 +198,7 @@ void ConnectionManager::onHeartbeat(RemotingCommandPtr request) { void ConnectionManager::addOrUpdateGroupMember(absl::string_view group, absl::string_view client_id) { ENVOY_LOG(trace, "#addOrUpdateGroupMember. Group: {}, client ID: {}", group, client_id); - auto search = group_members_.find(std::string(group.data(), group.length())); + auto search = group_members_.find(group); if (search == group_members_.end()) { std::vector members; members.emplace_back(ConsumerGroupMember(client_id, *this)); From 0458877b46f9bcb96f843331e2515f7aaf6bb800 Mon Sep 17 00:00:00 2001 From: Snow Pettersen Date: Tue, 9 Nov 2021 22:29:07 -0500 Subject: [PATCH 059/110] bazel: add repository arg to benchmark_test (#18795) Signed-off-by: Snow Pettersen --- bazel/envoy_test.bzl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bazel/envoy_test.bzl b/bazel/envoy_test.bzl index 0cd48ba286200..4fa57c80843f9 100644 --- a/bazel/envoy_test.bzl +++ b/bazel/envoy_test.bzl @@ -258,10 +258,11 @@ def envoy_benchmark_test( benchmark_binary, data = [], tags = [], + repository = "", **kargs): native.sh_test( name = name, - srcs = ["//bazel:test_for_benchmark_wrapper.sh"], + srcs = [repository + "//bazel:test_for_benchmark_wrapper.sh"], data = [":" + benchmark_binary] + data, args = ["%s/%s" % (native.package_name(), benchmark_binary)], tags = tags + ["nocoverage"], From 6df4b00211a667396752e9c90d6a89860011c319 Mon Sep 17 00:00:00 2001 From: Rei Shimizu Date: Wed, 10 Nov 2021 16:11:45 +0900 Subject: [PATCH 060/110] grpc: implement BufferedAsyncClient for bidirectional gRPC stream (#18129) Commit Message: grpc: implement BufferedAsyncClient for bidirectional gRPC stream Additional Description: This function is a component for buffering and tracking the state of messages sent in the gRPC bidirectional stream. This component makes it possible to monitor and control the transmission status of highly granular messages from the Envoy side, and is identical to the one implemented in https://github.com/envoyproxy/envoy/pull/17486. Risk Level: Low Testing: Unit Docs Changes: Release Notes: Platform Specific Features: Signed-off-by: Shikugawa --- source/common/grpc/BUILD | 9 ++ source/common/grpc/buffered_async_client.h | 128 ++++++++++++++++ test/common/grpc/BUILD | 15 ++ .../common/grpc/buffered_async_client_test.cc | 141 ++++++++++++++++++ 4 files changed, 293 insertions(+) create mode 100644 source/common/grpc/buffered_async_client.h create mode 100644 test/common/grpc/buffered_async_client_test.cc diff --git a/source/common/grpc/BUILD b/source/common/grpc/BUILD index 8e3cc89fd7e77..6729a434b2466 100644 --- a/source/common/grpc/BUILD +++ b/source/common/grpc/BUILD @@ -206,3 +206,12 @@ envoy_cc_library( "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], ) + +envoy_cc_library( + name = "buffered_async_client_lib", + hdrs = ["buffered_async_client.h"], + deps = [ + ":typed_async_client_lib", + "//source/common/protobuf:utility_lib", + ], +) diff --git a/source/common/grpc/buffered_async_client.h b/source/common/grpc/buffered_async_client.h new file mode 100644 index 0000000000000..7c04673429429 --- /dev/null +++ b/source/common/grpc/buffered_async_client.h @@ -0,0 +1,128 @@ +#pragma once + +#include + +#include "source/common/grpc/typed_async_client.h" +#include "source/common/protobuf/utility.h" + +#include "absl/container/btree_map.h" + +namespace Envoy { +namespace Grpc { + +enum class BufferState { Buffered, PendingFlush }; + +// This class wraps bidirectional gRPC and provides message arrival guarantee. +// It stores messages to be sent or in the process of being sent in a buffer, +// and can track the status of the message based on the ID assigned to each message. +// If a message fails to be sent, it can be re-buffered to guarantee its arrival. +template class BufferedAsyncClient { +public: + BufferedAsyncClient(uint32_t max_buffer_bytes, const Protobuf::MethodDescriptor& service_method, + Grpc::AsyncStreamCallbacks& callbacks, + const Grpc::AsyncClient& client) + : max_buffer_bytes_(max_buffer_bytes), service_method_(service_method), callbacks_(callbacks), + client_(client) {} + + ~BufferedAsyncClient() { + if (active_stream_ != nullptr) { + active_stream_ = nullptr; + } + } + + // It push message into internal message buffer. + // If the buffer is full, it will return absl::nullopt. + absl::optional bufferMessage(RequestType& message) { + const auto buffer_size = message.ByteSizeLong(); + if (current_buffer_bytes_ + buffer_size > max_buffer_bytes_) { + return absl::nullopt; + } + + auto id = publishId(); + message_buffer_[id] = std::make_pair(BufferState::Buffered, message); + current_buffer_bytes_ += buffer_size; + return id; + } + + absl::flat_hash_set sendBufferedMessages() { + if (active_stream_ == nullptr) { + active_stream_ = + client_.start(service_method_, callbacks_, Http::AsyncClient::StreamOptions()); + } + + if (active_stream_->isAboveWriteBufferHighWatermark()) { + return {}; + } + + absl::flat_hash_set inflight_message_ids; + + for (auto&& it : message_buffer_) { + const auto id = it.first; + auto& state = it.second.first; + auto& message = it.second.second; + + if (state == BufferState::PendingFlush) { + continue; + } + + state = BufferState::PendingFlush; + inflight_message_ids.emplace(id); + active_stream_->sendMessage(message, false); + } + + return inflight_message_ids; + } + + void onSuccess(uint64_t message_id) { erasePendingMessage(message_id); } + + void onError(uint64_t message_id) { + if (message_buffer_.find(message_id) == message_buffer_.end()) { + return; + } + + message_buffer_.at(message_id).first = BufferState::Buffered; + } + + bool hasActiveStream() { return active_stream_ != nullptr; } + + const absl::btree_map>& messageBuffer() { + return message_buffer_; + } + +private: + void erasePendingMessage(uint64_t message_id) { + // This case will be considered if `onSuccess` had called with unknown message id that is not + // received by envoy as response. + if (message_buffer_.find(message_id) == message_buffer_.end()) { + return; + } + auto& buffer = message_buffer_.at(message_id); + + // There may be cases where the buffer status is not PendingFlush when + // this function is called. For example, a message_buffer that was + // PendingFlush may become Buffered due to an external state change + // (e.g. re-buffering due to timeout). + if (buffer.first == BufferState::PendingFlush) { + const auto buffer_size = buffer.second.ByteSizeLong(); + current_buffer_bytes_ -= buffer_size; + message_buffer_.erase(message_id); + } + } + + uint64_t publishId() { return next_message_id_++; } + + const uint32_t max_buffer_bytes_ = 0; + const Protobuf::MethodDescriptor& service_method_; + Grpc::AsyncStreamCallbacks& callbacks_; + Grpc::AsyncClient client_; + Grpc::AsyncStream active_stream_; + absl::btree_map> message_buffer_; + uint32_t current_buffer_bytes_ = 0; + uint64_t next_message_id_ = 0; +}; + +template +using BufferedAsyncClientPtr = std::unique_ptr>; + +} // namespace Grpc +} // namespace Envoy diff --git a/test/common/grpc/BUILD b/test/common/grpc/BUILD index c4e37301fa708..ca9a7a068b666 100644 --- a/test/common/grpc/BUILD +++ b/test/common/grpc/BUILD @@ -186,3 +186,18 @@ envoy_cc_test_library( "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], ) + +envoy_cc_test( + name = "buffered_async_client_test", + srcs = ["buffered_async_client_test.cc"], + deps = [ + "//source/common/grpc:async_client_lib", + "//source/common/grpc:buffered_async_client_lib", + "//test/mocks/http:http_mocks", + "//test/mocks/tracing:tracing_mocks", + "//test/mocks/upstream:cluster_manager_mocks", + "//test/proto:helloworld_proto_cc_proto", + "//test/test_common:test_time_lib", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + ], +) diff --git a/test/common/grpc/buffered_async_client_test.cc b/test/common/grpc/buffered_async_client_test.cc new file mode 100644 index 0000000000000..fc025f6532a00 --- /dev/null +++ b/test/common/grpc/buffered_async_client_test.cc @@ -0,0 +1,141 @@ +#include "envoy/config/core/v3/grpc_service.pb.h" + +#include "source/common/grpc/async_client_impl.h" +#include "source/common/grpc/buffered_async_client.h" +#include "source/common/network/address_impl.h" +#include "source/common/network/socket_impl.h" + +#include "test/mocks/http/mocks.h" +#include "test/mocks/tracing/mocks.h" +#include "test/mocks/upstream/cluster_manager.h" +#include "test/proto/helloworld.pb.h" +#include "test/test_common/test_time.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using testing::_; +using testing::NiceMock; +using testing::Return; +using testing::ReturnRef; + +namespace Envoy { +namespace Grpc { +namespace { + +class BufferedAsyncClientTest : public testing::Test { +public: + BufferedAsyncClientTest() + : method_descriptor_(helloworld::Greeter::descriptor()->FindMethodByName("SayHello")) { + config_.mutable_envoy_grpc()->set_cluster_name("test_cluster"); + + cm_.initializeThreadLocalClusters({"test_cluster"}); + ON_CALL(cm_.thread_local_cluster_, httpAsyncClient()).WillByDefault(ReturnRef(http_client_)); + } + + const Protobuf::MethodDescriptor* method_descriptor_; + envoy::config::core::v3::GrpcService config_; + NiceMock cm_; + NiceMock http_client_; +}; + +TEST_F(BufferedAsyncClientTest, BasicSendFlow) { + Http::MockAsyncClientStream http_stream; + EXPECT_CALL(http_client_, start(_, _)).WillOnce(Return(&http_stream)); + EXPECT_CALL(http_stream, sendHeaders(_, _)); + EXPECT_CALL(http_stream, isAboveWriteBufferHighWatermark()).WillOnce(Return(false)); + EXPECT_CALL(http_stream, sendData(_, _)); + EXPECT_CALL(http_stream, reset()); + + DangerousDeprecatedTestTime test_time_; + auto raw_client = std::make_shared(cm_, config_, test_time_.timeSystem()); + AsyncClient client(raw_client); + + NiceMock> callback; + BufferedAsyncClient buffered_client( + 100000, *method_descriptor_, callback, client); + + helloworld::HelloRequest request; + request.set_name("Alice"); + EXPECT_EQ(0, buffered_client.bufferMessage(request).value()); + const auto inflight_message_ids = buffered_client.sendBufferedMessages(); + EXPECT_TRUE(buffered_client.hasActiveStream()); + EXPECT_EQ(1, inflight_message_ids.size()); + + // Pending messages should not be re-sent. + EXPECT_CALL(http_stream, isAboveWriteBufferHighWatermark()).WillOnce(Return(false)); + const auto inflight_message_ids2 = buffered_client.sendBufferedMessages(); + EXPECT_EQ(0, inflight_message_ids2.size()); + + // Re-buffer, and transport. + buffered_client.onError(*inflight_message_ids.begin()); + + EXPECT_CALL(http_stream, sendData(_, _)).Times(2); + EXPECT_CALL(http_stream, isAboveWriteBufferHighWatermark()).WillOnce(Return(false)); + + helloworld::HelloRequest request2; + request2.set_name("Bob"); + EXPECT_EQ(1, buffered_client.bufferMessage(request2).value()); + auto ids2 = buffered_client.sendBufferedMessages(); + EXPECT_EQ(2, ids2.size()); + + // Clear existing messages. + for (auto&& id : ids2) { + buffered_client.onSuccess(id); + } + + // Successfully cleared pending messages. + EXPECT_CALL(http_stream, isAboveWriteBufferHighWatermark()).WillOnce(Return(false)); + auto ids3 = buffered_client.sendBufferedMessages(); + EXPECT_EQ(0, ids3.size()); +} + +TEST_F(BufferedAsyncClientTest, BufferLimitExceeded) { + Http::MockAsyncClientStream http_stream; + EXPECT_CALL(http_client_, start(_, _)).WillOnce(Return(&http_stream)); + EXPECT_CALL(http_stream, sendHeaders(_, _)); + EXPECT_CALL(http_stream, isAboveWriteBufferHighWatermark()).WillOnce(Return(false)); + EXPECT_CALL(http_stream, reset()); + + DangerousDeprecatedTestTime test_time_; + auto raw_client = std::make_shared(cm_, config_, test_time_.timeSystem()); + AsyncClient client(raw_client); + + NiceMock> callback; + BufferedAsyncClient buffered_client( + 0, *method_descriptor_, callback, client); + + helloworld::HelloRequest request; + request.set_name("Alice"); + EXPECT_EQ(absl::nullopt, buffered_client.bufferMessage(request)); + + EXPECT_EQ(0, buffered_client.sendBufferedMessages().size()); + EXPECT_TRUE(buffered_client.hasActiveStream()); +} + +TEST_F(BufferedAsyncClientTest, BufferHighWatermarkTest) { + Http::MockAsyncClientStream http_stream; + EXPECT_CALL(http_client_, start(_, _)).WillOnce(Return(&http_stream)); + EXPECT_CALL(http_stream, sendHeaders(_, _)); + EXPECT_CALL(http_stream, isAboveWriteBufferHighWatermark()).WillOnce(Return(true)); + EXPECT_CALL(http_stream, reset()); + + DangerousDeprecatedTestTime test_time_; + auto raw_client = std::make_shared(cm_, config_, test_time_.timeSystem()); + AsyncClient client(raw_client); + + NiceMock> callback; + BufferedAsyncClient buffered_client( + 100000, *method_descriptor_, callback, client); + + helloworld::HelloRequest request; + request.set_name("Alice"); + EXPECT_EQ(0, buffered_client.bufferMessage(request).value()); + + EXPECT_EQ(0, buffered_client.sendBufferedMessages().size()); + EXPECT_TRUE(buffered_client.hasActiveStream()); +} + +} // namespace +} // namespace Grpc +} // namespace Envoy From d9c392766b4de503f009439141e1d81071484482 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 10 Nov 2021 11:29:52 -0500 Subject: [PATCH 061/110] http: switching from 100 to 1xx (#18904) Changing envoy APIs to reflect upcoming 102 support. This should be a functional no-op other than making every filter change their API calls :-/ Risk Level: Medium Testing: n/a Docs Changes: n/a Release Notes: n/a Part of #18844 Signed-off-by: Alyssa Wilk --- envoy/http/codec.h | 14 ++-- envoy/http/filter.h | 21 +++-- source/common/http/async_client_impl.h | 5 +- source/common/http/codec_wrappers.h | 4 +- source/common/http/conn_manager_impl.cc | 7 +- source/common/http/conn_manager_impl.h | 2 +- source/common/http/filter_manager.cc | 57 +++++++------ source/common/http/filter_manager.h | 40 ++++----- source/common/http/header_utility.cc | 4 + source/common/http/header_utility.h | 5 ++ source/common/http/http1/codec_impl.cc | 8 +- source/common/http/http1/codec_impl.h | 2 +- source/common/http/http2/codec_impl.cc | 6 +- source/common/http/http2/codec_impl.h | 2 +- .../common/quic/envoy_quic_client_stream.cc | 9 +- source/common/quic/envoy_quic_client_stream.h | 2 +- .../common/quic/envoy_quic_server_stream.cc | 4 +- source/common/quic/envoy_quic_server_stream.h | 2 +- source/common/router/router.cc | 16 ++-- source/common/router/router.h | 16 ++-- source/common/router/upstream_request.cc | 10 +-- source/common/router/upstream_request.h | 2 +- source/common/tcp_proxy/upstream.h | 2 +- source/common/upstream/health_checker_impl.h | 4 +- source/docs/h2_metadata.md | 2 +- source/extensions/common/wasm/context.cc | 2 +- source/extensions/common/wasm/context.h | 2 +- .../http/bandwidth_limit/bandwidth_limit.h | 2 +- .../filters/http/common/pass_through_filter.h | 2 +- .../filters/http/composite/filter.cc | 8 +- .../filters/http/composite/filter.h | 4 +- .../filters/http/cors/cors_filter.h | 2 +- .../filters/http/dynamo/dynamo_filter.h | 2 +- .../filters/http/ext_authz/ext_authz.cc | 2 +- .../filters/http/ext_authz/ext_authz.h | 2 +- .../filters/http/fault/fault_filter.h | 2 +- .../grpc_http1_bridge/http1_bridge_filter.h | 2 +- .../json_transcoder_filter.h | 2 +- .../filters/http/grpc_web/grpc_web_filter.h | 2 +- .../header_to_metadata_filter.h | 2 +- .../filters/http/health_check/health_check.h | 2 +- .../http/kill_request/kill_request_filter.h | 2 +- .../extensions/filters/http/lua/lua_filter.h | 2 +- .../filters/http/ratelimit/ratelimit.cc | 2 +- .../filters/http/ratelimit/ratelimit.h | 2 +- .../extensions/filters/http/tap/tap_filter.h | 2 +- test/common/http/async_client_impl_test.cc | 2 +- test/common/http/codec_impl_fuzz_test.cc | 2 +- .../http/conn_manager_impl_fuzz_test.cc | 2 +- test/common/http/conn_manager_impl_test.cc | 48 +++++------ test/common/http/http1/codec_impl_test.cc | 10 +-- test/common/http/http2/codec_impl_test.cc | 28 +++---- .../quic/envoy_quic_client_stream_test.cc | 4 +- test/common/router/router_test.cc | 21 +++-- .../upstream/health_checker_impl_test.cc | 5 +- .../http/bandwidth_limit/filter_test.cc | 12 +-- .../filters/http/composite/filter_test.cc | 5 +- .../http/compressor/compressor_filter_test.cc | 3 +- .../filters/http/cors/cors_filter_test.cc | 6 +- .../filters/http/dynamo/dynamo_filter_test.cc | 3 +- .../filters/http/ext_proc/filter_test.cc | 6 +- .../filters/http/fault/fault_filter_test.cc | 3 +- .../http1_bridge_filter_test.cc | 6 +- .../json_transcoder_filter_test.cc | 15 ++-- .../filters/http/grpc_stats/config_test.cc | 3 +- .../http/grpc_web/grpc_web_filter_test.cc | 3 +- .../header_to_metadata_filter_test.cc | 3 +- .../http/health_check/health_check_test.cc | 3 +- .../kill_request/kill_request_filter_test.cc | 5 +- .../filters/http/lua/lua_filter_test.cc | 3 +- .../filters/http/ratelimit/ratelimit_test.cc | 84 +++++++------------ .../filters/http/tap/tap_filter_test.cc | 6 +- .../filters/http/wasm/wasm_filter_test.cc | 3 +- test/integration/fake_upstream.cc | 4 +- test/integration/fake_upstream.h | 2 +- .../filters/response_metadata_filter.cc | 4 +- .../http2_flood_integration_test.cc | 2 +- test/integration/http_integration.cc | 26 ++---- test/integration/http_integration.h | 4 +- .../idle_timeout_integration_test.cc | 8 +- .../integration/integration_stream_decoder.cc | 2 +- test/integration/integration_stream_decoder.h | 2 +- test/integration/integration_test.cc | 10 +-- .../multiplexed_integration_test.cc | 2 +- test/integration/protocol_integration_test.cc | 16 ++-- test/integration/utility.h | 2 +- test/mocks/http/mocks.h | 12 ++- test/mocks/http/stream_decoder.h | 6 +- test/mocks/http/stream_encoder.h | 2 +- test/mocks/router/mocks.h | 2 +- test/mocks/router/router_filter_interface.h | 2 +- 91 files changed, 321 insertions(+), 377 deletions(-) diff --git a/envoy/http/codec.h b/envoy/http/codec.h index 9a336b267fd6c..9dec51e385ff5 100644 --- a/envoy/http/codec.h +++ b/envoy/http/codec.h @@ -140,10 +140,11 @@ class RequestEncoder : public virtual StreamEncoder { class ResponseEncoder : public virtual StreamEncoder { public: /** - * Encode 100-Continue headers. - * @param headers supplies the 100-Continue header map to encode. + * Encode supported 1xx headers. + * Currently only 100-Continue headers are supported. + * @param headers supplies the 1xx header map to encode. */ - virtual void encode100ContinueHeaders(const ResponseHeaderMap& headers) PURE; + virtual void encode1xxHeaders(const ResponseHeaderMap& headers) PURE; /** * Encode headers, optionally indicating end of stream. Response headers must @@ -235,10 +236,11 @@ class RequestDecoder : public virtual StreamDecoder { class ResponseDecoder : public virtual StreamDecoder { public: /** - * Called with decoded 100-Continue headers. - * @param headers supplies the decoded 100-Continue headers map. + * Called with decoded 1xx headers. + * Currently only 100-Continue headers are supported. + * @param headers supplies the decoded 1xx headers map. */ - virtual void decode100ContinueHeaders(ResponseHeaderMapPtr&& headers) PURE; + virtual void decode1xxHeaders(ResponseHeaderMapPtr&& headers) PURE; /** * Called with decoded headers, optionally indicating end of stream. diff --git a/envoy/http/filter.h b/envoy/http/filter.h index 9a6527ca6519a..d6372cee98bd9 100644 --- a/envoy/http/filter.h +++ b/envoy/http/filter.h @@ -448,19 +448,22 @@ class StreamDecoderFilterCallbacks : public virtual StreamFilterCallbacks { virtual MetadataMapVector& addDecodedMetadata() PURE; /** - * Called with 100-Continue headers to be encoded. + * Called with 1xx headers to be encoded. + * + * Currently supported codes for this function include 100. * * This is not folded into encodeHeaders because most Envoy users and filters will not be proxying - * 100-continue and with it split out, can ignore the complexity of multiple encodeHeaders calls. + * 1xx headers and with it split out, can ignore the complexity of multiple encodeHeaders calls. * - * This must not be invoked more than once per request. + * This is currently only called once per request but implementations should + * handle multiple calls as multiple 1xx headers are legal. * * @param headers supplies the headers to be encoded. */ - virtual void encode100ContinueHeaders(ResponseHeaderMapPtr&& headers) PURE; + virtual void encode1xxHeaders(ResponseHeaderMapPtr&& headers) PURE; /** - * Returns the 100-Continue headers provided to encode100ContinueHeaders. Returns absl::nullopt if + * Returns the headers provided to encode1xxHeaders. Returns absl::nullopt if * no headers have been provided yet. */ virtual ResponseHeaderMapOptRef continueHeaders() const PURE; @@ -903,19 +906,19 @@ class StreamEncoderFilterCallbacks : public virtual StreamFilterCallbacks { class StreamEncoderFilter : public StreamFilterBase { public: /** - * Called with 100-continue headers. + * Called with supported 1xx headers. * * This is not folded into encodeHeaders because most Envoy users and filters - * will not be proxying 100-continue and with it split out, can ignore the + * will not be proxying 1xxs and with it split out, can ignore the * complexity of multiple encodeHeaders calls. * * This will only be invoked once per request. * - * @param headers supplies the 100-continue response headers to be encoded. + * @param headers supplies the 1xx response headers to be encoded. * @return FilterHeadersStatus determines how filter chain iteration proceeds. * */ - virtual FilterHeadersStatus encode100ContinueHeaders(ResponseHeaderMap& headers) PURE; + virtual FilterHeadersStatus encode1xxHeaders(ResponseHeaderMap& headers) PURE; /** * Called with headers to be encoded, optionally indicating end of stream. diff --git a/source/common/http/async_client_impl.h b/source/common/http/async_client_impl.h index 608813cc01722..f30abb5329860 100644 --- a/source/common/http/async_client_impl.h +++ b/source/common/http/async_client_impl.h @@ -376,9 +376,8 @@ class AsyncStreamImpl : public AsyncClient::Stream, }}, Utility::LocalReplyData{is_grpc_request_, code, body, grpc_status, is_head_request_}); } - // The async client won't pause if sending an Expect: 100-Continue so simply - // swallows any incoming encode100Continue. - void encode100ContinueHeaders(ResponseHeaderMapPtr&&) override {} + // The async client won't pause if sending 1xx headers so simply swallow any. + void encode1xxHeaders(ResponseHeaderMapPtr&&) override {} ResponseHeaderMapOptRef continueHeaders() const override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } void encodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream, absl::string_view details) override; diff --git a/source/common/http/codec_wrappers.h b/source/common/http/codec_wrappers.h index c3660af26c00f..37b62d35fbfa0 100644 --- a/source/common/http/codec_wrappers.h +++ b/source/common/http/codec_wrappers.h @@ -11,8 +11,8 @@ namespace Http { class ResponseDecoderWrapper : public ResponseDecoder { public: // ResponseDecoder - void decode100ContinueHeaders(ResponseHeaderMapPtr&& headers) override { - inner_.decode100ContinueHeaders(std::move(headers)); + void decode1xxHeaders(ResponseHeaderMapPtr&& headers) override { + inner_.decode1xxHeaders(std::move(headers)); } void decodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream) override { diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index 5a27e44a5f32d..58f1c02007ac3 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -885,7 +885,7 @@ void ConnectionManagerImpl::ActiveStream::decodeHeaders(RequestHeaderMapPtr&& he // Note in the case Envoy is handling 100-Continue complexity, it skips the filter chain // and sends the 100-Continue directly to the encoder. chargeStats(continueHeader()); - response_encoder_->encode100ContinueHeaders(continueHeader()); + response_encoder_->encode1xxHeaders(continueHeader()); // Remove the Expect header so it won't be handled again upstream. request_headers_->removeExpect(); } @@ -1332,8 +1332,7 @@ void ConnectionManagerImpl::ActiveStream::onLocalReply(Code code) { } } -void ConnectionManagerImpl::ActiveStream::encode100ContinueHeaders( - ResponseHeaderMap& response_headers) { +void ConnectionManagerImpl::ActiveStream::encode1xxHeaders(ResponseHeaderMap& response_headers) { // Strip the T-E headers etc. Defer other header additions as well as drain-close logic to the // continuation headers. ConnectionManagerUtility::mutateResponseHeaders( @@ -1346,7 +1345,7 @@ void ConnectionManagerImpl::ActiveStream::encode100ContinueHeaders( ENVOY_STREAM_LOG(debug, "encoding 100 continue headers via codec:\n{}", *this, response_headers); // Now actually encode via the codec. - response_encoder_->encode100ContinueHeaders(response_headers); + response_encoder_->encode1xxHeaders(response_headers); } void ConnectionManagerImpl::ActiveStream::encodeHeaders(ResponseHeaderMap& headers, diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h index c9a5b9eb5e1ec..17682f35b1943 100644 --- a/source/common/http/conn_manager_impl.h +++ b/source/common/http/conn_manager_impl.h @@ -214,7 +214,7 @@ class ConnectionManagerImpl : Logger::Loggable, // FilterManagerCallbacks void encodeHeaders(ResponseHeaderMap& response_headers, bool end_stream) override; - void encode100ContinueHeaders(ResponseHeaderMap& response_headers) override; + void encode1xxHeaders(ResponseHeaderMap& response_headers) override; void encodeData(Buffer::Instance& data, bool end_stream) override; void encodeTrailers(ResponseTrailerMap& trailers) override; void encodeMetadata(MetadataMapVector& metadata) override; diff --git a/source/common/http/filter_manager.cc b/source/common/http/filter_manager.cc index fc15f8041f224..7e2b1e9f23fc3 100644 --- a/source/common/http/filter_manager.cc +++ b/source/common/http/filter_manager.cc @@ -77,10 +77,10 @@ void ActiveStreamFilterBase::commonContinue() { } allowIteration(); - // Only resume with do100ContinueHeaders() if we've actually seen a 100-Continue. - if (has100Continueheaders()) { - continue_headers_continued_ = true; - do100ContinueHeaders(); + // Only resume with do1xxHeaders() if we've actually seen 1xx headers. + if (has1xxheaders()) { + continued_1xx_headers_ = true; + do1xxHeaders(); // If the response headers have not yet come in, don't continue on with // headers and body. doHeaders expects request headers to exist. if (!parent_.filter_manager_callbacks_.responseHeaders()) { @@ -109,10 +109,9 @@ void ActiveStreamFilterBase::commonContinue() { iterate_from_current_filter_ = false; } -bool ActiveStreamFilterBase::commonHandleAfter100ContinueHeadersCallback( - FilterHeadersStatus status) { - ASSERT(parent_.state_.has_continue_headers_); - ASSERT(!continue_headers_continued_); +bool ActiveStreamFilterBase::commonHandleAfter1xxHeadersCallback(FilterHeadersStatus status) { + ASSERT(parent_.state_.has_1xx_headers_); + ASSERT(!continued_1xx_headers_); ASSERT(canIterate()); if (status == FilterHeadersStatus::StopIteration) { @@ -120,7 +119,7 @@ bool ActiveStreamFilterBase::commonHandleAfter100ContinueHeadersCallback( return false; } else { ASSERT(status == FilterHeadersStatus::Continue); - continue_headers_continued_ = true; + continued_1xx_headers_ = true; return true; } } @@ -391,13 +390,13 @@ void ActiveStreamDecoderFilter::sendLocalReply( parent_.sendLocalReply(code, body, modify_headers, grpc_status, details); } -void ActiveStreamDecoderFilter::encode100ContinueHeaders(ResponseHeaderMapPtr&& headers) { +void ActiveStreamDecoderFilter::encode1xxHeaders(ResponseHeaderMapPtr&& headers) { // If Envoy is not configured to proxy 100-Continue responses, swallow the 100 Continue // here. This avoids the potential situation where Envoy strips Expect: 100-Continue and sends a // 100-Continue, then proxies a duplicate 100 Continue from upstream. if (parent_.proxy_100_continue_) { parent_.filter_manager_callbacks_.setContinueHeaders(std::move(headers)); - parent_.encode100ContinueHeaders(nullptr, *parent_.filter_manager_callbacks_.continueHeaders()); + parent_.encode1xxHeaders(nullptr, *parent_.filter_manager_callbacks_.continueHeaders()); } } @@ -1024,16 +1023,16 @@ void FilterManager::sendDirectLocalReply( Utility::LocalReplyData{state_.is_grpc_request_, code, body, grpc_status, is_head_request}); } -void FilterManager::encode100ContinueHeaders(ActiveStreamEncoderFilter* filter, - ResponseHeaderMap& headers) { +void FilterManager::encode1xxHeaders(ActiveStreamEncoderFilter* filter, + ResponseHeaderMap& headers) { filter_manager_callbacks_.resetIdleTimer(); ASSERT(proxy_100_continue_); - // The caller must guarantee that encode100ContinueHeaders() is invoked at most once. - ASSERT(!state_.has_continue_headers_ || filter != nullptr); - // Make sure commonContinue continues encode100ContinueHeaders. - state_.has_continue_headers_ = true; + // The caller must guarantee that encode1xxHeaders() is invoked at most once. + ASSERT(!state_.has_1xx_headers_ || filter != nullptr); + // Make sure commonContinue continues encode1xxHeaders. + state_.has_1xx_headers_ = true; - // Similar to the block in encodeHeaders, run encode100ContinueHeaders on each + // Similar to the block in encodeHeaders, run encode1xxHeaders on each // filter. This is simpler than that case because 100 continue implies no // end-stream, and because there are normal headers coming there's no need for // complex continuation logic. @@ -1045,18 +1044,18 @@ void FilterManager::encode100ContinueHeaders(ActiveStreamEncoderFilter* filter, continue; } - ASSERT(!(state_.filter_call_state_ & FilterCallState::Encode100ContinueHeaders)); - state_.filter_call_state_ |= FilterCallState::Encode100ContinueHeaders; - FilterHeadersStatus status = (*entry)->handle_->encode100ContinueHeaders(headers); - state_.filter_call_state_ &= ~FilterCallState::Encode100ContinueHeaders; - ENVOY_STREAM_LOG(trace, "encode 100 continue headers called: filter={} status={}", *this, + ASSERT(!(state_.filter_call_state_ & FilterCallState::Encode1xxHeaders)); + state_.filter_call_state_ |= FilterCallState::Encode1xxHeaders; + FilterHeadersStatus status = (*entry)->handle_->encode1xxHeaders(headers); + state_.filter_call_state_ &= ~FilterCallState::Encode1xxHeaders; + ENVOY_STREAM_LOG(trace, "encode 1xx continue headers called: filter={} status={}", *this, static_cast((*entry).get()), static_cast(status)); - if (!(*entry)->commonHandleAfter100ContinueHeadersCallback(status)) { + if (!(*entry)->commonHandleAfter1xxHeadersCallback(status)) { return; } } - filter_manager_callbacks_.encode100ContinueHeaders(headers); + filter_manager_callbacks_.encode1xxHeaders(headers); } void FilterManager::maybeContinueEncoding( @@ -1525,11 +1524,11 @@ Buffer::InstancePtr& ActiveStreamEncoderFilter::bufferedData() { return parent_.buffered_response_data_; } bool ActiveStreamEncoderFilter::complete() { return parent_.state_.local_complete_; } -bool ActiveStreamEncoderFilter::has100Continueheaders() { - return parent_.state_.has_continue_headers_ && !continue_headers_continued_; +bool ActiveStreamEncoderFilter::has1xxheaders() { + return parent_.state_.has_1xx_headers_ && !continued_1xx_headers_; } -void ActiveStreamEncoderFilter::do100ContinueHeaders() { - parent_.encode100ContinueHeaders(this, *parent_.filter_manager_callbacks_.continueHeaders()); +void ActiveStreamEncoderFilter::do1xxHeaders() { + parent_.encode1xxHeaders(this, *parent_.filter_manager_callbacks_.continueHeaders()); } void ActiveStreamEncoderFilter::doHeaders(bool end_stream) { parent_.encodeHeaders(this, *parent_.filter_manager_callbacks_.responseHeaders(), end_stream); diff --git a/source/common/http/filter_manager.h b/source/common/http/filter_manager.h index 633fa6862d3de..2ebb1d194c578 100644 --- a/source/common/http/filter_manager.h +++ b/source/common/http/filter_manager.h @@ -94,13 +94,13 @@ struct ActiveStreamFilterBase : public virtual StreamFilterCallbacks, FilterMatchStateSharedPtr match_state) : parent_(parent), iteration_state_(IterationState::Continue), filter_match_state_(std::move(match_state)), iterate_from_current_filter_(false), - headers_continued_(false), continue_headers_continued_(false), end_stream_(false), + headers_continued_(false), continued_1xx_headers_(false), end_stream_(false), dual_filter_(dual_filter), decode_headers_called_(false), encode_headers_called_(false) {} // Functions in the following block are called after the filter finishes processing // corresponding data. Those functions handle state updates and data storage (if needed) // according to the status returned by filter's callback functions. - bool commonHandleAfter100ContinueHeadersCallback(FilterHeadersStatus status); + bool commonHandleAfter1xxHeadersCallback(FilterHeadersStatus status); bool commonHandleAfterHeadersCallback(FilterHeadersStatus status, bool& end_stream); bool commonHandleAfterDataCallback(FilterDataStatus status, Buffer::Instance& provided_data, bool& buffer_was_streaming); @@ -118,8 +118,8 @@ struct ActiveStreamFilterBase : public virtual StreamFilterCallbacks, virtual Buffer::InstancePtr createBuffer() PURE; virtual Buffer::InstancePtr& bufferedData() PURE; virtual bool complete() PURE; - virtual bool has100Continueheaders() PURE; - virtual void do100ContinueHeaders() PURE; + virtual bool has1xxheaders() PURE; + virtual void do1xxHeaders() PURE; virtual void doHeaders(bool end_stream) PURE; virtual void doData(bool end_stream) PURE; virtual void doTrailers() PURE; @@ -201,7 +201,7 @@ struct ActiveStreamFilterBase : public virtual StreamFilterCallbacks, // filter. Otherwise, starts with the next filter in the chain. bool iterate_from_current_filter_ : 1; bool headers_continued_ : 1; - bool continue_headers_continued_ : 1; + bool continued_1xx_headers_ : 1; // If true, end_stream is called for this filter. bool end_stream_ : 1; const bool dual_filter_ : 1; @@ -226,8 +226,8 @@ struct ActiveStreamDecoderFilter : public ActiveStreamFilterBase, Buffer::InstancePtr createBuffer() override; Buffer::InstancePtr& bufferedData() override; bool complete() override; - bool has100Continueheaders() override { return false; } - void do100ContinueHeaders() override { NOT_REACHED_GCOVR_EXCL_LINE; } + bool has1xxheaders() override { return false; } + void do1xxHeaders() override { NOT_REACHED_GCOVR_EXCL_LINE; } void doHeaders(bool end_stream) override; void doData(bool end_stream) override; void doMetadata() override { @@ -259,7 +259,7 @@ struct ActiveStreamDecoderFilter : public ActiveStreamFilterBase, std::function modify_headers, const absl::optional grpc_status, absl::string_view details) override; - void encode100ContinueHeaders(ResponseHeaderMapPtr&& headers) override; + void encode1xxHeaders(ResponseHeaderMapPtr&& headers) override; ResponseHeaderMapOptRef continueHeaders() const override; void encodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream, absl::string_view details) override; @@ -319,8 +319,8 @@ struct ActiveStreamEncoderFilter : public ActiveStreamFilterBase, Buffer::InstancePtr createBuffer() override; Buffer::InstancePtr& bufferedData() override; bool complete() override; - bool has100Continueheaders() override; - void do100ContinueHeaders() override; + bool has1xxheaders() override; + void do1xxHeaders() override; void doHeaders(bool end_stream) override; void doData(bool end_stream) override; void drainSavedResponseMetadata(); @@ -380,7 +380,7 @@ class FilterManagerCallbacks { * chain. * @param response_headers the encoded headers. */ - virtual void encode100ContinueHeaders(ResponseHeaderMap& response_headers) PURE; + virtual void encode1xxHeaders(ResponseHeaderMap& response_headers) PURE; /** * Called when the provided data has been encoded by all filters in the chain. @@ -677,7 +677,7 @@ class FilterManager : public ScopeTrackedObject, // ScopeTrackedObject void dumpState(std::ostream& os, int indent_level = 0) const override { const char* spaces = spacesForLevel(indent_level); - os << spaces << "FilterManager " << this << DUMP_MEMBER(state_.has_continue_headers_) << "\n"; + os << spaces << "FilterManager " << this << DUMP_MEMBER(state_.has_1xx_headers_) << "\n"; DUMP_DETAILS(filter_manager_callbacks_.requestHeaders()); DUMP_DETAILS(filter_manager_callbacks_.requestTrailers()); @@ -970,7 +970,7 @@ class FilterManager : public ScopeTrackedObject, void decodeMetadata(ActiveStreamDecoderFilter* filter, MetadataMap& metadata_map); void addEncodedData(ActiveStreamEncoderFilter& filter, Buffer::Instance& data, bool streaming); ResponseTrailerMap& addEncodedTrailers(); - void encode100ContinueHeaders(ActiveStreamEncoderFilter* filter, ResponseHeaderMap& headers); + void encode1xxHeaders(ActiveStreamEncoderFilter* filter, ResponseHeaderMap& headers); // As with most of the encode functions, this runs encodeHeaders on various // filters before calling encodeHeadersInternal which does final header munging and passes the // headers to the encoder. @@ -1046,11 +1046,11 @@ class FilterManager : public ScopeTrackedObject, static constexpr uint32_t EncodeHeaders = 0x08; static constexpr uint32_t EncodeData = 0x10; static constexpr uint32_t EncodeTrailers = 0x20; - // Encode100ContinueHeaders is a bit of a special state as 100 continue + // Encode1xxHeaders is a bit of a special state as 1xx // headers may be sent during request processing. This state is only used - // to verify we do not encode100Continue headers more than once per + // to verify we do not encode1xx headers more than once per // filter. - static constexpr uint32_t Encode100ContinueHeaders = 0x40; + static constexpr uint32_t Encode1xxHeaders = 0x40; // Used to indicate that we're processing the final [En|De]codeData frame, // i.e. end_stream = true static constexpr uint32_t LastDataFrame = 0x80; @@ -1059,7 +1059,7 @@ class FilterManager : public ScopeTrackedObject, struct State { State() - : remote_complete_(false), local_complete_(false), has_continue_headers_(false), + : remote_complete_(false), local_complete_(false), has_1xx_headers_(false), created_filter_chain_(false), is_head_request_(false), is_grpc_request_(false), non_100_response_headers_encoded_(false), under_on_local_reply_(false), decoder_filter_chain_aborted_(false), encoder_filter_chain_aborted_(false) {} @@ -1070,9 +1070,9 @@ class FilterManager : public ScopeTrackedObject, bool local_complete_ : 1; // This indicates that local is complete prior to filter processing. // A filter can still stop the stream from being complete as seen // by the codec. - // By default, we will assume there are no 100-Continue headers. If encode100ContinueHeaders - // is ever called, this is set to true so commonContinue resumes processing the 100-Continue. - bool has_continue_headers_ : 1; + // By default, we will assume there are no 1xx. If encode1xxHeaders + // is ever called, this is set to true so commonContinue resumes processing the 1xx. + bool has_1xx_headers_ : 1; bool created_filter_chain_ : 1; // These two are latched on initial header read, to determine if the original headers // constituted a HEAD or gRPC request, respectively. diff --git a/source/common/http/header_utility.cc b/source/common/http/header_utility.cc index 5ec89d6228c60..c278b30bd19f3 100644 --- a/source/common/http/header_utility.cc +++ b/source/common/http/header_utility.cc @@ -201,6 +201,10 @@ bool HeaderUtility::authorityIsValid(const absl::string_view header_value) { header_value.size()) != 0; } +bool HeaderUtility::isSpecial1xx(const ResponseHeaderMap& response_headers) { + return response_headers.Status()->value() == "100"; +} + bool HeaderUtility::isConnect(const RequestHeaderMap& headers) { return headers.Method() && headers.Method()->value() == Http::Headers::get().MethodValues.Connect; } diff --git a/source/common/http/header_utility.h b/source/common/http/header_utility.h index 8e1ae6decd1c3..40a42c7da6049 100644 --- a/source/common/http/header_utility.h +++ b/source/common/http/header_utility.h @@ -152,6 +152,11 @@ class HeaderUtility { */ static bool authorityIsValid(const absl::string_view authority_value); + /** + * @brief return if the 1xx should be handled by the [encode|decode]1xx calls. + */ + static bool isSpecial1xx(const ResponseHeaderMap& response_headers); + /** * @brief a helper function to determine if the headers represent a CONNECT request. */ diff --git a/source/common/http/http1/codec_impl.cc b/source/common/http/http1/codec_impl.cc index bcca80fd2582c..a3c9eeb871f3b 100644 --- a/source/common/http/http1/codec_impl.cc +++ b/source/common/http/http1/codec_impl.cc @@ -126,8 +126,8 @@ void StreamEncoderImpl::encodeFormattedHeader(absl::string_view key, absl::strin } } -void ResponseEncoderImpl::encode100ContinueHeaders(const ResponseHeaderMap& headers) { - ASSERT(headers.Status()->value() == "100"); +void ResponseEncoderImpl::encode1xxHeaders(const ResponseHeaderMap& headers) { + ASSERT(HeaderUtility::isSpecial1xx(headers)); encodeHeaders(headers, false); } @@ -1347,8 +1347,8 @@ Envoy::StatusOr ClientConnectionImpl::onHeadersCompleteBase() { } } - if (parser_->statusCode() == enumToInt(Http::Code::Continue)) { - pending_response_.value().decoder_->decode100ContinueHeaders(std::move(headers)); + if (HeaderUtility::isSpecial1xx(*headers)) { + pending_response_.value().decoder_->decode1xxHeaders(std::move(headers)); } else if (cannotHaveBody() && !handling_upgrade_) { deferred_end_stream_headers_ = true; } else { diff --git a/source/common/http/http1/codec_impl.h b/source/common/http/http1/codec_impl.h index 44df405a0a2af..0038f60c94cb9 100644 --- a/source/common/http/http1/codec_impl.h +++ b/source/common/http/http1/codec_impl.h @@ -160,7 +160,7 @@ class ResponseEncoderImpl : public StreamEncoderImpl, public ResponseEncoder { bool startedResponse() { return started_response_; } // Http::ResponseEncoder - void encode100ContinueHeaders(const ResponseHeaderMap& headers) override; + void encode1xxHeaders(const ResponseHeaderMap& headers) override; void encodeHeaders(const ResponseHeaderMap& headers, bool end_stream) override; void encodeTrailers(const ResponseTrailerMap& trailers) override { encodeTrailersBase(trailers); } diff --git a/source/common/http/http2/codec_impl.cc b/source/common/http/http2/codec_impl.cc index 9ae282779949d..9e4f76fa7b1f6 100644 --- a/source/common/http/http2/codec_impl.cc +++ b/source/common/http/http2/codec_impl.cc @@ -195,8 +195,8 @@ void ConnectionImpl::StreamImpl::buildHeaders(std::vector& final_hea }); } -void ConnectionImpl::ServerStreamImpl::encode100ContinueHeaders(const ResponseHeaderMap& headers) { - ASSERT(headers.Status()->value() == "100"); +void ConnectionImpl::ServerStreamImpl::encode1xxHeaders(const ResponseHeaderMap& headers) { + ASSERT(HeaderUtility::isSpecial1xx(headers)); encodeHeaders(headers, false); } @@ -354,7 +354,7 @@ void ConnectionImpl::ClientStreamImpl::decodeHeaders() { if (status == enumToInt(Http::Code::Continue)) { ASSERT(!remote_end_stream_); - response_decoder_.decode100ContinueHeaders(std::move(headers)); + response_decoder_.decode1xxHeaders(std::move(headers)); } else { response_decoder_.decodeHeaders(std::move(headers), remote_end_stream_); } diff --git a/source/common/http/http2/codec_impl.h b/source/common/http/http2/codec_impl.h index 9a8bfc7b7c6b5..a6f82e4f6d6f1 100644 --- a/source/common/http/http2/codec_impl.h +++ b/source/common/http/http2/codec_impl.h @@ -411,7 +411,7 @@ class ConnectionImpl : public virtual Connection, void resetStream(StreamResetReason reason) override; // ResponseEncoder - void encode100ContinueHeaders(const ResponseHeaderMap& headers) override; + void encode1xxHeaders(const ResponseHeaderMap& headers) override; void encodeHeaders(const ResponseHeaderMap& headers, bool end_stream) override; void encodeTrailers(const ResponseTrailerMap& trailers) override { encodeTrailersBase(trailers); diff --git a/source/common/quic/envoy_quic_client_stream.cc b/source/common/quic/envoy_quic_client_stream.cc index 222f35f56e78f..0e239658f1aa3 100644 --- a/source/common/quic/envoy_quic_client_stream.cc +++ b/source/common/quic/envoy_quic_client_stream.cc @@ -176,11 +176,12 @@ void EnvoyQuicClientStream::OnInitialHeadersComplete(bool fin, size_t frame_len, set_headers_decompressed(false); } - if (status == enumToInt(Http::Code::Continue) && !decoded_100_continue_) { + const bool is_special_1xx = Http::HeaderUtility::isSpecial1xx(*headers); + if (is_special_1xx && !decoded_1xx_) { // This is 100 Continue, only decode it once to support Expect:100-Continue header. - decoded_100_continue_ = true; - response_decoder_->decode100ContinueHeaders(std::move(headers)); - } else if (status != enumToInt(Http::Code::Continue)) { + decoded_1xx_ = true; + response_decoder_->decode1xxHeaders(std::move(headers)); + } else if (!is_special_1xx) { response_decoder_->decodeHeaders(std::move(headers), /*end_stream=*/fin); if (status == enumToInt(Http::Code::NotModified)) { diff --git a/source/common/quic/envoy_quic_client_stream.h b/source/common/quic/envoy_quic_client_stream.h index 165d751680f92..bb1084122215f 100644 --- a/source/common/quic/envoy_quic_client_stream.h +++ b/source/common/quic/envoy_quic_client_stream.h @@ -74,7 +74,7 @@ class EnvoyQuicClientStream : public quic::QuicSpdyClientStream, Http::ResponseDecoder* response_decoder_{nullptr}; - bool decoded_100_continue_{false}; + bool decoded_1xx_{false}; }; } // namespace Quic diff --git a/source/common/quic/envoy_quic_server_stream.cc b/source/common/quic/envoy_quic_server_stream.cc index b93e3a216acb7..41f4bf5d30af8 100644 --- a/source/common/quic/envoy_quic_server_stream.cc +++ b/source/common/quic/envoy_quic_server_stream.cc @@ -39,8 +39,8 @@ EnvoyQuicServerStream::EnvoyQuicServerStream( // send the correct SETTINGS. } -void EnvoyQuicServerStream::encode100ContinueHeaders(const Http::ResponseHeaderMap& headers) { - ASSERT(headers.Status()->value() == "100"); +void EnvoyQuicServerStream::encode1xxHeaders(const Http::ResponseHeaderMap& headers) { + ASSERT(Http::HeaderUtility::isSpecial1xx(headers)); encodeHeaders(headers, false); } diff --git a/source/common/quic/envoy_quic_server_stream.h b/source/common/quic/envoy_quic_server_stream.h index 5662b08fab7d8..fe7cd5a19281e 100644 --- a/source/common/quic/envoy_quic_server_stream.h +++ b/source/common/quic/envoy_quic_server_stream.h @@ -21,7 +21,7 @@ class EnvoyQuicServerStream : public quic::QuicSpdyServerStreamBase, void setRequestDecoder(Http::RequestDecoder& decoder) { request_decoder_ = &decoder; } // Http::StreamEncoder - void encode100ContinueHeaders(const Http::ResponseHeaderMap& headers) override; + void encode1xxHeaders(const Http::ResponseHeaderMap& headers) override; void encodeHeaders(const Http::ResponseHeaderMap& headers, bool end_stream) override; void encodeData(Buffer::Instance& data, bool end_stream) override; void encodeTrailers(const Http::ResponseTrailerMap& trailers) override; diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 054b6a6858ecd..f58d36f56eacc 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -1225,8 +1225,8 @@ void Filter::handleNon5xxResponseHeaders(absl::optionalencode100ContinueHeaders(std::move(headers)); + if (!downstream_1xx_headers_encoded_) { + downstream_1xx_headers_encoded_ = true; + callbacks_->encode1xxHeaders(std::move(headers)); } } diff --git a/source/common/router/router.h b/source/common/router/router.h index e9d0234aed5d0..22e9ab0ecaa68 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -258,8 +258,8 @@ class RouterFilterInterface { public: virtual ~RouterFilterInterface() = default; - virtual void onUpstream100ContinueHeaders(Http::ResponseHeaderMapPtr&& headers, - UpstreamRequest& upstream_request) PURE; + virtual void onUpstream1xxHeaders(Http::ResponseHeaderMapPtr&& headers, + UpstreamRequest& upstream_request) PURE; virtual void onUpstreamHeaders(uint64_t response_code, Http::ResponseHeaderMapPtr&& headers, UpstreamRequest& upstream_request, bool end_stream) PURE; virtual void onUpstreamData(Buffer::Instance& data, UpstreamRequest& upstream_request, @@ -301,9 +301,9 @@ class Filter : Logger::Loggable, public RouterFilterInterface { public: Filter(FilterConfig& config) - : config_(config), final_upstream_request_(nullptr), - downstream_100_continue_headers_encoded_(false), downstream_response_started_(false), - downstream_end_stream_(false), is_retry_(false), request_buffer_overflowed_(false) {} + : config_(config), final_upstream_request_(nullptr), downstream_1xx_headers_encoded_(false), + downstream_response_started_(false), downstream_end_stream_(false), is_retry_(false), + request_buffer_overflowed_(false) {} ~Filter() override; @@ -434,8 +434,8 @@ class Filter : Logger::Loggable, } // RouterFilterInterface - void onUpstream100ContinueHeaders(Http::ResponseHeaderMapPtr&& headers, - UpstreamRequest& upstream_request) override; + void onUpstream1xxHeaders(Http::ResponseHeaderMapPtr&& headers, + UpstreamRequest& upstream_request) override; void onUpstreamHeaders(uint64_t response_code, Http::ResponseHeaderMapPtr&& headers, UpstreamRequest& upstream_request, bool end_stream) override; void onUpstreamData(Buffer::Instance& data, UpstreamRequest& upstream_request, @@ -562,7 +562,7 @@ class Filter : Logger::Loggable, // list of cookies to add to upstream headers std::vector downstream_set_cookies_; - bool downstream_100_continue_headers_encoded_ : 1; + bool downstream_1xx_headers_encoded_ : 1; bool downstream_response_started_ : 1; bool downstream_end_stream_ : 1; bool is_retry_ : 1; diff --git a/source/common/router/upstream_request.cc b/source/common/router/upstream_request.cc index 34ccc3f38c5fa..b406c0f37afd3 100644 --- a/source/common/router/upstream_request.cc +++ b/source/common/router/upstream_request.cc @@ -129,12 +129,12 @@ UpstreamRequest::~UpstreamRequest() { } } -void UpstreamRequest::decode100ContinueHeaders(Http::ResponseHeaderMapPtr&& headers) { +void UpstreamRequest::decode1xxHeaders(Http::ResponseHeaderMapPtr&& headers) { ScopeTrackerScopeState scope(&parent_.callbacks()->scope(), parent_.callbacks()->dispatcher()); - ASSERT(100 == Http::Utility::getResponseStatus(*headers)); + ASSERT(Http::HeaderUtility::isSpecial1xx(*headers)); addResponseHeadersSize(headers->byteSize()); - parent_.onUpstream100ContinueHeaders(std::move(headers), *this); + parent_.onUpstream1xxHeaders(std::move(headers), *this); } void UpstreamRequest::decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool end_stream) { @@ -143,8 +143,8 @@ void UpstreamRequest::decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool e resetPerTryIdleTimer(); addResponseHeadersSize(headers->byteSize()); - // We drop 1xx other than 101 on the floor; 101 upgrade headers need to be passed to the client as - // part of the final response. 100-continue headers are handled in onUpstream100ContinueHeaders. + // We drop unsupported 1xx on the floor here. 101 upgrade headers need to be passed to the client + // as part of the final response. Most 1xx headers are handled in onUpstream1xxHeaders. // // We could in principle handle other headers here, but this might result in the double invocation // of decodeHeaders() (once for informational, again for non-informational), which is likely an diff --git a/source/common/router/upstream_request.h b/source/common/router/upstream_request.h index 112c2a5216e6b..c5a6272f470e6 100644 --- a/source/common/router/upstream_request.h +++ b/source/common/router/upstream_request.h @@ -54,7 +54,7 @@ class UpstreamRequest : public Logger::Loggable, void decodeMetadata(Http::MetadataMapPtr&& metadata_map) override; // UpstreamToDownstream (Http::ResponseDecoder) - void decode100ContinueHeaders(Http::ResponseHeaderMapPtr&& headers) override; + void decode1xxHeaders(Http::ResponseHeaderMapPtr&& headers) override; void decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool end_stream) override; void decodeTrailers(Http::ResponseTrailerMapPtr&& trailers) override; void dumpState(std::ostream& os, int indent_level) const override; diff --git a/source/common/tcp_proxy/upstream.h b/source/common/tcp_proxy/upstream.h index 19c10cbb3694e..fbae213d57fb4 100644 --- a/source/common/tcp_proxy/upstream.h +++ b/source/common/tcp_proxy/upstream.h @@ -165,7 +165,7 @@ class HttpUpstream : public GenericUpstream, protected Http::StreamCallbacks { public: DecoderShim(HttpUpstream& parent) : parent_(parent) {} // Http::ResponseDecoder - void decode100ContinueHeaders(Http::ResponseHeaderMapPtr&&) override {} + void decode1xxHeaders(Http::ResponseHeaderMapPtr&&) override {} void decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool end_stream) override { if (!parent_.isValidResponse(*headers) || end_stream) { parent_.resetEncoder(Network::ConnectionEvent::LocalClose); diff --git a/source/common/upstream/health_checker_impl.h b/source/common/upstream/health_checker_impl.h index cb8d62a3d4a61..3dd90a6dc1401 100644 --- a/source/common/upstream/health_checker_impl.h +++ b/source/common/upstream/health_checker_impl.h @@ -103,7 +103,7 @@ class HttpHealthCheckerImpl : public HealthCheckerImplBase { void decodeMetadata(Http::MetadataMapPtr&&) override {} // Http::ResponseDecoder - void decode100ContinueHeaders(Http::ResponseHeaderMapPtr&&) override {} + void decode1xxHeaders(Http::ResponseHeaderMapPtr&&) override {} void decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool end_stream) override; void decodeTrailers(Http::ResponseTrailerMapPtr&&) override { onResponseComplete(); } void dumpState(std::ostream& os, int indent_level) const override { @@ -341,7 +341,7 @@ class GrpcHealthCheckerImpl : public HealthCheckerImplBase { void decodeMetadata(Http::MetadataMapPtr&&) override {} // Http::ResponseDecoder - void decode100ContinueHeaders(Http::ResponseHeaderMapPtr&&) override {} + void decode1xxHeaders(Http::ResponseHeaderMapPtr&&) override {} void decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool end_stream) override; void decodeTrailers(Http::ResponseTrailerMapPtr&&) override; void dumpState(std::ostream& os, int indent_level) const override { diff --git a/source/docs/h2_metadata.md b/source/docs/h2_metadata.md index 71d5eba1a32ca..133fdbb57be8f 100644 --- a/source/docs/h2_metadata.md +++ b/source/docs/h2_metadata.md @@ -102,7 +102,7 @@ StreamEncoderFilterCallbacks::addEncodedMetadata(MetadataMapPtr&& metadata\_map\_ptr). By calling the interface, users can directly pass in the metadata to be added. This function can be called in the StreamEncoderFilter::encode*() methods except for encodeMetadata(): -StreamEncoderFilter::encode100ContinueHeaders(HeaderMap& headers), +StreamEncoderFilter::encode1xxHeaders(HeaderMap& headers), StreamEncoderFilter::encodeHeaders(HeaderMap& headers, bool end\_stream), StreamEncoderFilter::encodeData(Buffer::Instance& data, bool end\_stream), StreamEncoderFilter::encodeTrailers(HeaderMap& trailers), and diff --git a/source/extensions/common/wasm/context.cc b/source/extensions/common/wasm/context.cc index 8e703227657cb..d3db5864dd47b 100644 --- a/source/extensions/common/wasm/context.cc +++ b/source/extensions/common/wasm/context.cc @@ -1707,7 +1707,7 @@ void Context::setDecoderFilterCallbacks(Envoy::Http::StreamDecoderFilterCallback decoder_callbacks_ = &callbacks; } -Http::FilterHeadersStatus Context::encode100ContinueHeaders(Http::ResponseHeaderMap&) { +Http::FilterHeadersStatus Context::encode1xxHeaders(Http::ResponseHeaderMap&) { return Http::FilterHeadersStatus::Continue; } diff --git a/source/extensions/common/wasm/context.h b/source/extensions/common/wasm/context.h index b163c96c9edae..46dadc4b76777 100644 --- a/source/extensions/common/wasm/context.h +++ b/source/extensions/common/wasm/context.h @@ -185,7 +185,7 @@ class Context : public proxy_wasm::ContextBase, void setDecoderFilterCallbacks(Envoy::Http::StreamDecoderFilterCallbacks& callbacks) override; // Http::StreamEncoderFilter - Http::FilterHeadersStatus encode100ContinueHeaders(Http::ResponseHeaderMap&) override; + Http::FilterHeadersStatus encode1xxHeaders(Http::ResponseHeaderMap&) override; Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers, bool end_stream) override; Http::FilterDataStatus encodeData(::Envoy::Buffer::Instance& data, bool end_stream) override; diff --git a/source/extensions/filters/http/bandwidth_limit/bandwidth_limit.h b/source/extensions/filters/http/bandwidth_limit/bandwidth_limit.h index c7c2242a0258c..482fde9a05bf9 100644 --- a/source/extensions/filters/http/bandwidth_limit/bandwidth_limit.h +++ b/source/extensions/filters/http/bandwidth_limit/bandwidth_limit.h @@ -116,7 +116,7 @@ class BandwidthLimiter : public Http::StreamFilter, Logger::Loggable, void setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callbacks) override; // Http::StreamEncoderFilter - Http::FilterHeadersStatus encode100ContinueHeaders(Http::ResponseHeaderMap& headers) override; + Http::FilterHeadersStatus encode1xxHeaders(Http::ResponseHeaderMap& headers) override; Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers, bool end_stream) override; Http::FilterDataStatus encodeData(Buffer::Instance& data, bool end_stream) override; diff --git a/source/extensions/filters/http/fault/fault_filter.h b/source/extensions/filters/http/fault/fault_filter.h index f96756f16c133..1d8fea719ca48 100644 --- a/source/extensions/filters/http/fault/fault_filter.h +++ b/source/extensions/filters/http/fault/fault_filter.h @@ -171,7 +171,7 @@ class FaultFilter : public Http::StreamFilter, Logger::Loggable { } // Http::StreamEncoderFilter - Http::FilterHeadersStatus encode100ContinueHeaders(Http::ResponseHeaderMap&) override { + Http::FilterHeadersStatus encode1xxHeaders(Http::ResponseHeaderMap&) override { return Http::FilterHeadersStatus::Continue; } Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers, diff --git a/source/extensions/filters/http/ratelimit/ratelimit.cc b/source/extensions/filters/http/ratelimit/ratelimit.cc index b10e12688b0d1..869801a04e1e7 100644 --- a/source/extensions/filters/http/ratelimit/ratelimit.cc +++ b/source/extensions/filters/http/ratelimit/ratelimit.cc @@ -108,7 +108,7 @@ void Filter::setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callb callbacks_ = &callbacks; } -Http::FilterHeadersStatus Filter::encode100ContinueHeaders(Http::ResponseHeaderMap&) { +Http::FilterHeadersStatus Filter::encode1xxHeaders(Http::ResponseHeaderMap&) { return Http::FilterHeadersStatus::Continue; } diff --git a/source/extensions/filters/http/ratelimit/ratelimit.h b/source/extensions/filters/http/ratelimit/ratelimit.h index bee2d04ff2e07..07dc086fd1a3a 100644 --- a/source/extensions/filters/http/ratelimit/ratelimit.h +++ b/source/extensions/filters/http/ratelimit/ratelimit.h @@ -135,7 +135,7 @@ class Filter : public Http::StreamFilter, public Filters::Common::RateLimit::Req void setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callbacks) override; // Http::StreamEncoderFilter - Http::FilterHeadersStatus encode100ContinueHeaders(Http::ResponseHeaderMap& headers) override; + Http::FilterHeadersStatus encode1xxHeaders(Http::ResponseHeaderMap& headers) override; Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers, bool end_stream) override; Http::FilterDataStatus encodeData(Buffer::Instance& data, bool end_stream) override; diff --git a/source/extensions/filters/http/tap/tap_filter.h b/source/extensions/filters/http/tap/tap_filter.h index a6ea1a5059f22..1414b077cd6ec 100644 --- a/source/extensions/filters/http/tap/tap_filter.h +++ b/source/extensions/filters/http/tap/tap_filter.h @@ -93,7 +93,7 @@ class Filter : public Http::StreamFilter, public AccessLog::Instance { } // Http::StreamEncoderFilter - Http::FilterHeadersStatus encode100ContinueHeaders(Http::ResponseHeaderMap&) override { + Http::FilterHeadersStatus encode1xxHeaders(Http::ResponseHeaderMap&) override { return Http::FilterHeadersStatus::Continue; } Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers, diff --git a/test/common/http/async_client_impl_test.cc b/test/common/http/async_client_impl_test.cc index 28d78009c2fdb..cceea46b4e82b 100644 --- a/test/common/http/async_client_impl_test.cc +++ b/test/common/http/async_client_impl_test.cc @@ -147,7 +147,7 @@ TEST_F(AsyncClientImplTest, BasicStream) { stream->sendHeaders(headers, false); stream->sendData(*body, true); - response_decoder_->decode100ContinueHeaders( + response_decoder_->decode1xxHeaders( ResponseHeaderMapPtr(new TestResponseHeaderMapImpl{{":status", "100"}})); response_decoder_->decodeHeaders( ResponseHeaderMapPtr(new TestResponseHeaderMapImpl{{":status", "200"}}), false); diff --git a/test/common/http/codec_impl_fuzz_test.cc b/test/common/http/codec_impl_fuzz_test.cc index 61e8bbd736ce6..1fa32ed7de37e 100644 --- a/test/common/http/codec_impl_fuzz_test.cc +++ b/test/common/http/codec_impl_fuzz_test.cc @@ -215,7 +215,7 @@ class HttpStream : public LinkedObject { auto headers = fromSanitizedHeaders(directional_action.continue_headers()); headers.setReferenceKey(Headers::get().Status, "100"); - state.response_encoder_->encode100ContinueHeaders(headers); + state.response_encoder_->encode1xxHeaders(headers); } break; } diff --git a/test/common/http/conn_manager_impl_fuzz_test.cc b/test/common/http/conn_manager_impl_fuzz_test.cc index 35aa7dd8ad977..4177b065e607c 100644 --- a/test/common/http/conn_manager_impl_fuzz_test.cc +++ b/test/common/http/conn_manager_impl_fuzz_test.cc @@ -473,7 +473,7 @@ class FuzzStream { auto headers = std::make_unique( Fuzz::fromHeaders(response_action.continue_headers())); headers->setReferenceKey(Headers::get().Status, "100"); - decoder_filter_->callbacks_->encode100ContinueHeaders(std::move(headers)); + decoder_filter_->callbacks_->encode1xxHeaders(std::move(headers)); // We don't allow multiple 100-continue headers in HCM, UpstreamRequest is responsible // for coalescing. state = StreamState::PendingNonInformationalHeaders; diff --git a/test/common/http/conn_manager_impl_test.cc b/test/common/http/conn_manager_impl_test.cc index 05a3a0d59d055..152f799b4ac8a 100644 --- a/test/common/http/conn_manager_impl_test.cc +++ b/test/common/http/conn_manager_impl_test.cc @@ -82,7 +82,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponse) { EXPECT_EQ(1U, listener_stats_.downstream_rq_completed_.value()); } -TEST_F(HttpConnectionManagerImplTest, 100ContinueResponse) { +TEST_F(HttpConnectionManagerImplTest, 1xxResponse) { proxy_100_continue_ = true; setup(false, "envoy-custom-server", false); @@ -117,7 +117,7 @@ TEST_F(HttpConnectionManagerImplTest, 100ContinueResponse) { decoder_->decodeHeaders(std::move(headers), true); ResponseHeaderMapPtr continue_headers{new TestResponseHeaderMapImpl{{":status", "100"}}}; - filter->callbacks_->encode100ContinueHeaders(std::move(continue_headers)); + filter->callbacks_->encode1xxHeaders(std::move(continue_headers)); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; filter->callbacks_->streamInfo().setResponseCodeDetails(""); filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); @@ -138,20 +138,20 @@ TEST_F(HttpConnectionManagerImplTest, 100ContinueResponse) { EXPECT_EQ(2U, listener_stats_.downstream_rq_completed_.value()); } -TEST_F(HttpConnectionManagerImplTest, 100ContinueResponseWithEncoderFiltersProxyingDisabled) { +TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithEncoderFiltersProxyingDisabled) { proxy_100_continue_ = false; setup(false, "envoy-custom-server", false); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); - // Akin to 100ContinueResponseWithEncoderFilters below, but with + // Akin to 1xxResponseWithEncoderFilters below, but with // proxy_100_continue_ false. Verify the filters do not get the 100 continue // headers. - EXPECT_CALL(*encoder_filters_[0], encode100ContinueHeaders(_)).Times(0); - EXPECT_CALL(*encoder_filters_[1], encode100ContinueHeaders(_)).Times(0); - EXPECT_CALL(response_encoder_, encode100ContinueHeaders(_)).Times(0); + EXPECT_CALL(*encoder_filters_[0], encode1xxHeaders(_)).Times(0); + EXPECT_CALL(*encoder_filters_[1], encode1xxHeaders(_)).Times(0); + EXPECT_CALL(response_encoder_, encode1xxHeaders(_)).Times(0); ResponseHeaderMapPtr continue_headers{new TestResponseHeaderMapImpl{{":status", "100"}}}; - decoder_filters_[0]->callbacks_->encode100ContinueHeaders(std::move(continue_headers)); + decoder_filters_[0]->callbacks_->encode1xxHeaders(std::move(continue_headers)); EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) .WillOnce(Return(FilterHeadersStatus::Continue)); @@ -165,19 +165,19 @@ TEST_F(HttpConnectionManagerImplTest, 100ContinueResponseWithEncoderFiltersProxy doRemoteClose(); } -TEST_F(HttpConnectionManagerImplTest, 100ContinueResponseWithEncoderFilters) { +TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithEncoderFilters) { proxy_100_continue_ = true; setup(false, "envoy-custom-server", false); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); - EXPECT_CALL(*encoder_filters_[0], encode100ContinueHeaders(_)) + EXPECT_CALL(*encoder_filters_[0], encode1xxHeaders(_)) .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[1], encode100ContinueHeaders(_)) + EXPECT_CALL(*encoder_filters_[1], encode1xxHeaders(_)) .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(response_encoder_, encode100ContinueHeaders(_)); + EXPECT_CALL(response_encoder_, encode1xxHeaders(_)); ResponseHeaderMapPtr continue_headers{new TestResponseHeaderMapImpl{{":status", "100"}}}; - decoder_filters_[0]->callbacks_->encode100ContinueHeaders(std::move(continue_headers)); + decoder_filters_[0]->callbacks_->encode1xxHeaders(std::move(continue_headers)); EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) .WillOnce(Return(FilterHeadersStatus::Continue)); @@ -191,7 +191,7 @@ TEST_F(HttpConnectionManagerImplTest, 100ContinueResponseWithEncoderFilters) { doRemoteClose(); } -TEST_F(HttpConnectionManagerImplTest, PauseResume100Continue) { +TEST_F(HttpConnectionManagerImplTest, PauseResume1xx) { proxy_100_continue_ = true; setup(false, "envoy-custom-server", false); setUpEncoderAndDecoder(false, false); @@ -199,17 +199,17 @@ TEST_F(HttpConnectionManagerImplTest, PauseResume100Continue) { // Stop the 100-Continue at encoder filter 1. Encoder filter 0 should not yet receive the // 100-Continue - EXPECT_CALL(*encoder_filters_[1], encode100ContinueHeaders(_)) + EXPECT_CALL(*encoder_filters_[1], encode1xxHeaders(_)) .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*encoder_filters_[0], encode100ContinueHeaders(_)).Times(0); - EXPECT_CALL(response_encoder_, encode100ContinueHeaders(_)).Times(0); + EXPECT_CALL(*encoder_filters_[0], encode1xxHeaders(_)).Times(0); + EXPECT_CALL(response_encoder_, encode1xxHeaders(_)).Times(0); ResponseHeaderMapPtr continue_headers{new TestResponseHeaderMapImpl{{":status", "100"}}}; - decoder_filters_[1]->callbacks_->encode100ContinueHeaders(std::move(continue_headers)); + decoder_filters_[1]->callbacks_->encode1xxHeaders(std::move(continue_headers)); // Have the encoder filter 1 continue. Make sure the 100-Continue is resumed as expected. - EXPECT_CALL(*encoder_filters_[0], encode100ContinueHeaders(_)) + EXPECT_CALL(*encoder_filters_[0], encode1xxHeaders(_)) .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(response_encoder_, encode100ContinueHeaders(_)); + EXPECT_CALL(response_encoder_, encode1xxHeaders(_)); encoder_filters_[1]->callbacks_->continueEncoding(); EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) @@ -225,7 +225,7 @@ TEST_F(HttpConnectionManagerImplTest, PauseResume100Continue) { } // Regression test for https://github.com/envoyproxy/envoy/issues/10923. -TEST_F(HttpConnectionManagerImplTest, 100ContinueResponseWithDecoderPause) { +TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithDecoderPause) { proxy_100_continue_ = true; setup(false, "envoy-custom-server", false); @@ -264,7 +264,7 @@ TEST_F(HttpConnectionManagerImplTest, 100ContinueResponseWithDecoderPause) { decoder_->decodeData(data, false); ResponseHeaderMapPtr continue_headers{new TestResponseHeaderMapImpl{{":status", "100"}}}; - filter->callbacks_->encode100ContinueHeaders(std::move(continue_headers)); + filter->callbacks_->encode1xxHeaders(std::move(continue_headers)); // Resume decode pipeline after encoding 100 continue headers, we're now // ready to trigger #10923. @@ -2944,7 +2944,7 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterBidiData) { ResponseHeaderMapPtr response_continue_headers{ new TestResponseHeaderMapImpl{{":status", "100"}}}; EXPECT_CALL(*idle_timer, enableTimer(_, _)); - filter->callbacks_->encode100ContinueHeaders(std::move(response_continue_headers)); + filter->callbacks_->encode1xxHeaders(std::move(response_continue_headers)); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; EXPECT_CALL(*idle_timer, enableTimer(_, _)); @@ -2970,7 +2970,7 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterBidiData) { })); // 100 continue. - EXPECT_CALL(response_encoder_, encode100ContinueHeaders(_)); + EXPECT_CALL(response_encoder_, encode1xxHeaders(_)); // 200 upstream response. EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) diff --git a/test/common/http/http1/codec_impl_test.cc b/test/common/http/http1/codec_impl_test.cc index 86e8be81686ff..ba35778dfc4e0 100644 --- a/test/common/http/http1/codec_impl_test.cc +++ b/test/common/http/http1/codec_impl_test.cc @@ -1582,7 +1582,7 @@ TEST_F(Http1ServerConnectionImplTest, HeaderOnlyResponseWith100Then200) { ON_CALL(connection_, write(_, _)).WillByDefault(AddBufferToString(&output)); TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - response_encoder->encode100ContinueHeaders(continue_headers); + response_encoder->encode1xxHeaders(continue_headers); EXPECT_EQ("HTTP/1.1 100 Continue\r\n\r\n", output); output.clear(); @@ -2486,7 +2486,7 @@ TEST_F(Http1ClientConnectionImplTest, 204ResponseTransferEncodingNotAllowed) { } } -// 100 response followed by 200 results in a [decode100ContinueHeaders, decodeHeaders] sequence. +// 100 response followed by 200 results in a [decode1xxHeaders, decodeHeaders] sequence. TEST_F(Http1ClientConnectionImplTest, ContinueHeaders) { initialize(); @@ -2495,7 +2495,7 @@ TEST_F(Http1ClientConnectionImplTest, ContinueHeaders) { TestRequestHeaderMapImpl headers{{":method", "GET"}, {":path", "/"}, {":authority", "host"}}; EXPECT_TRUE(request_encoder.encodeHeaders(headers, true).ok()); - EXPECT_CALL(response_decoder, decode100ContinueHeaders_(_)); + EXPECT_CALL(response_decoder, decode1xxHeaders_(_)); EXPECT_CALL(response_decoder, decodeData(_, _)).Times(0); Buffer::OwnedImpl initial_response("HTTP/1.1 100 Continue\r\n\r\n"); auto status = codec_->dispatch(initial_response); @@ -2517,13 +2517,13 @@ TEST_F(Http1ClientConnectionImplTest, MultipleContinueHeaders) { TestRequestHeaderMapImpl headers{{":method", "GET"}, {":path", "/"}, {":authority", "host"}}; EXPECT_TRUE(request_encoder.encodeHeaders(headers, true).ok()); - EXPECT_CALL(response_decoder, decode100ContinueHeaders_(_)); + EXPECT_CALL(response_decoder, decode1xxHeaders_(_)); EXPECT_CALL(response_decoder, decodeData(_, _)).Times(0); Buffer::OwnedImpl initial_response("HTTP/1.1 100 Continue\r\n\r\n"); auto status = codec_->dispatch(initial_response); EXPECT_TRUE(status.ok()); - EXPECT_CALL(response_decoder, decode100ContinueHeaders_(_)); + EXPECT_CALL(response_decoder, decode1xxHeaders_(_)); EXPECT_CALL(response_decoder, decodeData(_, _)).Times(0); Buffer::OwnedImpl another_100_response("HTTP/1.1 100 Continue\r\n\r\n"); status = codec_->dispatch(another_100_response); diff --git a/test/common/http/http2/codec_impl_test.cc b/test/common/http/http2/codec_impl_test.cc index a23b056b9a775..47ca8418fc97c 100644 --- a/test/common/http/http2/codec_impl_test.cc +++ b/test/common/http/http2/codec_impl_test.cc @@ -366,7 +366,7 @@ TEST_P(Http2CodecImplTest, ProtocolErrorForTest) { EXPECT_EQ(StatusCode::CodecProtocolError, getStatusCode(raw_server->protocolErrorForTest())); } -// 100 response followed by 200 results in a [decode100ContinueHeaders, decodeHeaders] sequence. +// 100 response followed by 200 results in a [decode1xxHeaders, decodeHeaders] sequence. TEST_P(Http2CodecImplTest, ContinueHeaders) { initialize(); @@ -376,8 +376,8 @@ TEST_P(Http2CodecImplTest, ContinueHeaders) { EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_CALL(response_decoder_, decode100ContinueHeaders_(_)); - response_encoder_->encode100ContinueHeaders(continue_headers); + EXPECT_CALL(response_decoder_, decode1xxHeaders_(_)); + response_encoder_->encode1xxHeaders(continue_headers); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, true)); @@ -396,15 +396,15 @@ TEST_P(Http2CodecImplTest, TrailerStatus) { EXPECT_FALSE(Http2CodecImplTestFixture::slowContainsStreamId(100, *client_)); TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_CALL(response_decoder_, decode100ContinueHeaders_(_)); - response_encoder_->encode100ContinueHeaders(continue_headers); + EXPECT_CALL(response_decoder_, decode1xxHeaders_(_)); + response_encoder_->encode1xxHeaders(continue_headers); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); response_encoder_->encodeHeaders(response_headers, false); // nghttp2 doesn't allow :status in trailers - response_encoder_->encode100ContinueHeaders(continue_headers); + response_encoder_->encode1xxHeaders(continue_headers); EXPECT_FALSE(client_wrapper_.status_.ok()); EXPECT_TRUE(isCodecProtocolError(client_wrapper_.status_)); EXPECT_EQ(1, client_stats_store_.counter("http2.rx_messaging_error").value()); @@ -420,10 +420,10 @@ TEST_P(Http2CodecImplTest, MultipleContinueHeaders) { EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_CALL(response_decoder_, decode100ContinueHeaders_(_)); - response_encoder_->encode100ContinueHeaders(continue_headers); - EXPECT_CALL(response_decoder_, decode100ContinueHeaders_(_)); - response_encoder_->encode100ContinueHeaders(continue_headers); + EXPECT_CALL(response_decoder_, decode1xxHeaders_(_)); + response_encoder_->encode1xxHeaders(continue_headers); + EXPECT_CALL(response_decoder_, decode1xxHeaders_(_)); + response_encoder_->encode1xxHeaders(continue_headers); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, true)); @@ -539,8 +539,8 @@ TEST_P(Http2CodecImplTest, InvalidRepeatContinue) { EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_CALL(response_decoder_, decode100ContinueHeaders_(_)); - response_encoder_->encode100ContinueHeaders(continue_headers); + EXPECT_CALL(response_decoder_, decode1xxHeaders_(_)); + response_encoder_->encode1xxHeaders(continue_headers); response_encoder_->encodeHeaders(continue_headers, true); EXPECT_FALSE(client_wrapper_.status_.ok()); @@ -561,8 +561,8 @@ TEST_P(Http2CodecImplTest, InvalidRepeatContinueAllowed) { EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_CALL(response_decoder_, decode100ContinueHeaders_(_)); - response_encoder_->encode100ContinueHeaders(continue_headers); + EXPECT_CALL(response_decoder_, decode1xxHeaders_(_)); + response_encoder_->encode1xxHeaders(continue_headers); // Buffer client data to avoid mock recursion causing lifetime issues. ON_CALL(server_connection_, write(_, _)) diff --git a/test/common/quic/envoy_quic_client_stream_test.cc b/test/common/quic/envoy_quic_client_stream_test.cc index 854e72166ceec..a2b8ac4da7d8c 100644 --- a/test/common/quic/envoy_quic_client_stream_test.cc +++ b/test/common/quic/envoy_quic_client_stream_test.cc @@ -194,11 +194,11 @@ TEST_F(EnvoyQuicClientStreamTest, PostRequestAndResponse) { quic_stream_->OnStreamFrame(frame); } -TEST_F(EnvoyQuicClientStreamTest, PostRequestAnd100Continue) { +TEST_F(EnvoyQuicClientStreamTest, PostRequestAnd1xx) { const auto result = quic_stream_->encodeHeaders(request_headers_, false); EXPECT_TRUE(result.ok()); - EXPECT_CALL(stream_decoder_, decode100ContinueHeaders_(_)) + EXPECT_CALL(stream_decoder_, decode1xxHeaders_(_)) .WillOnce(Invoke([this](const Http::ResponseHeaderMapPtr& headers) { EXPECT_EQ("100", headers->getStatusValue()); EXPECT_EQ("0", headers->get(Http::LowerCaseString("i"))[0]->value().getStringView()); diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index ef956636f488f..105dadbba072b 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -3749,7 +3749,7 @@ TEST_F(RouterTest, RetryUpstreamResetResponseStarted) { } // The router filter is responsible for not propagating 100-continue headers after the initial 100. -TEST_F(RouterTest, Coalesce100ContinueHeaders) { +TEST_F(RouterTest, Coalesce1xxHeaders) { // Setup. NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; @@ -3771,23 +3771,25 @@ TEST_F(RouterTest, Coalesce100ContinueHeaders) { callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // Initial 100-continue, this is processed normally. - EXPECT_CALL(callbacks_, encode100ContinueHeaders_(_)); + EXPECT_CALL(callbacks_, encode1xxHeaders_(_)); { Http::ResponseHeaderMapPtr continue_headers( new Http::TestResponseHeaderMapImpl{{":status", "100"}}); - response_decoder->decode100ContinueHeaders(std::move(continue_headers)); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + response_decoder->decode1xxHeaders(std::move(continue_headers)); } EXPECT_EQ( 1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("upstream_rq_100").value()); - // No encode100ContinueHeaders() invocation for the second 100-continue (but we continue to track + // No encode1xxHeaders() invocation for the second 100-continue (but we continue to track // stats from upstream). - EXPECT_CALL(callbacks_, encode100ContinueHeaders_(_)).Times(0); + EXPECT_CALL(callbacks_, encode1xxHeaders_(_)).Times(0); { Http::ResponseHeaderMapPtr continue_headers( new Http::TestResponseHeaderMapImpl{{":status", "100"}}); - response_decoder->decode100ContinueHeaders(std::move(continue_headers)); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + response_decoder->decode1xxHeaders(std::move(continue_headers)); } EXPECT_EQ( 2U, @@ -3801,7 +3803,7 @@ TEST_F(RouterTest, Coalesce100ContinueHeaders) { callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); } -TEST_F(RouterTest, RetryUpstreamReset100ContinueResponseStarted) { +TEST_F(RouterTest, RetryUpstreamReset1xxResponseStarted) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) @@ -3825,10 +3827,11 @@ TEST_F(RouterTest, RetryUpstreamReset100ContinueResponseStarted) { // is reset we won't even check shouldRetryReset() (or shouldRetryHeaders()). EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _)).Times(0); EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).Times(0); - EXPECT_CALL(callbacks_, encode100ContinueHeaders_(_)); + EXPECT_CALL(callbacks_, encode1xxHeaders_(_)); Http::ResponseHeaderMapPtr continue_headers( new Http::TestResponseHeaderMapImpl{{":status", "100"}}); - response_decoder->decode100ContinueHeaders(std::move(continue_headers)); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + response_decoder->decode1xxHeaders(std::move(continue_headers)); EXPECT_EQ( 1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("upstream_rq_100").value()); diff --git a/test/common/upstream/health_checker_impl_test.cc b/test/common/upstream/health_checker_impl_test.cc index ba2dfe80fce2e..04f6686bed639 100644 --- a/test/common/upstream/health_checker_impl_test.cc +++ b/test/common/upstream/health_checker_impl_test.cc @@ -900,7 +900,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessIntervalJitterPercent) { } } -TEST_F(HttpHealthCheckerImplTest, SuccessWithSpurious100Continue) { +TEST_F(HttpHealthCheckerImplTest, SuccessWithSpurious1xx) { setupNoServiceValidationHC(); EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); @@ -921,8 +921,7 @@ TEST_F(HttpHealthCheckerImplTest, SuccessWithSpurious100Continue) { std::unique_ptr continue_headers( new Http::TestResponseHeaderMapImpl{{":status", "100"}}); - test_sessions_[0]->stream_response_callbacks_->decode100ContinueHeaders( - std::move(continue_headers)); + test_sessions_[0]->stream_response_callbacks_->decode1xxHeaders(std::move(continue_headers)); respond(0, "200", false, false, true); EXPECT_EQ(Host::Health::Healthy, cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->health()); diff --git a/test/extensions/filters/http/bandwidth_limit/filter_test.cc b/test/extensions/filters/http/bandwidth_limit/filter_test.cc index 338c53ffa6c40..7e2961cf90e11 100644 --- a/test/extensions/filters/http/bandwidth_limit/filter_test.cc +++ b/test/extensions/filters/http/bandwidth_limit/filter_test.cc @@ -218,8 +218,7 @@ TEST_F(FilterTest, LimitOnEncode) { EXPECT_EQ(1UL, config_->limit()); EXPECT_EQ(50UL, config_->fillInterval().count()); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); Http::MetadataMap metadata_map; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_->encodeMetadata(metadata_map)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); @@ -334,8 +333,7 @@ TEST_F(FilterTest, LimitOnDecodeAndEncode) { EXPECT_EQ(1UL, config_->limit()); EXPECT_EQ(50UL, config_->fillInterval().count()); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); Http::MetadataMap metadata_map; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_->decodeMetadata(metadata_map)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); @@ -467,8 +465,7 @@ TEST_F(FilterTest, WithTrailers) { EXPECT_EQ(1UL, config_->limit()); EXPECT_EQ(50UL, config_->fillInterval().count()); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); Http::MetadataMap metadata_map; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_->decodeMetadata(metadata_map)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); @@ -541,8 +538,7 @@ TEST_F(FilterTest, WithTrailersNoEndStream) { EXPECT_EQ(1UL, config_->limit()); EXPECT_EQ(50UL, config_->fillInterval().count()); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); Http::MetadataMap metadata_map; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_->decodeMetadata(metadata_map)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); diff --git a/test/extensions/filters/http/composite/filter_test.cc b/test/extensions/filters/http/composite/filter_test.cc index 93f65c5bd3974..9f609507a4e49 100644 --- a/test/extensions/filters/http/composite/filter_test.cc +++ b/test/extensions/filters/http/composite/filter_test.cc @@ -33,8 +33,7 @@ class FilterTest : public ::testing::Test { // Templated since MockStreamFilter and MockStreamEncoder filter doesn't share a mock base class. template void expectDelegatedEncoding(T& filter_mock) { - EXPECT_CALL(filter_mock, - encode100ContinueHeaders(HeaderMapEqualRef(&default_response_headers_))); + EXPECT_CALL(filter_mock, encode1xxHeaders(HeaderMapEqualRef(&default_response_headers_))); EXPECT_CALL(filter_mock, encodeHeaders(HeaderMapEqualRef(&default_response_headers_), false)); EXPECT_CALL(filter_mock, encodeMetadata(_)); EXPECT_CALL(filter_mock, encodeData(_, false)); @@ -57,7 +56,7 @@ class FilterTest : public ::testing::Test { } void doAllEncodingCallbacks() { - filter_.encode100ContinueHeaders(default_response_headers_); + filter_.encode1xxHeaders(default_response_headers_); filter_.encodeHeaders(default_response_headers_, false); diff --git a/test/extensions/filters/http/compressor/compressor_filter_test.cc b/test/extensions/filters/http/compressor/compressor_filter_test.cc index c95efec61682b..2a71f84c59c35 100644 --- a/test/extensions/filters/http/compressor/compressor_filter_test.cc +++ b/test/extensions/filters/http/compressor/compressor_filter_test.cc @@ -141,8 +141,7 @@ class CompressorFilterTest : public testing::Test { } populateBuffer(buffer_content_size); Http::TestResponseHeaderMapImpl continue_headers; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(continue_headers)); Http::MetadataMap metadata_map{{"metadata", "metadata"}}; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_->encodeMetadata(metadata_map)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(headers, false)); diff --git a/test/extensions/filters/http/cors/cors_filter_test.cc b/test/extensions/filters/http/cors/cors_filter_test.cc index 31c7f211cd25a..1a9dd4ed6f746 100644 --- a/test/extensions/filters/http/cors/cors_filter_test.cc +++ b/test/extensions/filters/http/cors/cors_filter_test.cc @@ -450,8 +450,7 @@ TEST_F(CorsFilterTest, EncodeWithAllowCredentialsTrue) { EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_.decodeTrailers(request_trailers_)); Http::TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_.encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.encode1xxHeaders(continue_headers)); Http::TestResponseHeaderMapImpl response_headers{}; EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.encodeHeaders(response_headers, false)); @@ -471,8 +470,7 @@ TEST_F(CorsFilterTest, EncodeWithExposeHeaders) { EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_.decodeTrailers(request_trailers_)); Http::TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_.encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.encode1xxHeaders(continue_headers)); Http::MetadataMap metadata_map{{"metadata", "metadata"}}; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_.encodeMetadata(metadata_map)); diff --git a/test/extensions/filters/http/dynamo/dynamo_filter_test.cc b/test/extensions/filters/http/dynamo/dynamo_filter_test.cc index 612537e8c1578..0ce73327d2941 100644 --- a/test/extensions/filters/http/dynamo/dynamo_filter_test.cc +++ b/test/extensions/filters/http/dynamo/dynamo_filter_test.cc @@ -64,8 +64,7 @@ TEST_F(DynamoFilterTest, OperatorPresent) { EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_->encodeMetadata(metadata_map)); Http::TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(continue_headers)); Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(stats_, counter("prefix.dynamodb.operation_missing")).Times(0); diff --git a/test/extensions/filters/http/ext_proc/filter_test.cc b/test/extensions/filters/http/ext_proc/filter_test.cc index 386f7401d48ff..ac5f1d689f9b9 100644 --- a/test/extensions/filters/http/ext_proc/filter_test.cc +++ b/test/extensions/filters/http/ext_proc/filter_test.cc @@ -559,7 +559,7 @@ TEST_F(HttpFilterTest, PostAndChangeRequestBodyBuffered) { response_headers_.addCopy(LowerCaseString("content-type"), "text/plain"); response_headers_.addCopy(LowerCaseString("content-length"), "3"); - EXPECT_EQ(FilterHeadersStatus::Continue, filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(FilterHeadersStatus::StopIteration, filter_->encodeHeaders(response_headers_, false)); processResponseHeaders(false, absl::nullopt); @@ -755,7 +755,7 @@ TEST_F(HttpFilterTest, PostAndChangeBothBodiesBufferedOneChunk) { response_headers_.addCopy(LowerCaseString("content-type"), "text/plain"); response_headers_.addCopy(LowerCaseString("content-length"), "100"); - EXPECT_EQ(FilterHeadersStatus::Continue, filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); Buffer::OwnedImpl resp_data; @@ -831,7 +831,7 @@ TEST_F(HttpFilterTest, PostAndChangeBothBodiesBufferedMultiChunk) { response_headers_.addCopy(LowerCaseString(":status"), "200"); response_headers_.addCopy(LowerCaseString("content-type"), "text/plain"); - EXPECT_EQ(FilterHeadersStatus::Continue, filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); Buffer::OwnedImpl resp_data_1; diff --git a/test/extensions/filters/http/fault/fault_filter_test.cc b/test/extensions/filters/http/fault/fault_filter_test.cc index 9b6e1347c9e3e..a9c4be91ecc26 100644 --- a/test/extensions/filters/http/fault/fault_filter_test.cc +++ b/test/extensions/filters/http/fault/fault_filter_test.cc @@ -1368,8 +1368,7 @@ TEST_F(FaultFilterRateLimitTest, ResponseRateLimitEnabled) { EXPECT_EQ(1UL, config_->stats().response_rl_injected_.value()); EXPECT_EQ(1UL, config_->stats().active_faults_.value()); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); Http::MetadataMap metadata_map; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_->encodeMetadata(metadata_map)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); diff --git a/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc b/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc index 96f7fb3aac75a..8f66fd52f6167 100644 --- a/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc +++ b/test/extensions/filters/http/grpc_http1_bridge/http1_bridge_filter_test.cc @@ -82,8 +82,7 @@ TEST_F(GrpcHttp1BridgeFilterTest, Http2HeaderOnlyResponse) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.decodeHeaders(request_headers, true)); Http::TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_.encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.encode1xxHeaders(continue_headers)); Http::MetadataMap metadata_map{{"metadata", "metadata"}}; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_.encodeMetadata(metadata_map)); @@ -109,8 +108,7 @@ TEST_F(GrpcHttp1BridgeFilterTest, StatsHttp2HeaderOnlyResponse) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.decodeHeaders(request_headers, true)); Http::TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_.encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.encode1xxHeaders(continue_headers)); Http::MetadataMap metadata_map{{"metadata", "metadata"}}; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_.encodeMetadata(metadata_map)); diff --git a/test/extensions/filters/http/grpc_json_transcoder/json_transcoder_filter_test.cc b/test/extensions/filters/http/grpc_json_transcoder/json_transcoder_filter_test.cc index e72327d13094b..8245856ad8d2f 100644 --- a/test/extensions/filters/http/grpc_json_transcoder/json_transcoder_filter_test.cc +++ b/test/extensions/filters/http/grpc_json_transcoder/json_transcoder_filter_test.cc @@ -476,8 +476,7 @@ TEST_F(GrpcJsonTranscoderFilterTest, TranscodingUnaryPost) { EXPECT_TRUE(MessageDifferencer::Equals(expected_request, request)); Http::TestResponseHeaderMapImpl continue_headers{{":status", "000"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_.encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.encode1xxHeaders(continue_headers)); Http::MetadataMap metadata_map{{"metadata", "metadata"}}; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_.encodeMetadata(metadata_map)); @@ -544,8 +543,7 @@ TEST_F(GrpcJsonTranscoderFilterTest, TranscodingUnaryPostWithPackageServiceMetho EXPECT_TRUE(MessageDifferencer::Equals(expected_request, request)); Http::TestResponseHeaderMapImpl continue_headers{{":status", "000"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_.encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.encode1xxHeaders(continue_headers)); Http::TestResponseHeaderMapImpl response_headers{{"content-type", "application/grpc"}, {":status", "200"}}; @@ -605,8 +603,7 @@ TEST_F(GrpcJsonTranscoderFilterTest, ForwardUnaryPostGrpc) { EXPECT_TRUE(MessageDifferencer::Equals(expected_request, forwarded_request)); Http::TestResponseHeaderMapImpl continue_headers{{":status", "000"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_.encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.encode1xxHeaders(continue_headers)); Http::TestResponseHeaderMapImpl response_headers{{"content-type", "application/grpc"}, {":status", "200"}}; @@ -669,8 +666,7 @@ TEST_F(GrpcJsonTranscoderFilterTest, ResponseBodyExceedsBufferLimit) { EXPECT_EQ(Http::FilterDataStatus::Continue, filter_.decodeData(request_data, true)); Http::TestResponseHeaderMapImpl continue_headers{{":status", "000"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_.encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.encode1xxHeaders(continue_headers)); Http::MetadataMap metadata_map{{"metadata", "metadata"}}; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_.encodeMetadata(metadata_map)); @@ -1245,8 +1241,7 @@ class GrpcJsonTranscoderFilterGrpcStatusTest : public GrpcJsonTranscoderFilterTe EXPECT_EQ(Http::FilterDataStatus::Continue, filter_.decodeData(request_data, true)); Http::TestResponseHeaderMapImpl continue_headers{{":status", "000"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_.encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.encode1xxHeaders(continue_headers)); } private: diff --git a/test/extensions/filters/http/grpc_stats/config_test.cc b/test/extensions/filters/http/grpc_stats/config_test.cc index 50602a60e3a9f..c8b15739736bb 100644 --- a/test/extensions/filters/http/grpc_stats/config_test.cc +++ b/test/extensions/filters/http/grpc_stats/config_test.cc @@ -79,8 +79,7 @@ TEST_F(GrpcStatsFilterConfigTest, StatsHttp2HeaderOnlyResponse) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true)); Http::TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(continue_headers)); Http::MetadataMap metadata_map{{"metadata", "metadata"}}; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_->encodeMetadata(metadata_map)); diff --git a/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc b/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc index 555c937e5da1e..561484f56d0d4 100644 --- a/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc +++ b/test/extensions/filters/http/grpc_web/grpc_web_filter_test.cc @@ -435,8 +435,7 @@ TEST_P(GrpcWebFilterTest, StatsNormalResponse) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.decodeHeaders(request_headers, false)); Http::TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_.encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.encode1xxHeaders(continue_headers)); Http::MetadataMap metadata_map{{"metadata", "metadata"}}; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_.encodeMetadata(metadata_map)); diff --git a/test/extensions/filters/http/header_to_metadata/header_to_metadata_filter_test.cc b/test/extensions/filters/http/header_to_metadata/header_to_metadata_filter_test.cc index 02c5d56f6e8ec..82b2dceffc244 100644 --- a/test/extensions/filters/http/header_to_metadata/header_to_metadata_filter_test.cc +++ b/test/extensions/filters/http/header_to_metadata/header_to_metadata_filter_test.cc @@ -203,8 +203,7 @@ TEST_F(HeaderToMetadataTest, HeaderRemovedTest) { EXPECT_CALL(req_info_, setDynamicMetadata("envoy.filters.http.header_to_metadata", MapEq(expected))); Http::TestResponseHeaderMapImpl continue_response{{":status", "100"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(continue_response)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(continue_response)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(incoming_headers, false)); EXPECT_EQ(empty_headers, incoming_headers); Http::MetadataMap metadata_map{{"metadata", "metadata"}}; diff --git a/test/extensions/filters/http/health_check/health_check_test.cc b/test/extensions/filters/http/health_check/health_check_test.cc index 2eaf8db8acb37..7dcf99d5129c4 100644 --- a/test/extensions/filters/http/health_check/health_check_test.cc +++ b/test/extensions/filters/http/health_check/health_check_test.cc @@ -274,8 +274,7 @@ TEST_F(HealthCheckFilterPassThroughTest, OkWithContinue) { // Goodness only knows why there would be a 100-Continue response in health // checks but we can still verify Envoy handles it. Http::TestResponseHeaderMapImpl continue_response{{":status", "100"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(continue_response)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(continue_response)); Http::MetadataMap metadata_map{{"metadata", "metadata"}}; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_->encodeMetadata(metadata_map)); Http::TestResponseHeaderMapImpl service_hc_response{{":status", "200"}}; diff --git a/test/extensions/filters/http/kill_request/kill_request_filter_test.cc b/test/extensions/filters/http/kill_request/kill_request_filter_test.cc index b6fde6589e2a3..37801cf88bb59 100644 --- a/test/extensions/filters/http/kill_request/kill_request_filter_test.cc +++ b/test/extensions/filters/http/kill_request/kill_request_filter_test.cc @@ -153,12 +153,11 @@ TEST_F(KillRequestFilterTest, DecodeTrailersReturnsContinue) { EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers)); } -TEST_F(KillRequestFilterTest, Encode100ContinueHeadersReturnsContinue) { +TEST_F(KillRequestFilterTest, Encode1xxHeadersReturnsContinue) { envoy::extensions::filters::http::kill_request::v3::KillRequest kill_request; setUpTest(kill_request); Http::TestResponseHeaderMapImpl response_headers; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers)); } TEST_F(KillRequestFilterTest, EncodeTrailersReturnsContinue) { diff --git a/test/extensions/filters/http/lua/lua_filter_test.cc b/test/extensions/filters/http/lua/lua_filter_test.cc index 9c34c4e081dee..f1438a29cb256 100644 --- a/test/extensions/filters/http/lua/lua_filter_test.cc +++ b/test/extensions/filters/http/lua/lua_filter_test.cc @@ -730,8 +730,7 @@ TEST_F(LuaHttpFilterTest, RequestAndResponse) { Http::TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; // No lua hooks for 100-continue EXPECT_CALL(*filter_, scriptLog(spdlog::level::trace, StrEq("100"))).Times(0); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(continue_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(continue_headers)); Http::MetadataMap metadata_map{{"metadata", "metadata"}}; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_->encodeMetadata(metadata_map)); diff --git a/test/extensions/filters/http/ratelimit/ratelimit_test.cc b/test/extensions/filters/http/ratelimit/ratelimit_test.cc index 8cc7751ea8e9c..9ce82f406696a 100644 --- a/test/extensions/filters/http/ratelimit/ratelimit_test.cc +++ b/test/extensions/filters/http/ratelimit/ratelimit_test.cc @@ -123,8 +123,7 @@ TEST_F(HttpRateLimitFilterTest, NoRoute) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); Http::MetadataMap metadata_map{{"metadata", "metadata"}}; EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_->encodeMetadata(metadata_map)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); @@ -140,8 +139,7 @@ TEST_F(HttpRateLimitFilterTest, NoCluster) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -155,8 +153,7 @@ TEST_F(HttpRateLimitFilterTest, NoApplicableRateLimit) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -171,8 +168,7 @@ TEST_F(HttpRateLimitFilterTest, NoDescriptor) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -186,8 +182,7 @@ TEST_F(HttpRateLimitFilterTest, RuntimeDisabled) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -221,8 +216,7 @@ TEST_F(HttpRateLimitFilterTest, OkResponse) { EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_->decodeMetadata(metadata_map)); EXPECT_EQ(Http::FilterDataStatus::StopIterationAndWatermark, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::StopIteration, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -264,8 +258,7 @@ TEST_F(HttpRateLimitFilterTest, OkResponseWithHeaders) { filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::StopIterationAndWatermark, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::StopIteration, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -321,8 +314,7 @@ TEST_F(HttpRateLimitFilterTest, OkResponseWithFilterHeaders) { filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::StopIterationAndWatermark, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::StopIteration, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -374,8 +366,7 @@ TEST_F(HttpRateLimitFilterTest, ImmediateOkResponse) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -405,8 +396,7 @@ TEST_F(HttpRateLimitFilterTest, ImmediateErrorResponse) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -594,8 +584,7 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithHeaders) { EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, filter_->decodeHeaders(request_headers_, false)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -647,8 +636,7 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithBody) { EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, filter_->decodeHeaders(request_headers_, false)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -711,8 +699,7 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithBodyAndContentType) { EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, filter_->decodeHeaders(request_headers_, false)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -784,8 +771,7 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithFilterHeaders) { EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, filter_->decodeHeaders(request_headers_, false)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -883,8 +869,7 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseRuntimeDisabled) { EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -933,8 +918,7 @@ TEST_F(HttpRateLimitFilterTest, RouteRateLimitDisabledForRouteKey) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -953,8 +937,7 @@ TEST_F(HttpRateLimitFilterTest, VirtualHostRateLimitDisabledForRouteKey) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -975,8 +958,7 @@ TEST_F(HttpRateLimitFilterTest, IncorrectRequestType) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -996,8 +978,7 @@ TEST_F(HttpRateLimitFilterTest, IncorrectRequestType) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -1036,8 +1017,7 @@ TEST_F(HttpRateLimitFilterTest, InternalRequestType) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -1080,8 +1060,7 @@ TEST_F(HttpRateLimitFilterTest, ExternalRequestType) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -1134,8 +1113,7 @@ TEST_F(HttpRateLimitFilterTest, DEPRECATED_FEATURE_TEST(ExcludeVirtualHost)) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -1185,8 +1163,7 @@ TEST_F(HttpRateLimitFilterTest, OverrideVHRateLimitOptionWithRouteRateLimitSet) EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -1236,8 +1213,7 @@ TEST_F(HttpRateLimitFilterTest, OverrideVHRateLimitOptionWithoutRouteRateLimit) EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -1284,8 +1260,7 @@ TEST_F(HttpRateLimitFilterTest, IncludeVHRateLimitOptionWithOnlyVHRateLimitSet) EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -1334,8 +1309,7 @@ TEST_F(HttpRateLimitFilterTest, IncludeVHRateLimitOptionWithRouteAndVHRateLimitS EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -1382,8 +1356,7 @@ TEST_F(HttpRateLimitFilterTest, IgnoreVHRateLimitOptionWithRouteRateLimitSet) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); @@ -1418,8 +1391,7 @@ TEST_F(HttpRateLimitFilterTest, IgnoreVHRateLimitOptionWithOutRouteRateLimit) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers_)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); diff --git a/test/extensions/filters/http/tap/tap_filter_test.cc b/test/extensions/filters/http/tap/tap_filter_test.cc index 06ca5fcbff38b..c7666fcd5b56c 100644 --- a/test/extensions/filters/http/tap/tap_filter_test.cc +++ b/test/extensions/filters/http/tap/tap_filter_test.cc @@ -80,8 +80,7 @@ TEST_F(TapFilterTest, NoConfig) { EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers)); Http::TestResponseHeaderMapImpl response_headers; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers, false)); Buffer::OwnedImpl response_body; EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_body, false)); @@ -109,8 +108,7 @@ TEST_F(TapFilterTest, Config) { EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers)); Http::TestResponseHeaderMapImpl response_headers; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, - filter_->encode100ContinueHeaders(response_headers)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers)); EXPECT_CALL(*http_per_request_tapper_, onResponseHeaders(_)); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers, false)); Buffer::OwnedImpl response_body("hello"); diff --git a/test/extensions/filters/http/wasm/wasm_filter_test.cc b/test/extensions/filters/http/wasm/wasm_filter_test.cc index 3077c669b51b1..000e65353f051 100644 --- a/test/extensions/filters/http/wasm/wasm_filter_test.cc +++ b/test/extensions/filters/http/wasm/wasm_filter_test.cc @@ -150,8 +150,7 @@ TEST_P(WasmHttpFilterTest, HeadersOnlyRequestHeadersOnlyWithEnvVars) { EXPECT_EQ(filter().closeStream(static_cast(9999)), proxy_wasm::WasmResult::BadArgument); Http::TestResponseHeaderMapImpl response_headers; - EXPECT_EQ(filter().encode100ContinueHeaders(response_headers), - Http::FilterHeadersStatus::Continue); + EXPECT_EQ(filter().encode1xxHeaders(response_headers), Http::FilterHeadersStatus::Continue); filter().onDestroy(); } diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index e72b03a52fe75..0e976a30e9d9c 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -73,7 +73,7 @@ void FakeStream::postToConnectionThread(std::function cb) { parent_.postToConnectionThread(cb); } -void FakeStream::encode100ContinueHeaders(const Http::ResponseHeaderMap& headers) { +void FakeStream::encode1xxHeaders(const Http::ResponseHeaderMap& headers) { std::shared_ptr headers_copy( Http::createHeaderMap(headers)); postToConnectionThread([this, headers_copy]() -> void { @@ -84,7 +84,7 @@ void FakeStream::encode100ContinueHeaders(const Http::ResponseHeaderMap& headers return; } } - encoder_.encode100ContinueHeaders(*headers_copy); + encoder_.encode1xxHeaders(*headers_copy); }); } diff --git a/test/integration/fake_upstream.h b/test/integration/fake_upstream.h index c282ad207ee0a..5f7c74ee20d43 100644 --- a/test/integration/fake_upstream.h +++ b/test/integration/fake_upstream.h @@ -80,7 +80,7 @@ class FakeStream : public Http::RequestDecoder, // allows execution of non-interrupted sequences of operations on the fake stream which may run // into trouble if client-side events are interleaved. void postToConnectionThread(std::function cb); - void encode100ContinueHeaders(const Http::ResponseHeaderMap& headers); + void encode1xxHeaders(const Http::ResponseHeaderMap& headers); void encodeHeaders(const Http::HeaderMap& headers, bool end_stream); void encodeData(uint64_t size, bool end_stream); void encodeData(Buffer::Instance& data, bool end_stream); diff --git a/test/integration/filters/response_metadata_filter.cc b/test/integration/filters/response_metadata_filter.cc index 73fdee7008ec7..ababbe0dbd5e7 100644 --- a/test/integration/filters/response_metadata_filter.cc +++ b/test/integration/filters/response_metadata_filter.cc @@ -11,7 +11,7 @@ namespace Envoy { // A filter tests response metadata process. The filter inserts new -// metadata when encodeHeaders/Data/Trailers/100ContinueHeaders/Metadata() are called, and consumes +// metadata when encodeHeaders/Data/Trailers/1xxHeaders/Metadata() are called, and consumes // metadata in encodeMetadata(). class ResponseMetadataStreamFilter : public Http::PassThroughFilter { public: @@ -43,7 +43,7 @@ class ResponseMetadataStreamFilter : public Http::PassThroughFilter { } // Inserts two metadata_maps by calling decoder_callbacks_->encodeMetadata() twice. - Http::FilterHeadersStatus encode100ContinueHeaders(Http::ResponseHeaderMap&) override { + Http::FilterHeadersStatus encode1xxHeaders(Http::ResponseHeaderMap&) override { Http::MetadataMap metadata_map = {{"100-continue", "100-continue"}, {"duplicate", "duplicate"}}; Http::MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); encoder_callbacks_->addEncodedMetadata(std::move(metadata_map_ptr)); diff --git a/test/integration/http2_flood_integration_test.cc b/test/integration/http2_flood_integration_test.cc index ddd9810a86b9f..7a6a590e8a4cf 100644 --- a/test/integration/http2_flood_integration_test.cc +++ b/test/integration/http2_flood_integration_test.cc @@ -418,7 +418,7 @@ TEST_P(Http2FloodMitigationTest, Headers) { } // Verify that the server can detect overflow by 100 continue response sent by Envoy itself -TEST_P(Http2FloodMitigationTest, Envoy100ContinueHeaders) { +TEST_P(Http2FloodMitigationTest, Envoy1xxHeaders) { // pre-fill one away from overflow prefillOutboundDownstreamQueue(AllFrameFloodLimit - 1); diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index 61418440f839e..ab8b5bd93049f 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -955,9 +955,8 @@ void HttpIntegrationTest::testGrpcRetry() { } } -void HttpIntegrationTest::testEnvoyHandling100Continue(bool additional_continue_from_upstream, - const std::string& via, - bool disconnect_after_100) { +void HttpIntegrationTest::testEnvoyHandling1xx(bool additional_continue_from_upstream, + const std::string& via, bool disconnect_after_100) { useAccessLog("%RESPONSE_CODE%"); initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -991,8 +990,7 @@ void HttpIntegrationTest::testEnvoyHandling100Continue(bool additional_continue_ if (additional_continue_from_upstream) { // Make sure if upstream sends an 100-Continue Envoy doesn't send its own and proxy the one // from upstream! - upstream_request_->encode100ContinueHeaders( - Http::TestResponseHeaderMapImpl{{":status", "100"}}); + upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); } if (disconnect_after_100) { @@ -1048,16 +1046,13 @@ void HttpIntegrationTest::testEnvoyProxying1xx(bool continue_before_upstream_com if (continue_before_upstream_complete) { if (with_multiple_1xx_headers) { - upstream_request_->encode100ContinueHeaders( - Http::TestResponseHeaderMapImpl{{":status", "100"}}); + upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "102"}}, false); - upstream_request_->encode100ContinueHeaders( - Http::TestResponseHeaderMapImpl{{":status", "100"}}); + upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); } // This case tests sending on 100-Continue headers before the client has sent all the // request data. - upstream_request_->encode100ContinueHeaders( - Http::TestResponseHeaderMapImpl{{":status", "100"}}); + upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); response->waitForContinueHeaders(); } // Send all of the request data and wait for it to be received upstream. @@ -1066,15 +1061,12 @@ void HttpIntegrationTest::testEnvoyProxying1xx(bool continue_before_upstream_com if (!continue_before_upstream_complete) { if (with_multiple_1xx_headers) { - upstream_request_->encode100ContinueHeaders( - Http::TestResponseHeaderMapImpl{{":status", "100"}}); + upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "102"}}, false); - upstream_request_->encode100ContinueHeaders( - Http::TestResponseHeaderMapImpl{{":status", "100"}}); + upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); } // This case tests forwarding 100-Continue after the client has sent all data. - upstream_request_->encode100ContinueHeaders( - Http::TestResponseHeaderMapImpl{{":status", "100"}}); + upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); response->waitForContinueHeaders(); } // Now send the rest of the response. diff --git a/test/integration/http_integration.h b/test/integration/http_integration.h index 07b2a67891e62..7bdc2b749e2e6 100644 --- a/test/integration/http_integration.h +++ b/test/integration/http_integration.h @@ -248,8 +248,8 @@ class HttpIntegrationTest : public BaseIntegrationTest { void testRetryAttemptCountHeader(); void testGrpcRetry(); - void testEnvoyHandling100Continue(bool additional_continue_from_upstream = false, - const std::string& via = "", bool disconnect_after_100 = false); + void testEnvoyHandling1xx(bool additional_continue_from_upstream = false, + const std::string& via = "", bool disconnect_after_100 = false); void testEnvoyProxying1xx(bool continue_before_upstream_complete = false, bool with_encoder_filter = false, bool with_multiple_1xx_headers = false); diff --git a/test/integration/idle_timeout_integration_test.cc b/test/integration/idle_timeout_integration_test.cc index 0acf5ef9515ce..167a79630c707 100644 --- a/test/integration/idle_timeout_integration_test.cc +++ b/test/integration/idle_timeout_integration_test.cc @@ -44,7 +44,7 @@ class IdleTimeoutIntegrationTest : public HttpProtocolIntegrationTest { retry_policy->mutable_per_try_idle_timeout()->set_nanos(IdleTimeoutMs * 1000 * 1000); } - // For validating encode100ContinueHeaders() timer kick. + // For validating encode1xxHeaders() timer kick. hcm.set_proxy_100_continue(true); }); HttpProtocolIntegrationTest::initialize(); @@ -357,7 +357,7 @@ TEST_P(IdleTimeoutIntegrationTest, PerStreamIdleTimeoutAfterBidiData) { auto response = setupPerStreamIdleTimeoutTest(); sleep(); - upstream_request_->encode100ContinueHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); + upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); sleep(); upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, false); @@ -472,11 +472,11 @@ TEST_P(IdleTimeoutIntegrationTest, RequestTimeoutIsDisarmedByPrematureEncodeHead EXPECT_NE("request timeout", response->body()); } -TEST_P(IdleTimeoutIntegrationTest, RequestTimeoutIsNotDisarmedByEncode100ContinueHeaders) { +TEST_P(IdleTimeoutIntegrationTest, RequestTimeoutIsNotDisarmedByEncode1xxHeaders) { enable_request_timeout_ = true; auto response = setupPerStreamIdleTimeoutTest("POST"); - upstream_request_->encode100ContinueHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); + upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); waitForTimeout(*response, "downstream_rq_timeout"); diff --git a/test/integration/integration_stream_decoder.cc b/test/integration/integration_stream_decoder.cc index b4cc939b87bfe..8bf5307a268bb 100644 --- a/test/integration/integration_stream_decoder.cc +++ b/test/integration/integration_stream_decoder.cc @@ -84,7 +84,7 @@ AssertionResult IntegrationStreamDecoder::waitForReset(std::chrono::milliseconds return AssertionSuccess(); } -void IntegrationStreamDecoder::decode100ContinueHeaders(Http::ResponseHeaderMapPtr&& headers) { +void IntegrationStreamDecoder::decode1xxHeaders(Http::ResponseHeaderMapPtr&& headers) { continue_headers_ = std::move(headers); if (waiting_for_continue_headers_) { dispatcher_.exit(); diff --git a/test/integration/integration_stream_decoder.h b/test/integration/integration_stream_decoder.h index 317ea6e1c8473..210a7dfa488c4 100644 --- a/test/integration/integration_stream_decoder.h +++ b/test/integration/integration_stream_decoder.h @@ -52,7 +52,7 @@ class IntegrationStreamDecoder : public Http::ResponseDecoder, public Http::Stre void decodeMetadata(Http::MetadataMapPtr&& metadata_map) override; // Http::ResponseDecoder - void decode100ContinueHeaders(Http::ResponseHeaderMapPtr&& headers) override; + void decode1xxHeaders(Http::ResponseHeaderMapPtr&& headers) override; void decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool end_stream) override; void decodeTrailers(Http::ResponseTrailerMapPtr&& trailers) override; void dumpState(std::ostream& os, int indent_level) const override { diff --git a/test/integration/integration_test.cc b/test/integration/integration_test.cc index e8e6c0562310a..67003055050ab 100644 --- a/test/integration/integration_test.cc +++ b/test/integration/integration_test.cc @@ -407,16 +407,16 @@ TEST_P(IntegrationTest, RouterUpstreamResponseBeforeRequestComplete) { testRouterUpstreamResponseBeforeRequestComplete(); } -TEST_P(IntegrationTest, EnvoyProxyingEarly100ContinueWithEncoderFilter) { +TEST_P(IntegrationTest, EnvoyProxyingEarly1xxWithEncoderFilter) { testEnvoyProxying1xx(true, true); } -TEST_P(IntegrationTest, EnvoyProxyingLate100ContinueWithEncoderFilter) { +TEST_P(IntegrationTest, EnvoyProxyingLate1xxWithEncoderFilter) { testEnvoyProxying1xx(false, true); } // Regression test for https://github.com/envoyproxy/envoy/issues/10923. -TEST_P(IntegrationTest, EnvoyProxying100ContinueWithDecodeDataPause) { +TEST_P(IntegrationTest, EnvoyProxying1xxWithDecodeDataPause) { config_helper_.prependFilter(R"EOF( name: stop-iteration-and-continue-filter typed_config: @@ -1468,9 +1468,9 @@ TEST_P(IntegrationTest, ViaAppendHeaderOnly) { // Validate that 100-continue works as expected with via header addition on both request and // response path. -TEST_P(IntegrationTest, ViaAppendWith100Continue) { +TEST_P(IntegrationTest, ViaAppendWith1xx) { config_helper_.addConfigModifier(setVia("foo")); - testEnvoyHandling100Continue(false, "foo"); + testEnvoyHandling1xx(false, "foo"); } // Test delayed close semantics for downstream HTTP/1.1 connections. When an early response is diff --git a/test/integration/multiplexed_integration_test.cc b/test/integration/multiplexed_integration_test.cc index fbde3f043ace9..df5622fff917c 100644 --- a/test/integration/multiplexed_integration_test.cc +++ b/test/integration/multiplexed_integration_test.cc @@ -506,7 +506,7 @@ TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { 10); waitForNextUpstreamRequest(); - upstream_request_->encode100ContinueHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); + upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); response->waitForContinueHeaders(); upstream_request_->encodeHeaders(default_response_headers_, false); upstream_request_->encodeData(100, true); diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 50a7d249fe9c2..5946dc06a3c27 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -1318,19 +1318,15 @@ TEST_P(ProtocolIntegrationTest, HittingEncoderFilterLimit) { // The downstream connection is closed when it is read disabled, and on OSX the // connection error is not detected under these circumstances. #if !defined(__APPLE__) -TEST_P(ProtocolIntegrationTest, 100ContinueAndClose) { - testEnvoyHandling100Continue(false, "", true); -} +TEST_P(ProtocolIntegrationTest, 1xxAndClose) { testEnvoyHandling1xx(false, "", true); } #endif -TEST_P(ProtocolIntegrationTest, EnvoyHandling100Continue) { testEnvoyHandling100Continue(); } +TEST_P(ProtocolIntegrationTest, EnvoyHandling1xx) { testEnvoyHandling1xx(); } -TEST_P(ProtocolIntegrationTest, EnvoyHandlingDuplicate100Continue) { - testEnvoyHandling100Continue(true); -} +TEST_P(ProtocolIntegrationTest, EnvoyHandlingDuplicate1xx) { testEnvoyHandling1xx(true); } // 100-continue before the request completes. -TEST_P(ProtocolIntegrationTest, EnvoyProxyingEarly100Continue) { testEnvoyProxying1xx(true); } +TEST_P(ProtocolIntegrationTest, EnvoyProxyingEarly1xx) { testEnvoyProxying1xx(true); } // Multiple 1xx before the request completes. TEST_P(ProtocolIntegrationTest, EnvoyProxyingEarlyMultiple1xx) { @@ -1338,7 +1334,7 @@ TEST_P(ProtocolIntegrationTest, EnvoyProxyingEarlyMultiple1xx) { } // 100-continue after the request completes. -TEST_P(ProtocolIntegrationTest, EnvoyProxyingLate100Continue) { testEnvoyProxying1xx(false); } +TEST_P(ProtocolIntegrationTest, EnvoyProxyingLate1xx) { testEnvoyProxying1xx(false); } // Multiple 1xx after the request completes. TEST_P(ProtocolIntegrationTest, EnvoyProxyingLateMultiple1xx) { @@ -2746,7 +2742,7 @@ TEST_P(DownstreamProtocolIntegrationTest, Test100AndDisconnect) { codec_client_ = makeHttpConnection(lookupPort("http")); auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); waitForNextUpstreamRequest(); - upstream_request_->encode100ContinueHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); + upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); ASSERT_TRUE(fake_upstream_connection_->close()); // Make sure that a disconnect results in valid 5xx response headers even when preceded by a 100. diff --git a/test/integration/utility.h b/test/integration/utility.h index 8f7e36178c91c..8db43d1d86e9d 100644 --- a/test/integration/utility.h +++ b/test/integration/utility.h @@ -39,7 +39,7 @@ class BufferingStreamDecoder : public Http::ResponseDecoder, public Http::Stream void decodeMetadata(Http::MetadataMapPtr&&) override {} // Http::ResponseDecoder - void decode100ContinueHeaders(Http::ResponseHeaderMapPtr&&) override {} + void decode1xxHeaders(Http::ResponseHeaderMapPtr&&) override {} void decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool end_stream) override; void decodeTrailers(Http::ResponseTrailerMapPtr&& trailers) override; void dumpState(std::ostream& os, int indent_level) const override { diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index 11db5041411af..1ac03a1429824 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -59,7 +59,7 @@ class MockFilterManagerCallbacks : public FilterManagerCallbacks { ~MockFilterManagerCallbacks() override; MOCK_METHOD(void, encodeHeaders, (ResponseHeaderMap&, bool)); - MOCK_METHOD(void, encode100ContinueHeaders, (ResponseHeaderMap&)); + MOCK_METHOD(void, encode1xxHeaders, (ResponseHeaderMap&)); MOCK_METHOD(void, encodeData, (Buffer::Instance&, bool)); MOCK_METHOD(void, encodeTrailers, (ResponseTrailerMap&)); MOCK_METHOD(void, encodeMetadata, (MetadataMapVector&)); @@ -232,9 +232,7 @@ class MockStreamDecoderFilterCallbacks : public StreamDecoderFilterCallbacks, const absl::optional grpc_status, absl::string_view details); - void encode100ContinueHeaders(ResponseHeaderMapPtr&& headers) override { - encode100ContinueHeaders_(*headers); - } + void encode1xxHeaders(ResponseHeaderMapPtr&& headers) override { encode1xxHeaders_(*headers); } MOCK_METHOD(ResponseHeaderMapOptRef, continueHeaders, (), (const)); void encodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream, absl::string_view details) override { @@ -261,7 +259,7 @@ class MockStreamDecoderFilterCallbacks : public StreamDecoderFilterCallbacks, MOCK_METHOD(MetadataMapVector&, addDecodedMetadata, ()); MOCK_METHOD(const Buffer::Instance*, decodingBuffer, ()); MOCK_METHOD(void, modifyDecodingBuffer, (std::function)); - MOCK_METHOD(void, encode100ContinueHeaders_, (HeaderMap & headers)); + MOCK_METHOD(void, encode1xxHeaders_, (HeaderMap & headers)); MOCK_METHOD(void, encodeHeaders_, (ResponseHeaderMap & headers, bool end_stream)); MOCK_METHOD(void, encodeData, (Buffer::Instance & data, bool end_stream)); MOCK_METHOD(void, encodeTrailers_, (ResponseTrailerMap & trailers)); @@ -372,7 +370,7 @@ class MockStreamEncoderFilter : public StreamEncoderFilter { MOCK_METHOD(LocalErrorStatus, onLocalReply, (const LocalReplyData&)); // Http::MockStreamEncoderFilter - MOCK_METHOD(FilterHeadersStatus, encode100ContinueHeaders, (ResponseHeaderMap & headers)); + MOCK_METHOD(FilterHeadersStatus, encode1xxHeaders, (ResponseHeaderMap & headers)); MOCK_METHOD(FilterHeadersStatus, encodeHeaders, (ResponseHeaderMap & headers, bool end_stream)); MOCK_METHOD(FilterDataStatus, encodeData, (Buffer::Instance & data, bool end_stream)); MOCK_METHOD(FilterTrailersStatus, encodeTrailers, (ResponseTrailerMap & trailers)); @@ -403,7 +401,7 @@ class MockStreamFilter : public StreamFilter { MOCK_METHOD(void, decodeComplete, ()); // Http::MockStreamEncoderFilter - MOCK_METHOD(FilterHeadersStatus, encode100ContinueHeaders, (ResponseHeaderMap & headers)); + MOCK_METHOD(FilterHeadersStatus, encode1xxHeaders, (ResponseHeaderMap & headers)); MOCK_METHOD(ResponseHeaderMapOptRef, continueHeaders, (), (const)); MOCK_METHOD(FilterHeadersStatus, encodeHeaders, (ResponseHeaderMap & headers, bool end_stream)); MOCK_METHOD(ResponseHeaderMapOptRef, responseHeaders, (), (const)); diff --git a/test/mocks/http/stream_decoder.h b/test/mocks/http/stream_decoder.h index 3e930a629cce4..8bf5e5305d4fd 100644 --- a/test/mocks/http/stream_decoder.h +++ b/test/mocks/http/stream_decoder.h @@ -44,16 +44,14 @@ class MockResponseDecoder : public ResponseDecoder { MOCK_METHOD(void, decodeData, (Buffer::Instance & data, bool end_stream)); MOCK_METHOD(void, decodeMetadata_, (MetadataMapPtr & metadata_map)); - void decode100ContinueHeaders(ResponseHeaderMapPtr&& headers) override { - decode100ContinueHeaders_(headers); - } + void decode1xxHeaders(ResponseHeaderMapPtr&& headers) override { decode1xxHeaders_(headers); } void decodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream) override { decodeHeaders_(headers, end_stream); } void decodeTrailers(ResponseTrailerMapPtr&& trailers) override { decodeTrailers_(trailers); } // Http::ResponseDecoder - MOCK_METHOD(void, decode100ContinueHeaders_, (ResponseHeaderMapPtr & headers)); + MOCK_METHOD(void, decode1xxHeaders_, (ResponseHeaderMapPtr & headers)); MOCK_METHOD(void, decodeHeaders_, (ResponseHeaderMapPtr & headers, bool end_stream)); MOCK_METHOD(void, decodeTrailers_, (ResponseTrailerMapPtr & trailers)); MOCK_METHOD(void, dumpState, (std::ostream&, int), (const)); diff --git a/test/mocks/http/stream_encoder.h b/test/mocks/http/stream_encoder.h index 8f783e69b283c..c15da88f170d2 100644 --- a/test/mocks/http/stream_encoder.h +++ b/test/mocks/http/stream_encoder.h @@ -45,7 +45,7 @@ class MockResponseEncoder : public ResponseEncoder { ~MockResponseEncoder() override; // Http::ResponseEncoder - MOCK_METHOD(void, encode100ContinueHeaders, (const ResponseHeaderMap& headers)); + MOCK_METHOD(void, encode1xxHeaders, (const ResponseHeaderMap& headers)); MOCK_METHOD(void, encodeHeaders, (const ResponseHeaderMap& headers, bool end_stream)); MOCK_METHOD(void, encodeTrailers, (const ResponseTrailerMap& trailers)); diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index e9ccd1de05a2e..b6439818f4051 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -573,7 +573,7 @@ class MockUpstreamToDownstream : public UpstreamToDownstream { MOCK_METHOD(void, decodeData, (Buffer::Instance&, bool)); MOCK_METHOD(void, decodeMetadata, (Http::MetadataMapPtr &&)); - MOCK_METHOD(void, decode100ContinueHeaders, (Http::ResponseHeaderMapPtr &&)); + MOCK_METHOD(void, decode1xxHeaders, (Http::ResponseHeaderMapPtr &&)); MOCK_METHOD(void, decodeHeaders, (Http::ResponseHeaderMapPtr&&, bool)); MOCK_METHOD(void, decodeTrailers, (Http::ResponseTrailerMapPtr &&)); MOCK_METHOD(void, dumpState, (std::ostream&, int), (const)); diff --git a/test/mocks/router/router_filter_interface.h b/test/mocks/router/router_filter_interface.h index d1349a4103ce1..3116cf251d0ac 100644 --- a/test/mocks/router/router_filter_interface.h +++ b/test/mocks/router/router_filter_interface.h @@ -16,7 +16,7 @@ class MockRouterFilterInterface : public RouterFilterInterface { MockRouterFilterInterface(); ~MockRouterFilterInterface() override; - MOCK_METHOD(void, onUpstream100ContinueHeaders, + MOCK_METHOD(void, onUpstream1xxHeaders, (Envoy::Http::ResponseHeaderMapPtr && headers, UpstreamRequest& upstream_request)); MOCK_METHOD(void, onUpstreamHeaders, (uint64_t response_code, Envoy::Http::ResponseHeaderMapPtr&& headers, From 1a8b09f5bd7770293e9dce39c1d87d19999166b8 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 10 Nov 2021 09:15:02 -0800 Subject: [PATCH 062/110] bazel: fix macOS build (#18920) It seems that some combination of macOS Monterey and Xcode 13 started causing a build issue. The problem is that `mach/boolean.h` adds macros for `TRUE` and `FALSE` which conflicts with some of Envoy's own uses of these. We never import this directly so it's possible these macros are not new, but just exposed through headers in a new way. The core fix here is to patch cel-cpp working around https://github.com/google/cel-cpp/issues/121 The secondary fix is to remove this macro as envoy already did for windows. This stops any code that imports the platform header from hitting this in the future. This fixes: https://github.com/envoyproxy/envoy/issues/18842 Signed-off-by: Keith Smiley --- bazel/repository_locations.bzl | 6 +++--- envoy/common/platform.h | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index a88b25bfeb2c6..ebf0436a99963 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -863,8 +863,8 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Common Expression Language (CEL) C++ library", project_desc = "Common Expression Language (CEL) C++ library", project_url = "https://opensource.google/projects/cel", - version = "89d81b2d2c24943b6e4fd5e8fc321099c2ab6d3f", - sha256 = "1408ef31e77ed847b420ff108da9652ad1702401008f2a75b671fba860a9707d", + version = "60c7aeabb4e6fa633b49c14d6c6fc8f0516761b9", + sha256 = "7cb1e8ce293182e1d28321d4d6baecdacbc263cffcd9da1f7ffd25312611a329", strip_prefix = "cel-cpp-{version}", urls = ["https://github.com/google/cel-cpp/archive/{version}.tar.gz"], use_category = ["dataplane_ext"], @@ -879,7 +879,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( "envoy.stat_sinks.wasm", "envoy.rbac.matchers.upstream_ip_port", ], - release_date = "2021-10-07", + release_date = "2021-11-08", cpe = "N/A", ), com_github_google_flatbuffers = dict( diff --git a/envoy/common/platform.h b/envoy/common/platform.h index cefe0da63a31e..8ae695d46c274 100644 --- a/envoy/common/platform.h +++ b/envoy/common/platform.h @@ -205,6 +205,8 @@ constexpr bool win32SupportsOriginalDestination() { #define be16toh(x) OSSwapBigToHostInt16((x)) #define be32toh(x) OSSwapBigToHostInt32((x)) #define be64toh(x) OSSwapBigToHostInt64((x)) + +#undef TRUE #else #include #endif From fa308957f747dcadce9af48e1bd6b0f87de7e2c4 Mon Sep 17 00:00:00 2001 From: phlax Date: Wed, 10 Nov 2021 18:43:26 +0000 Subject: [PATCH 063/110] deps: Bump `com_github_curl` -> 7.80.0 (#18957) * deps: Bump `com_github_curl` -> 7.80.0 Signed-off-by: Ryan Northey --- bazel/repository_locations.bzl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index ebf0436a99963..c5ac92c86cae6 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -805,8 +805,8 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "curl", project_desc = "Library for transferring data with URLs", project_url = "https://curl.haxx.se", - version = "7.79.1", - sha256 = "370b11201349816287fb0ccc995e420277fbfcaf76206e309b3f60f0eda090c2", + version = "7.80.0", + sha256 = "dab997c9b08cb4a636a03f2f7f985eaba33279c1c52692430018fae4a4878dc7", strip_prefix = "curl-{version}", urls = ["https://github.com/curl/curl/releases/download/curl-{underscore_version}/curl-{version}.tar.gz"], use_category = ["dataplane_ext", "observability_ext"], @@ -816,7 +816,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( "envoy.grpc_credentials.aws_iam", "envoy.tracers.opencensus", ], - release_date = "2021-09-22", + release_date = "2021-11-10", cpe = "cpe:2.3:a:haxx:libcurl:*", ), com_googlesource_chromium_v8 = dict( From a1487bfc49365f2bdf621c4451505a88b7bc1287 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 10 Nov 2021 14:51:42 -0500 Subject: [PATCH 064/110] http: making upstream ALPN accesible. (#18884) Part of envoyproxy/envoy-mobile#1546 Risk Level: Low Testing: new integration test Docs Changes: n/a Release Notes: n/a Signed-off-by: Alyssa Wilk --- envoy/ssl/connection.h | 5 + source/common/network/socket_impl.h | 1 + .../common/quic/envoy_quic_client_session.cc | 7 +- .../common/quic/envoy_quic_server_session.cc | 4 +- .../quic_filter_manager_connection_impl.cc | 7 +- .../quic_filter_manager_connection_impl.h | 3 +- .../tls/connection_info_impl_base.cc | 12 ++ .../tls/connection_info_impl_base.h | 2 + .../transport_sockets/tls/ssl_socket.cc | 7 +- .../formatter/substitution_formatter_test.cc | 119 ++++++++++++++---- test/common/quic/test_utils.h | 2 +- test/integration/BUILD | 1 + test/integration/filters/BUILD | 15 +++ .../filters/stream_info_to_headers_filter.cc | 36 ++++++ .../multiplexed_upstream_integration_test.cc | 9 ++ test/mocks/ssl/mocks.h | 1 + 16 files changed, 190 insertions(+), 41 deletions(-) create mode 100644 test/integration/filters/stream_info_to_headers_filter.cc diff --git a/envoy/ssl/connection.h b/envoy/ssl/connection.h index c0bca9c6ab321..501a99b80f280 100644 --- a/envoy/ssl/connection.h +++ b/envoy/ssl/connection.h @@ -137,6 +137,11 @@ class ConnectionInfo { * connection. **/ virtual const std::string& tlsVersion() const PURE; + + /** + * @return std::string the protocol negotiated via ALPN. + **/ + virtual const std::string& alpn() const PURE; }; using ConnectionInfoConstSharedPtr = std::shared_ptr; diff --git a/source/common/network/socket_impl.h b/source/common/network/socket_impl.h index a606690c7e575..ece583ae7b2ac 100644 --- a/source/common/network/socket_impl.h +++ b/source/common/network/socket_impl.h @@ -54,6 +54,7 @@ class ConnectionInfoSetterImpl : public ConnectionInfoSetter { void setConnectionID(uint64_t id) override { connection_id_ = id; } Ssl::ConnectionInfoConstSharedPtr sslConnection() const override { return ssl_info_; } void setSslConnection(const Ssl::ConnectionInfoConstSharedPtr& ssl_connection_info) override { + ASSERT(!ssl_info_); ssl_info_ = ssl_connection_info; } diff --git a/source/common/quic/envoy_quic_client_session.cc b/source/common/quic/envoy_quic_client_session.cc index 21ceba761eef7..78fce45499b76 100644 --- a/source/common/quic/envoy_quic_client_session.cc +++ b/source/common/quic/envoy_quic_client_session.cc @@ -16,13 +16,12 @@ EnvoyQuicClientSession::EnvoyQuicClientSession( uint32_t send_buffer_limit, EnvoyQuicCryptoClientStreamFactoryInterface& crypto_stream_factory, QuicStatNames& quic_stat_names, Stats::Scope& scope) : QuicFilterManagerConnectionImpl(*connection, connection->connection_id(), dispatcher, - send_buffer_limit), + send_buffer_limit, + std::make_shared(*this)), quic::QuicSpdyClientSession(config, supported_versions, connection.release(), server_id, crypto_config.get(), push_promise_index), crypto_config_(crypto_config), crypto_stream_factory_(crypto_stream_factory), - quic_stat_names_(quic_stat_names), scope_(scope) { - quic_ssl_info_ = std::make_shared(*this); -} + quic_stat_names_(quic_stat_names), scope_(scope) {} EnvoyQuicClientSession::~EnvoyQuicClientSession() { ASSERT(!connection()->connected()); diff --git a/source/common/quic/envoy_quic_server_session.cc b/source/common/quic/envoy_quic_server_session.cc index 869a92a82e4bc..2f83f9b3201b6 100644 --- a/source/common/quic/envoy_quic_server_session.cc +++ b/source/common/quic/envoy_quic_server_session.cc @@ -22,10 +22,10 @@ EnvoyQuicServerSession::EnvoyQuicServerSession( : quic::QuicServerSessionBase(config, supported_versions, connection.get(), visitor, helper, crypto_config, compressed_certs_cache), QuicFilterManagerConnectionImpl(*connection, connection->connection_id(), dispatcher, - send_buffer_limit), + send_buffer_limit, + std::make_shared(*this)), quic_connection_(std::move(connection)), quic_stat_names_(quic_stat_names), listener_scope_(listener_scope), crypto_server_stream_factory_(crypto_server_stream_factory) { - quic_ssl_info_ = std::make_shared(*this); } EnvoyQuicServerSession::~EnvoyQuicServerSession() { diff --git a/source/common/quic/quic_filter_manager_connection_impl.cc b/source/common/quic/quic_filter_manager_connection_impl.cc index 168ddcd6d5847..8792457535b66 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.cc +++ b/source/common/quic/quic_filter_manager_connection_impl.cc @@ -10,11 +10,12 @@ namespace Quic { QuicFilterManagerConnectionImpl::QuicFilterManagerConnectionImpl( QuicNetworkConnection& connection, const quic::QuicConnectionId& connection_id, - Event::Dispatcher& dispatcher, uint32_t send_buffer_limit) + Event::Dispatcher& dispatcher, uint32_t send_buffer_limit, + std::shared_ptr&& info) // Using this for purpose other than logging is not safe. Because QUIC connection id can be // 18 bytes, so there might be collision when it's hashed to 8 bytes. : Network::ConnectionImplBase(dispatcher, /*id=*/connection_id.Hash()), - network_connection_(&connection), + network_connection_(&connection), quic_ssl_info_(std::move(info)), filter_manager_( std::make_unique(*this, *connection.connectionSocket())), stream_info_(dispatcher.timeSource(), @@ -23,6 +24,8 @@ QuicFilterManagerConnectionImpl::QuicFilterManagerConnectionImpl( send_buffer_limit / 2, send_buffer_limit, [this]() { onSendBufferLowWatermark(); }, [this]() { onSendBufferHighWatermark(); }, ENVOY_LOGGER()) { stream_info_.protocol(Http::Protocol::Http3); + network_connection_->connectionSocket()->connectionInfoProvider().setSslConnection( + Ssl::ConnectionInfoConstSharedPtr(quic_ssl_info_)); } void QuicFilterManagerConnectionImpl::addWriteFilter(Network::WriteFilterSharedPtr filter) { diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index 7b2138d5e1ee0..54eef33d7a236 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -31,7 +31,8 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, public: QuicFilterManagerConnectionImpl(QuicNetworkConnection& connection, const quic::QuicConnectionId& connection_id, - Event::Dispatcher& dispatcher, uint32_t send_buffer_limit); + Event::Dispatcher& dispatcher, uint32_t send_buffer_limit, + std::shared_ptr&& info); // Network::FilterManager // Overridden to delegate calls to filter_manager_. void addWriteFilter(Network::WriteFilterSharedPtr filter) override; diff --git a/source/extensions/transport_sockets/tls/connection_info_impl_base.cc b/source/extensions/transport_sockets/tls/connection_info_impl_base.cc index de692e42fff43..3aa9974ff8b93 100644 --- a/source/extensions/transport_sockets/tls/connection_info_impl_base.cc +++ b/source/extensions/transport_sockets/tls/connection_info_impl_base.cc @@ -190,6 +190,18 @@ const std::string& ConnectionInfoImplBase::tlsVersion() const { return cached_tls_version_; } +const std::string& ConnectionInfoImplBase::alpn() const { + if (alpn_.empty()) { + const unsigned char* proto; + unsigned int proto_len; + SSL_get0_alpn_selected(ssl(), &proto, &proto_len); + if (proto != nullptr) { + alpn_ = std::string(reinterpret_cast(proto), proto_len); + } + } + return alpn_; +} + const std::string& ConnectionInfoImplBase::serialNumberPeerCertificate() const { if (!cached_serial_number_peer_certificate_.empty()) { return cached_serial_number_peer_certificate_; diff --git a/source/extensions/transport_sockets/tls/connection_info_impl_base.h b/source/extensions/transport_sockets/tls/connection_info_impl_base.h index b591b4733f107..f5bfa73b0ee1d 100644 --- a/source/extensions/transport_sockets/tls/connection_info_impl_base.h +++ b/source/extensions/transport_sockets/tls/connection_info_impl_base.h @@ -38,6 +38,7 @@ class ConnectionInfoImplBase : public Ssl::ConnectionInfo { uint16_t ciphersuiteId() const override; std::string ciphersuiteString() const override; const std::string& tlsVersion() const override; + const std::string& alpn() const override; virtual SSL* ssl() const PURE; @@ -56,6 +57,7 @@ class ConnectionInfoImplBase : public Ssl::ConnectionInfo { mutable std::vector cached_dns_san_local_certificate_; mutable std::string cached_session_id_; mutable std::string cached_tls_version_; + mutable std::string alpn_; }; } // namespace Tls diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index 31ed7fd52f012..e59ca7b802302 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -335,12 +335,7 @@ void SslSocket::closeSocket(Network::ConnectionEvent) { } } -std::string SslSocket::protocol() const { - const unsigned char* proto; - unsigned int proto_len; - SSL_get0_alpn_selected(rawSsl(), &proto, &proto_len); - return std::string(reinterpret_cast(proto), proto_len); -} +std::string SslSocket::protocol() const { return ssl()->alpn(); } absl::string_view SslSocket::failureReason() const { return failure_reason_; } diff --git a/test/common/formatter/substitution_formatter_test.cc b/test/common/formatter/substitution_formatter_test.cc index e2b9217e21ef1..6753aff7d7117 100644 --- a/test/common/formatter/substitution_formatter_test.cc +++ b/test/common/formatter/substitution_formatter_test.cc @@ -720,8 +720,41 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { stream_info, body), ProtoEq(ValueUtil::nullValue())); } + { + StreamInfoFormatter upstream_format("UPSTREAM_TRANSPORT_FAILURE_REASON"); + std::string upstream_transport_failure_reason = "SSL error"; + EXPECT_CALL(stream_info, upstreamTransportFailureReason()) + .WillRepeatedly(ReturnRef(upstream_transport_failure_reason)); + EXPECT_EQ("SSL error", upstream_format.format(request_headers, response_headers, + response_trailers, stream_info, body)); + EXPECT_THAT(upstream_format.formatValue(request_headers, response_headers, response_trailers, + stream_info, body), + ProtoEq(ValueUtil::stringValue("SSL error"))); + } + { + StreamInfoFormatter upstream_format("UPSTREAM_TRANSPORT_FAILURE_REASON"); + std::string upstream_transport_failure_reason; + EXPECT_CALL(stream_info, upstreamTransportFailureReason()) + .WillRepeatedly(ReturnRef(upstream_transport_failure_reason)); + EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, + response_trailers, stream_info, body)); + EXPECT_THAT(upstream_format.formatValue(request_headers, response_headers, response_trailers, + stream_info, body), + ProtoEq(ValueUtil::nullValue())); + } +} + +TEST(SubstitutionFormatterTest, streamInfoFormatterWithSsl) { + EXPECT_THROW(StreamInfoFormatter formatter("unknown_field"), EnvoyException); + + Http::TestRequestHeaderMapImpl request_headers{{":method", "GET"}, {":path", "/"}}; + Http::TestResponseHeaderMapImpl response_headers; + Http::TestResponseTrailerMapImpl response_trailers; + std::string body; { + // Use a local stream info for these tests as as setSslConnection can only be called once. + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_URI_SAN"); auto connection_info = std::make_shared(); const std::vector sans{"san"}; @@ -735,6 +768,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_URI_SAN"); auto connection_info = std::make_shared(); const std::vector sans{"san1", "san2"}; @@ -744,6 +778,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { response_trailers, stream_info, body)); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_URI_SAN"); auto connection_info = std::make_shared(); EXPECT_CALL(*connection_info, uriSanPeerCertificate()) @@ -756,6 +791,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_URI_SAN"); EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, @@ -765,6 +801,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_LOCAL_URI_SAN"); auto connection_info = std::make_shared(); const std::vector sans{"san"}; @@ -777,6 +814,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::stringValue("san"))); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_LOCAL_URI_SAN"); auto connection_info = std::make_shared(); const std::vector sans{"san1", "san2"}; @@ -786,6 +824,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { response_trailers, stream_info, body)); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_LOCAL_URI_SAN"); auto connection_info = std::make_shared(); EXPECT_CALL(*connection_info, uriSanLocalCertificate()) @@ -798,6 +837,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); StreamInfoFormatter upstream_format("DOWNSTREAM_LOCAL_URI_SAN"); EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, @@ -807,6 +847,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_LOCAL_SUBJECT"); auto connection_info = std::make_shared(); const std::string subject_local = "subject"; @@ -820,6 +861,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::stringValue("subject"))); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_LOCAL_SUBJECT"); auto connection_info = std::make_shared(); EXPECT_CALL(*connection_info, subjectLocalCertificate()) @@ -832,6 +874,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); StreamInfoFormatter upstream_format("DOWNSTREAM_LOCAL_SUBJECT"); EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, @@ -841,6 +884,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_SUBJECT"); auto connection_info = std::make_shared(); const std::string subject_peer = "subject"; @@ -853,6 +897,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::stringValue("subject"))); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_SUBJECT"); auto connection_info = std::make_shared(); EXPECT_CALL(*connection_info, subjectPeerCertificate()).WillRepeatedly(ReturnRef(EMPTY_STRING)); @@ -864,6 +909,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_SUBJECT"); EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, @@ -873,6 +919,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_TLS_SESSION_ID"); auto connection_info = std::make_shared(); const std::string session_id = "deadbeef"; @@ -885,6 +932,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::stringValue("deadbeef"))); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_TLS_SESSION_ID"); auto connection_info = std::make_shared(); EXPECT_CALL(*connection_info, sessionId()).WillRepeatedly(ReturnRef(EMPTY_STRING)); @@ -896,6 +944,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); StreamInfoFormatter upstream_format("DOWNSTREAM_TLS_SESSION_ID"); EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, @@ -905,6 +954,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_TLS_CIPHER"); auto connection_info = std::make_shared(); EXPECT_CALL(*connection_info, ciphersuiteString()) @@ -915,6 +965,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { stream_info, body)); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_TLS_CIPHER"); auto connection_info = std::make_shared(); EXPECT_CALL(*connection_info, ciphersuiteString()).WillRepeatedly(Return("")); @@ -926,6 +977,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); StreamInfoFormatter upstream_format("DOWNSTREAM_TLS_CIPHER"); EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, @@ -935,6 +987,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_TLS_VERSION"); auto connection_info = std::make_shared(); std::string tlsVersion = "TLSv1.2"; @@ -947,6 +1000,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::stringValue("TLSv1.2"))); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_TLS_VERSION"); auto connection_info = std::make_shared(); EXPECT_CALL(*connection_info, tlsVersion()).WillRepeatedly(ReturnRef(EMPTY_STRING)); @@ -958,6 +1012,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); StreamInfoFormatter upstream_format("DOWNSTREAM_TLS_VERSION"); @@ -968,6 +1023,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_FINGERPRINT_256"); auto connection_info = std::make_shared(); std::string expected_sha = "685a2db593d5f86d346cb1a297009c3b467ad77f1944aa799039a2fb3d531f3f"; @@ -981,6 +1037,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::stringValue(expected_sha))); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_FINGERPRINT_256"); auto connection_info = std::make_shared(); std::string expected_sha; @@ -994,6 +1051,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_FINGERPRINT_256"); EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, @@ -1003,6 +1061,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_FINGERPRINT_1"); auto connection_info = std::make_shared(); std::string expected_sha = "685a2db593d5f86d346cb1a297009c3b467ad77f1944aa799039a2fb3d531f3f"; @@ -1016,6 +1075,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::stringValue(expected_sha))); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_FINGERPRINT_1"); auto connection_info = std::make_shared(); std::string expected_sha; @@ -1029,6 +1089,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_FINGERPRINT_1"); EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, @@ -1038,6 +1099,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_SERIAL"); auto connection_info = std::make_shared(); const std::string serial_number = "b8b5ecc898f2124a"; @@ -1051,6 +1113,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::stringValue("b8b5ecc898f2124a"))); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_SERIAL"); auto connection_info = std::make_shared(); EXPECT_CALL(*connection_info, serialNumberPeerCertificate()) @@ -1063,6 +1126,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_SERIAL"); EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, @@ -1072,6 +1136,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_ISSUER"); auto connection_info = std::make_shared(); const std::string issuer_peer = @@ -1083,6 +1148,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { stream_info, body)); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_ISSUER"); auto connection_info = std::make_shared(); EXPECT_CALL(*connection_info, issuerPeerCertificate()).WillRepeatedly(ReturnRef(EMPTY_STRING)); @@ -1094,6 +1160,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_ISSUER"); EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, @@ -1103,6 +1170,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_SUBJECT"); auto connection_info = std::make_shared(); const std::string subject_peer = @@ -1114,6 +1182,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { stream_info, body)); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_SUBJECT"); auto connection_info = std::make_shared(); EXPECT_CALL(*connection_info, subjectPeerCertificate()).WillRepeatedly(ReturnRef(EMPTY_STRING)); @@ -1125,6 +1194,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_SUBJECT"); EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, @@ -1134,6 +1204,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_CERT"); auto connection_info = std::make_shared(); std::string expected_cert = ""; @@ -1147,6 +1218,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::stringValue(expected_cert))); } { + NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_CERT"); auto connection_info = std::make_shared(); std::string expected_cert = ""; @@ -1160,6 +1232,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_CERT"); EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, @@ -1168,28 +1241,6 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { stream_info, body), ProtoEq(ValueUtil::nullValue())); } - { - StreamInfoFormatter upstream_format("UPSTREAM_TRANSPORT_FAILURE_REASON"); - std::string upstream_transport_failure_reason = "SSL error"; - EXPECT_CALL(stream_info, upstreamTransportFailureReason()) - .WillRepeatedly(ReturnRef(upstream_transport_failure_reason)); - EXPECT_EQ("SSL error", upstream_format.format(request_headers, response_headers, - response_trailers, stream_info, body)); - EXPECT_THAT(upstream_format.formatValue(request_headers, response_headers, response_trailers, - stream_info, body), - ProtoEq(ValueUtil::stringValue("SSL error"))); - } - { - StreamInfoFormatter upstream_format("UPSTREAM_TRANSPORT_FAILURE_REASON"); - std::string upstream_transport_failure_reason; - EXPECT_CALL(stream_info, upstreamTransportFailureReason()) - .WillRepeatedly(ReturnRef(upstream_transport_failure_reason)); - EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, - response_trailers, stream_info, body)); - EXPECT_THAT(upstream_format.formatValue(request_headers, response_headers, response_trailers, - stream_info, body), - ProtoEq(ValueUtil::nullValue())); - } } TEST(SubstitutionFormatterTest, requestHeaderFormatter) { @@ -1637,7 +1688,6 @@ TEST(SubstitutionFormatterTest, FilterStateFormatter) { } TEST(SubstitutionFormatterTest, DownstreamPeerCertVStartFormatter) { - NiceMock stream_info; Http::TestRequestHeaderMapImpl request_headers{{":method", "GET"}, {":path", "/"}}; Http::TestResponseHeaderMapImpl response_headers; Http::TestResponseTrailerMapImpl response_trailers; @@ -1645,6 +1695,7 @@ TEST(SubstitutionFormatterTest, DownstreamPeerCertVStartFormatter) { // No downstreamSslConnection { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); DownstreamPeerCertVStartFormatter cert_start_formart("DOWNSTREAM_PEER_CERT_V_START(%Y/%m/%d)"); EXPECT_EQ(absl::nullopt, cert_start_formart.format(request_headers, response_headers, @@ -1655,6 +1706,7 @@ TEST(SubstitutionFormatterTest, DownstreamPeerCertVStartFormatter) { } // No validFromPeerCertificate { + NiceMock stream_info; DownstreamPeerCertVStartFormatter cert_start_formart("DOWNSTREAM_PEER_CERT_V_START(%Y/%m/%d)"); auto connection_info = std::make_shared(); EXPECT_CALL(*connection_info, validFromPeerCertificate()).WillRepeatedly(Return(absl::nullopt)); @@ -1667,6 +1719,7 @@ TEST(SubstitutionFormatterTest, DownstreamPeerCertVStartFormatter) { } // Default format string { + NiceMock stream_info; DownstreamPeerCertVStartFormatter cert_start_format("DOWNSTREAM_PEER_CERT_V_START"); auto connection_info = std::make_shared(); time_t test_epoch = 1522280158; @@ -1679,6 +1732,7 @@ TEST(SubstitutionFormatterTest, DownstreamPeerCertVStartFormatter) { } // Custom format string { + NiceMock stream_info; DownstreamPeerCertVStartFormatter cert_start_format( "DOWNSTREAM_PEER_CERT_V_START(%b %e %H:%M:%S %Y %Z)"); auto connection_info = std::make_shared(); @@ -1693,7 +1747,6 @@ TEST(SubstitutionFormatterTest, DownstreamPeerCertVStartFormatter) { } TEST(SubstitutionFormatterTest, DownstreamPeerCertVEndFormatter) { - NiceMock stream_info; Http::TestRequestHeaderMapImpl request_headers{{":method", "GET"}, {":path", "/"}}; Http::TestResponseHeaderMapImpl response_headers; Http::TestResponseTrailerMapImpl response_trailers; @@ -1701,6 +1754,7 @@ TEST(SubstitutionFormatterTest, DownstreamPeerCertVEndFormatter) { // No downstreamSslConnection { + NiceMock stream_info; stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); DownstreamPeerCertVEndFormatter cert_end_format("DOWNSTREAM_PEER_CERT_V_END(%Y/%m/%d)"); EXPECT_EQ(absl::nullopt, cert_end_format.format(request_headers, response_headers, @@ -1711,6 +1765,7 @@ TEST(SubstitutionFormatterTest, DownstreamPeerCertVEndFormatter) { } // No expirationPeerCertificate { + NiceMock stream_info; DownstreamPeerCertVEndFormatter cert_end_format("DOWNSTREAM_PEER_CERT_V_END(%Y/%m/%d)"); auto connection_info = std::make_shared(); EXPECT_CALL(*connection_info, expirationPeerCertificate()) @@ -1724,6 +1779,7 @@ TEST(SubstitutionFormatterTest, DownstreamPeerCertVEndFormatter) { } // Default format string { + NiceMock stream_info; DownstreamPeerCertVEndFormatter cert_end_format("DOWNSTREAM_PEER_CERT_V_END"); auto connection_info = std::make_shared(); time_t test_epoch = 1522280158; @@ -1736,6 +1792,7 @@ TEST(SubstitutionFormatterTest, DownstreamPeerCertVEndFormatter) { } // Custom format string { + NiceMock stream_info; DownstreamPeerCertVEndFormatter cert_end_format( "DOWNSTREAM_PEER_CERT_V_END(%b %e %H:%M:%S %Y %Z)"); auto connection_info = std::make_shared(); @@ -2639,13 +2696,13 @@ TEST(SubstitutionFormatterTest, JsonFormatterTest) { } TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { - NiceMock stream_info; Http::TestRequestHeaderMapImpl request_header{{"first", "GET"}, {":path", "/"}}; Http::TestResponseHeaderMapImpl response_header{{"second", "PUT"}, {"test", "test"}}; Http::TestResponseTrailerMapImpl response_trailer{{"third", "POST"}, {"test-2", "test-2"}}; std::string body; { + NiceMock stream_info; const std::string format = "{{%PROTOCOL%}} %RESP(not exist)%++%RESP(test)% " "%REQ(FIRST?SECOND)% %RESP(FIRST?SECOND)%" "\t@%TRAILER(THIRD)%@\t%TRAILER(TEST?TEST-2)%[]"; @@ -2660,6 +2717,7 @@ TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { } { + NiceMock stream_info; const std::string format = "{}*JUST PLAIN string]"; FormatterImpl formatter(format, false); @@ -2668,6 +2726,7 @@ TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { } { + NiceMock stream_info; const std::string format = "%REQ(first):3%|%REQ(first):1%|%RESP(first?second):2%|%REQ(first):" "10%|%TRAILER(second?third):3%"; @@ -2678,6 +2737,7 @@ TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { } { + NiceMock stream_info; envoy::config::core::v3::Metadata metadata; populateMetadataTestData(metadata); EXPECT_CALL(stream_info, dynamicMetadata()).WillRepeatedly(ReturnRef(metadata)); @@ -2692,6 +2752,7 @@ TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { } { + NiceMock stream_info; EXPECT_CALL(Const(stream_info), filterState()).Times(testing::AtLeast(1)); stream_info.filter_state_->setData("testing", std::make_unique("test_value"), @@ -2711,6 +2772,7 @@ TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { } { + NiceMock stream_info; // Various START_TIME formats const std::string format = "%START_TIME(%Y/%m/%d)%|%START_TIME(%s)%|%START_TIME(bad_format)%|" "%START_TIME%|%START_TIME(%f.%1f.%2f.%3f)%"; @@ -2727,6 +2789,7 @@ TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { } { + NiceMock stream_info; // Various DOWNSTREAM_PEER_CERT_V_START formats (similar to START_TIME) const std::string format = "%DOWNSTREAM_PEER_CERT_V_START(%Y/%m/" @@ -2747,6 +2810,7 @@ TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { } { + NiceMock stream_info; // Various DOWNSTREAM_PEER_CERT_V_END formats (similar to START_TIME) const std::string format = "%DOWNSTREAM_PEER_CERT_V_END(%Y/%m/" @@ -2767,6 +2831,7 @@ TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { } { + NiceMock stream_info; // This tests the beginning of time. const std::string format = "%START_TIME(%Y/%m/%d)%|%START_TIME(%s)%|%START_TIME(bad_format)%|" "%START_TIME%|%START_TIME(%f.%1f.%2f.%3f)%"; @@ -2782,6 +2847,7 @@ TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { } { + NiceMock stream_info; // This tests multiple START_TIMEs. const std::string format = "%START_TIME(%s.%3f)%|%START_TIME(%s.%4f)%|%START_TIME(%s.%5f)%|%START_TIME(%s.%6f)%"; @@ -2794,6 +2860,7 @@ TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { } { + NiceMock stream_info; const std::string format = "%START_TIME(segment1:%s.%3f|segment2:%s.%4f|seg3:%s.%6f|%s-%3f-asdf-%9f|.%7f:segm5:%Y)%"; const SystemTime start_time(std::chrono::microseconds(1522796769123456)); @@ -2806,6 +2873,7 @@ TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { } { + NiceMock stream_info; // This tests START_TIME specifier that has shorter segments when formatted, i.e. // absl::FormatTime("%%%%"") equals "%%", %1f will have 1 as its size. const std::string format = "%START_TIME(%%%%|%%%%%f|%s%%%%%3f|%1f%%%%%s)%"; @@ -2824,6 +2892,7 @@ TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { // https://github.com/google/cctz/issues/180 #if !defined(WIN32) && !defined(__APPLE__) { + NiceMock stream_info; const std::string format = "%START_TIME(%E4n)%"; const SystemTime start_time(std::chrono::microseconds(1522796769123456)); EXPECT_CALL(stream_info, startTime()).WillOnce(Return(start_time)); diff --git a/test/common/quic/test_utils.h b/test/common/quic/test_utils.h index 68ea85e782344..76cde969e24d9 100644 --- a/test/common/quic/test_utils.h +++ b/test/common/quic/test_utils.h @@ -83,7 +83,7 @@ class MockEnvoyQuicSession : public quic::QuicSpdySession, public QuicFilterMana uint32_t send_buffer_limit) : quic::QuicSpdySession(connection, /*visitor=*/nullptr, config, supported_versions), QuicFilterManagerConnectionImpl(*connection, connection->connection_id(), dispatcher, - send_buffer_limit), + send_buffer_limit, {nullptr}), crypto_stream_(std::make_unique(this)) {} void Initialize() override { diff --git a/test/integration/BUILD b/test/integration/BUILD index d8c451a7220e5..75180edec85e4 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -546,6 +546,7 @@ envoy_cc_test( "//source/extensions/filters/http/buffer:config", "//test/integration/filters:encoder_decoder_buffer_filter_lib", "//test/integration/filters:random_pause_filter_lib", + "//test/integration/filters:stream_info_to_headers_filter_lib", "//test/test_common:utility_lib", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", diff --git a/test/integration/filters/BUILD b/test/integration/filters/BUILD index 1011e3c74776e..9ebfa7cb928aa 100644 --- a/test/integration/filters/BUILD +++ b/test/integration/filters/BUILD @@ -635,3 +635,18 @@ envoy_cc_test_library( "//test/extensions/filters/http/common:empty_http_filter_config_lib", ], ) + +envoy_cc_test_library( + name = "stream_info_to_headers_filter_lib", + srcs = [ + "stream_info_to_headers_filter.cc", + ], + deps = [ + ":common_lib", + "//envoy/http:filter_interface", + "//envoy/registry", + "//envoy/server:filter_config_interface", + "//source/extensions/filters/http/common:pass_through_filter_lib", + "//test/extensions/filters/http/common:empty_http_filter_config_lib", + ], +) diff --git a/test/integration/filters/stream_info_to_headers_filter.cc b/test/integration/filters/stream_info_to_headers_filter.cc new file mode 100644 index 0000000000000..30aedfe2b6212 --- /dev/null +++ b/test/integration/filters/stream_info_to_headers_filter.cc @@ -0,0 +1,36 @@ +#include "envoy/registry/registry.h" +#include "envoy/server/filter_config.h" + +#include "source/extensions/filters/http/common/pass_through_filter.h" + +#include "test/extensions/filters/http/common/empty_http_filter_config.h" +#include "test/integration/filters/common.h" + +#include "gtest/gtest.h" + +namespace Envoy { + +// A filter that sticks stream info into headers for integration testing. +class StreamInfoToHeadersFilter : public Http::PassThroughFilter { +public: + constexpr static char name[] = "stream-info-to-headers-filter"; + + Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap&, bool) override { + return Http::FilterHeadersStatus::Continue; + } + + Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers, bool) override { + if (decoder_callbacks_->streamInfo().upstreamSslConnection()) { + headers.addCopy(Http::LowerCaseString("alpn"), + decoder_callbacks_->streamInfo().upstreamSslConnection()->alpn()); + } + return Http::FilterHeadersStatus::Continue; + } +}; + +constexpr char StreamInfoToHeadersFilter::name[]; +static Registry::RegisterFactory, + Server::Configuration::NamedHttpFilterConfigFactory> + register_; + +} // namespace Envoy diff --git a/test/integration/multiplexed_upstream_integration_test.cc b/test/integration/multiplexed_upstream_integration_test.cc index ae1e3c8166fb0..be204334e66c3 100644 --- a/test/integration/multiplexed_upstream_integration_test.cc +++ b/test/integration/multiplexed_upstream_integration_test.cc @@ -111,6 +111,11 @@ TEST_P(MultiplexedUpstreamIntegrationTest, TestSchemeAndXFP) { // Ensure Envoy handles streaming requests and responses simultaneously. void MultiplexedUpstreamIntegrationTest::bidirectionalStreaming(uint32_t bytes) { + config_helper_.prependFilter(fmt::format(R"EOF( + name: stream-info-to-headers-filter + typed_config: + "@type": type.googleapis.com/google.protobuf.Empty)EOF")); + initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -143,6 +148,10 @@ void MultiplexedUpstreamIntegrationTest::bidirectionalStreaming(uint32_t bytes) upstream_request_->encodeTrailers(Http::TestResponseTrailerMapImpl{{"trailer", "bar"}}); ASSERT_TRUE(response->waitForEndStream()); EXPECT_TRUE(response->complete()); + std::string expected_alpn = upstreamProtocol() == Http::CodecType::HTTP2 ? "h2" : "h3"; + ASSERT_FALSE(response->headers().get(Http::LowerCaseString("alpn")).empty()); + ASSERT_EQ(response->headers().get(Http::LowerCaseString("alpn"))[0]->value().getStringView(), + expected_alpn); } TEST_P(MultiplexedUpstreamIntegrationTest, BidirectionalStreaming) { bidirectionalStreaming(1024); } diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index ae545d126d55f..7d8dde75a0108 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -62,6 +62,7 @@ class MockConnectionInfo : public ConnectionInfo { MOCK_METHOD(uint16_t, ciphersuiteId, (), (const)); MOCK_METHOD(std::string, ciphersuiteString, (), (const)); MOCK_METHOD(const std::string&, tlsVersion, (), (const)); + MOCK_METHOD(const std::string&, alpn, (), (const)); }; class MockClientContext : public ClientContext { From 5424c7e357e6841213267651c8fa5bd2a7669813 Mon Sep 17 00:00:00 2001 From: chaoqin-li1123 <55518381+chaoqin-li1123@users.noreply.github.com> Date: Wed, 10 Nov 2021 23:22:44 -0600 Subject: [PATCH 065/110] grpc: Use lru cache for grpc raw async client (#18491) As we are migrating to cache raw async grpc client, it is necessary to evict unused client to prevent the size of the cache to grow infinitely. Risk Level: low Testing: Some test to verify the behavior. Signed-off-by: chaoqin-li1123 --- .../common/grpc/async_client_manager_impl.cc | 59 +++++++++++-- .../common/grpc/async_client_manager_impl.h | 26 ++++-- test/common/grpc/BUILD | 1 + .../grpc/async_client_manager_impl_test.cc | 84 +++++++++++++++++++ 4 files changed, 156 insertions(+), 14 deletions(-) diff --git a/source/common/grpc/async_client_manager_impl.cc b/source/common/grpc/async_client_manager_impl.cc index ff712fca14bf0..72de83c82b4ac 100644 --- a/source/common/grpc/async_client_manager_impl.cc +++ b/source/common/grpc/async_client_manager_impl.cc @@ -53,8 +53,9 @@ AsyncClientManagerImpl::AsyncClientManagerImpl(Upstream::ClusterManager& cm, Api::Api& api, const StatNames& stat_names) : cm_(cm), tls_(tls), time_source_(time_source), api_(api), stat_names_(stat_names), raw_async_client_cache_(tls_) { - raw_async_client_cache_.set( - [](Event::Dispatcher&) { return std::make_shared(); }); + raw_async_client_cache_.set([](Event::Dispatcher& dispatcher) { + return std::make_shared(dispatcher); + }); #ifdef ENVOY_GOOGLE_GRPC google_tls_slot_ = tls.allocateSlot(); google_tls_slot_->set( @@ -148,18 +149,58 @@ RawAsyncClientSharedPtr AsyncClientManagerImpl::getOrCreateRawAsyncClient( return client; } +AsyncClientManagerImpl::RawAsyncClientCache::RawAsyncClientCache(Event::Dispatcher& dispatcher) + : dispatcher_(dispatcher) { + cache_eviction_timer_ = dispatcher.createTimer([this] { evictEntriesAndResetEvictionTimer(); }); +} + +void AsyncClientManagerImpl::RawAsyncClientCache::setCache( + const envoy::config::core::v3::GrpcService& config, const RawAsyncClientSharedPtr& client) { + ASSERT(lru_map_.find(config) == lru_map_.end()); + // Create a new cache entry at the beginning of the list. + lru_list_.emplace_front(config, client, dispatcher_.timeSource().monotonicTime()); + lru_map_[config] = lru_list_.begin(); + // If inserting to an empty cache, enable eviction timer. + if (lru_list_.size() == 1) { + evictEntriesAndResetEvictionTimer(); + } +} + RawAsyncClientSharedPtr AsyncClientManagerImpl::RawAsyncClientCache::getCache( - const envoy::config::core::v3::GrpcService& config) const { - auto it = cache_.find(config); - if (it == cache_.end()) { + const envoy::config::core::v3::GrpcService& config) { + auto it = lru_map_.find(config); + if (it == lru_map_.end()) { return nullptr; } - return it->second; + const auto cache_entry = it->second; + // Reset the eviction timer if the next entry to expire is accessed. + const bool should_reset_timer = (cache_entry == --lru_list_.end()); + cache_entry->accessed_time_ = dispatcher_.timeSource().monotonicTime(); + // Move the cache entry to the beginning of the list upon access. + lru_list_.splice(lru_list_.begin(), lru_list_, cache_entry); + // Get the cached async client before any cache eviction. + RawAsyncClientSharedPtr client = cache_entry->client_; + if (should_reset_timer) { + evictEntriesAndResetEvictionTimer(); + } + return client; } -void AsyncClientManagerImpl::RawAsyncClientCache::setCache( - const envoy::config::core::v3::GrpcService& config, const RawAsyncClientSharedPtr& client) { - cache_[config] = client; +void AsyncClientManagerImpl::RawAsyncClientCache::evictEntriesAndResetEvictionTimer() { + MonotonicTime now = dispatcher_.timeSource().monotonicTime(); + // Evict all the entries that have expired. + while (!lru_list_.empty()) { + MonotonicTime next_expire = lru_list_.back().accessed_time_ + EntryTimeoutInterval; + if (now >= next_expire) { + // Erase the expired entry. + lru_map_.erase(lru_list_.back().config_); + lru_list_.pop_back(); + } else { + cache_eviction_timer_->enableTimer( + std::chrono::duration_cast(next_expire - now)); + return; + } + } } } // namespace Grpc diff --git a/source/common/grpc/async_client_manager_impl.h b/source/common/grpc/async_client_manager_impl.h index 14531c02ce30c..9fab1d8018b1f 100644 --- a/source/common/grpc/async_client_manager_impl.h +++ b/source/common/grpc/async_client_manager_impl.h @@ -54,19 +54,35 @@ class AsyncClientManagerImpl : public AsyncClientManager { AsyncClientFactoryPtr factoryForGrpcService(const envoy::config::core::v3::GrpcService& config, Stats::Scope& scope, bool skip_cluster_check) override; - -private: class RawAsyncClientCache : public ThreadLocal::ThreadLocalObject { public: + explicit RawAsyncClientCache(Event::Dispatcher& dispatcher); void setCache(const envoy::config::core::v3::GrpcService& config, const RawAsyncClientSharedPtr& client); - RawAsyncClientSharedPtr getCache(const envoy::config::core::v3::GrpcService& config) const; + + RawAsyncClientSharedPtr getCache(const envoy::config::core::v3::GrpcService& config); private: - absl::flat_hash_map; + absl::flat_hash_map - cache_; + lru_map_; + LruList lru_list_; + Event::Dispatcher& dispatcher_; + Envoy::Event::TimerPtr cache_eviction_timer_; + static constexpr std::chrono::seconds EntryTimeoutInterval{50}; }; + +private: Upstream::ClusterManager& cm_; ThreadLocal::Instance& tls_; ThreadLocal::SlotPtr google_tls_slot_; diff --git a/test/common/grpc/BUILD b/test/common/grpc/BUILD index ca9a7a068b666..2a7c3681ae72d 100644 --- a/test/common/grpc/BUILD +++ b/test/common/grpc/BUILD @@ -15,6 +15,7 @@ envoy_cc_test( name = "async_client_impl_test", srcs = ["async_client_impl_test.cc"], deps = [ + "//source/common/event:dispatcher_lib", "//source/common/grpc:async_client_lib", "//test/mocks/http:http_mocks", "//test/mocks/tracing:tracing_mocks", diff --git a/test/common/grpc/async_client_manager_impl_test.cc b/test/common/grpc/async_client_manager_impl_test.cc index c0fe6baddae8d..a6c1440d17c3d 100644 --- a/test/common/grpc/async_client_manager_impl_test.cc +++ b/test/common/grpc/async_client_manager_impl_test.cc @@ -1,7 +1,10 @@ +#include + #include "envoy/config/core/v3/grpc_service.pb.h" #include "envoy/grpc/async_client.h" #include "source/common/api/api_impl.h" +#include "source/common/event/dispatcher_impl.h" #include "source/common/grpc/async_client_manager_impl.h" #include "test/mocks/stats/mocks.h" @@ -21,6 +24,87 @@ namespace Envoy { namespace Grpc { namespace { +class RawAsyncClientCacheTest : public testing::Test { +public: + RawAsyncClientCacheTest() + : api_(Api::createApiForTest(time_system_)), + dispatcher_(api_->allocateDispatcher("test_thread")), client_cache_(*dispatcher_) {} + + // advanceTimeAndRun moves the current time as requested, and then executes + // all executable timers in a non-deterministic order. This mimics real-time behavior in + // libevent if there is a long delay between libevent regaining control. Here we want to + // test behavior with a specific sequence of events, where each timer fires within a + // simulated second of what was programmed. + void waitForSeconds(int seconds) { + for (int i = 0; i < seconds; i++) { + time_system_.advanceTimeAndRun(std::chrono::seconds(1), *dispatcher_, + Event::Dispatcher::RunType::NonBlock); + } + } + +protected: + Event::SimulatedTimeSystem time_system_; + Api::ApiPtr api_; + Event::DispatcherPtr dispatcher_; + AsyncClientManagerImpl::RawAsyncClientCache client_cache_; +}; + +TEST_F(RawAsyncClientCacheTest, CacheEviction) { + envoy::config::core::v3::GrpcService foo_service; + foo_service.mutable_envoy_grpc()->set_cluster_name("foo"); + RawAsyncClientSharedPtr foo_client = std::make_shared(); + client_cache_.setCache(foo_service, foo_client); + waitForSeconds(49); + // Cache entry hasn't been evicted because it was created 49s ago. + EXPECT_EQ(client_cache_.getCache(foo_service).get(), foo_client.get()); + waitForSeconds(49); + // Cache entry hasn't been evicted because it was accessed 49s ago. + EXPECT_EQ(client_cache_.getCache(foo_service).get(), foo_client.get()); + waitForSeconds(51); + EXPECT_EQ(client_cache_.getCache(foo_service).get(), nullptr); +} + +TEST_F(RawAsyncClientCacheTest, MultipleCacheEntriesEviction) { + envoy::config::core::v3::GrpcService grpc_service; + RawAsyncClientSharedPtr foo_client = std::make_shared(); + for (int i = 1; i <= 50; i++) { + grpc_service.mutable_envoy_grpc()->set_cluster_name(std::to_string(i)); + client_cache_.setCache(grpc_service, foo_client); + } + waitForSeconds(20); + for (int i = 51; i <= 100; i++) { + grpc_service.mutable_envoy_grpc()->set_cluster_name(std::to_string(i)); + client_cache_.setCache(grpc_service, foo_client); + } + waitForSeconds(30); + // Cache entries created 50s before have expired. + for (int i = 1; i <= 50; i++) { + grpc_service.mutable_envoy_grpc()->set_cluster_name(std::to_string(i)); + EXPECT_EQ(client_cache_.getCache(grpc_service).get(), nullptr); + } + // Cache entries 30s before haven't expired. + for (int i = 51; i <= 100; i++) { + grpc_service.mutable_envoy_grpc()->set_cluster_name(std::to_string(i)); + EXPECT_EQ(client_cache_.getCache(grpc_service).get(), foo_client.get()); + } +} + +// Test the case when the eviction timer doesn't fire on time, getting the oldest entry that has +// already expired but hasn't been evicted should succeed. +TEST_F(RawAsyncClientCacheTest, GetExpiredButNotEvictedCacheEntry) { + envoy::config::core::v3::GrpcService foo_service; + foo_service.mutable_envoy_grpc()->set_cluster_name("foo"); + RawAsyncClientSharedPtr foo_client = std::make_shared(); + client_cache_.setCache(foo_service, foo_client); + time_system_.advanceTimeAsyncImpl(std::chrono::seconds(50)); + // Cache entry hasn't been evicted because it is accessed before timer fire. + EXPECT_EQ(client_cache_.getCache(foo_service).get(), foo_client.get()); + time_system_.advanceTimeAndRun(std::chrono::seconds(50), *dispatcher_, + Event::Dispatcher::RunType::NonBlock); + // Cache entry has been evicted because it is accessed after timer fire. + EXPECT_EQ(client_cache_.getCache(foo_service).get(), nullptr); +} + class AsyncClientManagerImplTest : public testing::Test { public: AsyncClientManagerImplTest() From 098b43b39e24fdf3d1cfde71409b6217d9c1d101 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Nov 2021 13:08:56 +0000 Subject: [PATCH 066/110] build(deps): bump jinja2 from 3.0.2 to 3.0.3 in /configs (#18960) Bumps [jinja2](https://github.com/pallets/jinja) from 3.0.2 to 3.0.3. - [Release notes](https://github.com/pallets/jinja/releases) - [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/jinja/compare/3.0.2...3.0.3) --- updated-dependencies: - dependency-name: jinja2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- configs/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/requirements.txt b/configs/requirements.txt index 7e65450464ab1..d0aaf38b7b60b 100644 --- a/configs/requirements.txt +++ b/configs/requirements.txt @@ -1,6 +1,6 @@ -Jinja2==3.0.2 \ - --hash=sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c \ - --hash=sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45 +Jinja2==3.0.3 \ + --hash=sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8 \ + --hash=sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7 MarkupSafe==2.0.1 \ --hash=sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51 \ --hash=sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff \ From db2202b64dd81b83dd3b5dabd91ada7fa7c6bc0e Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Thu, 11 Nov 2021 09:44:37 -0500 Subject: [PATCH 067/110] quic: removing obsolete files (#18968) Risk Level: n/a Testing: n/a Docs Changes: n/a Release Notes: n/a Signed-off-by: Alyssa Wilk --- source/common/quic/platform/BUILD | 49 ---------- .../quic/platform/http2_flag_utils_impl.h | 11 --- .../common/quic/platform/http2_flags_impl.h | 18 ---- .../common/quic/platform/http2_logging_impl.h | 23 ----- .../quic/platform/http2_string_piece_impl.h | 15 --- .../quic/platform/quic_containers_impl.h | 29 ------ .../common/quic/platform/quic_export_impl.h | 11 --- .../quic/platform/quic_pcc_sender_impl.h | 32 ------- .../quic/platform/spdy_bug_tracker_impl.h | 13 --- source/common/quic/platform/spdy_flags_impl.h | 21 ---- .../common/quic/platform/spdy_logging_impl.h | 21 ---- .../quic/platform/spdy_test_utils_prod_impl.h | 10 -- source/common/quic/platform/string_utils.cc | 95 ------------------- source/common/quic/platform/string_utils.h | 28 ------ 14 files changed, 376 deletions(-) delete mode 100644 source/common/quic/platform/http2_flag_utils_impl.h delete mode 100644 source/common/quic/platform/http2_flags_impl.h delete mode 100644 source/common/quic/platform/http2_logging_impl.h delete mode 100644 source/common/quic/platform/http2_string_piece_impl.h delete mode 100644 source/common/quic/platform/quic_containers_impl.h delete mode 100644 source/common/quic/platform/quic_export_impl.h delete mode 100644 source/common/quic/platform/quic_pcc_sender_impl.h delete mode 100644 source/common/quic/platform/spdy_bug_tracker_impl.h delete mode 100644 source/common/quic/platform/spdy_flags_impl.h delete mode 100644 source/common/quic/platform/spdy_logging_impl.h delete mode 100644 source/common/quic/platform/spdy_test_utils_prod_impl.h delete mode 100644 source/common/quic/platform/string_utils.cc delete mode 100644 source/common/quic/platform/string_utils.h diff --git a/source/common/quic/platform/BUILD b/source/common/quic/platform/BUILD index 60fcfe741ae02..715245d04c1a4 100644 --- a/source/common/quic/platform/BUILD +++ b/source/common/quic/platform/BUILD @@ -49,22 +49,9 @@ envoy_cc_library( ], ) -envoy_cc_library( - name = "string_utils_lib", - srcs = ["string_utils.cc"], - hdrs = ["string_utils.h"], - external_deps = ["abseil_str_format"], - visibility = ["//visibility:private"], - deps = [ - "//source/common/common:assert_lib", - "//source/common/common:base64_lib", - ], -) - envoy_cc_library( name = "http2_platform_impl_lib", hdrs = [ - "http2_logging_impl.h", "http2_macros_impl.h", ], external_deps = [ @@ -75,17 +62,9 @@ envoy_cc_library( deps = [ ":quic_platform_logging_impl_lib", ":quiche_flags_impl_lib", - ":string_utils_lib", ], ) -envoy_cc_library( - name = "quic_platform_export_impl_lib", - hdrs = ["quic_export_impl.h"], - tags = ["nofips"], - visibility = ["//visibility:public"], -) - envoy_cc_library( name = "quic_platform_logging_impl_lib", srcs = [ @@ -112,7 +91,6 @@ envoy_cc_library( ], hdrs = [ "quic_client_stats_impl.h", - "quic_containers_impl.h", "quic_error_code_wrappers_impl.h", "quic_flags_impl.h", "quic_iovec_impl.h", @@ -135,7 +113,6 @@ envoy_cc_library( visibility = ["//visibility:public"], deps = [ ":quiche_flags_impl_lib", - ":string_utils_lib", "//envoy/api:io_error_interface", "//source/common/buffer:buffer_lib", "//source/common/common:assert_lib", @@ -156,7 +133,6 @@ envoy_cc_library( hdrs = [ "quic_hostname_utils_impl.h", "quic_mutex_impl.h", - "quic_pcc_sender_impl.h", ], external_deps = [ "quiche_quic_platform_base", @@ -212,30 +188,5 @@ envoy_cc_library( deps = [ ":quic_platform_logging_impl_lib", ":quiche_flags_impl_lib", - ":string_utils_lib", - ], -) - -envoy_cc_library( - name = "spdy_platform_impl_lib", - hdrs = [ - "spdy_bug_tracker_impl.h", - "spdy_logging_impl.h", - "spdy_test_utils_prod_impl.h", - ], - external_deps = [ - "abseil_base", - "abseil_hash", - "abseil_inlined_vector", - "abseil_memory", - "abseil_str_format", - ], - visibility = ["//visibility:public"], - deps = [ - ":quic_platform_logging_impl_lib", - ":quiche_flags_impl_lib", - ":string_utils_lib", - "//source/common/common:assert_lib", - "@com_github_google_quiche//:quiche_common_lib", ], ) diff --git a/source/common/quic/platform/http2_flag_utils_impl.h b/source/common/quic/platform/http2_flag_utils_impl.h deleted file mode 100644 index 550b91951484e..0000000000000 --- a/source/common/quic/platform/http2_flag_utils_impl.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -// NOLINT(namespace-envoy) - -// This file is part of the QUICHE platform implementation, and is not to be -// consumed or referenced directly by other Envoy code. It serves purely as a -// porting layer for QUICHE. - -#define HTTP2_RELOADABLE_FLAG_COUNT_IMPL(flag) \ - do { \ - } while (0) diff --git a/source/common/quic/platform/http2_flags_impl.h b/source/common/quic/platform/http2_flags_impl.h deleted file mode 100644 index 27e2b8448a558..0000000000000 --- a/source/common/quic/platform/http2_flags_impl.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -// NOLINT(namespace-envoy) - -// This file is part of the QUICHE platform implementation, and is not to be -// consumed or referenced directly by other Envoy code. It serves purely as a -// porting layer for QUICHE. - -#include "source/common/quic/platform/flags_impl.h" - -#define GetHttp2ReloadableFlagImpl(flag) quiche::FLAGS_quic_reloadable_flag_##flag->value() - -#define SetHttp2ReloadableFlagImpl(flag, value) \ - quiche::FLAGS_quic_reloadable_flag_##flag->setValue(value) - -#define HTTP2_CODE_COUNT_N_IMPL(flag, instance, total) \ - do { \ - } while (0) diff --git a/source/common/quic/platform/http2_logging_impl.h b/source/common/quic/platform/http2_logging_impl.h deleted file mode 100644 index 5ef754f7aea82..0000000000000 --- a/source/common/quic/platform/http2_logging_impl.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -// NOLINT(namespace-envoy) -// -// This file is part of the QUICHE platform implementation, and is not to be -// consumed or referenced directly by other Envoy code. It serves purely as a -// porting layer for QUICHE. - -#include "source/common/quic/platform/quic_logging_impl.h" - -#define HTTP2_LOG_IMPL(severity) QUICHE_LOG_IMPL(severity) - -#define HTTP2_VLOG_IMPL(verbose_level) QUICHE_VLOG_IMPL(verbose_level) - -#define HTTP2_DLOG_IMPL(severity) QUICHE_DLOG_IMPL(severity) - -#define HTTP2_DLOG_IF_IMPL(severity, condition) QUICHE_DLOG_IF_IMPL(severity, condition) - -#define HTTP2_DVLOG_IMPL(verbose_level) QUICHE_DVLOG_IMPL(verbose_level) - -#define HTTP2_DVLOG_IF_IMPL(verbose_level, condition) QUICHE_DVLOG_IF_IMPL(verbose_level, condition) - -#define HTTP2_DLOG_EVERY_N_IMPL(severity, n) QUICHE_DLOG_EVERY_N_IMPL(severity, n) diff --git a/source/common/quic/platform/http2_string_piece_impl.h b/source/common/quic/platform/http2_string_piece_impl.h deleted file mode 100644 index 711372c021dd2..0000000000000 --- a/source/common/quic/platform/http2_string_piece_impl.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "absl/strings/string_view.h" - -// NOLINT(namespace-envoy) - -// This file is part of the QUICHE platform implementation, and is not to be -// consumed or referenced directly by other Envoy code. It serves purely as a -// porting layer for QUICHE. - -namespace http2 { - -using Http2StringPieceImpl = absl::string_view; - -} // namespace http2 diff --git a/source/common/quic/platform/quic_containers_impl.h b/source/common/quic/platform/quic_containers_impl.h deleted file mode 100644 index 2e45c63eba05f..0000000000000 --- a/source/common/quic/platform/quic_containers_impl.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "absl/container/btree_set.h" -#include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" -#include "absl/container/inlined_vector.h" -#include "absl/container/node_hash_map.h" -#include "absl/container/node_hash_set.h" -#include "quiche/common/quiche_linked_hash_map.h" -#include "quiche/quic/platform/api/quic_flags.h" - -// NOLINT(namespace-envoy) - -// This file is part of the QUICHE platform implementation, and is not to be -// consumed or referenced directly by other Envoy code. It serves purely as a -// porting layer for QUICHE. - -namespace quic { - -template -using QuicSmallOrderedSetImpl = absl::btree_set; - -} // namespace quic diff --git a/source/common/quic/platform/quic_export_impl.h b/source/common/quic/platform/quic_export_impl.h deleted file mode 100644 index afdcee72b8c51..0000000000000 --- a/source/common/quic/platform/quic_export_impl.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -// NOLINT(namespace-envoy) - -// This file is part of the QUICHE platform implementation, and is not to be -// consumed or referenced directly by other Envoy code. It serves purely as a -// porting layer for QUICHE. - -#define QUIC_EXPORT -#define QUIC_EXPORT_PRIVATE -#define QUIC_NO_EXPORT diff --git a/source/common/quic/platform/quic_pcc_sender_impl.h b/source/common/quic/platform/quic_pcc_sender_impl.h deleted file mode 100644 index de6ac4ed2a753..0000000000000 --- a/source/common/quic/platform/quic_pcc_sender_impl.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -// NOLINT(namespace-envoy) - -// This file is part of the QUICHE platform implementation, and is not to be -// consumed or referenced directly by other Envoy code. It serves purely as a -// porting layer for QUICHE. - -#include "source/common/common/assert.h" - -#include "quiche/quic/core/quic_types.h" - -namespace quic { - -class QuicClock; -struct QuicConnectionStats; -class QuicRandom; -class QuicUnackedPacketMap; -class RttStats; -class SendAlgorithmInterface; - -// Interface for creating a PCC SendAlgorithmInterface. -inline SendAlgorithmInterface* -CreatePccSenderImpl(const QuicClock* /*clock*/, const RttStats* /*rtt_stats*/, - const QuicUnackedPacketMap* /*unacked_packets*/, QuicRandom* /*random*/, - QuicConnectionStats* /*stats*/, QuicPacketCount /*initial_congestion_window*/, - QuicPacketCount /*max_congestion_window*/) { - PANIC("PccSender is not supported."); - return nullptr; -} - -} // namespace quic diff --git a/source/common/quic/platform/spdy_bug_tracker_impl.h b/source/common/quic/platform/spdy_bug_tracker_impl.h deleted file mode 100644 index 29990935d94f1..0000000000000 --- a/source/common/quic/platform/spdy_bug_tracker_impl.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -// NOLINT(namespace-envoy) -// -// This file is part of the QUICHE platform implementation, and is not to be -// consumed or referenced directly by other Envoy code. It serves purely as a -// porting layer for QUICHE. - -#include "source/common/quic/platform/quiche_bug_tracker_impl.h" - -#define SPDY_BUG_IMPL QUICHE_BUG_IMPL -#define SPDY_BUG_IF_IMPL QUICHE_BUG_IF_IMPL -#define FLAGS_spdy_always_log_bugs_for_tests_impl true diff --git a/source/common/quic/platform/spdy_flags_impl.h b/source/common/quic/platform/spdy_flags_impl.h deleted file mode 100644 index 701aff548c1e9..0000000000000 --- a/source/common/quic/platform/spdy_flags_impl.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -// NOLINT(namespace-envoy) - -// This file is part of the QUICHE platform implementation, and is not to be -// consumed or referenced directly by other Envoy code. It serves purely as a -// porting layer for QUICHE. - -#include "source/common/quic/platform/flags_impl.h" - -#define GetSpdyReloadableFlagImpl(flag) quiche::FLAGS_quic_reloadable_flag_##flag->value() - -#define GetSpdyRestartFlagImpl(flag) quiche::FLAGS_quic_restart_flag_##flag->value() - -#define SPDY_CODE_COUNT_N_IMPL(flag, instance, total) \ - do { \ - } while (0) - -#define SPDY_CODE_COUNT_IMPL(name) \ - do { \ - } while (0) diff --git a/source/common/quic/platform/spdy_logging_impl.h b/source/common/quic/platform/spdy_logging_impl.h deleted file mode 100644 index b572688364caa..0000000000000 --- a/source/common/quic/platform/spdy_logging_impl.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -// NOLINT(namespace-envoy) -// -// This file is part of the QUICHE platform implementation, and is not to be -// consumed or referenced directly by other Envoy code. It serves purely as a -// porting layer for QUICHE. - -#include "source/common/quic/platform/quic_logging_impl.h" - -#define SPDY_LOG_IMPL(severity) QUICHE_LOG_IMPL(severity) - -#define SPDY_VLOG_IMPL(verbose_level) QUICHE_VLOG_IMPL(verbose_level) - -#define SPDY_DLOG_IMPL(severity) QUICHE_DLOG_IMPL(severity) - -#define SPDY_DLOG_IF_IMPL(severity, condition) QUICHE_DLOG_IF_IMPL(severity, condition) - -#define SPDY_DVLOG_IMPL(verbose_level) QUICHE_DVLOG_IMPL(verbose_level) - -#define SPDY_DVLOG_IF_IMPL(verbose_level, condition) QUICHE_DVLOG_IF_IMPL(verbose_level, condition) diff --git a/source/common/quic/platform/spdy_test_utils_prod_impl.h b/source/common/quic/platform/spdy_test_utils_prod_impl.h deleted file mode 100644 index 70a00ad152175..0000000000000 --- a/source/common/quic/platform/spdy_test_utils_prod_impl.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -// NOLINT(namespace-envoy) - -// This file is part of the QUICHE platform implementation, and is not to be -// consumed or referenced directly by other Envoy code. It serves purely as a -// porting layer for QUICHE. - -// TODO: implement -#define SPDY_FRIEND_TEST_IMPL 0 diff --git a/source/common/quic/platform/string_utils.cc b/source/common/quic/platform/string_utils.cc deleted file mode 100644 index 5353033aee516..0000000000000 --- a/source/common/quic/platform/string_utils.cc +++ /dev/null @@ -1,95 +0,0 @@ -#include "source/common/quic/platform/string_utils.h" - -// NOLINT(namespace-envoy) -// -// This file is part of the QUICHE platform implementation, and is not to be -// consumed or referenced directly by other Envoy code. It serves purely as a -// porting layer for QUICHE. - -#include -#include - -#include "envoy/common/platform.h" -#include "absl/strings/ascii.h" -#include "absl/strings/escaping.h" -#include "absl/strings/str_format.h" -#include "source/common/common/assert.h" - -namespace quiche { - -// NOLINTNEXTLINE(readability-identifier-naming) -std::string HexDump(absl::string_view data) { - const int kBytesPerLine = 16; - const char* buf = data.data(); - int bytes_remaining = data.size(); - int offset = 0; - std::string out; - const char* p = buf; - while (bytes_remaining > 0) { - const int line_bytes = std::min(bytes_remaining, kBytesPerLine); - absl::StrAppendFormat(&out, "0x%04x: ", offset); // Do the line header - for (int i = 0; i < kBytesPerLine; ++i) { - if (i < line_bytes) { - absl::StrAppendFormat(&out, "%02x", p[i]); - } else { - out += " "; // two-space filler instead of two-space hex digits - } - if (i % 2) { - out += ' '; - } - } - out += ' '; - for (int i = 0; i < line_bytes; ++i) { // Do the ASCII dump - out += absl::ascii_isgraph(p[i]) ? p[i] : '.'; - } - - bytes_remaining -= line_bytes; - offset += line_bytes; - p += line_bytes; - out += '\n'; - } - return out; -} - -// NOLINTNEXTLINE(readability-identifier-naming) -char HexDigitToInt(char c) { - ASSERT(std::isxdigit(c)); - - if (std::isdigit(c)) { - return c - '0'; - } - if (c >= 'A' && c <= 'F') { - return c - 'A' + 10; - } - if (c >= 'a' && c <= 'f') { - return c - 'a' + 10; - } - return 0; -} - -// NOLINTNEXTLINE(readability-identifier-naming) -bool HexDecodeToUInt32(absl::string_view data, uint32_t* out) { - if (data.empty() || data.size() > 8u) { - return false; - } - - for (char c : data) { - if (!absl::ascii_isxdigit(c)) { - return false; - } - } - - // Pad with leading zeros. - std::string data_padded(data.data(), data.size()); - data_padded.insert(0, 8u - data.size(), '0'); - - std::string byte_string = absl::HexStringToBytes(data_padded); - - RELEASE_ASSERT(byte_string.size() == 4u, "padded data is not 4 byte long."); - uint32_t bytes; - memcpy(&bytes, byte_string.data(), byte_string.length()); // NOLINT(safe-memcpy) - *out = ntohl(bytes); - return true; -} - -} // namespace quiche diff --git a/source/common/quic/platform/string_utils.h b/source/common/quic/platform/string_utils.h deleted file mode 100644 index bc9b960d7070e..0000000000000 --- a/source/common/quic/platform/string_utils.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -// NOLINT(namespace-envoy) -// -// This file is part of the QUICHE platform implementation, and is not to be -// consumed or referenced directly by other Envoy code. It serves purely as a -// porting layer for QUICHE. - -#include -#include - -#include "absl/strings/string_view.h" - -namespace quiche { - -// NOLINTNEXTLINE(readability-identifier-naming) -std::string HexDump(absl::string_view data); - -// '0' => 0, '1' => 1, 'a' or 'A' => 10, etc. -// NOLINTNEXTLINE(readability-identifier-naming) -char HexDigitToInt(char c); - -// Turns a 8-byte hex string into a uint32 in host byte order. -// e.g. "12345678" => 0x12345678 -// NOLINTNEXTLINE(readability-identifier-naming) -bool HexDecodeToUInt32(absl::string_view data, uint32_t* out); - -} // namespace quiche From 7136c3ade0a8366a86621a1a3a63993af5573486 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Thu, 11 Nov 2021 12:44:10 -0500 Subject: [PATCH 068/110] test: deflaking starttls test (#18977) changes from failing 16:200 to passing 100% Risk Level: n/a (test only) Testing: yep Docs Changes: n/a Release Notes: n/a Signed-off-by: Alyssa Wilk --- .../transport_sockets/starttls/starttls_integration_test.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/extensions/transport_sockets/starttls/starttls_integration_test.cc b/test/extensions/transport_sockets/starttls/starttls_integration_test.cc index 597c0939d3123..5d0225e8d3fde 100644 --- a/test/extensions/transport_sockets/starttls/starttls_integration_test.cc +++ b/test/extensions/transport_sockets/starttls/starttls_integration_test.cc @@ -258,7 +258,6 @@ TEST_P(StartTlsIntegrationTest, SwitchToTlsFromClient) { FakeRawConnectionPtr fake_upstream_connection; ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - ASSERT_THAT(test_server_->server().listenerManager().numConnections(), 1); Buffer::OwnedImpl buffer; buffer.add("hello"); @@ -326,7 +325,6 @@ TEST_P(StartTlsIntegrationTest, SwitchToTlsFromUpstream) { FakeRawConnectionPtr fake_upstream_connection; ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - ASSERT_THAT(test_server_->server().listenerManager().numConnections(), 1); Buffer::OwnedImpl buffer; buffer.add("hello"); From 61787f813b8ddbc7441496a6cccb2f02e6dbbb3b Mon Sep 17 00:00:00 2001 From: Florin Coras Date: Thu, 11 Nov 2021 23:34:31 -0800 Subject: [PATCH 069/110] vcl: fix mq drain if max events threshold hit (#18970) Program message queue drain if max events per dispatch threshold is hit to avoid accumulating/losing socket events. Signed-off-by: Florin Coras --- contrib/vcl/source/vcl_interface.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contrib/vcl/source/vcl_interface.cc b/contrib/vcl/source/vcl_interface.cc index 3b877fa00c4e6..9d576581ba414 100644 --- a/contrib/vcl/source/vcl_interface.cc +++ b/contrib/vcl/source/vcl_interface.cc @@ -77,6 +77,11 @@ void onMqSocketEvents(uint32_t flags) { vcl_handle->cb(evts); } } + + // There might be more unhandled events, so program drain + if (max_events == 0) { + vclInterfaceDrainEvents(); + } } } // namespace From 6e62e10bab97f60229b1018807845229fd0b283c Mon Sep 17 00:00:00 2001 From: Dianna Hu Date: Fri, 12 Nov 2021 10:12:54 -0500 Subject: [PATCH 070/110] Fix test-only Http2Frame: host -> :authority. (#18984) Signed-off-by: Dianna Hu --- test/common/http/http2/http2_frame.cc | 6 +++--- test/common/http/http2/http2_frame.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/common/http/http2/http2_frame.cc b/test/common/http/http2/http2_frame.cc index 2f6dfeacb8785..68569c0daf2ee 100644 --- a/test/common/http/http2/http2_frame.cc +++ b/test/common/http/http2/http2_frame.cc @@ -316,7 +316,7 @@ Http2Frame Http2Frame::makeMalformedRequestWithZerolenHeader(uint32_t stream_ind frame.appendStaticHeader(StaticHeaderIndex::MethodGet); frame.appendStaticHeader(StaticHeaderIndex::SchemeHttps); frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Path, path); - frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Host, host); + frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Authority, host); frame.appendEmptyHeader(); frame.adjustPayloadSize(); return frame; @@ -340,7 +340,7 @@ Http2Frame Http2Frame::makeRequest(uint32_t stream_index, absl::string_view host frame.appendStaticHeader(StaticHeaderIndex::MethodGet); frame.appendStaticHeader(StaticHeaderIndex::SchemeHttps); frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Path, path); - frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Host, host); + frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Authority, host); frame.adjustPayloadSize(); return frame; } @@ -364,7 +364,7 @@ Http2Frame Http2Frame::makePostRequest(uint32_t stream_index, absl::string_view frame.appendStaticHeader(StaticHeaderIndex::MethodPost); frame.appendStaticHeader(StaticHeaderIndex::SchemeHttps); frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Path, path); - frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Host, host); + frame.appendHeaderWithoutIndexing(StaticHeaderIndex::Authority, host); frame.adjustPayloadSize(); return frame; } diff --git a/test/common/http/http2/http2_frame.h b/test/common/http/http2/http2_frame.h index fbb3f5a96ae12..dca17c4804348 100644 --- a/test/common/http/http2/http2_frame.h +++ b/test/common/http/http2/http2_frame.h @@ -78,6 +78,7 @@ class Http2Frame { // See https://tools.ietf.org/html/rfc7541#appendix-A for static header indexes enum class StaticHeaderIndex : uint8_t { Unknown, + Authority = 1, MethodGet = 2, MethodPost = 3, Path = 4, @@ -89,7 +90,6 @@ class Http2Frame { Status404 = 13, Status500 = 14, SchemeHttps = 7, - Host = 38, }; enum class ErrorCode : uint8_t { From 7a5dd7f6129e96e6a5c00d4b76c7ee367e8ba6db Mon Sep 17 00:00:00 2001 From: Sotiris Nanopoulos Date: Fri, 12 Nov 2021 07:41:19 -0800 Subject: [PATCH 071/110] windows: fix exception when running tests as exe (#18915) when tests are run as standalone exe outside of bazel the yaml-cpp library is having issues with backslashes in paths. With this changes both bazel and yaml-cpp are supported. Signed-off-by: Sotiris Nanopoulos --- bazel/README.md | 4 ++-- test/test_common/environment.cc | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/bazel/README.md b/bazel/README.md index 9337efb33ca81..1f0463e226177 100644 --- a/bazel/README.md +++ b/bazel/README.md @@ -197,9 +197,9 @@ for how to update or override dependencies. Set the `TMPDIR` environment variable to a path usable as a temporary directory (e.g. `C:\Windows\TEMP`), and create a directory symlink `C:\c` to `C:\`, so that the MSYS2 - path `/c/Windows/TEMP` is equivalent to the Windows path `C:\Windows\TEMP`: + path `/c/Windows/TEMP` is equivalent to the Windows path `C:/Windows/TEMP`: ```cmd - set TMPDIR=C:\Windows\TEMP + set TMPDIR=C:/Windows/TEMP mklink /d C:\c C:\ ``` diff --git a/test/test_common/environment.cc b/test/test_common/environment.cc index e19a5ebb8af23..37ee5679ec3a6 100644 --- a/test/test_common/environment.cc +++ b/test/test_common/environment.cc @@ -264,7 +264,11 @@ const std::string& TestEnvironment::temporaryDirectory() { std::string TestEnvironment::runfilesDirectory(const std::string& workspace) { RELEASE_ASSERT(runfiles_ != nullptr, ""); - return runfiles_->Rlocation(workspace); + auto path = runfiles_->Rlocation(workspace); +#ifdef WIN32 + path = std::regex_replace(path, std::regex("\\\\"), "/"); +#endif + return path; } std::string TestEnvironment::runfilesPath(const std::string& path, const std::string& workspace) { From 602ba669e1dcea56995fc70ff6a097310f47b32d Mon Sep 17 00:00:00 2001 From: Kevin Baichoo Date: Fri, 12 Nov 2021 07:46:12 -0800 Subject: [PATCH 072/110] H1: Byte Counting Fix (#18969) * Use the move constructor of the std::shared_ptr instead of the copy constructor. Signed-off-by: Kevin Baichoo --- source/common/http/http1/codec_impl.cc | 2 +- test/integration/http_protocol_integration.cc | 2 +- test/integration/protocol_integration_test.cc | 59 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/source/common/http/http1/codec_impl.cc b/source/common/http/http1/codec_impl.cc index a3c9eeb871f3b..2bf8b4cc1dc92 100644 --- a/source/common/http/http1/codec_impl.cc +++ b/source/common/http/http1/codec_impl.cc @@ -92,7 +92,7 @@ StreamEncoderImpl::StreamEncoderImpl(ConnectionImpl& connection, StreamInfo::BytesMeterSharedPtr&& bytes_meter) : connection_(connection), disable_chunk_encoding_(false), chunk_encoding_(true), connect_request_(false), is_tcp_tunneling_(false), is_response_to_head_request_(false), - is_response_to_connect_request_(false), bytes_meter_(bytes_meter) { + is_response_to_connect_request_(false), bytes_meter_(std::move(bytes_meter)) { if (!bytes_meter_) { bytes_meter_ = std::make_shared(); } diff --git a/test/integration/http_protocol_integration.cc b/test/integration/http_protocol_integration.cc index 72dfbac95d8a9..871065ba698ad 100644 --- a/test/integration/http_protocol_integration.cc +++ b/test/integration/http_protocol_integration.cc @@ -94,7 +94,7 @@ void HttpProtocolIntegrationTest::expectUpstreamBytesSentAndReceived( void HttpProtocolIntegrationTest::expectDownstreamBytesSentAndReceived( BytesCountExpectation h1_expectation, BytesCountExpectation h2_expectation, const int id) { - auto integer_near = [](int x, int y) -> bool { return std::abs(x - y) <= (x / 10); }; + auto integer_near = [](int x, int y) -> bool { return std::abs(x - y) <= (x / 5); }; std::string access_log = waitForAccessLog(access_log_name_, id); std::vector log_entries = absl::StrSplit(access_log, ' '); int wire_bytes_sent = std::stoi(log_entries[0]), wire_bytes_received = std::stoi(log_entries[1]), diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 5946dc06a3c27..3caf5d6575881 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -3274,6 +3274,65 @@ TEST_P(ProtocolIntegrationTest, HeaderAndBodyWireBytesCountDownstream) { BytesCountExpectation(177, 173, 68, 64)); } +TEST_P(ProtocolIntegrationTest, HeaderAndBodyWireBytesCountReuseDownstream) { + // We only care about the downstream protocol. + if (upstreamProtocol() != Http::CodecType::HTTP2) { + return; + } + + useAccessLog("%DOWNSTREAM_WIRE_BYTES_SENT% %DOWNSTREAM_WIRE_BYTES_RECEIVED% " + "%DOWNSTREAM_HEADER_BYTES_SENT% %DOWNSTREAM_HEADER_BYTES_RECEIVED%"); + + initialize(); + codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http"))); + const int request_size = 100; + const int response_size = 100; + + // Send first request on the connection + auto response_one = sendRequestAndWaitForResponse(default_request_headers_, request_size, + default_response_headers_, response_size, 0); + checkSimpleRequestSuccess(request_size, response_size, response_one.get()); + expectDownstreamBytesSentAndReceived(BytesCountExpectation(244, 182, 114, 38), + BytesCountExpectation(177, 137, 68, 28), 0); + + // Reuse connection, send the second request on the connection. + auto response_two = sendRequestAndWaitForResponse(default_request_headers_, request_size, + default_response_headers_, response_size, 0); + checkSimpleRequestSuccess(request_size, response_size, response_two.get()); + expectDownstreamBytesSentAndReceived(BytesCountExpectation(244, 182, 114, 38), + BytesCountExpectation(148, 137, 15, 27), 1); +} + +TEST_P(ProtocolIntegrationTest, HeaderAndBodyWireBytesCountReuseUpstream) { + // We only care about the upstream protocol. + if (downstreamProtocol() != Http::CodecType::HTTP2) { + return; + } + + useAccessLog("%UPSTREAM_WIRE_BYTES_SENT% %UPSTREAM_WIRE_BYTES_RECEIVED% " + "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%"); + + initialize(); + codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http"))); + auto second_client = makeHttpConnection(makeClientConnection(lookupPort("http"))); + const int request_size = 100; + const int response_size = 100; + + // Send to the same upstream from the two clients. + auto response_one = sendRequestAndWaitForResponse(default_request_headers_, request_size, + default_response_headers_, response_size, 0); + expectUpstreamBytesSentAndReceived(BytesCountExpectation(298, 158, 156, 27), + BytesCountExpectation(223, 122, 114, 13), 0); + + // Swap clients so the other connection is used to send the request. + std::swap(codec_client_, second_client); + auto response_two = sendRequestAndWaitForResponse(default_request_headers_, request_size, + default_response_headers_, response_size, 0); + expectUpstreamBytesSentAndReceived(BytesCountExpectation(298, 158, 156, 27), + BytesCountExpectation(167, 119, 58, 10), 1); + second_client->close(); +} + TEST_P(ProtocolIntegrationTest, TrailersWireBytesCountUpstream) { // we only care about upstream protocol. if (downstreamProtocol() != Http::CodecType::HTTP2) { From e3c4364cbb1f0754ae14039ea7b2123103e3545a Mon Sep 17 00:00:00 2001 From: Andres Guedez <34292400+AndresGuedez@users.noreply.github.com> Date: Fri, 12 Nov 2021 10:46:34 -0500 Subject: [PATCH 073/110] Parameterize 'InlineScopedRoutesIntegrationTest' with IP versions. (#18963) Signed-off-by: Andres Guedez --- .../integration/scoped_rds_integration_test.cc | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/test/integration/scoped_rds_integration_test.cc b/test/integration/scoped_rds_integration_test.cc index f114338e4358d..1454c60ce3703 100644 --- a/test/integration/scoped_rds_integration_test.cc +++ b/test/integration/scoped_rds_integration_test.cc @@ -13,6 +13,7 @@ #include "test/integration/http_integration.h" #include "test/test_common/printers.h" #include "test/test_common/resources.h" +#include "test/test_common/utility.h" #include "absl/strings/str_cat.h" #include "gmock/gmock.h" @@ -21,10 +22,11 @@ namespace Envoy { namespace { -class InlineScopedRoutesIntegrationTest : public HttpIntegrationTest, public testing::Test { +class InlineScopedRoutesIntegrationTest + : public HttpIntegrationTest, + public testing::TestWithParam { protected: - InlineScopedRoutesIntegrationTest() - : HttpIntegrationTest(Http::CodecType::HTTP1, Network::Address::IpVersion::v4) {} + InlineScopedRoutesIntegrationTest() : HttpIntegrationTest(Http::CodecType::HTTP1, GetParam()) {} void setScopedRoutesConfig(absl::string_view config_yaml) { config_helper_.addConfigModifier( @@ -50,7 +52,11 @@ name: foo-scoped-routes } }; -TEST_F(InlineScopedRoutesIntegrationTest, NoScopeFound) { +INSTANTIATE_TEST_SUITE_P(IpVersions, InlineScopedRoutesIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + +TEST_P(InlineScopedRoutesIntegrationTest, NoScopeFound) { absl::string_view config_yaml = R"EOF( scoped_route_configurations_list: scoped_route_configurations: @@ -82,7 +88,7 @@ TEST_F(InlineScopedRoutesIntegrationTest, NoScopeFound) { cleanupUpstreamAndDownstream(); } -TEST_F(InlineScopedRoutesIntegrationTest, ScopeWithSingleRouteConfiguration) { +TEST_P(InlineScopedRoutesIntegrationTest, ScopeWithSingleRouteConfiguration) { absl::string_view config_yaml = R"EOF( scoped_route_configurations_list: scoped_route_configurations: @@ -112,7 +118,7 @@ TEST_F(InlineScopedRoutesIntegrationTest, ScopeWithSingleRouteConfiguration) { /*backend_idx=*/0); } -TEST_F(InlineScopedRoutesIntegrationTest, ScopeWithMultipleRouteConfigurations) { +TEST_P(InlineScopedRoutesIntegrationTest, ScopeWithMultipleRouteConfigurations) { absl::string_view config_yaml = R"EOF( scoped_route_configurations_list: scoped_route_configurations: From 40203190e2c6f15cf32d63a982216e3ef4db98fc Mon Sep 17 00:00:00 2001 From: Xin Date: Fri, 12 Nov 2021 10:48:00 -0500 Subject: [PATCH 074/110] =?UTF-8?q?move=20config=20validation=20for=20RDS?= =?UTF-8?q?=20into=20receiver,=20remove=20the=20temp=20ConfigI=E2=80=A6=20?= =?UTF-8?q?(#18903)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * move config validation for RDS into receiver, remove the temp ConfigImpl in rds config provider Signed-off-by: Xin Zhuang --- envoy/router/rds.h | 6 -- envoy/router/route_config_update_receiver.h | 1 + source/common/router/rds_impl.cc | 9 --- source/common/router/rds_impl.h | 2 - .../route_config_update_receiver_impl.cc | 68 ++++++++++--------- .../route_config_update_receiver_impl.h | 5 -- source/server/admin/admin.h | 1 - test/common/router/rds_impl_test.cc | 46 +++++++++++-- test/mocks/router/mocks.h | 1 - tools/spelling/spelling_dictionary.txt | 1 + 10 files changed, 78 insertions(+), 62 deletions(-) diff --git a/envoy/router/rds.h b/envoy/router/rds.h index 486a95050f9e8..1b4d0ac0431c1 100644 --- a/envoy/router/rds.h +++ b/envoy/router/rds.h @@ -50,12 +50,6 @@ class RouteConfigProvider { */ virtual void onConfigUpdate() PURE; - /** - * Validate if the route configuration can be applied to the context of the route config provider. - */ - virtual void - validateConfig(const envoy::config::route::v3::RouteConfiguration& config) const PURE; - /** * Callback used to request an update to the route configuration from the management server. * @param for_domain supplies the domain name that virtual hosts must match on diff --git a/envoy/router/route_config_update_receiver.h b/envoy/router/route_config_update_receiver.h index 13ab7c45cefb5..a81f33a16f9da 100644 --- a/envoy/router/route_config_update_receiver.h +++ b/envoy/router/route_config_update_receiver.h @@ -27,6 +27,7 @@ class RouteConfigUpdateReceiver { * @param rc supplies the RouteConfiguration. * @param version_info supplies RouteConfiguration version. * @return bool whether RouteConfiguration has been updated. + * @throw EnvoyException if the new config can't be applied. */ virtual bool onRdsUpdate(const envoy::config::route::v3::RouteConfiguration& rc, const std::string& version_info) PURE; diff --git a/source/common/router/rds_impl.cc b/source/common/router/rds_impl.cc index 62f47d9221a11..00cf44c82907b 100644 --- a/source/common/router/rds_impl.cc +++ b/source/common/router/rds_impl.cc @@ -122,9 +122,6 @@ void RdsRouteConfigSubscription::onConfigUpdate( throw EnvoyException(fmt::format("Unexpected RDS configuration (expecting {}): {}", route_config_name_, route_config.name())); } - if (route_config_provider_opt_.has_value()) { - route_config_provider_opt_.value()->validateConfig(route_config); - } std::unique_ptr noop_init_manager; std::unique_ptr resume_rds; if (config_update_info_->onRdsUpdate(route_config, version_info)) { @@ -292,12 +289,6 @@ void RdsRouteConfigProviderImpl::onConfigUpdate() { } } -void RdsRouteConfigProviderImpl::validateConfig( - const envoy::config::route::v3::RouteConfiguration& config) const { - // TODO(lizan): consider cache the config here until onConfigUpdate. - ConfigImpl validation_config(config, optional_http_filters_, factory_context_, validator_, false); -} - // Schedules a VHDS request on the main thread and queues up the callback to use when the VHDS // response has been propagated to the worker thread that was the request origin. void RdsRouteConfigProviderImpl::requestVirtualHostsUpdate( diff --git a/source/common/router/rds_impl.h b/source/common/router/rds_impl.h index 4c4df57d4482b..8b99ce2f741e4 100644 --- a/source/common/router/rds_impl.h +++ b/source/common/router/rds_impl.h @@ -81,7 +81,6 @@ class StaticRouteConfigProviderImpl : public RouteConfigProvider { } SystemTime lastUpdated() const override { return last_updated_; } void onConfigUpdate() override {} - void validateConfig(const envoy::config::route::v3::RouteConfiguration&) const override {} void requestVirtualHostsUpdate(const std::string&, Event::Dispatcher&, std::weak_ptr) override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; @@ -209,7 +208,6 @@ class RdsRouteConfigProviderImpl : public RouteConfigProvider, void requestVirtualHostsUpdate( const std::string& for_domain, Event::Dispatcher& thread_local_dispatcher, std::weak_ptr route_config_updated_cb) override; - void validateConfig(const envoy::config::route::v3::RouteConfiguration& config) const override; private: struct ThreadLocalConfig : public ThreadLocal::ThreadLocalObject { diff --git a/source/common/router/route_config_update_receiver_impl.cc b/source/common/router/route_config_update_receiver_impl.cc index 61d451ce62910..baec90c90bbc3 100644 --- a/source/common/router/route_config_update_receiver_impl.cc +++ b/source/common/router/route_config_update_receiver_impl.cc @@ -1,6 +1,7 @@ #include "source/common/router/route_config_update_receiver_impl.h" #include +#include #include "envoy/config/route/v3/route.pb.h" #include "envoy/service/discovery/v3/discovery.pb.h" @@ -14,23 +15,49 @@ namespace Envoy { namespace Router { +namespace { + +// Resets 'route_config::virtual_hosts' by merging VirtualHost contained in +// 'rds_vhosts' and 'vhds_vhosts'. +void rebuildRouteConfigVirtualHosts( + const std::map& rds_vhosts, + const std::map& vhds_vhosts, + envoy::config::route::v3::RouteConfiguration& route_config) { + route_config.clear_virtual_hosts(); + for (const auto& vhost : rds_vhosts) { + route_config.mutable_virtual_hosts()->Add()->CopyFrom(vhost.second); + } + for (const auto& vhost : vhds_vhosts) { + route_config.mutable_virtual_hosts()->Add()->CopyFrom(vhost.second); + } +} + +} // namespace + bool RouteConfigUpdateReceiverImpl::onRdsUpdate( const envoy::config::route::v3::RouteConfiguration& rc, const std::string& version_info) { const uint64_t new_hash = MessageUtil::hash(rc); if (new_hash == last_config_hash_) { return false; } - route_config_proto_ = std::make_unique(rc); - last_config_hash_ = new_hash; const uint64_t new_vhds_config_hash = rc.has_vhds() ? MessageUtil::hash(rc.vhds()) : 0ul; + std::map rds_virtual_hosts; + for (const auto& vhost : rc.virtual_hosts()) { + rds_virtual_hosts.emplace(vhost.name(), vhost); + } + envoy::config::route::v3::RouteConfiguration new_route_config = rc; + rebuildRouteConfigVirtualHosts(rds_virtual_hosts, *vhds_virtual_hosts_, new_route_config); + auto new_config = std::make_shared( + new_route_config, optional_http_filters_, factory_context_, + factory_context_.messageValidationContext().dynamicValidationVisitor(), false); + // If the above validation/validation doesn't raise exception, update the + // other cached config entries. + config_ = new_config; + rds_virtual_hosts_ = std::move(rds_virtual_hosts); + last_config_hash_ = new_hash; + *route_config_proto_ = std::move(new_route_config); vhds_configuration_changed_ = new_vhds_config_hash != last_vhds_config_hash_; last_vhds_config_hash_ = new_vhds_config_hash; - initializeRdsVhosts(*route_config_proto_); - - rebuildRouteConfig(rds_virtual_hosts_, *vhds_virtual_hosts_, *route_config_proto_); - config_ = std::make_shared( - *route_config_proto_, optional_http_filters_, factory_context_, - factory_context_.messageValidationContext().dynamicValidationVisitor(), false); onUpdateCommon(version_info); return true; @@ -50,8 +77,8 @@ bool RouteConfigUpdateReceiverImpl::onVhdsUpdate( auto route_config_after_this_update = std::make_unique(); route_config_after_this_update->CopyFrom(*route_config_proto_); - rebuildRouteConfig(rds_virtual_hosts_, *vhosts_after_this_update, - *route_config_after_this_update); + rebuildRouteConfigVirtualHosts(rds_virtual_hosts_, *vhosts_after_this_update, + *route_config_after_this_update); auto new_config = std::make_shared( *route_config_after_this_update, optional_http_filters_, factory_context_, @@ -73,14 +100,6 @@ void RouteConfigUpdateReceiverImpl::onUpdateCommon(const std::string& version_in config_info_.emplace(RouteConfigProvider::ConfigInfo{*route_config_proto_, last_config_version_}); } -void RouteConfigUpdateReceiverImpl::initializeRdsVhosts( - const envoy::config::route::v3::RouteConfiguration& route_configuration) { - rds_virtual_hosts_.clear(); - for (const auto& vhost : route_configuration.virtual_hosts()) { - rds_virtual_hosts_.emplace(vhost.name(), vhost); - } -} - bool RouteConfigUpdateReceiverImpl::removeVhosts( std::map& vhosts, const Protobuf::RepeatedPtrField& removed_vhost_names) { @@ -110,18 +129,5 @@ bool RouteConfigUpdateReceiverImpl::updateVhosts( return vhosts_added; } -void RouteConfigUpdateReceiverImpl::rebuildRouteConfig( - const std::map& rds_vhosts, - const std::map& vhds_vhosts, - envoy::config::route::v3::RouteConfiguration& route_config) { - route_config.clear_virtual_hosts(); - for (const auto& vhost : rds_vhosts) { - route_config.mutable_virtual_hosts()->Add()->CopyFrom(vhost.second); - } - for (const auto& vhost : vhds_vhosts) { - route_config.mutable_virtual_hosts()->Add()->CopyFrom(vhost.second); - } -} - } // namespace Router } // namespace Envoy diff --git a/source/common/router/route_config_update_receiver_impl.h b/source/common/router/route_config_update_receiver_impl.h index fc07c9f9e3f98..721e46095dfc0 100644 --- a/source/common/router/route_config_update_receiver_impl.h +++ b/source/common/router/route_config_update_receiver_impl.h @@ -27,15 +27,10 @@ class RouteConfigUpdateReceiverImpl : public RouteConfigUpdateReceiver { std::make_unique>()), vhds_configuration_changed_(true), optional_http_filters_(optional_http_filters) {} - void initializeRdsVhosts(const envoy::config::route::v3::RouteConfiguration& route_configuration); bool removeVhosts(std::map& vhosts, const Protobuf::RepeatedPtrField& removed_vhost_names); bool updateVhosts(std::map& vhosts, const VirtualHostRefVector& added_vhosts); - void rebuildRouteConfig( - const std::map& rds_vhosts, - const std::map& vhds_vhosts, - envoy::config::route::v3::RouteConfiguration& route_config); bool onDemandFetchFailed(const envoy::service::discovery::v3::Resource& resource) const; void onUpdateCommon(const std::string& version_info); diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index e99330c2c8fc7..15e06294dc9d2 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -230,7 +230,6 @@ class AdminImpl : public Admin, absl::optional configInfo() const override { return {}; } SystemTime lastUpdated() const override { return time_source_.systemTime(); } void onConfigUpdate() override {} - void validateConfig(const envoy::config::route::v3::RouteConfiguration&) const override {} void requestVirtualHostsUpdate(const std::string&, Event::Dispatcher&, std::weak_ptr) override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; diff --git a/test/common/router/rds_impl_test.cc b/test/common/router/rds_impl_test.cc index 05aba26966cc4..62eb723c12685 100644 --- a/test/common/router/rds_impl_test.cc +++ b/test/common/router/rds_impl_test.cc @@ -528,34 +528,66 @@ stat_prefix: foo rds_callbacks_->onConfigUpdate(decoded_resources.refvec_, response1.version_info()); } -// Validate behavior when the config is delivered but it fails PGV validation. +// Validates behavior when the config is delivered but it fails PGV validation. +// The invalid config won't affect existing valid config. TEST_F(RdsImplTest, FailureInvalidConfig) { InSequence s; setup(); + EXPECT_CALL(init_watcher_, ready()); - const std::string response1_json = R"EOF( + const std::string valid_json = R"EOF( { "version_info": "1", "resources": [ { "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", - "name": "INVALID_NAME_FOR_route_config", + "name": "foo_route_config", "virtual_hosts": null } ] } )EOF"; + auto response1 = - TestUtility::parseYaml(response1_json); + TestUtility::parseYaml(valid_json); const auto decoded_resources = TestUtility::decodeResources(response1); + EXPECT_NO_THROW( + rds_callbacks_->onConfigUpdate(decoded_resources.refvec_, response1.version_info())); + // Sadly the RdsRouteConfigSubscription privately inherited from + // SubscriptionCallbacks, so we has to use reinterpret_cast here. + RdsRouteConfigSubscription* rds_subscription = + reinterpret_cast(rds_callbacks_); + auto config_impl_pointer = rds_subscription->routeConfigProvider().value()->config(); + // Now send an invalid config update. + const std::string invalid_json = + R"EOF( +{ + "version_info": "1", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", + "name": "INVALID_NAME_FOR_route_config", + "virtual_hosts": null + } + ] +} +)EOF"; + + auto response2 = + TestUtility::parseYaml(invalid_json); + const auto decoded_resources_2 = + TestUtility::decodeResources(response2); - EXPECT_CALL(init_watcher_, ready()); EXPECT_THROW_WITH_MESSAGE( - rds_callbacks_->onConfigUpdate(decoded_resources.refvec_, response1.version_info()), + rds_callbacks_->onConfigUpdate(decoded_resources_2.refvec_, response2.version_info()), EnvoyException, - "Unexpected RDS configuration (expecting foo_route_config): INVALID_NAME_FOR_route_config"); + "Unexpected RDS configuration (expecting foo_route_config): " + "INVALID_NAME_FOR_route_config"); + + // Verify that the config is still the old value. + ASSERT_EQ(config_impl_pointer, rds_subscription->routeConfigProvider().value()->config()); } // rds and vhds configurations change together diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index b6439818f4051..e3ea24f69e9d2 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -506,7 +506,6 @@ class MockRouteConfigProvider : public RouteConfigProvider { MOCK_METHOD(absl::optional, configInfo, (), (const)); MOCK_METHOD(SystemTime, lastUpdated, (), (const)); MOCK_METHOD(void, onConfigUpdate, ()); - MOCK_METHOD(void, validateConfig, (const envoy::config::route::v3::RouteConfiguration&), (const)); MOCK_METHOD(void, requestVirtualHostsUpdate, (const std::string&, Event::Dispatcher&, std::weak_ptr route_config_updated_cb)); diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index b0da4a180dc89..da28a8a4cd245 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -1311,3 +1311,4 @@ ep suri transid routable +vhosts From cf8ac302492a1dbdf5e7d2ccf4cf1ec6592b2d5c Mon Sep 17 00:00:00 2001 From: Brentley Jones Date: Fri, 12 Nov 2021 11:39:43 -0600 Subject: [PATCH 075/110] bazel: Use `--experimental_allow_tags_propagation` (#18913) This allows tags to influence execution requirements. There is already one use of `no-remote` in this repo, but this is also needed by envoy-mobile, so I thought it was best to add here. Signed-off-by: Brentley Jones --- .bazelrc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.bazelrc b/.bazelrc index 8685001b9b2ca..a74bad9e745f5 100644 --- a/.bazelrc +++ b/.bazelrc @@ -20,6 +20,9 @@ build --host_javabase=@bazel_tools//tools/jdk:remote_jdk11 build --javabase=@bazel_tools//tools/jdk:remote_jdk11 build --enable_platform_specific_config +# Allow tags to influence execution requirements +common --experimental_allow_tags_propagation + # Enable position independent code (this is the default on macOS and Windows) # (Workaround for https://github.com/bazelbuild/rules_foreign_cc/issues/421) build:linux --copt=-fPIC From dfeb86ce37ab5fdf4909eb227ad508e7a4646e0d Mon Sep 17 00:00:00 2001 From: phlax Date: Fri, 12 Nov 2021 18:41:43 +0000 Subject: [PATCH 076/110] dependabot: Python (docs) updates (#18990) * build(deps): bump jinja2 from 3.0.2 to 3.0.3 in /tools/base Bumps [jinja2](https://github.com/pallets/jinja) from 3.0.2 to 3.0.3. - [Release notes](https://github.com/pallets/jinja/releases) - [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/jinja/compare/3.0.2...3.0.3) --- updated-dependencies: - dependency-name: jinja2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: Ryan Northey * build(deps): bump sphinx from 4.2.0 to 4.3.0 in /tools/base Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 4.2.0 to 4.3.0. - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/4.x/CHANGES) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v4.2.0...v4.3.0) --- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: Ryan Northey Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index c26dedabb09fd..3a9d0d0b9f692 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -370,9 +370,9 @@ iniconfig==1.1.1 \ --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \ --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32 # via pytest -jinja2==3.0.2 \ - --hash=sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45 \ - --hash=sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c +jinja2==3.0.3 \ + --hash=sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8 \ + --hash=sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7 # via # -r requirements.in # sphinx @@ -645,9 +645,9 @@ snowballstemmer==2.1.0 \ --hash=sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2 \ --hash=sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914 # via sphinx -sphinx==4.2.0 \ - --hash=sha256:94078db9184491e15bce0a56d9186e0aec95f16ac20b12d00e06d4e36f1058a6 \ - --hash=sha256:98a535c62a4fcfcc362528592f69b26f7caec587d32cd55688db580be0287ae0 +sphinx==4.3.0 \ + --hash=sha256:6d051ab6e0d06cba786c4656b0fe67ba259fe058410f49e95bee6e49c4052cbf \ + --hash=sha256:7e2b30da5f39170efcd95c6270f07669d623c276521fee27ad6c380f49d2bf5b # via # -r requirements.in # envoy.docs.sphinx-runner From a6fc192705b7a55ebef5b534cfac522a6c54af83 Mon Sep 17 00:00:00 2001 From: Piotr Sikora Date: Sat, 13 Nov 2021 08:45:20 -0800 Subject: [PATCH 077/110] wasm: update Wasmtime to v0.31.0. (#18860) Signed-off-by: Piotr Sikora --- bazel/repository_locations.bzl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index c5ac92c86cae6..2112fe2c3831a 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -763,11 +763,11 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "wasmtime", project_desc = "A standalone runtime for WebAssembly", project_url = "https://github.com/bytecodealliance/wasmtime", - version = "0.30.0", - sha256 = "78eccfd8c8d63c30e85762bf36cf032409b7c34ac34f329b7e228ea6cc7aebca", + version = "0.31.0", + sha256 = "4f9fc62453f2d8faf2374699a40e95d9265829e675b5a28e45e2af4b642e7219", strip_prefix = "wasmtime-{version}", urls = ["https://github.com/bytecodealliance/wasmtime/archive/v{version}.tar.gz"], - release_date = "2021-09-17", + release_date = "2021-10-29", use_category = ["dataplane_ext"], extensions = ["envoy.wasm.runtime.wasmtime"], cpe = "cpe:2.3:a:bytecodealliance:wasmtime:*", @@ -1021,8 +1021,8 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "WebAssembly for Proxies (C++ host implementation)", project_desc = "WebAssembly for Proxies (C++ host implementation)", project_url = "https://github.com/proxy-wasm/proxy-wasm-cpp-host", - version = "9ec1f94005071a9a57ec04fe64031e8b5456253b", - sha256 = "74c7e73c0f60f2d1af2457d293d9faa461d5c7efc1664c15df74bbf678460251", + version = "e0636f7119f5fb15841799289a7e3558d7234374", + sha256 = "c5ea68cba0fda6d81eb007dccaf0433839c6e5b6185c9e1a40945a1b67ab1382", strip_prefix = "proxy-wasm-cpp-host-{version}", urls = ["https://github.com/proxy-wasm/proxy-wasm-cpp-host/archive/{version}.tar.gz"], use_category = ["dataplane_ext"], @@ -1038,7 +1038,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( "envoy.wasm.runtime.wavm", "envoy.wasm.runtime.wasmtime", ], - release_date = "2021-10-18", + release_date = "2021-11-12", cpe = "N/A", ), proxy_wasm_rust_sdk = dict( From 120384faf4f6401725e7ca04761543a6bb37a4f4 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Mon, 15 Nov 2021 09:22:30 -0500 Subject: [PATCH 078/110] stream_info: moving downstream timing out (#18967) Signed-off-by: Alyssa Wilk --- envoy/stream_info/stream_info.h | 55 ++++++++++++++----- source/common/http/conn_manager_impl.cc | 3 +- source/common/http/conn_manager_impl.h | 3 +- source/common/http/filter_manager.cc | 2 +- source/common/stream_info/stream_info_impl.h | 42 +++++++------- .../stream_info/stream_info_impl_test.cc | 8 +-- test/common/stream_info/test_util.h | 25 ++------- test/mocks/stream_info/mocks.cc | 1 + test/mocks/stream_info/mocks.h | 5 +- 9 files changed, 77 insertions(+), 67 deletions(-) diff --git a/envoy/stream_info/stream_info.h b/envoy/stream_info/stream_info.h index 5c4c2b473b611..9d187570300b8 100644 --- a/envoy/stream_info/stream_info.h +++ b/envoy/stream_info/stream_info.h @@ -238,6 +238,41 @@ struct UpstreamTiming { absl::optional last_upstream_rx_byte_received_; }; +class DownstreamTiming { +public: + absl::optional lastDownstreamRxByteReceived() const { + return last_downstream_rx_byte_received_; + } + absl::optional firstDownstreamTxByteSent() const { + return first_downstream_tx_byte_sent_; + } + absl::optional lastDownstreamTxByteSent() const { + return last_downstream_tx_byte_sent_; + } + + void onLastDownstreamRxByteReceived(TimeSource& time_source) { + ASSERT(!last_downstream_rx_byte_received_); + last_downstream_rx_byte_received_ = time_source.monotonicTime(); + } + void onFirstDownstreamTxByteSent(TimeSource& time_source) { + ASSERT(!first_downstream_tx_byte_sent_); + first_downstream_tx_byte_sent_ = time_source.monotonicTime(); + } + void onLastDownstreamTxByteSent(TimeSource& time_source) { + ASSERT(!last_downstream_tx_byte_sent_); + last_downstream_tx_byte_sent_ = time_source.monotonicTime(); + } + +private: + absl::flat_hash_map timings_; + // The time when the last byte of the request was received. + absl::optional last_downstream_rx_byte_received_; + // The time when the first byte of the response was sent downstream. + absl::optional first_downstream_tx_byte_sent_; + // The time when the last byte of the response was sent downstream. + absl::optional last_downstream_tx_byte_sent_; +}; + // Measure the number of bytes sent and received for a stream. struct BytesMeter { uint64_t wireBytesSent() const { return wire_bytes_sent_; } @@ -363,11 +398,6 @@ class StreamInfo { */ virtual absl::optional lastDownstreamRxByteReceived() const PURE; - /** - * Sets the time when the last byte of the request was received. - */ - virtual void onLastDownstreamRxByteReceived() PURE; - /** * Sets the upstream timing information for this stream. This is useful for * when multiple upstream requests are issued and we want to save timing @@ -406,22 +436,12 @@ class StreamInfo { */ virtual absl::optional firstDownstreamTxByteSent() const PURE; - /** - * Sets the time when the first byte of the response is sent downstream. - */ - virtual void onFirstDownstreamTxByteSent() PURE; - /** * @return the duration between the last byte of the response is sent downstream and the start of * the request. */ virtual absl::optional lastDownstreamTxByteSent() const PURE; - /** - * Sets the time when the last byte of the response is sent downstream. - */ - virtual void onLastDownstreamTxByteSent() PURE; - /** * @return the total duration of the request (i.e., when the request's ActiveStream is destroyed) * and may be longer than lastDownstreamTxByteSent. @@ -434,6 +454,11 @@ class StreamInfo { */ virtual void onRequestComplete() PURE; + /** + * @return the downstream timing information. + */ + virtual DownstreamTiming& downstreamTiming() PURE; + /** * @param bytes_sent denotes the number of bytes to add to total sent bytes. */ diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index 58f1c02007ac3..d9c654a596339 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -1463,7 +1463,8 @@ void ConnectionManagerImpl::ActiveStream::encodeHeaders(ResponseHeaderMap& heade headers); // Now actually encode via the codec. - filter_manager_.streamInfo().onFirstDownstreamTxByteSent(); + filter_manager_.streamInfo().downstreamTiming().onFirstDownstreamTxByteSent( + connection_manager_.time_source_); response_encoder_->encodeHeaders(headers, end_stream); } diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h index 17682f35b1943..9d75c753453a8 100644 --- a/source/common/http/conn_manager_impl.h +++ b/source/common/http/conn_manager_impl.h @@ -255,7 +255,8 @@ class ConnectionManagerImpl : Logger::Loggable, void endStream() override { ASSERT(!state_.codec_saw_local_complete_); state_.codec_saw_local_complete_ = true; - filter_manager_.streamInfo().onLastDownstreamTxByteSent(); + filter_manager_.streamInfo().downstreamTiming().onLastDownstreamTxByteSent( + connection_manager_.time_source_); request_response_timespan_->complete(); connection_manager_.doEndStream(*this); } diff --git a/source/common/http/filter_manager.cc b/source/common/http/filter_manager.cc index 7e2b1e9f23fc3..a0787c20df311 100644 --- a/source/common/http/filter_manager.cc +++ b/source/common/http/filter_manager.cc @@ -830,7 +830,7 @@ void FilterManager::maybeEndDecode(bool end_stream) { ASSERT(!state_.remote_complete_); state_.remote_complete_ = end_stream; if (end_stream) { - stream_info_.onLastDownstreamRxByteReceived(); + stream_info_.downstreamTiming().onLastDownstreamRxByteReceived(dispatcher().timeSource()); ENVOY_STREAM_LOG(debug, "request end stream", *this); } } diff --git a/source/common/stream_info/stream_info_impl.h b/source/common/stream_info/stream_info_impl.h index 403ab403154bf..fa7c2ead3169e 100644 --- a/source/common/stream_info/stream_info_impl.h +++ b/source/common/stream_info/stream_info_impl.h @@ -76,12 +76,10 @@ struct StreamInfoImpl : public StreamInfo { absl::optional upstreamConnectionId() const override { return upstream_connection_id_; } absl::optional lastDownstreamRxByteReceived() const override { - return duration(last_downstream_rx_byte_received); - } - - void onLastDownstreamRxByteReceived() override { - ASSERT(!last_downstream_rx_byte_received); - last_downstream_rx_byte_received = time_source_.monotonicTime(); + if (!downstream_timing_.has_value()) { + return absl::nullopt; + } + return duration(downstream_timing_.value().lastDownstreamRxByteReceived()); } void setUpstreamTiming(const UpstreamTiming& upstream_timing) override { @@ -105,21 +103,17 @@ struct StreamInfoImpl : public StreamInfo { } absl::optional firstDownstreamTxByteSent() const override { - return duration(first_downstream_tx_byte_sent_); - } - - void onFirstDownstreamTxByteSent() override { - ASSERT(!first_downstream_tx_byte_sent_); - first_downstream_tx_byte_sent_ = time_source_.monotonicTime(); + if (!downstream_timing_.has_value()) { + return absl::nullopt; + } + return duration(downstream_timing_.value().firstDownstreamTxByteSent()); } absl::optional lastDownstreamTxByteSent() const override { - return duration(last_downstream_tx_byte_sent_); - } - - void onLastDownstreamTxByteSent() override { - ASSERT(!last_downstream_tx_byte_sent_); - last_downstream_tx_byte_sent_ = time_source_.monotonicTime(); + if (!downstream_timing_.has_value()) { + return absl::nullopt; + } + return duration(downstream_timing_.value().lastDownstreamTxByteSent()); } absl::optional requestComplete() const override { @@ -131,6 +125,13 @@ struct StreamInfoImpl : public StreamInfo { final_time_ = time_source_.monotonicTime(); } + DownstreamTiming& downstreamTiming() override { + if (!downstream_timing_.has_value()) { + downstream_timing_ = DownstreamTiming(); + } + return downstream_timing_.value(); + } + void addBytesReceived(uint64_t bytes_received) override { bytes_received_ += bytes_received; } uint64_t bytesReceived() const override { return bytes_received_; } @@ -312,10 +313,6 @@ struct StreamInfoImpl : public StreamInfo { TimeSource& time_source_; const SystemTime start_time_; const MonotonicTime start_time_monotonic_; - - absl::optional last_downstream_rx_byte_received; - absl::optional first_downstream_tx_byte_sent_; - absl::optional last_downstream_tx_byte_sent_; absl::optional final_time_; absl::optional protocol_; @@ -360,6 +357,7 @@ struct StreamInfoImpl : public StreamInfo { std::string requested_server_name_; const Http::RequestHeaderMap* request_headers_{}; Http::RequestIdStreamInfoProviderSharedPtr request_id_provider_; + absl::optional downstream_timing_; UpstreamTiming upstream_timing_; std::string upstream_transport_failure_reason_; absl::optional upstream_cluster_info_; diff --git a/test/common/stream_info/stream_info_impl_test.cc b/test/common/stream_info/stream_info_impl_test.cc index cfe761f58f3a0..40343f1c04224 100644 --- a/test/common/stream_info/stream_info_impl_test.cc +++ b/test/common/stream_info/stream_info_impl_test.cc @@ -48,7 +48,7 @@ TEST_F(StreamInfoImplTest, TimingTest) { EXPECT_GE(post_start, start) << "Start time was higher than expected"; EXPECT_FALSE(info.lastDownstreamRxByteReceived()); - info.onLastDownstreamRxByteReceived(); + info.downstreamTiming().onLastDownstreamRxByteReceived(test_time_.timeSystem()); std::chrono::nanoseconds dur = checkDuration(std::chrono::nanoseconds{0}, info.lastDownstreamRxByteReceived()); @@ -72,12 +72,12 @@ TEST_F(StreamInfoImplTest, TimingTest) { info.setUpstreamTiming(upstream_timing); dur = checkDuration(dur, info.lastUpstreamRxByteReceived()); - EXPECT_FALSE(info.firstDownstreamTxByteSent()); - info.onFirstDownstreamTxByteSent(); + EXPECT_FALSE(info.downstreamTiming().firstDownstreamTxByteSent()); + info.downstreamTiming().onFirstDownstreamTxByteSent(test_time_.timeSystem()); dur = checkDuration(dur, info.firstDownstreamTxByteSent()); EXPECT_FALSE(info.lastDownstreamTxByteSent()); - info.onLastDownstreamTxByteSent(); + info.downstreamTiming().onLastDownstreamTxByteSent(test_time_.timeSystem()); dur = checkDuration(dur, info.lastDownstreamTxByteSent()); EXPECT_FALSE(info.requestComplete()); diff --git a/test/common/stream_info/test_util.h b/test/common/stream_info/test_util.h index 2cebca68cd1a1..e1101fee6871f 100644 --- a/test/common/stream_info/test_util.h +++ b/test/common/stream_info/test_util.h @@ -107,10 +107,6 @@ class TestStreamInfo : public StreamInfo::StreamInfo { return duration(last_rx_byte_received_); } - void onLastDownstreamRxByteReceived() override { - last_rx_byte_received_ = timeSystem().monotonicTime(); - } - absl::optional firstUpstreamTxByteSent() const override { return duration(upstream_timing_.first_upstream_tx_byte_sent_); } @@ -127,23 +123,17 @@ class TestStreamInfo : public StreamInfo::StreamInfo { } absl::optional firstDownstreamTxByteSent() const override { - return duration(first_downstream_tx_byte_sent_); - } - - void onFirstDownstreamTxByteSent() override { - first_downstream_tx_byte_sent_ = timeSystem().monotonicTime(); + return duration(downstream_timing_.firstDownstreamTxByteSent()); } absl::optional lastDownstreamTxByteSent() const override { - return duration(last_downstream_tx_byte_sent_); - } - - void onLastDownstreamTxByteSent() override { - last_downstream_tx_byte_sent_ = timeSystem().monotonicTime(); + return duration(downstream_timing_.lastDownstreamTxByteSent()); } void onRequestComplete() override { end_time_ = timeSystem().monotonicTime(); } + Envoy::StreamInfo::DownstreamTiming& downstreamTiming() override { return downstream_timing_; } + void setUpstreamTiming(const Envoy::StreamInfo::UpstreamTiming& upstream_timing) override { upstream_timing_ = upstream_timing; } @@ -241,13 +231,8 @@ class TestStreamInfo : public StreamInfo::StreamInfo { SystemTime start_time_; MonotonicTime start_time_monotonic_; + Envoy::StreamInfo::DownstreamTiming downstream_timing_; absl::optional last_rx_byte_received_; - absl::optional first_upstream_tx_byte_sent_; - absl::optional last_upstream_tx_byte_sent_; - absl::optional first_upstream_rx_byte_received_; - absl::optional last_upstream_rx_byte_received_; - absl::optional first_downstream_tx_byte_sent_; - absl::optional last_downstream_tx_byte_sent_; absl::optional end_time_; absl::optional protocol_{Http::Protocol::Http11}; diff --git a/test/mocks/stream_info/mocks.cc b/test/mocks/stream_info/mocks.cc index 2eaad512dcd7e..5f651e4130e5a 100644 --- a/test/mocks/stream_info/mocks.cc +++ b/test/mocks/stream_info/mocks.cc @@ -56,6 +56,7 @@ MockStreamInfo::MockStreamInfo() std::chrono::duration_cast(ts_.systemTime() - start_time_) .count()); })); + ON_CALL(*this, downstreamTiming()).WillByDefault(ReturnRef(downstream_timing_)); ON_CALL(*this, setUpstreamLocalAddress(_)) .WillByDefault( Invoke([this](const Network::Address::InstanceConstSharedPtr& upstream_local_address) { diff --git a/test/mocks/stream_info/mocks.h b/test/mocks/stream_info/mocks.h index be4c47a77eadf..b0355ea773909 100644 --- a/test/mocks/stream_info/mocks.h +++ b/test/mocks/stream_info/mocks.h @@ -30,7 +30,6 @@ class MockStreamInfo : public StreamInfo { MOCK_METHOD(SystemTime, startTime, (), (const)); MOCK_METHOD(MonotonicTime, startTimeMonotonic, (), (const)); MOCK_METHOD(absl::optional, lastDownstreamRxByteReceived, (), (const)); - MOCK_METHOD(void, onLastDownstreamRxByteReceived, ()); MOCK_METHOD(void, setUpstreamTiming, (const UpstreamTiming&)); MOCK_METHOD(absl::optional, firstUpstreamTxByteSent, (), (const)); MOCK_METHOD(void, onFirstUpstreamTxByteSent, ()); @@ -41,11 +40,10 @@ class MockStreamInfo : public StreamInfo { MOCK_METHOD(absl::optional, lastUpstreamRxByteReceived, (), (const)); MOCK_METHOD(void, onLastUpstreamRxByteReceived, ()); MOCK_METHOD(absl::optional, firstDownstreamTxByteSent, (), (const)); - MOCK_METHOD(void, onFirstDownstreamTxByteSent, ()); MOCK_METHOD(absl::optional, lastDownstreamTxByteSent, (), (const)); - MOCK_METHOD(void, onLastDownstreamTxByteSent, ()); MOCK_METHOD(void, onRequestComplete, ()); MOCK_METHOD(absl::optional, requestComplete, (), (const)); + MOCK_METHOD(DownstreamTiming&, downstreamTiming, ()); MOCK_METHOD(void, addBytesReceived, (uint64_t)); MOCK_METHOD(uint64_t, bytesReceived, (), (const)); MOCK_METHOD(void, addWireBytesReceived, (uint64_t)); @@ -141,6 +139,7 @@ class MockStreamInfo : public StreamInfo { std::string filter_chain_name_; absl::optional upstream_connection_id_; absl::optional attempt_count_; + DownstreamTiming downstream_timing_; }; } // namespace StreamInfo From b67fa28099d42167739b97412648d2fcb9b3a6c2 Mon Sep 17 00:00:00 2001 From: itamarkam Date: Mon, 15 Nov 2021 17:07:46 +0200 Subject: [PATCH 079/110] OpenTelemetry proto formatter for access logging (#18306) Signed-off-by: Itamar Kaminski --- .../formatter/substitution_formatter.cc | 1 + .../access_loggers/open_telemetry/BUILD | 15 +- .../open_telemetry/access_log_impl.cc | 54 +- .../open_telemetry/access_log_impl.h | 6 +- .../open_telemetry/substitution_formatter.cc | 151 ++++ .../open_telemetry/substitution_formatter.h | 97 ++ .../access_loggers/open_telemetry/BUILD | 41 + .../open_telemetry/access_log_impl_test.cc | 68 +- .../grpc_access_log_impl_test.cc | 31 +- .../substitution_formatter_speed_test.cc | 88 ++ .../substitution_formatter_test.cc | 849 ++++++++++++++++++ tools/spelling/spelling_dictionary.txt | 1 + 12 files changed, 1325 insertions(+), 77 deletions(-) create mode 100644 source/extensions/access_loggers/open_telemetry/substitution_formatter.cc create mode 100644 source/extensions/access_loggers/open_telemetry/substitution_formatter.h create mode 100644 test/extensions/access_loggers/open_telemetry/substitution_formatter_speed_test.cc create mode 100644 test/extensions/access_loggers/open_telemetry/substitution_formatter_test.cc diff --git a/source/common/formatter/substitution_formatter.cc b/source/common/formatter/substitution_formatter.cc index 51da65e89e362..42e5f9df8ab4d 100644 --- a/source/common/formatter/substitution_formatter.cc +++ b/source/common/formatter/substitution_formatter.cc @@ -1,5 +1,6 @@ #include "source/common/formatter/substitution_formatter.h" +#include #include #include #include diff --git a/source/extensions/access_loggers/open_telemetry/BUILD b/source/extensions/access_loggers/open_telemetry/BUILD index 1599f25a4c9c9..fef41675f5525 100644 --- a/source/extensions/access_loggers/open_telemetry/BUILD +++ b/source/extensions/access_loggers/open_telemetry/BUILD @@ -34,9 +34,9 @@ envoy_cc_library( hdrs = ["access_log_impl.h"], deps = [ ":grpc_access_log_lib", + ":substitution_formatter_lib", "//envoy/access_log:access_log_interface", "//envoy/protobuf:message_validator_interface", - "//source/common/formatter:substitution_formatter_lib", "//source/common/protobuf:utility_lib", "//source/extensions/access_loggers/common:access_log_base", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", @@ -72,3 +72,16 @@ envoy_cc_extension( "@envoy_api//envoy/extensions/access_loggers/open_telemetry/v3:pkg_cc_proto", ], ) + +envoy_cc_library( + name = "substitution_formatter_lib", + srcs = ["substitution_formatter.cc"], + hdrs = ["substitution_formatter.h"], + external_deps = ["abseil_str_format"], + deps = [ + "//envoy/stream_info:stream_info_interface", + "//source/common/common:assert_lib", + "//source/common/formatter:substitution_formatter_lib", + "@opentelemetry_proto//:common_cc_proto", + ], +) diff --git a/source/extensions/access_loggers/open_telemetry/access_log_impl.cc b/source/extensions/access_loggers/open_telemetry/access_log_impl.cc index 38b6a5d644b93..3acfe950c85ca 100644 --- a/source/extensions/access_loggers/open_telemetry/access_log_impl.cc +++ b/source/extensions/access_loggers/open_telemetry/access_log_impl.cc @@ -9,23 +9,46 @@ #include "source/common/common/assert.h" #include "source/common/config/utility.h" -#include "source/common/formatter/substitution_formatter.h" #include "source/common/http/headers.h" #include "source/common/network/utility.h" #include "source/common/protobuf/message_validator_impl.h" #include "source/common/protobuf/utility.h" #include "source/common/stream_info/utility.h" +#include "source/extensions/access_loggers/open_telemetry/substitution_formatter.h" #include "opentelemetry/proto/collector/logs/v1/logs_service.pb.h" #include "opentelemetry/proto/common/v1/common.pb.h" #include "opentelemetry/proto/logs/v1/logs.pb.h" #include "opentelemetry/proto/resource/v1/resource.pb.h" +// Used to pack/unpack the body AnyValue to a KeyValueList. +const char BODY_KEY[] = "body"; + namespace Envoy { namespace Extensions { namespace AccessLoggers { namespace OpenTelemetry { +namespace { + +// Packing the body "AnyValue" to a "KeyValueList" with a single key and the body as value. +::opentelemetry::proto::common::v1::KeyValueList +packBody(const ::opentelemetry::proto::common::v1::AnyValue& body) { + ::opentelemetry::proto::common::v1::KeyValueList output; + auto* kv = output.add_values(); + kv->set_key(BODY_KEY); + *kv->mutable_value() = body; + return output; +} + +::opentelemetry::proto::common::v1::AnyValue +unpackBody(const ::opentelemetry::proto::common::v1::KeyValueList& value) { + ASSERT(value.values().size() == 1 && value.values(0).key() == BODY_KEY); + return value.values(0).value(); +} + +} // namespace + Http::RegisterCustomInlineHeader referer_handle(Http::CustomHeaders::get().Referer); @@ -45,13 +68,12 @@ AccessLog::AccessLog( config.common_config(), Common::GrpcAccessLoggerType::HTTP)); }); - ProtobufWkt::Struct body_format; - MessageUtil::jsonConvert(config.body(), body_format); - body_formatter_ = std::make_unique(body_format, false, false); - ProtobufWkt::Struct attributes_format; - MessageUtil::jsonConvert(config.attributes(), attributes_format); - attributes_formatter_ = - std::make_unique(attributes_format, false, false); + // Packing the body "AnyValue" to a "KeyValueList" only if it's not empty, otherwise the + // formatter would fail to parse it. + if (config.body().value_case() != ::opentelemetry::proto::common::v1::AnyValue::VALUE_NOT_SET) { + body_formatter_ = std::make_unique(packBody(config.body())); + } + attributes_formatter_ = std::make_unique(config.attributes()); } void AccessLog::emitLog(const Http::RequestHeaderMap& request_headers, @@ -62,16 +84,16 @@ void AccessLog::emitLog(const Http::RequestHeaderMap& request_headers, log_entry.set_time_unix_nano(std::chrono::duration_cast( stream_info.startTime().time_since_epoch()) .count()); - const auto formatted_body = body_formatter_->format( - request_headers, response_headers, response_trailers, stream_info, absl::string_view()); - MessageUtil::jsonConvert(formatted_body, ProtobufMessage::getNullValidationVisitor(), - *log_entry.mutable_body()); + + // Unpacking the body "KeyValueList" to "AnyValue". + if (body_formatter_) { + const auto formatted_body = unpackBody(body_formatter_->format( + request_headers, response_headers, response_trailers, stream_info, absl::string_view())); + *log_entry.mutable_body() = formatted_body; + } const auto formatted_attributes = attributes_formatter_->format( request_headers, response_headers, response_trailers, stream_info, absl::string_view()); - opentelemetry::proto::common::v1::KeyValueList attributes; - MessageUtil::jsonConvert(formatted_attributes, ProtobufMessage::getNullValidationVisitor(), - attributes); - *log_entry.mutable_attributes() = attributes.values(); + *log_entry.mutable_attributes() = formatted_attributes.values(); tls_slot_->getTyped().logger_->log(std::move(log_entry)); } diff --git a/source/extensions/access_loggers/open_telemetry/access_log_impl.h b/source/extensions/access_loggers/open_telemetry/access_log_impl.h index 7a36bad2639e0..b35db3bd32d9f 100644 --- a/source/extensions/access_loggers/open_telemetry/access_log_impl.h +++ b/source/extensions/access_loggers/open_telemetry/access_log_impl.h @@ -11,10 +11,10 @@ #include "envoy/singleton/instance.h" #include "envoy/thread_local/thread_local.h" -#include "source/common/formatter/substitution_formatter.h" #include "source/common/grpc/typed_async_client.h" #include "source/extensions/access_loggers/common/access_log_base.h" #include "source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.h" +#include "source/extensions/access_loggers/open_telemetry/substitution_formatter.h" #include "opentelemetry/proto/collector/logs/v1/logs_service.pb.h" #include "opentelemetry/proto/common/v1/common.pb.h" @@ -56,8 +56,8 @@ class AccessLog : public Common::ImplBase { const ThreadLocal::SlotPtr tls_slot_; const GrpcAccessLoggerCacheSharedPtr access_logger_cache_; - std::unique_ptr body_formatter_; - std::unique_ptr attributes_formatter_; + std::unique_ptr body_formatter_; + std::unique_ptr attributes_formatter_; }; using AccessLogPtr = std::unique_ptr; diff --git a/source/extensions/access_loggers/open_telemetry/substitution_formatter.cc b/source/extensions/access_loggers/open_telemetry/substitution_formatter.cc new file mode 100644 index 0000000000000..d590ca2d5ca2c --- /dev/null +++ b/source/extensions/access_loggers/open_telemetry/substitution_formatter.cc @@ -0,0 +1,151 @@ +#include "source/extensions/access_loggers/open_telemetry/substitution_formatter.h" + +#include +#include +#include +#include + +#include "envoy/stream_info/stream_info.h" + +#include "source/common/common/assert.h" +#include "source/common/formatter/substitution_formatter.h" + +#include "absl/strings/str_join.h" +#include "opentelemetry/proto/common/v1/common.pb.h" + +static const std::string DefaultUnspecifiedValueString = "-"; + +namespace Envoy { +namespace Extensions { +namespace AccessLoggers { +namespace OpenTelemetry { + +OpenTelemetryFormatter::OpenTelemetryFormatter( + const ::opentelemetry::proto::common::v1::KeyValueList& format_mapping) + : kv_list_output_format_(FormatBuilder().toFormatMapValue(format_mapping)) {} + +OpenTelemetryFormatter::OpenTelemetryFormatMapWrapper +OpenTelemetryFormatter::FormatBuilder::toFormatMapValue( + const ::opentelemetry::proto::common::v1::KeyValueList& kv_list_format) const { + auto output = std::make_unique(); + for (const auto& pair : kv_list_format.values()) { + switch (pair.value().value_case()) { + case ::opentelemetry::proto::common::v1::AnyValue::kStringValue: + output->emplace_back(pair.key(), toFormatStringValue(pair.value().string_value())); + break; + + case ::opentelemetry::proto::common::v1::AnyValue::kKvlistValue: + output->emplace_back(pair.key(), toFormatMapValue(pair.value().kvlist_value())); + break; + + case ::opentelemetry::proto::common::v1::AnyValue::kArrayValue: + output->emplace_back(pair.key(), toFormatListValue(pair.value().array_value())); + break; + + default: + throw EnvoyException("Only string values, nested key value lists and array values are " + "supported in OpenTelemetry access log format."); + } + } + return {std::move(output)}; +} + +OpenTelemetryFormatter::OpenTelemetryFormatListWrapper +OpenTelemetryFormatter::FormatBuilder::toFormatListValue( + const ::opentelemetry::proto::common::v1::ArrayValue& list_value_format) const { + auto output = std::make_unique(); + for (const auto& value : list_value_format.values()) { + switch (value.value_case()) { + case ::opentelemetry::proto::common::v1::AnyValue::kStringValue: + output->emplace_back(toFormatStringValue(value.string_value())); + break; + + case ::opentelemetry::proto::common::v1::AnyValue::kKvlistValue: + output->emplace_back(toFormatMapValue(value.kvlist_value())); + break; + + case ::opentelemetry::proto::common::v1::AnyValue::kArrayValue: + output->emplace_back(toFormatListValue(value.array_value())); + break; + default: + throw EnvoyException("Only string values, nested key value lists and array values are " + "supported in OpenTelemetry access log format."); + } + } + return {std::move(output)}; +} + +std::vector +OpenTelemetryFormatter::FormatBuilder::toFormatStringValue(const std::string& string_format) const { + return Formatter::SubstitutionFormatParser::parse(string_format, {}); +} + +::opentelemetry::proto::common::v1::AnyValue OpenTelemetryFormatter::providersCallback( + const std::vector& providers, + const Http::RequestHeaderMap& request_headers, const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers, const StreamInfo::StreamInfo& stream_info, + absl::string_view local_reply_body) const { + ASSERT(!providers.empty()); + ::opentelemetry::proto::common::v1::AnyValue output; + std::vector bits(providers.size()); + std::transform(providers.begin(), providers.end(), bits.begin(), + [&](const Formatter::FormatterProviderPtr& provider) { + return provider + ->format(request_headers, response_headers, response_trailers, stream_info, + local_reply_body) + .value_or(DefaultUnspecifiedValueString); + }); + output.set_string_value(absl::StrJoin(bits, "")); + return output; +} + +::opentelemetry::proto::common::v1::AnyValue OpenTelemetryFormatter::openTelemetryFormatMapCallback( + const OpenTelemetryFormatter::OpenTelemetryFormatMapWrapper& format_map, + const OpenTelemetryFormatter::OpenTelemetryFormatMapVisitor& visitor) const { + ::opentelemetry::proto::common::v1::AnyValue output; + auto* kv_list = output.mutable_kvlist_value(); + for (const auto& pair : *format_map.value_) { + ::opentelemetry::proto::common::v1::AnyValue value = absl::visit(visitor, pair.second); + auto* kv = kv_list->add_values(); + kv->set_key(pair.first); + *kv->mutable_value() = value; + } + return output; +} + +::opentelemetry::proto::common::v1::AnyValue +OpenTelemetryFormatter::openTelemetryFormatListCallback( + const OpenTelemetryFormatter::OpenTelemetryFormatListWrapper& format_list, + const OpenTelemetryFormatter::OpenTelemetryFormatMapVisitor& visitor) const { + ::opentelemetry::proto::common::v1::AnyValue output; + auto* array_value = output.mutable_array_value(); + for (const auto& val : *format_list.value_) { + ::opentelemetry::proto::common::v1::AnyValue value = absl::visit(visitor, val); + *array_value->add_values() = value; + } + return output; +} + +::opentelemetry::proto::common::v1::KeyValueList OpenTelemetryFormatter::format( + const Http::RequestHeaderMap& request_headers, const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers, const StreamInfo::StreamInfo& stream_info, + absl::string_view local_reply_body) const { + OpenTelemetryFormatMapVisitor visitor{ + [&](const std::vector& providers) { + return providersCallback(providers, request_headers, response_headers, response_trailers, + stream_info, local_reply_body); + }, + [&, this](const OpenTelemetryFormatter::OpenTelemetryFormatMapWrapper& format_map) { + return openTelemetryFormatMapCallback(format_map, visitor); + }, + [&, this](const OpenTelemetryFormatter::OpenTelemetryFormatListWrapper& format_list) { + return openTelemetryFormatListCallback(format_list, visitor); + }, + }; + return openTelemetryFormatMapCallback(kv_list_output_format_, visitor).kvlist_value(); +} + +} // namespace OpenTelemetry +} // namespace AccessLoggers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/access_loggers/open_telemetry/substitution_formatter.h b/source/extensions/access_loggers/open_telemetry/substitution_formatter.h new file mode 100644 index 0000000000000..dcbe0c050c545 --- /dev/null +++ b/source/extensions/access_loggers/open_telemetry/substitution_formatter.h @@ -0,0 +1,97 @@ +#pragma once + +#include +#include +#include + +#include "envoy/formatter/substitution_formatter.h" +#include "envoy/stream_info/stream_info.h" + +#include "opentelemetry/proto/common/v1/common.pb.h" + +namespace Envoy { +namespace Extensions { +namespace AccessLoggers { +namespace OpenTelemetry { + +// Helper classes for OpenTelemetryFormatter::OpenTelemetryMapFormatVisitor. +template struct OpenTelemetryFormatMapVisitorHelper : Ts... { + using Ts::operator()...; +}; +template +OpenTelemetryFormatMapVisitorHelper(Ts...) -> OpenTelemetryFormatMapVisitorHelper; + +/** + * A formatter for OpenTelemetry logs, which returns a KeyValueList proto. + */ +class OpenTelemetryFormatter { +public: + OpenTelemetryFormatter(const ::opentelemetry::proto::common::v1::KeyValueList& format_mapping); + + ::opentelemetry::proto::common::v1::KeyValueList + format(const Http::RequestHeaderMap& request_headers, + const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers, + const StreamInfo::StreamInfo& stream_info, absl::string_view local_reply_body) const; + +private: + struct OpenTelemetryFormatMapWrapper; + struct OpenTelemetryFormatListWrapper; + using OpenTelemetryFormatValue = + absl::variant, + const OpenTelemetryFormatMapWrapper, const OpenTelemetryFormatListWrapper>; + // We use std::list to keep the order of the repeated "values" field in the OpenTelemetry + // KeyValueList. + using OpenTelemetryFormatMap = std::list>; + using OpenTelemetryFormatMapPtr = std::unique_ptr; + struct OpenTelemetryFormatMapWrapper { + OpenTelemetryFormatMapPtr value_; + }; + + using OpenTelemetryFormatList = std::list; + using OpenTelemetryFormatListPtr = std::unique_ptr; + struct OpenTelemetryFormatListWrapper { + OpenTelemetryFormatListPtr value_; + }; + + using OpenTelemetryFormatMapVisitor = OpenTelemetryFormatMapVisitorHelper< + const std::function<::opentelemetry::proto::common::v1::AnyValue( + const std::vector&)>, + const std::function<::opentelemetry::proto::common::v1::AnyValue( + const OpenTelemetryFormatter::OpenTelemetryFormatMapWrapper&)>, + const std::function<::opentelemetry::proto::common::v1::AnyValue( + const OpenTelemetryFormatter::OpenTelemetryFormatListWrapper&)>>; + + // Methods for building the format map. + class FormatBuilder { + public: + std::vector + toFormatStringValue(const std::string& string_format) const; + OpenTelemetryFormatMapWrapper + toFormatMapValue(const ::opentelemetry::proto::common::v1::KeyValueList& struct_format) const; + OpenTelemetryFormatListWrapper toFormatListValue( + const ::opentelemetry::proto::common::v1::ArrayValue& list_value_format) const; + }; + + // Methods for doing the actual formatting. + ::opentelemetry::proto::common::v1::AnyValue + providersCallback(const std::vector& providers, + const Http::RequestHeaderMap& request_headers, + const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers, + const StreamInfo::StreamInfo& stream_info, + absl::string_view local_reply_body) const; + ::opentelemetry::proto::common::v1::AnyValue openTelemetryFormatMapCallback( + const OpenTelemetryFormatter::OpenTelemetryFormatMapWrapper& format_map, + const OpenTelemetryFormatMapVisitor& visitor) const; + ::opentelemetry::proto::common::v1::AnyValue openTelemetryFormatListCallback( + const OpenTelemetryFormatter::OpenTelemetryFormatListWrapper& format_list, + const OpenTelemetryFormatMapVisitor& visitor) const; + + const OpenTelemetryFormatMapWrapper kv_list_output_format_; +}; + +} // namespace OpenTelemetry +} // namespace AccessLoggers +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/access_loggers/open_telemetry/BUILD b/test/extensions/access_loggers/open_telemetry/BUILD index d77fbfb8e09c2..1b13ed5d33f6c 100644 --- a/test/extensions/access_loggers/open_telemetry/BUILD +++ b/test/extensions/access_loggers/open_telemetry/BUILD @@ -1,5 +1,7 @@ load( "//bazel:envoy_build_system.bzl", + "envoy_benchmark_test", + "envoy_cc_benchmark_binary", "envoy_package", ) load( @@ -17,6 +19,7 @@ envoy_extension_cc_test( extension_names = ["envoy.access_loggers.open_telemetry"], deps = [ "//source/common/buffer:zero_copy_input_stream_lib", + "//source/common/protobuf", "//source/extensions/access_loggers/open_telemetry:grpc_access_log_lib", "//test/mocks/grpc:grpc_mocks", "//test/mocks/local_info:local_info_mocks", @@ -81,3 +84,41 @@ envoy_extension_cc_test( "@opentelemetry_proto//:logs_cc_proto", ], ) + +envoy_extension_cc_test( + name = "substitution_formatter_test", + srcs = ["substitution_formatter_test.cc"], + extension_names = ["envoy.access_loggers.open_telemetry"], + deps = [ + "//source/common/formatter:substitution_formatter_lib", + "//source/common/http:exception_lib", + "//source/common/http:header_map_lib", + "//source/common/router:string_accessor_lib", + "//source/extensions/access_loggers/open_telemetry:substitution_formatter_lib", + "//test/mocks/stream_info:stream_info_mocks", + "//test/mocks/upstream:cluster_info_mocks", + "//test/test_common:utility_lib", + "@opentelemetry_proto//:logs_cc_proto", + ], +) + +envoy_cc_benchmark_binary( + name = "substitution_formatter_speed_test", + srcs = ["substitution_formatter_speed_test.cc"], + external_deps = [ + "benchmark", + ], + deps = [ + "//source/common/http:header_map_lib", + "//source/common/network:address_lib", + "//source/extensions/access_loggers/open_telemetry:substitution_formatter_lib", + "//test/common/stream_info:test_util", + "//test/mocks/stream_info:stream_info_mocks", + "@opentelemetry_proto//:common_cc_proto", + ], +) + +envoy_benchmark_test( + name = "substitution_formatter_speed_test_benchmark_test", + benchmark_binary = "substitution_formatter_speed_test", +) diff --git a/test/extensions/access_loggers/open_telemetry/access_log_impl_test.cc b/test/extensions/access_loggers/open_telemetry/access_log_impl_test.cc index 8ddbe5b1d91b4..c5ec330a9db52 100644 --- a/test/extensions/access_loggers/open_telemetry/access_log_impl_test.cc +++ b/test/extensions/access_loggers/open_telemetry/access_log_impl_test.cc @@ -26,6 +26,9 @@ using namespace std::chrono_literals; using ::Envoy::AccessLog::FilterPtr; using ::Envoy::AccessLog::MockFilter; +using envoy::extensions::access_loggers::open_telemetry::v3::OpenTelemetryAccessLogConfig; +using opentelemetry::proto::common::v1::AnyValue; +using opentelemetry::proto::common::v1::KeyValueList; using opentelemetry::proto::logs::v1::LogRecord; using testing::_; using testing::An; @@ -57,28 +60,10 @@ class MockGrpcAccessLoggerCache : public GrpcAccessLoggerCache { class AccessLogTest : public testing::Test { public: - void initAdminAccessLog() { + AccessLogPtr makeAccessLog(const AnyValue& body_config, const KeyValueList& attributes_config) { ON_CALL(*filter_, evaluate(_, _, _, _)).WillByDefault(Return(true)); - - TestUtility::loadFromYaml(R"EOF( -string_value: "x-request-header: %REQ(x-request-header)%, protocol: %PROTOCOL%" -)EOF", - *config_.mutable_body()); - - TestUtility::loadFromYaml(R"EOF( -values: - - key: "status_code" - value: - string_value: "%RESPONSE_CODE%" - - key: "duration_ms" - value: - string_value: "%REQUEST_DURATION%" - - key: "request_bytes" - value: - string_value: "%BYTES_RECEIVED%" -)EOF", - *config_.mutable_attributes()); - + *config_.mutable_body() = body_config; + *config_.mutable_attributes() = attributes_config; config_.mutable_common_config()->set_log_name("test_log"); config_.mutable_common_config()->set_transport_api_version( envoy::config::core::v3::ApiVersion::V3); @@ -91,14 +76,10 @@ string_value: "x-request-header: %REQ(x-request-header)%, protocol: %PROTOCOL%" EXPECT_EQ(Common::GrpcAccessLoggerType::HTTP, logger_type); return logger_; }); - access_log_ = std::make_unique(FilterPtr{filter_}, config_, tls_, logger_cache_); + return std::make_unique(FilterPtr{filter_}, config_, tls_, logger_cache_); } void expectLog(const std::string& expected_log_entry_yaml) { - if (access_log_ == nullptr) { - initAdminAccessLog(); - } - LogRecord expected_log_entry; TestUtility::loadFromYaml(expected_log_entry_yaml, expected_log_entry); EXPECT_CALL(*logger_, log(An())) @@ -107,13 +88,11 @@ string_value: "x-request-header: %REQ(x-request-header)%, protocol: %PROTOCOL%" })); } - Stats::IsolatedStoreImpl scope_; MockFilter* filter_{new NiceMock()}; NiceMock tls_; envoy::extensions::access_loggers::open_telemetry::v3::OpenTelemetryAccessLogConfig config_; std::shared_ptr logger_{new MockGrpcAccessLogger()}; std::shared_ptr logger_cache_{new MockGrpcAccessLoggerCache()}; - AccessLogPtr access_log_; }; // Test log marshaling. @@ -130,7 +109,22 @@ TEST_F(AccessLogTest, Marshalling) { {"x-request-header", "test-request-header"}, }; Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}}; - + const auto body_config = TestUtility::parseYaml(R"EOF( + string_value: "x-request-header: %REQ(x-request-header)%, protocol: %PROTOCOL%" + )EOF"); + const auto attributes_config = TestUtility::parseYaml(R"EOF( + values: + - key: "status_code" + value: + string_value: "%RESPONSE_CODE%" + - key: "duration_ms" + value: + string_value: "%REQUEST_DURATION%" + - key: "request_bytes" + value: + string_value: "%BYTES_RECEIVED%" + )EOF"); + auto access_log = makeAccessLog(body_config, attributes_config); expectLog(R"EOF( time_unix_nano: 3600000000000 body: @@ -146,7 +140,21 @@ TEST_F(AccessLogTest, Marshalling) { value: string_value: "10" )EOF"); - access_log_->log(&request_headers, &response_headers, nullptr, stream_info); + access_log->log(&request_headers, &response_headers, nullptr, stream_info); +} + +// Test log with empty config. +TEST_F(AccessLogTest, EmptyConfig) { + InSequence s; + NiceMock stream_info; + stream_info.start_time_ = SystemTime(1h); + Http::TestRequestHeaderMapImpl request_headers; + Http::TestResponseHeaderMapImpl response_headers; + auto access_log = makeAccessLog({}, {}); + expectLog(R"EOF( + time_unix_nano: 3600000000000 + )EOF"); + access_log->log(&request_headers, &response_headers, nullptr, stream_info); } } // namespace diff --git a/test/extensions/access_loggers/open_telemetry/grpc_access_log_impl_test.cc b/test/extensions/access_loggers/open_telemetry/grpc_access_log_impl_test.cc index 6fa166cdf5c3f..79059617eecc1 100644 --- a/test/extensions/access_loggers/open_telemetry/grpc_access_log_impl_test.cc +++ b/test/extensions/access_loggers/open_telemetry/grpc_access_log_impl_test.cc @@ -4,6 +4,7 @@ #include "envoy/extensions/access_loggers/grpc/v3/als.pb.h" #include "source/common/buffer/zero_copy_input_stream_impl.h" +#include "source/common/protobuf/protobuf.h" #include "source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.h" #include "test/mocks/grpc/mocks.h" @@ -95,33 +96,7 @@ class GrpcAccessLoggerImplTest : public testing::Test { envoy::extensions::access_loggers::grpc::v3::CommonGrpcAccessLogConfig config_; }; -TEST_F(GrpcAccessLoggerImplTest, LogHttp) { - grpc_access_logger_impl_test_helper_.expectStreamMessage(R"EOF( - resource_logs: - resource: - attributes: - - key: "log_name" - value: - string_value: "test_log_name" - - key: "zone_name" - value: - string_value: "zone_name" - - key: "cluster_name" - value: - string_value: "cluster_name" - - key: "node_name" - value: - string_value: "node_name" - instrumentation_library_logs: - - logs: - - severity_text: "test-severity-text" - )EOF"); - opentelemetry::proto::logs::v1::LogRecord entry; - entry.set_severity_text("test-severity-text"); - logger_->log(opentelemetry::proto::logs::v1::LogRecord(entry)); -} - -TEST_F(GrpcAccessLoggerImplTest, LogTcp) { +TEST_F(GrpcAccessLoggerImplTest, Log) { grpc_access_logger_impl_test_helper_.expectStreamMessage(R"EOF( resource_logs: resource: @@ -145,6 +120,8 @@ TEST_F(GrpcAccessLoggerImplTest, LogTcp) { opentelemetry::proto::logs::v1::LogRecord entry; entry.set_severity_text("test-severity-text"); logger_->log(opentelemetry::proto::logs::v1::LogRecord(entry)); + // TCP logging shouldn't do anything. + logger_->log(ProtobufWkt::Empty()); } class GrpcAccessLoggerCacheImplTest : public testing::Test { diff --git a/test/extensions/access_loggers/open_telemetry/substitution_formatter_speed_test.cc b/test/extensions/access_loggers/open_telemetry/substitution_formatter_speed_test.cc new file mode 100644 index 0000000000000..27d69795849c5 --- /dev/null +++ b/test/extensions/access_loggers/open_telemetry/substitution_formatter_speed_test.cc @@ -0,0 +1,88 @@ +#include "source/common/http/header_map_impl.h" +#include "source/common/network/address_impl.h" +#include "source/extensions/access_loggers/open_telemetry/substitution_formatter.h" + +#include "test/common/stream_info/test_util.h" + +#include "benchmark/benchmark.h" +#include "opentelemetry/proto/common/v1/common.pb.h" + +namespace Envoy { +namespace Extensions { +namespace AccessLoggers { +namespace OpenTelemetry { + +namespace { + +std::unique_ptr makeOpenTelemetryFormatter() { + ::opentelemetry::proto::common::v1::KeyValueList otel_log_format; + const std::string format_yaml = R"EOF( + values: + - key: "remote_address" + value: + string_value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%" + - key: "start_time" + value: + string_value: '%START_TIME(%Y/%m/%dT%H:%M:%S%z %s)%' + - key: "method" + value: + string_value: '%REQ(:METHOD)%' + - key: "url" + value: + string_value: '%REQ(X-FORWARDED-PROTO)%://%REQ(:AUTHORITY)%%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%' + - key: "protocol" + value: + string_value: '%PROTOCOL%' + - key: "response_code" + value: + string_value: '%RESPONSE_CODE%' + - key: "bytes_sent" + value: + string_value: '%BYTES_SENT%' + - key: "duration" + value: + string_value: '%DURATION%' + - key: "referer" + value: + string_value: '%REQ(REFERER)%' + - key: "user-agent" + value: + string_value: '%REQ(USER-AGENT)%' + )EOF"; + TestUtility::loadFromYaml(format_yaml, otel_log_format); + return std::make_unique(otel_log_format); +} + +std::unique_ptr makeStreamInfo() { + auto stream_info = std::make_unique(); + stream_info->downstream_connection_info_provider_->setRemoteAddress( + std::make_shared("203.0.113.1")); + return stream_info; +} + +} // namespace + +// NOLINTNEXTLINE(readability-identifier-naming) +static void BM_OpenTelemetryAccessLogFormatter(benchmark::State& state) { + std::unique_ptr stream_info = makeStreamInfo(); + std::unique_ptr otel_formatter = makeOpenTelemetryFormatter(); + + size_t output_bytes = 0; + Http::TestRequestHeaderMapImpl request_headers; + Http::TestResponseHeaderMapImpl response_headers; + Http::TestResponseTrailerMapImpl response_trailers; + std::string body; + for (auto _ : state) { // NOLINT: Silences warning about dead store + output_bytes += + otel_formatter + ->format(request_headers, response_headers, response_trailers, *stream_info, body) + .ByteSize(); + } + benchmark::DoNotOptimize(output_bytes); +} +BENCHMARK(BM_OpenTelemetryAccessLogFormatter); + +} // namespace OpenTelemetry +} // namespace AccessLoggers +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/access_loggers/open_telemetry/substitution_formatter_test.cc b/test/extensions/access_loggers/open_telemetry/substitution_formatter_test.cc new file mode 100644 index 0000000000000..13e8dcc7277c3 --- /dev/null +++ b/test/extensions/access_loggers/open_telemetry/substitution_formatter_test.cc @@ -0,0 +1,849 @@ +#include +#include +#include + +#include "envoy/common/exception.h" +#include "envoy/stream_info/stream_info.h" + +#include "source/common/formatter/substitution_formatter.h" +#include "source/common/http/header_map_impl.h" +#include "source/common/router/string_accessor_impl.h" +#include "source/extensions/access_loggers/open_telemetry/substitution_formatter.h" + +#include "test/mocks/stream_info/mocks.h" +#include "test/mocks/upstream/cluster_info.h" +#include "test/test_common/utility.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "opentelemetry/proto/common/v1/common.pb.h" + +using ::opentelemetry::proto::common::v1::KeyValueList; +using OpenTelemetryFormatMap = std::list>; +using testing::Const; +using testing::NiceMock; +using testing::Return; +using testing::ReturnPointee; +using testing::ReturnRef; + +namespace Envoy { +namespace Extensions { +namespace AccessLoggers { +namespace OpenTelemetry { +namespace { + +class TestSerializedStructFilterState : public StreamInfo::FilterState::Object { +public: + TestSerializedStructFilterState() : use_struct_(true) { + (*struct_.mutable_fields())["inner_key"] = ValueUtil::stringValue("inner_value"); + } + + explicit TestSerializedStructFilterState(const ProtobufWkt::Struct& s) : use_struct_(true) { + struct_.CopyFrom(s); + } + + explicit TestSerializedStructFilterState(std::chrono::seconds seconds) { + duration_.set_seconds(seconds.count()); + } + + ProtobufTypes::MessagePtr serializeAsProto() const override { + if (use_struct_) { + auto s = std::make_unique(); + s->CopyFrom(struct_); + return s; + } + + auto d = std::make_unique(); + d->CopyFrom(duration_); + return d; + } + +private: + const bool use_struct_{false}; + ProtobufWkt::Struct struct_; + ProtobufWkt::Duration duration_; +}; + +// Class used to test serializeAsString and serializeAsProto of FilterState +class TestSerializedStringFilterState : public StreamInfo::FilterState::Object { +public: + TestSerializedStringFilterState(std::string str) : raw_string_(str) {} + absl::optional serializeAsString() const override { + return raw_string_ + " By PLAIN"; + } + ProtobufTypes::MessagePtr serializeAsProto() const override { + auto message = std::make_unique(); + message->set_value(raw_string_ + " By TYPED"); + return message; + } + +private: + std::string raw_string_; +}; + +/** + * Populate a metadata object with the following test data: + * "com.test": {"test_key":"test_value","test_obj":{"inner_key":"inner_value"}} + */ +void populateMetadataTestData(envoy::config::core::v3::Metadata& metadata) { + ProtobufWkt::Struct struct_obj; + auto& fields_map = *struct_obj.mutable_fields(); + fields_map["test_key"] = ValueUtil::stringValue("test_value"); + ProtobufWkt::Struct struct_inner; + (*struct_inner.mutable_fields())["inner_key"] = ValueUtil::stringValue("inner_value"); + ProtobufWkt::Value val; + *val.mutable_struct_value() = struct_inner; + fields_map["test_obj"] = val; + (*metadata.mutable_filter_metadata())["com.test"] = struct_obj; +} + +void verifyOpenTelemetryOutput(KeyValueList output, OpenTelemetryFormatMap expected_map) { + EXPECT_EQ(output.values().size(), expected_map.size()); + OpenTelemetryFormatMap list_output; + for (const auto& pair : output.values()) { + list_output.emplace_back(pair.key(), pair.value().string_value()); + } + + for (auto output_it = list_output.begin(), expected_it = expected_map.begin(); + output_it != list_output.end() && expected_it != expected_map.end(); + ++output_it, ++expected_it) { + EXPECT_EQ(*output_it, *expected_it); + } +} + +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterPlainStringTest) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header; + Http::TestResponseHeaderMapImpl response_header; + Http::TestResponseTrailerMapImpl response_trailer; + std::string body; + + absl::optional protocol = Http::Protocol::Http11; + EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); + + OpenTelemetryFormatMap expected = {{"plain_string", "plain_string_value"}}; + + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "plain_string" + value: + string_value: "plain_string_value" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + + verifyOpenTelemetryOutput( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + expected); +} + +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterTypesTest) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header; + Http::TestResponseHeaderMapImpl response_header; + Http::TestResponseTrailerMapImpl response_trailer; + std::string body; + + absl::optional protocol = Http::Protocol::Http11; + EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); + + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "string_type" + value: + string_value: "plain_string_value" + - key: "kvlist_type" + value: + kvlist_value: + values: + - key: "plain_string" + value: + string_value: "plain_string_value" + - key: "protocol" + value: + string_value: "%PROTOCOL%" + - key: "array_type" + value: + array_value: + values: + - string_value: "plain_string_value" + - string_value: "%PROTOCOL%" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + + KeyValueList expected; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "string_type" + value: + string_value: "plain_string_value" + - key: "kvlist_type" + value: + kvlist_value: + values: + - key: "plain_string" + value: + string_value: "plain_string_value" + - key: "protocol" + value: + string_value: "HTTP/1.1" + - key: "array_type" + value: + array_value: + values: + - string_value: "plain_string_value" + - string_value: "HTTP/1.1" + )EOF", + expected); + const KeyValueList output = + formatter.format(request_header, response_header, response_trailer, stream_info, body); + EXPECT_TRUE(TestUtility::protoEqual(output, expected)); +} + +// Test that nested values are formatted properly, including inter-type nesting. +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterNestedObjectsTest) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header; + Http::TestResponseHeaderMapImpl response_header; + Http::TestResponseTrailerMapImpl response_trailer; + std::string body; + + absl::optional protocol = Http::Protocol::Http11; + EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); + + KeyValueList key_mapping; + // For both kvlist and array, we test 3 nesting levels of all types (string, kvlist and + // array). + TestUtility::loadFromYaml(R"EOF( + values: + - key: "kvlist" + value: + kvlist_value: + values: + - key: "kvlist_string" + value: + string_value: "plain_string_value" + - key: "kvlist_protocol" + value: + string_value: "%PROTOCOL%" + - key: "kvlist_kvlist" + value: + kvlist_value: + values: + - key: "kvlist_kvlist_string" + value: + string_value: "plain_string_value" + - key: "kvlist_kvlist_protocol" + value: + string_value: "%PROTOCOL%" + - key: "kvlist_kvlist_kvlist" + value: + kvlist_value: + values: + - key: "kvlist_kvlist_kvlist_string" + value: + string_value: "plain_string_value" + - key: "kvlist_kvlist_kvlist_protocol" + value: + string_value: "%PROTOCOL%" + - key: "kvlist_kvlist_array" + value: + array_value: + values: + - string_value: "kvlist_kvlist_array_string" + - string_value: "%PROTOCOL%" + - key: "kvlist_array" + value: + array_value: + values: + - string_value: "kvlist_array_string" + - string_value: "%PROTOCOL%" + - kvlist_value: + values: + - key: "kvlist_array_kvlist_string" + value: + string_value: "plain_string_value" + - key: "kvlist_array_kvlist_protocol" + value: + string_value: "%PROTOCOL%" + - array_value: + values: + - string_value: "kvlist_array_array_string" + - string_value: "%PROTOCOL%" + - key: "array" + value: + array_value: + values: + - string_value: "array_string" + - string_value: "%PROTOCOL%" + - kvlist_value: + values: + - key: "array_kvlist_string" + value: + string_value: "plain_string_value" + - key: "array_kvlist_protocol" + value: + string_value: "%PROTOCOL%" + - key: "array_kvlist_kvlist" + value: + kvlist_value: + values: + - key: "array_kvlist_kvlist_string" + value: + string_value: "plain_string_value" + - key: "array_kvlist_kvlist_protocol" + value: + string_value: "%PROTOCOL%" + - key: "array_kvlist_array" + value: + array_value: + values: + - string_value: "array_kvlist_array_string" + - string_value: "%PROTOCOL%" + - array_value: + values: + - string_value: "array_array_string" + - string_value: "%PROTOCOL%" + - array_value: + values: + - string_value: "array_array_array_string" + - string_value: "%PROTOCOL%" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + KeyValueList expected; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "kvlist" + value: + kvlist_value: + values: + - key: "kvlist_string" + value: + string_value: "plain_string_value" + - key: "kvlist_protocol" + value: + string_value: "HTTP/1.1" + - key: "kvlist_kvlist" + value: + kvlist_value: + values: + - key: "kvlist_kvlist_string" + value: + string_value: "plain_string_value" + - key: "kvlist_kvlist_protocol" + value: + string_value: "HTTP/1.1" + - key: "kvlist_kvlist_kvlist" + value: + kvlist_value: + values: + - key: "kvlist_kvlist_kvlist_string" + value: + string_value: "plain_string_value" + - key: "kvlist_kvlist_kvlist_protocol" + value: + string_value: "HTTP/1.1" + - key: "kvlist_kvlist_array" + value: + array_value: + values: + - string_value: "kvlist_kvlist_array_string" + - string_value: "HTTP/1.1" + - key: "kvlist_array" + value: + array_value: + values: + - string_value: "kvlist_array_string" + - string_value: "HTTP/1.1" + - kvlist_value: + values: + - key: "kvlist_array_kvlist_string" + value: + string_value: "plain_string_value" + - key: "kvlist_array_kvlist_protocol" + value: + string_value: "HTTP/1.1" + - array_value: + values: + - string_value: "kvlist_array_array_string" + - string_value: "HTTP/1.1" + - key: "array" + value: + array_value: + values: + - string_value: "array_string" + - string_value: "HTTP/1.1" + - kvlist_value: + values: + - key: "array_kvlist_string" + value: + string_value: "plain_string_value" + - key: "array_kvlist_protocol" + value: + string_value: "HTTP/1.1" + - key: "array_kvlist_kvlist" + value: + kvlist_value: + values: + - key: "array_kvlist_kvlist_string" + value: + string_value: "plain_string_value" + - key: "array_kvlist_kvlist_protocol" + value: + string_value: "HTTP/1.1" + - key: "array_kvlist_array" + value: + array_value: + values: + - string_value: "array_kvlist_array_string" + - string_value: "HTTP/1.1" + - array_value: + values: + - string_value: "array_array_string" + - string_value: "HTTP/1.1" + - array_value: + values: + - string_value: "array_array_array_string" + - string_value: "HTTP/1.1" + )EOF", + expected); + const KeyValueList output = + formatter.format(request_header, response_header, response_trailer, stream_info, body); + EXPECT_TRUE(TestUtility::protoEqual(output, expected)); +} + +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterSingleOperatorTest) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header; + Http::TestResponseHeaderMapImpl response_header; + Http::TestResponseTrailerMapImpl response_trailer; + std::string body; + + absl::optional protocol = Http::Protocol::Http11; + EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); + + OpenTelemetryFormatMap expected = {{"protocol", "HTTP/1.1"}}; + + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "protocol" + value: + string_value: "%PROTOCOL%" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + + verifyOpenTelemetryOutput( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + expected); +} + +TEST(SubstitutionFormatterTest, EmptyOpenTelemetryFormatterTest) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header; + Http::TestResponseHeaderMapImpl response_header; + Http::TestResponseTrailerMapImpl response_trailer; + std::string body; + + absl::optional protocol = Http::Protocol::Http11; + EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); + + OpenTelemetryFormatMap expected = {{"protocol", ""}}; + + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "protocol" + value: + string_value: "" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + + verifyOpenTelemetryOutput( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + expected); +} + +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterNonExistentHeaderTest) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header{{"some_request_header", "SOME_REQUEST_HEADER"}}; + Http::TestResponseHeaderMapImpl response_header{{"some_response_header", "SOME_RESPONSE_HEADER"}}; + Http::TestResponseTrailerMapImpl response_trailer; + std::string body; + + OpenTelemetryFormatMap expected = {{"protocol", "HTTP/1.1"}, + {"some_request_header", "SOME_REQUEST_HEADER"}, + {"nonexistent_response_header", "-"}, + {"some_response_header", "SOME_RESPONSE_HEADER"}}; + + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "protocol" + value: + string_value: "%PROTOCOL%" + - key: "some_request_header" + value: + string_value: "%REQ(some_request_header)%" + - key: "nonexistent_response_header" + value: + string_value: "%RESP(nonexistent_response_header)%" + - key: "some_response_header" + value: + string_value: "%RESP(some_response_header)%" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + + absl::optional protocol = Http::Protocol::Http11; + EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); + + verifyOpenTelemetryOutput( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + expected); +} + +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterAlternateHeaderTest) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header{ + {"request_present_header", "REQUEST_PRESENT_HEADER"}}; + Http::TestResponseHeaderMapImpl response_header{ + {"response_present_header", "RESPONSE_PRESENT_HEADER"}}; + Http::TestResponseTrailerMapImpl response_trailer; + std::string body; + + OpenTelemetryFormatMap expected = { + {"request_present_header_or_request_absent_header", "REQUEST_PRESENT_HEADER"}, + {"request_absent_header_or_request_present_header", "REQUEST_PRESENT_HEADER"}, + {"response_absent_header_or_response_absent_header", "RESPONSE_PRESENT_HEADER"}, + {"response_present_header_or_response_absent_header", "RESPONSE_PRESENT_HEADER"}}; + + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "request_present_header_or_request_absent_header" + value: + string_value: "%REQ(request_present_header?request_absent_header)%" + - key: "request_absent_header_or_request_present_header" + value: + string_value: "%REQ(request_absent_header?request_present_header)%" + - key: "response_absent_header_or_response_absent_header" + value: + string_value: "%RESP(response_absent_header?response_present_header)%" + - key: "response_present_header_or_response_absent_header" + value: + string_value: "%RESP(response_present_header?response_absent_header)%" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + + absl::optional protocol = Http::Protocol::Http11; + EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); + + verifyOpenTelemetryOutput( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + expected); +} + +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterDynamicMetadataTest) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header{{"first", "GET"}, {":path", "/"}}; + Http::TestResponseHeaderMapImpl response_header{{"second", "PUT"}, {"test", "test"}}; + Http::TestResponseTrailerMapImpl response_trailer{{"third", "POST"}, {"test-2", "test-2"}}; + std::string body; + + envoy::config::core::v3::Metadata metadata; + populateMetadataTestData(metadata); + EXPECT_CALL(stream_info, dynamicMetadata()).WillRepeatedly(ReturnRef(metadata)); + EXPECT_CALL(Const(stream_info), dynamicMetadata()).WillRepeatedly(ReturnRef(metadata)); + + OpenTelemetryFormatMap expected = {{"test_key", "test_value"}, + {"test_obj", "{\"inner_key\":\"inner_value\"}"}, + {"test_obj.inner_key", "inner_value"}}; + + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "test_key" + value: + string_value: "%DYNAMIC_METADATA(com.test:test_key)%" + - key: "test_obj" + value: + string_value: "%DYNAMIC_METADATA(com.test:test_obj)%" + - key: "test_obj.inner_key" + value: + string_value: "%DYNAMIC_METADATA(com.test:test_obj:inner_key)%" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + + verifyOpenTelemetryOutput( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + expected); +} + +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterClusterMetadataTest) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header{{"first", "GET"}, {":path", "/"}}; + Http::TestResponseHeaderMapImpl response_header{{"second", "PUT"}, {"test", "test"}}; + Http::TestResponseTrailerMapImpl response_trailer{{"third", "POST"}, {"test-2", "test-2"}}; + std::string body; + + envoy::config::core::v3::Metadata metadata; + populateMetadataTestData(metadata); + absl::optional>> cluster = + std::make_shared>(); + EXPECT_CALL(**cluster, metadata()).WillRepeatedly(ReturnRef(metadata)); + EXPECT_CALL(stream_info, upstreamClusterInfo()).WillRepeatedly(ReturnPointee(cluster)); + EXPECT_CALL(Const(stream_info), upstreamClusterInfo()).WillRepeatedly(ReturnPointee(cluster)); + + OpenTelemetryFormatMap expected = { + {"test_key", "test_value"}, + {"test_obj", "{\"inner_key\":\"inner_value\"}"}, + {"test_obj.inner_key", "inner_value"}, + {"test_obj.non_existing_key", "-"}, + }; + + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "test_key" + value: + string_value: "%CLUSTER_METADATA(com.test:test_key)%" + - key: "test_obj" + value: + string_value: "%CLUSTER_METADATA(com.test:test_obj)%" + - key: "test_obj.inner_key" + value: + string_value: "%CLUSTER_METADATA(com.test:test_obj:inner_key)%" + - key: "test_obj.non_existing_key" + value: + string_value: "%CLUSTER_METADATA(com.test:test_obj:non_existing_key)%" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + + verifyOpenTelemetryOutput( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + expected); +} + +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterClusterMetadataNoClusterInfoTest) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header{{"first", "GET"}, {":path", "/"}}; + Http::TestResponseHeaderMapImpl response_header{{"second", "PUT"}, {"test", "test"}}; + Http::TestResponseTrailerMapImpl response_trailer{{"third", "POST"}, {"test-2", "test-2"}}; + std::string body; + + OpenTelemetryFormatMap expected = {{"test_key", "-"}}; + + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "test_key" + value: + string_value: "%CLUSTER_METADATA(com.test:test_key)%" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + + // Empty optional (absl::nullopt) + { + EXPECT_CALL(Const(stream_info), upstreamClusterInfo()).WillOnce(Return(absl::nullopt)); + verifyOpenTelemetryOutput( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + expected); + } + // Empty cluster info (nullptr) + { + EXPECT_CALL(Const(stream_info), upstreamClusterInfo()).WillOnce(Return(nullptr)); + verifyOpenTelemetryOutput( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + expected); + } +} + +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterFilterStateTest) { + Http::TestRequestHeaderMapImpl request_headers; + Http::TestResponseHeaderMapImpl response_headers; + Http::TestResponseTrailerMapImpl response_trailers; + StreamInfo::MockStreamInfo stream_info; + std::string body; + stream_info.filter_state_->setData("test_key", + std::make_unique("test_value"), + StreamInfo::FilterState::StateType::ReadOnly); + stream_info.filter_state_->setData("test_obj", + std::make_unique(), + StreamInfo::FilterState::StateType::ReadOnly); + EXPECT_CALL(Const(stream_info), filterState()).Times(testing::AtLeast(1)); + + OpenTelemetryFormatMap expected = {{"test_key", "\"test_value\""}, + {"test_obj", "{\"inner_key\":\"inner_value\"}"}}; + + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "test_key" + value: + string_value: "%FILTER_STATE(test_key)%" + - key: "test_obj" + value: + string_value: "%FILTER_STATE(test_obj)%" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + + verifyOpenTelemetryOutput( + formatter.format(request_headers, response_headers, response_trailers, stream_info, body), + expected); +} + +// Test new specifier (PLAIN/TYPED) of FilterState. Ensure that after adding additional specifier, +// the FilterState can call the serializeAsProto or serializeAsString methods correctly. +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterFilterStateSpeciferTest) { + Http::TestRequestHeaderMapImpl request_headers; + Http::TestResponseHeaderMapImpl response_headers; + Http::TestResponseTrailerMapImpl response_trailers; + StreamInfo::MockStreamInfo stream_info; + std::string body; + stream_info.filter_state_->setData( + "test_key", std::make_unique("test_value"), + StreamInfo::FilterState::StateType::ReadOnly); + EXPECT_CALL(Const(stream_info), filterState()).Times(testing::AtLeast(1)); + + OpenTelemetryFormatMap expected = { + {"test_key_plain", "test_value By PLAIN"}, + {"test_key_typed", "\"test_value By TYPED\""}, + }; + + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "test_key_plain" + value: + string_value: "%FILTER_STATE(test_key:PLAIN)%" + - key: "test_key_typed" + value: + string_value: "%FILTER_STATE(test_key:TYPED)%" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + + verifyOpenTelemetryOutput( + formatter.format(request_headers, response_headers, response_trailers, stream_info, body), + expected); +} + +// Error specifier will cause an exception to be thrown. +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterFilterStateErrorSpeciferTest) { + Http::TestRequestHeaderMapImpl request_headers; + Http::TestResponseHeaderMapImpl response_headers; + Http::TestResponseTrailerMapImpl response_trailers; + StreamInfo::MockStreamInfo stream_info; + std::string body; + stream_info.filter_state_->setData( + "test_key", std::make_unique("test_value"), + StreamInfo::FilterState::StateType::ReadOnly); + + // 'ABCDE' is error specifier. + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "test_key_plain" + value: + string_value: "%FILTER_STATE(test_key:ABCDE)%" + - key: "test_key_typed" + value: + string_value: "%FILTER_STATE(test_key:TYPED)%" + )EOF", + key_mapping); + EXPECT_THROW_WITH_MESSAGE(OpenTelemetryFormatter formatter(key_mapping), EnvoyException, + "Invalid filter state serialize type, only support PLAIN/TYPED."); +} + +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterStartTimeTest) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header; + Http::TestResponseHeaderMapImpl response_header; + Http::TestResponseTrailerMapImpl response_trailer; + std::string body; + + time_t expected_time_in_epoch = 1522280158; + SystemTime time = std::chrono::system_clock::from_time_t(expected_time_in_epoch); + EXPECT_CALL(stream_info, startTime()).WillRepeatedly(Return(time)); + + OpenTelemetryFormatMap expected = {{"simple_date", "2018/03/28"}, + {"test_time", fmt::format("{}", expected_time_in_epoch)}, + {"bad_format", "bad_format"}, + {"default", "2018-03-28T23:35:58.000Z"}, + {"all_zeroes", "000000000.0.00.000"}}; + + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "simple_date" + value: + string_value: "%START_TIME(%Y/%m/%d)%" + - key: "test_time" + value: + string_value: "%START_TIME(%s)%" + - key: "bad_format" + value: + string_value: "%START_TIME(bad_format)%" + - key: "default" + value: + string_value: "%START_TIME%" + - key: "all_zeroes" + value: + string_value: "%START_TIME(%f.%1f.%2f.%3f)%" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + + verifyOpenTelemetryOutput( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + expected); +} + +TEST(SubstitutionFormatterTest, OpenTelemetryFormatterMultiTokenTest) { + { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header{{"some_request_header", "SOME_REQUEST_HEADER"}}; + Http::TestResponseHeaderMapImpl response_header{ + {"some_response_header", "SOME_RESPONSE_HEADER"}}; + Http::TestResponseTrailerMapImpl response_trailer; + std::string body; + + OpenTelemetryFormatMap expected = { + {"multi_token_field", "HTTP/1.1 plainstring SOME_REQUEST_HEADER SOME_RESPONSE_HEADER"}}; + + KeyValueList key_mapping; + TestUtility::loadFromYaml(R"EOF( + values: + - key: "multi_token_field" + value: + string_value: "%PROTOCOL% plainstring %REQ(some_request_header)% %RESP(some_response_header)%" + )EOF", + key_mapping); + OpenTelemetryFormatter formatter(key_mapping); + + absl::optional protocol = Http::Protocol::Http11; + EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); + + verifyOpenTelemetryOutput( + formatter.format(request_header, response_header, response_trailer, stream_info, body), + expected); + } +} + +} // namespace +} // namespace OpenTelemetry +} // namespace AccessLoggers +} // namespace Extensions +} // namespace Envoy diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index da28a8a4cd245..224d5d398f769 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -771,6 +771,7 @@ keyder kqueue kubernetes kv +kvlist kvs lala latencies From 03db1e1932e898742fd7d53dad6336c083fcf4b9 Mon Sep 17 00:00:00 2001 From: birenroy Date: Mon, 15 Nov 2021 11:17:45 -0500 Subject: [PATCH 080/110] Uses a new nghttp2 codec wrapper API in codec_impl. (#18294) Guarded by runtime config "envoy.reloadable_features.http2_new_codec_wrapper". The codec_impl unit tests have been parameterized to run with the setting both enabled and disabled. I have run integration tests in my local checkout by temporarily moving the new runtime config setting to the default-enabled group. This work will enable us to switch to a new HTTP/2 implementation with minimal further modifications to codec_impl. Signed-off-by: Biren Roy --- bazel/external/quiche.BUILD | 79 ++++ bazel/external/quiche.genrule_cmd | 2 + bazel/external/quiche.patch | 139 ++++++ bazel/genrule_repository.bzl | 2 +- bazel/repositories.bzl | 5 + source/common/http/http2/BUILD | 2 + source/common/http/http2/codec_impl.cc | 398 ++++++++++++++---- source/common/http/http2/codec_impl.h | 76 +++- source/common/http/http2/metadata_decoder.h | 2 + source/common/http/http2/metadata_encoder.cc | 53 +++ source/common/http/http2/metadata_encoder.h | 28 ++ source/common/runtime/runtime_features.cc | 2 + test/common/http/http2/BUILD | 27 +- test/common/http/http2/codec_impl_test.cc | 321 +++++++++----- test/common/http/http2/codec_impl_test_util.h | 20 +- .../http/http2/metadata_encoder_test.cc | 341 +++++++++++++++ test/extensions/filters/http/jwt_authn/BUILD | 1 + test/integration/BUILD | 10 +- .../buffer_accounting_integration_test.cc | 24 +- .../h2_wrapped_capture_fuzz_test.cc | 32 ++ .../http2_flood_integration_test.cc | 29 +- test/integration/http_protocol_integration.cc | 19 +- test/integration/http_protocol_integration.h | 6 +- tools/spelling/spelling_dictionary.txt | 1 + 24 files changed, 1380 insertions(+), 239 deletions(-) create mode 100644 bazel/external/quiche.patch create mode 100644 test/common/http/http2/metadata_encoder_test.cc create mode 100644 test/integration/h2_wrapped_capture_fuzz_test.cc diff --git a/bazel/external/quiche.BUILD b/bazel/external/quiche.BUILD index 8c6415833a678..8341f4832ec7b 100644 --- a/bazel/external/quiche.BUILD +++ b/bazel/external/quiche.BUILD @@ -83,6 +83,59 @@ envoy_cc_test_library( deps = [":http2_platform"], ) +envoy_cc_library( + name = "http2_adapter", + srcs = [ + "quiche/http2/adapter/callback_visitor.cc", + "quiche/http2/adapter/header_validator.cc", + "quiche/http2/adapter/http2_protocol.cc", + "quiche/http2/adapter/http2_util.cc", + "quiche/http2/adapter/nghttp2_adapter.cc", + "quiche/http2/adapter/nghttp2_callbacks.cc", + "quiche/http2/adapter/nghttp2_data_provider.cc", + "quiche/http2/adapter/nghttp2_session.cc", + "quiche/http2/adapter/nghttp2_util.cc", + "quiche/http2/adapter/oghttp2_adapter.cc", + "quiche/http2/adapter/oghttp2_session.cc", + "quiche/http2/adapter/oghttp2_util.cc", + "quiche/http2/adapter/window_manager.cc", + ], + hdrs = [ + "quiche/http2/adapter/callback_visitor.h", + "quiche/http2/adapter/data_source.h", + "quiche/http2/adapter/header_validator.h", + "quiche/http2/adapter/http2_adapter.h", + "quiche/http2/adapter/http2_protocol.h", + "quiche/http2/adapter/http2_session.h", + "quiche/http2/adapter/http2_util.h", + "quiche/http2/adapter/http2_visitor_interface.h", + "quiche/http2/adapter/nghttp2_adapter.h", + "quiche/http2/adapter/nghttp2_callbacks.h", + "quiche/http2/adapter/nghttp2_data_provider.h", + "quiche/http2/adapter/nghttp2_session.h", + "quiche/http2/adapter/nghttp2_util.h", + "quiche/http2/adapter/oghttp2_adapter.h", + "quiche/http2/adapter/oghttp2_session.h", + "quiche/http2/adapter/oghttp2_util.h", + "quiche/http2/adapter/window_manager.h", + ], + copts = quiche_copts, + external_deps = [ + "abseil_algorithm", + "nghttp2", + ], + repository = "@envoy", + visibility = ["//visibility:public"], + deps = [ + ":http2_core_http2_trace_logging_lib", + ":http2_core_priority_write_scheduler_lib", + ":spdy_core_framer_lib", + ":spdy_core_header_block_lib", + ":spdy_core_http2_deframer_lib", + ":spdy_core_protocol_lib", + ], +) + envoy_cc_library( name = "http2_core_http2_priority_write_scheduler_lib", hdrs = ["quiche/http2/core/http2_priority_write_scheduler.h"], @@ -108,6 +161,21 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "http2_core_http2_trace_logging_lib", + srcs = ["quiche/http2/core/http2_trace_logging.cc"], + hdrs = ["quiche/http2/core/http2_trace_logging.h"], + copts = quiche_copts, + repository = "@envoy", + deps = [ + ":quiche_common_platform", + ":spdy_core_headers_handler_interface_lib", + ":spdy_core_http2_deframer_lib", + ":spdy_core_protocol_lib", + ":spdy_core_recording_headers_handler_lib", + ], +) + envoy_cc_library( name = "http2_core_write_scheduler_lib", hdrs = ["quiche/http2/core/write_scheduler.h"], @@ -960,6 +1028,17 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "spdy_core_recording_headers_handler_lib", + srcs = ["quiche/spdy/core/recording_headers_handler.cc"], + hdrs = ["quiche/spdy/core/recording_headers_handler.h"], + repository = "@envoy", + deps = [ + ":spdy_core_header_block_lib", + ":spdy_core_headers_handler_interface_lib", + ], +) + envoy_cc_library( name = "spdy_core_write_scheduler_lib", hdrs = ["quiche/spdy/core/write_scheduler.h"], diff --git a/bazel/external/quiche.genrule_cmd b/bazel/external/quiche.genrule_cmd index ed451e6d9e338..c80eaa882b5d1 100644 --- a/bazel/external/quiche.genrule_cmd +++ b/bazel/external/quiche.genrule_cmd @@ -62,6 +62,8 @@ cat <sed_commands # Rewrite third_party includes. /^#include/ s!third_party/boringssl/src/include/!! +/^#include/ s!third_party/nghttp2/src/lib/includes/nghttp2!nghttp2! +/^#include/ s!third_party/nghttp2!nghttp2! /^#include/ s!third_party/zlib/zlib!zlib! /^import/ s!quic/core/proto/cached_network_parameters!quiche/quic/core/proto/cached_network_parameters! diff --git a/bazel/external/quiche.patch b/bazel/external/quiche.patch new file mode 100644 index 0000000000000..9a07b463b2aeb --- /dev/null +++ b/bazel/external/quiche.patch @@ -0,0 +1,139 @@ +--- http2/adapter/callback_visitor.h ++++ http2/adapter/callback_visitor.h +@@ -8,6 +8,10 @@ + #include "absl/container/flat_hash_map.h" + #include "http2/adapter/http2_visitor_interface.h" + #include "http2/adapter/nghttp2_util.h" ++ ++// Required to build on Windows. ++typedef ptrdiff_t ssize_t; ++ + #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" + #include "common/platform/api/quiche_export.h" + +--- http2/adapter/http2_protocol.h ++++ http2/adapter/http2_protocol.h +@@ -10,6 +10,8 @@ + #include "absl/types/variant.h" + #include "common/platform/api/quiche_export.h" + ++#undef NO_ERROR ++ + namespace http2 { + namespace adapter { + +--- http2/adapter/nghttp2_callbacks.h ++++ http2/adapter/nghttp2_callbacks.h +@@ -5,6 +5,10 @@ + + #include "http2/adapter/http2_protocol.h" + #include "http2/adapter/nghttp2_util.h" ++ ++// Required to build on Windows. ++typedef ptrdiff_t ssize_t; ++ + #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" + + namespace http2 { +--- http2/adapter/nghttp2_data_provider.h ++++ http2/adapter/nghttp2_data_provider.h +@@ -5,6 +5,10 @@ + #include + + #include "http2/adapter/data_source.h" ++ ++// Required to build on Windows. ++typedef ptrdiff_t ssize_t; ++ + #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" + + namespace http2 { +--- http2/adapter/nghttp2_session.h ++++ http2/adapter/nghttp2_session.h +@@ -5,6 +5,10 @@ + + #include "http2/adapter/http2_session.h" + #include "http2/adapter/nghttp2_util.h" ++ ++// Required to build on Windows. ++typedef ptrdiff_t ssize_t; ++ + #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" + #include "common/platform/api/quiche_export.h" + +--- http2/adapter/nghttp2_util.h ++++ http2/adapter/nghttp2_util.h +@@ -10,6 +10,10 @@ + #include "absl/types/span.h" + #include "http2/adapter/data_source.h" + #include "http2/adapter/http2_protocol.h" ++ ++// Required to build on Windows. ++typedef ptrdiff_t ssize_t; ++ + #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" + #include "spdy/core/spdy_header_block.h" + +--- http2/adapter/oghttp2_session.cc ++++ http2/adapter/oghttp2_session.cc +@@ -131,6 +131,7 @@ absl::string_view TracePerspectiveAsStri + case Perspective::kServer: + return "OGHTTP2_SERVER"; + } ++ return "OGHTTP2_SERVER"; + } + + class RunOnExit { +@@ -186,6 +187,7 @@ Http2ErrorCode GetHttp2ErrorCode(SpdyFra + case SpdyFramerError::LAST_ERROR: + return Http2ErrorCode::INTERNAL_ERROR; + } ++ return Http2ErrorCode::INTERNAL_ERROR; + } + + } // namespace +--- http2/adapter/http2_util.cc ++++ http2/adapter/http2_util.cc +@@ -1,5 +1,7 @@ + #include "third_party/http2/adapter/http2_util.h" + ++#undef NO_ERROR ++ + namespace http2 { + namespace adapter { + namespace { +@@ -42,6 +44,7 @@ spdy::SpdyErrorCode TranslateErrorCode(H + case Http2ErrorCode::HTTP_1_1_REQUIRED: + return spdy::ERROR_CODE_HTTP_1_1_REQUIRED; + } ++ return spdy::ERROR_CODE_INTERNAL_ERROR; + } + + Http2ErrorCode TranslateErrorCode(spdy::SpdyErrorCode code) { +@@ -76,6 +77,7 @@ Http2ErrorCode TranslateErrorCode(spdy:: + case spdy::ERROR_CODE_HTTP_1_1_REQUIRED: + return Http2ErrorCode::HTTP_1_1_REQUIRED; + } ++ return Http2ErrorCode::INTERNAL_ERROR; + } + + absl::string_view ConnectionErrorToString(ConnectionError error) { +@@ -95,6 +96,7 @@ absl::string_view ConnectionErrorToStrin + case ConnectionError::kInvalidPushPromise: + return "InvalidPushPromise"; + } ++ return "UnknownConnectionError"; + } + + } // namespace adapter +--- http2/adapter/oghttp2_session.cc ++++ http2/adapter/oghttp2_session.cc +@@ -11,6 +11,8 @@ + #include "http2/adapter/oghttp2_util.h" + #include "spdy/core/spdy_protocol.h" + ++#undef NO_ERROR ++ + namespace http2 { + namespace adapter { + diff --git a/bazel/genrule_repository.bzl b/bazel/genrule_repository.bzl index cfa9e7be44e9a..e263c43d4689f 100644 --- a/bazel/genrule_repository.bzl +++ b/bazel/genrule_repository.bzl @@ -11,7 +11,7 @@ def _genrule_repository(ctx): ctx.symlink(patch, patch_input) patch_result = ctx.execute(["patch", "-p0", "--input", patch_input]) if patch_result.return_code != 0: - fail("Failed to apply patch %r: %s" % (patch, patch_result.stderr)) + fail("Failed to apply patch %r: %s, %s" % (patch, patch_result.stderr, patch_result.stdout)) genrule_cmd = ctx.read(ctx.attr.genrule_cmd_file) ctx.file("WORKSPACE", "workspace(name=%r)" % (ctx.name,)) diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 0fd7212d032ba..22dbc2c6b92e9 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -813,11 +813,16 @@ def _com_github_google_quiche(): name = "com_github_google_quiche", genrule_cmd_file = "@envoy//bazel/external:quiche.genrule_cmd", build_file = "@envoy//bazel/external:quiche.BUILD", + patches = ["@envoy//bazel/external:quiche.patch"], ) native.bind( name = "quiche_common_platform", actual = "@com_github_google_quiche//:quiche_common_platform", ) + native.bind( + name = "quiche_http2_adapter", + actual = "@com_github_google_quiche//:http2_adapter", + ) native.bind( name = "quiche_http2_platform", actual = "@com_github_google_quiche//:http2_platform", diff --git a/source/common/http/http2/BUILD b/source/common/http/http2/BUILD index 3ec423ce65c1d..c4c4bb1fb25dc 100644 --- a/source/common/http/http2/BUILD +++ b/source/common/http/http2/BUILD @@ -24,6 +24,7 @@ envoy_cc_library( hdrs = ["codec_impl.h"], external_deps = [ "nghttp2", + "quiche_http2_adapter", "abseil_optional", "abseil_inlined_vector", "abseil_algorithm", @@ -91,6 +92,7 @@ envoy_cc_library( hdrs = ["metadata_encoder.h"], external_deps = [ "nghttp2", + "quiche_http2_adapter", ], deps = [ "//envoy/http:codec_interface", diff --git a/source/common/http/http2/codec_impl.cc b/source/common/http/http2/codec_impl.cc index 9e4f76fa7b1f6..d663aca876d5f 100644 --- a/source/common/http/http2/codec_impl.cc +++ b/source/common/http/http2/codec_impl.cc @@ -28,6 +28,8 @@ #include "source/common/runtime/runtime_features.h" #include "absl/container/fixed_array.h" +#include "quiche/http2/adapter/callback_visitor.h" +#include "quiche/http2/adapter/nghttp2_adapter.h" namespace Envoy { namespace Http { @@ -106,17 +108,31 @@ bool Utility::reconstituteCrumbledCookies(const HeaderString& key, const HeaderS return true; } -ConnectionImpl::Http2Callbacks ConnectionImpl::http2_callbacks_; - -nghttp2_session* ProdNghttp2SessionFactory::create(const nghttp2_session_callbacks* callbacks, - ConnectionImpl* connection, - const nghttp2_option* options) { +nghttp2_session* ProdNghttp2SessionFactory::createOld(const nghttp2_session_callbacks* callbacks, + ConnectionImpl* connection, + const nghttp2_option* options) { nghttp2_session* session; nghttp2_session_client_new2(&session, callbacks, connection, options); return session; } -void ProdNghttp2SessionFactory::init(nghttp2_session*, ConnectionImpl* connection, +void ProdNghttp2SessionFactory::initOld( + nghttp2_session*, ConnectionImpl* connection, + const envoy::config::core::v3::Http2ProtocolOptions& options) { + connection->sendSettings(options, true); +} + +std::unique_ptr +ProdNghttp2SessionFactory::create(const nghttp2_session_callbacks* callbacks, + ConnectionImpl* connection, const nghttp2_option* options) { + auto visitor = std::make_unique( + http2::adapter::Perspective::kClient, *callbacks, connection); + http2::adapter::Http2VisitorInterface& v = *visitor; + connection->setVisitor(std::move(visitor)); + return http2::adapter::NgHttp2Adapter::CreateClientAdapter(v, options); +} + +void ProdNghttp2SessionFactory::init(ConnectionImpl* connection, const envoy::config::core::v3::Http2ProtocolOptions& options) { connection->sendSettings(options, true); } @@ -195,6 +211,25 @@ void ConnectionImpl::StreamImpl::buildHeaders(std::vector& final_hea }); } +http2::adapter::HeaderRep getRep(const HeaderString& str) { + if (str.isReference()) { + return str.getStringView(); + } else { + return std::string(str.getStringView()); + } +} + +std::vector +ConnectionImpl::StreamImpl::buildHeaders(const HeaderMap& headers) { + std::vector out; + out.reserve(headers.size()); + headers.iterate([&out](const HeaderEntry& header) -> HeaderMap::Iterate { + out.push_back({getRep(header.key()), getRep(header.value())}); + return HeaderMap::Iterate::Continue; + }); + return out; +} + void ConnectionImpl::ServerStreamImpl::encode1xxHeaders(const ResponseHeaderMap& headers) { ASSERT(HeaderUtility::isSpecial1xx(headers)); encodeHeaders(headers, false); @@ -292,13 +327,22 @@ void ConnectionImpl::StreamImpl::encodeTrailersBase(const HeaderMap& trailers) { void ConnectionImpl::StreamImpl::encodeMetadata(const MetadataMapVector& metadata_map_vector) { ASSERT(parent_.allow_metadata_); - MetadataEncoder& metadata_encoder = getMetadataEncoder(); - if (!metadata_encoder.createPayload(metadata_map_vector)) { - return; - } - for (uint8_t flags : metadata_encoder.payloadFrameFlagBytes()) { - submitMetadata(flags); + if (parent_.use_new_codec_wrapper_) { + NewMetadataEncoder& metadata_encoder = getMetadataEncoder(); + auto sources_vec = metadata_encoder.createSources(metadata_map_vector); + for (auto& source : sources_vec) { + parent_.adapter_->SubmitMetadata(stream_id_, 16 * 1024, std::move(source)); + } + } else { + MetadataEncoder& metadata_encoder = getMetadataEncoderOld(); + if (!metadata_encoder.createPayload(metadata_map_vector)) { + return; + } + for (uint8_t flags : metadata_encoder.payloadFrameFlagBytes()) { + submitMetadata(flags); + } } + if (parent_.sendPendingFramesAndHandleError()) { // Intended to check through coverage that this error case is tested return; @@ -315,7 +359,11 @@ void ConnectionImpl::StreamImpl::readDisable(bool disable) { ASSERT(read_disable_count_ > 0); --read_disable_count_; if (!buffersOverrun()) { - nghttp2_session_consume(parent_.session_, stream_id_, unconsumed_bytes_); + if (parent_.use_new_codec_wrapper_) { + parent_.adapter_->MarkDataConsumedForStream(stream_id_, unconsumed_bytes_); + } else { + nghttp2_session_consume(parent_.session_, stream_id_, unconsumed_bytes_); + } unconsumed_bytes_ = 0; if (parent_.sendPendingFramesAndHandleError()) { // Intended to check through coverage that this error case is tested @@ -410,14 +458,20 @@ void ConnectionImpl::StreamImpl::submitTrailers(const HeaderMap& trailers) { return; } - std::vector final_headers; - buildHeaders(final_headers, trailers); - int rc = nghttp2_submit_trailer(parent_.session_, stream_id_, final_headers.data(), - final_headers.size()); - ASSERT(rc == 0); + if (parent_.use_new_codec_wrapper_) { + std::vector final_headers = buildHeaders(trailers); + parent_.adapter_->SubmitTrailer(stream_id_, final_headers); + } else { + std::vector final_headers; + buildHeaders(final_headers, trailers); + int rc = nghttp2_submit_trailer(parent_.session_, stream_id_, final_headers.data(), + final_headers.size()); + ASSERT(rc == 0); + } } void ConnectionImpl::StreamImpl::submitMetadata(uint8_t flags) { + ASSERT(parent_.use_new_codec_wrapper_ == false); ASSERT(stream_id_ > 0); const int result = nghttp2_submit_extension(parent_.session_, METADATA_FRAME_TYPE, flags, stream_id_, nullptr); @@ -468,21 +522,55 @@ void ConnectionImpl::StreamImpl::onDataSourceSend(const uint8_t* framehd, size_t void ConnectionImpl::ClientStreamImpl::submitHeaders(const HeaderMap& headers, nghttp2_data_provider* provider) { ASSERT(stream_id_ == -1); - std::vector final_headers; - buildHeaders(final_headers, headers); - stream_id_ = nghttp2_submit_request(parent_.session_, nullptr, final_headers.data(), - final_headers.size(), provider, base()); + if (parent_.use_new_codec_wrapper_) { + // TODO(birenroy): Once using the new wrapper, migrate callers from nghttp2_data_provider to + // DataFrameSource. + std::unique_ptr data_frame_source; + if (provider) { + data_frame_source = http2::adapter::MakeZeroCopyDataFrameSource( + *provider, &parent_.connection_, + [](nghttp2_session*, nghttp2_frame* frame, const uint8_t* framehd, size_t length, + nghttp2_data_source* source, void*) -> int { + ASSERT(frame->data.padlen == 0); + static_cast(source->ptr)->onDataSourceSend(framehd, length); + return 0; + }); + } + stream_id_ = parent_.adapter_->SubmitRequest(buildHeaders(headers), + std::move(data_frame_source), base()); + } else { + std::vector final_headers; + buildHeaders(final_headers, headers); + stream_id_ = nghttp2_submit_request(parent_.session_, nullptr, final_headers.data(), + final_headers.size(), provider, base()); + } ASSERT(stream_id_ > 0); } void ConnectionImpl::ServerStreamImpl::submitHeaders(const HeaderMap& headers, nghttp2_data_provider* provider) { ASSERT(stream_id_ != -1); - std::vector final_headers; - buildHeaders(final_headers, headers); - int rc = nghttp2_submit_response(parent_.session_, stream_id_, final_headers.data(), - final_headers.size(), provider); - ASSERT(rc == 0); + if (parent_.use_new_codec_wrapper_) { + std::unique_ptr data_frame_source; + if (provider) { + data_frame_source = http2::adapter::MakeZeroCopyDataFrameSource( + *provider, &parent_.connection_, + [](nghttp2_session*, nghttp2_frame* frame, const uint8_t* framehd, size_t length, + nghttp2_data_source* source, void*) -> int { + ASSERT(frame->data.padlen == 0); + static_cast(source->ptr)->onDataSourceSend(framehd, length); + return 0; + }); + } + parent_.adapter_->SubmitResponse(stream_id_, buildHeaders(headers), + std::move(data_frame_source)); + } else { + std::vector final_headers; + buildHeaders(final_headers, headers); + int rc = nghttp2_submit_response(parent_.session_, stream_id_, final_headers.data(), + final_headers.size(), provider); + ASSERT(rc == 0); + } } void ConnectionImpl::StreamImpl::onPendingFlushTimer() { @@ -516,8 +604,13 @@ void ConnectionImpl::StreamImpl::encodeDataHelper(Buffer::Instance& data, bool e parent_.stats_.pending_send_bytes_.add(data.length()); pending_send_data_->move(data); if (data_deferred_) { - int rc = nghttp2_session_resume_data(parent_.session_, stream_id_); - ASSERT(rc == 0); + if (parent_.use_new_codec_wrapper_) { + bool success = parent_.adapter_->ResumeStream(stream_id_); + ASSERT(success); + } else { + int rc = nghttp2_session_resume_data(parent_.session_, stream_id_); + ASSERT(rc == 0); + } data_deferred_ = false; } @@ -566,14 +659,26 @@ void ConnectionImpl::StreamImpl::resetStream(StreamResetReason reason) { } void ConnectionImpl::StreamImpl::resetStreamWorker(StreamResetReason reason) { - int rc = nghttp2_submit_rst_stream(parent_.session_, NGHTTP2_FLAG_NONE, stream_id_, - reasonToReset(reason)); - ASSERT(rc == 0); + if (parent_.use_new_codec_wrapper_) { + parent_.adapter_->SubmitRst(stream_id_, + static_cast(reasonToReset(reason))); + } else { + int rc = nghttp2_submit_rst_stream(parent_.session_, NGHTTP2_FLAG_NONE, stream_id_, + reasonToReset(reason)); + ASSERT(rc == 0); + } +} + +MetadataEncoder& ConnectionImpl::StreamImpl::getMetadataEncoderOld() { + if (metadata_encoder_old_ == nullptr) { + metadata_encoder_old_ = std::make_unique(); + } + return *metadata_encoder_old_; } -MetadataEncoder& ConnectionImpl::StreamImpl::getMetadataEncoder() { +NewMetadataEncoder& ConnectionImpl::StreamImpl::getMetadataEncoder() { if (metadata_encoder_ == nullptr) { - metadata_encoder_ = std::make_unique(); + metadata_encoder_ = std::make_unique(); } return *metadata_encoder_; } @@ -608,8 +713,10 @@ ConnectionImpl::ConnectionImpl(Network::Connection& connection, CodecStats& stat Random::RandomGenerator& random_generator, const envoy::config::core::v3::Http2ProtocolOptions& http2_options, const uint32_t max_headers_kb, const uint32_t max_headers_count) - : stats_(stats), connection_(connection), max_headers_kb_(max_headers_kb), - max_headers_count_(max_headers_count), + : use_new_codec_wrapper_( + Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_new_codec_wrapper")), + http2_callbacks_(use_new_codec_wrapper_), stats_(stats), connection_(connection), + max_headers_kb_(max_headers_kb), max_headers_count_(max_headers_count), per_stream_buffer_limit_(http2_options.initial_stream_window_size().value()), stream_error_on_invalid_http_messaging_( http2_options.override_stream_error_on_invalid_http_message().value()), @@ -641,7 +748,9 @@ ConnectionImpl::~ConnectionImpl() { for (const auto& stream : active_streams_) { stream->destroy(); } - nghttp2_session_del(session_); + if (!use_new_codec_wrapper_) { + nghttp2_session_del(session_); + } } void ConnectionImpl::sendKeepalive() { @@ -657,9 +766,14 @@ void ConnectionImpl::sendKeepalive() { std::chrono::duration_cast(now.time_since_epoch()).count(); ENVOY_CONN_LOG(trace, "Sending keepalive PING {}", connection_, ms_since_epoch); - // The last parameter is an opaque 8-byte buffer, so this cast is safe. - int rc = nghttp2_submit_ping(session_, 0 /*flags*/, reinterpret_cast(&ms_since_epoch)); - ASSERT(rc == 0); + if (use_new_codec_wrapper_) { + adapter_->SubmitPing(ms_since_epoch); + } else { + // The last parameter is an opaque 8-byte buffer, so this cast is safe. + int rc = + nghttp2_submit_ping(session_, 0 /*flags*/, reinterpret_cast(&ms_since_epoch)); + ASSERT(rc == 0); + } if (sendPendingFramesAndHandleError()) { // Intended to check through coverage that this error case is tested @@ -715,8 +829,12 @@ Http::Status ConnectionImpl::dispatch(Buffer::Instance& data) { for (const Buffer::RawSlice& slice : data.getRawSlices()) { current_slice_ = &slice; dispatching_ = true; - ssize_t rc = - nghttp2_session_mem_recv(session_, static_cast(slice.mem_), slice.len_); + ssize_t rc; + if (use_new_codec_wrapper_) { + rc = adapter_->ProcessBytes(absl::string_view(static_cast(slice.mem_), slice.len_)); + } else { + rc = nghttp2_session_mem_recv(session_, static_cast(slice.mem_), slice.len_); + } if (!nghttp2_callback_status_.ok()) { return nghttp2_callback_status_; } @@ -750,8 +868,12 @@ const ConnectionImpl::StreamImpl* ConnectionImpl::getStream(int32_t stream_id) c } ConnectionImpl::StreamImpl* ConnectionImpl::getStream(int32_t stream_id) { - StreamImpl* stream = - static_cast(nghttp2_session_get_stream_user_data(session_, stream_id)); + StreamImpl* stream; + if (use_new_codec_wrapper_) { + stream = static_cast(adapter_->GetStreamUserData(stream_id)); + } else { + stream = static_cast(nghttp2_session_get_stream_user_data(session_, stream_id)); + } SLOW_ASSERT(stream != nullptr || !slowContainsStreamId(stream_id)); return stream; } @@ -766,7 +888,11 @@ int ConnectionImpl::onData(int32_t stream_id, const uint8_t* data, size_t len) { // Update the window to the peer unless some consumer of this stream's data has hit a flow control // limit and disabled reads on this stream if (!stream->buffersOverrun()) { - nghttp2_session_consume(session_, stream_id, len); + if (use_new_codec_wrapper_) { + adapter_->MarkDataConsumedForStream(stream_id, len); + } else { + nghttp2_session_consume(session_, stream_id, len); + } } else { stream->unconsumed_bytes_ += len; } @@ -774,10 +900,15 @@ int ConnectionImpl::onData(int32_t stream_id, const uint8_t* data, size_t len) { } void ConnectionImpl::goAway() { - int rc = nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, - nghttp2_session_get_last_proc_stream_id(session_), - NGHTTP2_NO_ERROR, nullptr, 0); - ASSERT(rc == 0); + if (use_new_codec_wrapper_) { + adapter_->SubmitGoAway(adapter_->GetHighestReceivedStreamId(), + http2::adapter::Http2ErrorCode::NO_ERROR, ""); + } else { + int rc = nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, + nghttp2_session_get_last_proc_stream_id(session_), + NGHTTP2_NO_ERROR, nullptr, 0); + ASSERT(rc == 0); + } if (sendPendingFramesAndHandleError()) { // Intended to check through coverage that this error case is tested @@ -786,8 +917,12 @@ void ConnectionImpl::goAway() { } void ConnectionImpl::shutdownNotice() { - int rc = nghttp2_submit_shutdown_notice(session_); - ASSERT(rc == 0); + if (use_new_codec_wrapper_) { + adapter_->SubmitShutdownNotice(); + } else { + int rc = nghttp2_submit_shutdown_notice(session_); + ASSERT(rc == 0); + } if (sendPendingFramesAndHandleError()) { // Intended to check through coverage that this error case is tested @@ -796,10 +931,15 @@ void ConnectionImpl::shutdownNotice() { } Status ConnectionImpl::protocolErrorForTest() { - int rc = nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, - nghttp2_session_get_last_proc_stream_id(session_), - NGHTTP2_PROTOCOL_ERROR, nullptr, 0); - ASSERT(rc == 0); + if (use_new_codec_wrapper_) { + adapter_->SubmitGoAway(adapter_->GetHighestReceivedStreamId(), + http2::adapter::Http2ErrorCode::PROTOCOL_ERROR, ""); + } else { + int rc = nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, + nghttp2_session_get_last_proc_stream_id(session_), + NGHTTP2_PROTOCOL_ERROR, nullptr, 0); + ASSERT(rc == 0); + } return sendPendingFrames(); } @@ -904,8 +1044,10 @@ Status ConnectionImpl::onFrameReceived(const nghttp2_frame* frame) { // It's possible that we are waiting to send a deferred reset, so only raise headers/trailers // if local is not complete. if (!stream->deferred_reset_) { - if (nghttp2_session_check_server_session(session_) || - stream->received_noninformational_headers_) { + const bool is_server_session = use_new_codec_wrapper_ + ? adapter_->IsServerSession() + : nghttp2_session_check_server_session(session_); + if (is_server_session || stream->received_noninformational_headers_) { ASSERT(stream->remote_end_stream_); stream->decodeTrailers(); } else { @@ -1132,9 +1274,15 @@ int ConnectionImpl::onStreamClose(int32_t stream_id, uint32_t error_code) { // Any unconsumed data must be consumed before the stream is deleted. // nghttp2 does not appear to track this internally, and any stream deleted // with outstanding window will contribute to a slow connection-window leak. - nghttp2_session_consume(session_, stream_id, stream->unconsumed_bytes_); - stream->unconsumed_bytes_ = 0; - nghttp2_session_set_stream_user_data(session_, stream->stream_id_, nullptr); + if (use_new_codec_wrapper_) { + adapter_->MarkDataConsumedForStream(stream_id, stream->unconsumed_bytes_); + stream->unconsumed_bytes_ = 0; + adapter_->SetStreamUserData(stream->stream_id_, nullptr); + } else { + nghttp2_session_consume(session_, stream_id, stream->unconsumed_bytes_); + stream->unconsumed_bytes_ = 0; + nghttp2_session_set_stream_user_data(session_, stream->stream_id_, nullptr); + } } return 0; @@ -1166,6 +1314,7 @@ int ConnectionImpl::onMetadataFrameComplete(int32_t stream_id, bool end_metadata } ssize_t ConnectionImpl::packMetadata(int32_t stream_id, uint8_t* buf, size_t len) { + ASSERT(use_new_codec_wrapper_ == false); ENVOY_CONN_LOG(trace, "pack METADATA frame on stream {}", connection_, stream_id); StreamImpl* stream = getStream(stream_id); @@ -1173,7 +1322,7 @@ ssize_t ConnectionImpl::packMetadata(int32_t stream_id, uint8_t* buf, size_t len return 0; } - MetadataEncoder& encoder = stream->getMetadataEncoder(); + MetadataEncoder& encoder = stream->getMetadataEncoderOld(); return encoder.packNextFramePayload(buf, len); } @@ -1218,7 +1367,12 @@ Status ConnectionImpl::sendPendingFrames() { return okStatus(); } - const int rc = nghttp2_session_send(session_); + int rc; + if (use_new_codec_wrapper_) { + rc = adapter_->Send(); + } else { + rc = nghttp2_session_send(session_); + } if (rc != 0) { ASSERT(rc == NGHTTP2_ERR_CALLBACK_FAILURE); return codecProtocolError(nghttp2_strerror(rc)); @@ -1267,7 +1421,53 @@ bool ConnectionImpl::sendPendingFramesAndHandleError() { return false; } -void ConnectionImpl::sendSettings( +void ConnectionImpl::sendSettingsHelper( + const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push) { + absl::InlinedVector settings; + auto insertParameter = [&settings](const http2::adapter::Http2Setting& entry) mutable -> bool { + // Consider using a set as an intermediate data structure, rather than this ad-hoc + // deduplication. + const auto it = std::find_if( + settings.cbegin(), settings.cend(), + [&entry](const http2::adapter::Http2Setting& existing) { return entry.id == existing.id; }); + if (it != settings.end()) { + return false; + } + settings.push_back(entry); + return true; + }; + + // Universally disable receiving push promise frames as we don't currently + // support them. nghttp2 will fail the connection if the other side still + // sends them. + // TODO(mattklein123): Remove this when we correctly proxy push promise. + // NOTE: This is a special case with respect to custom parameter overrides in + // that server push is not supported and therefore not end user configurable. + if (disable_push) { + settings.push_back({static_cast(http2::adapter::ENABLE_PUSH), disable_push ? 0U : 1U}); + } + + for (const auto& it : http2_options.custom_settings_parameters()) { + ASSERT(it.identifier().value() <= std::numeric_limits::max()); + const bool result = + insertParameter({static_cast(it.identifier().value()), + it.value().value()}); + ASSERT(result); + ENVOY_CONN_LOG(debug, "adding custom settings parameter with id {:#x} to {}", connection_, + it.identifier().value(), it.value().value()); + } + + // Insert named parameters. + settings.insert( + settings.end(), + {{http2::adapter::HEADER_TABLE_SIZE, http2_options.hpack_table_size().value()}, + {http2::adapter::ENABLE_CONNECT_PROTOCOL, http2_options.allow_connect()}, + {http2::adapter::MAX_CONCURRENT_STREAMS, http2_options.max_concurrent_streams().value()}, + {http2::adapter::INITIAL_WINDOW_SIZE, http2_options.initial_stream_window_size().value()}}); + adapter_->SubmitSettings(settings); +} + +void ConnectionImpl::sendSettingsHelperOld( const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push) { absl::InlinedVector settings; auto insertParameter = [&settings](const nghttp2_settings_entry& entry) mutable -> bool { @@ -1316,6 +1516,15 @@ void ConnectionImpl::sendSettings( int rc = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, nullptr, 0); ASSERT(rc == 0); } +} + +void ConnectionImpl::sendSettings( + const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push) { + if (use_new_codec_wrapper_) { + sendSettingsHelper(http2_options, disable_push); + } else { + sendSettingsHelperOld(http2_options, disable_push); + } const uint32_t initial_connection_window_size = http2_options.initial_connection_window_size().value(); @@ -1323,10 +1532,15 @@ void ConnectionImpl::sendSettings( if (initial_connection_window_size != NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE) { ENVOY_CONN_LOG(debug, "updating connection-level initial window size to {}", connection_, initial_connection_window_size); - int rc = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, - initial_connection_window_size - - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE); - ASSERT(rc == 0); + if (use_new_codec_wrapper_) { + adapter_->SubmitWindowUpdate(0, initial_connection_window_size - + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE); + } else { + int rc = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, + initial_connection_window_size - + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE); + ASSERT(rc == 0); + } } } @@ -1356,7 +1570,7 @@ void ConnectionImpl::onProtocolConstraintViolation() { connection_.close(Envoy::Network::ConnectionCloseType::NoFlush); } -ConnectionImpl::Http2Callbacks::Http2Callbacks() { +ConnectionImpl::Http2Callbacks::Http2Callbacks(bool use_new_codec_wrapper) { nghttp2_session_callbacks_new(&callbacks_); nghttp2_session_callbacks_set_send_callback( callbacks_, @@ -1459,13 +1673,17 @@ ConnectionImpl::Http2Callbacks::Http2Callbacks() { hd->stream_id, hd->flags == END_METADATA_FLAG); }); - nghttp2_session_callbacks_set_pack_extension_callback( - callbacks_, - [](nghttp2_session*, uint8_t* buf, size_t len, const nghttp2_frame* frame, - void* user_data) -> ssize_t { - ASSERT(frame->hd.length <= len); - return static_cast(user_data)->packMetadata(frame->hd.stream_id, buf, len); - }); + // The new codec does not use the pack_extension callback. + if (!use_new_codec_wrapper) { + nghttp2_session_callbacks_set_pack_extension_callback( + callbacks_, + [](nghttp2_session*, uint8_t* buf, size_t len, const nghttp2_frame* frame, + void* user_data) -> ssize_t { + ASSERT(frame->hd.length <= len); + return static_cast(user_data)->packMetadata(frame->hd.stream_id, buf, + len); + }); + } nghttp2_session_callbacks_set_error_callback2( callbacks_, [](nghttp2_session*, int, const char* msg, size_t len, void* user_data) -> int { @@ -1649,9 +1867,15 @@ ClientConnectionImpl::ClientConnectionImpl( callbacks_(callbacks), enable_upstream_http2_flood_checks_(Runtime::runtimeFeatureEnabled( "envoy.reloadable_features.upstream_http2_flood_checks")) { ClientHttp2Options client_http2_options(http2_options); - session_ = http2_session_factory.create(http2_callbacks_.callbacks(), base(), - client_http2_options.options()); - http2_session_factory.init(session_, base(), http2_options); + if (use_new_codec_wrapper_) { + adapter_ = http2_session_factory.create(http2_callbacks_.callbacks(), base(), + client_http2_options.options()); + http2_session_factory.init(base(), http2_options); + } else { + session_ = http2_session_factory.createOld(http2_callbacks_.callbacks(), base(), + client_http2_options.options()); + http2_session_factory.initOld(session_, base(), http2_options); + } allow_metadata_ = http2_options.allow_metadata(); idle_session_requires_ping_interval_ = std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT( http2_options.connection_keepalive(), connection_idle_interval, 0)); @@ -1756,8 +1980,14 @@ ServerConnectionImpl::ServerConnectionImpl( callbacks_(callbacks), headers_with_underscores_action_(headers_with_underscores_action) { Http2Options h2_options(http2_options); - nghttp2_session_server_new2(&session_, http2_callbacks_.callbacks(), base(), - h2_options.options()); + if (use_new_codec_wrapper_) { + visitor_ = std::make_unique( + http2::adapter::Perspective::kServer, *http2_callbacks_.callbacks(), base()); + adapter_ = http2::adapter::NgHttp2Adapter::CreateServerAdapter(*visitor_, h2_options.options()); + } else { + nghttp2_session_server_new2(&session_, http2_callbacks_.callbacks(), base(), + h2_options.options()); + } sendSettings(http2_options, false); allow_metadata_ = http2_options.allow_metadata(); } @@ -1785,8 +2015,12 @@ Status ServerConnectionImpl::onBeginHeaders(const nghttp2_frame* frame) { stream->request_decoder_ = &callbacks_.newStream(*stream); stream->stream_id_ = frame->hd.stream_id; LinkedList::moveIntoList(std::move(stream), active_streams_); - nghttp2_session_set_stream_user_data(session_, frame->hd.stream_id, - active_streams_.front().get()); + if (use_new_codec_wrapper_) { + adapter_->SetStreamUserData(frame->hd.stream_id, active_streams_.front().get()); + } else { + nghttp2_session_set_stream_user_data(session_, frame->hd.stream_id, + active_streams_.front().get()); + } protocol_constraints_.incrementOpenedStreamCount(); return okStatus(); } diff --git a/source/common/http/http2/codec_impl.h b/source/common/http/http2/codec_impl.h index a6f82e4f6d6f1..bf750fc543e5b 100644 --- a/source/common/http/http2/codec_impl.h +++ b/source/common/http/http2/codec_impl.h @@ -34,6 +34,7 @@ #include "absl/types/optional.h" #include "nghttp2/nghttp2.h" +#include "quiche/http2/adapter/http2_adapter.h" namespace Envoy { namespace Http { @@ -81,26 +82,42 @@ class Nghttp2SessionFactory { virtual ~Nghttp2SessionFactory() = default; // Returns a new nghttp2_session to be used with |connection|. - virtual nghttp2_session* create(const nghttp2_session_callbacks* callbacks, - ConnectionImplType* connection, - const nghttp2_option* options) PURE; + virtual nghttp2_session* createOld(const nghttp2_session_callbacks* callbacks, + ConnectionImplType* connection, + const nghttp2_option* options) PURE; // Initializes the |session|. - virtual void init(nghttp2_session* session, ConnectionImplType* connection, + virtual void initOld(nghttp2_session* session, ConnectionImplType* connection, + const envoy::config::core::v3::Http2ProtocolOptions& options) PURE; + + // Returns a new nghttp2_session to be used with |connection|. + virtual std::unique_ptr + create(const nghttp2_session_callbacks* callbacks, ConnectionImplType* connection, + const nghttp2_option* options) PURE; + + // Initializes the |session|. + virtual void init(ConnectionImplType* connection, const envoy::config::core::v3::Http2ProtocolOptions& options) PURE; }; class ProdNghttp2SessionFactory : public Nghttp2SessionFactory { public: - nghttp2_session* create(const nghttp2_session_callbacks* callbacks, ConnectionImpl* connection, - const nghttp2_option* options) override; + nghttp2_session* createOld(const nghttp2_session_callbacks* callbacks, ConnectionImpl* connection, + const nghttp2_option* options) override; + + void initOld(nghttp2_session* session, ConnectionImpl* connection, + const envoy::config::core::v3::Http2ProtocolOptions& options) override; + + std::unique_ptr create(const nghttp2_session_callbacks* callbacks, + ConnectionImpl* connection, + const nghttp2_option* options) override; - void init(nghttp2_session* session, ConnectionImpl* connection, + void init(ConnectionImpl* connection, const envoy::config::core::v3::Http2ProtocolOptions& options) override; - // Returns a global factory instance. Note that this is possible because no internal state is - // maintained; the thread safety of create() and init()'s side effects is guaranteed by Envoy's - // worker based threading model. + // Returns a global factory instance. Note that this is possible because no + // internal state is maintained; the thread safety of create() and init()'s + // side effects is guaranteed by Envoy's worker based threading model. static ProdNghttp2SessionFactory& get() { static ProdNghttp2SessionFactory* instance = new ProdNghttp2SessionFactory(); return *instance; @@ -128,7 +145,13 @@ class ConnectionImpl : public virtual Connection, Protocol protocol() override { return Protocol::Http2; } void shutdownNotice() override; Status protocolErrorForTest(); // Used in tests to simulate errors. - bool wantsToWrite() override { return nghttp2_session_want_write(session_); } + bool wantsToWrite() override { + if (use_new_codec_wrapper_) { + return adapter_->want_write(); + } else { + return nghttp2_session_want_write(session_); + } + } // Propagate network connection watermark events to each stream on the connection. void onUnderlyingConnectionAboveWriteBufferHighWatermark() override { for (auto& stream : active_streams_) { @@ -141,6 +164,10 @@ class ConnectionImpl : public virtual Connection, } } + void setVisitor(std::unique_ptr visitor) { + visitor_ = std::move(visitor); + } + // ScopeTrackedObject void dumpState(std::ostream& os, int indent_level) const override; @@ -152,7 +179,7 @@ class ConnectionImpl : public virtual Connection, */ class Http2Callbacks { public: - Http2Callbacks(); + explicit Http2Callbacks(bool use_new_codec_wrapper); ~Http2Callbacks(); const nghttp2_session_callbacks* callbacks() { return callbacks_; } @@ -200,11 +227,13 @@ class ConnectionImpl : public virtual Connection, void onDataSourceSend(const uint8_t* framehd, size_t length); void resetStreamWorker(StreamResetReason reason); static void buildHeaders(std::vector& final_headers, const HeaderMap& headers); + static std::vector buildHeaders(const HeaderMap& headers); void saveHeader(HeaderString&& name, HeaderString&& value); void encodeHeadersBase(const HeaderMap& headers, bool end_stream); virtual void submitHeaders(const HeaderMap& headers, nghttp2_data_provider* provider) PURE; void encodeTrailersBase(const HeaderMap& headers); void submitTrailers(const HeaderMap& trailers); + // Called iff use_new_codec_wrapper_ is false. void submitMetadata(uint8_t flags); // Returns true if the stream should defer the local reset stream until after the next call to // sendPendingFrames so pending outbound frames have one final chance to be flushed. If we @@ -272,7 +301,8 @@ class ConnectionImpl : public virtual Connection, virtual void decodeTrailers() PURE; // Get MetadataEncoder for this stream. - MetadataEncoder& getMetadataEncoder(); + MetadataEncoder& getMetadataEncoderOld(); + NewMetadataEncoder& getMetadataEncoder(); // Get MetadataDecoder for this stream. MetadataDecoder& getMetadataDecoder(); // Callback function for MetadataDecoder. @@ -300,7 +330,8 @@ class ConnectionImpl : public virtual Connection, Buffer::InstancePtr pending_send_data_; HeaderMapPtr pending_trailers_to_encode_; std::unique_ptr metadata_decoder_; - std::unique_ptr metadata_encoder_; + std::unique_ptr metadata_encoder_; + std::unique_ptr metadata_encoder_old_; absl::optional deferred_reset_; HeaderString cookies_; bool local_end_stream_sent_ : 1; @@ -461,6 +492,10 @@ class ConnectionImpl : public virtual Connection, bool sendPendingFramesAndHandleError(); void sendSettings(const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push); + void sendSettingsHelper(const envoy::config::core::v3::Http2ProtocolOptions& http2_options, + bool disable_push); + void sendSettingsHelperOld(const envoy::config::core::v3::Http2ProtocolOptions& http2_options, + bool disable_push); // Callback triggered when the peer's SETTINGS frame is received. virtual void onSettings(const nghttp2_settings& settings) { ReceivedSettingsImpl received_settings(settings); @@ -492,13 +527,23 @@ class ConnectionImpl : public virtual Connection, void scheduleProtocolConstraintViolationCallback(); void onProtocolConstraintViolation(); - static Http2Callbacks http2_callbacks_; + // Uses a new wrapper API around the underlying HTTP/2 codec. Guarded by the + // "envoy.reloadable_features.http2_new_codec_wrapper" runtime feature flag. + const bool use_new_codec_wrapper_; + // TODO(birenroy): Make this static again when removing + // use_new_codec_wrapper_. + Http2Callbacks http2_callbacks_; std::list active_streams_; // Tracks the stream id of the current stream we're processing. // This should only be set while we're in the context of dispatching to nghttp2. absl::optional current_stream_id_; + // Used iff use_new_codec_wrapper_ is false. nghttp2_session* session_{}; + // Used iff use_new_codec_wrapper_ is true. + std::unique_ptr visitor_; + std::unique_ptr adapter_; + CodecStats& stats_; Network::Connection& connection_; const uint32_t max_headers_kb_; @@ -551,6 +596,7 @@ class ConnectionImpl : public virtual Connection, int onStreamClose(int32_t stream_id, uint32_t error_code); int onMetadataReceived(int32_t stream_id, const uint8_t* data, size_t len); int onMetadataFrameComplete(int32_t stream_id, bool end_metadata); + // Called iff use_new_codec_wrapper_ is false. ssize_t packMetadata(int32_t stream_id, uint8_t* buf, size_t len); // Adds buffer fragment for a new outbound frame to the supplied Buffer::OwnedImpl. diff --git a/source/common/http/http2/metadata_decoder.h b/source/common/http/http2/metadata_decoder.h index e5f91925d5083..9971a35f8349e 100644 --- a/source/common/http/http2/metadata_decoder.h +++ b/source/common/http/http2/metadata_decoder.h @@ -52,6 +52,8 @@ class MetadataDecoder : Logger::Loggable { private: friend class MetadataEncoderDecoderTest_VerifyEncoderDecoderOnMultipleMetadataMaps_Test; friend class MetadataEncoderDecoderTest_VerifyEncoderDecoderMultipleMetadataReachSizeLimit_Test; + friend class MetadataEncoderTest_VerifyEncoderDecoderOnMultipleMetadataMaps_Test; + friend class MetadataEncoderTest_VerifyEncoderDecoderMultipleMetadataReachSizeLimit_Test; /** * Decodes METADATA payload using nghttp2. * @param end_metadata indicates is END_METADATA is true. diff --git a/source/common/http/http2/metadata_encoder.cc b/source/common/http/http2/metadata_encoder.cc index 81002de8566d9..544e85600dbfc 100644 --- a/source/common/http/http2/metadata_encoder.cc +++ b/source/common/http/http2/metadata_encoder.cc @@ -125,6 +125,59 @@ std::vector MetadataEncoder::payloadFrameFlagBytes() { return flags; } +class BufferMetadataSource : public http2::adapter::MetadataSource { +public: + explicit BufferMetadataSource(Buffer::OwnedImpl payload) + : payload_(std::move(payload)), original_payload_length_(payload_.length()) {} + + size_t NumFrames(size_t max_frame_size) const override { + // Rounds up, so a trailing partial frame is counted. + return (original_payload_length_ + max_frame_size - 1) / max_frame_size; + } + + std::pair Pack(uint8_t* dest, size_t dest_len) override { + const size_t to_copy = std::min(dest_len, static_cast(payload_.length())); + payload_.copyOut(0, to_copy, dest); + payload_.drain(to_copy); + return std::make_pair(static_cast(to_copy), payload_.length() == 0); + } + +private: + Buffer::OwnedImpl payload_; + const size_t original_payload_length_; +}; + +NewMetadataEncoder::NewMetadataEncoder() { + deflater_.SetIndexingPolicy([](absl::string_view, absl::string_view) { return false; }); +} + +std::vector> +NewMetadataEncoder::createSources(const MetadataMapVector& metadata_map_vector) { + MetadataSourceVector v; + v.reserve(metadata_map_vector.size()); + for (const auto& metadata_map : metadata_map_vector) { + v.push_back(createSource(*metadata_map)); + } + return v; +} + +std::unique_ptr +NewMetadataEncoder::createSource(const MetadataMap& metadata_map) { + static const size_t kMaxEncodingChunkSize = 64 * 1024; + spdy::HpackEncoder::Representations r; + r.reserve(metadata_map.size()); + for (const auto& header : metadata_map) { + r.push_back({header.first, header.second}); + } + ASSERT(r.size() == metadata_map.size()); + Buffer::OwnedImpl payload; + auto progressive_encoder = deflater_.EncodeRepresentations(r); + while (progressive_encoder->HasNext()) { + payload.add(progressive_encoder->Next(kMaxEncodingChunkSize)); + } + return std::make_unique(std::move(payload)); +} + } // namespace Http2 } // namespace Http } // namespace Envoy diff --git a/source/common/http/http2/metadata_encoder.h b/source/common/http/http2/metadata_encoder.h index 9fe06ce6e459b..1f1295677448e 100644 --- a/source/common/http/http2/metadata_encoder.h +++ b/source/common/http/http2/metadata_encoder.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "envoy/http/codec.h" @@ -11,6 +12,8 @@ #include "source/common/common/logger.h" #include "nghttp2/nghttp2.h" +#include "quiche/http2/adapter/data_source.h" +#include "quiche/spdy/core/hpack/hpack_encoder.h" namespace Envoy { namespace Http { @@ -90,6 +93,31 @@ class MetadataEncoder : Logger::Loggable { std::deque payload_size_queue_; }; +/** + * A class that creates and sends METADATA payloads. A METADATA payload is a + * group of string key value pairs encoded in HTTP/2 header blocks. METADATA + * frames are constructed in two steps: first, the stream submits the + * MetadataMapVector to the encoder, and later, the MetadataSources generate + * frame payloads for transmission on the wire. + */ +class NewMetadataEncoder : Logger::Loggable { +public: + using MetadataSourceVector = std::vector>; + NewMetadataEncoder(); + + /** + * Creates wire format HTTP/2 header block from a vector of metadata maps. + * @param metadata_map_vector supplies the metadata map vector to encode. + * @return whether encoding is successful. + */ + MetadataSourceVector createSources(const MetadataMapVector& metadata_map_vector); + +private: + std::unique_ptr createSource(const MetadataMap& metadata_map); + + spdy::HpackEncoder deflater_; +}; + } // namespace Http2 } // namespace Http } // namespace Envoy diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index b62c35bfc2496..ca17ceb29c271 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -116,6 +116,8 @@ constexpr const char* disabled_runtime_features[] = { "envoy.reloadable_features.enable_grpc_async_client_cache", // TODO(dmitri-d) reset to true to enable unified mux by default "envoy.reloadable_features.unified_mux", + // TODO(birenroy): flip to true in a future PR to enable by default + "envoy.reloadable_features.http2_new_codec_wrapper", }; RuntimeFeatures::RuntimeFeatures() { diff --git a/test/common/http/http2/BUILD b/test/common/http/http2/BUILD index 075f7607ea42d..b9c22bdacdf01 100644 --- a/test/common/http/http2/BUILD +++ b/test/common/http/http2/BUILD @@ -14,6 +14,9 @@ envoy_package() envoy_cc_test( name = "codec_impl_test", srcs = ["codec_impl_test.cc"], + external_deps = [ + "quiche_http2_adapter", + ], shard_count = 5, deps = [ ":codec_impl_test_util", @@ -45,7 +48,10 @@ envoy_cc_test( envoy_cc_test_library( name = "codec_impl_test_util", hdrs = ["codec_impl_test_util.h"], - external_deps = ["abseil_optional"], + external_deps = [ + "abseil_optional", + "quiche_http2_adapter", + ], deps = [ "//source/common/http/http2:codec_lib", "//test/mocks:common_lib", @@ -140,6 +146,25 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "metadata_encoder_test", + srcs = ["metadata_encoder_test.cc"], + external_deps = [ + "nghttp2", + "quiche_http2_adapter", + ], + deps = [ + "//source/common/buffer:buffer_lib", + "//source/common/common:random_generator_lib", + "//source/common/http/http2:metadata_decoder_lib", + "//source/common/http/http2:metadata_encoder_lib", + "//source/common/runtime:runtime_lib", + "//test/common/http/http2:http2_frame", + "//test/test_common:logging_lib", + "//test/test_common:utility_lib", + ], +) + envoy_cc_test( name = "http2_frame_test", srcs = ["http2_frame_test.cc"], diff --git a/test/common/http/http2/codec_impl_test.cc b/test/common/http/http2/codec_impl_test.cc index 47ca8418fc97c..9855f487fefe3 100644 --- a/test/common/http/http2/codec_impl_test.cc +++ b/test/common/http/http2/codec_impl_test.cc @@ -29,6 +29,8 @@ #include "codec_impl_test_util.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "quiche/http2/adapter/callback_visitor.h" +#include "quiche/http2/adapter/nghttp2_adapter.h" using testing::_; using testing::AnyNumber; @@ -47,7 +49,7 @@ namespace Http { namespace Http2 { using Http2SettingsTuple = ::testing::tuple; -using Http2SettingsTestParam = ::testing::tuple; +using Http2SettingsTestParam = ::testing::tuple; namespace CommonUtility = ::Envoy::Http2::Utility; class Http2CodecImplTestFixture { @@ -107,8 +109,10 @@ class Http2CodecImplTestFixture { }; Http2CodecImplTestFixture() = default; - Http2CodecImplTestFixture(Http2SettingsTuple client_settings, Http2SettingsTuple server_settings) - : client_settings_(client_settings), server_settings_(server_settings) { + Http2CodecImplTestFixture(Http2SettingsTuple client_settings, Http2SettingsTuple server_settings, + bool enable_new_codec_wrapper) + : client_settings_(client_settings), server_settings_(server_settings), + enable_new_codec_wrapper_(enable_new_codec_wrapper) { // Make sure we explicitly test for stream flush timer creation. EXPECT_CALL(client_connection_.dispatcher_, createTimer_(_)).Times(0); EXPECT_CALL(server_connection_.dispatcher_, createTimer_(_)).Times(0); @@ -131,6 +135,9 @@ class Http2CodecImplTestFixture { } virtual void initialize() { + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.reloadable_features.http2_new_codec_wrapper", + enable_new_codec_wrapper_ ? "true" : "false"}}); http2OptionsFromTuple(client_http2_options_, client_settings_); http2OptionsFromTuple(server_http2_options_, server_settings_); client_ = std::make_unique( @@ -219,8 +226,89 @@ class Http2CodecImplTestFixture { EXPECT_EQ(details, response_encoder_->getStream().responseDetails()); } + template + uint32_t getStreamReceiveWindowLimit(std::unique_ptr& connection, int32_t stream_id) { + if (enable_new_codec_wrapper_) { + return connection->adapter()->GetStreamReceiveWindowLimit(stream_id); + } else { + return nghttp2_session_get_stream_effective_local_window_size(connection->session(), + stream_id); + } + } + + template + uint32_t getStreamReceiveWindowSize(std::unique_ptr& connection, int32_t stream_id) { + if (enable_new_codec_wrapper_) { + return connection->adapter()->GetStreamReceiveWindowSize(stream_id); + } else { + return nghttp2_session_get_stream_local_window_size(connection->session(), stream_id); + } + } + + template + uint32_t getStreamSendWindowSize(std::unique_ptr& connection, int32_t stream_id) { + if (enable_new_codec_wrapper_) { + return connection->adapter()->GetStreamSendWindowSize(stream_id); + } else { + return nghttp2_session_get_stream_remote_window_size(connection->session(), stream_id); + } + } + + template uint32_t getSendWindowSize(std::unique_ptr& connection) { + if (enable_new_codec_wrapper_) { + return connection->adapter()->GetSendWindowSize(); + } else { + return nghttp2_session_get_remote_window_size(connection->session()); + } + } + + template + void submitSettings(std::unique_ptr& connection, + const std::list>& settings_values) { + if (enable_new_codec_wrapper_) { + std::vector settings; + for (const auto& setting_pair : settings_values) { + settings.push_back({setting_pair.first, setting_pair.second}); + } + connection->adapter()->SubmitSettings(settings); + } else { + std::vector settings; + for (const auto& setting_pair : settings_values) { + settings.push_back({static_cast(setting_pair.first), setting_pair.second}); + } + EXPECT_EQ(0, nghttp2_submit_settings(connection->session(), NGHTTP2_FLAG_NONE, + settings.data(), settings.size())); + } + } + + template int getHpackEncoderDynamicTableSize(std::unique_ptr& connection) { + if (enable_new_codec_wrapper_) { + return connection->adapter()->GetHpackEncoderDynamicTableSize(); + } else { + return nghttp2_session_get_hd_deflate_dynamic_table_size(connection->session()); + } + } + + template int getHpackDecoderDynamicTableSize(std::unique_ptr& connection) { + if (enable_new_codec_wrapper_) { + return connection->adapter()->GetHpackDecoderDynamicTableSize(); + } else { + return nghttp2_session_get_hd_inflate_dynamic_table_size(connection->session()); + } + } + + template void submitPing(std::unique_ptr& connection, uint32_t ping_id) { + if (enable_new_codec_wrapper_) { + connection->adapter()->SubmitPing(ping_id); + } else { + EXPECT_EQ(0, nghttp2_submit_ping(connection->session(), NGHTTP2_FLAG_NONE, nullptr)); + } + } + + TestScopedRuntime scoped_runtime_; absl::optional client_settings_; absl::optional server_settings_; + bool enable_new_codec_wrapper_ = false; bool allow_metadata_ = false; bool stream_error_on_invalid_http_messaging_ = false; Stats::TestUtil::TestStore client_stats_store_; @@ -264,7 +352,8 @@ class Http2CodecImplTest : public ::testing::TestWithParam(GetParam()), ::testing::get<1>(GetParam())) {} + : Http2CodecImplTestFixture(::testing::get<0>(GetParam()), ::testing::get<1>(GetParam()), + ::testing::get<2>(GetParam())) {} protected: void priorityFlood() { @@ -280,7 +369,11 @@ class Http2CodecImplTest : public ::testing::TestWithParamsession(), NGHTTP2_FLAG_NONE, 1, &spec)); + if (enable_new_codec_wrapper_) { + client_->adapter()->SubmitPriorityForStream(1, 0, 10, false); + } else { + EXPECT_EQ(0, nghttp2_submit_priority(client_->session(), NGHTTP2_FLAG_NONE, 1, &spec)); + } } } @@ -307,7 +400,11 @@ class Http2CodecImplTest : public ::testing::TestWithParamsession(), NGHTTP2_FLAG_NONE, 1, 1)); + if (enable_new_codec_wrapper_) { + client_->adapter()->SubmitWindowUpdate(1, 1); + } else { + EXPECT_EQ(0, nghttp2_submit_window_update(client_->session(), NGHTTP2_FLAG_NONE, 1, 1)); + } } } @@ -697,8 +794,6 @@ TEST_P(Http2CodecImplTest, TrailingHeaders) { // When having empty trailers, codec submits empty buffer and end_stream instead. TEST_P(Http2CodecImplTest, IgnoreTrailingEmptyHeaders) { - TestScopedRuntime scoped_runtime; - initialize(); Buffer::OwnedImpl empty_buffer; @@ -1349,8 +1444,7 @@ TEST_P(Http2CodecImplFlowControlTest, TestFlowControlInPendingSendData) { EXPECT_EQ(1, TestUtility::findGauge(client_stats_store_, "http2.streams_active")->value()); EXPECT_EQ(1, TestUtility::findGauge(server_stats_store_, "http2.streams_active")->value()); - uint32_t initial_stream_window = - nghttp2_session_get_stream_effective_local_window_size(client_->session(), 1); + uint32_t initial_stream_window = getStreamReceiveWindowLimit(client_, 1); // If this limit is changed, this test will fail due to the initial large writes being divided // into more than 4 frames. Fast fail here with this explanatory comment. ASSERT_EQ(65535, initial_stream_window); @@ -1365,8 +1459,8 @@ TEST_P(Http2CodecImplFlowControlTest, TestFlowControlInPendingSendData) { // Verify that the window is full. The client will not send more data to the server for this // stream. - EXPECT_EQ(0, nghttp2_session_get_stream_local_window_size(server_->session(), 1)); - EXPECT_EQ(0, nghttp2_session_get_stream_remote_window_size(client_->session(), 1)); + EXPECT_EQ(0, getStreamReceiveWindowSize(server_, 1)); + EXPECT_EQ(0, getStreamSendWindowSize(client_, 1)); EXPECT_EQ(initial_stream_window, server_->getStream(1)->unconsumed_bytes_); // Now that the flow control window is full, further data causes the send buffer to back up. @@ -1430,10 +1524,8 @@ TEST_P(Http2CodecImplFlowControlTest, TestFlowControlInPendingSendData) { EXPECT_EQ(0, TestUtility::findGauge(client_stats_store_, "http2.pending_send_bytes")->value()); // The extra 1 byte sent won't trigger another window update, so the final window should be the // initial window minus the last 1 byte flush from the client to server. - EXPECT_EQ(initial_stream_window - 1, - nghttp2_session_get_stream_local_window_size(server_->session(), 1)); - EXPECT_EQ(initial_stream_window - 1, - nghttp2_session_get_stream_remote_window_size(client_->session(), 1)); + EXPECT_EQ(initial_stream_window - 1, getStreamReceiveWindowSize(server_, 1)); + EXPECT_EQ(initial_stream_window - 1, getStreamSendWindowSize(client_, 1)); } // Set up the same asTestFlowControlInPendingSendData, but tears the stream down with an early reset @@ -1454,9 +1546,8 @@ TEST_P(Http2CodecImplFlowControlTest, EarlyResetRestoresWindow) { // updates to the client. server_->getStream(1)->readDisable(true); - uint32_t initial_stream_window = - nghttp2_session_get_stream_effective_local_window_size(client_->session(), 1); - uint32_t initial_connection_window = nghttp2_session_get_remote_window_size(client_->session()); + uint32_t initial_stream_window = getStreamReceiveWindowLimit(client_, 1); + uint32_t initial_connection_window = getSendWindowSize(client_); // If this limit is changed, this test will fail due to the initial large writes being divided // into more than 4 frames. Fast fail here with this explanatory comment. ASSERT_EQ(65535, initial_stream_window); @@ -1469,10 +1560,10 @@ TEST_P(Http2CodecImplFlowControlTest, EarlyResetRestoresWindow) { // Verify that the window is full. The client will not send more data to the server for this // stream. - EXPECT_EQ(0, nghttp2_session_get_stream_local_window_size(server_->session(), 1)); - EXPECT_EQ(0, nghttp2_session_get_stream_remote_window_size(client_->session(), 1)); + EXPECT_EQ(0, getStreamReceiveWindowSize(server_, 1)); + EXPECT_EQ(0, getStreamSendWindowSize(client_, 1)); EXPECT_EQ(initial_stream_window, server_->getStream(1)->unconsumed_bytes_); - EXPECT_GT(initial_connection_window, nghttp2_session_get_remote_window_size(client_->session())); + EXPECT_GT(initial_connection_window, getSendWindowSize(client_)); EXPECT_CALL(server_stream_callbacks_, onResetStream(StreamResetReason::LocalRefusedStreamReset, _)); @@ -1494,7 +1585,7 @@ TEST_P(Http2CodecImplFlowControlTest, EarlyResetRestoresWindow) { response_encoder_->getStream().resetStream(StreamResetReason::LocalRefusedStreamReset); // Regression test that the window is consumed even if the stream is destroyed early. - EXPECT_EQ(initial_connection_window, nghttp2_session_get_remote_window_size(client_->session())); + EXPECT_EQ(initial_connection_window, getSendWindowSize(client_)); } // Test the HTTP2 pending_recv_data_ buffer going over and under watermark limits. @@ -1678,15 +1769,11 @@ TEST_P(Http2CodecImplFlowControlTest, WindowUpdateOnReadResumingFlood) { buffer.move(frame); })); - auto* violation_callback = - new NiceMock(&server_connection_.dispatcher_); - // Force the server stream to be read disabled. This will cause it to stop sending window // updates to the client. server_->getStream(1)->readDisable(true); - uint32_t initial_stream_window = - nghttp2_session_get_stream_effective_local_window_size(client_->session(), 1); + uint32_t initial_stream_window = getStreamReceiveWindowLimit(client_, 1); // If this limit is changed, this test will fail due to the initial large writes being divided // into more than 4 frames. Fast fail here with this explanatory comment. ASSERT_EQ(65535, initial_stream_window); @@ -1694,6 +1781,9 @@ TEST_P(Http2CodecImplFlowControlTest, WindowUpdateOnReadResumingFlood) { EXPECT_EQ(initial_stream_window, server_->getStream(1)->bufferLimit()); EXPECT_EQ(initial_stream_window, client_->getStream(1)->bufferLimit()); + auto* violation_callback = + new NiceMock(&server_connection_.dispatcher_); + // One large write gets broken into smaller frames. EXPECT_CALL(request_decoder_, decodeData(_, false)).Times(AnyNumber()); Buffer::OwnedImpl long_data(std::string(initial_stream_window / 2, 'a')); @@ -1854,38 +1944,17 @@ TEST_P(Http2CodecImplStreamLimitTest, MaxClientStreams) { } TEST_P(Http2CodecImplStreamLimitTest, LazyDecreaseMaxConcurrentStreamsConsumeError) { - TestScopedRuntime scoped_runtime; + initialize(); Runtime::LoaderSingleton::getExisting()->mergeValues( {{"envoy.reloadable_features.http2_consume_stream_refused_errors", "true"}}); - http2OptionsFromTuple(client_http2_options_, ::testing::get<0>(GetParam())); - http2OptionsFromTuple(server_http2_options_, ::testing::get<1>(GetParam())); - client_ = std::make_unique( - client_connection_, client_callbacks_, client_stats_store_, client_http2_options_, random_, - max_request_headers_kb_, max_response_headers_count_, ProdNghttp2SessionFactory::get()); - server_ = std::make_unique( - server_connection_, server_callbacks_, server_stats_store_, server_http2_options_, random_, - max_request_headers_kb_, max_request_headers_count_, headers_with_underscores_action_); - - request_encoder_ = &client_->newStream(response_decoder_); - setupDefaultConnectionMocks(); - EXPECT_CALL(server_callbacks_, newStream(_, _)) - .WillOnce(Invoke([&](ResponseEncoder& encoder, bool) -> RequestDecoder& { - response_encoder_ = &encoder; - encoder.getStream().addCallbacks(server_stream_callbacks_); - return request_decoder_; - })); - TestRequestHeaderMapImpl request_headers; HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); // This causes the next stream creation to fail with a "invalid frame: Stream was refused" error. - absl::InlinedVector settings{ - {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 1}}; - EXPECT_EQ(0, nghttp2_submit_settings(server_->session(), NGHTTP2_FLAG_NONE, settings.data(), - settings.size())); + submitSettings(server_, {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 1}}); request_encoder_ = &client_->newStream(response_decoder_); setupDefaultConnectionMocks(); @@ -1900,38 +1969,17 @@ TEST_P(Http2CodecImplStreamLimitTest, LazyDecreaseMaxConcurrentStreamsConsumeErr } TEST_P(Http2CodecImplStreamLimitTest, LazyDecreaseMaxConcurrentStreamsIgnoreError) { - TestScopedRuntime scoped_runtime; + initialize(); Runtime::LoaderSingleton::getExisting()->mergeValues( {{"envoy.reloadable_features.http2_consume_stream_refused_errors", "false"}}); - http2OptionsFromTuple(client_http2_options_, ::testing::get<0>(GetParam())); - http2OptionsFromTuple(server_http2_options_, ::testing::get<1>(GetParam())); - client_ = std::make_unique( - client_connection_, client_callbacks_, client_stats_store_, client_http2_options_, random_, - max_request_headers_kb_, max_response_headers_count_, ProdNghttp2SessionFactory::get()); - server_ = std::make_unique( - server_connection_, server_callbacks_, server_stats_store_, server_http2_options_, random_, - max_request_headers_kb_, max_request_headers_count_, headers_with_underscores_action_); - - request_encoder_ = &client_->newStream(response_decoder_); - setupDefaultConnectionMocks(); - EXPECT_CALL(server_callbacks_, newStream(_, _)) - .WillOnce(Invoke([&](ResponseEncoder& encoder, bool) -> RequestDecoder& { - response_encoder_ = &encoder; - encoder.getStream().addCallbacks(server_stream_callbacks_); - return request_decoder_; - })); - TestRequestHeaderMapImpl request_headers; HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); // This causes the next stream creation to fail with a "invalid frame: Stream was refused" error. - absl::InlinedVector settings{ - {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 1}}; - EXPECT_EQ(0, nghttp2_submit_settings(server_->session(), NGHTTP2_FLAG_NONE, settings.data(), - settings.size())); + submitSettings(server_, {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 1}}); request_encoder_ = &client_->newStream(response_decoder_); setupDefaultConnectionMocks(); @@ -1960,12 +2008,12 @@ TEST_P(Http2CodecImplStreamLimitTest, LazyDecreaseMaxConcurrentStreamsIgnoreErro // Deferred reset tests use only small windows so that we can test certain conditions. INSTANTIATE_TEST_SUITE_P(Http2CodecImplDeferredResetTest, Http2CodecImplDeferredResetTest, ::testing::Combine(HTTP2SETTINGS_SMALL_WINDOW_COMBINE, - HTTP2SETTINGS_SMALL_WINDOW_COMBINE)); + HTTP2SETTINGS_SMALL_WINDOW_COMBINE, ::testing::Bool())); // Flow control tests only use only small windows so that we can test certain conditions. INSTANTIATE_TEST_SUITE_P(Http2CodecImplFlowControlTest, Http2CodecImplFlowControlTest, ::testing::Combine(HTTP2SETTINGS_SMALL_WINDOW_COMBINE, - HTTP2SETTINGS_SMALL_WINDOW_COMBINE)); + HTTP2SETTINGS_SMALL_WINDOW_COMBINE, ::testing::Bool())); // we separate default/edge cases here to avoid combinatorial explosion #define HTTP2SETTINGS_DEFAULT_COMBINE \ @@ -1979,11 +2027,11 @@ INSTANTIATE_TEST_SUITE_P(Http2CodecImplFlowControlTest, Http2CodecImplFlowContro // edge settings allow for the number of streams needed by the test. INSTANTIATE_TEST_SUITE_P(Http2CodecImplStreamLimitTest, Http2CodecImplStreamLimitTest, ::testing::Combine(HTTP2SETTINGS_DEFAULT_COMBINE, - HTTP2SETTINGS_DEFAULT_COMBINE)); + HTTP2SETTINGS_DEFAULT_COMBINE, ::testing::Bool())); INSTANTIATE_TEST_SUITE_P(Http2CodecImplTestDefaultSettings, Http2CodecImplTest, ::testing::Combine(HTTP2SETTINGS_DEFAULT_COMBINE, - HTTP2SETTINGS_DEFAULT_COMBINE)); + HTTP2SETTINGS_DEFAULT_COMBINE, ::testing::Bool())); #define HTTP2SETTINGS_EDGE_COMBINE \ ::testing::Combine( \ @@ -2003,10 +2051,10 @@ using Http2CodecImplTestAll = Http2CodecImplTest; INSTANTIATE_TEST_SUITE_P(Http2CodecImplTestDefaultSettings, Http2CodecImplTestAll, ::testing::Combine(HTTP2SETTINGS_DEFAULT_COMBINE, - HTTP2SETTINGS_DEFAULT_COMBINE)); + HTTP2SETTINGS_DEFAULT_COMBINE, ::testing::Bool())); INSTANTIATE_TEST_SUITE_P(Http2CodecImplTestEdgeSettings, Http2CodecImplTestAll, - ::testing::Combine(HTTP2SETTINGS_EDGE_COMBINE, - HTTP2SETTINGS_EDGE_COMBINE)); + ::testing::Combine(HTTP2SETTINGS_EDGE_COMBINE, HTTP2SETTINGS_EDGE_COMBINE, + ::testing::Bool())); TEST(Http2CodecUtility, reconstituteCrumbledCookies) { { @@ -2059,8 +2107,9 @@ class Http2CustomSettingsTestBase : public Http2CodecImplTestFixture { }; Http2CustomSettingsTestBase(Http2SettingsTuple client_settings, - Http2SettingsTuple server_settings, bool validate_client) - : Http2CodecImplTestFixture(client_settings, server_settings), + Http2SettingsTuple server_settings, bool use_new_codec_wrapper, + bool validate_client) + : Http2CodecImplTestFixture(client_settings, server_settings, use_new_codec_wrapper), validate_client_(validate_client) {} ~Http2CustomSettingsTestBase() override = default; @@ -2104,15 +2153,16 @@ class Http2CustomSettingsTestBase : public Http2CodecImplTestFixture { class Http2CustomSettingsTest : public Http2CustomSettingsTestBase, public ::testing::TestWithParam< - ::testing::tuple> { + ::testing::tuple> { public: Http2CustomSettingsTest() : Http2CustomSettingsTestBase(::testing::get<0>(GetParam()), ::testing::get<1>(GetParam()), - ::testing::get<2>(GetParam())) {} + ::testing::get<2>(GetParam()), ::testing::get<3>(GetParam())) {} }; INSTANTIATE_TEST_SUITE_P(Http2CodecImplTestEdgeSettings, Http2CustomSettingsTest, ::testing::Combine(HTTP2SETTINGS_DEFAULT_COMBINE, - HTTP2SETTINGS_DEFAULT_COMBINE, ::testing::Bool())); + HTTP2SETTINGS_DEFAULT_COMBINE, ::testing::Bool(), + ::testing::Bool())); // Validates that custom parameters (those which are not explicitly named in the // envoy::config::core::v3::Http2ProtocolOptions proto) are properly sent and processed by @@ -2398,20 +2448,18 @@ TEST_P(Http2CodecImplTestAll, TestCodecHeaderCompression) { response_encoder_->encodeHeaders(response_headers, true); // Sanity check to verify that state of encoders and decoders matches. - EXPECT_EQ(nghttp2_session_get_hd_deflate_dynamic_table_size(server_->session()), - nghttp2_session_get_hd_inflate_dynamic_table_size(client_->session())); - EXPECT_EQ(nghttp2_session_get_hd_deflate_dynamic_table_size(client_->session()), - nghttp2_session_get_hd_inflate_dynamic_table_size(server_->session())); + EXPECT_EQ(getHpackEncoderDynamicTableSize(server_), getHpackDecoderDynamicTableSize(client_)); + EXPECT_EQ(getHpackEncoderDynamicTableSize(client_), getHpackDecoderDynamicTableSize(server_)); // Verify that headers are compressed only when both client and server advertise table size // > 0: if (client_http2_options_.hpack_table_size().value() && server_http2_options_.hpack_table_size().value()) { - EXPECT_NE(0, nghttp2_session_get_hd_deflate_dynamic_table_size(client_->session())); - EXPECT_NE(0, nghttp2_session_get_hd_deflate_dynamic_table_size(server_->session())); + EXPECT_NE(0, getHpackEncoderDynamicTableSize(client_)); + EXPECT_NE(0, getHpackEncoderDynamicTableSize(server_)); } else { - EXPECT_EQ(0, nghttp2_session_get_hd_deflate_dynamic_table_size(client_->session())); - EXPECT_EQ(0, nghttp2_session_get_hd_deflate_dynamic_table_size(server_->session())); + EXPECT_EQ(0, getHpackEncoderDynamicTableSize(client_)); + EXPECT_EQ(0, getHpackEncoderDynamicTableSize(server_)); } } @@ -2427,7 +2475,7 @@ TEST_P(Http2CodecImplTest, PingFlood) { // Send one frame above the outbound control queue size limit for (uint32_t i = 0; i < CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_CONTROL_FRAMES + 1; ++i) { - EXPECT_EQ(0, nghttp2_submit_ping(client_->session(), NGHTTP2_FLAG_NONE, nullptr)); + submitPing(client_, i); } int ack_count = 0; @@ -2459,7 +2507,7 @@ TEST_P(Http2CodecImplTest, PingFloodMitigationDisabled) { // Send one frame above the outbound control queue size limit for (uint32_t i = 0; i < CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_CONTROL_FRAMES + 1; ++i) { - EXPECT_EQ(0, nghttp2_submit_ping(client_->session(), NGHTTP2_FLAG_NONE, nullptr)); + submitPing(client_, i); } EXPECT_CALL(server_connection_, write(_, _)) @@ -2482,7 +2530,7 @@ TEST_P(Http2CodecImplTest, PingFloodCounterReset) { EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); for (int i = 0; i < kMaxOutboundControlFrames; ++i) { - EXPECT_EQ(0, nghttp2_submit_ping(client_->session(), NGHTTP2_FLAG_NONE, nullptr)); + submitPing(client_, i); } int ack_count = 0; @@ -2502,7 +2550,7 @@ TEST_P(Http2CodecImplTest, PingFloodCounterReset) { // Send floor(kMaxOutboundFrames / 2) more pings. for (int i = 0; i < kMaxOutboundControlFrames / 2; ++i) { - EXPECT_EQ(0, nghttp2_submit_ping(client_->session(), NGHTTP2_FLAG_NONE, nullptr)); + submitPing(client_, i); } // The number of outbound frames should be half of max so the connection should not be // terminated. @@ -2510,7 +2558,7 @@ TEST_P(Http2CodecImplTest, PingFloodCounterReset) { EXPECT_EQ(ack_count, kMaxOutboundControlFrames + kMaxOutboundControlFrames / 2); // 1 more ping frame should overflow the outbound frame limit. - EXPECT_EQ(0, nghttp2_submit_ping(client_->session(), NGHTTP2_FLAG_NONE, nullptr)); + submitPing(client_, 0); client_->sendPendingFrames().IgnoreError(); // The server codec should fail when it gets 1 PING too many. EXPECT_FALSE(server_wrapper_.status_.ok()); @@ -2611,7 +2659,7 @@ TEST_P(Http2CodecImplTest, ResponseDataFloodMitigationDisabled) { } // Presently flood mitigation is done only when processing downstream data // So we need to send stream from downstream client to trigger mitigation - EXPECT_EQ(0, nghttp2_submit_ping(client_->session(), NGHTTP2_FLAG_NONE, nullptr)); + submitPing(client_, 0); EXPECT_NO_THROW(client_->sendPendingFrames().IgnoreError()); } @@ -2684,7 +2732,7 @@ TEST_P(Http2CodecImplTest, PingStacksWithDataFlood) { EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } // Send one PING frame above the outbound queue size limit - EXPECT_EQ(0, nghttp2_submit_ping(client_->session(), NGHTTP2_FLAG_NONE, nullptr)); + submitPing(client_, 0); client_->sendPendingFrames().IgnoreError(); // The server codec should fail when it gets 1 frame too many. EXPECT_FALSE(server_wrapper_.status_.ok()); @@ -3053,23 +3101,33 @@ class MetadataTestClientConnectionImpl : public TestClientConnectionImpl { // Overrides TestClientConnectionImpl::submitMetadata(). bool submitMetadata(const MetadataMapVector& metadata_map_vector, int32_t stream_id) override { // Creates metadata payload. - encoder_.createPayload(metadata_map_vector); - for (uint8_t flags : encoder_.payloadFrameFlagBytes()) { - int result = nghttp2_submit_extension(session(), ::Envoy::Http::METADATA_FRAME_TYPE, flags, - stream_id, nullptr); - if (result != 0) { - return false; + if (use_new_codec_wrapper_) { + auto sources = encoder_.createSources(metadata_map_vector); + for (auto& source : sources) { + adapter()->SubmitMetadata(stream_id, 16 * 1024, std::move(source)); } + int result = adapter()->Send(); + return result == 0; + } else { + encoder_old_.createPayload(metadata_map_vector); + for (uint8_t flags : encoder_old_.payloadFrameFlagBytes()) { + int result = nghttp2_submit_extension(session(), ::Envoy::Http::METADATA_FRAME_TYPE, flags, + stream_id, nullptr); + if (result != 0) { + return false; + } + } + // Triggers nghttp2 to populate the payloads of the METADATA frames. + int result = nghttp2_session_send(session()); + return result == 0; } - // Triggers nghttp2 to populate the payloads of the METADATA frames. - int result = nghttp2_session_send(session()); - return result == 0; } protected: friend class TestNghttp2SessionFactory; - MetadataEncoder encoder_; + MetadataEncoder encoder_old_; + NewMetadataEncoder encoder_; }; class TestNghttp2SessionFactory : public Nghttp2SessionFactory { @@ -3079,8 +3137,8 @@ class TestNghttp2SessionFactory : public Nghttp2SessionFactory { nghttp2_option_del(options_); } - nghttp2_session* create(const nghttp2_session_callbacks*, ConnectionImpl* connection, - const nghttp2_option*) override { + nghttp2_session* createOld(const nghttp2_session_callbacks*, ConnectionImpl* connection, + const nghttp2_option*) override { // Only need to provide callbacks required to send METADATA frames. nghttp2_session_callbacks_new(&callbacks_); nghttp2_session_callbacks_set_pack_extension_callback( @@ -3090,7 +3148,7 @@ class TestNghttp2SessionFactory : public Nghttp2SessionFactory { // Double cast required due to multiple inheritance. return static_cast( static_cast(user_data)) - ->encoder_.packNextFramePayload(data, length); + ->encoder_old_.packNextFramePayload(data, length); }); nghttp2_session_callbacks_set_send_callback( callbacks_, @@ -3107,8 +3165,34 @@ class TestNghttp2SessionFactory : public Nghttp2SessionFactory { return session; } - void init(nghttp2_session*, ConnectionImpl*, - const envoy::config::core::v3::Http2ProtocolOptions&) override {} + void initOld(nghttp2_session*, ConnectionImpl*, + const envoy::config::core::v3::Http2ProtocolOptions&) override {} + + std::unique_ptr create(const nghttp2_session_callbacks*, + ConnectionImpl* connection, + const nghttp2_option*) override { + // Only need to provide callbacks required to send METADATA frames. The new codec wrapper + // requires the send callback, but not the pack_extension callback. + nghttp2_session_callbacks_new(&callbacks_); + nghttp2_session_callbacks_set_send_callback( + callbacks_, + [](nghttp2_session*, const uint8_t* data, size_t length, int, void* user_data) -> ssize_t { + // Cast down to MetadataTestClientConnectionImpl to leverage friendship. + return static_cast( + static_cast(user_data)) + ->onSend(data, length); + }); + nghttp2_option_new(&options_); + nghttp2_option_set_user_recv_extension_type(options_, METADATA_FRAME_TYPE); + + auto visitor = std::make_unique( + http2::adapter::Perspective::kClient, *callbacks_, connection); + http2::adapter::Http2VisitorInterface& v = *visitor; + connection->setVisitor(std::move(visitor)); + return http2::adapter::NgHttp2Adapter::CreateClientAdapter(v, options_); + } + + void init(ConnectionImpl*, const envoy::config::core::v3::Http2ProtocolOptions&) override {} private: nghttp2_session_callbacks* callbacks_; @@ -3121,6 +3205,9 @@ class Http2CodecMetadataTest : public Http2CodecImplTestFixture, public ::testin protected: void initialize() override { + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.reloadable_features.http2_new_codec_wrapper", + enable_new_codec_wrapper_ ? "true" : "false"}}); allow_metadata_ = true; http2OptionsFromTuple(client_http2_options_, client_settings_); http2OptionsFromTuple(server_http2_options_, server_settings_); @@ -3154,7 +3241,7 @@ TEST_F(Http2CodecMetadataTest, UnknownStreamId) { MetadataMapVector metadata_vector; metadata_vector.emplace_back(std::make_unique(metadata_map)); // SETTINGS are required as part of the preface. - ASSERT_EQ(nghttp2_submit_settings(client_->session(), NGHTTP2_FLAG_NONE, nullptr, 0), 0); + submitSettings(client_, {}); // Validate both the ID = 0 special case and a non-zero ID not already bound to a stream (any ID > // 0 for this test). EXPECT_TRUE(client_->submitMetadata(metadata_vector, 0)); diff --git a/test/common/http/http2/codec_impl_test_util.h b/test/common/http/http2/codec_impl_test_util.h index 2188b011aa8a9..88e9d71f890fa 100644 --- a/test/common/http/http2/codec_impl_test_util.h +++ b/test/common/http/http2/codec_impl_test_util.h @@ -7,6 +7,8 @@ #include "test/mocks/common.h" +#include "quiche/http2/adapter/http2_adapter.h" + namespace Envoy { namespace Http { namespace Http2 { @@ -75,7 +77,14 @@ class TestServerConnectionImpl : public TestCodecStatsProvider, max_request_headers_kb, max_request_headers_count, headers_with_underscores_action) {} - nghttp2_session* session() { return session_; } + nghttp2_session* session() { + ASSERT(!use_new_codec_wrapper_); + return session_; + } + http2::adapter::Http2Adapter* adapter() { + ASSERT(use_new_codec_wrapper_); + return adapter_.get(); + } using ServerConnectionImpl::getStream; using ServerConnectionImpl::sendPendingFrames; @@ -101,7 +110,14 @@ class TestClientConnectionImpl : public TestCodecStatsProvider, max_request_headers_kb, max_request_headers_count, http2_session_factory) {} - nghttp2_session* session() { return session_; } + nghttp2_session* session() { + ASSERT(!use_new_codec_wrapper_); + return session_; + } + http2::adapter::Http2Adapter* adapter() { + ASSERT(use_new_codec_wrapper_); + return adapter_.get(); + } // Submits an H/2 METADATA frame to the peer. // Returns true on success, false otherwise. virtual bool submitMetadata(const MetadataMapVector& mm_vector, int32_t stream_id) { diff --git a/test/common/http/http2/metadata_encoder_test.cc b/test/common/http/http2/metadata_encoder_test.cc new file mode 100644 index 0000000000000..1d073b80a7be6 --- /dev/null +++ b/test/common/http/http2/metadata_encoder_test.cc @@ -0,0 +1,341 @@ +#include "envoy/http/metadata_interface.h" + +#include "source/common/buffer/buffer_impl.h" +#include "source/common/common/logger.h" +#include "source/common/common/random_generator.h" +#include "source/common/http/http2/metadata_decoder.h" +#include "source/common/http/http2/metadata_encoder.h" + +#include "test/test_common/logging.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "http2_frame.h" +#include "nghttp2/nghttp2.h" +#include "quiche/http2/adapter/callback_visitor.h" +#include "quiche/http2/adapter/data_source.h" +#include "quiche/http2/adapter/nghttp2_adapter.h" + +// A global variable in nghttp2 to disable preface and initial settings for tests. +// TODO(soya3129): Remove after issue https://github.com/nghttp2/nghttp2/issues/1246 is fixed. +extern "C" { +extern int nghttp2_enable_strict_preface; +} + +namespace Envoy { +namespace Http { +namespace Http2 { +namespace { + +absl::string_view toStringView(uint8_t* data, size_t length) { + return absl::string_view(reinterpret_cast(data), length); +} + +static const uint64_t STREAM_ID = 1; + +// The buffer stores data sent by encoder and received by decoder. +struct TestBuffer { + uint8_t buf[1024 * 1024] = {0}; + size_t length = 0; +}; + +// The application data structure passes to nghttp2 session. +struct UserData { + NewMetadataEncoder* encoder; + MetadataDecoder* decoder; + // Stores data sent by encoder and received by the decoder. + TestBuffer* output_buffer; +}; + +// Nghttp2 callback function for receiving extension frame. +static int onExtensionChunkRecvCallback(nghttp2_session* /*session*/, const nghttp2_frame_hd* hd, + const uint8_t* data, size_t len, void* user_data) { + EXPECT_GE(hd->length, len); + + MetadataDecoder* decoder = reinterpret_cast(user_data)->decoder; + bool success = decoder->receiveMetadata(data, len); + return success ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE; +} + +// Nghttp2 callback function for unpack extension frames. +static int unpackExtensionCallback(nghttp2_session* /*session*/, void** payload, + const nghttp2_frame_hd* hd, void* user_data) { + EXPECT_NE(nullptr, hd); + EXPECT_NE(nullptr, payload); + + MetadataDecoder* decoder = reinterpret_cast(user_data)->decoder; + bool result = decoder->onMetadataFrameComplete((hd->flags == END_METADATA_FLAG) ? true : false); + return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE; +} + +// Nghttp2 callback function for sending data to peer. +static ssize_t sendCallback(nghttp2_session* /*session*/, const uint8_t* buf, size_t len, int flags, + void* user_data) { + EXPECT_LE(0, flags); + + TestBuffer* buffer = (reinterpret_cast(user_data))->output_buffer; + memcpy(buffer->buf + buffer->length, buf, len); + buffer->length += len; + return len; +} + +} // namespace + +class MetadataEncoderTest : public testing::Test { +public: + void initialize(MetadataCallback cb) { + decoder_ = std::make_unique(cb); + + // Enables extension frame. + nghttp2_option* option; + nghttp2_option_new(&option); + nghttp2_option_set_user_recv_extension_type(option, METADATA_FRAME_TYPE); + + // Sets callback functions. + nghttp2_session_callbacks* callbacks; + nghttp2_session_callbacks_new(&callbacks); + nghttp2_session_callbacks_set_send_callback(callbacks, sendCallback); + nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(callbacks, + onExtensionChunkRecvCallback); + nghttp2_session_callbacks_set_unpack_extension_callback(callbacks, unpackExtensionCallback); + + // Sets application data to pass to nghttp2 session. + user_data_.encoder = &encoder_; + user_data_.decoder = decoder_.get(); + user_data_.output_buffer = &output_buffer_; + + // Creates new nghttp2 session. + nghttp2_enable_strict_preface = 0; + visitor_ = std::make_unique( + http2::adapter::Perspective::kClient, *callbacks, &user_data_); + session_ = http2::adapter::NgHttp2Adapter::CreateClientAdapter(*visitor_, option); + nghttp2_enable_strict_preface = 1; + nghttp2_option_del(option); + nghttp2_session_callbacks_del(callbacks); + } + + void verifyMetadataMapVector(MetadataMapVector& expect, MetadataMapPtr&& metadata_map_ptr) { + for (const auto& metadata : *metadata_map_ptr) { + EXPECT_EQ(expect.front()->find(metadata.first)->second, metadata.second); + } + expect.erase(expect.begin()); + } + + void submitMetadata(const MetadataMapVector& metadata_map_vector) { + // Creates metadata payload. + NewMetadataEncoder::MetadataSourceVector sources = encoder_.createSources(metadata_map_vector); + for (auto& source : sources) { + session_->SubmitMetadata(STREAM_ID, 16 * 1024, std::move(source)); + } + // Triggers nghttp2 to populate the payloads of the METADATA frames. + int result = session_->Send(); + EXPECT_EQ(0, result); + } + + std::unique_ptr session_; + std::unique_ptr visitor_; + NewMetadataEncoder encoder_; + std::unique_ptr decoder_; + int count_ = 0; + + // Stores data received by peer. + TestBuffer output_buffer_; + + // Application data passed to nghttp2. + UserData user_data_; + + Random::RandomGeneratorImpl random_generator_; +}; + +TEST_F(MetadataEncoderTest, TestTotalPayloadSize) { + initialize([](MetadataMapPtr&&) {}); + + const std::string payload = std::string(1024, 'a'); + EXPECT_EQ(0, decoder_->totalPayloadSize()); + EXPECT_TRUE( + decoder_->receiveMetadata(reinterpret_cast(payload.data()), payload.size())); + EXPECT_EQ(payload.size(), decoder_->totalPayloadSize()); + EXPECT_TRUE( + decoder_->receiveMetadata(reinterpret_cast(payload.data()), payload.size())); + EXPECT_EQ(2 * payload.size(), decoder_->totalPayloadSize()); +} + +TEST_F(MetadataEncoderTest, TestDecodeBadData) { + MetadataMap metadata_map = { + {"header_key1", "header_value1"}, + }; + MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); + MetadataMapVector metadata_map_vector; + metadata_map_vector.push_back(std::move(metadata_map_ptr)); + + // Verifies the encoding/decoding result in decoder's callback functions. + initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { + this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); + }); + submitMetadata(metadata_map_vector); + + // Messes up with the encoded payload, and passes it to the decoder. + output_buffer_.buf[10] |= 0xff; + decoder_->receiveMetadata(output_buffer_.buf, output_buffer_.length); + EXPECT_FALSE(decoder_->onMetadataFrameComplete(true)); +} + +// Checks if accumulated metadata size reaches size limit, returns failure. +TEST_F(MetadataEncoderTest, VerifyEncoderDecoderMultipleMetadataReachSizeLimit) { + MetadataMap metadata_map_empty = {}; + MetadataCallback cb = [](std::unique_ptr) -> void {}; + initialize(cb); + + ssize_t result = 0; + + for (int i = 0; i < 100; i++) { + // Cleans up the output buffer. + memset(output_buffer_.buf, 0, output_buffer_.length); + output_buffer_.length = 0; + + MetadataMap metadata_map = { + {"header_key1", std::string(10000, 'a')}, + {"header_key2", std::string(10000, 'b')}, + }; + MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); + MetadataMapVector metadata_map_vector; + metadata_map_vector.push_back(std::move(metadata_map_ptr)); + + // Encode and decode the second MetadataMap. + decoder_->callback_ = [this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { + this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); + }; + submitMetadata(metadata_map_vector); + + result = session_->ProcessBytes(toStringView(output_buffer_.buf, output_buffer_.length)); + if (result < 0) { + break; + } + } + // Verifies max metadata limit reached. + EXPECT_LT(result, 0); + EXPECT_LE(decoder_->max_payload_size_bound_, decoder_->total_payload_size_); +} + +// Tests encoding an empty map. +TEST_F(MetadataEncoderTest, EncodeMetadataMapEmpty) { + MetadataMap empty = {}; + MetadataMapPtr metadata_map_ptr = std::make_unique(empty); + + MetadataMapVector metadata_map_vector; + metadata_map_vector.push_back(std::move(metadata_map_ptr)); + + // Verifies the encoding/decoding result in decoder's callback functions. + initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { + this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); + }); + submitMetadata(metadata_map_vector); + + // The empty metadata map is ignored, and does not result in any frames being emitted. + EXPECT_EQ(output_buffer_.length, 0); +} + +// Tests encoding/decoding small metadata map vectors. +TEST_F(MetadataEncoderTest, EncodeMetadataMapVectorSmall) { + MetadataMap metadata_map = { + {"header_key1", std::string(5, 'a')}, + {"header_key2", std::string(5, 'b')}, + }; + MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); + MetadataMap metadata_map_2 = { + {"header_key3", std::string(5, 'a')}, + {"header_key4", std::string(5, 'b')}, + }; + MetadataMapPtr metadata_map_ptr_2 = std::make_unique(metadata_map); + MetadataMap metadata_map_3 = { + {"header_key1", std::string(1, 'a')}, + {"header_key2", std::string(1, 'b')}, + }; + MetadataMapPtr metadata_map_ptr_3 = std::make_unique(metadata_map); + + MetadataMapVector metadata_map_vector; + metadata_map_vector.push_back(std::move(metadata_map_ptr)); + metadata_map_vector.push_back(std::move(metadata_map_ptr_2)); + metadata_map_vector.push_back(std::move(metadata_map_ptr_3)); + + // Verifies the encoding/decoding result in decoder's callback functions. + initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { + this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); + }); + submitMetadata(metadata_map_vector); + + // Verifies flag and payload are encoded correctly. + const uint64_t consume_size = random_generator_.random() % output_buffer_.length; + session_->ProcessBytes(toStringView(output_buffer_.buf, consume_size)); + session_->ProcessBytes( + toStringView(output_buffer_.buf + consume_size, output_buffer_.length - consume_size)); +} + +// Tests encoding/decoding large metadata map vectors. +TEST_F(MetadataEncoderTest, EncodeMetadataMapVectorLarge) { + MetadataMapVector metadata_map_vector; + for (int i = 0; i < 10; i++) { + MetadataMap metadata_map = { + {"header_key1", std::string(50000, 'a')}, + {"header_key2", std::string(50000, 'b')}, + }; + MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); + metadata_map_vector.push_back(std::move(metadata_map_ptr)); + } + // Verifies the encoding/decoding result in decoder's callback functions. + initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { + this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); + }); + submitMetadata(metadata_map_vector); + // Verifies flag and payload are encoded correctly. + const uint64_t consume_size = random_generator_.random() % output_buffer_.length; + session_->ProcessBytes(toStringView(output_buffer_.buf, consume_size)); + session_->ProcessBytes( + toStringView(output_buffer_.buf + consume_size, output_buffer_.length - consume_size)); +} + +// Tests encoding/decoding with fuzzed metadata size. +TEST_F(MetadataEncoderTest, EncodeFuzzedMetadata) { + MetadataMapVector metadata_map_vector; + for (int i = 0; i < 10; i++) { + Random::RandomGeneratorImpl random; + int value_size_1 = random.random() % (2 * Http::METADATA_MAX_PAYLOAD_SIZE) + 1; + int value_size_2 = random.random() % (2 * Http::METADATA_MAX_PAYLOAD_SIZE) + 1; + MetadataMap metadata_map = { + {"header_key1", std::string(value_size_1, 'a')}, + {"header_key2", std::string(value_size_2, 'a')}, + }; + MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); + metadata_map_vector.push_back(std::move(metadata_map_ptr)); + } + + // Verifies the encoding/decoding result in decoder's callback functions. + initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { + this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); + }); + submitMetadata(metadata_map_vector); + + // Verifies flag and payload are encoded correctly. + session_->ProcessBytes(toStringView(output_buffer_.buf, output_buffer_.length)); +} + +TEST_F(MetadataEncoderTest, EncodeDecodeFrameTest) { + MetadataMap metadataMap = { + {"Connections", "15"}, + {"Timeout Seconds", "10"}, + }; + MetadataMapPtr metadataMapPtr = std::make_unique(metadataMap); + MetadataMapVector metadata_map_vector; + metadata_map_vector.push_back(std::move(metadataMapPtr)); + Http2Frame http2FrameFromUltility = Http2Frame::makeMetadataFrameFromMetadataMap( + 1, metadataMap, Http2Frame::MetadataFlags::EndMetadata); + MetadataDecoder decoder([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { + this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); + }); + decoder.receiveMetadata(http2FrameFromUltility.data() + 9, http2FrameFromUltility.size() - 9); + decoder.onMetadataFrameComplete(true); +} + +} // namespace Http2 +} // namespace Http +} // namespace Envoy diff --git a/test/extensions/filters/http/jwt_authn/BUILD b/test/extensions/filters/http/jwt_authn/BUILD index 1a7c3f0723247..41398042e7189 100644 --- a/test/extensions/filters/http/jwt_authn/BUILD +++ b/test/extensions/filters/http/jwt_authn/BUILD @@ -143,6 +143,7 @@ envoy_extension_cc_test( name = "filter_integration_test", srcs = ["filter_integration_test.cc"], extension_names = ["envoy.filters.http.jwt_authn"], + shard_count = 4, deps = [ "//source/common/router:string_accessor_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", diff --git a/test/integration/BUILD b/test/integration/BUILD index 75180edec85e4..0ac91631679ab 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -354,6 +354,7 @@ envoy_cc_test( "//test/integration/filters:set_response_code_filter_config_proto_cc_proto", "//test/integration/filters:set_response_code_filter_lib", "//test/mocks/http:http_mocks", + "//test/test_common:test_runtime_lib", "//test/test_common:utility_lib", "@com_google_absl//absl/synchronization", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", @@ -527,7 +528,7 @@ envoy_cc_test( ], # As this test has many H1/H2/v4/v6 tests it takes a while to run. # Shard it enough to bring the run time in line with other integration tests. - shard_count = 5, + shard_count = 10, deps = [ ":protocol_integration_test_lib", ], @@ -1540,6 +1541,13 @@ envoy_cc_fuzz_test( deps = [":h2_fuzz_lib"], ) +envoy_cc_fuzz_test( + name = "h2_wrapped_capture_fuzz_test", + srcs = ["h2_wrapped_capture_fuzz_test.cc"], + corpus = "h2_corpus", + deps = [":h2_fuzz_lib"], +) + envoy_cc_fuzz_test( name = "h2_capture_persistent_fuzz_test", srcs = ["h2_capture_fuzz_test.cc"], diff --git a/test/integration/buffer_accounting_integration_test.cc b/test/integration/buffer_accounting_integration_test.cc index 4543850d83aa4..391cf60ec8615 100644 --- a/test/integration/buffer_accounting_integration_test.cc +++ b/test/integration/buffer_accounting_integration_test.cc @@ -24,13 +24,14 @@ namespace Envoy { namespace { std::string protocolTestParamsAndBoolToString( - const ::testing::TestParamInfo>& params) { - return fmt::format("{}_{}", + const ::testing::TestParamInfo>& params) { + return fmt::format("{}_{}_{}", HttpProtocolIntegrationTest::protocolTestParamsToString( ::testing::TestParamInfo(std::get<0>(params.param), /*an_index=*/0)), std::get<1>(params.param) ? "with_per_stream_buffer_accounting" - : "without_per_stream_buffer_accounting"); + : "without_per_stream_buffer_accounting", + std::get<2>(params.param) ? "WrappedHttp2" : "BareHttp2"); } void runOnWorkerThreadsAndWaitforCompletion(Server::Instance& server, std::function func) { @@ -64,7 +65,7 @@ void runOnWorkerThreadsAndWaitforCompletion(Server::Instance& server, std::funct class Http2BufferWatermarksTest : public SocketInterfaceSwap, - public testing::TestWithParam>, + public testing::TestWithParam>, public HttpIntegrationTest { public: std::vector @@ -97,7 +98,9 @@ class Http2BufferWatermarksTest } else { buffer_factory_ = std::make_shared(); } - + const bool enable_new_wrapper = std::get<2>(GetParam()); + config_helper_.addRuntimeOverride("envoy.reloadable_features.http2_new_codec_wrapper", + enable_new_wrapper ? "true" : "false"); setServerBufferFactory(buffer_factory_); setUpstreamProtocol(std::get<0>(GetParam()).upstream_protocol); } @@ -136,7 +139,7 @@ INSTANTIATE_TEST_SUITE_P( IpVersions, Http2BufferWatermarksTest, testing::Combine(testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( {Http::CodecType::HTTP2}, {FakeHttpConnection::Type::HTTP2})), - testing::Bool()), + testing::Bool(), testing::Bool()), protocolTestParamsAndBoolToString); // We should create four buffers each billing the same downstream request's @@ -272,7 +275,7 @@ TEST_P(Http2BufferWatermarksTest, ShouldTrackAllocatedBytesToDownstream) { // up notifying the BufferMemoryAccount when the dtor of the downstream stream // occurs. class ProtocolsBufferWatermarksTest - : public testing::TestWithParam>, + : public testing::TestWithParam>, public HttpIntegrationTest { public: ProtocolsBufferWatermarksTest() @@ -287,6 +290,9 @@ class ProtocolsBufferWatermarksTest } else { buffer_factory_ = std::make_shared(); } + const bool enable_new_wrapper = std::get<2>(GetParam()); + config_helper_.addRuntimeOverride("envoy.reloadable_features.http2_new_codec_wrapper", + enable_new_wrapper ? "true" : "false"); setServerBufferFactory(buffer_factory_); setUpstreamProtocol(std::get<0>(GetParam()).upstream_protocol); } @@ -302,7 +308,7 @@ INSTANTIATE_TEST_SUITE_P( testing::Combine(testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( {Http::CodecType::HTTP1, Http::CodecType::HTTP2, Http::CodecType::HTTP3}, {FakeHttpConnection::Type::HTTP2})), - testing::Bool()), + testing::Bool(), testing::Bool()), protocolTestParamsAndBoolToString); TEST_P(ProtocolsBufferWatermarksTest, AccountShouldBeRegisteredAndUnregisteredOnce) { @@ -427,7 +433,7 @@ INSTANTIATE_TEST_SUITE_P( IpVersions, Http2OverloadManagerIntegrationTest, testing::Combine(testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( {Http::CodecType::HTTP2}, {FakeHttpConnection::Type::HTTP2})), - testing::Bool()), + testing::Bool(), testing::Bool()), protocolTestParamsAndBoolToString); TEST_P(Http2OverloadManagerIntegrationTest, diff --git a/test/integration/h2_wrapped_capture_fuzz_test.cc b/test/integration/h2_wrapped_capture_fuzz_test.cc new file mode 100644 index 0000000000000..8a8c98a671077 --- /dev/null +++ b/test/integration/h2_wrapped_capture_fuzz_test.cc @@ -0,0 +1,32 @@ +#include "test/integration/h2_fuzz.h" + +namespace Envoy { +void H2FuzzIntegrationTest::initialize() { + config_helper_.addConfigModifier([&](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() + ->mutable_http2_protocol_options() + ->set_allow_metadata(true); + ConfigHelper::setProtocolOptions(*bootstrap.mutable_static_resources()->mutable_clusters(0), + protocol_options); + }); + config_helper_.addConfigModifier( + [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) -> void { hcm.mutable_http2_protocol_options()->set_allow_metadata(true); }); + config_helper_.addRuntimeOverride("envoy.reloadable_features.http2_new_codec_wrapper", "true"); + setDownstreamProtocol(Http::CodecType::HTTP2); + setUpstreamProtocol(Http::CodecType::HTTP2); + + HttpIntegrationTest::initialize(); +} + +DEFINE_PROTO_FUZZER(const test::integration::H2CaptureFuzzTestCase& input) { + // Pick an IP version to use for loopback, it doesn't matter which. + FUZZ_ASSERT(!TestEnvironment::getIpVersionsForTest().empty()); + const auto ip_version = TestEnvironment::getIpVersionsForTest()[0]; + PERSISTENT_FUZZ_VAR H2FuzzIntegrationTest h2_fuzz_integration_test(ip_version); + h2_fuzz_integration_test.replay(input, false); +} + +} // namespace Envoy diff --git a/test/integration/http2_flood_integration_test.cc b/test/integration/http2_flood_integration_test.cc index 7a6a590e8a4cf..f24d631531773 100644 --- a/test/integration/http2_flood_integration_test.cc +++ b/test/integration/http2_flood_integration_test.cc @@ -18,6 +18,7 @@ #include "test/mocks/http/mocks.h" #include "test/test_common/network_utility.h" #include "test/test_common/printers.h" +#include "test/test_common/test_runtime.h" #include "test/test_common/utility.h" #include "gtest/gtest.h" @@ -31,19 +32,31 @@ const uint32_t ControlFrameFloodLimit = 100; const uint32_t AllFrameFloodLimit = 1000; } // namespace +std::string testParamsToString( + const ::testing::TestParamInfo> params) { + const bool is_v4 = (std::get<0>(params.param) == Network::Address::IpVersion::v4); + const bool http2_new_codec_wrapper = std::get<1>(params.param); + return absl::StrCat(is_v4 ? "IPv4" : "IPv6", + http2_new_codec_wrapper ? "WrappedHttp2" : "BareHttp2"); +} + // It is important that the new socket interface is installed before any I/O activity starts and // the previous one is restored after all I/O activity stops. Since the HttpIntegrationTest // destructor stops Envoy the SocketInterfaceSwap destructor needs to run after it. This order of // multiple inheritance ensures that SocketInterfaceSwap destructor runs after // Http2FrameIntegrationTest destructor completes. -class Http2FloodMitigationTest : public SocketInterfaceSwap, - public testing::TestWithParam, - public Http2RawFrameIntegrationTest { +class Http2FloodMitigationTest + : public SocketInterfaceSwap, + public testing::TestWithParam>, + public Http2RawFrameIntegrationTest { public: - Http2FloodMitigationTest() : Http2RawFrameIntegrationTest(GetParam()) { + Http2FloodMitigationTest() : Http2RawFrameIntegrationTest(std::get<0>(GetParam())) { config_helper_.addConfigModifier( [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) { hcm.mutable_delayed_close_timeout()->set_seconds(1); }); + const bool enable_new_wrapper = std::get<1>(GetParam()); + config_helper_.addRuntimeOverride("envoy.reloadable_features.http2_new_codec_wrapper", + enable_new_wrapper ? "true" : "false"); } protected: @@ -62,9 +75,11 @@ class Http2FloodMitigationTest : public SocketInterfaceSwap, void triggerListenerDrain(); }; -INSTANTIATE_TEST_SUITE_P(IpVersions, Http2FloodMitigationTest, - testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), - TestUtility::ipTestParamsToString); +INSTANTIATE_TEST_SUITE_P( + IpVersions, Http2FloodMitigationTest, + testing::Combine(testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + testing::ValuesIn({false, true})), + testParamsToString); bool Http2FloodMitigationTest::initializeUpstreamFloodTest() { setDownstreamProtocol(Http::CodecType::HTTP2); diff --git a/test/integration/http_protocol_integration.cc b/test/integration/http_protocol_integration.cc index 871065ba698ad..3773920e7f93a 100644 --- a/test/integration/http_protocol_integration.cc +++ b/test/integration/http_protocol_integration.cc @@ -12,13 +12,25 @@ std::vector HttpProtocolIntegrationTest::getProtocolTest for (auto downstream_protocol : downstream_protocols) { for (auto upstream_protocol : upstream_protocols) { #ifdef ENVOY_ENABLE_QUIC - ret.push_back(HttpProtocolTestParams{ip_version, downstream_protocol, upstream_protocol}); + ret.push_back( + HttpProtocolTestParams{ip_version, downstream_protocol, upstream_protocol, false}); + if (downstream_protocol == Http::CodecType::HTTP2 || + upstream_protocol == Http::CodecType::HTTP2) { + ret.push_back( + HttpProtocolTestParams{ip_version, downstream_protocol, upstream_protocol, true}); + } #else if (downstream_protocol == Http::CodecType::HTTP3 || upstream_protocol == Http::CodecType::HTTP3) { ENVOY_LOG_MISC(warn, "Skipping HTTP/3 as support is compiled out"); } else { - ret.push_back(HttpProtocolTestParams{ip_version, downstream_protocol, upstream_protocol}); + ret.push_back( + HttpProtocolTestParams{ip_version, downstream_protocol, upstream_protocol, false}); + if (downstream_protocol == Http::CodecType::HTTP2 || + upstream_protocol == Http::CodecType::HTTP2) { + ret.push_back( + HttpProtocolTestParams{ip_version, downstream_protocol, upstream_protocol, true}); + } } #endif } @@ -55,7 +67,8 @@ std::string HttpProtocolIntegrationTest::protocolTestParamsToString( const ::testing::TestParamInfo& params) { return absl::StrCat((params.param.version == Network::Address::IpVersion::v4 ? "IPv4_" : "IPv6_"), downstreamToString(params.param.downstream_protocol), - upstreamToString(params.param.upstream_protocol)); + upstreamToString(params.param.upstream_protocol), + params.param.http2_new_codec_wrapper ? "WrappedHttp2" : "BareHttp2"); } void HttpProtocolIntegrationTest::expectUpstreamBytesSentAndReceived( diff --git a/test/integration/http_protocol_integration.h b/test/integration/http_protocol_integration.h index ba47a134d9485..f68d5ee8f5021 100644 --- a/test/integration/http_protocol_integration.h +++ b/test/integration/http_protocol_integration.h @@ -10,6 +10,7 @@ struct HttpProtocolTestParams { Network::Address::IpVersion version; Http::CodecType downstream_protocol; Http::CodecType upstream_protocol; + bool http2_new_codec_wrapper; }; // Allows easy testing of Envoy code for HTTP/HTTP2 upstream/downstream. @@ -52,7 +53,10 @@ class HttpProtocolIntegrationTest : public testing::TestWithParam Date: Mon, 15 Nov 2021 10:01:11 -0800 Subject: [PATCH 081/110] tls: Add pkcs12 support to TlsCertificate API. (#18733) Signed-off-by: Anthony Rossi --- .../transport_sockets/tls/v3/common.proto | 17 +- docs/root/version_history/current.rst | 1 + envoy/ssl/tls_certificate_config.h | 11 + .../common/ssl/tls_certificate_config_impl.cc | 50 +++-- .../common/ssl/tls_certificate_config_impl.h | 4 + .../tls/context_config_impl.cc | 2 +- .../transport_sockets/tls/context_impl.cc | 206 +++++++++++------- .../transport_sockets/tls/context_impl.h | 6 + .../tls/context_impl_test.cc | 194 +++++++++++++++++ .../transport_sockets/tls/ssl_socket_test.cc | 78 +++++++ .../transport_sockets/tls/test_data/README.md | 2 +- .../transport_sockets/tls/test_data/certs.sh | 12 + .../test_data/password_protected_certkey.p12 | Bin 0 -> 2669 bytes .../tls/test_data/san_dns3_certkeychain.p12 | Bin 0 -> 4703 bytes .../selfsigned_ecdsa_p384_certkey.p12 | Bin 0 -> 1184 bytes .../test_data/selfsigned_rsa_1024_certkey.p12 | Bin 0 -> 1714 bytes test/mocks/ssl/mocks.h | 2 + tools/spelling/spelling_dictionary.txt | 1 + 18 files changed, 494 insertions(+), 92 deletions(-) create mode 100644 test/extensions/transport_sockets/tls/test_data/password_protected_certkey.p12 create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns3_certkeychain.p12 create mode 100644 test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p384_certkey.p12 create mode 100644 test/extensions/transport_sockets/tls/test_data/selfsigned_rsa_1024_certkey.p12 diff --git a/api/envoy/extensions/transport_sockets/tls/v3/common.proto b/api/envoy/extensions/transport_sockets/tls/v3/common.proto index ab88abcbe2ce6..369ee4d657eb3 100644 --- a/api/envoy/extensions/transport_sockets/tls/v3/common.proto +++ b/api/envoy/extensions/transport_sockets/tls/v3/common.proto @@ -149,7 +149,7 @@ message PrivateKeyProvider { } } -// [#next-free-field: 8] +// [#next-free-field: 9] message TlsCertificate { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.auth.TlsCertificate"; @@ -168,6 +168,21 @@ message TlsCertificate { // applies to dynamic secrets, when the *TlsCertificate* is delivered via SDS. config.core.v3.DataSource private_key = 2 [(udpa.annotations.sensitive) = true]; + // `Pkcs12` data containing TLS certificate, chain, and private key. + // + // If *pkcs12* is a filesystem path, the file will be read, but no watch will + // be added to the parent directory, since *pkcs12* isn't used by SDS. + // This field is mutually exclusive with *certificate_chain*, *private_key* and *private_key_provider*. + // This can't be marked as ``oneof`` due to API compatibility reasons. Setting + // both :ref:`private_key `, + // :ref:`certificate_chain `, + // or :ref:`private_key_provider ` + // and :ref:`pkcs12 ` + // fields will result in an error. Use :ref:`password + // ` + // to specify the password to unprotect the `PKCS12` data, if necessary. + config.core.v3.DataSource pkcs12 = 8 [(udpa.annotations.sensitive) = true]; + // If specified, updates of file-based *certificate_chain* and *private_key* // sources will be triggered by this watch. The certificate/key pair will be // read together and validated for atomic read consistency (i.e. no diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 0d9dcd8fa1d65..d04b950b3cc9f 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -74,6 +74,7 @@ New Features * thrift_proxy: support header flags. * thrift_proxy: support subset lb when using request or route metadata. * tls: added support for only verifying the leaf CRL in the certificate chain with :ref:`only_verify_leaf_cert_crl `. +* tls: support loading certificate chain and private key via :ref:`pkcs12 `. * transport_socket: added :ref:`envoy.transport_sockets.tcp_stats ` which generates additional statistics gathered from the OS TCP stack. * udp: add support for multiple listener filters. * upstream: added the ability to :ref:`configure max connection duration ` for upstream clusters. diff --git a/envoy/ssl/tls_certificate_config.h b/envoy/ssl/tls_certificate_config.h index 06113a9f33df6..634c4ed4635ac 100644 --- a/envoy/ssl/tls_certificate_config.h +++ b/envoy/ssl/tls_certificate_config.h @@ -35,6 +35,17 @@ class TlsCertificateConfig { */ virtual const std::string& privateKeyPath() const PURE; + /** + * @return a string of pkcs12 data. + */ + virtual const std::string& pkcs12() const PURE; + + /** + * @return path of the pkcs12 file used to identify the local side or "" if the pkcs12 + * data was inlined. + */ + virtual const std::string& pkcs12Path() const PURE; + /** * @return private key method provider. */ diff --git a/source/common/ssl/tls_certificate_config_impl.cc b/source/common/ssl/tls_certificate_config_impl.cc index fd0332c126af6..0d4c1dfb83bd6 100644 --- a/source/common/ssl/tls_certificate_config_impl.cc +++ b/source/common/ssl/tls_certificate_config_impl.cc @@ -36,6 +36,9 @@ TlsCertificateConfigImpl::TlsCertificateConfigImpl( private_key_(Config::DataSource::read(config.private_key(), true, api)), private_key_path_(Config::DataSource::getPath(config.private_key()) .value_or(private_key_.empty() ? EMPTY_STRING : INLINE_STRING)), + pkcs12_(Config::DataSource::read(config.pkcs12(), true, api)), + pkcs12_path_(Config::DataSource::getPath(config.pkcs12()) + .value_or(pkcs12_.empty() ? EMPTY_STRING : INLINE_STRING)), password_(Config::DataSource::read(config.password(), true, api)), password_path_(Config::DataSource::getPath(config.password()) .value_or(password_.empty() ? EMPTY_STRING : INLINE_STRING)), @@ -47,24 +50,39 @@ TlsCertificateConfigImpl::TlsCertificateConfigImpl( throw EnvoyException(fmt::format( "Certificate configuration can't have both private_key and private_key_provider")); } - if (config.has_private_key_provider()) { - private_key_method_ = - factory_context.sslContextManager() - .privateKeyMethodManager() - .createPrivateKeyMethodProvider(config.private_key_provider(), factory_context); - } - if (certificate_chain_.empty()) { - throw EnvoyException( - fmt::format("Failed to load incomplete certificate from {}: certificate chain not set", - certificate_chain_path_)); - } - if (private_key_.empty() && private_key_method_ == nullptr) { + if (config.has_pkcs12()) { + if (config.has_private_key()) { + throw EnvoyException( + fmt::format("Certificate configuration can't have both pkcs12 and private_key")); + } + if (config.has_certificate_chain()) { + throw EnvoyException( + fmt::format("Certificate configuration can't have both pkcs12 and certificate_chain")); + } if (config.has_private_key_provider()) { - throw EnvoyException(fmt::format("Failed to load private key provider: {}", - config.private_key_provider().provider_name())); - } else { throw EnvoyException( - fmt::format("Failed to load incomplete private key from path: {}", private_key_path_)); + fmt::format("Certificate configuration can't have both pkcs12 and private_key_provider")); + } + } else { + if (config.has_private_key_provider()) { + private_key_method_ = + factory_context.sslContextManager() + .privateKeyMethodManager() + .createPrivateKeyMethodProvider(config.private_key_provider(), factory_context); + } + if (certificate_chain_.empty()) { + throw EnvoyException( + fmt::format("Failed to load incomplete certificate from {}: certificate chain not set", + certificate_chain_path_)); + } + if (private_key_.empty() && private_key_method_ == nullptr) { + if (config.has_private_key_provider()) { + throw EnvoyException(fmt::format("Failed to load private key provider: {}", + config.private_key_provider().provider_name())); + } else { + throw EnvoyException( + fmt::format("Failed to load incomplete private key from path: {}", private_key_path_)); + } } } } diff --git a/source/common/ssl/tls_certificate_config_impl.h b/source/common/ssl/tls_certificate_config_impl.h index 074ddcb955a4e..b93d429fc9893 100644 --- a/source/common/ssl/tls_certificate_config_impl.h +++ b/source/common/ssl/tls_certificate_config_impl.h @@ -20,6 +20,8 @@ class TlsCertificateConfigImpl : public TlsCertificateConfig { const std::string& certificateChainPath() const override { return certificate_chain_path_; } const std::string& privateKey() const override { return private_key_; } const std::string& privateKeyPath() const override { return private_key_path_; } + const std::string& pkcs12() const override { return pkcs12_; } + const std::string& pkcs12Path() const override { return pkcs12_path_; } const std::string& password() const override { return password_; } const std::string& passwordPath() const override { return password_path_; } const std::vector& ocspStaple() const override { return ocsp_staple_; } @@ -33,6 +35,8 @@ class TlsCertificateConfigImpl : public TlsCertificateConfig { const std::string certificate_chain_path_; const std::string private_key_; const std::string private_key_path_; + const std::string pkcs12_; + const std::string pkcs12_path_; const std::string password_; const std::string password_path_; const std::vector ocsp_staple_; diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index aa5fa77be56cf..6eea3e42e1738 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -29,7 +29,7 @@ std::vector getTlsCertificateConf if (!config.tls_certificates().empty()) { for (const auto& tls_certificate : config.tls_certificates()) { if (!tls_certificate.has_private_key_provider() && !tls_certificate.has_certificate_chain() && - !tls_certificate.has_private_key()) { + !tls_certificate.has_private_key() && !tls_certificate.has_pkcs12()) { continue; } providers.push_back( diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index 0afc83d5a67ae..9ded6a83016ff 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -30,6 +30,7 @@ #include "absl/strings/str_join.h" #include "openssl/evp.h" #include "openssl/hmac.h" +#include "openssl/pkcs12.h" #include "openssl/rand.h" namespace Envoy { @@ -53,6 +54,15 @@ bool cbsContainsU16(CBS& cbs, uint16_t n) { return false; } +void logSslErrorChain() { + while (uint64_t err = ERR_get_error()) { + ENVOY_LOG_MISC(debug, "SSL error: {}:{}:{}:{}", err, + absl::NullSafeStringView(ERR_lib_error_string(err)), + absl::NullSafeStringView(ERR_func_error_string(err)), ERR_GET_REASON(err), + absl::NullSafeStringView(ERR_reason_error_string(err))); + } +} + } // namespace int ContextImpl::sslExtendedSocketInfoIndex() { @@ -166,43 +176,12 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c auto& ctx = tls_contexts_[i]; // Load certificate chain. const auto& tls_certificate = tls_certificates[i].get(); - ctx.cert_chain_file_path_ = tls_certificate.certificateChainPath(); - bssl::UniquePtr bio( - BIO_new_mem_buf(const_cast(tls_certificate.certificateChain().data()), - tls_certificate.certificateChain().size())); - RELEASE_ASSERT(bio != nullptr, ""); - ctx.cert_chain_.reset(PEM_read_bio_X509_AUX(bio.get(), nullptr, nullptr, nullptr)); - if (ctx.cert_chain_ == nullptr || - !SSL_CTX_use_certificate(ctx.ssl_ctx_.get(), ctx.cert_chain_.get())) { - while (uint64_t err = ERR_get_error()) { - ENVOY_LOG_MISC(debug, "SSL error: {}:{}:{}:{}", err, - absl::NullSafeStringView(ERR_lib_error_string(err)), - absl::NullSafeStringView(ERR_func_error_string(err)), ERR_GET_REASON(err), - absl::NullSafeStringView(ERR_reason_error_string(err))); - } - throw EnvoyException( - absl::StrCat("Failed to load certificate chain from ", ctx.cert_chain_file_path_)); - } - // Read rest of the certificate chain. - while (true) { - bssl::UniquePtr cert(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); - if (cert == nullptr) { - break; - } - if (!SSL_CTX_add_extra_chain_cert(ctx.ssl_ctx_.get(), cert.get())) { - throw EnvoyException( - absl::StrCat("Failed to load certificate chain from ", ctx.cert_chain_file_path_)); - } - // SSL_CTX_add_extra_chain_cert() takes ownership. - cert.release(); - } - // Check for EOF. - const uint32_t err = ERR_peek_last_error(); - if (ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE) { - ERR_clear_error(); + if (!tls_certificate.pkcs12().empty()) { + ctx.loadPkcs12(tls_certificate.pkcs12(), tls_certificate.pkcs12Path(), + tls_certificate.password()); } else { - throw EnvoyException( - absl::StrCat("Failed to load certificate chain from ", ctx.cert_chain_file_path_)); + ctx.loadCertificateChain(tls_certificate.certificateChain(), + tls_certificate.certificateChainPath()); } // The must staple extension means the certificate promises to carry @@ -287,44 +266,10 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c } #endif SSL_CTX_set_private_key_method(ctx.ssl_ctx_.get(), private_key_method.get()); - } else { + } else if (!tls_certificate.privateKey().empty()) { // Load private key. - bio.reset(BIO_new_mem_buf(const_cast(tls_certificate.privateKey().data()), - tls_certificate.privateKey().size())); - RELEASE_ASSERT(bio != nullptr, ""); - bssl::UniquePtr pkey( - PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, - !tls_certificate.password().empty() - ? const_cast(tls_certificate.password().c_str()) - : nullptr)); - - if (pkey == nullptr || !SSL_CTX_use_PrivateKey(ctx.ssl_ctx_.get(), pkey.get())) { - throw EnvoyException(fmt::format("Failed to load private key from {}, Cause: {}", - tls_certificate.privateKeyPath(), - Utility::getLastCryptoError().value_or("unknown"))); - } - -#ifdef BORINGSSL_FIPS - // Verify that private keys are passing FIPS pairwise consistency tests. - switch (pkey_id) { - case EVP_PKEY_EC: { - const EC_KEY* ecdsa_private_key = EVP_PKEY_get0_EC_KEY(pkey.get()); - if (!EC_KEY_check_fips(ecdsa_private_key)) { - throw EnvoyException(fmt::format("Failed to load private key from {}, ECDSA key failed " - "pairwise consistency test required in FIPS mode", - tls_certificate.privateKeyPath())); - } - } break; - case EVP_PKEY_RSA: { - RSA* rsa_private_key = EVP_PKEY_get0_RSA(pkey.get()); - if (!RSA_check_fips(rsa_private_key)) { - throw EnvoyException(fmt::format("Failed to load private key from {}, RSA key failed " - "pairwise consistency test required in FIPS mode", - tls_certificate.privateKeyPath())); - } - } break; - } -#endif + ctx.loadPrivateKey(tls_certificate.privateKey(), tls_certificate.privateKeyPath(), + tls_certificate.password()); } } } @@ -1189,6 +1134,121 @@ bool ContextImpl::verifyCertChain(X509& leaf_cert, STACK_OF(X509) & intermediate return true; } +void TlsContext::loadCertificateChain(const std::string& data, const std::string& data_path) { + cert_chain_file_path_ = data_path; + bssl::UniquePtr bio(BIO_new_mem_buf(const_cast(data.data()), data.size())); + RELEASE_ASSERT(bio != nullptr, ""); + cert_chain_.reset(PEM_read_bio_X509_AUX(bio.get(), nullptr, nullptr, nullptr)); + if (cert_chain_ == nullptr || !SSL_CTX_use_certificate(ssl_ctx_.get(), cert_chain_.get())) { + logSslErrorChain(); + throw EnvoyException( + absl::StrCat("Failed to load certificate chain from ", cert_chain_file_path_)); + } + // Read rest of the certificate chain. + while (true) { + bssl::UniquePtr cert(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); + if (cert == nullptr) { + break; + } + if (!SSL_CTX_add_extra_chain_cert(ssl_ctx_.get(), cert.get())) { + throw EnvoyException( + absl::StrCat("Failed to load certificate chain from ", cert_chain_file_path_)); + } + // SSL_CTX_add_extra_chain_cert() takes ownership. + cert.release(); + } + // Check for EOF. + const uint32_t err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE) { + ERR_clear_error(); + } else { + throw EnvoyException( + absl::StrCat("Failed to load certificate chain from ", cert_chain_file_path_)); + } +} + +void TlsContext::loadPrivateKey(const std::string& data, const std::string& data_path, + const std::string& password) { + bssl::UniquePtr bio(BIO_new_mem_buf(const_cast(data.data()), data.size())); + RELEASE_ASSERT(bio != nullptr, ""); + bssl::UniquePtr pkey( + PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, + !password.empty() ? const_cast(password.c_str()) : nullptr)); + + if (pkey == nullptr || !SSL_CTX_use_PrivateKey(ssl_ctx_.get(), pkey.get())) { + throw EnvoyException(fmt::format("Failed to load private key from {}, Cause: {}", data_path, + Utility::getLastCryptoError().value_or("unknown"))); + } + + checkPrivateKey(pkey, data_path); +} + +void TlsContext::loadPkcs12(const std::string& data, const std::string& data_path, + const std::string& password) { + cert_chain_file_path_ = data_path; + bssl::UniquePtr bio(BIO_new_mem_buf(const_cast(data.data()), data.size())); + RELEASE_ASSERT(bio != nullptr, ""); + bssl::UniquePtr pkcs12(d2i_PKCS12_bio(bio.get(), nullptr)); + + EVP_PKEY* temp_private_key = nullptr; + X509* temp_cert = nullptr; + STACK_OF(X509)* temp_ca_certs = nullptr; + if (pkcs12 == nullptr || + !PKCS12_parse(pkcs12.get(), !password.empty() ? const_cast(password.c_str()) : nullptr, + &temp_private_key, &temp_cert, &temp_ca_certs)) { + logSslErrorChain(); + throw EnvoyException(absl::StrCat("Failed to load pkcs12 from ", data_path)); + } + cert_chain_.reset(temp_cert); + bssl::UniquePtr pkey(temp_private_key); + bssl::UniquePtr ca_certificates(temp_ca_certs); + if (ca_certificates != nullptr) { + X509* ca_cert = nullptr; + while ((ca_cert = sk_X509_pop(ca_certificates.get())) != nullptr) { + // This transfers ownership to ssl_ctx therefore ca_cert does not need to be freed. + SSL_CTX_add_extra_chain_cert(ssl_ctx_.get(), ca_cert); + } + } + if (!SSL_CTX_use_certificate(ssl_ctx_.get(), cert_chain_.get())) { + logSslErrorChain(); + throw EnvoyException(absl::StrCat("Failed to load certificate from ", data_path)); + } + if (temp_private_key == nullptr || !SSL_CTX_use_PrivateKey(ssl_ctx_.get(), pkey.get())) { + throw EnvoyException(fmt::format("Failed to load private key from {}, Cause: {}", data_path, + Utility::getLastCryptoError().value_or("unknown"))); + } + + checkPrivateKey(pkey, data_path); +} + +void TlsContext::checkPrivateKey(const bssl::UniquePtr& pkey, + const std::string& key_path) { +#ifdef BORINGSSL_FIPS + // Verify that private keys are passing FIPS pairwise consistency tests. + switch (EVP_PKEY_id(pkey.get())) { + case EVP_PKEY_EC: { + const EC_KEY* ecdsa_private_key = EVP_PKEY_get0_EC_KEY(pkey.get()); + if (!EC_KEY_check_fips(ecdsa_private_key)) { + throw EnvoyException(fmt::format("Failed to load private key from {}, ECDSA key failed " + "pairwise consistency test required in FIPS mode", + key_path)); + } + } break; + case EVP_PKEY_RSA: { + RSA* rsa_private_key = EVP_PKEY_get0_RSA(pkey.get()); + if (!RSA_check_fips(rsa_private_key)) { + throw EnvoyException(fmt::format("Failed to load private key from {}, RSA key failed " + "pairwise consistency test required in FIPS mode", + key_path)); + } + } break; + } +#else + UNREFERENCED_PARAMETER(pkey); + UNREFERENCED_PARAMETER(key_path); +#endif +} + } // namespace Tls } // namespace TransportSockets } // namespace Extensions diff --git a/source/extensions/transport_sockets/tls/context_impl.h b/source/extensions/transport_sockets/tls/context_impl.h index a3b981b68eb6b..0107a88ec6fac 100644 --- a/source/extensions/transport_sockets/tls/context_impl.h +++ b/source/extensions/transport_sockets/tls/context_impl.h @@ -52,6 +52,12 @@ struct TlsContext { Envoy::Ssl::PrivateKeyMethodProviderSharedPtr getPrivateKeyMethodProvider() { return private_key_method_provider_; } + void loadCertificateChain(const std::string& data, const std::string& data_path); + void loadPrivateKey(const std::string& data, const std::string& data_path, + const std::string& password); + void loadPkcs12(const std::string& data, const std::string& data_path, + const std::string& password); + void checkPrivateKey(const bssl::UniquePtr& pkey, const std::string& key_path); }; class ContextImpl : public virtual Envoy::Ssl::Context { diff --git a/test/extensions/transport_sockets/tls/context_impl_test.cc b/test/extensions/transport_sockets/tls/context_impl_test.cc index 0967e77d25b05..2e6c67a1da178 100644 --- a/test/extensions/transport_sockets/tls/context_impl_test.cc +++ b/test/extensions/transport_sockets/tls/context_impl_test.cc @@ -1096,6 +1096,32 @@ TEST_F(ClientContextConfigImplTest, RSA1024Cert) { EnvoyException, error_msg); } +// Validate that 1024-bit RSA certificates are rejected from `pkcs12`. +TEST_F(ClientContextConfigImplTest, RSA1024Pkcs12) { + envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; + const std::string tls_certificate_yaml = R"EOF( + pkcs12: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_rsa_1024_certkey.p12" + )EOF"; + TestUtility::loadFromYaml(TestEnvironment::substitute(tls_certificate_yaml), + *tls_context.mutable_common_tls_context()->add_tls_certificates()); + ClientContextConfigImpl client_context_config(tls_context, factory_context_); + Event::SimulatedTimeSystem time_system; + ContextManagerImpl manager(time_system); + Stats::IsolatedStoreImpl store; + + std::string error_msg("Failed to load certificate chain from .*selfsigned_rsa_1024_certkey.p12, " + "only RSA certificates " +#ifdef BORINGSSL_FIPS + "with 2048-bit, 3072-bit or 4096-bit keys are supported in FIPS mode" +#else + "with 2048-bit or larger keys are supported" +#endif + ); + EXPECT_THROW_WITH_REGEX(manager.createSslClientContext(store, client_context_config, nullptr), + EnvoyException, error_msg); +} + // Validate that 3072-bit RSA certificates load successfully. TEST_F(ClientContextConfigImplTest, RSA3072Cert) { envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; @@ -1171,6 +1197,25 @@ TEST_F(ClientContextConfigImplTest, NonP256EcdsaCert) { "only P-256 ECDSA certificates are supported"); } +// Validate that non-P256 ECDSA certs are rejected loaded from `pkcs12`. +TEST_F(ClientContextConfigImplTest, NonP256EcdsaPkcs12) { + envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; + const std::string tls_certificate_yaml = R"EOF( + pkcs12: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p384_certkey.p12" + )EOF"; + TestUtility::loadFromYaml(TestEnvironment::substitute(tls_certificate_yaml), + *tls_context.mutable_common_tls_context()->add_tls_certificates()); + ClientContextConfigImpl client_context_config(tls_context, factory_context_); + Event::SimulatedTimeSystem time_system; + ContextManagerImpl manager(time_system); + Stats::IsolatedStoreImpl store; + EXPECT_THROW_WITH_REGEX( + manager.createSslClientContext(store, client_context_config, nullptr), EnvoyException, + "Failed to load certificate chain from .*selfsigned_ecdsa_p384_certkey.p12, " + "only P-256 ECDSA certificates are supported"); +} + // Multiple TLS certificates are not yet supported. // TODO(PiotrSikora): Support multiple TLS certificates. TEST_F(ClientContextConfigImplTest, MultipleTlsCertificates) { @@ -1343,6 +1388,99 @@ TEST_F(ClientContextConfigImplTest, PasswordProtectedTlsCertificates) { client_context_config.tlsCertificates()[0].get().password()); } +// Validate that client context config with password-protected TLS certificates loaded from +// `PKCS12` is created successfully. +TEST_F(ClientContextConfigImplTest, PasswordProtectedPkcs12) { + envoy::extensions::transport_sockets::tls::v3::Secret secret_config; + secret_config.set_name("abc.com"); + + auto* tls_certificate = secret_config.mutable_tls_certificate(); + tls_certificate->mutable_pkcs12()->set_filename(TestEnvironment::substitute( + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/password_protected_certkey.p12")); + tls_certificate->mutable_password()->set_filename(TestEnvironment::substitute( + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/password_protected_password.txt")); + + envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; + tls_context.mutable_common_tls_context() + ->mutable_tls_certificate_sds_secret_configs() + ->Add() + ->set_name("abc.com"); + + factory_context_.secretManager().addStaticSecret(secret_config); + ClientContextConfigImpl client_context_config(tls_context, factory_context_); + + const std::string cert_p12 = + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/password_protected_certkey.p12"; + EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_p12)), + client_context_config.tlsCertificates()[0].get().pkcs12()); + const std::string password_file = + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/password_protected_password.txt"; + EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(password_file)), + client_context_config.tlsCertificates()[0].get().password()); +} + +// Validate that not supplying the incorrect passphrase for password-protected `PKCS12` +// triggers a failure loading the private key. +TEST_F(ClientContextConfigImplTest, PasswordWrongPkcs12) { + envoy::extensions::transport_sockets::tls::v3::Secret secret_config; + secret_config.set_name("abc.com"); + + auto* tls_certificate = secret_config.mutable_tls_certificate(); + const std::string pkcs12_path = TestEnvironment::substitute( + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/password_protected_certkey.p12"); + tls_certificate->mutable_pkcs12()->set_filename(pkcs12_path); + tls_certificate->mutable_password()->set_inline_string("WrongPassword"); + + envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; + tls_context.mutable_common_tls_context() + ->mutable_tls_certificate_sds_secret_configs() + ->Add() + ->set_name("abc.com"); + + factory_context_.secretManager().addStaticSecret(secret_config); + ClientContextConfigImpl client_context_config(tls_context, factory_context_); + + Event::SimulatedTimeSystem time_system; + ContextManagerImpl manager(time_system); + Stats::IsolatedStoreImpl store; + EXPECT_THROW_WITH_REGEX(manager.createSslClientContext(store, client_context_config, nullptr), + EnvoyException, absl::StrCat("Failed to load pkcs12 from ", pkcs12_path)); +} + +// Validate that not supplying a passphrase for password-protected `PKCS12` +// triggers a failure loading the private key. +TEST_F(ClientContextConfigImplTest, PasswordNotSuppliedPkcs12) { + envoy::extensions::transport_sockets::tls::v3::Secret secret_config; + secret_config.set_name("abc.com"); + + auto* tls_certificate = secret_config.mutable_tls_certificate(); + const std::string pkcs12_path = TestEnvironment::substitute( + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/password_protected_certkey.p12"); + tls_certificate->mutable_pkcs12()->set_filename(pkcs12_path); + // Don't supply the password. + + envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; + tls_context.mutable_common_tls_context() + ->mutable_tls_certificate_sds_secret_configs() + ->Add() + ->set_name("abc.com"); + + factory_context_.secretManager().addStaticSecret(secret_config); + ClientContextConfigImpl client_context_config(tls_context, factory_context_); + + Event::SimulatedTimeSystem time_system; + ContextManagerImpl manager(time_system); + Stats::IsolatedStoreImpl store; + EXPECT_THROW_WITH_REGEX(manager.createSslClientContext(store, client_context_config, nullptr), + EnvoyException, absl::StrCat("Failed to load pkcs12 from ", pkcs12_path)); +} + // Validate that not supplying a passphrase for password-protected TLS certificates // triggers a failure. TEST_F(ClientContextConfigImplTest, PasswordNotSuppliedTlsCertificates) { @@ -1769,6 +1907,62 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureBothKeyAndMethod) "Certificate configuration can't have both private_key and private_key_provider"); } +TEST_F(ServerContextConfigImplTest, Pkcs12LoadFailureBothPkcs12AndMethod) { + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; + NiceMock context_manager; + NiceMock private_key_method_manager; + auto private_key_method_provider_ptr = + std::make_shared>(); + const std::string tls_context_yaml = R"EOF( + common_tls_context: + tls_certificates: + - pkcs12: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns3_certkeychain.p12" + private_key_provider: + provider_name: mock_provider + typed_config: + "@type": type.googleapis.com/google.protobuf.Struct + value: + test_value: 100 + )EOF"; + TestUtility::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); + EXPECT_THROW_WITH_MESSAGE( + ServerContextConfigImpl server_context_config(tls_context, factory_context_), EnvoyException, + "Certificate configuration can't have both pkcs12 and private_key_provider"); +} + +TEST_F(ServerContextConfigImplTest, Pkcs12LoadFailureBothPkcs12AndKey) { + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; + const std::string tls_context_yaml = R"EOF( + common_tls_context: + tls_certificates: + - pkcs12: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns3_certkeychain.p12" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_key.pem" + )EOF"; + TestUtility::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); + EXPECT_THROW_WITH_MESSAGE( + ServerContextConfigImpl server_context_config(tls_context, factory_context_), EnvoyException, + "Certificate configuration can't have both pkcs12 and private_key"); +} + +TEST_F(ServerContextConfigImplTest, Pkcs12LoadFailureBothPkcs12AndCertChain) { + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; + const std::string tls_context_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" + pkcs12: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns3_certkeychain.p12" + )EOF"; + TestUtility::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); + EXPECT_THROW_WITH_MESSAGE( + ServerContextConfigImpl server_context_config(tls_context, factory_context_), EnvoyException, + "Certificate configuration can't have both pkcs12 and certificate_chain"); +} + // Subclass ContextImpl so we can instantiate directly from tests, despite the // constructor being protected. class TestContextImpl : public ContextImpl { diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index 4685f03829a4f..384c61cf52c34 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -1967,6 +1967,84 @@ TEST_P(SslSocketTest, CertificatesWithPassword) { testUtilV2(test_options); } +TEST_P(SslSocketTest, Pkcs12CertificatesWithPassword) { + envoy::config::listener::v3::Listener listener; + envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; + envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = + tls_context.mutable_common_tls_context()->add_tls_certificates(); + + server_cert->mutable_pkcs12()->set_filename(TestEnvironment::substitute( + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/password_protected_certkey.p12")); + server_cert->mutable_password()->set_filename(TestEnvironment::substitute( + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/password_protected_password.txt")); + envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); + server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); + server_validation_ctx->add_verify_certificate_hash( + "0000000000000000000000000000000000000000000000000000000000000000"); + server_validation_ctx->add_verify_certificate_hash(TEST_PASSWORD_PROTECTED_CERT_256_HASH); + updateFilterChain(tls_context, *filter_chain); + + envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; + envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = + client.mutable_common_tls_context()->add_tls_certificates(); + client_cert->mutable_pkcs12()->set_filename(TestEnvironment::substitute( + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/password_protected_certkey.p12")); + client_cert->mutable_password()->set_inline_string( + TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/password_protected_password.txt"))); + + TestUtilOptionsV2 test_options(listener, client, true, GetParam()); + testUtilV2(test_options.setExpectedClientCertUri("spiffe://lyft.com/test-team") + .setExpectedServerCertDigest(TEST_PASSWORD_PROTECTED_CERT_256_HASH)); + + // Works even with client renegotiation. + client.set_allow_renegotiation(true); + testUtilV2(test_options); +} + +TEST_P(SslSocketTest, Pkcs12CertificatesWithoutPassword) { + envoy::config::listener::v3::Listener listener; + envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; + envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = + tls_context.mutable_common_tls_context()->add_tls_certificates(); + + server_cert->mutable_pkcs12()->set_filename(TestEnvironment::substitute( + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/san_dns3_certkeychain.p12")); + envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); + server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); + server_validation_ctx->add_verify_certificate_hash( + "0000000000000000000000000000000000000000000000000000000000000000"); + server_validation_ctx->add_verify_certificate_hash(TEST_SAN_DNS3_CERT_256_HASH); + updateFilterChain(tls_context, *filter_chain); + + envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; + envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = + client.mutable_common_tls_context()->add_tls_certificates(); + client_cert->mutable_pkcs12()->set_filename(TestEnvironment::substitute( + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/san_dns3_certkeychain.p12")); + + TestUtilOptionsV2 test_options(listener, client, true, GetParam()); + testUtilV2(test_options.setExpectedServerCertDigest(TEST_SAN_DNS3_CERT_256_HASH)); + + // Works even with client renegotiation. + client.set_allow_renegotiation(true); + testUtilV2(test_options); +} + TEST_P(SslSocketTest, ClientCertificateSpkiVerification) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); diff --git a/test/extensions/transport_sockets/tls/test_data/README.md b/test/extensions/transport_sockets/tls/test_data/README.md index ab49d3da8cb27..6b16516074cf5 100644 --- a/test/extensions/transport_sockets/tls/test_data/README.md +++ b/test/extensions/transport_sockets/tls/test_data/README.md @@ -31,7 +31,7 @@ There are 15 identities: field of URI type. *san_uri_key.pem* is its private key. - **Password-protected**: The password-protected certificate *password_protected_cert.pem*, using the config *san_uri_cert.cfg*. *password_protected_key.pem* is - its private key encrypted using the password supplied in *password_protectted_password.txt*. + its private key encrypted using the password supplied in *password_protected_password.txt*. - **Self-signed**: The self-signed certificate *selfsigned_cert.pem*, using the config *selfsigned_cert.cfg*. *selfsigned_key.pem* is its private key. - **Self-signed RSA 1024**: The self-signed certificate *selfsigned_rsa_1024_cert.pem*, diff --git a/test/extensions/transport_sockets/tls/test_data/certs.sh b/test/extensions/transport_sockets/tls/test_data/certs.sh index fc45baa58ca39..aee99bc2d9361 100755 --- a/test/extensions/transport_sockets/tls/test_data/certs.sh +++ b/test/extensions/transport_sockets/tls/test_data/certs.sh @@ -139,6 +139,9 @@ rm -f san_dns3_cert.cfg # Concatenate san_dns3_cert.pem and Test Intermediate CA (intermediate_ca_cert.pem) to create valid certificate chain. cat san_dns3_cert.pem intermediate_ca_cert.pem > san_dns3_chain.pem +# Generate san_dns3_certkeychain.p12 with no password. +openssl pkcs12 -export -out san_dns3_certkeychain.p12 -inkey san_dns3_key.pem -in san_dns3_cert.pem -certfile san_dns3_chain.pem -keypbe NONE -certpbe NONE -nomaciter -passout pass: + # Generate san_dns4_cert.pm (signed by intermediate_ca_cert.pem). cp -f san_dns_cert.cfg san_dns4_cert.cfg generate_rsa_key san_dns4 @@ -174,6 +177,9 @@ generate_rsa_key password_protected "" "p4ssw0rd" generate_x509_cert password_protected ca rm -f password_protected_cert.cfg +# Generate password_protected_certkey.p12. +openssl pkcs12 -export -out password_protected_certkey.p12 -inkey password_protected_key.pem -in password_protected_cert.pem -passout "file:password_protected_password.txt" -passin "pass:p4ssw0rd" + # Generate selfsigned*_cert.pem. generate_rsa_key selfsigned generate_selfsigned_x509_cert selfsigned @@ -185,6 +191,9 @@ generate_rsa_key selfsigned_rsa_1024 1024 generate_selfsigned_x509_cert selfsigned_rsa_1024 rm -f selfsigned_rsa_1024_cert.cfg +# Generate selfsigned_rsa_1024_certkey.p12 with no password. +openssl pkcs12 -export -out selfsigned_rsa_1024_certkey.p12 -inkey selfsigned_rsa_1024_key.pem -in selfsigned_rsa_1024_cert.pem -keypbe NONE -certpbe NONE -nomaciter -passout pass: + # Generate selfsigned_rsa_3072.pem cp -f selfsigned_cert.cfg selfsigned_rsa_3072_cert.cfg generate_rsa_key selfsigned_rsa_3072 3072 @@ -210,6 +219,9 @@ generate_ecdsa_key selfsigned_ecdsa_p384 secp384r1 generate_selfsigned_x509_cert selfsigned_ecdsa_p384 rm -f selfsigned_ecdsa_p384_cert.cfg +# Generate selfsigned_ecdsa_p384_certkey.p12 with no password. +openssl pkcs12 -export -out selfsigned_ecdsa_p384_certkey.p12 -inkey selfsigned_ecdsa_p384_key.pem -in selfsigned_ecdsa_p384_cert.pem -keypbe NONE -certpbe NONE -nomaciter -passout pass: + # Generate long_validity_cert.pem as a self-signed, with expiry that exceeds 32bit time_t. cp -f selfsigned_cert.cfg long_validity_cert.cfg generate_rsa_key long_validity diff --git a/test/extensions/transport_sockets/tls/test_data/password_protected_certkey.p12 b/test/extensions/transport_sockets/tls/test_data/password_protected_certkey.p12 new file mode 100644 index 0000000000000000000000000000000000000000..3c6e231b3e81227716765f32f1e2933c656de13d GIT binary patch literal 2669 zcmY+^c{CJ^8U}E~jAe|au_f6vF$`nP_l2=lma&VGZ3<=Vk!7ONSca@gB-_}@USaIZ zh%k}J$4G{doh(`7_MLO@{qFtaJ?}ZsbI$YU4@m&y7y(R30$72W4IXU}y~hDy0gwn_ zF(3ha{&##9NdON0Q!$eWz>eQh3nPHx_pAG-0U*yaga5q%0Wc#ufvhzNffgmZZ@Lh?>Donj?E?P<<;ic8+)idXdN|YTFr8&yj{zCxJ2q* z_*1dGb8N4Xb)>Dj?r2k69^tWL-Ov=Bxk4AfC&dKJ4|fLTx7{$SnJ~4AIF#;_Vs`=9 zPcrSHwse=sX3pu7v@yqGxLyn0Cv~JS?W&MY*4USu^g8*E?H{f$Fa$}7{P_%+a!3yG z3itOg8F&!>N(2WAjD5%Lskk;bS>%%{(-wPb=dx>7F5?8Oe$kKNvyR1zU}3mp_suFxG$-KHdR%1`j{!QM`{$;cGz8HYqwO1nrB#1G*d5A0_>IGs3G z_@%p^sP|HkQ5X36~CLHC;@o!DWQGr@unv-%&wO zPMu~|ELI_tYudg1y!8}peWa3R=v3y-Es-IP=Hm;!smXS`t{Zop#lkv`9+G+7X~gd0 zVK(d7*UFDac&=$}fwaw`gdCXdY4%*|JI)0>H*;t*1E&=_YE7Bd*Wy`%y1(1w+oS=9P1~ z*eH{?B$Q6)q74E?)vx&D3$j+K;)16LQ zR|u&3@sc1d`LJjY&xEFL93WB{jkY1`n_^)B!2V%8z}cr`r@A*l`_zR2D{~C8*9&v8sDGt_kY4w z+}p;u63H=%DE}E<7~W8n^6)~0ULsh7p%Hixo;twaKAJ;q;W?uoJMv*R82-$3sj~6x zufT23adn~e!1MMGUzKCZwNT$!0<}kL9(v`9R*_CQ3=7X;6K)Fzko&2RU z9Pros6VvZCz6kA|C}<_nG8g>N-m>4a7}c+x16{-mHcdaD+#pC1Fa2~M5!ZeGQone=R_7fcc)(uy{x|2`Nq`+pb%0~mjMxBeTt z<^R)->2KX8WEYzI#W(4H>joqM-&Z)|?Ve?A)8)~+)0P{{??&^i@{9sXB9C4omG#v> zi}X+C*W`SZOXC=ph{O)ihd4n9fV+u;k6+Dq!G5AJo6u6T;{4<>uA6QQKUo#|marMwn1R>&nC}HmX1%9=Kin4=9wF$TT z3{$#&@WzZ;Aoy$ZKIB%uG;vAS5|R7(!ON_Dy&7tIb|fZ!X9g8KoTgPb;-Hseo_*0@ z!$+2%aVOftelXFdk^e&ZU8ag*;aPYw^daWSp#XCMb@#*tk&Av5;Tn(4zR3Md(8FtN z4qeTtlI_0Je0Y4vR)0$bFs{()p!FDQ+FB%EZF@wwyD>5;k=WM|#S00(x&KIxo}603 zvXl&|4C|XSvbB2JSI*@`eI(^(9%dPfm*~^J+uytkg$+wNV6T=I0bK5JjZnmN+zzOz zFjRDnBV`|@j9Y{q`xKni?-okj|Io5&=S(}Ao-RV8S36eT)=3Do5_3X)IV``Hnnwk9 znoiizk@ng}pgeX>i?Ikw;pY388Kd!5XJdt9TB<{iac5KiKb=~K8&Ot5BYOzWZO^7tqJ&4e}5wJ<|id|NKY zz8qF3Kc%v=q^dxygog0P@IUxVNEF#kU3RsgqbDw^gFef!S&2mUNjt*c#|^*^=qg8IGV-< zy<9p1oUbuyz7CYgZ-q5B7(d6O=2Kdet4uU`Z8%$WR#fOO5_e1qS37|0hCyB|>QQiS z%)oJ0MIAFKY1oSM#T-X7axKs7!-kMB@@6cZV;&{r#b9pdtoS`5`ZS6gy3N*YNm!^n zE3ngaS){|jIwB4x2cLmA%CY|V2GU#uUn1hRq(Uq*Cj-VAiK(c#>Nxa?Omn#p7SnDiN+Bixt0Nnq zB0rGVqPctaZoYf6JvlB;%Ah7&?eeh_QV}VRWMc-&axyWV12F)3xzcLm6amnm{=ns> ZmsQq?E&bh8AS+a`hdSyzr2Ds${{pH|2yp-a literal 0 HcmV?d00001 diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns3_certkeychain.p12 b/test/extensions/transport_sockets/tls/test_data/san_dns3_certkeychain.p12 new file mode 100644 index 0000000000000000000000000000000000000000..3210780bd5536897c54019e6757a109cf3cefb97 GIT binary patch literal 4703 zcmeI0do)yg8^`CeXE0_MjN525IFlilJt9ek$fZ&oBt*oJT++BiA;t;0i*m{(lzjV2X-DvJl9`&i*Nd9RZlwXOKJqG68?I;P0!j z7&tPQ3FtziHX8H0C?Emt1tKFGUKj7HLIi1ko8F6E6R4 z)8Hr=k_i`vK{^J(gu`JU=Cqx?N0w;X^YCWkNkll6XW^{qbe!@q=?xQO zo86dmrDmenPF_?(ZE8Xw;V^h!WHuOi5yV3MBZrtOC%Y^_5fRLvU62^bH z>$>o|WU5qiSOGic<~+ETw_E{`0wp2{g-?zXp59c8PLRwN8A)z%P7egjzdb?d+CwJ@ zhbeZGGB@k3m6J7D6Zea$X^;5?Y4cX{5WtP?lflx#S2vz!H9Mwu>dVb^f4VK7vK!qa zk}k1xOH*o&26JQdL}&`DAcx@-)gm-UnYPdMh#Z{A;I7fs8VyeUA*qyj=iH(6B5n8W zp;jCdsziWx#!d}F%9>kLSI=4rucAlZ!BUw?{pG=1<&|8TEhQST?U+^B?CdKo5@w5fQfJnPm4 z4}H>v%nudt%$Y`~+{NeFK{b!uJg%NT&ng@U&Kc>T=N)X*^JH~B4_1z8GiU8=A&)Q7 zD4@buGY}A`LA^D#-4iClML%%{&_VWD)o+;xZ_7YVvirO3{1?6PyX}0po&O`-f&2vj z7e7I+K&bNh&lrQ80GP-(kUj!35fA^sn3+){1J>@^AOGU#V9-AT2zXEk`p!TU!?y|u ze*;|pH}L8|Ab@F3|?-Od>nF)7qZ!}|0~Hj3W;lu4U?-D+G8+ol4RJwyY9E5!Yvzy5SYrC1jGEsVB0b8t-|;1 zy5CZ7v?R8FK^S+?Z505&01Q8fhByWRhpnDDNtg#U5Xr#N-=N69C5>-Uq_bX7VdUP` zS&h1b*7bV9o5XC8eSf*St0=NV#Oy^)Q{|e+oILe+=TEu?Ub|x+_f}N^P-4j9SW^k? zM?k^!o2|q71=q|RgHntn%JfCFbT2z3vz?5sk-aj#_Z~Tseyx~R(^Z`?mh$)UCsrnz zidTlu7kS$!-m-7N1{nRE%})Gub$07V+C+{aAeqF4)sVxP7eYaZS3C zJJ^XA!drQl-3?6g$VpjqsXy6Bdfj|xKv@(nsoV52Vt|~{{p@UqWeDlP`e-Q@ZR$Ez zsM!z^kJYgSUl#i%s?$=aO8x`L2^$njn!L_>`PqJlWX4PZGm%l z4Y@za_kOTx*@c;oeGn>B9WmX7Yfo1lr-05)ff)J50Q4}?^hJ~v&3()Wc7&+NNm9)s zE52jPDzo{EEkvgr5*9$qnVIwQ2{sHMvms})ekZ3#Dm+flC`YbdnYBB8GXY17(K8k;qnxJ6%ZeSMf z1sI~7ZC5(7%7vHOg?Fax+wbl3Ce)!-=hirt@N{jg`>p*0WjRlMuQd9Vh=+%qh^DzM z8g+67+7oT~TTwmv7FLT4g+utL9ZANVF?hO0m44@A{hxvwbA?qjIAd}2_#P{Lc_|m=7+adt+pY`e^wjGS$^2} zCa3kuwh8RPuAgNuS}l)s*Sg5I>@$76@7Art zlb)?avAtFH>xC^@k$Ye&V;f4#mbm5%r&Ltj`JXE+Jd~M<2~onu_3Q02*}E}FKu6xV zs>aE3co}_eu4l*2pN~>IrJ*+X>EMMN8no)vPDdFy)nS6Sa2~GX_-Jp7BjflqQ#5D- z4(boi^p2C@y5J*i(Nps3R$j@&L*kQ_&5j&u?l>j7*DiCZ3!z;2%VTcrP)S~FOkRB-RbZ#5?Yk0QfJvc#2(1d7k|*5Ec_Z literal 0 HcmV?d00001 diff --git a/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p384_certkey.p12 b/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p384_certkey.p12 new file mode 100644 index 0000000000000000000000000000000000000000..53dc6afa5627ab1d77dd706ba2a0cd910d80080a GIT binary patch literal 1184 zcmXqLVwuCl$ZXKWlE%iV)#lOmotKfFaX}MHI7<^tFi_avpo!T5MT*&wrHNS=D69#@ zs%+d)T|8Wj%nO>B-WoJ9y=3Ep3UZ18&0@OA(!_KXD1H%$!xu0!F)}fUXjdP#pFTg& z>W!!U7Ma&Ax!P&VmK$)faR4=*vt?msGN>}-HsEAq4rO5zW(o~96gCh9aX5ImoD*|0 z)AEb*G7}BO3`9VJ>^!`|iFpccMTvRInZ?QZhP(#cAaO1p7N5$r5<^J?aS)fAM*zfC zaLr54%u7uz%FIhQ6fqD2Nip+qholylC7};308(A2Xn3EV-s(-(gOV?pamJPlmw&n2U0}-okd3-KNd7J#= z+HWS6>h2F!6Ju%`R%N~X*-*qQXYud#eEsy-7b|PrlIHEr;??BeGxM&p+DnPb2C<-b zTE7C;OiE9_rJ%yfcx|`r>Dp`m_b+aoYtT5$fCm^_vcimv|5=y}7{H+{%g+LgF4hMI zvLGo{7EqjUXtOc0va&NX!C8z3av*6z7I6d7CgEamFdOQnRwU*Y}p=9)_X+kCM(@^VEGX6)HM2D>9IFLC+GfK*~C%4@VVWdoa(6_zITo=88U1VxO!t< zM%Jm%SHidsKjsZvmGZZs?%ii?@oxr)Jufs^7cP0WNP6$zA5XKy^XipLKO3qVD8n-c zr>G&&^%2_)n3K5GU8`DLgLsNJzbiFZcEzBHksYoKn7tZ*vNV1(X#8x@_yIZp03~)B zG;U*JWWbDLmd1GojkB2;8CeV#ZPZ|``@z3-)rYKES^n%C)*|-}Z$0!aWtW`M)BZ8% z$gwpO($2@`Pjj=6HFdB3w0>a8)p)#lOmotKfFaX}MXCQB1rDo{AlpozH)MT)tarHQ#5C|nH0 z`E1-!T|8Wj%nO>B&4Gf(Y+O)5PBEZa%*sFw@<4HEgC?ey1u6gO1 zd8w&InR)4kA_hVrDP|t-kksN5h2YepveY62IdNVi0|N^q14A=Yb5nyTAlC?qJD_fB zoR1t*jI0dIjlB#8jh#%5jSLUn3VL_Hu4^bdU>4S@?wfa3)#jebw4hTS(HBh%r9RKR zHg~BWZ}h``C(;@^O{8m=Rtf5rcQ-xz`Oluu^L3t3nJLdH2FWdl91B>V#w5nCd0Xmw zYRAo&SxsjinY;}=lYgi0N?PI(%V_7d|JEf7W+*i!RV4_nY5Lu@>d%Lii%eCEnV1JmX^-CE?3>4z2wLes zIrS{`QQFN_dA@Ob%|oyMYB+M$D&XJ$*8d!S48oCL58k7PhBp2XY=$+pV%y#RWpZJWrzNWO4U5@mR-Z(jzCF6MAg~alMpCTpJY8cGn`BLV-A<1CZ8{r32 zt|t3@%j3`zWO8Kqv0}%GJTTym-?~%0|OD_~$G~w5-zc}ITCw{fdOm}uzM&3&n`IggDXYpdO z?$wIsYg4b>-`C0ta>bd1i0&l!Em vi+NUUK4vB&, ocspStaple, (), (const)); diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index ce067b5c94f8e..20ebff34f2a4e 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -255,6 +255,7 @@ PEM PERF PGV PID +PKCS12 PKTINFO PNG Pointwise From c26471486d1618cdf5fda79add8b91785e1085a8 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Mon, 15 Nov 2021 11:43:24 -0800 Subject: [PATCH 082/110] test: disable QuicHttpIntegrationTest.AdminDrainDrainsListeners (#19007) quic: Disable flaky test QuicHttpIntegrationTest.AdminDrainDrainsListeners while we work on a fix to the underlying issue. Signed-off-by: Ryan Hamilton rch@google.com Risk Level: Low Testing: N/A Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A Signed-off-by: Ryan Hamilton --- test/integration/quic_http_integration_test.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index 43f00246bd0aa..8c0bad7851549 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -663,7 +663,8 @@ TEST_P(QuicHttpIntegrationTest, PortMigrationFailureOnPathDegrading) { EXPECT_EQ(1024u * 2, upstream_request_->bodyLength()); } -TEST_P(QuicHttpIntegrationTest, AdminDrainDrainsListeners) { +// TODO(#19006): Track down the flakiness and re-enable. +TEST_P(QuicHttpIntegrationTest, DISABLED_AdminDrainDrainsListeners) { testAdminDrain(Http::CodecType::HTTP1); } From 3b27d1d34f282be8a35d23b0417d6c90a6804ce2 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Mon, 15 Nov 2021 15:28:24 -0500 Subject: [PATCH 083/110] http: more 1xx renames (#18974) Risk Level: n/a Testing: n/a part of #18844 Signed-off-by: Alyssa Wilk --- envoy/http/filter.h | 2 +- source/common/http/async_client_impl.h | 2 +- source/common/http/conn_manager_impl.h | 12 ++++++------ source/common/http/filter_manager.cc | 10 +++++----- source/common/http/filter_manager.h | 12 ++++++------ test/integration/http_integration.cc | 10 +++++----- test/integration/integration_stream_decoder.h | 2 +- test/mocks/http/mocks.cc | 4 ++-- test/mocks/http/mocks.h | 16 ++++++++-------- 9 files changed, 35 insertions(+), 35 deletions(-) diff --git a/envoy/http/filter.h b/envoy/http/filter.h index d6372cee98bd9..c8fadf77f983a 100644 --- a/envoy/http/filter.h +++ b/envoy/http/filter.h @@ -466,7 +466,7 @@ class StreamDecoderFilterCallbacks : public virtual StreamFilterCallbacks { * Returns the headers provided to encode1xxHeaders. Returns absl::nullopt if * no headers have been provided yet. */ - virtual ResponseHeaderMapOptRef continueHeaders() const PURE; + virtual ResponseHeaderMapOptRef informationalHeaders() const PURE; /** * Called with headers to be encoded, optionally indicating end of stream. diff --git a/source/common/http/async_client_impl.h b/source/common/http/async_client_impl.h index f30abb5329860..1e7c7c8ce44d3 100644 --- a/source/common/http/async_client_impl.h +++ b/source/common/http/async_client_impl.h @@ -378,7 +378,7 @@ class AsyncStreamImpl : public AsyncClient::Stream, } // The async client won't pause if sending 1xx headers so simply swallow any. void encode1xxHeaders(ResponseHeaderMapPtr&&) override {} - ResponseHeaderMapOptRef continueHeaders() const override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } + ResponseHeaderMapOptRef informationalHeaders() const override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } void encodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream, absl::string_view details) override; ResponseHeaderMapOptRef responseHeaders() const override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h index 9d75c753453a8..11e59f4d68262 100644 --- a/source/common/http/conn_manager_impl.h +++ b/source/common/http/conn_manager_impl.h @@ -222,9 +222,9 @@ class ConnectionManagerImpl : Logger::Loggable, ASSERT(!request_trailers_); request_trailers_ = std::move(request_trailers); } - void setContinueHeaders(Http::ResponseHeaderMapPtr&& continue_headers) override { - ASSERT(!continue_headers_); - continue_headers_ = std::move(continue_headers); + void setInformationalHeaders(Http::ResponseHeaderMapPtr&& informational_headers) override { + ASSERT(!informational_headers_); + informational_headers_ = std::move(informational_headers); } void setResponseHeaders(Http::ResponseHeaderMapPtr&& response_headers) override { // We'll overwrite the headers in the case where we fail the stream after upstream headers @@ -242,8 +242,8 @@ class ConnectionManagerImpl : Logger::Loggable, Http::RequestTrailerMapOptRef requestTrailers() override { return makeOptRefFromPtr(request_trailers_.get()); } - Http::ResponseHeaderMapOptRef continueHeaders() override { - return makeOptRefFromPtr(continue_headers_.get()); + Http::ResponseHeaderMapOptRef informationalHeaders() override { + return makeOptRefFromPtr(informational_headers_.get()); } Http::ResponseHeaderMapOptRef responseHeaders() override { return makeOptRefFromPtr(response_headers_.get()); @@ -354,7 +354,7 @@ class ConnectionManagerImpl : Logger::Loggable, RequestHeaderMapPtr request_headers_; RequestTrailerMapPtr request_trailers_; - ResponseHeaderMapPtr continue_headers_; + ResponseHeaderMapPtr informational_headers_; ResponseHeaderMapPtr response_headers_; ResponseTrailerMapPtr response_trailers_; diff --git a/source/common/http/filter_manager.cc b/source/common/http/filter_manager.cc index a0787c20df311..c49c17829323a 100644 --- a/source/common/http/filter_manager.cc +++ b/source/common/http/filter_manager.cc @@ -395,13 +395,13 @@ void ActiveStreamDecoderFilter::encode1xxHeaders(ResponseHeaderMapPtr&& headers) // here. This avoids the potential situation where Envoy strips Expect: 100-Continue and sends a // 100-Continue, then proxies a duplicate 100 Continue from upstream. if (parent_.proxy_100_continue_) { - parent_.filter_manager_callbacks_.setContinueHeaders(std::move(headers)); - parent_.encode1xxHeaders(nullptr, *parent_.filter_manager_callbacks_.continueHeaders()); + parent_.filter_manager_callbacks_.setInformationalHeaders(std::move(headers)); + parent_.encode1xxHeaders(nullptr, *parent_.filter_manager_callbacks_.informationalHeaders()); } } -ResponseHeaderMapOptRef ActiveStreamDecoderFilter::continueHeaders() const { - return parent_.filter_manager_callbacks_.continueHeaders(); +ResponseHeaderMapOptRef ActiveStreamDecoderFilter::informationalHeaders() const { + return parent_.filter_manager_callbacks_.informationalHeaders(); } void ActiveStreamDecoderFilter::encodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream, @@ -1528,7 +1528,7 @@ bool ActiveStreamEncoderFilter::has1xxheaders() { return parent_.state_.has_1xx_headers_ && !continued_1xx_headers_; } void ActiveStreamEncoderFilter::do1xxHeaders() { - parent_.encode1xxHeaders(this, *parent_.filter_manager_callbacks_.continueHeaders()); + parent_.encode1xxHeaders(this, *parent_.filter_manager_callbacks_.informationalHeaders()); } void ActiveStreamEncoderFilter::doHeaders(bool end_stream) { parent_.encodeHeaders(this, *parent_.filter_manager_callbacks_.responseHeaders(), end_stream); diff --git a/source/common/http/filter_manager.h b/source/common/http/filter_manager.h index 2ebb1d194c578..d786df34e41ec 100644 --- a/source/common/http/filter_manager.h +++ b/source/common/http/filter_manager.h @@ -260,7 +260,7 @@ struct ActiveStreamDecoderFilter : public ActiveStreamFilterBase, const absl::optional grpc_status, absl::string_view details) override; void encode1xxHeaders(ResponseHeaderMapPtr&& headers) override; - ResponseHeaderMapOptRef continueHeaders() const override; + ResponseHeaderMapOptRef informationalHeaders() const override; void encodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream, absl::string_view details) override; ResponseHeaderMapOptRef responseHeaders() const override; @@ -407,10 +407,10 @@ class FilterManagerCallbacks { virtual void setRequestTrailers(RequestTrailerMapPtr&& request_trailers) PURE; /** - * Passes ownership of received continue headers to the parent. This may be called multiple times - * in the case of multiple upstream calls. + * Passes ownership of received informational headers to the parent. This may be called multiple + * times in the case of multiple upstream calls. */ - virtual void setContinueHeaders(ResponseHeaderMapPtr&& response_headers) PURE; + virtual void setInformationalHeaders(ResponseHeaderMapPtr&& response_headers) PURE; /** * Passes ownership of received response headers to the parent. This may be called multiple times @@ -443,9 +443,9 @@ class FilterManagerCallbacks { virtual RequestTrailerMapOptRef requestTrailers() PURE; /** - * Retrieves a pointer to the continue headers set via the call to setContinueHeaders. + * Retrieves a pointer to the continue headers set via the call to setInformationalHeaders. */ - virtual ResponseHeaderMapOptRef continueHeaders() PURE; + virtual ResponseHeaderMapOptRef informationalHeaders() PURE; /** * Retrieves a pointer to the response headers set via the last call to setResponseHeaders. diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index ab8b5bd93049f..814744f5ea4d4 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -1006,9 +1006,9 @@ void HttpIntegrationTest::testEnvoyHandling1xx(bool additional_continue_from_ups ASSERT_TRUE(response->waitForEndStream()); ASSERT_TRUE(response->complete()); - ASSERT(response->continueHeaders() != nullptr); - EXPECT_EQ("100", response->continueHeaders()->getStatusValue()); - EXPECT_EQ(nullptr, response->continueHeaders()->Via()); + ASSERT(response->informationalHeaders() != nullptr); + EXPECT_EQ("100", response->informationalHeaders()->getStatusValue()); + EXPECT_EQ(nullptr, response->informationalHeaders()->Via()); EXPECT_EQ("200", response->headers().getStatusValue()); if (via.empty()) { EXPECT_EQ(nullptr, response->headers().Via()); @@ -1073,8 +1073,8 @@ void HttpIntegrationTest::testEnvoyProxying1xx(bool continue_before_upstream_com upstream_request_->encodeHeaders(default_response_headers_, true); ASSERT_TRUE(response->waitForEndStream()); EXPECT_TRUE(response->complete()); - ASSERT(response->continueHeaders() != nullptr); - EXPECT_EQ("100", response->continueHeaders()->getStatusValue()); + ASSERT(response->informationalHeaders() != nullptr); + EXPECT_EQ("100", response->informationalHeaders()->getStatusValue()); EXPECT_EQ("200", response->headers().getStatusValue()); } diff --git a/test/integration/integration_stream_decoder.h b/test/integration/integration_stream_decoder.h index 210a7dfa488c4..dbdacf6930dbf 100644 --- a/test/integration/integration_stream_decoder.h +++ b/test/integration/integration_stream_decoder.h @@ -29,7 +29,7 @@ class IntegrationStreamDecoder : public Http::ResponseDecoder, public Http::Stre bool complete() { return saw_end_stream_; } bool reset() { return saw_reset_; } Http::StreamResetReason resetReason() { return reset_reason_; } - const Http::ResponseHeaderMap* continueHeaders() { return continue_headers_.get(); } + const Http::ResponseHeaderMap* informationalHeaders() { return continue_headers_.get(); } const Http::ResponseHeaderMap& headers() { return *headers_; } const Http::ResponseTrailerMapPtr& trailers() { return trailers_; } const Http::MetadataMap& metadataMap() { return *metadata_map_; } diff --git a/test/mocks/http/mocks.cc b/test/mocks/http/mocks.cc index ae55011dd2112..959e1bb07890d 100644 --- a/test/mocks/http/mocks.cc +++ b/test/mocks/http/mocks.cc @@ -23,8 +23,8 @@ MockServerConnectionCallbacks::MockServerConnectionCallbacks() = default; MockServerConnectionCallbacks::~MockServerConnectionCallbacks() = default; MockFilterManagerCallbacks::MockFilterManagerCallbacks() { - ON_CALL(*this, continueHeaders()).WillByDefault(Invoke([this]() -> ResponseHeaderMapOptRef { - return makeOptRefFromPtr(continue_headers_.get()); + ON_CALL(*this, informationalHeaders()).WillByDefault(Invoke([this]() -> ResponseHeaderMapOptRef { + return makeOptRefFromPtr(informational_headers_.get()); })); ON_CALL(*this, responseHeaders()).WillByDefault(Invoke([this]() -> ResponseHeaderMapOptRef { return makeOptRefFromPtr(response_headers_.get()); diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index 1ac03a1429824..58b20487a421e 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -65,10 +65,10 @@ class MockFilterManagerCallbacks : public FilterManagerCallbacks { MOCK_METHOD(void, encodeMetadata, (MetadataMapVector&)); MOCK_METHOD(void, chargeStats, (const ResponseHeaderMap&)); MOCK_METHOD(void, setRequestTrailers, (RequestTrailerMapPtr &&)); - MOCK_METHOD(void, setContinueHeaders_, (ResponseHeaderMap&)); - void setContinueHeaders(ResponseHeaderMapPtr&& continue_headers) override { - continue_headers_ = std::move(continue_headers); - setContinueHeaders_(*continue_headers_); + MOCK_METHOD(void, setInformationalHeaders_, (ResponseHeaderMap&)); + void setInformationalHeaders(ResponseHeaderMapPtr&& informational_headers) override { + informational_headers_ = std::move(informational_headers); + setInformationalHeaders_(*informational_headers_); } MOCK_METHOD(void, setResponseHeaders_, (ResponseHeaderMap&)); void setResponseHeaders(ResponseHeaderMapPtr&& response_headers) override { @@ -82,7 +82,7 @@ class MockFilterManagerCallbacks : public FilterManagerCallbacks { } MOCK_METHOD(RequestHeaderMapOptRef, requestHeaders, ()); MOCK_METHOD(RequestTrailerMapOptRef, requestTrailers, ()); - MOCK_METHOD(ResponseHeaderMapOptRef, continueHeaders, ()); + MOCK_METHOD(ResponseHeaderMapOptRef, informationalHeaders, ()); MOCK_METHOD(ResponseHeaderMapOptRef, responseHeaders, ()); MOCK_METHOD(ResponseTrailerMapOptRef, responseTrailers, ()); MOCK_METHOD(void, endStream, ()); @@ -110,7 +110,7 @@ class MockFilterManagerCallbacks : public FilterManagerCallbacks { MOCK_METHOD(void, restoreContextOnContinue, (ScopeTrackedObjectStack&)); MOCK_METHOD(bool, enableInternalRedirectsWithBody, (), (const)); - ResponseHeaderMapPtr continue_headers_; + ResponseHeaderMapPtr informational_headers_; ResponseHeaderMapPtr response_headers_; ResponseTrailerMapPtr response_trailers_; }; @@ -233,7 +233,7 @@ class MockStreamDecoderFilterCallbacks : public StreamDecoderFilterCallbacks, absl::string_view details); void encode1xxHeaders(ResponseHeaderMapPtr&& headers) override { encode1xxHeaders_(*headers); } - MOCK_METHOD(ResponseHeaderMapOptRef, continueHeaders, (), (const)); + MOCK_METHOD(ResponseHeaderMapOptRef, informationalHeaders, (), (const)); void encodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream, absl::string_view details) override { stream_info_.setResponseCodeDetails(details); @@ -402,7 +402,7 @@ class MockStreamFilter : public StreamFilter { // Http::MockStreamEncoderFilter MOCK_METHOD(FilterHeadersStatus, encode1xxHeaders, (ResponseHeaderMap & headers)); - MOCK_METHOD(ResponseHeaderMapOptRef, continueHeaders, (), (const)); + MOCK_METHOD(ResponseHeaderMapOptRef, informationalHeaders, (), (const)); MOCK_METHOD(FilterHeadersStatus, encodeHeaders, (ResponseHeaderMap & headers, bool end_stream)); MOCK_METHOD(ResponseHeaderMapOptRef, responseHeaders, (), (const)); MOCK_METHOD(FilterDataStatus, encodeData, (Buffer::Instance & data, bool end_stream)); From e39bc55b107cc3e9f100dcd043738c45d6b7f83c Mon Sep 17 00:00:00 2001 From: Piotr Sikora Date: Mon, 15 Nov 2021 21:28:11 -0800 Subject: [PATCH 084/110] wasm: update V8 to v9.6.180.12. (#18994) * wasm: update V8 to v9.6.180.12. Signed-off-by: Piotr Sikora * review: use_lld. Signed-off-by: Piotr Sikora --- bazel/external/wee8.genrule_cmd | 3 ++- bazel/external/wee8.patch | 12 ------------ bazel/repository_locations.bzl | 6 +++--- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/bazel/external/wee8.genrule_cmd b/bazel/external/wee8.genrule_cmd index 505f33085a23d..ae858cc54487e 100644 --- a/bazel/external/wee8.genrule_cmd +++ b/bazel/external/wee8.genrule_cmd @@ -79,8 +79,9 @@ fi # Clang or not Clang, that is the question. WEE8_BUILD_ARGS+=" is_clang=$$IS_CLANG" +WEE8_BUILD_ARGS+=" use_lld=$$IS_CLANG" # Hack to disable bleeding-edge compiler flags. -WEE8_BUILD_ARGS+=" use_xcode_clang=true" +WEE8_BUILD_ARGS+=" use_xcode_clang=$$IS_CLANG" # Use local toolchain. WEE8_BUILD_ARGS+=" custom_toolchain=\"//build/toolchain/linux/unbundle:default\"" # Use local stdlibc++ / libc++. diff --git a/bazel/external/wee8.patch b/bazel/external/wee8.patch index c15f5d867a3b2..c7566fe53e2b2 100644 --- a/bazel/external/wee8.patch +++ b/bazel/external/wee8.patch @@ -1,5 +1,4 @@ # 1. Fix linking with unbundled toolchain on macOS. -# 2. Increase VSZ limit to 64 TiB (allows us to start up to 6,553 VMs). # 3. Fix linking with MSAN. # 4. Fix build with LLVM/Clang versions older than 13.0.0. --- build/toolchain/gcc_toolchain.gni @@ -22,17 +21,6 @@ # the "--start-group .. --end-group" feature isn't available on the aix ld. start_group_flag = "-Wl,--start-group" end_group_flag = "-Wl,--end-group " ---- src/objects/backing-store.cc -+++ src/objects/backing-store.cc -@@ -47,7 +47,7 @@ constexpr size_t kAddressSpaceLimit = 0x8000000000L; // 512 GiB - // RISC-V64 has a user space of 256GB on the Sv39 scheme. - constexpr size_t kAddressSpaceLimit = 0x4000000000L; // 256 GiB - #elif V8_TARGET_ARCH_64_BIT --constexpr size_t kAddressSpaceLimit = 0x10100000000L; // 1 TiB + 4 GiB -+constexpr size_t kAddressSpaceLimit = 0x400100000000L; // 64 TiB + 4 GiB - #else - constexpr size_t kAddressSpaceLimit = 0xC0000000; // 3 GiB - #endif --- build/config/sanitizers/sanitizers.gni +++ build/config/sanitizers/sanitizers.gni @@ -158,7 +158,7 @@ if (!is_a_target_toolchain) { diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 2112fe2c3831a..0e8c2e5ec66bd 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -823,15 +823,15 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "V8", project_desc = "Google’s open source high-performance JavaScript and WebAssembly engine, written in C++", project_url = "https://v8.dev", - version = "9.5.172.21", + version = "9.6.180.12", # This archive was created using https://storage.googleapis.com/envoyproxy-wee8/wee8-archive.sh # and contains complete checkout of V8 with all dependencies necessary to build wee8. - sha256 = "cd19ab73840031b65f246ebf35a59b224fb043656d772b675b72d12215ec2fd0", + sha256 = "b6def6d8c859807e20b1c1c280dd9f30e153f6938b07e1fff38ab26648a7c4f6", urls = ["https://storage.googleapis.com/envoyproxy-wee8/wee8-{version}.tar.gz"], strip_prefix = "wee8", use_category = ["dataplane_ext"], extensions = ["envoy.wasm.runtime.v8"], - release_date = "2021-10-12", + release_date = "2021-11-09", cpe = "cpe:2.3:a:google:v8:*", ), com_github_google_quiche = dict( From 212c71a1d5618ae30b2897603e31ffbfe3f7839f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Nov 2021 05:53:48 +0000 Subject: [PATCH 085/110] build(deps): bump setuptools from 58.5.3 to 59.0.1 in /tools/base (#19003) Bumps [setuptools](https://github.com/pypa/setuptools) from 58.5.3 to 59.0.1. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/CHANGES.rst) - [Commits](https://github.com/pypa/setuptools/compare/v58.5.3...v59.0.1) --- updated-dependencies: - dependency-name: setuptools dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 3a9d0d0b9f692..33de28812db80 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -798,9 +798,9 @@ yarl==1.6.3 \ # via aiohttp # The following packages are considered to be unsafe in a requirements file: -setuptools==58.5.3 \ - --hash=sha256:a481fbc56b33f5d8f6b33dce41482e64c68b668be44ff42922903b03872590bf \ - --hash=sha256:dae6b934a965c8a59d6d230d3867ec408bb95e73bd538ff77e71fedf1eaca729 +setuptools==59.0.1 \ + --hash=sha256:899d27ec8104a68d4ba813b1afd66708a1a10e9391e79be92c8c60f9c77d05e5 \ + --hash=sha256:dedb38ba61844d9df36072dad313cb79426fd50497aaac9c0da4cd50dbeeb110 # via # -r requirements.in # sphinx From f159dade3a4e82b5216761eebbb4d6ca24e3654b Mon Sep 17 00:00:00 2001 From: Florin Coras Date: Mon, 15 Nov 2021 23:22:41 -0800 Subject: [PATCH 086/110] vcl: remove extraneous cb call in event activate (#19009) Signed-off-by: Florin Coras --- contrib/vcl/source/vcl_event.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/contrib/vcl/source/vcl_event.cc b/contrib/vcl/source/vcl_event.cc index b37ecbb415b3f..9e7fc87c49f5e 100644 --- a/contrib/vcl/source/vcl_event.cc +++ b/contrib/vcl/source/vcl_event.cc @@ -33,8 +33,6 @@ void VclEvent::activate(uint32_t events) { ASSERT((events & (Event::FileReadyType::Read | Event::FileReadyType::Write | Event::FileReadyType::Closed)) == events); - cb_(events); - // Schedule the activation callback so it runs as part of the next loop iteration if it is not // already scheduled. if (injected_activation_events_ == 0) { From 4c0a141f9057d91f02891af37bc399af8ccc1c24 Mon Sep 17 00:00:00 2001 From: Peter Jausovec Date: Mon, 15 Nov 2021 23:41:08 -0800 Subject: [PATCH 087/110] Fix a typo in admin interface documentation (#19012) Signed-off-by: Peter Jausovec --- docs/root/operations/admin.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/root/operations/admin.rst b/docs/root/operations/admin.rst index 6c785184a3cdb..0d6d57d750abe 100644 --- a/docs/root/operations/admin.rst +++ b/docs/root/operations/admin.rst @@ -261,7 +261,7 @@ modify different aspects of the server: .. http:get:: /init_dump - Dump currently information of unready targets of various Envoy components as JSON-serialized proto + Dump current information of unready targets of various Envoy components as JSON-serialized proto messages. See the :ref:`response definition ` for more information. From 6f7a015bc56be53bc12f0635746d52ff7a226e49 Mon Sep 17 00:00:00 2001 From: Kevin Baichoo Date: Mon, 15 Nov 2021 23:59:58 -0800 Subject: [PATCH 088/110] Cleanup unnecessary newlines in useAccessLog strings. (#19011) BaseIntegrationTest::waitForAccessLog expects one newline per access log so if the 2nd entry was waited for with an access log string ending in "\n" it'd return earlier than expected. Risk Level: low Testing: Ran tests Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A Signed-off-by: Kevin Baichoo --- test/integration/integration_test.cc | 6 ++-- test/integration/protocol_integration_test.cc | 28 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/test/integration/integration_test.cc b/test/integration/integration_test.cc index 67003055050ab..13163620fe9af 100644 --- a/test/integration/integration_test.cc +++ b/test/integration/integration_test.cc @@ -1235,7 +1235,7 @@ TEST_P(IntegrationTest, AbsolutePathUsingHttpsAllowedInternally) { // Make that both IPv4 and IPv6 hosts match when using relative and absolute URLs. TEST_P(IntegrationTest, TestHostWithAddress) { - useAccessLog("%REQ(Host)%\n"); + useAccessLog("%REQ(Host)%"); std::string address_string; if (GetParam() == Network::Address::IpVersion::v4) { address_string = TestUtility::getIpv4Loopback(); @@ -1391,7 +1391,7 @@ TEST_P(IntegrationTest, TestBind) { address_string = "::1"; } config_helper_.setSourceAddress(address_string); - useAccessLog("%UPSTREAM_LOCAL_ADDRESS%\n"); + useAccessLog("%UPSTREAM_LOCAL_ADDRESS%"); initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -2195,7 +2195,7 @@ TEST_P(IntegrationTest, RetryOptionsPredicate) { // successfully overrides the cached route, and subsequently, the request's upstream cluster // selection. TEST_P(IntegrationTest, SetRouteToDelegatingRouteWithClusterOverride) { - useAccessLog("%UPSTREAM_CLUSTER%\n"); + useAccessLog("%UPSTREAM_CLUSTER%"); config_helper_.prependFilter(R"EOF( name: set-route-filter diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 3caf5d6575881..72c79fea27df5 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -617,7 +617,7 @@ TEST_P(DownstreamProtocolIntegrationTest, MissingHeadersLocalReply) { TEST_P(DownstreamProtocolIntegrationTest, MissingHeadersLocalReplyDownstreamBytesCount) { useAccessLog("%DOWNSTREAM_WIRE_BYTES_SENT% %DOWNSTREAM_WIRE_BYTES_RECEIVED% " - "%DOWNSTREAM_HEADER_BYTES_SENT% %DOWNSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%DOWNSTREAM_HEADER_BYTES_SENT% %DOWNSTREAM_HEADER_BYTES_RECEIVED%"); config_helper_.addFilter("{ name: invalid-header-filter, typed_config: { \"@type\": " "type.googleapis.com/google.protobuf.Empty } }"); initialize(); @@ -640,7 +640,7 @@ TEST_P(DownstreamProtocolIntegrationTest, MissingHeadersLocalReplyDownstreamByte TEST_P(DownstreamProtocolIntegrationTest, MissingHeadersLocalReplyUpstreamBytesCount) { useAccessLog("%UPSTREAM_WIRE_BYTES_SENT% %UPSTREAM_WIRE_BYTES_RECEIVED% " - "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%"); config_helper_.addFilter("{ name: invalid-header-filter, typed_config: { \"@type\": " "type.googleapis.com/google.protobuf.Empty } }"); initialize(); @@ -685,7 +685,7 @@ TEST_P(DownstreamProtocolIntegrationTest, MissingHeadersLocalReplyWithBody) { TEST_P(DownstreamProtocolIntegrationTest, MissingHeadersLocalReplyWithBodyBytesCount) { useAccessLog("%DOWNSTREAM_WIRE_BYTES_SENT% %DOWNSTREAM_WIRE_BYTES_RECEIVED% " - "%DOWNSTREAM_HEADER_BYTES_SENT% %DOWNSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%DOWNSTREAM_HEADER_BYTES_SENT% %DOWNSTREAM_HEADER_BYTES_RECEIVED%"); config_helper_.addFilter("{ name: invalid-header-filter, typed_config: { \"@type\": " "type.googleapis.com/google.protobuf.Empty } }"); initialize(); @@ -751,7 +751,7 @@ TEST_P(ProtocolIntegrationTest, Retry) { cluster.mutable_track_cluster_stats()->set_request_response_sizes(true); }); useAccessLog("%UPSTREAM_WIRE_BYTES_SENT% %UPSTREAM_WIRE_BYTES_RECEIVED% " - "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%"); initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); auto response = codec_client_->makeRequestWithBody( @@ -3233,7 +3233,7 @@ TEST_P(ProtocolIntegrationTest, HeaderOnlyBytesCountUpstream) { return; } useAccessLog("%UPSTREAM_WIRE_BYTES_SENT% %UPSTREAM_WIRE_BYTES_RECEIVED% " - "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%"); testRouterRequestAndResponseWithBody(0, 0, false); expectUpstreamBytesSentAndReceived(BytesCountExpectation(251, 38, 219, 18), BytesCountExpectation(168, 13, 168, 13)); @@ -3256,7 +3256,7 @@ TEST_P(ProtocolIntegrationTest, HeaderAndBodyWireBytesCountUpstream) { return; } useAccessLog("%UPSTREAM_WIRE_BYTES_SENT% %UPSTREAM_WIRE_BYTES_RECEIVED% " - "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%"); testRouterRequestAndResponseWithBody(100, 100, false); expectUpstreamBytesSentAndReceived(BytesCountExpectation(371, 158, 228, 27), BytesCountExpectation(277, 122, 168, 13)); @@ -3268,7 +3268,7 @@ TEST_P(ProtocolIntegrationTest, HeaderAndBodyWireBytesCountDownstream) { return; } useAccessLog("%DOWNSTREAM_WIRE_BYTES_SENT% %DOWNSTREAM_WIRE_BYTES_RECEIVED% " - "%DOWNSTREAM_HEADER_BYTES_SENT% %DOWNSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%DOWNSTREAM_HEADER_BYTES_SENT% %DOWNSTREAM_HEADER_BYTES_RECEIVED%"); testRouterRequestAndResponseWithBody(100, 100, false); expectDownstreamBytesSentAndReceived(BytesCountExpectation(244, 231, 114, 84), BytesCountExpectation(177, 173, 68, 64)); @@ -3339,7 +3339,7 @@ TEST_P(ProtocolIntegrationTest, TrailersWireBytesCountUpstream) { return; } useAccessLog("%UPSTREAM_WIRE_BYTES_SENT% %UPSTREAM_WIRE_BYTES_RECEIVED% " - "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%"); config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); config_helper_.addConfigModifier(setEnableUpstreamTrailersHttp1()); @@ -3355,7 +3355,7 @@ TEST_P(ProtocolIntegrationTest, TrailersWireBytesCountDownstream) { return; } useAccessLog("%DOWNSTREAM_WIRE_BYTES_SENT% %DOWNSTREAM_WIRE_BYTES_RECEIVED% " - "%DOWNSTREAM_HEADER_BYTES_SENT% %DOWNSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%DOWNSTREAM_HEADER_BYTES_SENT% %DOWNSTREAM_HEADER_BYTES_RECEIVED%"); config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); config_helper_.addConfigModifier(setEnableUpstreamTrailersHttp1()); @@ -3371,7 +3371,7 @@ TEST_P(ProtocolIntegrationTest, DownstreamDisconnectBeforeRequestCompleteWireByt return; } useAccessLog("%UPSTREAM_WIRE_BYTES_SENT% %UPSTREAM_WIRE_BYTES_RECEIVED% " - "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%"); testRouterDownstreamDisconnectBeforeRequestComplete(nullptr); @@ -3385,7 +3385,7 @@ TEST_P(ProtocolIntegrationTest, DownstreamDisconnectBeforeRequestCompleteWireByt return; } useAccessLog("%DOWNSTREAM_WIRE_BYTES_SENT% %DOWNSTREAM_WIRE_BYTES_RECEIVED% " - "%DOWNSTREAM_HEADER_BYTES_SENT% %DOWNSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%DOWNSTREAM_HEADER_BYTES_SENT% %DOWNSTREAM_HEADER_BYTES_RECEIVED%"); testRouterDownstreamDisconnectBeforeRequestComplete(nullptr); @@ -3399,7 +3399,7 @@ TEST_P(ProtocolIntegrationTest, UpstreamDisconnectBeforeRequestCompleteWireBytes return; } useAccessLog("%UPSTREAM_WIRE_BYTES_SENT% %UPSTREAM_WIRE_BYTES_RECEIVED% " - "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%"); testRouterUpstreamDisconnectBeforeRequestComplete(); @@ -3413,7 +3413,7 @@ TEST_P(ProtocolIntegrationTest, UpstreamDisconnectBeforeResponseCompleteWireByte return; } useAccessLog("%UPSTREAM_WIRE_BYTES_SENT% %UPSTREAM_WIRE_BYTES_RECEIVED% " - "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%"); testRouterUpstreamDisconnectBeforeResponseComplete(); @@ -3427,7 +3427,7 @@ TEST_P(DownstreamProtocolIntegrationTest, BadRequest) { return; } useAccessLog("%DOWNSTREAM_WIRE_BYTES_SENT% %DOWNSTREAM_WIRE_BYTES_RECEIVED% " - "%DOWNSTREAM_HEADER_BYTES_SENT% %DOWNSTREAM_HEADER_BYTES_RECEIVED%\n"); + "%DOWNSTREAM_HEADER_BYTES_SENT% %DOWNSTREAM_HEADER_BYTES_RECEIVED%"); initialize(); std::string response; std::string full_request(100, '\r'); From 03ec8b6a966ba5578475eabb0906a4944ec1ea49 Mon Sep 17 00:00:00 2001 From: code Date: Wed, 17 Nov 2021 00:15:49 +0800 Subject: [PATCH 089/110] header map: removed runtime envoy.reloadable_features.header_map_correctly_coalesce_cookies (#19014) Signed-off-by: wbpcode --- docs/root/version_history/current.rst | 1 + source/common/http/header_map_impl.cc | 9 ++++--- source/common/http/header_map_impl.h | 2 -- source/common/runtime/runtime_features.cc | 1 - test/common/http/header_map_impl_test.cc | 4 ---- test/common/http/inline_cookie_test.cc | 24 ------------------- .../quic/envoy_quic_server_stream_test.cc | 11 ++++----- tools/code_format/check_format.py | 2 +- 8 files changed, 10 insertions(+), 44 deletions(-) diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index d04b950b3cc9f..2bf18985b28ad 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -34,6 +34,7 @@ Removed Config or Runtime * compression: removed ``envoy.reloadable_features.enable_compression_without_content_length_header`` runtime guard and legacy code paths. * grpc-web: removed ``envoy.reloadable_features.grpc_web_fix_non_proto_encoded_response_handling`` and legacy code paths. +* header map: removed ``envoy.reloadable_features.header_map_correctly_coalesce_cookies`` and legacy code paths. * health check: removed ``envoy.reloadable_features.health_check.immediate_failure_exclude_from_cluster`` runtime guard and legacy code paths. * http: removed ``envoy.reloadable_features.add_and_validate_scheme_header`` and legacy code paths. * http: removed ``envoy.reloadable_features.check_unsupported_typed_per_filter_config``, Envoy will always check unsupported typed per filter config if the filter isn't optional. diff --git a/source/common/http/header_map_impl.cc b/source/common/http/header_map_impl.cc index bfd77c8dcfa3a..1f7d51e357eb8 100644 --- a/source/common/http/header_map_impl.cc +++ b/source/common/http/header_map_impl.cc @@ -49,8 +49,8 @@ bool validatedLowerCaseString(absl::string_view str) { return lower_case_str == str; } -absl::string_view delimiterByHeader(const LowerCaseString& key, bool correctly_coalesce_cookies) { - if (correctly_coalesce_cookies && key == Http::Headers::get().Cookie) { +absl::string_view delimiterByHeader(const LowerCaseString& key) { + if (key == Http::Headers::get().Cookie) { return DelimiterForInlineCookies; } return DelimiterForInlineHeaders; @@ -378,8 +378,7 @@ void HeaderMapImpl::insertByKey(HeaderString&& key, HeaderString&& value) { if (*lookup.value().entry_ == nullptr) { maybeCreateInline(lookup.value().entry_, *lookup.value().key_, std::move(value)); } else { - const auto delimiter = - delimiterByHeader(*lookup.value().key_, header_map_correctly_coalesce_cookies_); + const auto delimiter = delimiterByHeader(*lookup.value().key_); const uint64_t added_size = appendToHeader((*lookup.value().entry_)->value(), value.getStringView(), delimiter); addSize(added_size); @@ -446,7 +445,7 @@ void HeaderMapImpl::appendCopy(const LowerCaseString& key, absl::string_view val // TODO(#9221): converge on and document a policy for coalescing multiple headers. auto entry = getExisting(key); if (!entry.empty()) { - const auto delimiter = delimiterByHeader(key, header_map_correctly_coalesce_cookies_); + const auto delimiter = delimiterByHeader(key); const uint64_t added_size = appendToHeader(entry[0]->value(), value, delimiter); addSize(added_size); } else { diff --git a/source/common/http/header_map_impl.h b/source/common/http/header_map_impl.h index 5f64bc311c5ca..441571b923f59 100644 --- a/source/common/http/header_map_impl.h +++ b/source/common/http/header_map_impl.h @@ -342,8 +342,6 @@ class HeaderMapImpl : NonCopyable { StatefulHeaderKeyFormatterPtr formatter_; // This holds the internal byte size of the HeaderMap. uint64_t cached_byte_size_ = 0; - const bool header_map_correctly_coalesce_cookies_ = Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.header_map_correctly_coalesce_cookies"); }; /** diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index ca17ceb29c271..8194a069014b5 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -90,7 +90,6 @@ constexpr const char* runtime_features[] = { "envoy.restart_features.explicit_wildcard_resource", "envoy.restart_features.use_apple_api_for_dns_lookups", // Misplaced flags: please do not add flags to this section. - "envoy.reloadable_features.header_map_correctly_coalesce_cookies", "envoy.reloadable_features.sanitize_http_header_referer", "envoy.reloadable_features.skip_dispatching_frames_for_closed_connection", // End misplaced flags: please do not add flags in this section. diff --git a/test/common/http/header_map_impl_test.cc b/test/common/http/header_map_impl_test.cc index ce4bb8a7ccd1e..1b95f4b49ad14 100644 --- a/test/common/http/header_map_impl_test.cc +++ b/test/common/http/header_map_impl_test.cc @@ -751,10 +751,6 @@ TEST_P(HeaderMapImplTest, DoubleCookieAdd) { } TEST_P(HeaderMapImplTest, AppendCookieHeadersWithSemicolon) { - if (!Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.header_map_correctly_coalesce_cookies")) { - return; - } TestRequestHeaderMapImpl headers; const std::string foo("foo=1"); const std::string bar("bar=2"); diff --git a/test/common/http/inline_cookie_test.cc b/test/common/http/inline_cookie_test.cc index 5d8269f81975e..b2d08a06682ab 100644 --- a/test/common/http/inline_cookie_test.cc +++ b/test/common/http/inline_cookie_test.cc @@ -19,15 +19,7 @@ TEST(InlineCookieTest, InlineCookieTest) { Http::CustomInlineHeaderRegistry::registerInlineHeader( Http::LowerCaseString("header_for_compare")); - auto mock_snapshot = std::make_shared>(); - testing::NiceMock mock_loader; - Runtime::LoaderSingleton::initialize(&mock_loader); - { - // Enable 'envoy.reloadable_features.header_map_correctly_coalesce_cookies' feature. - ON_CALL(mock_loader, threadsafeSnapshot()).WillByDefault(testing::Return(mock_snapshot)); - ON_CALL(*mock_snapshot, runtimeFeatureEnabled(_)).WillByDefault(testing::Return(true)); - Http::TestRequestHeaderMapImpl headers{{"cookie", "key1:value1"}, {"cookie", "key2:value2"}, {"header_for_compare", "value1"}, @@ -38,22 +30,6 @@ TEST(InlineCookieTest, InlineCookieTest) { // Delimiter for inline 'header_for_compare' header is default ','. EXPECT_EQ("value1,value2", headers.get_("header_for_compare")); } - - { - // Disable 'envoy.reloadable_features.header_map_correctly_coalesce_cookies' feature. - ON_CALL(mock_loader, threadsafeSnapshot()).WillByDefault(testing::Return(mock_snapshot)); - ON_CALL(*mock_snapshot, runtimeFeatureEnabled(_)).WillByDefault(testing::Return(false)); - - Http::TestRequestHeaderMapImpl headers{{"cookie", "key1:value1"}, - {"cookie", "key2:value2"}, - {"header_for_compare", "value1"}, - {"header_for_compare", "value2"}}; - - // 'envoy.reloadable_features.header_map_correctly_coalesce_cookies' is disabled then default - // ',' will be used as delimiter. - EXPECT_EQ("key1:value1,key2:value2", headers.get_("cookie")); - EXPECT_EQ("value1,value2", headers.get_("header_for_compare")); - } } } // namespace diff --git a/test/common/quic/envoy_quic_server_stream_test.cc b/test/common/quic/envoy_quic_server_stream_test.cc index ee01d965e3fd1..d34d569bdb927 100644 --- a/test/common/quic/envoy_quic_server_stream_test.cc +++ b/test/common/quic/envoy_quic_server_stream_test.cc @@ -172,13 +172,10 @@ TEST_F(EnvoyQuicServerStreamTest, GetRequestAndResponse) { EXPECT_EQ(host_, headers->getHostValue()); EXPECT_EQ("/", headers->getPathValue()); EXPECT_EQ(Http::Headers::get().MethodValues.Get, headers->getMethodValue()); - if (Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.header_map_correctly_coalesce_cookies")) { - // Verify that the duplicated headers are handled correctly before passing to stream - // decoder. - EXPECT_EQ("a=b; c=d", - headers->get(Http::Headers::get().Cookie)[0]->value().getStringView()); - } + // Verify that the duplicated headers are handled correctly before passing to stream + // decoder. + EXPECT_EQ("a=b; c=d", + headers->get(Http::Headers::get().Cookie)[0]->value().getStringView()); })); EXPECT_CALL(stream_decoder_, decodeData(BufferStringEqual(""), /*end_stream=*/true)); spdy::SpdyHeaderBlock spdy_headers; diff --git a/tools/code_format/check_format.py b/tools/code_format/check_format.py index b1c9853ec8091..09744dc0e2831 100755 --- a/tools/code_format/check_format.py +++ b/tools/code_format/check_format.py @@ -268,7 +268,7 @@ "envoy.reloadable_features.activate_timers_next_event_loop", "envoy.reloadable_features.grpc_json_transcoder_adhere_to_buffer_limits", "envoy.reloadable_features.upstream_http2_flood_checks", - "envoy.reloadable_features.header_map_correctly_coalesce_cookies", + "envoy.reloadable_features.sanitize_http_header_referer", } # yapf: enable From a305c74da5d4e0cfe4e11c7db5e72c9a79c54801 Mon Sep 17 00:00:00 2001 From: Jason Miller <88333215+jasonmillerstackpath@users.noreply.github.com> Date: Tue, 16 Nov 2021 11:26:46 -0600 Subject: [PATCH 090/110] tls_inspector: create JA3 client fingerprint (#18853) JA3 fingerprint information is available at https://github.com/salesforce/ja3 and at https://ja3er.com. Fixes #16622 Signed-off-by: Jason Miller Co-authored-by: Ryland Degnan --- .../tls_inspector/v3/tls_inspector.proto | 5 + docs/root/version_history/current.rst | 1 + envoy/network/listen_socket.h | 10 ++ envoy/network/socket.h | 10 ++ .../formatter/substitution_formatter.cc | 9 + source/common/http/filter_manager.h | 3 + source/common/network/listen_socket_impl.h | 5 + source/common/network/socket_impl.h | 3 + .../filters/listener/tls_inspector/BUILD | 3 + .../filters/listener/tls_inspector/config.cc | 10 +- .../listener/tls_inspector/tls_inspector.cc | 148 ++++++++++++++++- .../listener/tls_inspector/tls_inspector.h | 8 +- test/config/utility.cc | 11 +- test/config/utility.h | 2 +- .../common/fuzz/listener_filter_fakes.cc | 6 + .../common/fuzz/listener_filter_fakes.h | 5 + .../filters/listener/tls_inspector/BUILD | 1 + .../tls_inspector/tls_inspector_benchmark.cc | 3 +- .../tls_inspector/tls_inspector_fuzz_test.cc | 4 +- .../tls_inspector_fuzz_test.proto | 7 +- .../tls_inspector/tls_inspector_test.cc | 127 +++++++++++++- .../listener/tls_inspector/tls_utility.cc | 155 ++++++++++++++++++ .../listener/tls_inspector/tls_utility.h | 6 + .../listener_filter_integration_test.cc | 48 +++++- test/mocks/network/connection.h | 1 + test/mocks/network/mocks.h | 2 + test/server/filter_chain_benchmark_test.cc | 9 +- 27 files changed, 577 insertions(+), 25 deletions(-) diff --git a/api/envoy/extensions/filters/listener/tls_inspector/v3/tls_inspector.proto b/api/envoy/extensions/filters/listener/tls_inspector/v3/tls_inspector.proto index eff9774844f4b..4f87ccd9489b7 100644 --- a/api/envoy/extensions/filters/listener/tls_inspector/v3/tls_inspector.proto +++ b/api/envoy/extensions/filters/listener/tls_inspector/v3/tls_inspector.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.extensions.filters.listener.tls_inspector.v3; +import "google/protobuf/wrappers.proto"; + import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; @@ -17,4 +19,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; message TlsInspector { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.listener.tls_inspector.v2.TlsInspector"; + + // Populate `JA3` fingerprint hash using data from the TLS Client Hello packet. Default is false. + google.protobuf.BoolValue enable_ja3_fingerprinting = 1; } diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 2bf18985b28ad..c65a1fb0bf799 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -76,6 +76,7 @@ New Features * thrift_proxy: support subset lb when using request or route metadata. * tls: added support for only verifying the leaf CRL in the certificate chain with :ref:`only_verify_leaf_cert_crl `. * tls: support loading certificate chain and private key via :ref:`pkcs12 `. +* tls_inspector filter: added :ref:`enable_ja3_fingerprinting ` to create JA3 fingerprint hash from Client Hello message. * transport_socket: added :ref:`envoy.transport_sockets.tcp_stats ` which generates additional statistics gathered from the OS TCP stack. * udp: add support for multiple listener filters. * upstream: added the ability to :ref:`configure max connection duration ` for upstream clusters. diff --git a/envoy/network/listen_socket.h b/envoy/network/listen_socket.h index b7b89c050e634..a900ad7ee5e14 100644 --- a/envoy/network/listen_socket.h +++ b/envoy/network/listen_socket.h @@ -59,6 +59,16 @@ class ConnectionSocket : public virtual Socket, public virtual ScopeTrackedObjec */ virtual absl::string_view requestedServerName() const PURE; + /** + * @param ja3Hash Connection ja3 fingerprint hash of the downstream connection. + */ + virtual void setJA3Hash(absl::string_view ja3_hash) PURE; + + /** + * @return Connection ja3 fingerprint hash of the downstream connection, if any. + */ + virtual absl::string_view ja3Hash() const PURE; + /** * @return absl::optional An optional of the most recent round-trip * time of the connection. If the platform does not support this, then an empty optional is diff --git a/envoy/network/socket.h b/envoy/network/socket.h index 0d492c0e7a888..4f3e84452c692 100644 --- a/envoy/network/socket.h +++ b/envoy/network/socket.h @@ -98,6 +98,11 @@ class ConnectionInfoProvider { * connection does not use SSL. */ virtual Ssl::ConnectionInfoConstSharedPtr sslConnection() const PURE; + + /** + * @return ja3 fingerprint hash of the downstream connection, if any. + */ + virtual absl::string_view ja3Hash() const PURE; }; class ConnectionInfoSetter : public ConnectionInfoProvider { @@ -142,6 +147,11 @@ class ConnectionInfoSetter : public ConnectionInfoProvider { * @param connection_info sets the downstream ssl connection. */ virtual void setSslConnection(const Ssl::ConnectionInfoConstSharedPtr& ssl_connection_info) PURE; + + /** + * @param JA3 fingerprint. + */ + virtual void setJA3Hash(const absl::string_view ja3_hash) PURE; }; using ConnectionInfoSetterSharedPtr = std::shared_ptr; diff --git a/source/common/formatter/substitution_formatter.cc b/source/common/formatter/substitution_formatter.cc index 42e5f9df8ab4d..3e4c66c00b1a1 100644 --- a/source/common/formatter/substitution_formatter.cc +++ b/source/common/formatter/substitution_formatter.cc @@ -953,6 +953,15 @@ StreamInfoFormatter::StreamInfoFormatter(const std::string& field_name) { } return absl::nullopt; }); + } else if (field_name == "TLS_JA3_FINGERPRINT") { + field_extractor_ = std::make_unique( + [](const StreamInfo::StreamInfo& stream_info) { + absl::optional result; + if (!stream_info.downstreamAddressProvider().ja3Hash().empty()) { + result = std::string(stream_info.downstreamAddressProvider().ja3Hash()); + } + return result; + }); } else { throw EnvoyException(fmt::format("Not supported field in StreamInfo: {}", field_name)); } diff --git a/source/common/http/filter_manager.h b/source/common/http/filter_manager.h index d786df34e41ec..758d046e249d0 100644 --- a/source/common/http/filter_manager.h +++ b/source/common/http/filter_manager.h @@ -643,6 +643,9 @@ class OverridableRemoteConnectionInfoSetterStreamInfo : public StreamInfo::Strea << DUMP_MEMBER_AS(directRemoteAddress(), directRemoteAddress()->asStringView()) << DUMP_MEMBER_AS(localAddress(), localAddress()->asStringView()) << "\n"; } + absl::string_view ja3Hash() const override { + return StreamInfoImpl::downstreamAddressProvider().ja3Hash(); + } private: Network::Address::InstanceConstSharedPtr overridden_downstream_remote_address_; diff --git a/source/common/network/listen_socket_impl.h b/source/common/network/listen_socket_impl.h index b555224ac489e..b4030717e0cb6 100644 --- a/source/common/network/listen_socket_impl.h +++ b/source/common/network/listen_socket_impl.h @@ -194,6 +194,11 @@ class ConnectionSocketImpl : public SocketImpl, public ConnectionSocket { return connectionInfoProvider().requestedServerName(); } + void setJA3Hash(absl::string_view ja3_hash) override { + connectionInfoProvider().setJA3Hash(ja3_hash); + } + absl::string_view ja3Hash() const override { return connectionInfoProvider().ja3Hash(); } + absl::optional lastRoundTripTime() override { return ioHandle().lastRoundTripTime(); } diff --git a/source/common/network/socket_impl.h b/source/common/network/socket_impl.h index ece583ae7b2ac..f95115314c815 100644 --- a/source/common/network/socket_impl.h +++ b/source/common/network/socket_impl.h @@ -57,6 +57,8 @@ class ConnectionInfoSetterImpl : public ConnectionInfoSetter { ASSERT(!ssl_info_); ssl_info_ = ssl_connection_info; } + absl::string_view ja3Hash() const override { return ja3_hash_; } + void setJA3Hash(const absl::string_view ja3_hash) override { ja3_hash_ = std::string(ja3_hash); } private: Address::InstanceConstSharedPtr local_address_; @@ -66,6 +68,7 @@ class ConnectionInfoSetterImpl : public ConnectionInfoSetter { std::string server_name_; absl::optional connection_id_; Ssl::ConnectionInfoConstSharedPtr ssl_info_; + std::string ja3_hash_; }; class SocketImpl : public virtual Socket { diff --git a/source/extensions/filters/listener/tls_inspector/BUILD b/source/extensions/filters/listener/tls_inspector/BUILD index cc137ea1a5cc8..a540cc4285046 100644 --- a/source/extensions/filters/listener/tls_inspector/BUILD +++ b/source/extensions/filters/listener/tls_inspector/BUILD @@ -28,7 +28,10 @@ envoy_cc_library( "//envoy/network:listen_socket_interface", "//source/common/api:os_sys_calls_lib", "//source/common/common:assert_lib", + "//source/common/common:hex_lib", "//source/common/common:minimal_logger_lib", + "//source/common/protobuf:utility_lib", + "@envoy_api//envoy/extensions/filters/listener/tls_inspector/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/listener/tls_inspector/config.cc b/source/extensions/filters/listener/tls_inspector/config.cc index a53a448ac3096..9fad2181fc1a4 100644 --- a/source/extensions/filters/listener/tls_inspector/config.cc +++ b/source/extensions/filters/listener/tls_inspector/config.cc @@ -19,10 +19,16 @@ class TlsInspectorConfigFactory : public Server::Configuration::NamedListenerFil public: // NamedListenerFilterConfigFactory Network::ListenerFilterFactoryCb createListenerFilterFactoryFromProto( - const Protobuf::Message&, + const Protobuf::Message& message, const Network::ListenerFilterMatcherSharedPtr& listener_filter_matcher, Server::Configuration::ListenerFactoryContext& context) override { - ConfigSharedPtr config(new Config(context.scope())); + + // downcast it to the TLS inspector config + const auto& proto_config = MessageUtil::downcastAndValidate< + const envoy::extensions::filters::listener::tls_inspector::v3::TlsInspector&>( + message, context.messageValidationVisitor()); + + ConfigSharedPtr config = std::make_shared(context.scope(), proto_config); return [listener_filter_matcher, config](Network::ListenerFilterManager& filter_manager) -> void { filter_manager.addAcceptFilter(listener_filter_matcher, std::make_unique(config)); diff --git a/source/extensions/filters/listener/tls_inspector/tls_inspector.cc b/source/extensions/filters/listener/tls_inspector/tls_inspector.cc index 92a70d07a46f7..2924b8ae5ee05 100644 --- a/source/extensions/filters/listener/tls_inspector/tls_inspector.cc +++ b/source/extensions/filters/listener/tls_inspector/tls_inspector.cc @@ -12,8 +12,12 @@ #include "source/common/api/os_sys_calls_impl.h" #include "source/common/common/assert.h" +#include "source/common/common/hex.h" +#include "source/common/protobuf/utility.h" +#include "absl/strings/str_format.h" #include "absl/strings/str_join.h" +#include "openssl/md5.h" #include "openssl/ssl.h" namespace Envoy { @@ -25,9 +29,14 @@ namespace TlsInspector { const unsigned Config::TLS_MIN_SUPPORTED_VERSION = TLS1_VERSION; const unsigned Config::TLS_MAX_SUPPORTED_VERSION = TLS1_3_VERSION; -Config::Config(Stats::Scope& scope, uint32_t max_client_hello_size) +Config::Config( + Stats::Scope& scope, + const envoy::extensions::filters::listener::tls_inspector::v3::TlsInspector& proto_config, + uint32_t max_client_hello_size) : stats_{ALL_TLS_INSPECTOR_STATS(POOL_COUNTER_PREFIX(scope, "tls_inspector."))}, ssl_ctx_(SSL_CTX_new(TLS_with_buffers_method())), + enable_ja3_fingerprinting_( + PROTOBUF_GET_WRAPPED_OR_DEFAULT(proto_config, enable_ja3_fingerprinting, false)), max_client_hello_size_(max_client_hello_size) { if (max_client_hello_size_ > TLS_MAX_CLIENT_HELLO) { @@ -41,11 +50,13 @@ Config::Config(Stats::Scope& scope, uint32_t max_client_hello_size) SSL_CTX_set_session_cache_mode(ssl_ctx_.get(), SSL_SESS_CACHE_OFF); SSL_CTX_set_select_certificate_cb( ssl_ctx_.get(), [](const SSL_CLIENT_HELLO* client_hello) -> ssl_select_cert_result_t { + Filter* filter = static_cast(SSL_get_app_data(client_hello->ssl)); + filter->createJA3Hash(client_hello); + const uint8_t* data; size_t len; if (SSL_early_callback_ctx_extension_get( client_hello, TLSEXT_TYPE_application_layer_protocol_negotiation, &data, &len)) { - Filter* filter = static_cast(SSL_get_app_data(client_hello->ssl)); filter->onALPN(data, len); } return ssl_select_cert_success; @@ -233,6 +244,139 @@ ParseState Filter::parseClientHello(const void* data, size_t len) { } } +// Google GREASE values (https://datatracker.ietf.org/doc/html/rfc8701) +static constexpr std::array GREASE = { + 0x0a0a, 0x1a1a, 0x2a2a, 0x3a3a, 0x4a4a, 0x5a5a, 0x6a6a, 0x7a7a, + 0x8a8a, 0x9a9a, 0xaaaa, 0xbaba, 0xcaca, 0xdada, 0xeaea, 0xfafa, +}; + +bool isNotGrease(uint16_t id) { + for (size_t grease_id : GREASE) { + if (id == grease_id) { + return false; + } + } + return true; +} + +void writeCipherSuites(const SSL_CLIENT_HELLO* ssl_client_hello, std::string& fingerprint) { + CBS cipher_suites; + CBS_init(&cipher_suites, ssl_client_hello->cipher_suites, ssl_client_hello->cipher_suites_len); + + bool write_cipher = true; + bool first = true; + while (write_cipher && CBS_len(&cipher_suites) > 0) { + uint16_t id; + write_cipher = CBS_get_u16(&cipher_suites, &id); + if (write_cipher && isNotGrease(id)) { + if (!first) { + absl::StrAppend(&fingerprint, "-"); + } + absl::StrAppendFormat(&fingerprint, "%d", id); + first = false; + } + } +} + +void writeExtensions(const SSL_CLIENT_HELLO* ssl_client_hello, std::string& fingerprint) { + CBS extensions; + CBS_init(&extensions, ssl_client_hello->extensions, ssl_client_hello->extensions_len); + + bool write_extension = true; + bool first = true; + while (write_extension && CBS_len(&extensions) > 0) { + uint16_t id; + CBS extension; + + write_extension = + (CBS_get_u16(&extensions, &id) && CBS_get_u16_length_prefixed(&extensions, &extension)); + if (write_extension && isNotGrease(id)) { + if (!first) { + absl::StrAppend(&fingerprint, "-"); + } + absl::StrAppendFormat(&fingerprint, "%d", id); + first = false; + } + } +} + +void writeEllipticCurves(const SSL_CLIENT_HELLO* ssl_client_hello, std::string& fingerprint) { + const uint8_t* ec_data; + size_t ec_len; + if (SSL_early_callback_ctx_extension_get(ssl_client_hello, TLSEXT_TYPE_supported_groups, &ec_data, + &ec_len)) { + CBS ec; + CBS_init(&ec, ec_data, ec_len); + + // skip list length + uint16_t id; + bool write_elliptic_curve = CBS_get_u16(&ec, &id); + + bool first = true; + while (write_elliptic_curve && CBS_len(&ec) > 0) { + write_elliptic_curve = CBS_get_u16(&ec, &id); + if (write_elliptic_curve) { + if (!first) { + absl::StrAppend(&fingerprint, "-"); + } + absl::StrAppendFormat(&fingerprint, "%d", id); + first = false; + } + } + } +} + +void writeEllipticCurvePointFormats(const SSL_CLIENT_HELLO* ssl_client_hello, + std::string& fingerprint) { + const uint8_t* ecpf_data; + size_t ecpf_len; + if (SSL_early_callback_ctx_extension_get(ssl_client_hello, TLSEXT_TYPE_ec_point_formats, + &ecpf_data, &ecpf_len)) { + CBS ecpf; + CBS_init(&ecpf, ecpf_data, ecpf_len); + + // skip list length + uint8_t id; + bool write_point_format = CBS_get_u8(&ecpf, &id); + + bool first = true; + while (write_point_format && CBS_len(&ecpf) > 0) { + write_point_format = CBS_get_u8(&ecpf, &id); + if (write_point_format) { + if (!first) { + absl::StrAppend(&fingerprint, "-"); + } + absl::StrAppendFormat(&fingerprint, "%d", id); + first = false; + } + } + } +} + +void Filter::createJA3Hash(const SSL_CLIENT_HELLO* ssl_client_hello) { + if (config_->enableJA3Fingerprinting()) { + std::string fingerprint; + const uint16_t client_version = ssl_client_hello->version; + absl::StrAppendFormat(&fingerprint, "%d,", client_version); + writeCipherSuites(ssl_client_hello, fingerprint); + absl::StrAppend(&fingerprint, ","); + writeExtensions(ssl_client_hello, fingerprint); + absl::StrAppend(&fingerprint, ","); + writeEllipticCurves(ssl_client_hello, fingerprint); + absl::StrAppend(&fingerprint, ","); + writeEllipticCurvePointFormats(ssl_client_hello, fingerprint); + + ENVOY_LOG(trace, "tls:createJA3Hash(), fingerprint: {}", fingerprint); + + uint8_t buf[MD5_DIGEST_LENGTH]; + MD5(reinterpret_cast(fingerprint.data()), fingerprint.size(), buf); + std::string md5 = Envoy::Hex::encode(buf, MD5_DIGEST_LENGTH); + ENVOY_LOG(trace, "tls:createJA3Hash(), hash: {}", md5); + + cb_->socket().setJA3Hash(md5); + } +} + } // namespace TlsInspector } // namespace ListenerFilters } // namespace Extensions diff --git a/source/extensions/filters/listener/tls_inspector/tls_inspector.h b/source/extensions/filters/listener/tls_inspector/tls_inspector.h index f7adb93076e51..0e32ef2517c95 100644 --- a/source/extensions/filters/listener/tls_inspector/tls_inspector.h +++ b/source/extensions/filters/listener/tls_inspector/tls_inspector.h @@ -2,6 +2,7 @@ #include "envoy/event/file_event.h" #include "envoy/event/timer.h" +#include "envoy/extensions/filters/listener/tls_inspector/v3/tls_inspector.pb.h" #include "envoy/network/filter.h" #include "envoy/stats/scope.h" #include "envoy/stats/stats_macros.h" @@ -49,10 +50,13 @@ enum class ParseState { */ class Config { public: - Config(Stats::Scope& scope, uint32_t max_client_hello_size = TLS_MAX_CLIENT_HELLO); + Config(Stats::Scope& scope, + const envoy::extensions::filters::listener::tls_inspector::v3::TlsInspector& proto_config, + uint32_t max_client_hello_size = TLS_MAX_CLIENT_HELLO); const TlsInspectorStats& stats() const { return stats_; } bssl::UniquePtr newSsl(); + bool enableJA3Fingerprinting() const { return enable_ja3_fingerprinting_; } uint32_t maxClientHelloSize() const { return max_client_hello_size_; } static constexpr size_t TLS_MAX_CLIENT_HELLO = 64 * 1024; @@ -62,6 +66,7 @@ class Config { private: TlsInspectorStats stats_; bssl::UniquePtr ssl_ctx_; + bool enable_ja3_fingerprinting_; const uint32_t max_client_hello_size_; }; @@ -83,6 +88,7 @@ class Filter : public Network::ListenerFilter, Logger::Loggable lastRoundTripTime() override; @@ -49,6 +53,7 @@ class FakeConnectionSocket : public Network::MockConnectionSocket { std::vector application_protocols_; std::string transport_protocol_; std::string server_name_; + std::string ja3_hash_; }; // TODO: Move over to Fake (name is confusing) diff --git a/test/extensions/filters/listener/tls_inspector/BUILD b/test/extensions/filters/listener/tls_inspector/BUILD index 44a9e16b111fa..5978f030d237d 100644 --- a/test/extensions/filters/listener/tls_inspector/BUILD +++ b/test/extensions/filters/listener/tls_inspector/BUILD @@ -36,6 +36,7 @@ envoy_proto_library( srcs = ["tls_inspector_fuzz_test.proto"], deps = [ "//test/extensions/filters/listener/common/fuzz:listener_filter_fuzzer_proto", + "@envoy_api//envoy/extensions/filters/listener/tls_inspector/v3:pkg", ], ) diff --git a/test/extensions/filters/listener/tls_inspector/tls_inspector_benchmark.cc b/test/extensions/filters/listener/tls_inspector/tls_inspector_benchmark.cc index 15426aa9aff58..66d76337e089b 100644 --- a/test/extensions/filters/listener/tls_inspector/tls_inspector_benchmark.cc +++ b/test/extensions/filters/listener/tls_inspector/tls_inspector_benchmark.cc @@ -74,7 +74,8 @@ static void BM_TlsInspector(benchmark::State& state) { "\x02h2\x08http/1.1")); TestThreadsafeSingletonInjector os_calls{&os_sys_calls}; NiceMock store; - ConfigSharedPtr cfg(std::make_shared(store)); + envoy::extensions::filters::listener::tls_inspector::v3::TlsInspector proto_config; + ConfigSharedPtr cfg(std::make_shared(store, proto_config)); Network::IoHandlePtr io_handle = std::make_unique(); Network::ConnectionSocketImpl socket(std::move(io_handle), nullptr, nullptr); NiceMock dispatcher; diff --git a/test/extensions/filters/listener/tls_inspector/tls_inspector_fuzz_test.cc b/test/extensions/filters/listener/tls_inspector/tls_inspector_fuzz_test.cc index b722a2c50ec54..d1be04b6f8bcc 100644 --- a/test/extensions/filters/listener/tls_inspector/tls_inspector_fuzz_test.cc +++ b/test/extensions/filters/listener/tls_inspector/tls_inspector_fuzz_test.cc @@ -23,9 +23,9 @@ DEFINE_PROTO_FUZZER( if (input.max_size() == 0) { // If max_size not set, use default constructor - cfg = std::make_shared(store); + cfg = std::make_shared(store, input.config()); } else { - cfg = std::make_shared(store, input.max_size()); + cfg = std::make_shared(store, input.config(), input.max_size()); } auto filter = std::make_unique(std::move(cfg)); diff --git a/test/extensions/filters/listener/tls_inspector/tls_inspector_fuzz_test.proto b/test/extensions/filters/listener/tls_inspector/tls_inspector_fuzz_test.proto index a48b71bd77b6f..37c9423dac38d 100644 --- a/test/extensions/filters/listener/tls_inspector/tls_inspector_fuzz_test.proto +++ b/test/extensions/filters/listener/tls_inspector/tls_inspector_fuzz_test.proto @@ -2,11 +2,14 @@ syntax = "proto3"; package test.extensions.filters.listener.tls_inspector; +import "envoy/extensions/filters/listener/tls_inspector/v3/tls_inspector.proto"; import "test/extensions/filters/listener/common/fuzz/listener_filter_fuzzer.proto"; import "validate/validate.proto"; message TlsInspectorTestCase { - uint32 max_size = 1 [(validate.rules).uint32.lte = 65536]; - test.extensions.filters.listener.FilterFuzzTestCase fuzzed = 2 + envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector config = 1 + [(validate.rules).message.required = true]; + uint32 max_size = 2 [(validate.rules).uint32.lte = 65536]; + test.extensions.filters.listener.FilterFuzzTestCase fuzzed = 3 [(validate.rules).message.required = true]; } diff --git a/test/extensions/filters/listener/tls_inspector/tls_inspector_test.cc b/test/extensions/filters/listener/tls_inspector/tls_inspector_test.cc index 0a9d074d71388..5a1b214dbe207 100644 --- a/test/extensions/filters/listener/tls_inspector/tls_inspector_test.cc +++ b/test/extensions/filters/listener/tls_inspector/tls_inspector_test.cc @@ -1,3 +1,4 @@ +#include "source/common/common/hex.h" #include "source/common/http/utility.h" #include "source/common/network/io_socket_handle_impl.h" #include "source/extensions/filters/listener/tls_inspector/tls_inspector.h" @@ -8,7 +9,9 @@ #include "test/mocks/stats/mocks.h" #include "test/test_common/threadsafe_singleton_injector.h" +#include "absl/strings/str_format.h" #include "gtest/gtest.h" +#include "openssl/md5.h" #include "openssl/ssl.h" using testing::_; @@ -31,7 +34,8 @@ namespace { class TlsInspectorTest : public testing::TestWithParam> { public: TlsInspectorTest() - : cfg_(std::make_shared(store_)), + : cfg_(std::make_shared( + store_, envoy::extensions::filters::listener::tls_inspector::v3::TlsInspector())), io_handle_(std::make_unique(42)) {} ~TlsInspectorTest() override { io_handle_->close(); } @@ -56,6 +60,9 @@ class TlsInspectorTest : public testing::TestWithParamonAccept(cb_); } + void testJA3(const std::string& fingerprint, bool expect_server_name = true, + const std::string& hash = {}); + NiceMock os_sys_calls_; TestThreadsafeSingletonInjector os_calls_{&os_sys_calls_}; Stats::IsolatedStoreImpl store_; @@ -78,7 +85,9 @@ INSTANTIATE_TEST_SUITE_P(TlsProtocolVersions, TlsInspectorTest, // Test that an exception is thrown for an invalid value for max_client_hello_size TEST_P(TlsInspectorTest, MaxClientHelloSize) { - EXPECT_THROW_WITH_MESSAGE(Config(store_, Config::TLS_MAX_CLIENT_HELLO + 1), EnvoyException, + envoy::extensions::filters::listener::tls_inspector::v3::TlsInspector proto_config; + EXPECT_THROW_WITH_MESSAGE(Config(store_, proto_config, Config::TLS_MAX_CLIENT_HELLO + 1), + EnvoyException, "max_client_hello_size of 65537 is greater than maximum of 65536."); } @@ -214,8 +223,9 @@ TEST_P(TlsInspectorTest, NoExtensions) { // Test that the filter fails if the ClientHello is larger than the // maximum allowed size. TEST_P(TlsInspectorTest, ClientHelloTooBig) { + envoy::extensions::filters::listener::tls_inspector::v3::TlsInspector proto_config; const size_t max_size = 50; - cfg_ = std::make_shared(store_, static_cast(max_size)); + cfg_ = std::make_shared(store_, proto_config, static_cast(max_size)); std::vector client_hello = Tls::Test::generateClientHello( std::get<0>(GetParam()), std::get<1>(GetParam()), "example.com", ""); ASSERT(client_hello.size() > max_size); @@ -232,6 +242,117 @@ TEST_P(TlsInspectorTest, ClientHelloTooBig) { EXPECT_EQ(1, cfg_->stats().client_hello_too_large_.value()); } +// Test that the filter sets the `JA3` hash +TEST_P(TlsInspectorTest, ConnectionFingerprint) { + envoy::extensions::filters::listener::tls_inspector::v3::TlsInspector proto_config; + proto_config.mutable_enable_ja3_fingerprinting()->set_value(true); + cfg_ = std::make_shared(store_, proto_config); + std::vector client_hello = + Tls::Test::generateClientHello(std::get<0>(GetParam()), std::get<1>(GetParam()), "", ""); + init(); + EXPECT_CALL(os_sys_calls_, recv(42, _, _, MSG_PEEK)) + .WillOnce(Invoke( + [&client_hello](os_fd_t, void* buffer, size_t length, int) -> Api::SysCallSizeResult { + ASSERT(length >= client_hello.size()); + memcpy(buffer, client_hello.data(), client_hello.size()); + return Api::SysCallSizeResult{ssize_t(client_hello.size()), 0}; + })); + EXPECT_CALL(socket_, setJA3Hash(_)); + EXPECT_CALL(socket_, setRequestedServerName(_)).Times(0); + EXPECT_CALL(socket_, setRequestedApplicationProtocols(_)).Times(0); + EXPECT_CALL(socket_, setDetectedTransportProtocol(absl::string_view("tls"))); + EXPECT_CALL(cb_, continueFilterChain(true)); + file_event_callback_(Event::FileReadyType::Read); +} + +void TlsInspectorTest::testJA3(const std::string& fingerprint, bool expect_server_name, + const std::string& hash) { + envoy::extensions::filters::listener::tls_inspector::v3::TlsInspector proto_config; + proto_config.mutable_enable_ja3_fingerprinting()->set_value(true); + cfg_ = std::make_shared(store_, proto_config); + std::vector client_hello = Tls::Test::generateClientHelloFromJA3Fingerprint(fingerprint); + init(); + EXPECT_CALL(os_sys_calls_, recv(42, _, _, MSG_PEEK)) + .WillOnce(Invoke( + [&client_hello](os_fd_t, void* buffer, size_t length, int) -> Api::SysCallSizeResult { + ASSERT(length >= client_hello.size()); + memcpy(buffer, client_hello.data(), client_hello.size()); + return Api::SysCallSizeResult{ssize_t(client_hello.size()), 0}; + })); + if (hash.empty()) { + uint8_t buf[MD5_DIGEST_LENGTH]; + MD5(reinterpret_cast(fingerprint.data()), fingerprint.size(), buf); + EXPECT_CALL(socket_, setJA3Hash(absl::string_view(Envoy::Hex::encode(buf, MD5_DIGEST_LENGTH)))); + } else { + EXPECT_CALL(socket_, setJA3Hash(absl::string_view(hash))); + } + if (expect_server_name) { + EXPECT_CALL(socket_, setRequestedServerName(absl::string_view("www.envoyproxy.io"))); + } + EXPECT_CALL(socket_, setRequestedApplicationProtocols(_)).Times(0); + EXPECT_CALL(cb_, continueFilterChain(true)); + EXPECT_CALL(socket_, setDetectedTransportProtocol(absl::string_view("tls"))); + file_event_callback_(Event::FileReadyType::Read); +} + +// Test that the filter sets the correct `JA3` hash. +// Fingerprint created with User-Agent "curl/7.64.1" and a request to ja3er.com/json. +TEST_P(TlsInspectorTest, ConnectionJA3Hash) { + testJA3("771,49200-49196-49192-49188-49172-49162-159-107-57-52393-52392-52394-65413-196-136-" + "129-157-61-53-192-132-49199-49195-49191-49187-49171-49161-158-103-51-190-69-156-60-" + "47-186-65-49169-49159-5-4-49170-49160-22-10-255,0-11-10-13-16,29-23-24,0"); +} + +// Test that the filter sets the correct `JA3` hash with GREASE values in ClientHello message. +// Fingerprint created with User-Agent "curl/7.64.1" and a request to ja3er.com/json. +TEST_P(TlsInspectorTest, ConnectionJA3HashGREASE) { + const std::string version("771"); + const std::string ciphers( + "49200-49196-49192-49188-49172-49162-159-107-57-52393-52392-52394-65413-196-136-" + "129-157-61-53-192-132-49199-49195-49191-49187-49171-49161-158-103-51-190-69-156-60-" + "47-186-65-49169-49159-5-4-49170-49160-22-10-255"); + const std::string extensions_ec_formats("0-11-10-13-16,29-23-24,0"); + std::string fingerprint; + absl::StrAppend(&fingerprint, version, ",", ciphers, ",", extensions_ec_formats); + + std::string grease; + for (uint32_t i = 0x0a0a; i < 0xfafa; i += 0x1010) { + if (i != 0x0a0a) { + absl::StrAppend(&grease, "-"); + } + absl::StrAppendFormat(&grease, "%d", i); + } + std::string fingerprint_with_grease; + absl::StrAppend(&fingerprint_with_grease, version, ",", grease, "-", ciphers, ",", grease, "-", + extensions_ec_formats); + + uint8_t buf[MD5_DIGEST_LENGTH]; + MD5(reinterpret_cast(fingerprint.data()), fingerprint.size(), buf); + std::string hash = Envoy::Hex::encode(buf, MD5_DIGEST_LENGTH); + + testJA3(fingerprint_with_grease, true, hash); +} + +// Test that the filter sets the correct `JA3` hash with no elliptic curves or elliptic curve point +// formats in ClientHello message. Fingerprint is from ja3er.com/getAllHashesJson. +TEST_P(TlsInspectorTest, ConnectionJA3HashNoEllipticCurvesOrPointFormats) { + testJA3("771,157-49313-49309-156-49312-49308-61-60-53-47-255,0-35-16-22-23-13,,"); +} + +// Test that the filter sets the correct `JA3` hash with TLS1.0 and no extensions in ClientHello +// message. Fingerprint is from ja3er.com/getAllHashesJson. +TEST_P(TlsInspectorTest, ConnectionJA3HashTls10NoExtensions) { + testJA3("769,49162-49157-49161-49156-49159-49154-49160-49155-49172-49167-49171-49166-49169-49164-" + "49170-49165-57-51-53-47-5-4-10,,,", + false); +} + +// Test that the filter sets the correct `JA3` hash with TLS1.1. +// Fingerprint is from ja3er.com/getAllHashesJson. +TEST_P(TlsInspectorTest, ConnectionJA3HashTls11) { + testJA3("770,49162-49172-49161-49171-57-56-51-50-53-47-255,0-11-10-16-22-23,5,0-1-2"); +} + // Test that the filter fails on non-SSL data TEST_P(TlsInspectorTest, NotSsl) { init(); diff --git a/test/extensions/filters/listener/tls_inspector/tls_utility.cc b/test/extensions/filters/listener/tls_inspector/tls_utility.cc index c3777c479fdf2..8a353de4bb4f0 100644 --- a/test/extensions/filters/listener/tls_inspector/tls_utility.cc +++ b/test/extensions/filters/listener/tls_inspector/tls_utility.cc @@ -2,6 +2,7 @@ #include "source/common/common/assert.h" +#include "absl/strings/str_split.h" #include "openssl/ssl.h" namespace Envoy { @@ -40,6 +41,160 @@ std::vector generateClientHello(uint16_t tls_min_version, uint16_t tls_ return buf; } +std::vector generateClientHelloFromJA3Fingerprint(const std::string& ja3_fingerprint) { + // fingerprint should have this format: + // SSLVersion,Cipher,SSLExtension,EllipticCurve,EllipticCurvePointFormat + // Example: + // 769,47-53-5-10-49161-49162-49171-49172-50-56-19-4,0-10-11,23-24-25,0 + std::vector fingerprint = absl::StrSplit(ja3_fingerprint, ','); + ASSERT(fingerprint.size() == 5); + + // version + const uint16_t tls_version = std::stoi(fingerprint[0], nullptr); + + // ciphers + std::vector values = absl::StrSplit(fingerprint[1], '-'); + std::vector ciphers; + for (const std::string& v : values) { + uint16_t cipher = std::stoi(v, nullptr); + ciphers.push_back((cipher & 0xff00) >> 8); + ciphers.push_back(cipher & 0xff); + } + + // elliptic curves extension + const uint16_t elliptic_curves_id = 0xa; + values = absl::StrSplit(fingerprint[3], '-', absl::SkipEmpty()); + uint16_t length = values.size() * 2; + uint16_t ext_length = length + 2; + std::vector elliptic_curves = {(elliptic_curves_id & 0xff00) >> 8, + elliptic_curves_id & 0xff, + static_cast((ext_length & 0xff00) >> 8), + static_cast(ext_length & 0xff), + static_cast((length & 0xff00) >> 8), + static_cast(length & 0xff)}; + for (const std::string& v : values) { + uint16_t elliptic_curve = std::stoi(v, nullptr); + elliptic_curves.push_back((elliptic_curve & 0xff00) >> 8); + elliptic_curves.push_back(elliptic_curve & 0xff); + } + + // elliptic curve point formats extension + const uint16_t elliptic_curve_point_formats_id = 0xb; + values = absl::StrSplit(fingerprint[4], '-', absl::SkipEmpty()); + ext_length = values.size() + 1; + std::vector elliptic_curve_point_formats = { + (elliptic_curve_point_formats_id & 0xff00) >> 8, elliptic_curve_point_formats_id & 0xff, + static_cast((ext_length & 0xff00) >> 8), static_cast(ext_length & 0xff), + static_cast(values.size())}; + for (const std::string& v : values) { + uint8_t elliptic_curve_point_format = std::stoi(v, nullptr); + elliptic_curve_point_formats.push_back(elliptic_curve_point_format); + } + + // server name extension + const uint16_t server_name_id = 0x0; + std::vector server_name = {(server_name_id & 0xff00) >> 8, server_name_id & 0xff, + // length + 0x00, 0x16, + // list length + 0x00, 0x14, + // hostname type + 0x00, + // name length + 0x00, 0x11, + // name (www.envoyproxy.io) + 'w', 'w', 'w', '.', 'e', 'n', 'v', 'o', 'y', 'p', 'r', 'o', + 'x', 'y', '.', 'i', 'o'}; + + // signature algorithms extension + const uint16_t signature_algorithms_id = 0xd; + std::vector signature_algorithms = {(signature_algorithms_id & 0xff00) >> 8, + signature_algorithms_id & 0xff, + // length + 0x00, 0x04, + // list length + 0x00, 0x02, + // algorithm + 0x04, 0x03}; + + // extensions + values = absl::StrSplit(fingerprint[2], '-', absl::SkipEmpty()); + std::vector extensions; + for (const std::string& v : values) { + switch (std::stoi(v, nullptr)) { + case elliptic_curves_id: { + extensions.insert(std::end(extensions), std::begin(elliptic_curves), + std::end(elliptic_curves)); + break; + } + case elliptic_curve_point_formats_id: { + extensions.insert(std::end(extensions), std::begin(elliptic_curve_point_formats), + std::end(elliptic_curve_point_formats)); + break; + } + case server_name_id: { + extensions.insert(std::end(extensions), std::begin(server_name), std::end(server_name)); + break; + } + case signature_algorithms_id: { + extensions.insert(std::end(extensions), std::begin(signature_algorithms), + std::end(signature_algorithms)); + break; + } + default: { + uint16_t extension_id = std::stoi(v, nullptr); + extensions.push_back((extension_id & 0xff00) >> 8); + extensions.push_back(extension_id & 0xff); + extensions.push_back(0); + extensions.push_back(0); + } + } + } + + // client hello message + std::vector clienthello = {// client version + static_cast((tls_version & 0xff00) >> 8), + static_cast(tls_version & 0xff), + // client random (32 bytes) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // session id + 0}; + // cipher suite length and ciphers + uint16_t ciphers_length = ciphers.size(); + clienthello.push_back((ciphers_length & 0xff00) >> 8); + clienthello.push_back(ciphers_length & 0xff); + clienthello.insert(std::end(clienthello), std::begin(ciphers), std::end(ciphers)); + // compression methods + clienthello.push_back(0x01); + clienthello.push_back(0x00); + // extension length and extensions + uint16_t extensions_length = extensions.size(); + clienthello.push_back((extensions_length & 0xff00) >> 8); + clienthello.push_back(extensions_length & 0xff); + clienthello.insert(std::end(clienthello), std::begin(extensions), std::end(extensions)); + + // headers + uint32_t clienthello_bytes = clienthello.size(); + uint16_t handshake_bytes = clienthello.size() + 4; + std::vector clienthello_message = { + // record header + 0x16, 0x03, 0x01, + // handshake bytes + static_cast((handshake_bytes & 0xff00) >> 8), + static_cast(handshake_bytes & 0xff), + // handshake header + 0x01, + // client hello bytes + static_cast((clienthello_bytes & 0xff0000) >> 16), + static_cast((clienthello_bytes & 0xff00) >> 8), + static_cast(clienthello_bytes & 0xff)}; + clienthello_message.insert(std::end(clienthello_message), std::begin(clienthello), + std::end(clienthello)); + + return clienthello_message; +} + } // namespace Test } // namespace Tls } // namespace Envoy diff --git a/test/extensions/filters/listener/tls_inspector/tls_utility.h b/test/extensions/filters/listener/tls_inspector/tls_utility.h index 13e911e9e3c5e..213339ded21d7 100644 --- a/test/extensions/filters/listener/tls_inspector/tls_utility.h +++ b/test/extensions/filters/listener/tls_inspector/tls_utility.h @@ -19,6 +19,12 @@ namespace Test { std::vector generateClientHello(uint16_t tls_min_version, uint16_t tls_max_version, const std::string& sni_name, const std::string& alpn); +/** + * Generate a TLS ClientHello in wire-format from a `JA3` fingerprint. + * @param ja3_fingerprint The `JA3` fingerprint to use when creating the ClientHello message. + */ +std::vector generateClientHelloFromJA3Fingerprint(const std::string& ja3_fingerprint); + } // namespace Test } // namespace Tls } // namespace Envoy diff --git a/test/integration/listener_filter_integration_test.cc b/test/integration/listener_filter_integration_test.cc index ccd5f360a77ab..c487ad2d2895f 100644 --- a/test/integration/listener_filter_integration_test.cc +++ b/test/integration/listener_filter_integration_test.cc @@ -9,6 +9,7 @@ #include "source/common/network/utility.h" #include "source/extensions/filters/listener/tls_inspector/tls_inspector.h" #include "source/extensions/transport_sockets/tls/context_manager_impl.h" +#include "source/extensions/transport_sockets/tls/ssl_socket.h" #include "test/integration/integration.h" #include "test/integration/ssl_utility.h" @@ -48,10 +49,11 @@ class ListenerFilterIntegrationTest : public testing::TestWithParam listener_filter_disabled = absl::nullopt) { + void initializeWithListenerFilter(bool ssl_client, const std::string& log_format, + absl::optional listener_filter_disabled = absl::nullopt, + bool enable_ja3_fingerprinting = false) { config_helper_.renameListener("echo"); - std::string tls_inspector_config = ConfigHelper::tlsInspectorFilter(); + std::string tls_inspector_config = ConfigHelper::tlsInspectorFilter(enable_ja3_fingerprinting); if (listener_filter_disabled.has_value()) { tls_inspector_config = appendMatcher(tls_inspector_config, listener_filter_disabled.value()); } @@ -77,27 +79,38 @@ class ListenerFilterIntegrationTest : public testing::TestWithParam(timeSystem()); } - void setupConnections(bool listener_filter_disabled, bool expect_connection_open, - bool ssl_client) { - initializeWithListenerFilter(ssl_client, listener_filter_disabled); + void setupConnections(bool listener_filter_disabled, bool expect_connection_open, bool ssl_client, + const std::string& log_format = "%RESPONSE_CODE_DETAILS%", + const Ssl::ClientSslTransportOptions& ssl_options = {}, + const std::string& curves_list = "", + bool enable_ja3_fingerprinting = false) { + initializeWithListenerFilter(ssl_client, log_format, listener_filter_disabled, + enable_ja3_fingerprinting); // Set up the SSL client. Network::Address::InstanceConstSharedPtr address = Ssl::getSslAddress(version_, lookupPort("echo")); - context_ = Ssl::createClientSslTransportSocketFactory({}, *context_manager_, *api_); + context_ = Ssl::createClientSslTransportSocketFactory(ssl_options, *context_manager_, *api_); Network::TransportSocketPtr transport_socket; if (ssl_client) { transport_socket = context_->createTransportSocket(std::make_shared( absl::string_view(""), std::vector(), std::vector{"envoyalpn"})); + + if (!curves_list.empty()) { + auto ssl_socket = + dynamic_cast(transport_socket.get()); + ASSERT(ssl_socket != nullptr); + SSL_set1_curves_list(ssl_socket->rawSslForTest(), curves_list.c_str()); + } } else { auto transport_socket_factory = std::make_unique(); transport_socket = transport_socket_factory->createTransportSocket(nullptr); @@ -157,6 +170,25 @@ TEST_P(ListenerFilterIntegrationTest, ContinueOnListenerTimeout) { EXPECT_THAT(waitForAccessLog(listener_access_log_name_), testing::Eq("-")); } +// The `JA3` fingerprint is correct in the access log. +TEST_P(ListenerFilterIntegrationTest, JA3FingerprintIsSet) { + // These TLS options will create a client hello message with + // `JA3` fingerprint: + // `771,49199,23-65281-10-11-35-16-13,23,0` + // MD5 hash: + // `71d1f47d1125ac53c3c6a4863c087cfe` + Ssl::ClientSslTransportOptions ssl_options; + ssl_options.setCipherSuites({"ECDHE-RSA-AES128-GCM-SHA256"}); + ssl_options.setTlsVersion(envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_2); + setupConnections(/*listener_filter_disabled=*/false, /*expect_connection_open=*/true, + /*ssl_client=*/true, /*log_format=*/"%TLS_JA3_FINGERPRINT%", + /*ssl_options=*/ssl_options, /*curves_list=*/"P-256", + /*enable_`ja3`_fingerprinting=*/true); + client_->close(Network::ConnectionCloseType::NoFlush); + EXPECT_THAT(waitForAccessLog(listener_access_log_name_), + testing::Eq("71d1f47d1125ac53c3c6a4863c087cfe")); +} + INSTANTIATE_TEST_SUITE_P(IpVersions, ListenerFilterIntegrationTest, testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), TestUtility::ipTestParamsToString); diff --git a/test/mocks/network/connection.h b/test/mocks/network/connection.h index 949b6e2b09560..8d044ff2b33a4 100644 --- a/test/mocks/network/connection.h +++ b/test/mocks/network/connection.h @@ -72,6 +72,7 @@ class MockConnectionBase { MOCK_METHOD(void, setConnectionStats, (const ConnectionStats& stats)); \ MOCK_METHOD(Ssl::ConnectionInfoConstSharedPtr, ssl, (), (const)); \ MOCK_METHOD(absl::string_view, requestedServerName, (), (const)); \ + MOCK_METHOD(absl::string_view, ja3Hash, (), (const)); \ MOCK_METHOD(State, state, (), (const)); \ MOCK_METHOD(bool, connecting, (), (const)); \ MOCK_METHOD(void, write, (Buffer::Instance & data, bool end_stream)); \ diff --git a/test/mocks/network/mocks.h b/test/mocks/network/mocks.h index e4f5d6c18df9e..af7a6c7b179a4 100644 --- a/test/mocks/network/mocks.h +++ b/test/mocks/network/mocks.h @@ -330,6 +330,8 @@ class MockConnectionSocket : public ConnectionSocket { MOCK_METHOD(const std::vector&, requestedApplicationProtocols, (), (const)); MOCK_METHOD(void, setRequestedServerName, (absl::string_view)); MOCK_METHOD(absl::string_view, requestedServerName, (), (const)); + MOCK_METHOD(void, setJA3Hash, (absl::string_view)); + MOCK_METHOD(absl::string_view, ja3Hash, (), (const)); MOCK_METHOD(void, addOption_, (const Socket::OptionConstSharedPtr&)); MOCK_METHOD(void, addOptions_, (const Socket::OptionsSharedPtr&)); MOCK_METHOD(const Network::ConnectionSocket::OptionsSharedPtr&, options, (), (const)); diff --git a/test/server/filter_chain_benchmark_test.cc b/test/server/filter_chain_benchmark_test.cc index 919085fdcb945..e2d4a9b891e1d 100644 --- a/test/server/filter_chain_benchmark_test.cc +++ b/test/server/filter_chain_benchmark_test.cc @@ -44,7 +44,8 @@ class MockConnectionSocket : public Network::ConnectionSocket { static std::unique_ptr createMockConnectionSocket(uint16_t destination_port, const std::string& destination_address, - const std::string& server_name, const std::string& transport_protocol, + const std::string& server_name, const std::string& ja3_hash, + const std::string& transport_protocol, const std::vector& application_protocols, const std::string& source_address, uint16_t source_port) { auto res = std::make_unique(); @@ -66,6 +67,7 @@ class MockConnectionSocket : public Network::ConnectionSocket { Network::Utility::parseInternetAddress(source_address, source_port)); } res->server_name_ = server_name; + res->ja3_hash_ = ja3_hash; res->transport_protocol_ = transport_protocol; res->application_protocols_ = application_protocols; return res; @@ -73,6 +75,7 @@ class MockConnectionSocket : public Network::ConnectionSocket { absl::string_view detectedTransportProtocol() const override { return transport_protocol_; } absl::string_view requestedServerName() const override { return server_name_; } + absl::string_view ja3Hash() const override { return ja3_hash_; } const std::vector& requestedApplicationProtocols() const override { return application_protocols_; } @@ -107,6 +110,7 @@ class MockConnectionSocket : public Network::ConnectionSocket { void addOptions(const OptionsSharedPtr&) override {} const OptionsSharedPtr& options() const override { return options_; } void setRequestedServerName(absl::string_view) override {} + void setJA3Hash(absl::string_view) override {} Api::SysCallIntResult bind(Network::Address::InstanceConstSharedPtr) override { return {0, 0}; } Api::SysCallIntResult listen(int) override { return {0, 0}; } Api::SysCallIntResult connect(const Network::Address::InstanceConstSharedPtr) override { @@ -131,6 +135,7 @@ class MockConnectionSocket : public Network::ConnectionSocket { OptionsSharedPtr options_; std::shared_ptr connection_info_provider_; std::string server_name_; + std::string ja3_hash_; std::string transport_protocol_; std::vector application_protocols_; }; @@ -242,7 +247,7 @@ BENCHMARK_DEFINE_F(FilterChainBenchmarkFixture, FilterChainFindTest) sockets.reserve(state.range(0)); for (int i = 0; i < state.range(0); i++) { sockets.push_back(std::move(*MockConnectionSocket::createMockConnectionSocket( - 10000 + i, "127.0.0.1", "", "tls", {}, "8.8.8.8", 111))); + 10000 + i, "127.0.0.1", "", "", "tls", {}, "8.8.8.8", 111))); } NiceMock factory_context; FilterChainManagerImpl filter_chain_manager{ From d981914bb18d555d38e65cdb133cccf92da82cd3 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 16 Nov 2021 11:11:48 -0800 Subject: [PATCH 091/110] upstream: Implement Happy Eyeballs address sorting (#18906) upstream: Implement Happy Eyeballs address sorting with address families interleaved, as per Section 4 of RFC 8305, Happy Eyeballs v2. Sorting as per Section 6 of RFC6724 already happens in ares_getaddrinfo() and is not part of this. Risk Level: Low - Happy Eyeballs is not yet used Testing: Unit tests Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A Signed-off-by: Ryan Hamilton rch@google.com --- .../network/happy_eyeballs_connection_impl.cc | 41 +++++++++++++++- .../network/happy_eyeballs_connection_impl.h | 10 +++- .../happy_eyeballs_connection_impl_test.cc | 47 +++++++++++++++++-- 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 40802f0919fb3..3d6ebf229971f 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -10,7 +10,8 @@ HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( Address::InstanceConstSharedPtr source_address, TransportSocketFactory& socket_factory, TransportSocketOptionsConstSharedPtr transport_socket_options, const ConnectionSocket::OptionsSharedPtr options) - : id_(ConnectionImpl::next_global_id_++), dispatcher_(dispatcher), address_list_(address_list), + : id_(ConnectionImpl::next_global_id_++), dispatcher_(dispatcher), + address_list_(sortAddresses(address_list)), connection_construction_state_( {source_address, socket_factory, transport_socket_options, options}), next_attempt_timer_(dispatcher_.createTimer([this]() -> void { tryAnotherConnection(); })) { @@ -379,6 +380,44 @@ void HappyEyeballsConnectionImpl::dumpState(std::ostream& os, int indent_level) } } +namespace { +bool hasMatchingAddressFamily(const Address::InstanceConstSharedPtr& a, + const Address::InstanceConstSharedPtr& b) { + return (a->type() == Address::Type::Ip && b->type() == Address::Type::Ip && + a->ip()->version() == b->ip()->version()); +} + +} // namespace + +std::vector +HappyEyeballsConnectionImpl::sortAddresses(const std::vector& in) { + std::vector address_list; + address_list.reserve(in.size()); + // Iterator which will advance through all addresses matching the first family. + auto first = in.begin(); + // Iterator which will advance through all addresses not matching the first family. + // This initial value is ignored and will be overwritten in the loop below. + auto other = in.begin(); + while (first != in.end() || other != in.end()) { + if (first != in.end()) { + address_list.push_back(*first); + first = std::find_if(first + 1, in.end(), + [&](const auto& val) { return hasMatchingAddressFamily(in[0], val); }); + } + + if (other != in.end()) { + other = std::find_if(other + 1, in.end(), + [&](const auto& val) { return !hasMatchingAddressFamily(in[0], val); }); + + if (other != in.end()) { + address_list.push_back(*other); + } + } + } + ASSERT(address_list.size() == in.size()); + return address_list; +} + ClientConnectionPtr HappyEyeballsConnectionImpl::createNextConnection() { ASSERT(next_address_ < address_list_.size()); auto connection = dispatcher_.createClientConnection( diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 4a64bc420d676..d2235dc581708 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -98,6 +98,14 @@ class HappyEyeballsConnectionImpl : public ClientConnection, void hashKey(std::vector& hash_key) const override; void dumpState(std::ostream& os, int indent_level) const override; + // Returns a new vector containing the contents of |address_list| sorted + // with address families interleaved, as per Section 4 of RFC 8305, Happy + // Eyeballs v2. It is assumed that the list must already be sorted as per + // Section 6 of RFC6724, which happens in the DNS implementations (ares_getaddrinfo() + // and Apple DNS). + static std::vector + sortAddresses(const std::vector& address_list); + private: // ConnectionCallbacks which will be set on an ClientConnection which // sends connection events back to the HappyEyeballsConnectionImpl. @@ -196,7 +204,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection, Event::Dispatcher& dispatcher_; // List of addresses to attempt to connect to. - const std::vector& address_list_; + const std::vector address_list_; // Index of the next address to use. size_t next_address_ = 0; diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index faa25b383a5b8..2588af571f164 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -21,9 +21,10 @@ class HappyEyeballsConnectionImplTest : public testing::Test { : failover_timer_(new testing::StrictMock(&dispatcher_)), transport_socket_options_(std::make_shared()), options_(std::make_shared()), - address_list_({std::make_shared("127.0.0.1"), - std::make_shared("127.0.0.2"), - std::make_shared("127.0.0.3")}) { + raw_address_list_({std::make_shared("127.0.0.1"), + std::make_shared("127.0.0.2"), + std::make_shared("ff02::1", 0)}), + address_list_({raw_address_list_[0], raw_address_list_[2], raw_address_list_[1]}) { EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[0], _, _, _)) .WillOnce(testing::InvokeWithoutArgs( @@ -31,8 +32,8 @@ class HappyEyeballsConnectionImplTest : public testing::Test { next_connections_.push_back(std::make_unique>()); impl_ = std::make_unique( - dispatcher_, address_list_, Address::InstanceConstSharedPtr(), transport_socket_factory_, - transport_socket_options_, options_); + dispatcher_, raw_address_list_, Address::InstanceConstSharedPtr(), + transport_socket_factory_, transport_socket_options_, options_); } // Called by the dispatcher to return a MockClientConnection. In order to allow expectations to @@ -94,6 +95,7 @@ class HappyEyeballsConnectionImplTest : public testing::Test { MockTransportSocketFactory transport_socket_factory_; TransportSocketOptionsConstSharedPtr transport_socket_options_; const ConnectionSocket::OptionsSharedPtr options_; + const std::vector raw_address_list_; const std::vector address_list_; std::vector*> created_connections_; std::vector connection_callbacks_; @@ -1049,5 +1051,40 @@ TEST_F(HappyEyeballsConnectionImplTest, LastRoundTripTime) { EXPECT_EQ(rtt, impl_->lastRoundTripTime()); } +TEST_F(HappyEyeballsConnectionImplTest, SortAddresses) { + auto ip_v4_1 = std::make_shared("127.0.0.1"); + auto ip_v4_2 = std::make_shared("127.0.0.2"); + auto ip_v4_3 = std::make_shared("127.0.0.3"); + auto ip_v4_4 = std::make_shared("127.0.0.4"); + + auto ip_v6_1 = std::make_shared("ff02::1", 0); + auto ip_v6_2 = std::make_shared("ff02::2", 0); + auto ip_v6_3 = std::make_shared("ff02::3", 0); + auto ip_v6_4 = std::make_shared("ff02::4", 0); + + // All v4 address so unchanged. + std::vector v4_list = {ip_v4_1, ip_v4_2, ip_v4_3, ip_v4_4}; + EXPECT_EQ(v4_list, HappyEyeballsConnectionImpl::sortAddresses(v4_list)); + + // All v6 address so unchanged. + std::vector v6_list = {ip_v6_1, ip_v6_2, ip_v6_3, ip_v6_4}; + EXPECT_EQ(v6_list, HappyEyeballsConnectionImpl::sortAddresses(v6_list)); + + std::vector v6_then_v4 = {ip_v6_1, ip_v6_2, ip_v4_1, ip_v4_2}; + std::vector interleaved = {ip_v6_1, ip_v4_1, ip_v6_2, ip_v4_2}; + EXPECT_EQ(interleaved, HappyEyeballsConnectionImpl::sortAddresses(v6_then_v4)); + + std::vector v6_then_single_v4 = {ip_v6_1, ip_v6_2, ip_v6_3, + ip_v4_1}; + std::vector interleaved2 = {ip_v6_1, ip_v4_1, ip_v6_2, ip_v6_3}; + EXPECT_EQ(interleaved2, HappyEyeballsConnectionImpl::sortAddresses(v6_then_single_v4)); + + std::vector mixed = {ip_v6_1, ip_v6_2, ip_v6_3, ip_v4_1, + ip_v4_2, ip_v4_3, ip_v4_4, ip_v6_4}; + std::vector interleaved3 = {ip_v6_1, ip_v4_1, ip_v6_2, ip_v4_2, + ip_v6_3, ip_v4_3, ip_v6_4, ip_v4_4}; + EXPECT_EQ(interleaved3, HappyEyeballsConnectionImpl::sortAddresses(mixed)); +} + } // namespace Network } // namespace Envoy From 3da250c6759ed9d2698e4e626fe1146cf696c316 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 16 Nov 2021 14:26:50 -0500 Subject: [PATCH 092/110] stream_info: adding more upstream timing metrics. (#18976) Signed-off-by: Alyssa Wilk --- docs/root/version_history/current.rst | 1 + envoy/stream_info/stream_info.h | 25 +++++++++++++++++++ source/common/network/connection_impl.cc | 9 ++++++- source/common/network/connection_impl.h | 6 +++++ .../common/quic/envoy_quic_client_session.cc | 4 +++ source/common/router/upstream_request.cc | 5 ++++ source/common/stream_info/stream_info_impl.h | 3 +++ .../transport_sockets/tls/ssl_socket.cc | 3 +++ test/common/stream_info/test_util.h | 4 +++ .../transport_sockets/tls/ssl_socket_test.cc | 4 +-- .../filters/stream_info_to_headers_filter.cc | 23 +++++++++++++++++ .../multiplexed_upstream_integration_test.cc | 6 +++++ test/mocks/stream_info/mocks.cc | 2 ++ test/mocks/stream_info/mocks.h | 3 +++ 14 files changed, 95 insertions(+), 3 deletions(-) diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index c65a1fb0bf799..fe319d2c30ba6 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -62,6 +62,7 @@ New Features * dns_resolver: added :ref:`AppleDnsResolverConfig` to support apple DNS resolver as an extension. * ext_authz: added :ref:`query_parameters_to_set ` and :ref:`query_parameters_to_remove ` for adding and removing query string parameters when using a gRPC authorization server. * http: added support for :ref:`retriable health check status codes `. +* http: added timing information about upstream connection and encryption establishment to stream info. These can currently be accessed via custom access loggers. * listener: added API for extensions to access :ref:`typed_filter_metadata ` configured in the listener's :ref:`metadata ` field. * listener: added support for :ref:`MPTCP ` (multipath TCP). * oauth filter: added :ref:`cookie_names ` to allow overriding (default) cookie names (``BearerToken``, ``OauthHMAC``, and ``OauthExpires``) set by the filter. diff --git a/envoy/stream_info/stream_info.h b/envoy/stream_info/stream_info.h index 9d187570300b8..c81720053b047 100644 --- a/envoy/stream_info/stream_info.h +++ b/envoy/stream_info/stream_info.h @@ -232,10 +232,27 @@ struct UpstreamTiming { last_upstream_rx_byte_received_ = time_source.monotonicTime(); } + void onUpstreamConnectStart(TimeSource& time_source) { + ASSERT(!upstream_connect_start_); + upstream_connect_start_ = time_source.monotonicTime(); + } + + void onUpstreamConnectComplete(TimeSource& time_source) { + upstream_connect_complete_ = time_source.monotonicTime(); + } + + void onUpstreamHandshakeComplete(TimeSource& time_source) { + upstream_handshake_complete_ = time_source.monotonicTime(); + } + absl::optional first_upstream_tx_byte_sent_; absl::optional last_upstream_tx_byte_sent_; absl::optional first_upstream_rx_byte_received_; absl::optional last_upstream_rx_byte_received_; + + absl::optional upstream_connect_start_; + absl::optional upstream_connect_complete_; + absl::optional upstream_handshake_complete_; }; class DownstreamTiming { @@ -405,6 +422,14 @@ class StreamInfo { */ virtual void setUpstreamTiming(const UpstreamTiming& upstream_timing) PURE; + /** + * Returns the upstream timing information for this stream. + * It is not expected that the fields in upstreamTiming() will be set until + * the upstream request is complete. + */ + virtual UpstreamTiming& upstreamTiming() PURE; + virtual const UpstreamTiming& upstreamTiming() const PURE; + /** * @return the duration between the first byte of the request was sent upstream and the start of * the request. There may be a considerable delta between lastDownstreamByteReceived and this diff --git a/source/common/network/connection_impl.cc b/source/common/network/connection_impl.cc index edbd5adbe34a8..ad3b80033e809 100644 --- a/source/common/network/connection_impl.cc +++ b/source/common/network/connection_impl.cc @@ -671,6 +671,7 @@ void ConnectionImpl::onWriteReady() { if (error == 0) { ENVOY_CONN_LOG(debug, "connected", *this); connecting_ = false; + onConnected(); transport_socket_->onConnected(); // It's possible that we closed during the connect callback. if (state() != State::Open) { @@ -845,7 +846,7 @@ ClientConnectionImpl::ClientConnectionImpl( const Network::ConnectionSocket::OptionsSharedPtr& options) : ConnectionImpl(dispatcher, std::move(socket), std::move(transport_socket), stream_info_, false), - stream_info_(dispatcher.timeSource(), socket_->connectionInfoProviderSharedPtr()) { + stream_info_(dispatcher_.timeSource(), socket_->connectionInfoProviderSharedPtr()) { // There are no meaningful socket options or source address semantics for // non-IP sockets, so skip. @@ -890,6 +891,7 @@ void ClientConnectionImpl::connect() { socket_->connectionInfoProvider().remoteAddress()->asString()); const Api::SysCallIntResult result = socket_->connect(socket_->connectionInfoProvider().remoteAddress()); + stream_info_.upstreamTiming().onUpstreamConnectStart(dispatcher_.timeSource()); if (result.return_value_ == 0) { // write will become ready. ASSERT(connecting_); @@ -918,5 +920,10 @@ void ClientConnectionImpl::connect() { } } +void ClientConnectionImpl::onConnected() { + stream_info_.upstreamTiming().onUpstreamConnectComplete(dispatcher_.timeSource()); + ConnectionImpl::onConnected(); +} + } // namespace Network } // namespace Envoy diff --git a/source/common/network/connection_impl.h b/source/common/network/connection_impl.h index e20119ffb095a..85756548dfbaa 100644 --- a/source/common/network/connection_impl.h +++ b/source/common/network/connection_impl.h @@ -149,6 +149,10 @@ class ConnectionImpl : public ConnectionImplBase, public TransportSocketCallback void onWriteBufferLowWatermark(); void onWriteBufferHighWatermark(); + // This is called when the underlying socket is connected, not when the + // connected event is raised. + virtual void onConnected() {} + TransportSocketPtr transport_socket_; ConnectionSocketPtr socket_; StreamInfo::StreamInfo& stream_info_; @@ -254,6 +258,8 @@ class ClientConnectionImpl : public ConnectionImpl, virtual public ClientConnect void connect() override; private: + void onConnected() override; + StreamInfo::StreamInfoImpl stream_info_; }; diff --git a/source/common/quic/envoy_quic_client_session.cc b/source/common/quic/envoy_quic_client_session.cc index 78fce45499b76..6811493834b3e 100644 --- a/source/common/quic/envoy_quic_client_session.cc +++ b/source/common/quic/envoy_quic_client_session.cc @@ -31,6 +31,7 @@ EnvoyQuicClientSession::~EnvoyQuicClientSession() { absl::string_view EnvoyQuicClientSession::requestedServerName() const { return server_id().host(); } void EnvoyQuicClientSession::connect() { + streamInfo().upstreamTiming().onUpstreamConnectStart(dispatcher_.timeSource()); dynamic_cast(network_connection_) ->setUpConnectionSocket( *static_cast(connection())->connectionSocket(), *this); @@ -132,6 +133,9 @@ void EnvoyQuicClientSession::OnTlsHandshakeComplete() { // before use. This may result in OnCanCreateNewOutgoingStream with zero // available streams. OnCanCreateNewOutgoingStream(false); + streamInfo().upstreamTiming().onUpstreamConnectComplete(dispatcher_.timeSource()); + streamInfo().upstreamTiming().onUpstreamHandshakeComplete(dispatcher_.timeSource()); + raiseConnectionEvent(Network::ConnectionEvent::Connected); } diff --git a/source/common/router/upstream_request.cc b/source/common/router/upstream_request.cc index b406c0f37afd3..3098174538746 100644 --- a/source/common/router/upstream_request.cc +++ b/source/common/router/upstream_request.cc @@ -427,6 +427,11 @@ void UpstreamRequest::onPoolReady( stream_info_.protocol(protocol.value()); } + upstream_timing_.upstream_connect_start_ = info.upstreamTiming().upstream_connect_start_; + upstream_timing_.upstream_connect_complete_ = info.upstreamTiming().upstream_connect_complete_; + upstream_timing_.upstream_handshake_complete_ = + info.upstreamTiming().upstream_handshake_complete_; + stream_info_.setUpstreamFilterState(std::make_shared( info.filterState().parent()->parent(), StreamInfo::FilterState::LifeSpan::Request)); parent_.callbacks()->streamInfo().setUpstreamFilterState( diff --git a/source/common/stream_info/stream_info_impl.h b/source/common/stream_info/stream_info_impl.h index fa7c2ead3169e..f66f5e891de5f 100644 --- a/source/common/stream_info/stream_info_impl.h +++ b/source/common/stream_info/stream_info_impl.h @@ -86,6 +86,9 @@ struct StreamInfoImpl : public StreamInfo { upstream_timing_ = upstream_timing; } + UpstreamTiming& upstreamTiming() override { return upstream_timing_; } + const UpstreamTiming& upstreamTiming() const override { return upstream_timing_; } + absl::optional firstUpstreamTxByteSent() const override { return duration(upstream_timing_.first_upstream_tx_byte_sent_); } diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index e59ca7b802302..c94634a4c675c 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -43,6 +43,7 @@ class NotReadySslSocket : public Network::TransportSocket { Ssl::ConnectionInfoConstSharedPtr ssl() const override { return nullptr; } bool startSecureTransport() override { return false; } }; + } // namespace SslSocket::SslSocket(Envoy::Ssl::ContextSharedPtr ctx, InitialState state, @@ -179,6 +180,8 @@ Network::Connection& SslSocket::connection() const { return callbacks_->connecti void SslSocket::onSuccess(SSL* ssl) { ctx_->logHandshake(ssl); + callbacks_->connection().streamInfo().upstreamTiming().onUpstreamHandshakeComplete( + callbacks_->connection().dispatcher().timeSource()); callbacks_->raiseEvent(Network::ConnectionEvent::Connected); } diff --git a/test/common/stream_info/test_util.h b/test/common/stream_info/test_util.h index e1101fee6871f..4d4f40b066829 100644 --- a/test/common/stream_info/test_util.h +++ b/test/common/stream_info/test_util.h @@ -137,6 +137,10 @@ class TestStreamInfo : public StreamInfo::StreamInfo { void setUpstreamTiming(const Envoy::StreamInfo::UpstreamTiming& upstream_timing) override { upstream_timing_ = upstream_timing; } + Envoy::StreamInfo::UpstreamTiming& upstreamTiming() override { return upstream_timing_; } + const Envoy::StreamInfo::UpstreamTiming& upstreamTiming() const override { + return upstream_timing_; + } absl::optional requestComplete() const override { return duration(end_time_); diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index 384c61cf52c34..751d9e5459b9d 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -363,7 +363,7 @@ void testUtil(const TestUtilOptions& options) { client_ssl_socket_factory.createTransportSocket(nullptr), nullptr); Network::ConnectionPtr server_connection; Network::MockConnectionCallbacks server_connection_callbacks; - StreamInfo::MockStreamInfo stream_info; + NiceMock stream_info; EXPECT_CALL(callbacks, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection = dispatcher->createServerConnection( @@ -708,7 +708,7 @@ void testUtilV2(const TestUtilOptionsV2& options) { Network::ConnectionPtr server_connection; Network::MockConnectionCallbacks server_connection_callbacks; - StreamInfo::MockStreamInfo stream_info; + NiceMock stream_info; EXPECT_CALL(callbacks, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { std::string sni = options.transportSocketOptions() != nullptr && diff --git a/test/integration/filters/stream_info_to_headers_filter.cc b/test/integration/filters/stream_info_to_headers_filter.cc index 30aedfe2b6212..190cb006ee740 100644 --- a/test/integration/filters/stream_info_to_headers_filter.cc +++ b/test/integration/filters/stream_info_to_headers_filter.cc @@ -24,8 +24,31 @@ class StreamInfoToHeadersFilter : public Http::PassThroughFilter { headers.addCopy(Http::LowerCaseString("alpn"), decoder_callbacks_->streamInfo().upstreamSslConnection()->alpn()); } + return Http::FilterHeadersStatus::Continue; } + Http::FilterTrailersStatus encodeTrailers(Http::ResponseTrailerMap& trailers) override { + StreamInfo::UpstreamTiming& upstream_timing = decoder_callbacks_->streamInfo().upstreamTiming(); + // Upstream metrics aren't available until the response is complete. + if (upstream_timing.upstream_connect_start_.has_value()) { + trailers.addCopy( + Http::LowerCaseString("upstream_connect_start"), + absl::StrCat(upstream_timing.upstream_connect_start_.value().time_since_epoch().count())); + } + if (upstream_timing.upstream_connect_complete_.has_value()) { + trailers.addCopy( + Http::LowerCaseString("upstream_connect_complete"), + absl::StrCat( + upstream_timing.upstream_connect_complete_.value().time_since_epoch().count())); + } + if (upstream_timing.upstream_handshake_complete_.has_value()) { + trailers.addCopy( + Http::LowerCaseString("upstream_handshake_complete"), + absl::StrCat( + upstream_timing.upstream_handshake_complete_.value().time_since_epoch().count())); + } + return Http::FilterTrailersStatus::Continue; + } }; constexpr char StreamInfoToHeadersFilter::name[]; diff --git a/test/integration/multiplexed_upstream_integration_test.cc b/test/integration/multiplexed_upstream_integration_test.cc index be204334e66c3..d6b5baf652f3e 100644 --- a/test/integration/multiplexed_upstream_integration_test.cc +++ b/test/integration/multiplexed_upstream_integration_test.cc @@ -152,6 +152,12 @@ void MultiplexedUpstreamIntegrationTest::bidirectionalStreaming(uint32_t bytes) ASSERT_FALSE(response->headers().get(Http::LowerCaseString("alpn")).empty()); ASSERT_EQ(response->headers().get(Http::LowerCaseString("alpn"))[0]->value().getStringView(), expected_alpn); + + ASSERT_FALSE(response->trailers()->get(Http::LowerCaseString("upstream_connect_start")).empty()); + ASSERT_FALSE( + response->trailers()->get(Http::LowerCaseString("upstream_connect_complete")).empty()); + ASSERT_FALSE( + response->trailers()->get(Http::LowerCaseString("upstream_handshake_complete")).empty()); } TEST_P(MultiplexedUpstreamIntegrationTest, BidirectionalStreaming) { bidirectionalStreaming(1024); } diff --git a/test/mocks/stream_info/mocks.cc b/test/mocks/stream_info/mocks.cc index 5f651e4130e5a..4bdd1eaba8632 100644 --- a/test/mocks/stream_info/mocks.cc +++ b/test/mocks/stream_info/mocks.cc @@ -141,6 +141,8 @@ MockStreamInfo::MockStreamInfo() .WillByDefault(Invoke([this](const BytesMeterSharedPtr& downstream_bytes_meter) { downstream_bytes_meter_ = downstream_bytes_meter; })); + ON_CALL(*this, upstreamTiming()).WillByDefault(ReturnRef(upstream_timing_)); + ON_CALL(Const(*this), upstreamTiming()).WillByDefault(ReturnRef(upstream_timing_)); } MockStreamInfo::~MockStreamInfo() = default; diff --git a/test/mocks/stream_info/mocks.h b/test/mocks/stream_info/mocks.h index b0355ea773909..115a0f283c39d 100644 --- a/test/mocks/stream_info/mocks.h +++ b/test/mocks/stream_info/mocks.h @@ -31,6 +31,8 @@ class MockStreamInfo : public StreamInfo { MOCK_METHOD(MonotonicTime, startTimeMonotonic, (), (const)); MOCK_METHOD(absl::optional, lastDownstreamRxByteReceived, (), (const)); MOCK_METHOD(void, setUpstreamTiming, (const UpstreamTiming&)); + MOCK_METHOD(UpstreamTiming&, upstreamTiming, ()); + MOCK_METHOD(const UpstreamTiming&, upstreamTiming, (), (const)); MOCK_METHOD(absl::optional, firstUpstreamTxByteSent, (), (const)); MOCK_METHOD(void, onFirstUpstreamTxByteSent, ()); MOCK_METHOD(absl::optional, lastUpstreamTxByteSent, (), (const)); @@ -122,6 +124,7 @@ class MockStreamInfo : public StreamInfo { absl::optional response_code_; absl::optional response_code_details_; absl::optional connection_termination_details_; + UpstreamTiming upstream_timing_; uint64_t response_flags_{}; envoy::config::core::v3::Metadata metadata_; FilterStateSharedPtr upstream_filter_state_; From de5758965db14577aae3de692bad1b97cdd29eae Mon Sep 17 00:00:00 2001 From: Tarun Sharma Date: Wed, 17 Nov 2021 01:30:12 +0530 Subject: [PATCH 093/110] remove unnecessary std::string in source (#18948) Signed-off-by: Tarun Sharma --- source/common/common/utility.cc | 4 ++-- source/common/common/utility.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/common/common/utility.cc b/source/common/common/utility.cc index 06a2a7027fc46..d5cd020ab8af7 100644 --- a/source/common/common/utility.cc +++ b/source/common/common/utility.cc @@ -194,7 +194,7 @@ void DateFormatter::parse(const std::string& format_string) { std::string DateFormatter::fromTimeAndPrepareSpecifierOffsets(time_t time, SpecifierOffsets& specifier_offsets, - const std::string& seconds_str) const { + const absl::string_view seconds_str) const { std::string formatted_time; int32_t previous = 0; @@ -435,7 +435,7 @@ std::string StringUtil::subspan(absl::string_view source, size_t start, size_t e return std::string(source.data() + start, end - start); } -std::string StringUtil::escape(const std::string& source) { +std::string StringUtil::escape(const absl::string_view source) { std::string ret; // Prevent unnecessary allocation by allocating 2x original size. diff --git a/source/common/common/utility.h b/source/common/common/utility.h index 24b29a1273500..b41286f8851f1 100644 --- a/source/common/common/utility.h +++ b/source/common/common/utility.h @@ -56,7 +56,7 @@ class DateFormatter { using SpecifierOffsets = std::vector; std::string fromTimeAndPrepareSpecifierOffsets(time_t time, SpecifierOffsets& specifier_offsets, - const std::string& seconds_str) const; + const absl::string_view seconds_str) const; // A container to hold a specifiers (%f, %Nf, %s) found in a format string. struct Specifier { @@ -404,7 +404,7 @@ class StringUtil { * @param source supplies the string to escape. * @return escaped string. */ - static std::string escape(const std::string& source); + static std::string escape(const absl::string_view source); /** * Outputs the string to the provided ostream, while escaping \n, \r, \t, and " From c8cdcee2cbfaa8e32919569c3b55428073695675 Mon Sep 17 00:00:00 2001 From: Matt Klein Date: Tue, 16 Nov 2021 15:38:58 -0800 Subject: [PATCH 094/110] redis cluster: remove experimental from docs (#19025) Known large scale production use. Signed-off-by: Matt Klein --- docs/root/intro/arch_overview/other_protocols/redis.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/root/intro/arch_overview/other_protocols/redis.rst b/docs/root/intro/arch_overview/other_protocols/redis.rst index cfe2a45b77b43..0349a0d8b888c 100644 --- a/docs/root/intro/arch_overview/other_protocols/redis.rst +++ b/docs/root/intro/arch_overview/other_protocols/redis.rst @@ -62,10 +62,10 @@ close map to 5xx. All other responses from Redis are counted as a success. .. _arch_overview_redis_cluster_support: -Redis Cluster Support (Experimental) ----------------------------------------- +Redis Cluster Support +--------------------- -Envoy currently offers experimental support for `Redis Cluster `_. +Envoy offers support for `Redis Cluster `_. When using Envoy as a sidecar proxy for a Redis Cluster, the service can use a non-cluster Redis client implemented in any language to connect to the proxy as if it's a single node Redis instance. From b610fba9a94217ed48c939b04278635e7f2b2283 Mon Sep 17 00:00:00 2001 From: chaoqin-li1123 <55518381+chaoqin-li1123@users.noreply.github.com> Date: Tue, 16 Nov 2021 22:32:26 -0600 Subject: [PATCH 095/110] [filter state]: make jsonConvert no throw (#18863) Currently FilterStateFormatter::formatValue is converting ProtobufTypes::Message to ProtobufWkt::Value through intermediate json string format, which is not efficient and may throw exception in data plane. Add serializeAsProtoValue virtual method to do the conversion directly. Additional Description: NA Risk Level: Low Testing: NA Docs Changes: NA Release Notes: NA Signed-off-by: chaoqin-li1123 --- .../formatter/substitution_formatter.cc | 10 ++-- source/common/protobuf/utility.cc | 49 ++++++++++++++----- source/common/protobuf/utility.h | 13 ++++- test/common/protobuf/utility_test.cc | 13 +++-- 4 files changed, 63 insertions(+), 22 deletions(-) diff --git a/source/common/formatter/substitution_formatter.cc b/source/common/formatter/substitution_formatter.cc index 3e4c66c00b1a1..fb0cba19ff945 100644 --- a/source/common/formatter/substitution_formatter.cc +++ b/source/common/formatter/substitution_formatter.cc @@ -1345,14 +1345,10 @@ ProtobufWkt::Value FilterStateFormatter::formatValue(const Http::RequestHeaderMa } ProtobufWkt::Value val; - // TODO(chaoqin-li1123): make this conversion return an error status instead of throwing. - // Access logger conversion from protobufs occurs via json intermediate state, which can throw - // when converting that to a structure. - TRY_NEEDS_AUDIT { MessageUtil::jsonConvertValue(*proto, val); } - catch (EnvoyException& ex) { - return unspecifiedValue(); + if (MessageUtil::jsonConvertValue(*proto, val)) { + return val; } - return val; + return unspecifiedValue(); } // Given a token, extract the command string between parenthesis if it exists. diff --git a/source/common/protobuf/utility.cc b/source/common/protobuf/utility.cc index 7e5d60d0752b7..006182f0d7ac0 100644 --- a/source/common/protobuf/utility.cc +++ b/source/common/protobuf/utility.cc @@ -240,6 +240,25 @@ size_t MessageUtil::hash(const Protobuf::Message& message) { void MessageUtil::loadFromJson(const std::string& json, Protobuf::Message& message, ProtobufMessage::ValidationVisitor& validation_visitor) { + bool has_unknown_field; + auto status = loadFromJsonNoThrow(json, message, has_unknown_field); + if (status.ok()) { + return; + } + if (has_unknown_field) { + // If the parsing failure is caused by the unknown fields. + validation_visitor.onUnknownField("type " + message.GetTypeName() + " reason " + + status.ToString()); + } else { + // If the error has nothing to do with unknown field. + throw EnvoyException("Unable to parse JSON as proto (" + status.ToString() + "): " + json); + } +} + +Protobuf::util::Status MessageUtil::loadFromJsonNoThrow(const std::string& json, + Protobuf::Message& message, + bool& has_unknown_fileld) { + has_unknown_fileld = false; Protobuf::util::JsonParseOptions options; options.case_insensitive_enum_parsing = true; // Let's first try and get a clean parse when checking for unknown fields; @@ -248,7 +267,7 @@ void MessageUtil::loadFromJson(const std::string& json, Protobuf::Message& messa const auto strict_status = Protobuf::util::JsonStringToMessage(json, &message, options); if (strict_status.ok()) { // Success, no need to do any extra work. - return; + return strict_status; } // If we fail, we see if we get a clean parse when allowing unknown fields. // This is essentially a workaround @@ -259,15 +278,11 @@ void MessageUtil::loadFromJson(const std::string& json, Protobuf::Message& messa const auto relaxed_status = Protobuf::util::JsonStringToMessage(json, &message, options); // If we still fail with relaxed unknown field checking, the error has nothing // to do with unknown fields. - if (!relaxed_status.ok()) { - throw EnvoyException("Unable to parse JSON as proto (" + relaxed_status.ToString() + - "): " + json); + if (relaxed_status.ok()) { + has_unknown_fileld = true; + return strict_status; } - // We know it's an unknown field at this point. If we're at the latest - // version, then it's definitely an unknown field, otherwise we try to - // load again at a later version. - validation_visitor.onUnknownField("type " + message.GetTypeName() + " reason " + - strict_status.ToString()); + return relaxed_status; } void MessageUtil::loadFromJson(const std::string& json, ProtobufWkt::Struct& message) { @@ -526,8 +541,20 @@ void MessageUtil::jsonConvert(const ProtobufWkt::Struct& source, jsonConvertInternal(source, validation_visitor, dest); } -void MessageUtil::jsonConvertValue(const Protobuf::Message& source, ProtobufWkt::Value& dest) { - jsonConvertInternal(source, ProtobufMessage::getNullValidationVisitor(), dest); +bool MessageUtil::jsonConvertValue(const Protobuf::Message& source, ProtobufWkt::Value& dest) { + Protobuf::util::JsonPrintOptions json_options; + json_options.preserve_proto_field_names = true; + std::string json; + auto status = Protobuf::util::MessageToJsonString(source, &json, json_options); + if (!status.ok()) { + return false; + } + bool has_unknow_field; + status = MessageUtil::loadFromJsonNoThrow(json, dest, has_unknow_field); + if (status.ok() || has_unknow_field) { + return true; + } + return false; } ProtobufWkt::Struct MessageUtil::keyValueStruct(const std::string& key, const std::string& value) { diff --git a/source/common/protobuf/utility.h b/source/common/protobuf/utility.h index e8f4a61cd90b6..f4107c669c285 100644 --- a/source/common/protobuf/utility.h +++ b/source/common/protobuf/utility.h @@ -252,6 +252,16 @@ class MessageUtil { static void loadFromJson(const std::string& json, Protobuf::Message& message, ProtobufMessage::ValidationVisitor& validation_visitor); + /** + * Return ok only when strict conversion(don't ignore unknown field) succeeds. + * Return error status for strict conversion and set has_unknown_field to true if relaxed + * conversion(ignore unknown field) succeeds. + * Return error status for relaxed conversion and set has_unknown_field to false if relaxed + * conversion(ignore unknown field) fails. + */ + static Protobuf::util::Status loadFromJsonNoThrow(const std::string& json, + Protobuf::Message& message, + bool& has_unknown_fileld); static void loadFromJson(const std::string& json, ProtobufWkt::Struct& message); static void loadFromYaml(const std::string& yaml, Protobuf::Message& message, ProtobufMessage::ValidationVisitor& validation_visitor); @@ -416,7 +426,8 @@ class MessageUtil { static void jsonConvert(const ProtobufWkt::Struct& source, ProtobufMessage::ValidationVisitor& validation_visitor, Protobuf::Message& dest); - static void jsonConvertValue(const Protobuf::Message& source, ProtobufWkt::Value& dest); + // Convert a message to a ProtobufWkt::Value, return false upon failure. + static bool jsonConvertValue(const Protobuf::Message& source, ProtobufWkt::Value& dest); /** * Extract YAML as string from a google.protobuf.Message. diff --git a/test/common/protobuf/utility_test.cc b/test/common/protobuf/utility_test.cc index c89d4f78c5003..4f34718a4f211 100644 --- a/test/common/protobuf/utility_test.cc +++ b/test/common/protobuf/utility_test.cc @@ -1486,14 +1486,14 @@ TEST_F(ProtobufUtilityTest, JsonConvertCamelSnake) { .string_value()); } -// Test the jsonConvertValue happy path. Failure modes are converted by jsonConvert tests. +// Test the jsonConvertValue in both success and failure modes. TEST_F(ProtobufUtilityTest, JsonConvertValueSuccess) { { envoy::config::bootstrap::v3::Bootstrap source; source.set_flags_path("foo"); ProtobufWkt::Value tmp; envoy::config::bootstrap::v3::Bootstrap dest; - MessageUtil::jsonConvertValue(source, tmp); + EXPECT_TRUE(MessageUtil::jsonConvertValue(source, tmp)); TestUtility::jsonConvert(tmp, dest); EXPECT_EQ("foo", dest.flags_path()); } @@ -1502,12 +1502,19 @@ TEST_F(ProtobufUtilityTest, JsonConvertValueSuccess) { ProtobufWkt::StringValue source; source.set_value("foo"); ProtobufWkt::Value dest; - MessageUtil::jsonConvertValue(source, dest); + EXPECT_TRUE(MessageUtil::jsonConvertValue(source, dest)); ProtobufWkt::Value expected; expected.set_string_value("foo"); EXPECT_THAT(dest, ProtoEq(expected)); } + + { + ProtobufWkt::Duration source; + source.set_seconds(-281474976710656); + ProtobufWkt::Value dest; + EXPECT_FALSE(MessageUtil::jsonConvertValue(source, dest)); + } } TEST_F(ProtobufUtilityTest, YamlLoadFromStringFail) { From f4a1ad29fc00044990dfed7f14ffea01ba02cf0d Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 17 Nov 2021 04:00:49 -0500 Subject: [PATCH 096/110] stream_info: removing redundant test code. (#19019) Every update to the stream into interface requires updates to the impl, the mocks, and the test class. Making the test class inherit from the impl to reduce churn. Risk Level: n/a (test only) Testing: n/a Docs Changes: n/a Release Notes: n/a Signed-off-by: Alyssa Wilk --- .../common/access_log/access_log_impl_test.cc | 30 ++- .../substitution_formatter_fuzz_test.cc | 4 +- .../substitution_formatter_speed_test.cc | 19 +- test/common/router/header_parser_fuzz_test.cc | 4 +- test/common/stream_info/test_util.h | 223 +----------------- .../substitution_formatter_speed_test.cc | 5 +- .../common/expr/evaluator_fuzz_test.cc | 3 +- .../filters/network/mongo_proxy/proxy_test.cc | 4 +- test/fuzz/utility.h | 7 +- 9 files changed, 53 insertions(+), 246 deletions(-) diff --git a/test/common/access_log/access_log_impl_test.cc b/test/common/access_log/access_log_impl_test.cc index 07b03106064b3..93cf53e0baf5d 100644 --- a/test/common/access_log/access_log_impl_test.cc +++ b/test/common/access_log/access_log_impl_test.cc @@ -47,13 +47,17 @@ envoy::config::accesslog::v3::AccessLog parseAccessLogFromV3Yaml(const std::stri class AccessLogImplTest : public Event::TestUsingSimulatedTime, public testing::Test { public: - AccessLogImplTest() : file_(new MockAccessLogFile()) { + AccessLogImplTest() : stream_info_(time_source_), file_(new MockAccessLogFile()) { ON_CALL(context_, runtime()).WillByDefault(ReturnRef(runtime_)); ON_CALL(context_, accessLogManager()).WillByDefault(ReturnRef(log_manager_)); ON_CALL(log_manager_, createAccessLog(_)).WillByDefault(Return(file_)); ON_CALL(*file_, write(_)).WillByDefault(SaveArg<0>(&output_)); + stream_info_.addBytesReceived(1); + stream_info_.addBytesSent(2); + stream_info_.protocol(Http::Protocol::Http11); } + NiceMock time_source_; Http::TestRequestHeaderMapImpl request_headers_{{":method", "GET"}, {":path", "/"}}; Http::TestResponseHeaderMapImpl response_headers_; Http::TestResponseTrailerMapImpl response_trailers_; @@ -77,7 +81,7 @@ name: accesslog InstanceSharedPtr log = AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(yaml), context_); EXPECT_CALL(*file_, write(_)); - stream_info_.response_flags_ = StreamInfo::ResponseFlag::UpstreamConnectionFailure; + stream_info_.setResponseFlag(StreamInfo::ResponseFlag::UpstreamConnectionFailure); request_headers_.addCopy(Http::Headers::get().UserAgent, "user-agent-set"); request_headers_.addCopy(Http::Headers::get().RequestId, "id"); request_headers_.addCopy(Http::Headers::get().Host, "host"); @@ -104,7 +108,7 @@ name: accesslog auto cluster = std::make_shared>(); stream_info_.upstream_host_ = Upstream::makeTestHostDescription(cluster, "tcp://10.0.0.5:1234", simTime()); - stream_info_.response_flags_ = StreamInfo::ResponseFlag::DownstreamConnectionTermination; + stream_info_.setResponseFlag(StreamInfo::ResponseFlag::DownstreamConnectionTermination); log->log(&request_headers_, &response_headers_, &response_trailers_, stream_info_); EXPECT_EQ("[1999-01-01T00:00:00.000Z] \"GET / HTTP/1.1\" 0 DC 1 2 3 - \"-\" \"-\" \"-\" \"-\" " @@ -126,8 +130,8 @@ name: accesslog InstanceSharedPtr log = AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(yaml), context_); EXPECT_CALL(*file_, write(_)); - stream_info_.route_name_ = "route-test-name"; - stream_info_.response_flags_ = StreamInfo::ResponseFlag::UpstreamConnectionFailure; + stream_info_.setRouteName("route-test-name"); + stream_info_.setResponseFlag(StreamInfo::ResponseFlag::UpstreamConnectionFailure); request_headers_.addCopy(Http::Headers::get().UserAgent, "user-agent-set"); request_headers_.addCopy(Http::Headers::get().RequestId, "id"); request_headers_.addCopy(Http::Headers::get().Host, "host"); @@ -598,7 +602,7 @@ name: accesslog )EOF"; InstanceSharedPtr log = AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(yaml), context_); - stream_info_.response_code_ = 500; + stream_info_.setResponseCode(500); { EXPECT_CALL(*file_, write(_)); @@ -677,7 +681,8 @@ TEST(AccessLogFilterTest, DurationWithRuntimeKey) { Http::TestRequestHeaderMapImpl request_headers{{":method", "GET"}, {":path", "/"}}; Http::TestResponseHeaderMapImpl response_headers; Http::TestResponseTrailerMapImpl response_trailers; - TestStreamInfo stream_info; + NiceMock time_source; + TestStreamInfo stream_info(time_source); stream_info.end_time_ = stream_info.startTimeMonotonic() + std::chrono::microseconds(100000); EXPECT_CALL(runtime.snapshot_, getInteger("key", 1000000)).WillOnce(Return(1)); @@ -712,10 +717,11 @@ TEST(AccessLogFilterTest, StatusCodeWithRuntimeKey) { TestUtility::loadFromYaml(filter_yaml, config); StatusCodeFilter filter(config.status_code_filter(), runtime); + NiceMock time_source; Http::TestRequestHeaderMapImpl request_headers{{":method", "GET"}, {":path", "/"}}; Http::TestResponseHeaderMapImpl response_headers; Http::TestResponseTrailerMapImpl response_trailers; - TestStreamInfo info; + TestStreamInfo info(time_source); info.response_code_ = 400; EXPECT_CALL(runtime.snapshot_, getInteger("key", 300)).WillOnce(Return(350)); @@ -1002,7 +1008,7 @@ name: accesslog StreamInfo::ResponseFlagUtils::ALL_RESPONSE_STRING_FLAGS) { UNREFERENCED_PARAMETER(flag_string); - TestStreamInfo stream_info; + TestStreamInfo stream_info(time_source_); stream_info.setResponseFlag(response_flag); EXPECT_CALL(*file_, write(_)); log->log(&request_headers_, &response_headers_, &response_trailers_, stream_info); @@ -1323,7 +1329,7 @@ name: accesslog path: /dev/null )EOF"; - TestStreamInfo stream_info; + TestStreamInfo stream_info(time_source_); ProtobufWkt::Struct metadata_val; auto& fields_a = *metadata_val.mutable_fields(); auto& struct_b = *fields_a["a"].mutable_struct_value(); @@ -1359,7 +1365,7 @@ name: accesslog path: /dev/null )EOF"; - TestStreamInfo stream_info; + TestStreamInfo stream_info(time_source_); ProtobufWkt::Struct metadata_val; stream_info.setDynamicMetadata("some.namespace", metadata_val); @@ -1406,7 +1412,7 @@ name: accesslog path: /dev/null )EOF"; - TestStreamInfo stream_info; + TestStreamInfo stream_info(time_source_); ProtobufWkt::Struct metadata_val; auto& fields_a = *metadata_val.mutable_fields(); auto& struct_b = *fields_a["a"].mutable_struct_value(); diff --git a/test/common/formatter/substitution_formatter_fuzz_test.cc b/test/common/formatter/substitution_formatter_fuzz_test.cc index f92a34885023d..6a758272a7175 100644 --- a/test/common/formatter/substitution_formatter_fuzz_test.cc +++ b/test/common/formatter/substitution_formatter_fuzz_test.cc @@ -19,7 +19,9 @@ DEFINE_PROTO_FUZZER(const test::common::substitution::TestCase& input) { Fuzz::fromHeaders(input.response_headers()); const auto& response_trailers = Fuzz::fromHeaders(input.response_trailers()); - const std::unique_ptr stream_info = Fuzz::fromStreamInfo(input.stream_info()); + MockTimeSystem time_system; + const std::unique_ptr stream_info = + Fuzz::fromStreamInfo(input.stream_info(), time_system); for (const auto& it : formatters) { it->format(request_headers, response_headers, response_trailers, *stream_info, absl::string_view()); diff --git a/test/common/formatter/substitution_formatter_speed_test.cc b/test/common/formatter/substitution_formatter_speed_test.cc index 7c919bfc4c100..13f33f25e53ba 100644 --- a/test/common/formatter/substitution_formatter_speed_test.cc +++ b/test/common/formatter/substitution_formatter_speed_test.cc @@ -46,8 +46,8 @@ std::unique_ptr makeStructFormatter(bool type return std::make_unique(StructLogFormat, typed, false); } -std::unique_ptr makeStreamInfo() { - auto stream_info = std::make_unique(); +std::unique_ptr makeStreamInfo(TimeSource& time_source) { + auto stream_info = std::make_unique(time_source); stream_info->downstream_connection_info_provider_->setRemoteAddress( std::make_shared("203.0.113.1")); return stream_info; @@ -57,7 +57,8 @@ std::unique_ptr makeStreamInfo() { // NOLINTNEXTLINE(readability-identifier-naming) static void BM_AccessLogFormatter(benchmark::State& state) { - std::unique_ptr stream_info = makeStreamInfo(); + MockTimeSystem time_system; + std::unique_ptr stream_info = makeStreamInfo(time_system); static const char* LogFormat = "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT% %START_TIME(%Y/%m/%dT%H:%M:%S%z %s)% " "%REQ(:METHOD)% " @@ -83,7 +84,8 @@ BENCHMARK(BM_AccessLogFormatter); // NOLINTNEXTLINE(readability-identifier-naming) static void BM_StructAccessLogFormatter(benchmark::State& state) { - std::unique_ptr stream_info = makeStreamInfo(); + MockTimeSystem time_system; + std::unique_ptr stream_info = makeStreamInfo(time_system); std::unique_ptr struct_formatter = makeStructFormatter(false); size_t output_bytes = 0; @@ -103,7 +105,8 @@ BENCHMARK(BM_StructAccessLogFormatter); // NOLINTNEXTLINE(readability-identifier-naming) static void BM_TypedStructAccessLogFormatter(benchmark::State& state) { - std::unique_ptr stream_info = makeStreamInfo(); + MockTimeSystem time_system; + std::unique_ptr stream_info = makeStreamInfo(time_system); std::unique_ptr typed_struct_formatter = makeStructFormatter(true); @@ -124,7 +127,8 @@ BENCHMARK(BM_TypedStructAccessLogFormatter); // NOLINTNEXTLINE(readability-identifier-naming) static void BM_JsonAccessLogFormatter(benchmark::State& state) { - std::unique_ptr stream_info = makeStreamInfo(); + MockTimeSystem time_system; + std::unique_ptr stream_info = makeStreamInfo(time_system); std::unique_ptr json_formatter = makeJsonFormatter(false); size_t output_bytes = 0; @@ -144,7 +148,8 @@ BENCHMARK(BM_JsonAccessLogFormatter); // NOLINTNEXTLINE(readability-identifier-naming) static void BM_TypedJsonAccessLogFormatter(benchmark::State& state) { - std::unique_ptr stream_info = makeStreamInfo(); + MockTimeSystem time_system; + std::unique_ptr stream_info = makeStreamInfo(time_system); std::unique_ptr typed_json_formatter = makeJsonFormatter(true); diff --git a/test/common/router/header_parser_fuzz_test.cc b/test/common/router/header_parser_fuzz_test.cc index 528846cf0691f..6a514081cceff 100644 --- a/test/common/router/header_parser_fuzz_test.cc +++ b/test/common/router/header_parser_fuzz_test.cc @@ -15,7 +15,9 @@ DEFINE_PROTO_FUZZER(const test::common::router::TestCase& input) { Router::HeaderParserPtr parser = Router::HeaderParser::configure(input.headers_to_add(), input.headers_to_remove()); Http::TestRequestHeaderMapImpl header_map; - std::unique_ptr test_stream_info = fromStreamInfo(input.stream_info()); + MockTimeSystem time_system_; + std::unique_ptr test_stream_info = + fromStreamInfo(input.stream_info(), time_system_); parser->evaluateHeaders(header_map, *test_stream_info); ENVOY_LOG_MISC(trace, "Success"); } catch (const EnvoyException& e) { diff --git a/test/common/stream_info/test_util.h b/test/common/stream_info/test_util.h index 4d4f40b066829..adad891693ba4 100644 --- a/test/common/stream_info/test_util.h +++ b/test/common/stream_info/test_util.h @@ -7,22 +7,21 @@ #include "source/common/common/random_generator.h" #include "source/common/network/socket_impl.h" #include "source/common/stream_info/filter_state_impl.h" +#include "source/common/stream_info/stream_info_impl.h" #include "source/extensions/request_id/uuid/config.h" +#include "test/mocks/common.h" #include "test/test_common/simulated_time_system.h" namespace Envoy { -class TestStreamInfo : public StreamInfo::StreamInfo { +class TestStreamInfo : public StreamInfo::StreamInfoImpl { public: - TestStreamInfo() - : filter_state_(std::make_shared( - Envoy::StreamInfo::FilterState::LifeSpan::FilterChain)) { + TestStreamInfo(TimeSource& time_source) : StreamInfoImpl(time_source, nullptr) { // Use 1999-01-01 00:00:00 +0 time_t fake_time = 915148800; start_time_ = std::chrono::system_clock::from_time_t(fake_time); request_id_provider_ = Extensions::RequestId::UUIDRequestIDExtension::defaultInstance(random_); - MonotonicTime now = timeSystem().monotonicTime(); start_time_monotonic_ = now; end_time_ = now + std::chrono::milliseconds(3); @@ -31,153 +30,16 @@ class TestStreamInfo : public StreamInfo::StreamInfo { SystemTime startTime() const override { return start_time_; } MonotonicTime startTimeMonotonic() const override { return start_time_monotonic_; } - void addBytesReceived(uint64_t) override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } - uint64_t bytesReceived() const override { return 1; } - absl::optional protocol() const override { return protocol_; } - void protocol(Http::Protocol protocol) override { protocol_ = protocol; } - absl::optional responseCode() const override { return response_code_; } - const absl::optional& responseCodeDetails() const override { - return response_code_details_; - } - void setResponseCode(uint32_t code) override { response_code_ = code; } - void setResponseCodeDetails(absl::string_view rc_details) override { - response_code_details_.emplace(rc_details); - } - const absl::optional& connectionTerminationDetails() const override { - return connection_termination_details_; - } - void setConnectionTerminationDetails(absl::string_view details) override { - connection_termination_details_.emplace(details); - } - void addBytesSent(uint64_t) override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } - uint64_t bytesSent() const override { return 2; } - bool intersectResponseFlags(uint64_t response_flags) const override { - return (response_flags_ & response_flags) != 0; - } - bool hasResponseFlag(Envoy::StreamInfo::ResponseFlag response_flag) const override { - return response_flags_ & response_flag; - } - bool hasAnyResponseFlag() const override { return response_flags_ != 0; } - void setResponseFlag(Envoy::StreamInfo::ResponseFlag response_flag) override { - response_flags_ |= response_flag; - } - uint64_t responseFlags() const override { return response_flags_; } - void onUpstreamHostSelected(Upstream::HostDescriptionConstSharedPtr host) override { - upstream_host_ = host; - } - Upstream::HostDescriptionConstSharedPtr upstreamHost() const override { return upstream_host_; } - void setUpstreamLocalAddress( - const Network::Address::InstanceConstSharedPtr& upstream_local_address) override { - upstream_local_address_ = upstream_local_address; - } - const Network::Address::InstanceConstSharedPtr& upstreamLocalAddress() const override { - return upstream_local_address_; - } - bool healthCheck() const override { return health_check_request_; } - void healthCheck(bool is_health_check) override { health_check_request_ = is_health_check; } const Network::ConnectionInfoSetter& downstreamAddressProvider() const override { return *downstream_connection_info_provider_; } - void setUpstreamSslConnection(const Ssl::ConnectionInfoConstSharedPtr& connection_info) override { - upstream_connection_info_ = connection_info; - } - - Ssl::ConnectionInfoConstSharedPtr upstreamSslConnection() const override { - return upstream_connection_info_; - } - void setRouteName(absl::string_view route_name) override { - route_name_ = std::string(route_name); - } - const std::string& getRouteName() const override { return route_name_; } - - Router::RouteConstSharedPtr route() const override { return route_; } - - absl::optional - duration(const absl::optional& time) const { - if (!time) { - return {}; - } - - return std::chrono::duration_cast(time.value() - - start_time_monotonic_); - } - - absl::optional lastDownstreamRxByteReceived() const override { - return duration(last_rx_byte_received_); - } - - absl::optional firstUpstreamTxByteSent() const override { - return duration(upstream_timing_.first_upstream_tx_byte_sent_); - } - - absl::optional lastUpstreamTxByteSent() const override { - return duration(upstream_timing_.last_upstream_tx_byte_sent_); - } - absl::optional firstUpstreamRxByteReceived() const override { - return duration(upstream_timing_.first_upstream_rx_byte_received_); - } - - absl::optional lastUpstreamRxByteReceived() const override { - return duration(upstream_timing_.last_upstream_rx_byte_received_); - } - - absl::optional firstDownstreamTxByteSent() const override { - return duration(downstream_timing_.firstDownstreamTxByteSent()); - } - - absl::optional lastDownstreamTxByteSent() const override { - return duration(downstream_timing_.lastDownstreamTxByteSent()); - } - void onRequestComplete() override { end_time_ = timeSystem().monotonicTime(); } - Envoy::StreamInfo::DownstreamTiming& downstreamTiming() override { return downstream_timing_; } - - void setUpstreamTiming(const Envoy::StreamInfo::UpstreamTiming& upstream_timing) override { - upstream_timing_ = upstream_timing; - } - Envoy::StreamInfo::UpstreamTiming& upstreamTiming() override { return upstream_timing_; } - const Envoy::StreamInfo::UpstreamTiming& upstreamTiming() const override { - return upstream_timing_; - } - absl::optional requestComplete() const override { return duration(end_time_); } - envoy::config::core::v3::Metadata& dynamicMetadata() override { return metadata_; }; - const envoy::config::core::v3::Metadata& dynamicMetadata() const override { return metadata_; }; - - void setDynamicMetadata(const std::string& name, const ProtobufWkt::Struct& value) override { - (*metadata_.mutable_filter_metadata())[name].MergeFrom(value); - }; - - const Envoy::StreamInfo::FilterStateSharedPtr& filterState() override { return filter_state_; } - const Envoy::StreamInfo::FilterState& filterState() const override { return *filter_state_; } - - const Envoy::StreamInfo::FilterStateSharedPtr& upstreamFilterState() const override { - return upstream_filter_state_; - } - void - setUpstreamFilterState(const Envoy::StreamInfo::FilterStateSharedPtr& filter_state) override { - upstream_filter_state_ = filter_state; - } - - void setUpstreamTransportFailureReason(absl::string_view failure_reason) override { - upstream_transport_failure_reason_ = std::string(failure_reason); - } - - const std::string& upstreamTransportFailureReason() const override { - return upstream_transport_failure_reason_; - } - - void setRequestHeaders(const Http::RequestHeaderMap& headers) override { - request_headers_ = &headers; - } - - const Http::RequestHeaderMap* getRequestHeaders() const override { return request_headers_; } - void setRequestIDProvider(const Http::RequestIdStreamInfoProviderSharedPtr& provider) override { ASSERT(provider != nullptr); request_id_provider_ = provider; @@ -186,93 +48,16 @@ class TestStreamInfo : public StreamInfo::StreamInfo { return request_id_provider_.get(); } - void setTraceReason(Tracing::Reason reason) override { trace_reason_ = reason; } - Tracing::Reason traceReason() const override { return trace_reason_; } - Event::TimeSystem& timeSystem() { return test_time_.timeSystem(); } - void setUpstreamClusterInfo( - const Upstream::ClusterInfoConstSharedPtr& upstream_cluster_info) override { - upstream_cluster_info_ = upstream_cluster_info; - } - absl::optional upstreamClusterInfo() const override { - return upstream_cluster_info_; - } - - void setFilterChainName(absl::string_view filter_chain_name) override { - filter_chain_name_ = std::string(filter_chain_name); - } - - const std::string& filterChainName() const override { return filter_chain_name_; } - - void setUpstreamConnectionId(uint64_t id) override { upstream_connection_id_ = id; } - - absl::optional upstreamConnectionId() const override { return upstream_connection_id_; } - - void setAttemptCount(uint32_t attempt_count) override { attempt_count_ = attempt_count; } - - absl::optional attemptCount() const override { return attempt_count_; } - - const Envoy::StreamInfo::BytesMeterSharedPtr& getUpstreamBytesMeter() const override { - return upstream_bytes_meter_; - } - - const Envoy::StreamInfo::BytesMeterSharedPtr& getDownstreamBytesMeter() const override { - return downstream_bytes_meter_; - } - - void setUpstreamBytesMeter( - const Envoy::StreamInfo::BytesMeterSharedPtr& upstream_bytes_meter) override { - upstream_bytes_meter_ = upstream_bytes_meter; - } - - void setDownstreamBytesMeter( - const Envoy::StreamInfo::BytesMeterSharedPtr& downstream_bytes_meter) override { - downstream_bytes_meter_ = downstream_bytes_meter; - } - Random::RandomGeneratorImpl random_; SystemTime start_time_; MonotonicTime start_time_monotonic_; - - Envoy::StreamInfo::DownstreamTiming downstream_timing_; - absl::optional last_rx_byte_received_; absl::optional end_time_; - - absl::optional protocol_{Http::Protocol::Http11}; - absl::optional response_code_; - absl::optional response_code_details_; - absl::optional connection_termination_details_; - uint64_t response_flags_{}; - Upstream::HostDescriptionConstSharedPtr upstream_host_{}; - bool health_check_request_{}; - std::string route_name_; - Network::Address::InstanceConstSharedPtr upstream_local_address_; Network::ConnectionInfoSetterSharedPtr downstream_connection_info_provider_{ std::make_shared(nullptr, nullptr)}; - Ssl::ConnectionInfoConstSharedPtr downstream_connection_info_; - Ssl::ConnectionInfoConstSharedPtr upstream_connection_info_; - Router::RouteConstSharedPtr route_; - envoy::config::core::v3::Metadata metadata_{}; - Envoy::StreamInfo::FilterStateSharedPtr filter_state_{ - std::make_shared( - Envoy::StreamInfo::FilterState::LifeSpan::FilterChain)}; - Envoy::StreamInfo::FilterStateSharedPtr upstream_filter_state_; - Envoy::StreamInfo::UpstreamTiming upstream_timing_; - std::string requested_server_name_; - std::string upstream_transport_failure_reason_; - const Http::RequestHeaderMap* request_headers_{}; Envoy::Event::SimulatedTimeSystem test_time_; - absl::optional upstream_cluster_info_{}; Http::RequestIdStreamInfoProviderSharedPtr request_id_provider_; - std::string filter_chain_name_; - Tracing::Reason trace_reason_{Tracing::Reason::NotTraceable}; - absl::optional upstream_connection_id_; - absl::optional attempt_count_; - Envoy::StreamInfo::BytesMeterSharedPtr upstream_bytes_meter_{ - std::make_shared()}; - Envoy::StreamInfo::BytesMeterSharedPtr downstream_bytes_meter_{ - std::make_shared()}; }; } // namespace Envoy diff --git a/test/extensions/access_loggers/open_telemetry/substitution_formatter_speed_test.cc b/test/extensions/access_loggers/open_telemetry/substitution_formatter_speed_test.cc index 27d69795849c5..6ebfc5cc37484 100644 --- a/test/extensions/access_loggers/open_telemetry/substitution_formatter_speed_test.cc +++ b/test/extensions/access_loggers/open_telemetry/substitution_formatter_speed_test.cc @@ -7,6 +7,8 @@ #include "benchmark/benchmark.h" #include "opentelemetry/proto/common/v1/common.pb.h" +using testing::NiceMock; + namespace Envoy { namespace Extensions { namespace AccessLoggers { @@ -54,7 +56,8 @@ std::unique_ptr makeOpenTelemetryFormatter() { } std::unique_ptr makeStreamInfo() { - auto stream_info = std::make_unique(); + NiceMock time_source; + auto stream_info = std::make_unique(time_source); stream_info->downstream_connection_info_provider_->setRemoteAddress( std::make_shared("203.0.113.1")); return stream_info; diff --git a/test/extensions/filters/common/expr/evaluator_fuzz_test.cc b/test/extensions/filters/common/expr/evaluator_fuzz_test.cc index 108e7211a36dd..2446fb4a8ac31 100644 --- a/test/extensions/filters/common/expr/evaluator_fuzz_test.cc +++ b/test/extensions/filters/common/expr/evaluator_fuzz_test.cc @@ -20,13 +20,14 @@ namespace { DEFINE_PROTO_FUZZER(const test::extensions::filters::common::expr::EvaluatorTestCase& input) { // Create builder without constant folding. static Expr::BuilderPtr builder = Expr::createBuilder(nullptr); + MockTimeSystem time_source; std::unique_ptr stream_info; try { // Validate that the input has an expression. TestUtility::validate(input); // Create stream_info to test against, this may catch exceptions from invalid addresses. - stream_info = Fuzz::fromStreamInfo(input.stream_info()); + stream_info = Fuzz::fromStreamInfo(input.stream_info(), time_source); } catch (const EnvoyException& e) { ENVOY_LOG_MISC(debug, "EnvoyException: {}", e.what()); return; diff --git a/test/extensions/filters/network/mongo_proxy/proxy_test.cc b/test/extensions/filters/network/mongo_proxy/proxy_test.cc index b59f4cc2bfebc..a2718f9595b25 100644 --- a/test/extensions/filters/network/mongo_proxy/proxy_test.cc +++ b/test/extensions/filters/network/mongo_proxy/proxy_test.cc @@ -61,7 +61,8 @@ class MongoProxyFilterTest : public testing::Test { public: MongoProxyFilterTest() : mongo_stats_(std::make_shared(store_, "test", - std::vector{"insert", "count"})) { + std::vector{"insert", "count"})), + stream_info_(time_source_) { setup(); } @@ -127,6 +128,7 @@ class MongoProxyFilterTest : public testing::Test { NiceMock read_filter_callbacks_; Envoy::AccessLog::MockAccessLogManager log_manager_; NiceMock drain_decision_; + NiceMock time_source_; TestStreamInfo stream_info_; }; diff --git a/test/fuzz/utility.h b/test/fuzz/utility.h index 1c13400450046..1fc315ab732fe 100644 --- a/test/fuzz/utility.h +++ b/test/fuzz/utility.h @@ -129,11 +129,12 @@ inline test::fuzz::Headers toHeaders(const Http::HeaderMap& headers) { const std::string TestSubjectPeer = "CN=Test Server,OU=Lyft Engineering,O=Lyft,L=San Francisco,ST=California,C=US"; -inline std::unique_ptr fromStreamInfo(const test::fuzz::StreamInfo& stream_info) { +inline std::unique_ptr fromStreamInfo(const test::fuzz::StreamInfo& stream_info, + TimeSource& time_source) { // Set mocks' default string return value to be an empty string. // TODO(asraa): Speed up this function, which is slowed because of the use of mocks. testing::DefaultValue::Set(EMPTY_STRING); - auto test_stream_info = std::make_unique(); + auto test_stream_info = std::make_unique(time_source); test_stream_info->metadata_ = stream_info.dynamic_metadata(); // Truncate recursive filter metadata fields. // TODO(asraa): Resolve MessageToJsonString failure on recursive filter metadata. @@ -164,7 +165,7 @@ inline std::unique_ptr fromStreamInfo(const test::fuzz::StreamIn stream_info.has_upstream_local_address() ? Envoy::Network::Address::resolveProtoAddress(stream_info.upstream_local_address()) : Network::Utility::resolveUrl("tcp://10.0.0.1:10000"); - test_stream_info->upstream_local_address_ = upstream_local_address; + test_stream_info->setUpstreamLocalAddress(upstream_local_address); test_stream_info->downstream_connection_info_provider_ = std::make_shared(address, address); test_stream_info->downstream_connection_info_provider_->setRequestedServerName( From fc793835b88b5c505214b4d86a4126bcd65670bf Mon Sep 17 00:00:00 2001 From: Jose Ulises Nino Rivera Date: Wed, 17 Nov 2021 06:38:19 -0800 Subject: [PATCH 097/110] docs: update contract reference (#19028) Signed-off-by: Jose Nino --- docs/root/faq/extensions/contract.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/root/faq/extensions/contract.rst b/docs/root/faq/extensions/contract.rst index 755e15eb080b7..90c40bab66129 100644 --- a/docs/root/faq/extensions/contract.rst +++ b/docs/root/faq/extensions/contract.rst @@ -20,7 +20,7 @@ Is there a contract my HTTP filter must adhere to? ``FilterHeadersStatus::ContinueAndDontEndStream`` when called with ``end_stream`` set to *false*. In this case ``FilterHeadersStatus::Continue`` should be returned. - * A filter's ``encode100ContinueHeaders()`` must return ``FilterHeadersStatus::Continue`` or + * A filter's ``encode1xxHeaders()`` must return ``FilterHeadersStatus::Continue`` or ``FilterHeadersStatus::StopIteration``. * Data encoding/decoding @@ -47,4 +47,3 @@ The first filter of the decoding filter chain will have the following headers in Although these headers may be omitted by one of the filters on the decoding filter chain, they should be reinserted before the terminal filter is triggered. - From f4535b1deaf58a75779e6fb3d7a957d5d3847bbd Mon Sep 17 00:00:00 2001 From: Rex Chang <58710378+rexnp@users.noreply.github.com> Date: Wed, 17 Nov 2021 07:46:19 -0800 Subject: [PATCH 098/110] aws_request_signing: extend api to allow excluding headers from signing (#18998) Signed-off-by: Rex Chang --- .../filters/http/aws_request_signing/v3/BUILD | 5 +- .../v3/aws_request_signing.proto | 14 +++++ .../aws_request_signing_filter.rst | 8 +++ docs/root/version_history/current.rst | 1 + source/extensions/common/aws/BUILD | 1 + source/extensions/common/aws/signer_impl.cc | 2 +- source/extensions/common/aws/signer_impl.h | 34 ++++++++++- source/extensions/common/aws/utility.cc | 59 ++++++++++--------- source/extensions/common/aws/utility.h | 5 +- .../filters/http/aws_lambda/config.cc | 5 +- .../filters/http/aws_request_signing/BUILD | 1 + .../http/aws_request_signing/config.cc | 5 +- .../grpc_credentials/aws_iam/config.cc | 5 +- .../extensions/common/aws/signer_impl_test.cc | 4 +- test/extensions/common/aws/utility_test.cc | 48 +++++++++++---- .../http/aws_request_signing/config_test.cc | 16 +++++ 16 files changed, 161 insertions(+), 52 deletions(-) diff --git a/api/envoy/extensions/filters/http/aws_request_signing/v3/BUILD b/api/envoy/extensions/filters/http/aws_request_signing/v3/BUILD index ee92fb652582e..693f0b92ff34d 100644 --- a/api/envoy/extensions/filters/http/aws_request_signing/v3/BUILD +++ b/api/envoy/extensions/filters/http/aws_request_signing/v3/BUILD @@ -5,5 +5,8 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], + deps = [ + "//envoy/type/matcher/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], ) diff --git a/api/envoy/extensions/filters/http/aws_request_signing/v3/aws_request_signing.proto b/api/envoy/extensions/filters/http/aws_request_signing/v3/aws_request_signing.proto index ae46400130d52..dd439e97d6d08 100644 --- a/api/envoy/extensions/filters/http/aws_request_signing/v3/aws_request_signing.proto +++ b/api/envoy/extensions/filters/http/aws_request_signing/v3/aws_request_signing.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.extensions.filters.http.aws_request_signing.v3; +import "envoy/type/matcher/v3/string.proto"; + import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; @@ -16,6 +18,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#extension: envoy.filters.http.aws_request_signing] // Top level configuration for the AWS request signing filter. +// [#next-free-field: 6] message AwsRequestSigning { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.aws_request_signing.v2alpha.AwsRequestSigning"; @@ -48,4 +51,15 @@ message AwsRequestSigning { // to calculate the payload hash. Not all services support this option. See the `S3 // `_ policy for details. bool use_unsigned_payload = 4; + + // A list of request header string matchers that will be excluded from signing. The excluded header can be matched by + // any patterns defined in the StringMatcher proto (e.g. exact string, prefix, regex, etc). + // + // Example: + // match_excluded_headers: + // - prefix: x-envoy + // - exact: foo + // - exact: bar + // When applied, all headers that start with "x-envoy" and headers "foo" and "bar" will not be signed. + repeated type.matcher.v3.StringMatcher match_excluded_headers = 5; } diff --git a/docs/root/configuration/http/http_filters/aws_request_signing_filter.rst b/docs/root/configuration/http/http_filters/aws_request_signing_filter.rst index 4c31387c503a8..27d5f58a340d7 100644 --- a/docs/root/configuration/http/http_filters/aws_request_signing_filter.rst +++ b/docs/root/configuration/http/http_filters/aws_request_signing_filter.rst @@ -25,6 +25,10 @@ When :ref:`use_unsigned_payload ` for details. +The :ref:`match_excluded_headers ` +option allows excluding certain request headers from being signed. This usually applies to headers that are likely to mutate or +are added later such as in retries. By default, the headers ``x-forwarded-for``, ``x-forwarded-proto``, and ``x-amzn-trace-id`` are always excluded. + Example configuration --------------------- @@ -38,6 +42,10 @@ Example filter configuration: service_name: s3 region: us-west-2 use_unsigned_payload: true + match_excluded_headers: + - prefix: x-envoy + - prefix: x-forwarded + - exact: x-amzn-trace-id Statistics diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index fe319d2c30ba6..156d31a300502 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -52,6 +52,7 @@ New Features ------------ * access log: added :ref:`grpc_stream_retry_policy ` to the gRPC logger to reconnect when a connection fails to be established. * api: added support for *xds.type.v3.TypedStruct* in addition to the now-deprecated *udpa.type.v1.TypedStruct* proto message, which is a wrapper proto used to encode typed JSON data in a *google.protobuf.Any* field. +* aws_request_signing_filter: added :ref:`match_excluded_headers ` to the signing filter to optionally exclude request headers from signing. * bootstrap: added :ref:`typed_dns_resolver_config ` in the bootstrap to support DNS resolver as an extension. * cluster: added :ref:`typed_dns_resolver_config ` in the cluster to support DNS resolver as an extension. * config: added :ref:`environment_variable ` to the :ref:`DataSource `. diff --git a/source/extensions/common/aws/BUILD b/source/extensions/common/aws/BUILD index f8857b60a5531..31bd0c8fa72a9 100644 --- a/source/extensions/common/aws/BUILD +++ b/source/extensions/common/aws/BUILD @@ -62,6 +62,7 @@ envoy_cc_library( external_deps = ["curl"], deps = [ "//source/common/common:empty_string", + "//source/common/common:matchers_lib", "//source/common/common:utility_lib", "//source/common/http:headers_lib", ], diff --git a/source/extensions/common/aws/signer_impl.cc b/source/extensions/common/aws/signer_impl.cc index a071e29149671..9f7bf92b4eb0f 100644 --- a/source/extensions/common/aws/signer_impl.cc +++ b/source/extensions/common/aws/signer_impl.cc @@ -57,7 +57,7 @@ void SignerImpl::sign(Http::RequestHeaderMap& headers, const std::string& conten const auto short_date = short_date_formatter_.now(time_source_); headers.addCopy(SignatureHeaders::get().Date, long_date); // Phase 1: Create a canonical request - const auto canonical_headers = Utility::canonicalizeHeaders(headers); + const auto canonical_headers = Utility::canonicalizeHeaders(headers, excluded_header_matchers_); const auto canonical_request = Utility::createCanonicalRequest( service_name_, method_header->value().getStringView(), path_header->value().getStringView(), canonical_headers, content_hash); diff --git a/source/extensions/common/aws/signer_impl.h b/source/extensions/common/aws/signer_impl.h index 301ead3e40eec..208133ce26798 100644 --- a/source/extensions/common/aws/signer_impl.h +++ b/source/extensions/common/aws/signer_impl.h @@ -1,7 +1,11 @@ #pragma once +#include + #include "source/common/common/logger.h" +#include "source/common/common/matchers.h" #include "source/common/common/utility.h" +#include "source/common/http/headers.h" #include "source/common/singleton/const_singleton.h" #include "source/extensions/common/aws/credentials_provider.h" #include "source/extensions/common/aws/signer.h" @@ -38,6 +42,8 @@ class SignatureConstantValues { using SignatureConstants = ConstSingleton; +using AwsSigV4HeaderExclusionVector = std::vector; + /** * Implementation of the Signature V4 signing process. * See https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html @@ -45,10 +51,17 @@ using SignatureConstants = ConstSingleton; class SignerImpl : public Signer, public Logger::Loggable { public: SignerImpl(absl::string_view service_name, absl::string_view region, - const CredentialsProviderSharedPtr& credentials_provider, TimeSource& time_source) + const CredentialsProviderSharedPtr& credentials_provider, TimeSource& time_source, + const AwsSigV4HeaderExclusionVector& matcher_config) : service_name_(service_name), region_(region), credentials_provider_(credentials_provider), time_source_(time_source), long_date_formatter_(SignatureConstants::get().LongDateFormat), - short_date_formatter_(SignatureConstants::get().ShortDateFormat) {} + short_date_formatter_(SignatureConstants::get().ShortDateFormat) { + for (const auto& matcher : matcher_config) { + excluded_header_matchers_.emplace_back( + std::make_unique>( + matcher)); + } + } void sign(Http::RequestMessage& message, bool sign_body = false) override; void sign(Http::RequestHeaderMap& headers, const std::string& content_hash) override; @@ -71,9 +84,24 @@ class SignerImpl : public Signer, public Logger::Loggable { const std::map& canonical_headers, absl::string_view signature) const; + std::vector defaultMatchers() const { + std::vector matcher_ptrs{}; + for (const auto& header : default_excluded_headers_) { + envoy::type::matcher::v3::StringMatcher m; + m.set_exact(header); + matcher_ptrs.emplace_back( + std::make_unique>( + m)); + } + return matcher_ptrs; + } + const std::string service_name_; const std::string region_; - + const std::vector default_excluded_headers_ = { + Http::Headers::get().ForwardedFor.get(), Http::Headers::get().ForwardedProto.get(), + "x-amzn-trace-id"}; + std::vector excluded_header_matchers_ = defaultMatchers(); CredentialsProviderSharedPtr credentials_provider_; TimeSource& time_source_; DateFormatter long_date_formatter_; diff --git a/source/extensions/common/aws/utility.cc b/source/extensions/common/aws/utility.cc index a0ba42fe4eb89..b1929086ad6d9 100644 --- a/source/extensions/common/aws/utility.cc +++ b/source/extensions/common/aws/utility.cc @@ -24,36 +24,39 @@ const std::string URI_ENCODE = "%{:02X}"; const std::string URI_DOUBLE_ENCODE = "%25{:02X}"; std::map -Utility::canonicalizeHeaders(const Http::RequestHeaderMap& headers) { +Utility::canonicalizeHeaders(const Http::RequestHeaderMap& headers, + const std::vector& excluded_headers) { std::map out; - headers.iterate([&out](const Http::HeaderEntry& entry) -> Http::HeaderMap::Iterate { - // Skip empty headers - if (entry.key().empty() || entry.value().empty()) { - return Http::HeaderMap::Iterate::Continue; - } - // Pseudo-headers should not be canonicalized - if (!entry.key().getStringView().empty() && entry.key().getStringView()[0] == ':') { - return Http::HeaderMap::Iterate::Continue; - } - // Skip headers that are likely to mutate, when crossing proxies - const auto key = entry.key().getStringView(); - if (key == Http::Headers::get().ForwardedFor.get() || - key == Http::Headers::get().ForwardedProto.get() || key == "x-amzn-trace-id") { - return Http::HeaderMap::Iterate::Continue; - } + headers.iterate( + [&out, &excluded_headers](const Http::HeaderEntry& entry) -> Http::HeaderMap::Iterate { + // Skip empty headers + if (entry.key().empty() || entry.value().empty()) { + return Http::HeaderMap::Iterate::Continue; + } + // Pseudo-headers should not be canonicalized + if (!entry.key().getStringView().empty() && entry.key().getStringView()[0] == ':') { + return Http::HeaderMap::Iterate::Continue; + } + const auto key = entry.key().getStringView(); + if (std::any_of(excluded_headers.begin(), excluded_headers.end(), + [&key](const Matchers::StringMatcherPtr& matcher) { + return matcher->match(key); + })) { + return Http::HeaderMap::Iterate::Continue; + } - std::string value(entry.value().getStringView()); - // Remove leading, trailing, and deduplicate repeated ascii spaces - absl::RemoveExtraAsciiWhitespace(&value); - const auto iter = out.find(std::string(entry.key().getStringView())); - // If the entry already exists, append the new value to the end - if (iter != out.end()) { - iter->second += fmt::format(",{}", value); - } else { - out.emplace(std::string(entry.key().getStringView()), value); - } - return Http::HeaderMap::Iterate::Continue; - }); + std::string value(entry.value().getStringView()); + // Remove leading, trailing, and deduplicate repeated ascii spaces + absl::RemoveExtraAsciiWhitespace(&value); + const auto iter = out.find(std::string(entry.key().getStringView())); + // If the entry already exists, append the new value to the end + if (iter != out.end()) { + iter->second += fmt::format(",{}", value); + } else { + out.emplace(std::string(entry.key().getStringView()), value); + } + return Http::HeaderMap::Iterate::Continue; + }); // The AWS SDK has a quirk where it removes "default ports" (80, 443) from the host headers // Additionally, we canonicalize the :authority header as "host" // TODO(lavignes): This may need to be tweaked to canonicalize :authority for HTTP/2 requests diff --git a/source/extensions/common/aws/utility.h b/source/extensions/common/aws/utility.h index 36b34da02a9f2..e7cfdac2abb1c 100644 --- a/source/extensions/common/aws/utility.h +++ b/source/extensions/common/aws/utility.h @@ -1,5 +1,6 @@ #pragma once +#include "source/common/common/matchers.h" #include "source/common/http/headers.h" namespace Envoy { @@ -13,10 +14,12 @@ class Utility { * Creates a canonicalized header map used in creating a AWS Signature V4 canonical request. * See https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html * @param headers a header map to canonicalize. + * @param excluded_headers a list of string matchers to exclude a given header from signing. * @return a std::map of canonicalized headers to be used in building a canonical request. */ static std::map - canonicalizeHeaders(const Http::RequestHeaderMap& headers); + canonicalizeHeaders(const Http::RequestHeaderMap& headers, + const std::vector& excluded_headers); /** * Creates an AWS Signature V4 canonical request string. diff --git a/source/extensions/filters/http/aws_lambda/config.cc b/source/extensions/filters/http/aws_lambda/config.cc index a3b1838ff1949..45a9cfe75d826 100644 --- a/source/extensions/filters/http/aws_lambda/config.cc +++ b/source/extensions/filters/http/aws_lambda/config.cc @@ -48,7 +48,10 @@ Http::FilterFactoryCb AwsLambdaFilterFactory::createFilterFactoryFromProtoTyped( const std::string region = arn->region(); auto signer = std::make_shared( service_name, region, std::move(credentials_provider), - context.mainThreadDispatcher().timeSource()); + context.mainThreadDispatcher().timeSource(), + // TODO: extend API to allow specifying header exclusion. ref: + // https://github.com/envoyproxy/envoy/pull/18998 + Extensions::Common::Aws::AwsSigV4HeaderExclusionVector{}); FilterSettings filter_settings{*arn, getInvocationMode(proto_config), proto_config.payload_passthrough()}; diff --git a/source/extensions/filters/http/aws_request_signing/BUILD b/source/extensions/filters/http/aws_request_signing/BUILD index fd1ecabed3227..942ba57aff1f8 100644 --- a/source/extensions/filters/http/aws_request_signing/BUILD +++ b/source/extensions/filters/http/aws_request_signing/BUILD @@ -32,6 +32,7 @@ envoy_cc_extension( deps = [ ":aws_request_signing_filter_lib", "//envoy/registry", + "//source/common/common:matchers_lib", "//source/extensions/common/aws:credentials_provider_impl_lib", "//source/extensions/common/aws:signer_impl_lib", "//source/extensions/filters/http/common:factory_base_lib", diff --git a/source/extensions/filters/http/aws_request_signing/config.cc b/source/extensions/filters/http/aws_request_signing/config.cc index 0d2e9bc97c1f4..d9a1a5c74a7bd 100644 --- a/source/extensions/filters/http/aws_request_signing/config.cc +++ b/source/extensions/filters/http/aws_request_signing/config.cc @@ -21,10 +21,11 @@ Http::FilterFactoryCb AwsRequestSigningFilterFactory::createFilterFactoryFromPro auto credentials_provider = std::make_shared( context.api(), Extensions::Common::Aws::Utility::metadataFetcher); + const auto matcher_config = Extensions::Common::Aws::AwsSigV4HeaderExclusionVector( + config.match_excluded_headers().begin(), config.match_excluded_headers().end()); auto signer = std::make_unique( config.service_name(), config.region(), credentials_provider, - context.mainThreadDispatcher().timeSource()); - + context.mainThreadDispatcher().timeSource(), matcher_config); auto filter_config = std::make_shared(std::move(signer), stats_prefix, context.scope(), config.host_rewrite(), config.use_unsigned_payload()); diff --git a/source/extensions/grpc_credentials/aws_iam/config.cc b/source/extensions/grpc_credentials/aws_iam/config.cc index c70140a437cbf..6253e6cc5cbc5 100644 --- a/source/extensions/grpc_credentials/aws_iam/config.cc +++ b/source/extensions/grpc_credentials/aws_iam/config.cc @@ -47,7 +47,10 @@ std::shared_ptr AwsIamGrpcCredentialsFactory::getChann auto credentials_provider = std::make_shared( api, Common::Aws::Utility::metadataFetcher); auto signer = std::make_unique( - config.service_name(), getRegion(config), credentials_provider, api.timeSource()); + config.service_name(), getRegion(config), credentials_provider, api.timeSource(), + // TODO: extend API to allow specifying header exclusion. ref: + // https://github.com/envoyproxy/envoy/pull/18998 + Common::Aws::AwsSigV4HeaderExclusionVector{}); std::shared_ptr new_call_creds = grpc::MetadataCredentialsFromPlugin( std::make_unique(std::move(signer))); if (call_creds == nullptr) { diff --git a/test/extensions/common/aws/signer_impl_test.cc b/test/extensions/common/aws/signer_impl_test.cc index b3c060529ff85..ff5565def723b 100644 --- a/test/extensions/common/aws/signer_impl_test.cc +++ b/test/extensions/common/aws/signer_impl_test.cc @@ -22,7 +22,7 @@ class SignerImplTest : public testing::Test { : credentials_provider_(new NiceMock()), message_(new Http::RequestMessageImpl()), signer_("service", "region", CredentialsProviderSharedPtr{credentials_provider_}, - time_system_), + time_system_, Extensions::Common::Aws::AwsSigV4HeaderExclusionVector{}), credentials_("akid", "secret"), token_credentials_("akid", "secret", "token") { // 20180102T030405Z time_system_.setSystemTime(std::chrono::milliseconds(1514862245000)); @@ -48,7 +48,7 @@ class SignerImplTest : public testing::Test { headers.addCopy(Http::LowerCaseString("host"), "www.example.com"); SignerImpl signer(service_name, "region", CredentialsProviderSharedPtr{credentials_provider}, - time_system_); + time_system_, Extensions::Common::Aws::AwsSigV4HeaderExclusionVector{}); if (use_unsigned_payload) { signer.signUnsignedPayload(headers); } else { diff --git a/test/extensions/common/aws/utility_test.cc b/test/extensions/common/aws/utility_test.cc index d19f8e220356a..629f28c2c9577 100644 --- a/test/extensions/common/aws/utility_test.cc +++ b/test/extensions/common/aws/utility_test.cc @@ -19,7 +19,8 @@ TEST(UtilityTest, CanonicalizeHeadersInAlphabeticalOrder) { {"d", "d_value"}, {"f", "f_value"}, {"b", "b_value"}, {"e", "e_value"}, {"c", "c_value"}, {"a", "a_value"}, }; - const auto map = Utility::canonicalizeHeaders(headers); + std::vector exclusion_list = {}; + const auto map = Utility::canonicalizeHeaders(headers, exclusion_list); EXPECT_THAT(map, ElementsAre(Pair("a", "a_value"), Pair("b", "b_value"), Pair("c", "c_value"), Pair("d", "d_value"), Pair("e", "e_value"), Pair("f", "f_value"))); } @@ -31,7 +32,8 @@ TEST(UtilityTest, CanonicalizeHeadersSkippingPseudoHeaders) { {":method", "GET"}, {"normal", "normal_value"}, }; - const auto map = Utility::canonicalizeHeaders(headers); + std::vector exclusion_list = {}; + const auto map = Utility::canonicalizeHeaders(headers, exclusion_list); EXPECT_THAT(map, ElementsAre(Pair("normal", "normal_value"))); } @@ -42,7 +44,8 @@ TEST(UtilityTest, CanonicalizeHeadersJoiningDuplicatesWithCommas) { {"a", "a_value2"}, {"a", "a_value3"}, }; - const auto map = Utility::canonicalizeHeaders(headers); + std::vector exclusion_list = {}; + const auto map = Utility::canonicalizeHeaders(headers, exclusion_list); EXPECT_THAT(map, ElementsAre(Pair("a", "a_value1,a_value2,a_value3"))); } @@ -51,7 +54,8 @@ TEST(UtilityTest, CanonicalizeHeadersAuthorityToHost) { Http::TestRequestHeaderMapImpl headers{ {":authority", "authority_value"}, }; - const auto map = Utility::canonicalizeHeaders(headers); + std::vector exclusion_list = {}; + const auto map = Utility::canonicalizeHeaders(headers, exclusion_list); EXPECT_THAT(map, ElementsAre(Pair("host", "authority_value"))); } @@ -60,13 +64,14 @@ TEST(UtilityTest, CanonicalizeHeadersRemovingDefaultPortsFromHost) { Http::TestRequestHeaderMapImpl headers_port80{ {":authority", "example.com:80"}, }; - const auto map_port80 = Utility::canonicalizeHeaders(headers_port80); + std::vector exclusion_list = {}; + const auto map_port80 = Utility::canonicalizeHeaders(headers_port80, exclusion_list); EXPECT_THAT(map_port80, ElementsAre(Pair("host", "example.com"))); Http::TestRequestHeaderMapImpl headers_port443{ {":authority", "example.com:443"}, }; - const auto map_port443 = Utility::canonicalizeHeaders(headers_port443); + const auto map_port443 = Utility::canonicalizeHeaders(headers_port443, exclusion_list); EXPECT_THAT(map_port443, ElementsAre(Pair("host", "example.com"))); } @@ -78,20 +83,39 @@ TEST(UtilityTest, CanonicalizeHeadersTrimmingWhitespace) { {"internal", "internal value"}, {"all", " all value "}, }; - const auto map = Utility::canonicalizeHeaders(headers); + std::vector exclusion_list = {}; + const auto map = Utility::canonicalizeHeaders(headers, exclusion_list); EXPECT_THAT(map, ElementsAre(Pair("all", "all value"), Pair("internal", "internal value"), Pair("leading", "leading value"), Pair("trailing", "trailing value"))); } -// Headers that are likely to mutate are not considered canonical -TEST(UtilityTest, CanonicalizeHeadersDropMutatingHeaders) { +// Headers in the exclusion list are not canonicalized +TEST(UtilityTest, CanonicalizeHeadersDropExcludedMatchers) { Http::TestRequestHeaderMapImpl headers{ {":authority", "example.com"}, {"x-forwarded-for", "1.2.3.4"}, {"x-forwarded-proto", "https"}, {"x-amz-date", "20130708T220855Z"}, - {"x-amz-content-sha256", "e3b0c44..."}, - }; - const auto map = Utility::canonicalizeHeaders(headers); + {"x-amz-content-sha256", "e3b0c44..."}, {"x-envoy-retry-on", "5xx,reset"}, + {"x-envoy-max-retries", "3"}, {"x-amzn-trace-id", "0123456789"}}; + std::vector exclusion_list = {}; + std::vector exact_matches = {"x-amzn-trace-id", "x-forwarded-for", + "x-forwarded-proto"}; + for (auto& str : exact_matches) { + envoy::type::matcher::v3::StringMatcher config; + config.set_exact(str); + exclusion_list.emplace_back( + std::make_unique>( + config)); + } + std::vector prefixes = {"x-envoy"}; + for (auto& match_str : prefixes) { + envoy::type::matcher::v3::StringMatcher config; + config.set_prefix(match_str); + exclusion_list.emplace_back( + std::make_unique>( + config)); + } + const auto map = Utility::canonicalizeHeaders(headers, exclusion_list); EXPECT_THAT(map, ElementsAre(Pair("host", "example.com"), Pair("x-amz-content-sha256", "e3b0c44..."), Pair("x-amz-date", "20130708T220855Z"))); diff --git a/test/extensions/filters/http/aws_request_signing/config_test.cc b/test/extensions/filters/http/aws_request_signing/config_test.cc index 58842ca8fd4d6..27c9577a4b49b 100644 --- a/test/extensions/filters/http/aws_request_signing/config_test.cc +++ b/test/extensions/filters/http/aws_request_signing/config_test.cc @@ -21,11 +21,27 @@ TEST(AwsRequestSigningFilterConfigTest, SimpleConfig) { const std::string yaml = R"EOF( service_name: s3 region: us-west-2 +match_excluded_headers: + - prefix: x-envoy + - exact: foo + - exact: bar )EOF"; AwsRequestSigningProtoConfig proto_config; TestUtility::loadFromYamlAndValidate(yaml, proto_config); + AwsRequestSigningProtoConfig expected_config; + expected_config.set_service_name("s3"); + expected_config.set_region("us-west-2"); + expected_config.add_match_excluded_headers()->set_prefix("x-envoy"); + expected_config.add_match_excluded_headers()->set_exact("foo"); + expected_config.add_match_excluded_headers()->set_exact("bar"); + + Protobuf::util::MessageDifferencer differencer; + differencer.set_message_field_comparison(Protobuf::util::MessageDifferencer::EQUAL); + differencer.set_repeated_field_comparison(Protobuf::util::MessageDifferencer::AS_SET); + EXPECT_TRUE(differencer.Compare(expected_config, proto_config)); + testing::NiceMock context; AwsRequestSigningFilterFactory factory; From 8598c3f3b3ed9ff0090dbcb1c83464551300bd18 Mon Sep 17 00:00:00 2001 From: Yuchen Dai Date: Wed, 17 Nov 2021 09:03:33 -0800 Subject: [PATCH 099/110] listener: create internal listener (#18104) This PR allows creating server connection but the abilitity to connect to such a listener is located in #18105. Signed-off-by: Yuchen Dai --- envoy/network/listener.h | 49 +++ source/common/network/BUILD | 1 + source/common/network/listen_socket_impl.h | 24 ++ source/common/network/socket_impl.cc | 5 + source/common/network/utility.cc | 3 + source/common/runtime/runtime_features.cc | 1 + source/common/tcp_proxy/tcp_proxy.cc | 3 + source/common/tcp_proxy/tcp_proxy.h | 2 +- source/common/upstream/upstream_impl.cc | 5 +- source/server/BUILD | 32 ++ source/server/active_internal_listener.cc | 79 ++++ source/server/active_internal_listener.h | 96 +++++ source/server/admin/admin.h | 3 + source/server/connection_handler_impl.cc | 42 +- source/server/connection_handler_impl.h | 12 +- source/server/listener_impl.cc | 51 ++- source/server/listener_impl.h | 13 + source/server/listener_manager_impl.cc | 18 +- test/common/network/BUILD | 1 + test/common/network/connection_impl_test.cc | 37 ++ .../common/network/listen_socket_impl_test.cc | 12 +- test/config/utility.cc | 13 +- .../proxy_protocol_regression_test.cc | 3 + .../proxy_protocol/proxy_protocol_test.cc | 6 + test/integration/BUILD | 27 ++ test/integration/fake_upstream.h | 4 + test/integration/integration_tcp_client.cc | 1 - .../internal_listener_integration_test.cc | 104 +++++ .../socket_interface_integration_test.cc | 3 +- test/mocks/network/mocks.h | 2 +- test/server/BUILD | 21 +- test/server/active_internal_listener_test.cc | 230 +++++++++++ test/server/active_tcp_listener_test.cc | 1 - test/server/connection_handler_test.cc | 99 ++++- test/server/listener_manager_impl_test.cc | 387 +++++++++++++++++- 35 files changed, 1349 insertions(+), 41 deletions(-) create mode 100644 source/server/active_internal_listener.cc create mode 100644 source/server/active_internal_listener.h create mode 100644 test/integration/internal_listener_integration_test.cc create mode 100644 test/server/active_internal_listener_test.cc diff --git a/envoy/network/listener.h b/envoy/network/listener.h index 2e4598de04516..9969c4198fa93 100644 --- a/envoy/network/listener.h +++ b/envoy/network/listener.h @@ -107,6 +107,16 @@ class UdpListenerConfig { using UdpListenerConfigOptRef = OptRef; +/** + * Configuration for an internal listener. + */ +class InternalListenerConfig { +public: + virtual ~InternalListenerConfig() = default; +}; + +using InternalListenerConfigOptRef = OptRef; + /** * A configuration for an individual listener. */ @@ -185,6 +195,11 @@ class ListenerConfig { */ virtual UdpListenerConfigOptRef udpListenerConfig() PURE; + /** + * @return the internal configuration for the listener IFF it is an internal listener. + */ + virtual InternalListenerConfigOptRef internalListenerConfig() PURE; + /** * @return traffic direction of the listener. */ @@ -428,6 +443,40 @@ class UdpListener : public virtual Listener { using UdpListenerPtr = std::unique_ptr; +/** + * Internal listener callbacks. + */ +class InternalListener { +public: + virtual ~InternalListener() = default; + + /** + * Called when a new connection is accepted. + * @param socket supplies the socket that is moved into the callee. + */ + virtual void onAccept(ConnectionSocketPtr&& socket) PURE; +}; +using InternalListenerOptRef = OptRef; + +/** + * The query interface of the registered internal listener callbacks. + */ +class InternalListenerManager { +public: + virtual ~InternalListenerManager() = default; + + /** + * Return the internal listener callbacks binding the listener address. + * + * @param listen_address the internal address of the expected internal listener. + */ + virtual InternalListenerOptRef + findByAddress(const Address::InstanceConstSharedPtr& listen_address) PURE; +}; + +using InternalListenerManagerOptRef = + absl::optional>; + /** * Handles delivering datagrams to the correct worker. */ diff --git a/source/common/network/BUILD b/source/common/network/BUILD index 98af3afd269a2..c6ad2e9651200 100644 --- a/source/common/network/BUILD +++ b/source/common/network/BUILD @@ -64,6 +64,7 @@ envoy_cc_library( hdrs = ["connection_impl_base.h"], deps = [ ":filter_manager_lib", + ":listen_socket_lib", "//envoy/common:scope_tracker_interface", "//envoy/event:dispatcher_interface", "//source/common/common:assert_lib", diff --git a/source/common/network/listen_socket_impl.h b/source/common/network/listen_socket_impl.h index b4030717e0cb6..a02948f04be03 100644 --- a/source/common/network/listen_socket_impl.h +++ b/source/common/network/listen_socket_impl.h @@ -153,6 +153,30 @@ class UdsListenSocket : public ListenSocketImpl { Socket::Type socketType() const override { return Socket::Type::Stream; } }; +// This socket type adapts the ListenerComponentFactory. +class InternalListenSocket : public ListenSocketImpl { +public: + InternalListenSocket(const Address::InstanceConstSharedPtr& address) + : ListenSocketImpl(/* io_handle= */ nullptr, address) {} + Socket::Type socketType() const override { return Socket::Type::Stream; } + + // InternalListenSocket cannot be duplicated. + SocketPtr duplicate() override { + return std::make_unique(connectionInfoProvider().localAddress()); + } + + Api::SysCallIntResult bind(Network::Address::InstanceConstSharedPtr) override { + // internal listener socket does not support bind semantic. + NOT_IMPLEMENTED_GCOVR_EXCL_LINE; + } + + void close() override { ASSERT(io_handle_ == nullptr); } + bool isOpen() const override { + ASSERT(io_handle_ == nullptr); + return false; + } +}; + class ConnectionSocketImpl : public SocketImpl, public ConnectionSocket { public: ConnectionSocketImpl(IoHandlePtr&& io_handle, diff --git a/source/common/network/socket_impl.cc b/source/common/network/socket_impl.cc index 2de31a2fe7e7e..3e16eeddfd3c0 100644 --- a/source/common/network/socket_impl.cc +++ b/source/common/network/socket_impl.cc @@ -30,6 +30,11 @@ SocketImpl::SocketImpl(IoHandlePtr&& io_handle, return; } + if (connection_info_provider_->remoteAddress() != nullptr) { + addr_type_ = connection_info_provider_->remoteAddress()->type(); + return; + } + // Should not happen but some tests inject -1 fds if (!io_handle_->isOpen()) { return; diff --git a/source/common/network/utility.cc b/source/common/network/utility.cc index 7cd7b19c00be4..8138b1bcf41a8 100644 --- a/source/common/network/utility.cc +++ b/source/common/network/utility.cc @@ -534,6 +534,9 @@ Utility::protobufAddressSocketType(const envoy::config::core::v3::Address& proto } case envoy::config::core::v3::Address::AddressCase::kPipe: return Socket::Type::Stream; + case envoy::config::core::v3::Address::AddressCase::kEnvoyInternalAddress: + // Currently internal address supports stream operation only. + return Socket::Type::Stream; default: NOT_REACHED_GCOVR_EXCL_LINE; } diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 8194a069014b5..a31eb1e374c79 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -69,6 +69,7 @@ constexpr const char* runtime_features[] = { "envoy.reloadable_features.http_reject_path_with_fragment", "envoy.reloadable_features.http_strip_fragment_from_path_unsafe_if_disabled", "envoy.reloadable_features.http_transport_failure_reason_in_body", + "envoy.reloadable_features.internal_address", "envoy.reloadable_features.internal_redirects_with_body", "envoy.reloadable_features.listener_reuse_port_default_enabled", "envoy.reloadable_features.listener_wildcard_match_ip_family", diff --git a/source/common/tcp_proxy/tcp_proxy.cc b/source/common/tcp_proxy/tcp_proxy.cc index b22fa5e956e5f..4dd925c34cdc0 100644 --- a/source/common/tcp_proxy/tcp_proxy.cc +++ b/source/common/tcp_proxy/tcp_proxy.cc @@ -531,6 +531,8 @@ Network::FilterStatus Filter::onNewConnection() { } void Filter::onDownstreamEvent(Network::ConnectionEvent event) { + ENVOY_CONN_LOG(trace, "on downstream event {}, has upstream = {}", read_callbacks_->connection(), + static_cast(event), upstream_ == nullptr); if (upstream_) { Tcp::ConnectionPool::ConnectionDataPtr conn_data(upstream_->onDownstreamEvent(event)); if (conn_data != nullptr && @@ -699,6 +701,7 @@ Drainer::Drainer(UpstreamDrainManager& parent, const Config::SharedConfigSharedP const Upstream::HostDescriptionConstSharedPtr& upstream_host) : parent_(parent), callbacks_(callbacks), upstream_conn_data_(std::move(conn_data)), timer_(std::move(idle_timer)), upstream_host_(upstream_host), config_(config) { + ENVOY_CONN_LOG(trace, "draining the upstream connection", upstream_conn_data_->connection()); config_->stats().upstream_flush_total_.inc(); config_->stats().upstream_flush_active_.inc(); } diff --git a/source/common/tcp_proxy/tcp_proxy.h b/source/common/tcp_proxy/tcp_proxy.h index ccd01ac8c098e..22a49739c4ebe 100644 --- a/source/common/tcp_proxy/tcp_proxy.h +++ b/source/common/tcp_proxy/tcp_proxy.h @@ -380,7 +380,7 @@ class Filter : public Network::ReadFilter, // This class deals with an upstream connection that needs to finish flushing, when the downstream // connection has been closed. The TcpProxy is destroyed when the downstream connection is closed, // so handling the upstream connection here allows it to finish draining or timeout. -class Drainer : public Event::DeferredDeletable { +class Drainer : public Event::DeferredDeletable, protected Logger::Loggable { public: Drainer(UpstreamDrainManager& parent, const Config::SharedConfigSharedPtr& config, const std::shared_ptr& callbacks, diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 81e8654110d57..4a57f8d18b203 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -317,7 +317,10 @@ Network::ClientConnectionPtr HostImpl::createConnection( } else { connection_options = options; } - ASSERT(!address->envoyInternalAddress()); + + ASSERT(!address->envoyInternalAddress() || + Runtime::runtimeFeatureEnabled("envoy.reloadable_features.internal_address")); + Network::ClientConnectionPtr connection = address_list.size() > 1 ? std::make_unique( diff --git a/source/server/BUILD b/source/server/BUILD index 12886dfd10aad..38443e8245f8a 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -64,6 +64,7 @@ envoy_cc_library( envoy_cc_library( name = "connection_handler_lib", deps = [ + ":active_internal_listener", ":active_tcp_listener", ":active_udp_listener", ":connection_handler_impl", @@ -77,6 +78,7 @@ envoy_cc_library( "connection_handler_impl.h", ], deps = [ + ":active_internal_listener", ":active_tcp_listener", "//envoy/common:time_interface", "//envoy/event:deferred_deletable", @@ -179,6 +181,36 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "active_internal_listener", + srcs = ["active_internal_listener.cc"], + hdrs = [ + "active_internal_listener.h", + ], + deps = [ + ":active_listener_base", + ":active_tcp_listener_headers", + "//envoy/common:time_interface", + "//envoy/event:deferred_deletable", + "//envoy/event:dispatcher_interface", + "//envoy/event:timer_interface", + "//envoy/network:connection_handler_interface", + "//envoy/network:connection_interface", + "//envoy/network:exception_interface", + "//envoy/network:filter_interface", + "//envoy/network:listen_socket_interface", + "//envoy/network:listener_interface", + "//envoy/server:listener_manager_interface", + "//envoy/stats:timespan_interface", + "//source/common/common:linked_object", + "//source/common/common:non_copyable", + "//source/common/event:deferred_task", + "//source/common/network:connection_lib", + "//source/common/stats:timespan_lib", + "//source/common/stream_info:stream_info_lib", + ], +) + envoy_cc_library( name = "active_tcp_socket", srcs = ["active_tcp_socket.cc"], diff --git a/source/server/active_internal_listener.cc b/source/server/active_internal_listener.cc new file mode 100644 index 0000000000000..701d9376175e7 --- /dev/null +++ b/source/server/active_internal_listener.cc @@ -0,0 +1,79 @@ +#include "source/server/active_internal_listener.h" + +#include "envoy/network/filter.h" +#include "envoy/stats/scope.h" + +#include "source/common/network/address_impl.h" +#include "source/common/stats/timespan_impl.h" + +#include "active_stream_listener_base.h" + +namespace Envoy { +namespace Server { + +ActiveInternalListener::ActiveInternalListener(Network::ConnectionHandler& conn_handler, + Event::Dispatcher& dispatcher, + Network::ListenerConfig& config) + : OwnedActiveStreamListenerBase( + conn_handler, dispatcher, + std::make_unique(), config) {} + +ActiveInternalListener::ActiveInternalListener(Network::ConnectionHandler& conn_handler, + Event::Dispatcher& dispatcher, + Network::ListenerPtr listener, + Network::ListenerConfig& config) + : OwnedActiveStreamListenerBase(conn_handler, dispatcher, std::move(listener), config) {} + +ActiveInternalListener::~ActiveInternalListener() { + is_deleting_ = true; + // Purge sockets that have not progressed to connections. This should only happen when + // a listener filter stops iteration and never resumes. + while (!sockets_.empty()) { + auto removed = sockets_.front()->removeFromList(sockets_); + dispatcher().deferredDelete(std::move(removed)); + } + + for (auto& [chain, active_connections] : connections_by_context_) { + ASSERT(active_connections != nullptr); + auto& connections = active_connections->connections_; + while (!connections.empty()) { + connections.front()->connection_->close(Network::ConnectionCloseType::NoFlush); + } + } + dispatcher().clearDeferredDeleteList(); +} + +void ActiveInternalListener::updateListenerConfig(Network::ListenerConfig& config) { + ENVOY_LOG(trace, "replacing listener ", config_->listenerTag(), " by ", config.listenerTag()); + config_ = &config; +} + +void ActiveInternalListener::onAccept(Network::ConnectionSocketPtr&& socket) { + // Unlike tcp listener, no rebalancer is applied and won't call pickTargetHandler to account + // connections. + incNumConnections(); + + auto active_socket = std::make_unique( + *this, std::move(socket), false /* do not hand off at internal listener */); + + onSocketAccepted(std::move(active_socket)); +} + +void ActiveInternalListener::newActiveConnection( + const Network::FilterChain& filter_chain, Network::ServerConnectionPtr server_conn_ptr, + std::unique_ptr stream_info) { + auto& active_connections = getOrCreateActiveConnections(filter_chain); + auto active_connection = + std::make_unique(active_connections, std::move(server_conn_ptr), + dispatcher().timeSource(), std::move(stream_info)); + // If the connection is already closed, we can just let this connection immediately die. + if (active_connection->connection_->state() != Network::Connection::State::Closed) { + ENVOY_CONN_LOG( + debug, "new connection from {}", *active_connection->connection_, + active_connection->connection_->connectionInfoProvider().remoteAddress()->asString()); + active_connection->connection_->addConnectionCallbacks(*active_connection); + LinkedList::moveIntoList(std::move(active_connection), active_connections.connections_); + } +} +} // namespace Server +} // namespace Envoy diff --git a/source/server/active_internal_listener.h b/source/server/active_internal_listener.h new file mode 100644 index 0000000000000..f455162a6c9c3 --- /dev/null +++ b/source/server/active_internal_listener.h @@ -0,0 +1,96 @@ +#pragma once +#include +#include +#include +#include + +#include "envoy/common/time.h" +#include "envoy/event/deferred_deletable.h" +#include "envoy/event/dispatcher.h" +#include "envoy/network/connection.h" +#include "envoy/network/connection_handler.h" +#include "envoy/network/filter.h" +#include "envoy/network/listen_socket.h" +#include "envoy/network/listener.h" +#include "envoy/server/listener_manager.h" +#include "envoy/stats/scope.h" +#include "envoy/stats/timespan.h" + +#include "source/common/common/linked_object.h" +#include "source/common/common/non_copyable.h" +#include "source/common/stream_info/stream_info_impl.h" +#include "source/server/active_stream_listener_base.h" + +#include "spdlog/spdlog.h" + +namespace Envoy { +namespace Server { + +class ActiveInternalListener : public OwnedActiveStreamListenerBase, + public Network::InternalListener { +public: + ActiveInternalListener(Network::ConnectionHandler& conn_handler, Event::Dispatcher& dispatcher, + Network::ListenerConfig& config); + ActiveInternalListener(Network::ConnectionHandler& conn_handler, Event::Dispatcher& dispatcher, + Network::ListenerPtr listener, Network::ListenerConfig& config); + ~ActiveInternalListener() override; + + class NetworkInternalListener : public Network::Listener { + + void disable() override { + // Similar to the listeners that does not bind to port. Accept is not driven by OS io event so + // the disable is not working. + // TODO(lambdai): Explore the approach to elegantly disable internal listener. Maybe an user + // space accept queue should be put here. + ENVOY_LOG(debug, "Warning: the internal listener cannot be disabled."); + } + + void enable() override { + ENVOY_LOG(debug, "Warning: the internal listener is always enabled."); + } + + void setRejectFraction(UnitFloat) override {} + }; + + // ActiveListenerImplBase + Network::Listener* listener() override { return listener_.get(); } + + // Network::TcpConnectionHandler + Network::BalancedConnectionHandlerOptRef + getBalancedHandlerByAddress(const Network::Address::Instance&) override { + // Internal listener doesn't support migrate connection to another worker. + // TODO(lambdai): implement the function of handling off to another listener of the same worker. + NOT_IMPLEMENTED_GCOVR_EXCL_LINE; + } + + void pauseListening() override { + if (listener_ != nullptr) { + listener_->disable(); + } + } + void resumeListening() override { + if (listener_ != nullptr) { + listener_->enable(); + } + } + void shutdownListener() override { listener_.reset(); } + + // Network::InternalListener + void onAccept(Network::ConnectionSocketPtr&& socket) override; + + // Network::BalancedConnectionHandler + void incNumConnections() override { config_->openConnections().inc(); } + void decNumConnections() override { config_->openConnections().dec(); } + + void newActiveConnection(const Network::FilterChain& filter_chain, + Network::ServerConnectionPtr server_conn_ptr, + std::unique_ptr stream_info) override; + /** + * Update the listener config. The follow up connections will see the new config. The existing + * connections are not impacted. + */ + void updateListenerConfig(Network::ListenerConfig& config) override; +}; + +} // namespace Server +} // namespace Envoy diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index 15e06294dc9d2..abac710da8698 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -359,6 +359,9 @@ class AdminImpl : public Admin, Network::UdpListenerConfigOptRef udpListenerConfig() override { return Network::UdpListenerConfigOptRef(); } + Network::InternalListenerConfigOptRef internalListenerConfig() override { + return Network::InternalListenerConfigOptRef(); + } envoy::config::core::v3::TrafficDirection direction() const override { return envoy::config::core::v3::UNSPECIFIED; } diff --git a/source/server/connection_handler_impl.cc b/source/server/connection_handler_impl.cc index 4bcdcd9cc16aa..24789e5feb5aa 100644 --- a/source/server/connection_handler_impl.cc +++ b/source/server/connection_handler_impl.cc @@ -5,9 +5,11 @@ #include "envoy/event/dispatcher.h" #include "envoy/network/filter.h" +#include "source/common/common/logger.h" #include "source/common/event/deferred_task.h" #include "source/common/network/utility.h" #include "source/common/runtime/runtime_features.h" +#include "source/server/active_internal_listener.h" #include "source/server/active_tcp_listener.h" namespace Envoy { @@ -38,7 +40,20 @@ void ConnectionHandlerImpl::addListener(absl::optional overridden_list } ActiveListenerDetails details; - if (config.listenSocketFactory().socketType() == Network::Socket::Type::Stream) { + if (config.internalListenerConfig().has_value()) { + if (overridden_listener.has_value()) { + for (auto& listener : listeners_) { + if (listener.second.listener_->listenerTag() == overridden_listener) { + listener.second.internalListener()->get().updateListenerConfig(config); + return; + } + } + NOT_REACHED_GCOVR_EXCL_LINE; + } + auto internal_listener = std::make_unique(*this, dispatcher(), config); + details.typed_listener_ = *internal_listener; + details.listener_ = std::move(internal_listener); + } else if (config.listenSocketFactory().socketType() == Network::Socket::Type::Stream) { if (!support_udp_in_place_filter_chain_update && overridden_listener.has_value()) { for (auto& listener : listeners_) { if (listener.second.listener_->listenerTag() == overridden_listener) { @@ -144,6 +159,25 @@ void ConnectionHandlerImpl::setListenerRejectFraction(UnitFloat reject_fraction) } } +Network::InternalListenerOptRef +ConnectionHandlerImpl::findByAddress(const Network::Address::InstanceConstSharedPtr& address) { + ASSERT(address->type() == Network::Address::Type::EnvoyInternal); + auto listener_it = + std::find_if(listeners_.begin(), listeners_.end(), + [&address](std::pair& p) { + return p.second.internalListener().has_value() && + p.second.listener_->listener() != nullptr && + p.first->type() == Network::Address::Type::EnvoyInternal && + *(p.first) == *address; + }); + + if (listener_it != listeners_.end()) { + return Network::InternalListenerOptRef(listener_it->second.internalListener().value().get()); + } + return OptRef(); +} + ConnectionHandlerImpl::ActiveTcpListenerOptRef ConnectionHandlerImpl::ActiveListenerDetails::tcpListener() { auto* val = absl::get_if>(&typed_listener_); @@ -156,6 +190,12 @@ ConnectionHandlerImpl::ActiveListenerDetails::udpListener() { return (val != nullptr) ? absl::make_optional(*val) : absl::nullopt; } +ConnectionHandlerImpl::ActiveInternalListenerOptRef +ConnectionHandlerImpl::ActiveListenerDetails::internalListener() { + auto* val = absl::get_if>(&typed_listener_); + return (val != nullptr) ? absl::make_optional(*val) : absl::nullopt; +} + ConnectionHandlerImpl::ActiveListenerDetailsOptRef ConnectionHandlerImpl::findActiveListenerByTag(uint64_t listener_tag) { // TODO(mattklein123): We should probably use a hash table here to lookup the tag diff --git a/source/server/connection_handler_impl.h b/source/server/connection_handler_impl.h index f10424b7c1a2d..23a39659c55d1 100644 --- a/source/server/connection_handler_impl.h +++ b/source/server/connection_handler_impl.h @@ -21,6 +21,7 @@ namespace Server { class ActiveUdpListenerBase; class ActiveTcpListener; +class ActiveInternalListener; /** * Server side connection handler. This is used both by workers as well as the @@ -28,12 +29,15 @@ class ActiveTcpListener; */ class ConnectionHandlerImpl : public Network::TcpConnectionHandler, public Network::UdpConnectionHandler, + public Network::InternalListenerManager, NonCopyable, Logger::Loggable { public: using UdpListenerCallbacksOptRef = absl::optional>; using ActiveTcpListenerOptRef = absl::optional>; + using ActiveInternalListenerOptRef = + absl::optional>; ConnectionHandlerImpl(Event::Dispatcher& dispatcher, absl::optional worker_index); @@ -63,18 +67,24 @@ class ConnectionHandlerImpl : public Network::TcpConnectionHandler, // Network::UdpConnectionHandler Network::UdpListenerCallbacksOptRef getUdpListenerCallbacks(uint64_t listener_tag) override; + // Network::InternalListenerManager + Network::InternalListenerOptRef + findByAddress(const Network::Address::InstanceConstSharedPtr& listen_address) override; + private: struct ActiveListenerDetails { // Strong pointer to the listener, whether TCP, UDP, QUIC, etc. Network::ConnectionHandler::ActiveListenerPtr listener_; absl::variant, - std::reference_wrapper> + std::reference_wrapper, + std::reference_wrapper> typed_listener_; // Helpers for accessing the data in the variant for cleaner code. ActiveTcpListenerOptRef tcpListener(); UdpListenerCallbacksOptRef udpListener(); + ActiveInternalListenerOptRef internalListener(); }; using ActiveListenerDetailsOptRef = absl::optional>; ActiveListenerDetailsOptRef findActiveListenerByTag(uint64_t listener_tag); diff --git a/source/server/listener_impl.cc b/source/server/listener_impl.cc index 4be05bd5329e8..1ed954ba5a9c3 100644 --- a/source/server/listener_impl.cc +++ b/source/server/listener_impl.cc @@ -83,12 +83,16 @@ ListenSocketFactoryImpl::ListenSocketFactoryImpl( ASSERT(bind_type_ == ListenerComponentFactory::BindType::ReusePort); } } else { - ASSERT(local_address_->type() == Network::Address::Type::Pipe); - // Listeners with Unix domain socket always use shared socket. - // TODO(mattklein123): This should be blocked at the config parsing layer instead of getting - // here and disabling reuse_port. - if (bind_type_ == ListenerComponentFactory::BindType::ReusePort) { - bind_type_ = ListenerComponentFactory::BindType::NoReusePort; + if (local_address_->type() == Network::Address::Type::Pipe) { + // Listeners with Unix domain socket always use shared socket. + // TODO(mattklein123): This should be blocked at the config parsing layer instead of getting + // here and disabling reuse_port. + if (bind_type_ == ListenerComponentFactory::BindType::ReusePort) { + bind_type_ = ListenerComponentFactory::BindType::NoReusePort; + } + } else { + ASSERT(local_address_->type() == Network::Address::Type::EnvoyInternal); + bind_type_ = ListenerComponentFactory::BindType::NoBind; } } @@ -366,6 +370,7 @@ ListenerImpl::ListenerImpl(const envoy::config::listener::v3::Listener& config, buildSocketOptions(); buildOriginalDstListenerFilter(); buildProxyProtocolListenerFilter(); + buildInternalListener(); } if (!workers_started_) { // Initialize dynamic_init_manager_ from Server's init manager if it's not initialized. @@ -423,7 +428,7 @@ ListenerImpl::ListenerImpl(ListenerImpl& origin, createListenerFilterFactories(socket_type); validateFilterChains(socket_type); buildFilterChains(); - + buildInternalListener(); if (socket_type == Network::Socket::Type::Stream) { // Apply the options below only for TCP. buildSocketOptions(); @@ -459,6 +464,38 @@ void ListenerImpl::buildAccessLog() { } } +void ListenerImpl::buildInternalListener() { + if (config_.address().has_envoy_internal_address()) { + internal_listener_config_ = std::make_unique(); + if (config_.has_api_listener()) { + throw EnvoyException( + fmt::format("error adding listener '{}': internal address cannot be used in api listener", + address_->asString())); + } + if ((config_.has_connection_balance_config() && + config_.connection_balance_config().has_exact_balance()) || + config_.enable_mptcp() || + config_.has_enable_reuse_port() // internal listener doesn't use physical l4 port. + || (config_.has_freebind() && config_.freebind().value()) || + config_.has_tcp_backlog_size() || config_.has_tcp_fast_open_queue_length() || + (config_.has_transparent() && config_.transparent().value())) { + throw EnvoyException( + fmt::format("error adding listener '{}': has unsupported tcp listener feature", + address_->asString())); + } + if (!config_.socket_options().empty()) { + throw EnvoyException(fmt::format("error adding listener '{}': does not support socket option", + address_->asString())); + } + } else { + if (config_.has_internal_listener()) { + throw EnvoyException(fmt::format("error adding listener '{}': address is not an internal " + "address but an internal listener config is provided", + address_->asString())); + } + } +} + void ListenerImpl::buildUdpListenerFactory(Network::Socket::Type socket_type, uint32_t concurrency) { if (socket_type != Network::Socket::Type::Datagram) { diff --git a/source/server/listener_impl.h b/source/server/listener_impl.h index a6b8166d61a91..14153bfaa7d6c 100644 --- a/source/server/listener_impl.h +++ b/source/server/listener_impl.h @@ -326,6 +326,10 @@ class ListenerImpl final : public Network::ListenerConfig, return udp_listener_config_ != nullptr ? *udp_listener_config_ : Network::UdpListenerConfigOptRef(); } + Network::InternalListenerConfigOptRef internalListenerConfig() override { + return internal_listener_config_ != nullptr ? *internal_listener_config_ + : Network::InternalListenerConfigOptRef(); + } Network::ConnectionBalancer& connectionBalancer() override { return *connection_balancer_; } ResourceLimit& openConnections() override { return *open_connections_; } const std::vector& accessLogs() const override { @@ -372,6 +376,13 @@ class ListenerImpl final : public Network::ListenerConfig, Network::UdpListenerWorkerRouterPtr listener_worker_router_; }; + struct InternalListenerConfigImpl : public Network::InternalListenerConfig { + InternalListenerConfigImpl( + const envoy::config::listener::v3::Listener_InternalListenerConfig config) + : config_(config) {} + const envoy::config::listener::v3::Listener_InternalListenerConfig config_; + }; + /** * Create a new listener from an existing listener and the new config message if the in place * filter chain update is decided. Should be called only by newListenerWithFilterChain(). @@ -381,6 +392,7 @@ class ListenerImpl final : public Network::ListenerConfig, const std::string& name, bool added_via_api, bool workers_started, uint64_t hash); // Helpers for constructor. void buildAccessLog(); + void buildInternalListener(); void validateConfig(Network::Socket::Type socket_type); void buildUdpListenerFactory(Network::Socket::Type socket_type, uint32_t concurrency); void buildListenSocketOptions(Network::Socket::Type socket_type); @@ -428,6 +440,7 @@ class ListenerImpl final : public Network::ListenerConfig, const std::chrono::milliseconds listener_filters_timeout_; const bool continue_on_listener_filters_timeout_; std::shared_ptr udp_listener_config_; + std::unique_ptr internal_listener_config_; Network::ConnectionBalancerSharedPtr connection_balancer_; std::shared_ptr listener_factory_context_; FilterChainManagerImpl filter_chain_manager_; diff --git a/source/server/listener_manager_impl.cc b/source/server/listener_manager_impl.cc index 2fbc9ab7172cf..545645f01cc29 100644 --- a/source/server/listener_manager_impl.cc +++ b/source/server/listener_manager_impl.cc @@ -170,8 +170,6 @@ Network::SocketSharedPtr ProdListenerComponentFactory::createListenSocket( Network::Address::InstanceConstSharedPtr address, Network::Socket::Type socket_type, const Network::Socket::OptionsSharedPtr& options, BindType bind_type, const Network::SocketCreationOptions& creation_options, uint32_t worker_index) { - ASSERT(address->type() == Network::Address::Type::Ip || - address->type() == Network::Address::Type::Pipe); ASSERT(socket_type == Network::Socket::Type::Stream || socket_type == Network::Socket::Type::Datagram); @@ -192,6 +190,11 @@ Network::SocketSharedPtr ProdListenerComponentFactory::createListenSocket( return std::make_shared(std::move(io_handle), address); } return std::make_shared(address); + } else if (address->type() == Network::Address::Type::EnvoyInternal) { + // Listener manager should have validated that envoy internal address doesn't work with udp + // listener yet. + ASSERT(socket_type == Network::Socket::Type::Stream); + return std::make_shared(address); } const std::string scheme = (socket_type == Network::Socket::Type::Stream) @@ -329,11 +332,12 @@ ListenerManagerStats ListenerManagerImpl::generateStats(Stats::Scope& scope) { bool ListenerManagerImpl::addOrUpdateListener(const envoy::config::listener::v3::Listener& config, const std::string& version_info, bool added_via_api) { - RELEASE_ASSERT( - !config.address().has_envoy_internal_address(), - fmt::format("listener {} has envoy internal address {}. Internal address cannot be used by " - "listener yet", - config.name(), config.address().envoy_internal_address().DebugString())); + if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.internal_address")) { + RELEASE_ASSERT( + !config.address().has_envoy_internal_address(), + fmt::format("listener {} has envoy internal address {}. This runtime feature is disabled.", + config.name(), config.address().envoy_internal_address().DebugString())); + } // TODO(junr03): currently only one ApiListener can be installed via bootstrap to avoid having to // build a collection of listeners, and to have to be able to warm and drain the listeners. In the // future allow multiple ApiListeners, and allow them to be created via LDS as well as bootstrap. diff --git a/test/common/network/BUILD b/test/common/network/BUILD index 228068e269aa8..34fcb5c4a8536 100644 --- a/test/common/network/BUILD +++ b/test/common/network/BUILD @@ -91,6 +91,7 @@ envoy_cc_test( "//test/test_common:environment_lib", "//test/test_common:network_utility_lib", "//test/test_common:simulated_time_system_lib", + "//test/test_common:test_runtime_lib", "//test/test_common:test_time_lib", "//test/test_common:threadsafe_singleton_injector_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", diff --git a/test/common/network/connection_impl_test.cc b/test/common/network/connection_impl_test.cc index 7d2e600272484..ed24bf50a0601 100644 --- a/test/common/network/connection_impl_test.cc +++ b/test/common/network/connection_impl_test.cc @@ -6,6 +6,7 @@ #include "envoy/config/core/v3/base.pb.h" #include "envoy/event/scaled_range_timer_manager.h" #include "envoy/network/address.h" +#include "envoy/network/listener.h" #include "source/common/api/os_sys_calls_impl.h" #include "source/common/buffer/buffer_impl.h" @@ -29,6 +30,7 @@ #include "test/test_common/network_utility.h" #include "test/test_common/printers.h" #include "test/test_common/simulated_time_system.h" +#include "test/test_common/test_runtime.h" #include "test/test_common/threadsafe_singleton_injector.h" #include "test/test_common/utility.h" @@ -54,6 +56,11 @@ namespace Envoy { namespace Network { namespace { +class MockInternalListenerManager : public InternalListenerManager { +public: + MOCK_METHOD(InternalListenerOptRef, findByAddress, (const Address::InstanceConstSharedPtr&), ()); +}; + TEST(RawBufferSocket, TestBasics) { TransportSocketPtr raw_buffer_socket(Network::Test::createRawBufferSocket()); EXPECT_FALSE(raw_buffer_socket->ssl()); @@ -2972,6 +2979,36 @@ TEST_F(PipeClientConnectionImplTest, SkipSourceAddress) { connection->close(ConnectionCloseType::NoFlush); } +class InternalClientConnectionImplTest : public testing::Test { +protected: + InternalClientConnectionImplTest() + : api_(Api::createApiForTest()), dispatcher_(api_->allocateDispatcher("test_thread")) {} + + Api::ApiPtr api_; + Event::DispatcherPtr dispatcher_; + StrictMock client_callbacks_; +}; + +TEST_F(InternalClientConnectionImplTest, + CannotCreateConnectionToInternalAddressWithInternalAddressEnabled) { + auto scoped_runtime_guard = std::make_unique(); + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.reloadable_features.internal_address", "true"}}); + + const Network::SocketInterface* sock_interface = Network::socketInterface( + "envoy.extensions.network.socket_interface.default_socket_interface"); + Network::Address::InstanceConstSharedPtr address = + std::make_shared("listener_0", sock_interface); + // Not implemented yet. + ASSERT_DEATH( + { + ClientConnectionPtr connection = + dispatcher_->createClientConnection(address, Network::Address::InstanceConstSharedPtr(), + Network::Test::createRawBufferSocket(), nullptr); + }, + "panic: not implemented"); +} + } // namespace } // namespace Network } // namespace Envoy diff --git a/test/common/network/listen_socket_impl_test.cc b/test/common/network/listen_socket_impl_test.cc index 6a17c33b7d8f4..c6b8c5fd5e1a6 100644 --- a/test/common/network/listen_socket_impl_test.cc +++ b/test/common/network/listen_socket_impl_test.cc @@ -24,7 +24,12 @@ namespace Envoy { namespace Network { namespace { -TEST(ConnectionSocketImplTest, LowerCaseRequestedServerName) { +class ConnectionSocketImplTest : public testing::TestWithParam {}; + +INSTANTIATE_TEST_SUITE_P(IpVersions, ConnectionSocketImplTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest())); + +TEST_P(ConnectionSocketImplTest, LowerCaseRequestedServerName) { absl::string_view serverName("www.EXAMPLE.com"); absl::string_view expectedServerName("www.example.com"); auto loopback_addr = Network::Test::getCanonicalLoopbackAddress(Address::IpVersion::v4); @@ -33,6 +38,11 @@ TEST(ConnectionSocketImplTest, LowerCaseRequestedServerName) { EXPECT_EQ(expectedServerName, conn_socket_.requestedServerName()); } +TEST_P(ConnectionSocketImplTest, IpVersion) { + ClientSocketImpl socket(Network::Test::getCanonicalLoopbackAddress(GetParam()), nullptr); + EXPECT_EQ(socket.ipVersion(), GetParam()); +} + template class ListenSocketImplTest : public testing::TestWithParam { using ListenSocketType = NetworkListenSocket>; diff --git a/test/config/utility.cc b/test/config/utility.cc index d64e477fdd5a6..320fa3957c75c 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -629,6 +629,18 @@ ConfigHelper::ConfigHelper(const Network::Address::IpVersion version, Api::Api& auto* static_resources = bootstrap_.mutable_static_resources(); for (int i = 0; i < static_resources->listeners_size(); ++i) { auto* listener = static_resources->mutable_listeners(i); + if (listener->mutable_address()->has_envoy_internal_address()) { + ENVOY_LOG_MISC( + debug, "Listener {} has internal address {}. Will not reset to loop back socket address.", + i, listener->mutable_address()->envoy_internal_address().server_listener_name()); + continue; + } + if (listener->mutable_address()->has_pipe()) { + ENVOY_LOG_MISC(debug, + "Listener {} has pipe address {}. Will not reset to loop back socket address.", + i, listener->mutable_address()->pipe().path()); + continue; + } auto* listener_socket_addr = listener->mutable_address()->mutable_socket_address(); if (listener_socket_addr->address() == "0.0.0.0" || listener_socket_addr->address() == "::") { listener_socket_addr->set_address(Network::Test::getAnyAddressString(version)); @@ -913,7 +925,6 @@ void ConfigHelper::setDefaultHostAndRoute(const std::string& domains, const std: void ConfigHelper::setBufferLimits(uint32_t upstream_buffer_limit, uint32_t downstream_buffer_limit) { RELEASE_ASSERT(!finalized_, ""); - RELEASE_ASSERT(bootstrap_.mutable_static_resources()->listeners_size() == 1, ""); auto* listener = bootstrap_.mutable_static_resources()->mutable_listeners(0); listener->mutable_per_connection_buffer_limit_bytes()->set_value(downstream_buffer_limit); const uint32_t stream_buffer_size = std::max( diff --git a/test/extensions/common/proxy_protocol/proxy_protocol_regression_test.cc b/test/extensions/common/proxy_protocol/proxy_protocol_regression_test.cc index cb9d97d5593f8..705f80156db01 100644 --- a/test/extensions/common/proxy_protocol/proxy_protocol_regression_test.cc +++ b/test/extensions/common/proxy_protocol/proxy_protocol_regression_test.cc @@ -72,6 +72,9 @@ class ProxyProtocolRegressionTest : public testing::TestWithParam, uint64_t listenerTag() const override { return 0; } const std::string& name() const override { return name_; } Network::UdpListenerConfigOptRef udpListenerConfig() override { return udp_listener_config_; } + Network::InternalListenerConfigOptRef internalListenerConfig() override { + return Network::InternalListenerConfigOptRef(); + } Network::ConnectionBalancer& connectionBalancer() override { return connection_balancer_; } envoy::config::core::v3::TrafficDirection direction() const override { return envoy::config::core::v3::UNSPECIFIED; diff --git a/test/integration/integration_tcp_client.cc b/test/integration/integration_tcp_client.cc index 2396d74c06da4..0e145ae12c838 100644 --- a/test/integration/integration_tcp_client.cc +++ b/test/integration/integration_tcp_client.cc @@ -53,7 +53,6 @@ IntegrationTcpClient::IntegrationTcpClient( std::function above_overflow) -> Buffer::Instance* { return new Buffer::WatermarkBuffer(below_low, above_high, above_overflow); })); - ; connection_ = dispatcher.createClientConnection( Network::Utility::resolveUrl( diff --git a/test/integration/internal_listener_integration_test.cc b/test/integration/internal_listener_integration_test.cc new file mode 100644 index 0000000000000..11614b84874a9 --- /dev/null +++ b/test/integration/internal_listener_integration_test.cc @@ -0,0 +1,104 @@ +#include "envoy/config/bootstrap/v3/bootstrap.pb.h" +#include "envoy/config/core/v3/config_source.pb.h" +#include "envoy/config/core/v3/grpc_service.pb.h" +#include "envoy/extensions/filters/network/tcp_proxy/v3/tcp_proxy.pb.h" +#include "envoy/network/connection.h" +#include "envoy/service/discovery/v3/discovery.pb.h" + +#include "source/common/config/api_version.h" + +#include "test/common/grpc/grpc_client_integration.h" +#include "test/integration/base_integration_test.h" +#include "test/test_common/network_utility.h" +#include "test/test_common/resources.h" + +#include "absl/strings/str_cat.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace { + +class InternalListenerIntegrationTest : public testing::TestWithParam, + public BaseIntegrationTest { +public: + InternalListenerIntegrationTest() + : BaseIntegrationTest(GetParam(), ConfigHelper::tcpProxyConfig()) {} + + void initialize() override { + config_helper_.renameListener("tcp"); + config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + auto& listener = *bootstrap.mutable_static_resources()->mutable_listeners(0); + listener.mutable_address()->mutable_envoy_internal_address()->set_server_listener_name( + "internal_listener"); + }); + BaseIntegrationTest::initialize(); + } +}; + +TEST_P(InternalListenerIntegrationTest, BasicConfigUpdate) { + initialize(); + EXPECT_EQ(1, test_server_->counter("listener_manager.lds.update_success")->value()); + + ConfigHelper new_config_helper( + version_, *api_, MessageUtil::getJsonStringFromMessageOrDie(config_helper_.bootstrap())); + new_config_helper.addConfigModifier( + [&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { + auto* listener = bootstrap.mutable_static_resources()->mutable_listeners(0); + (*(*listener->mutable_metadata()->mutable_filter_metadata())["random_filter_name"] + .mutable_fields())["random_key"] + .set_number_value(1); + }); + + new_config_helper.setLds("1"); + + test_server_->waitForCounterEq("listener_manager.listener_modified", 1); + test_server_->waitForGaugeEq("listener_manager.total_listeners_draining", 0); +} + +TEST_P(InternalListenerIntegrationTest, InplaceUpdate) { + initialize(); + EXPECT_EQ(1, test_server_->counter("listener_manager.lds.update_success")->value()); + + ConfigHelper new_config_helper( + version_, *api_, MessageUtil::getJsonStringFromMessageOrDie(config_helper_.bootstrap())); + new_config_helper.addConfigModifier( + [&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { + auto* listener = bootstrap.mutable_static_resources()->mutable_listeners(0); + auto new_filter_chain = *listener->mutable_filter_chains(0); + listener->mutable_filter_chains()->Add()->MergeFrom(new_filter_chain); + *(listener->mutable_filter_chains(1) + ->mutable_filter_chain_match() + ->mutable_application_protocols() + ->Add()) = "alpn"; + }); + + new_config_helper.setLds("1"); + + test_server_->waitForCounterEq("listener_manager.listener_modified", 1); + test_server_->waitForGaugeEq("listener_manager.total_listeners_draining", 0); +} + +TEST_P(InternalListenerIntegrationTest, DeleteListener) { + initialize(); + EXPECT_EQ(1, test_server_->counter("listener_manager.lds.update_success")->value()); + + ConfigHelper new_config_helper( + version_, *api_, MessageUtil::getJsonStringFromMessageOrDie(config_helper_.bootstrap())); + new_config_helper.addConfigModifier( + [&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { + bootstrap.mutable_static_resources()->mutable_listeners()->RemoveLast(); + }); + + new_config_helper.setLds("1"); + + test_server_->waitForCounterEq("listener_manager.listener_removed", 1); + test_server_->waitForGaugeEq("listener_manager.total_listeners_draining", 0); +} + +INSTANTIATE_TEST_SUITE_P(IpVersions, InternalListenerIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + +} // namespace +} // namespace Envoy diff --git a/test/integration/socket_interface_integration_test.cc b/test/integration/socket_interface_integration_test.cc index 14bbc28500130..9cae29698a006 100644 --- a/test/integration/socket_interface_integration_test.cc +++ b/test/integration/socket_interface_integration_test.cc @@ -90,7 +90,6 @@ TEST_P(SocketInterfaceIntegrationTest, AddressWithSocketInterface) { } // Test that connecting to internal address will crash. -// TODO(lambdai): Add internal connection implementation to enable the connection creation. TEST_P(SocketInterfaceIntegrationTest, InternalAddressWithSocketInterface) { BaseIntegrationTest::initialize(); @@ -108,7 +107,7 @@ TEST_P(SocketInterfaceIntegrationTest, InternalAddressWithSocketInterface) { } // Test that recv from internal address will crash. -// TODO(lambdai): Add internal socket implementation to enable the io path. +// TODO(lambdai): Add UDP internal listener implementation to enable the io path. TEST_P(SocketInterfaceIntegrationTest, UdpRecvFromInternalAddressWithSocketInterface) { BaseIntegrationTest::initialize(); diff --git a/test/mocks/network/mocks.h b/test/mocks/network/mocks.h index af7a6c7b179a4..c48098fc06668 100644 --- a/test/mocks/network/mocks.h +++ b/test/mocks/network/mocks.h @@ -359,7 +359,6 @@ class MockConnectionSocket : public ConnectionSocket { IoHandlePtr io_handle_; std::shared_ptr connection_info_provider_; - bool is_closed_; }; class MockListenerFilterCallbacks : public ListenerFilterCallbacks { @@ -430,6 +429,7 @@ class MockListenerConfig : public ListenerConfig { MOCK_METHOD(uint64_t, listenerTag, (), (const)); MOCK_METHOD(const std::string&, name, (), (const)); MOCK_METHOD(Network::UdpListenerConfigOptRef, udpListenerConfig, ()); + MOCK_METHOD(InternalListenerConfigOptRef, internalListenerConfig, ()); MOCK_METHOD(ConnectionBalancer&, connectionBalancer, ()); MOCK_METHOD(ResourceLimit&, openConnections, ()); MOCK_METHOD(uint32_t, tcpBacklogSize, (), (const)); diff --git a/test/server/BUILD b/test/server/BUILD index 3195047b65ac4..ff737d73f7808 100644 --- a/test/server/BUILD +++ b/test/server/BUILD @@ -88,8 +88,6 @@ envoy_cc_test( "//test/test_common:network_utility_lib", "//test/test_common:test_runtime_lib", "//test/test_common:threadsafe_singleton_injector_lib", - "@envoy_api//envoy/config/core/v3:pkg_cc_proto", - "@envoy_api//envoy/config/listener/v3:pkg_cc_proto", ], ) @@ -102,14 +100,25 @@ envoy_cc_test( "//source/common/network:address_lib", "//source/common/network:connection_balancer_lib", "//source/common/stats:stats_lib", - "//source/server:active_raw_udp_listener_config", "//source/server:connection_handler_lib", - "//test/mocks/access_log:access_log_mocks", - "//test/mocks/api:api_mocks", "//test/mocks/network:io_handle_mocks", "//test/mocks/network:network_mocks", "//test/test_common:network_utility_lib", - "//test/test_common:threadsafe_singleton_injector_lib", + ], +) + +envoy_cc_test( + name = "active_internal_listener_test", + srcs = ["active_internal_listener_test.cc"], + deps = [ + ":utility_lib", + "//source/common/network:address_lib", + "//source/common/network:listen_socket_lib", + "//source/common/network:utility_lib", + "//source/common/stats:stats_lib", + "//source/extensions/transport_sockets/raw_buffer:config", + "//source/server:connection_handler_lib", + "//test/mocks/network:network_mocks", ], ) diff --git a/test/server/active_internal_listener_test.cc b/test/server/active_internal_listener_test.cc new file mode 100644 index 0000000000000..a2e60f3bc8c89 --- /dev/null +++ b/test/server/active_internal_listener_test.cc @@ -0,0 +1,230 @@ +#include + +#include "envoy/network/filter.h" +#include "envoy/network/listener.h" +#include "envoy/stats/scope.h" + +#include "source/common/network/address_impl.h" +#include "source/common/network/raw_buffer_socket.h" +#include "source/server/active_internal_listener.h" +#include "source/server/connection_handler_impl.h" + +#include "test/mocks/common.h" +#include "test/mocks/network/mocks.h" +#include "test/test_common/network_utility.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using testing::_; +using testing::Invoke; +using testing::NiceMock; +using testing::Return; +using testing::ReturnRef; + +namespace Envoy { +namespace Server { +namespace { + +class MockInternalListenerCallback : public Network::InternalListener { +public: + MOCK_METHOD(void, onAccept, (Network::ConnectionSocketPtr && socket), ()); + MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); +}; +class ActiveInternalListenerTest : public testing::Test, + protected Logger::Loggable { +public: + ActiveInternalListenerTest() { + EXPECT_CALL(listener_config_, listenerScope).Times(testing::AnyNumber()); + EXPECT_CALL(conn_handler_, statPrefix()).WillRepeatedly(ReturnRef(listener_stat_prefix_)); + listener_filter_matcher_ = std::make_shared>(); + } + void addListener() { + EXPECT_CALL(listener_config_, listenerFiltersTimeout()); + EXPECT_CALL(listener_config_, continueOnListenerFiltersTimeout()); + EXPECT_CALL(listener_config_, filterChainManager()).WillRepeatedly(ReturnRef(manager_)); + EXPECT_CALL(listener_config_, openConnections()).WillRepeatedly(ReturnRef(resource_limit_)); + auto mock_listener_will_be_moved = std::make_unique(); + generic_listener_ = mock_listener_will_be_moved.get(); + internal_listener_ = std::make_shared( + conn_handler_, dispatcher_, std::move(mock_listener_will_be_moved), listener_config_); + } + Network::Listener* addListenerWithRealNetworkListener() { + EXPECT_CALL(listener_config_, listenerFiltersTimeout()); + EXPECT_CALL(listener_config_, continueOnListenerFiltersTimeout()); + EXPECT_CALL(listener_config_, filterChainManager()).WillRepeatedly(ReturnRef(manager_)); + EXPECT_CALL(listener_config_, openConnections()).WillRepeatedly(ReturnRef(resource_limit_)); + + internal_listener_ = + std::make_shared(conn_handler_, dispatcher_, listener_config_); + return internal_listener_->listener(); + } + void expectFilterChainFactory() { + EXPECT_CALL(listener_config_, filterChainFactory()) + .WillRepeatedly(ReturnRef(filter_chain_factory_)); + } + std::string listener_stat_prefix_{"listener_stat_prefix"}; + NiceMock dispatcher_{"test"}; + BasicResourceLimitImpl resource_limit_; + Network::MockConnectionHandler conn_handler_; + Network::MockListener* generic_listener_; + Network::MockListenerConfig listener_config_; + NiceMock manager_; + NiceMock filter_chain_factory_; + std::shared_ptr filter_chain_; + std::shared_ptr> listener_filter_matcher_; + std::shared_ptr internal_listener_; +}; + +TEST_F(ActiveInternalListenerTest, BasicInternalListener) { + addListener(); + EXPECT_CALL(*generic_listener_, onDestroy()); +} + +TEST_F(ActiveInternalListenerTest, AcceptSocketAndCreateListenerFilter) { + addListener(); + expectFilterChainFactory(); + Network::MockListenerFilter* test_listener_filter = new Network::MockListenerFilter(); + // FIX-ME: replace by mock socket + Network::Address::InstanceConstSharedPtr original_dst_address( + new Network::Address::Ipv4Instance("127.0.0.2", 8080)); + Network::MockConnectionSocket* accepted_socket = new NiceMock(); + + EXPECT_CALL(filter_chain_factory_, createListenerFilterChain(_)) + .WillRepeatedly(Invoke([&](Network::ListenerFilterManager& manager) -> bool { + // Insert the Mock filter. + manager.addAcceptFilter(listener_filter_matcher_, + Network::ListenerFilterPtr{test_listener_filter}); + return true; + })); + EXPECT_CALL(*test_listener_filter, onAccept(_)) + .WillOnce(Invoke([&](Network::ListenerFilterCallbacks& cb) -> Network::FilterStatus { + cb.socket().connectionInfoProvider().restoreLocalAddress(original_dst_address); + return Network::FilterStatus::Continue; + })); + EXPECT_CALL(*test_listener_filter, destroy_()); + EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(nullptr)); + internal_listener_->onAccept(Network::ConnectionSocketPtr{accepted_socket}); + EXPECT_CALL(*generic_listener_, onDestroy()); +} + +TEST_F(ActiveInternalListenerTest, DestroyListenerClosesActiveSocket) { + addListener(); + expectFilterChainFactory(); + Network::MockListenerFilter* test_listener_filter = new Network::MockListenerFilter(); + Network::MockConnectionSocket* accepted_socket = new NiceMock(); + NiceMock io_handle; + EXPECT_CALL(*accepted_socket, ioHandle()).WillOnce(ReturnRef(io_handle)); + EXPECT_CALL(io_handle, isOpen()).WillOnce(Return(true)); + + EXPECT_CALL(filter_chain_factory_, createListenerFilterChain(_)) + .WillRepeatedly(Invoke([&](Network::ListenerFilterManager& manager) -> bool { + manager.addAcceptFilter(listener_filter_matcher_, + Network::ListenerFilterPtr{test_listener_filter}); + return true; + })); + EXPECT_CALL(*test_listener_filter, onAccept(_)) + .WillOnce(Invoke([&](Network::ListenerFilterCallbacks&) -> Network::FilterStatus { + return Network::FilterStatus::StopIteration; + })); + + internal_listener_->onAccept(Network::ConnectionSocketPtr{accepted_socket}); + + EXPECT_CALL(*test_listener_filter, destroy_()); + EXPECT_CALL(*generic_listener_, onDestroy()); + internal_listener_.reset(); +} + +TEST_F(ActiveInternalListenerTest, AcceptSocketAndCreateNetworkFilter) { + addListener(); + expectFilterChainFactory(); + + Network::MockListenerFilter* test_listener_filter = new Network::MockListenerFilter(); + // FIX-ME: replace by mock socket + Network::Address::InstanceConstSharedPtr original_dst_address( + new Network::Address::Ipv4Instance("127.0.0.2", 8080)); + + Network::MockConnectionSocket* accepted_socket = new NiceMock(); + + EXPECT_CALL(filter_chain_factory_, createListenerFilterChain(_)) + .WillRepeatedly(Invoke([&](Network::ListenerFilterManager& manager) -> bool { + // Insert the Mock filter. + manager.addAcceptFilter(listener_filter_matcher_, + Network::ListenerFilterPtr{test_listener_filter}); + return true; + })); + EXPECT_CALL(*test_listener_filter, onAccept(_)) + .WillOnce(Invoke([&](Network::ListenerFilterCallbacks& cb) -> Network::FilterStatus { + cb.socket().connectionInfoProvider().restoreLocalAddress(original_dst_address); + return Network::FilterStatus::Continue; + })); + EXPECT_CALL(*test_listener_filter, destroy_()); + auto filter_factory_callback = std::make_shared>(); + filter_chain_ = std::make_shared>(); + auto transport_socket_factory = Network::Test::createRawBufferSocketFactory(); + + EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(filter_chain_.get())); + EXPECT_CALL(*filter_chain_, transportSocketFactory) + .WillOnce(testing::ReturnRef(*transport_socket_factory)); + EXPECT_CALL(*filter_chain_, networkFilterFactories).WillOnce(ReturnRef(*filter_factory_callback)); + auto* connection = new NiceMock(); + EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(connection)); + EXPECT_CALL(conn_handler_, incNumConnections()); + EXPECT_CALL(filter_chain_factory_, createNetworkFilterChain(_, _)).WillOnce(Return(true)); + EXPECT_CALL(listener_config_, perConnectionBufferLimitBytes()); + internal_listener_->onAccept(Network::ConnectionSocketPtr{accepted_socket}); + EXPECT_CALL(conn_handler_, decNumConnections()); + connection->close(Network::ConnectionCloseType::NoFlush); + dispatcher_.clearDeferredDeleteList(); + EXPECT_CALL(*generic_listener_, onDestroy()); +} + +TEST_F(ActiveInternalListenerTest, StopListener) { + addListener(); + EXPECT_CALL(*generic_listener_, onDestroy()); + internal_listener_->shutdownListener(); +} + +TEST_F(ActiveInternalListenerTest, PausedListenerAcceptNewSocket) { + addListenerWithRealNetworkListener(); + internal_listener_->pauseListening(); + + expectFilterChainFactory(); + Network::MockConnectionSocket* accepted_socket = new NiceMock(); + + EXPECT_CALL(filter_chain_factory_, createListenerFilterChain(_)) + .WillRepeatedly(Invoke([&](Network::ListenerFilterManager&) -> bool { return true; })); + EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(nullptr)); + internal_listener_->onAccept(Network::ConnectionSocketPtr{accepted_socket}); +} + +TEST_F(ActiveInternalListenerTest, DestroyListenerCloseAllConnections) { + addListenerWithRealNetworkListener(); + internal_listener_->pauseListening(); + + expectFilterChainFactory(); + Network::MockConnectionSocket* accepted_socket = new NiceMock(); + + auto filter_factory_callback = std::make_shared>(); + filter_chain_ = std::make_shared>(); + auto transport_socket_factory = Network::Test::createRawBufferSocketFactory(); + + EXPECT_CALL(filter_chain_factory_, createListenerFilterChain(_)) + .WillRepeatedly(Invoke([&](Network::ListenerFilterManager&) -> bool { return true; })); + EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(filter_chain_.get())); + EXPECT_CALL(*filter_chain_, transportSocketFactory) + .WillOnce(testing::ReturnRef(*transport_socket_factory)); + EXPECT_CALL(*filter_chain_, networkFilterFactories).WillOnce(ReturnRef(*filter_factory_callback)); + auto* connection = new NiceMock(); + EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(connection)); + EXPECT_CALL(conn_handler_, incNumConnections()); + EXPECT_CALL(filter_chain_factory_, createNetworkFilterChain(_, _)).WillOnce(Return(true)); + EXPECT_CALL(listener_config_, perConnectionBufferLimitBytes()); + internal_listener_->onAccept(Network::ConnectionSocketPtr{accepted_socket}); + + EXPECT_CALL(conn_handler_, decNumConnections()); + internal_listener_.reset(); +} +} // namespace +} // namespace Server +} // namespace Envoy diff --git a/test/server/active_tcp_listener_test.cc b/test/server/active_tcp_listener_test.cc index 2e6e2b807b03f..2217c3f64020d 100644 --- a/test/server/active_tcp_listener_test.cc +++ b/test/server/active_tcp_listener_test.cc @@ -10,7 +10,6 @@ #include "source/common/network/utility.h" #include "source/server/active_tcp_listener.h" -#include "test/mocks/api/mocks.h" #include "test/mocks/common.h" #include "test/mocks/network/io_handle.h" #include "test/mocks/network/mocks.h" diff --git a/test/server/connection_handler_test.cc b/test/server/connection_handler_test.cc index 672f24e901648..74a5ca332db0b 100644 --- a/test/server/connection_handler_test.cc +++ b/test/server/connection_handler_test.cc @@ -1,6 +1,9 @@ -#include "envoy/config/core/v3/base.pb.h" -#include "envoy/config/listener/v3/udp_listener_config.pb.h" -#include "envoy/network/exception.h" +#include +#include +#include +#include +#include + #include "envoy/network/filter.h" #include "envoy/stats/scope.h" @@ -104,6 +107,8 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable udp_listener_config_; + std::unique_ptr internal_listener_config_; Network::ConnectionBalancerSharedPtr connection_balancer_; BasicResourceLimitImpl open_connections_; const std::vector access_logs_; @@ -256,6 +269,22 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable> overridden_filter_chain_manager = + nullptr) { + listeners_.emplace_back(std::make_unique( + *this, tag, /*bind_to_port*/ false, /*hand_off_restored_destination_connections*/ false, + name, Network::Socket::Type::Stream, listener_filters_timeout, + continue_on_listener_filters_timeout, access_log_, overridden_filter_chain_manager, + ENVOY_TCP_BACKLOG_SIZE, nullptr)); + listeners_.back()->internal_listener_config_ = + std::make_unique(); + return listeners_.back().get(); + } + void validateOriginalDst(Network::TcpListenerCallbacks** listener_callbacks, TestListener* test_listener, Network::MockListener* listener) { Network::Address::InstanceConstSharedPtr normal_address( @@ -300,7 +329,7 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable dispatcher_{"test"}; std::list listeners_; - Network::ConnectionHandlerPtr handler_; + std::unique_ptr handler_; NiceMock manager_; NiceMock factory_; const std::shared_ptr filter_chain_; @@ -1546,6 +1575,68 @@ TEST_F(ConnectionHandlerTest, ShutdownUdpListener) { << "The read_filter_ should be deleted before the udp_listener_ is deleted."; } +TEST_F(ConnectionHandlerTest, DisableInternalListener) { + InSequence s; + Network::Address::InstanceConstSharedPtr local_address{ + new Network::Address::EnvoyInternalInstance("server_internal_address")}; + + TestListener* internal_listener = + addInternalListener(1, "test_internal_listener", std::chrono::milliseconds(), false, nullptr); + EXPECT_CALL(internal_listener->socket_factory_, localAddress()) + .WillOnce(ReturnRef(local_address)); + handler_->addListener(absl::nullopt, *internal_listener); + auto internal_listener_cb = handler_->findByAddress(local_address); + ASSERT_TRUE(internal_listener_cb.has_value()); + + handler_->disableListeners(); + auto internal_listener_cb_disabled = handler_->findByAddress(local_address); + ASSERT_TRUE(internal_listener_cb_disabled.has_value()); + ASSERT_EQ(&internal_listener_cb_disabled.value().get(), &internal_listener_cb.value().get()); + + handler_->enableListeners(); + auto internal_listener_cb_enabled = handler_->findByAddress(local_address); + ASSERT_TRUE(internal_listener_cb_enabled.has_value()); + ASSERT_EQ(&internal_listener_cb_enabled.value().get(), &internal_listener_cb.value().get()); +} + +TEST_F(ConnectionHandlerTest, InternalListenerInplaceUpdate) { + InSequence s; + uint64_t old_listener_tag = 1; + uint64_t new_listener_tag = 2; + Network::Address::InstanceConstSharedPtr local_address{ + new Network::Address::EnvoyInternalInstance("server_internal_address")}; + + TestListener* internal_listener = addInternalListener( + old_listener_tag, "test_internal_listener", std::chrono::milliseconds(), false, nullptr); + EXPECT_CALL(internal_listener->socket_factory_, localAddress()) + .WillOnce(ReturnRef(local_address)); + handler_->addListener(absl::nullopt, *internal_listener); + + ASSERT_NE(internal_listener, nullptr); + + auto overridden_filter_chain_manager = + std::make_shared>(); + TestListener* new_test_listener = + addInternalListener(new_listener_tag, "test_internal_listener", std::chrono::milliseconds(), + false, overridden_filter_chain_manager); + + handler_->addListener(old_listener_tag, *new_test_listener); + + Network::MockConnectionSocket* connection = new NiceMock(); + + auto internal_listener_cb = handler_->findByAddress(local_address); + + EXPECT_CALL(manager_, findFilterChain(_)).Times(0); + EXPECT_CALL(*overridden_filter_chain_manager, findFilterChain(_)).WillOnce(Return(nullptr)); + EXPECT_CALL(*access_log_, log(_, _, _, _)); + internal_listener_cb.value().get().onAccept(Network::ConnectionSocketPtr{connection}); + EXPECT_EQ(0UL, handler_->numConnections()); + + testing::MockFunction completion; + handler_->removeFilterChains(old_listener_tag, {}, completion.AsStdFunction()); + EXPECT_CALL(completion, Call()); + dispatcher_.clearDeferredDeleteList(); +} } // namespace } // namespace Server } // namespace Envoy diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index 3798dea0a84fe..8002094c46467 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -519,15 +519,93 @@ bind_to_port: false } TEST_F(ListenerManagerImplTest, UnsupportedInternalListener) { + auto scoped_runtime_guard = std::make_unique(); + // Workaround of triggering death at windows platform. + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.reloadable_features.internal_address", "false"}}); + const std::string yaml = R"EOF( -address: - envoy_internal_address: - server_listener_name: a_listener_name -filter_chains: -- filters: [] + name: "foo" + address: + envoy_internal_address: + server_listener_name: a_listener_name + filter_chains: + - filters: [] + )EOF"; + + EXPECT_DEATH(manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true), ".*"); +} + +TEST_F(ListenerManagerImplTest, RejectListenerWithSocketAddressWithInternalListenerConfig) { + auto scoped_runtime_guard = std::make_unique(); + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.reloadable_features.internal_address", "true"}}); + + const std::string yaml = R"EOF( + name: "foo" + address: + socket_address: + address: 127.0.0.1 + port_value: 1234 + internal_listener: {} + filter_chains: + - filters: [] )EOF"; - ASSERT_DEATH(manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true), ""); + EXPECT_THROW_WITH_MESSAGE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true), + EnvoyException, + "error adding listener '127.0.0.1:1234': address is not an internal " + "address but an internal listener config is provided"); +} + +TEST_F(ListenerManagerImplTest, RejectTcpOptionsWithInternalListenerConfig) { + auto scoped_runtime_guard = std::make_unique(); + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.reloadable_features.internal_address", "true"}}); + + const std::string yaml = R"EOF( + name: "foo" + address: + envoy_internal_address: + server_listener_name: test_internal_listener_name + filter_chains: + - filters: [] + )EOF"; + + auto listener = parseListenerFromV3Yaml(yaml); + auto listener_mutators = std::vector>{ + [](envoy::config::listener::v3::Listener& l) { + l.mutable_connection_balance_config()->mutable_exact_balance(); + }, + [](envoy::config::listener::v3::Listener& l) { l.mutable_enable_reuse_port(); }, + [](envoy::config::listener::v3::Listener& l) { l.mutable_freebind()->set_value(true); }, + [](envoy::config::listener::v3::Listener& l) { l.mutable_tcp_backlog_size(); }, + [](envoy::config::listener::v3::Listener& l) { l.mutable_tcp_fast_open_queue_length(); }, + [](envoy::config::listener::v3::Listener& l) { l.mutable_transparent()->set_value(true); }, + + }; + for (const auto& f : listener_mutators) { + auto new_listener = listener; + f(new_listener); + EXPECT_THROW_WITH_MESSAGE(new ListenerImpl(new_listener, "version", *manager_, "foo", true, + false, /*hash=*/static_cast(0), 1), + EnvoyException, + "error adding listener 'envoy://test_internal_listener_name': has " + "unsupported tcp listener feature"); + } + { + auto new_listener = listener; + new_listener.mutable_socket_options()->Add(); + EXPECT_THROW_WITH_MESSAGE(manager_->addOrUpdateListener(new_listener, "", true), EnvoyException, + "error adding listener 'envoy://test_internal_listener_name': does " + "not support socket option") + } + { + auto new_listener = listener; + new_listener.set_enable_mptcp(true); + EXPECT_THROW_WITH_MESSAGE(manager_->addOrUpdateListener(new_listener, "", true), EnvoyException, + "listener foo: enable_mptcp can only be used with IP addresses") + } } TEST_F(ListenerManagerImplTest, NotDefaultListenerFiltersTimeout) { @@ -4768,6 +4846,303 @@ name: test_api_listener_2 EXPECT_EQ("test_api_listener", manager_->apiListener()->get().name()); } +TEST_F(ListenerManagerImplWithRealFiltersTest, AddOrUpdateInternalListener) { + auto scoped_runtime_guard = std::make_unique(); + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.reloadable_features.internal_address", "true"}}); + time_system_.setSystemTime(std::chrono::milliseconds(1001001001001)); + + InSequence s; + + auto* lds_api = new MockLdsApi(); + EXPECT_CALL(listener_factory_, createLdsApi_(_, _)).WillOnce(Return(lds_api)); + envoy::config::core::v3::ConfigSource lds_config; + manager_->createLdsApi(lds_config, nullptr); + + EXPECT_CALL(*lds_api, versionInfo()).WillOnce(Return("")); + checkConfigDump(R"EOF( +static_listeners: +)EOF"); + + // Add foo listener. The internal listener does not need explicit internal listener field. + const std::string listener_foo_yaml = R"EOF( +name: test_internal_listener +address: + envoy_internal_address: + server_listener_name: test_internal_listener_name +filter_chains: {} + )EOF"; + + ListenerHandle* listener_foo = expectListenerCreate(false, true); + EXPECT_TRUE( + manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "version1", true)); + checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); + EXPECT_CALL(*lds_api, versionInfo()).WillOnce(Return("version1")); + checkConfigDump(R"EOF( +version_info: version1 +static_listeners: +dynamic_listeners: + - name: test_internal_listener + warming_state: + version_info: version1 + listener: + "@type": type.googleapis.com/envoy.config.listener.v3.Listener + name: test_internal_listener + address: + envoy_internal_address: + server_listener_name: test_internal_listener_name + filter_chains: {} + last_updated: + seconds: 1001001001 + nanos: 1000000 +)EOF"); + + // Update duplicate should be a NOP. + EXPECT_FALSE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); + checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); + + // Update foo listener. Should share socket. + const std::string listener_foo_update1_yaml = R"EOF( +name: test_internal_listener +address: + envoy_internal_address: + server_listener_name: test_internal_listener_name +filter_chains: {} +per_connection_buffer_limit_bytes: 10 + )EOF"; + + time_system_.setSystemTime(std::chrono::milliseconds(2002002002002)); + + ListenerHandle* listener_foo_update1 = expectListenerCreate(false, true); + EXPECT_CALL(*listener_foo, onDestroy()); + EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_update1_yaml), + "version2", true)); + checkStats(__LINE__, 1, 1, 0, 0, 1, 0, 0); + EXPECT_CALL(*lds_api, versionInfo()).WillOnce(Return("version2")); + checkConfigDump(R"EOF( + version_info: version2 + static_listeners: + dynamic_listeners: + - name: test_internal_listener + warming_state: + version_info: version2 + listener: + "@type": type.googleapis.com/envoy.config.listener.v3.Listener + name: test_internal_listener + address: + envoy_internal_address: + server_listener_name: test_internal_listener_name + filter_chains: {} + per_connection_buffer_limit_bytes: 10 + last_updated: + seconds: 2002002002 + nanos: 2000000 + )EOF"); + + // Validate that workers_started stat is zero before calling startWorkers. + EXPECT_EQ(0, server_.stats_store_ + .gauge("listener_manager.workers_started", Stats::Gauge::ImportMode::NeverImport) + .value()); + + // Start workers. + EXPECT_CALL(*worker_, addListener(_, _, _)); + EXPECT_CALL(*worker_, start(_, _)); + manager_->startWorkers(guard_dog_, callback_.AsStdFunction()); + // Validate that workers_started stat is still zero before workers set the status via + // completion callback. + EXPECT_EQ(0, server_.stats_store_ + .gauge("listener_manager.workers_started", Stats::Gauge::ImportMode::NeverImport) + .value()); + worker_->callAddCompletion(); + + // Validate that workers_started stat is set to 1 after workers have responded with initialization + // status. + EXPECT_EQ(1, server_.stats_store_ + .gauge("listener_manager.workers_started", Stats::Gauge::ImportMode::NeverImport) + .value()); + + // Update duplicate should be a NOP. + EXPECT_FALSE( + manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_update1_yaml), "", true)); + checkStats(__LINE__, 1, 1, 0, 0, 1, 0, 0); + + time_system_.setSystemTime(std::chrono::milliseconds(3003003003003)); + + // Update foo. Should go into warming, have an immediate warming callback, and start immediate + // removal. + ListenerHandle* listener_foo_update2 = expectListenerCreate(false, true); + EXPECT_CALL(*worker_, addListener(_, _, _)); + EXPECT_CALL(*worker_, stopListener(_, _)); + EXPECT_CALL(*listener_foo_update1->drain_manager_, startDrainSequence(_)); + EXPECT_TRUE( + manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "version3", true)); + worker_->callAddCompletion(); + checkStats(__LINE__, 1, 2, 0, 0, 1, 1, 0); + EXPECT_CALL(*lds_api, versionInfo()).WillOnce(Return("version3")); + checkConfigDump(R"EOF( + version_info: version3 + static_listeners: + dynamic_listeners: + - name: test_internal_listener + active_state: + version_info: version3 + listener: + "@type": type.googleapis.com/envoy.config.listener.v3.Listener + name: test_internal_listener + address: + envoy_internal_address: + server_listener_name: test_internal_listener_name + filter_chains: {} + last_updated: + seconds: 3003003003 + nanos: 3000000 + draining_state: + version_info: version2 + listener: + "@type": type.googleapis.com/envoy.config.listener.v3.Listener + name: test_internal_listener + address: + envoy_internal_address: + server_listener_name: test_internal_listener_name + filter_chains: {} + per_connection_buffer_limit_bytes: 10 + last_updated: + seconds: 2002002002 + nanos: 2000000 + )EOF"); + + EXPECT_CALL(*worker_, removeListener(_, _)); + listener_foo_update1->drain_manager_->drain_sequence_completion_(); + checkStats(__LINE__, 1, 2, 0, 0, 1, 1, 0); + EXPECT_CALL(*listener_foo_update1, onDestroy()); + worker_->callRemovalCompletion(); + checkStats(__LINE__, 1, 2, 0, 0, 1, 0, 0); + + time_system_.setSystemTime(std::chrono::milliseconds(4004004004004)); + + // Add bar listener. + const std::string listener_bar_yaml = R"EOF( + name: test_internal_listener_bar + address: + envoy_internal_address: + server_listener_name: test_internal_listener_bar + filter_chains: {} + internal_listener: {} + )EOF"; + + ListenerHandle* listener_bar = expectListenerCreate(false, true); + EXPECT_CALL(*worker_, addListener(_, _, _)); + EXPECT_TRUE( + manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_bar_yaml), "version4", true)); + EXPECT_EQ(2UL, manager_->listeners().size()); + worker_->callAddCompletion(); + checkStats(__LINE__, 2, 2, 0, 0, 2, 0, 0); + + time_system_.setSystemTime(std::chrono::milliseconds(5005005005005)); + + // Add baz listener, this time requiring initializing. + const std::string listener_baz_yaml = R"EOF( + name: test_internal_listener_baz + address: + envoy_internal_address: + server_listener_name: test_internal_listener_baz + filter_chains: {} + internal_listener: {} + )EOF"; + + ListenerHandle* listener_baz = expectListenerCreate(true, true); + EXPECT_CALL(listener_baz->target_, initialize()); + EXPECT_TRUE( + manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_baz_yaml), "version5", true)); + EXPECT_EQ(2UL, manager_->listeners().size()); + checkStats(__LINE__, 3, 2, 0, 1, 2, 0, 0); + EXPECT_CALL(*lds_api, versionInfo()).WillOnce(Return("version5")); + checkConfigDump(R"EOF( + version_info: version5 + dynamic_listeners: + - name: test_internal_listener + active_state: + version_info: version3 + listener: + "@type": type.googleapis.com/envoy.config.listener.v3.Listener + name: test_internal_listener + address: + envoy_internal_address: + server_listener_name: test_internal_listener_name + filter_chains: {} + last_updated: + seconds: 3003003003 + nanos: 3000000 + - name: test_internal_listener_bar + active_state: + version_info: version4 + listener: + "@type": type.googleapis.com/envoy.config.listener.v3.Listener + name: test_internal_listener_bar + address: + envoy_internal_address: + server_listener_name: test_internal_listener_bar + filter_chains: {} + internal_listener: {} + last_updated: + seconds: 4004004004 + nanos: 4000000 + - name: test_internal_listener_baz + warming_state: + version_info: version5 + listener: + "@type": type.googleapis.com/envoy.config.listener.v3.Listener + name: test_internal_listener_baz + address: + envoy_internal_address: + server_listener_name: test_internal_listener_baz + filter_chains: {} + internal_listener: {} + last_updated: + seconds: 5005005005 + nanos: 5000000 + )EOF"); + + // Update a duplicate baz that is currently warming. + EXPECT_FALSE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_baz_yaml), "", true)); + checkStats(__LINE__, 3, 2, 0, 1, 2, 0, 0); + + // Update baz while it is warming. + const std::string listener_baz_update1_yaml = R"EOF( + name: test_internal_listener_baz + address: + envoy_internal_address: + server_listener_name: test_internal_listener_baz + internal_listener: {} + filter_chains: + - filters: + - name: fake + typed_config: {} + )EOF"; + + ListenerHandle* listener_baz_update1 = expectListenerCreate(true, true); + EXPECT_CALL(*listener_baz, onDestroy()).WillOnce(Invoke([listener_baz]() -> void { + // Call the initialize callback during destruction like RDS will. + listener_baz->target_.ready(); + })); + EXPECT_CALL(listener_baz_update1->target_, initialize()); + EXPECT_TRUE( + manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_baz_update1_yaml), "", true)); + EXPECT_EQ(2UL, manager_->listeners().size()); + checkStats(__LINE__, 3, 3, 0, 1, 2, 0, 0); + + // Finish initialization for baz which should make it active. + EXPECT_CALL(*worker_, addListener(_, _, _)); + listener_baz_update1->target_.ready(); + EXPECT_EQ(3UL, manager_->listeners().size()); + worker_->callAddCompletion(); + checkStats(__LINE__, 3, 3, 0, 0, 3, 0, 0); + + EXPECT_CALL(*listener_foo_update2, onDestroy()); + EXPECT_CALL(*listener_bar, onDestroy()); + EXPECT_CALL(*listener_baz_update1, onDestroy()); +} + TEST_F(ListenerManagerImplTest, StopInplaceWarmingListener) { InSequence s; From fe2405ae79b601e14f62ccf31e5393163af90f31 Mon Sep 17 00:00:00 2001 From: Yao Zengzeng Date: Thu, 18 Nov 2021 01:15:36 +0800 Subject: [PATCH 100/110] http2: transist READY client to busy when SETTING decreases the capacity (#18999) Signed-off-by: YaoZengzeng --- source/common/conn_pool/conn_pool_base.cc | 2 +- source/common/http/conn_pool_base.cc | 3 ++ test/common/http/http2/conn_pool_test.cc | 47 +++++++++++++++++++---- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/source/common/conn_pool/conn_pool_base.cc b/source/common/conn_pool/conn_pool_base.cc index de35a059d3ad5..6a3f603148003 100644 --- a/source/common/conn_pool/conn_pool_base.cc +++ b/source/common/conn_pool/conn_pool_base.cc @@ -228,7 +228,7 @@ void ConnPoolImplBase::onStreamClosed(Envoy::ConnectionPool::ActiveClient& clien if (client.state() == ActiveClient::State::DRAINING && client.numActiveStreams() == 0) { // Close out the draining client if we no longer have active streams. client.close(); - } else if (client.state() == ActiveClient::State::BUSY && client.currentUnusedCapacity() != 0) { + } else if (client.state() == ActiveClient::State::BUSY && client.currentUnusedCapacity() > 0) { transitionActiveClientState(client, ActiveClient::State::READY); if (!delay_attaching_stream) { onUpstreamReady(); diff --git a/source/common/http/conn_pool_base.cc b/source/common/http/conn_pool_base.cc index 295220d79db91..e43b369c0b01c 100644 --- a/source/common/http/conn_pool_base.cc +++ b/source/common/http/conn_pool_base.cc @@ -122,6 +122,9 @@ void MultiplexedActiveClientBase::onSettings(ReceivedSettings& settings) { ASSERT(std::numeric_limits::max() >= old_unused_capacity); concurrent_stream_limit_ = settings.maxConcurrentStreams().value(); int64_t delta = old_unused_capacity - currentUnusedCapacity(); + if (state() == ActiveClient::State::READY && currentUnusedCapacity() <= 0) { + parent_.transitionActiveClientState(*this, ActiveClient::State::BUSY); + } parent_.decrClusterStreamCapacity(delta); ENVOY_CONN_LOG(trace, "Decreasing stream capacity by {}", *codec_client_, delta); negative_capacity_ += delta; diff --git a/test/common/http/http2/conn_pool_test.cc b/test/common/http/http2/conn_pool_test.cc index 23c86c527b225..aaf674207d974 100644 --- a/test/common/http/http2/conn_pool_test.cc +++ b/test/common/http/http2/conn_pool_test.cc @@ -1436,32 +1436,63 @@ TEST_F(Http2ConnPoolImplTest, PreconnectWithoutMultiplexing) { TEST_F(Http2ConnPoolImplTest, DisconnectWithNegativeCapacity) { TestScopedRuntime scoped_runtime; - cluster_->http2_options_.mutable_max_concurrent_streams()->set_value(2); + cluster_->http2_options_.mutable_max_concurrent_streams()->set_value(6); ON_CALL(*cluster_, perUpstreamPreconnectRatio).WillByDefault(Return(1)); - // One stream results in one connection. Two streams result in two connections. expectClientsCreate(1); ActiveTestRequest r1(*this, 0, false); - CHECK_STATE(0 /*active*/, 1 /*pending*/, 2 /*capacity*/); + CHECK_STATE(0 /*active*/, 1 /*pending*/, 6 /*capacity*/); ActiveTestRequest r2(*this, 0, false); - CHECK_STATE(0 /*active*/, 2 /*pending*/, 2 /*capacity*/); + CHECK_STATE(0 /*active*/, 2 /*pending*/, 6 /*capacity*/); + ActiveTestRequest r3(*this, 0, false); + CHECK_STATE(0 /*active*/, 3 /*pending*/, 6 /*capacity*/); + ActiveTestRequest r4(*this, 0, false); + CHECK_STATE(0 /*active*/, 4 /*pending*/, 6 /*capacity*/); + ActiveTestRequest r5(*this, 0, false); + CHECK_STATE(0 /*active*/, 5 /*pending*/, 6 /*capacity*/); - // When the connection connects, there is zero spare capacity in this pool. + // When the connection connects, there is 1 spare capacity in this pool. EXPECT_CALL(*test_clients_[0].codec_, newStream(_)) .WillOnce(DoAll(SaveArgAddress(&r1.inner_decoder_), ReturnRef(r1.inner_encoder_))) - .WillOnce(DoAll(SaveArgAddress(&r2.inner_decoder_), ReturnRef(r2.inner_encoder_))); + .WillOnce(DoAll(SaveArgAddress(&r2.inner_decoder_), ReturnRef(r2.inner_encoder_))) + .WillOnce(DoAll(SaveArgAddress(&r3.inner_decoder_), ReturnRef(r3.inner_encoder_))) + .WillOnce(DoAll(SaveArgAddress(&r4.inner_decoder_), ReturnRef(r4.inner_encoder_))) + .WillOnce(DoAll(SaveArgAddress(&r5.inner_decoder_), ReturnRef(r5.inner_encoder_))); EXPECT_CALL(r1.callbacks_.pool_ready_, ready()); EXPECT_CALL(r2.callbacks_.pool_ready_, ready()); + EXPECT_CALL(r3.callbacks_.pool_ready_, ready()); + EXPECT_CALL(r4.callbacks_.pool_ready_, ready()); + EXPECT_CALL(r5.callbacks_.pool_ready_, ready()); expectClientConnect(0); - CHECK_STATE(2 /*active*/, 0 /*pending*/, 0 /*capacity*/); + CHECK_STATE(5 /*active*/, 0 /*pending*/, 1 /*capacity*/); + EXPECT_EQ(pool_->owningList(Envoy::ConnectionPool::ActiveClient::State::READY).size(), 1); - // Settings frame reducing capacity to one stream per connection results in -1 capacity. + // Settings frame results in -2 capacity. NiceMock settings; + settings.max_concurrent_streams_ = 3; + test_clients_[0].codec_client_->onSettings(settings); + CHECK_STATE(5 /*active*/, 0 /*pending*/, -2 /*capacity*/); + EXPECT_EQ(pool_->owningList(Envoy::ConnectionPool::ActiveClient::State::READY).size(), 0); + + // Close 1 stream, concurrency capacity goes to -1, still no ready client available. + completeRequest(r1); + EXPECT_EQ(pool_->owningList(Envoy::ConnectionPool::ActiveClient::State::READY).size(), 0); + + // Close 2 streams, concurrency capacity goes to 1, there should be one ready client. + completeRequest(r2); + completeRequest(r3); + EXPECT_EQ(pool_->owningList(Envoy::ConnectionPool::ActiveClient::State::READY).size(), 1); + CHECK_STATE(2 /*active*/, 0 /*pending*/, 1 /*capacity*/); + + // Another settings frame results in -1 capacity. settings.max_concurrent_streams_ = 1; test_clients_[0].codec_client_->onSettings(settings); CHECK_STATE(2 /*active*/, 0 /*pending*/, -1 /*capacity*/); + EXPECT_EQ(pool_->owningList(Envoy::ConnectionPool::ActiveClient::State::READY).size(), 0); + // Close connection with negative capacity. closeAllClients(); + CHECK_STATE(0 /*active*/, 0 /*pending*/, 0 /*capacity*/); } TEST_F(Http2ConnPoolImplTest, PreconnectWithMultiplexing) { From 41438c95a92be7ef24b6c389fc3a93f6fbedd7fb Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 17 Nov 2021 14:03:49 -0500 Subject: [PATCH 101/110] dfp: adding timing information about DNS resolution (#18934) Risk Level: low Testing: new unit, integration tests Docs Changes: Release Notes: Part of envoyproxy/envoy-mobile#1520 Signed-off-by: Alyssa Wilk --- envoy/stream_info/stream_info.h | 10 +++++ .../dynamic_forward_proxy/proxy_filter.cc | 11 ++++++ .../http/dynamic_forward_proxy/proxy_filter.h | 3 ++ .../filters/http/dynamic_forward_proxy/BUILD | 1 + .../proxy_filter_integration_test.cc | 10 +++++ .../proxy_filter_test.cc | 38 +++++++++++++++++-- .../filters/stream_info_to_headers_filter.cc | 20 ++++++++++ 7 files changed, 89 insertions(+), 4 deletions(-) diff --git a/envoy/stream_info/stream_info.h b/envoy/stream_info/stream_info.h index c81720053b047..28374144eff0c 100644 --- a/envoy/stream_info/stream_info.h +++ b/envoy/stream_info/stream_info.h @@ -257,6 +257,16 @@ struct UpstreamTiming { class DownstreamTiming { public: + void setValue(absl::string_view key, MonotonicTime value) { timings_[key] = value; } + + absl::optional getValue(absl::string_view value) const { + auto ret = timings_.find(value); + if (ret == timings_.end()) { + return {}; + } + return ret->second; + } + absl::optional lastDownstreamRxByteReceived() const { return last_downstream_rx_byte_received_; } diff --git a/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.cc b/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.cc index a25f157ea9eae..15d817c9b05bc 100644 --- a/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.cc +++ b/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.cc @@ -12,7 +12,15 @@ namespace Envoy { namespace Extensions { namespace HttpFilters { namespace DynamicForwardProxy { +namespace { +void latchTime(Http::StreamDecoderFilterCallbacks* decoder_callbacks, absl::string_view key) { + StreamInfo::DownstreamTiming& downstream_timing = + decoder_callbacks->streamInfo().downstreamTiming(); + downstream_timing.setValue(key, decoder_callbacks->dispatcher().timeSource().monotonicTime()); +} + +} // namespace struct ResponseStringValues { const std::string DnsCacheOverflow = "DNS cache overflow"; const std::string PendingRequestOverflow = "Dynamic forward proxy pending request overflow"; @@ -110,6 +118,7 @@ Http::FilterHeadersStatus ProxyFilter::decodeHeaders(Http::RequestHeaderMap& hea } } + latchTime(decoder_callbacks_, DNS_START); // See the comments in dns_cache.h for how loadDnsCacheEntry() handles hosts with embedded ports. // TODO(mattklein123): Because the filter and cluster have independent configuration, it is // not obvious to the user if something is misconfigured. We should see if @@ -132,6 +141,7 @@ Http::FilterHeadersStatus ProxyFilter::decodeHeaders(Http::RequestHeaderMap& hea addHostAddressToFilterState(host.value()->address()); } + latchTime(decoder_callbacks_, DNS_END); return Http::FilterHeadersStatus::Continue; } case LoadDnsCacheEntryStatus::Loading: @@ -183,6 +193,7 @@ void ProxyFilter::onLoadDnsCacheComplete( const Common::DynamicForwardProxy::DnsHostInfoSharedPtr& host_info) { ENVOY_STREAM_LOG(debug, "load DNS cache complete, continuing after adding resolved host: {}", *decoder_callbacks_, host_info->resolvedHost()); + latchTime(decoder_callbacks_, DNS_END); ASSERT(circuit_breaker_ != nullptr); circuit_breaker_.reset(); diff --git a/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.h b/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.h index a0920e553c704..0bc97e646d328 100644 --- a/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.h +++ b/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.h @@ -51,6 +51,9 @@ class ProxyFilter public: ProxyFilter(const ProxyFilterConfigSharedPtr& config) : config_(config) {} + static constexpr absl::string_view DNS_START = "envoy.dynamic_forward_proxy.dns_start_ms"; + static constexpr absl::string_view DNS_END = "envoy.dynamic_forward_proxy.dns_end_ms"; + // Http::PassThroughDecoderFilter Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& headers, bool end_stream) override; diff --git a/test/extensions/filters/http/dynamic_forward_proxy/BUILD b/test/extensions/filters/http/dynamic_forward_proxy/BUILD index ec2a0c9534eda..1dcafd08831a5 100644 --- a/test/extensions/filters/http/dynamic_forward_proxy/BUILD +++ b/test/extensions/filters/http/dynamic_forward_proxy/BUILD @@ -57,6 +57,7 @@ envoy_extension_cc_test( "//source/extensions/filters/http/dynamic_forward_proxy:config", "//source/extensions/key_value/file_based:config_lib", "//test/integration:http_integration_lib", + "//test/integration/filters:stream_info_to_headers_filter_lib", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", "@envoy_api//envoy/config/cluster/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", diff --git a/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_integration_test.cc b/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_integration_test.cc index b8d03fa681b98..b9829dd6286d0 100644 --- a/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_integration_test.cc +++ b/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_integration_test.cc @@ -43,6 +43,10 @@ name: dynamic_forward_proxy max_hosts, max_pending_requests, filename); config_helper_.prependFilter(filter); + config_helper_.prependFilter(fmt::format(R"EOF( +name: stream-info-to-headers-filter +typed_config: + "@type": type.googleapis.com/google.protobuf.Empty)EOF")); config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { // Switch predefined cluster_0 to CDS filesystem sourcing. bootstrap.mutable_dynamic_resources()->mutable_cds_config()->set_resource_api_version( @@ -174,12 +178,18 @@ TEST_P(ProxyFilterIntegrationTest, RequestWithBody) { checkSimpleRequestSuccess(1024, 1024, response.get()); EXPECT_EQ(1, test_server_->counter("dns_cache.foo.dns_query_attempt")->value()); EXPECT_EQ(1, test_server_->counter("dns_cache.foo.host_added")->value()); + // Make sure dns timings are tracked for cache-misses. + ASSERT_FALSE(response->headers().get(Http::LowerCaseString("dns_start")).empty()); + ASSERT_FALSE(response->headers().get(Http::LowerCaseString("dns_end")).empty()); // Now send another request. This should hit the DNS cache. response = sendRequestAndWaitForResponse(request_headers, 512, default_response_headers_, 512); checkSimpleRequestSuccess(512, 512, response.get()); EXPECT_EQ(1, test_server_->counter("dns_cache.foo.dns_query_attempt")->value()); EXPECT_EQ(1, test_server_->counter("dns_cache.foo.host_added")->value()); + // Make sure dns timings are tracked for cache-hits. + ASSERT_FALSE(response->headers().get(Http::LowerCaseString("dns_start")).empty()); + ASSERT_FALSE(response->headers().get(Http::LowerCaseString("dns_end")).empty()); } // Currently if the first DNS resolution fails, the filter will continue with diff --git a/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_test.cc b/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_test.cc index c2150e500dd91..6c6413066ceb8 100644 --- a/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_test.cc +++ b/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_test.cc @@ -103,6 +103,9 @@ TEST_F(ProxyFilterTest, HttpDefaultPort) { EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_()) .WillOnce(Return(circuit_breakers_)); EXPECT_CALL(*transport_socket_factory_, implementsSecureTransport()).WillOnce(Return(false)); + EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, dispatcher()); + Extensions::Common::DynamicForwardProxy::MockLoadDnsCacheEntryHandle* handle = new Extensions::Common::DynamicForwardProxy::MockLoadDnsCacheEntryHandle(); EXPECT_CALL(*dns_cache_manager_->dns_cache_, loadDnsCacheEntry_(Eq("foo"), 80, _)) @@ -126,6 +129,9 @@ TEST_F(ProxyFilterTest, HttpsDefaultPort) { EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_()) .WillOnce(Return(circuit_breakers_)); EXPECT_CALL(*transport_socket_factory_, implementsSecureTransport()).WillOnce(Return(true)); + EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, dispatcher()); + Extensions::Common::DynamicForwardProxy::MockLoadDnsCacheEntryHandle* handle = new Extensions::Common::DynamicForwardProxy::MockLoadDnsCacheEntryHandle(); EXPECT_CALL(*dns_cache_manager_->dns_cache_, loadDnsCacheEntry_(Eq("foo"), 443, _)) @@ -149,6 +155,9 @@ TEST_F(ProxyFilterTest, CacheOverflow) { EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_()) .WillOnce(Return(circuit_breakers_)); EXPECT_CALL(*transport_socket_factory_, implementsSecureTransport()).WillOnce(Return(true)); + EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, dispatcher()); + EXPECT_CALL(*dns_cache_manager_->dns_cache_, loadDnsCacheEntry_(Eq("foo"), 443, _)) .WillOnce(Return( MockLoadDnsCacheEntryResult{LoadDnsCacheEntryStatus::Overflow, nullptr, absl::nullopt})); @@ -173,6 +182,8 @@ TEST_F(ProxyFilterTest, CircuitBreakerOverflow) { EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_()) .WillOnce(Return(circuit_breakers_)); EXPECT_CALL(*transport_socket_factory_, implementsSecureTransport()).WillOnce(Return(true)); + EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, dispatcher()); Extensions::Common::DynamicForwardProxy::MockLoadDnsCacheEntryHandle* handle = new Extensions::Common::DynamicForwardProxy::MockLoadDnsCacheEntryHandle(); EXPECT_CALL(*dns_cache_manager_->dns_cache_, loadDnsCacheEntry_(Eq("foo"), 443, _)) @@ -213,6 +224,8 @@ TEST_F(ProxyFilterTest, CircuitBreakerOverflowWithDnsCacheResourceManager) { EXPECT_CALL(*transport_socket_factory_, implementsSecureTransport()).WillOnce(Return(true)); Extensions::Common::DynamicForwardProxy::MockLoadDnsCacheEntryHandle* handle = new Extensions::Common::DynamicForwardProxy::MockLoadDnsCacheEntryHandle(); + EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, dispatcher()); EXPECT_CALL(*dns_cache_manager_->dns_cache_, loadDnsCacheEntry_(Eq("foo"), 443, _)) .WillOnce(Return( MockLoadDnsCacheEntryResult{LoadDnsCacheEntryStatus::Loading, handle, absl::nullopt})); @@ -301,6 +314,8 @@ TEST_F(ProxyFilterTest, HostRewrite) { EXPECT_CALL(*callbacks_.route_, mostSpecificPerFilterConfig("envoy.filters.http.dynamic_forward_proxy")) .WillOnce(Return(&config)); + EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, dispatcher()); EXPECT_CALL(*dns_cache_manager_->dns_cache_, loadDnsCacheEntry_(Eq("bar"), 80, _)) .WillOnce(Return( MockLoadDnsCacheEntryResult{LoadDnsCacheEntryStatus::Loading, handle, absl::nullopt})); @@ -330,6 +345,8 @@ TEST_F(ProxyFilterTest, HostRewriteViaHeader) { EXPECT_CALL(*callbacks_.route_, mostSpecificPerFilterConfig("envoy.filters.http.dynamic_forward_proxy")) .WillOnce(Return(&config)); + EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, dispatcher()); EXPECT_CALL(*dns_cache_manager_->dns_cache_, loadDnsCacheEntry_(Eq("bar:82"), 80, _)) .WillOnce(Return( MockLoadDnsCacheEntryResult{LoadDnsCacheEntryStatus::Loading, handle, absl::nullopt})); @@ -377,6 +394,8 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, AddResolvedHostFilterStateMetadata EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_()) .WillOnce(Return(circuit_breakers_)); EXPECT_CALL(*transport_socket_factory_, implementsSecureTransport()).WillOnce(Return(false)); + EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, dispatcher()); EXPECT_CALL(*dns_cache_manager_->dns_cache_, loadDnsCacheEntry_(Eq("foo"), 80, _)) .WillOnce(Invoke([&](absl::string_view, uint16_t, ProxyFilter::LoadDnsCacheEntryCallbacks&) { @@ -393,6 +412,8 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, AddResolvedHostFilterStateMetadata EXPECT_CALL(*host_info, address()); EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, dispatcher()); // Host was resolved successfully, so continue filter iteration. EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); @@ -410,7 +431,7 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, UpdateResolvedHostFilterStateMetad Upstream::ResourceAutoIncDec* circuit_breakers_( new Upstream::ResourceAutoIncDec(pending_requests_)); - EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, streamInfo()).Times(testing::AnyNumber()); // Pre-populate the filter state with an address. auto& filter_state = callbacks_.streamInfo().filterState(); @@ -432,6 +453,8 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, UpdateResolvedHostFilterStateMetad EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_()) .WillOnce(Return(circuit_breakers_)); EXPECT_CALL(*transport_socket_factory_, implementsSecureTransport()).WillOnce(Return(false)); + EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, dispatcher()); EXPECT_CALL(*dns_cache_manager_->dns_cache_, loadDnsCacheEntry_(Eq("foo"), 80, _)) .WillOnce(Invoke([&](absl::string_view, uint16_t, ProxyFilter::LoadDnsCacheEntryCallbacks&) { @@ -447,14 +470,18 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, UpdateResolvedHostFilterStateMetad EXPECT_CALL(*host_info, address()); + EXPECT_CALL(callbacks_, dispatcher()); + EXPECT_CALL(callbacks_, streamInfo()); EXPECT_CALL(callbacks_, streamInfo()); // Host was resolved successfully, so continue filter iteration. EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); - // We expect FilterState to be populated + // We expect FilterState and resolution times to be populated EXPECT_TRUE( - filter_state->hasData(StreamInfo::UpstreamAddress::key())); + callbacks_.streamInfo().downstreamTiming().getValue(ProxyFilter::DNS_START).has_value()); + EXPECT_TRUE( + callbacks_.streamInfo().downstreamTiming().getValue(ProxyFilter::DNS_END).has_value()); const StreamInfo::UpstreamAddress& updated_address_obj = filter_state->getDataReadOnly( @@ -487,7 +514,8 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, IgnoreFilterStateMetadataNullAddre EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_()) .WillOnce(Return(circuit_breakers_)); EXPECT_CALL(*transport_socket_factory_, implementsSecureTransport()).WillOnce(Return(false)); - + EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, dispatcher()); EXPECT_CALL(*dns_cache_manager_->dns_cache_, loadDnsCacheEntry_(Eq("foo"), 80, _)) .WillOnce(Invoke([&](absl::string_view, uint16_t, ProxyFilter::LoadDnsCacheEntryCallbacks&) { return MockLoadDnsCacheEntryResult{LoadDnsCacheEntryStatus::InCache, nullptr, host_info}; @@ -501,6 +529,8 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, IgnoreFilterStateMetadataNullAddre })); EXPECT_CALL(*host_info, address()); + EXPECT_CALL(callbacks_, streamInfo()); + EXPECT_CALL(callbacks_, dispatcher()); EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); diff --git a/test/integration/filters/stream_info_to_headers_filter.cc b/test/integration/filters/stream_info_to_headers_filter.cc index 190cb006ee740..8914454b4e0f4 100644 --- a/test/integration/filters/stream_info_to_headers_filter.cc +++ b/test/integration/filters/stream_info_to_headers_filter.cc @@ -9,6 +9,13 @@ #include "gtest/gtest.h" namespace Envoy { +namespace { + +uint64_t toMs(MonotonicTime time) { + return std::chrono::duration_cast(time.time_since_epoch()).count(); +} + +} // namespace // A filter that sticks stream info into headers for integration testing. class StreamInfoToHeadersFilter : public Http::PassThroughFilter { @@ -20,6 +27,19 @@ class StreamInfoToHeadersFilter : public Http::PassThroughFilter { } Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers, bool) override { + const std::string dns_start = "envoy.dynamic_forward_proxy.dns_start_ms"; + const std::string dns_end = "envoy.dynamic_forward_proxy.dns_end_ms"; + StreamInfo::StreamInfo& stream_info = decoder_callbacks_->streamInfo(); + + if (stream_info.downstreamTiming().getValue(dns_start).has_value()) { + headers.addCopy( + Http::LowerCaseString("dns_start"), + absl::StrCat(toMs(stream_info.downstreamTiming().getValue(dns_start).value()))); + } + if (stream_info.downstreamTiming().getValue(dns_end).has_value()) { + headers.addCopy(Http::LowerCaseString("dns_end"), + absl::StrCat(toMs(stream_info.downstreamTiming().getValue(dns_end).value()))); + } if (decoder_callbacks_->streamInfo().upstreamSslConnection()) { headers.addCopy(Http::LowerCaseString("alpn"), decoder_callbacks_->streamInfo().upstreamSslConnection()->alpn()); From 93cdfce6c80fda4107d73f7037744b4953abf92e Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 17 Nov 2021 15:46:15 -0500 Subject: [PATCH 102/110] http: proxying 102s. (#19023) Proxying 102s from upstream Risk Level: Low (minor refactor) Testing: new integration test Docs Changes: n/a Release Notes: inline Runtime guard: envoy.reloadable_features.proxy_102 Fixes #18844 Signed-off-by: Alyssa Wilk --- docs/root/version_history/current.rst | 1 + envoy/http/codec.h | 4 +- source/common/http/header_utility.cc | 5 ++ source/common/http/http1/codec_impl.cc | 4 +- source/common/http/http2/codec_impl.cc | 2 +- source/common/router/router.cc | 5 +- source/common/runtime/runtime_features.cc | 1 + test/common/http/http1/codec_impl_test.cc | 50 +++++++++++++++++-- test/common/http/http2/codec_impl_test.cc | 6 +-- .../quic/envoy_quic_client_stream_test.cc | 4 +- test/integration/http_integration.cc | 25 ++++++---- test/integration/http_integration.h | 3 +- .../integration/integration_stream_decoder.cc | 2 +- test/integration/integration_stream_decoder.h | 2 +- .../multiplexed_integration_test.cc | 2 +- test/integration/protocol_integration_test.cc | 8 +++ 16 files changed, 93 insertions(+), 31 deletions(-) diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 156d31a300502..0da2fd5d1f46c 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -15,6 +15,7 @@ Minor Behavior Changes * bandwidth_limit: added :ref:`bandwidth limit stats ` *request_enforced* and *response_enforced*. * config: the log message for "gRPC config stream closed" now uses the most recent error message, and reports seconds instead of milliseconds for how long the most recent status has been received. * dns: now respecting the returned DNS TTL for resolved hosts, rather than always relying on the hard-coded :ref:`dns_refresh_rate. ` This behavior can be temporarily reverted by setting the runtime guard ``envoy.reloadable_features.use_dns_ttl`` to false. +* http: envoy will now proxy 102 and 103 headers from upstream, though as with 100s only the first 1xx response headers will be sent. This behavioral change by can temporarily reverted by setting runtime guard ``envoy.reloadable_features.proxy_102_103`` to false. * http: usage of the experimental matching API is no longer guarded behind a feature flag, as the corresponding protobuf fields have been marked as WIP. * json: switching from rapidjson to nlohmann/json. This behavioral change can be temporarily reverted by setting runtime guard ``envoy.reloadable_features.remove_legacy_json`` to false. * listener: destroy per network filter chain stats when a network filter chain is removed during the listener in place update. diff --git a/envoy/http/codec.h b/envoy/http/codec.h index 9dec51e385ff5..7445fb20eec52 100644 --- a/envoy/http/codec.h +++ b/envoy/http/codec.h @@ -141,7 +141,7 @@ class ResponseEncoder : public virtual StreamEncoder { public: /** * Encode supported 1xx headers. - * Currently only 100-Continue headers are supported. + * Currently 100-Continue, 102-Processing, and 103-Early-Data headers are supported. * @param headers supplies the 1xx header map to encode. */ virtual void encode1xxHeaders(const ResponseHeaderMap& headers) PURE; @@ -237,7 +237,7 @@ class ResponseDecoder : public virtual StreamDecoder { public: /** * Called with decoded 1xx headers. - * Currently only 100-Continue headers are supported. + * Currently 100-Continue, 102-Processing, and 103-Early-Data headers are supported. * @param headers supplies the decoded 1xx headers map. */ virtual void decode1xxHeaders(ResponseHeaderMapPtr&& headers) PURE; diff --git a/source/common/http/header_utility.cc b/source/common/http/header_utility.cc index c278b30bd19f3..a7f630b041b4c 100644 --- a/source/common/http/header_utility.cc +++ b/source/common/http/header_utility.cc @@ -202,6 +202,11 @@ bool HeaderUtility::authorityIsValid(const absl::string_view header_value) { } bool HeaderUtility::isSpecial1xx(const ResponseHeaderMap& response_headers) { + if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.proxy_102_103") && + (response_headers.Status()->value() == "102" || + response_headers.Status()->value() == "103")) { + return true; + } return response_headers.Status()->value() == "100"; } diff --git a/source/common/http/http1/codec_impl.cc b/source/common/http/http1/codec_impl.cc index 2bf8b4cc1dc92..0468bdbcb86fd 100644 --- a/source/common/http/http1/codec_impl.cc +++ b/source/common/http/http1/codec_impl.cc @@ -181,8 +181,8 @@ void StreamEncoderImpl::encodeHeadersBase(const RequestOrResponseHeaderMap& head if (saw_content_length || disable_chunk_encoding_) { chunk_encoding_ = false; } else { - if (status && *status == 100) { - // Make sure we don't serialize chunk information with 100-Continue headers. + if (status && (*status == 100 || *status == 102 || *status == 103)) { + // Make sure we don't serialize chunk information with 1xx headers. chunk_encoding_ = false; } else if (status && *status == 304 && connection_.noChunkedEncodingHeaderFor304()) { // For 304 response, since it should never have a body, we should not need to chunk_encode at diff --git a/source/common/http/http2/codec_impl.cc b/source/common/http/http2/codec_impl.cc index d663aca876d5f..694cbb7bc27ca 100644 --- a/source/common/http/http2/codec_impl.cc +++ b/source/common/http/http2/codec_impl.cc @@ -400,7 +400,7 @@ void ConnectionImpl::ClientStreamImpl::decodeHeaders() { received_noninformational_headers_ = !CodeUtility::is1xx(status) || status == enumToInt(Http::Code::SwitchingProtocols); - if (status == enumToInt(Http::Code::Continue)) { + if (HeaderUtility::isSpecial1xx(*headers)) { ASSERT(!remote_end_stream_); response_decoder_.decode1xxHeaders(std::move(headers)); } else { diff --git a/source/common/router/router.cc b/source/common/router/router.cc index f58d36f56eacc..59336369ab11d 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -1227,8 +1227,9 @@ void Filter::handleNon5xxResponseHeaders(absl::optional response_decoder; + Http::RequestEncoder& request_encoder = codec_->newStream(response_decoder); + TestRequestHeaderMapImpl headers{{":method", "GET"}, {":path", "/"}, {":authority", "host"}}; + EXPECT_TRUE(request_encoder.encodeHeaders(headers, true).ok()); + + EXPECT_CALL(response_decoder, decode1xxHeaders_(_)); + EXPECT_CALL(response_decoder, decodeData(_, _)).Times(0); + Buffer::OwnedImpl initial_response("HTTP/1.1 102 Processing\r\n\r\n"); + auto status = codec_->dispatch(initial_response); + EXPECT_TRUE(status.ok()); + + EXPECT_CALL(response_decoder, decodeHeaders_(_, false)); + EXPECT_CALL(response_decoder, decodeData(_, _)).Times(0); + Buffer::OwnedImpl response("HTTP/1.1 200 OK\r\n\r\n"); + status = codec_->dispatch(response); + EXPECT_TRUE(status.ok()); +} + +// 103 response followed by 200 results in a [decode1xxHeaders, decodeHeaders] sequence. +TEST_F(Http1ClientConnectionImplTest, EarlyHintHeaders) { + initialize(); + + NiceMock response_decoder; + Http::RequestEncoder& request_encoder = codec_->newStream(response_decoder); + TestRequestHeaderMapImpl headers{{":method", "GET"}, {":path", "/"}, {":authority", "host"}}; + EXPECT_TRUE(request_encoder.encodeHeaders(headers, true).ok()); + + EXPECT_CALL(response_decoder, decode1xxHeaders_(_)); + EXPECT_CALL(response_decoder, decodeData(_, _)).Times(0); + Buffer::OwnedImpl initial_response("HTTP/1.1 103 Early Hints\r\n\r\n"); + auto status = codec_->dispatch(initial_response); + EXPECT_TRUE(status.ok()); + + EXPECT_CALL(response_decoder, decodeHeaders_(_, false)); + EXPECT_CALL(response_decoder, decodeData(_, _)).Times(0); + Buffer::OwnedImpl response("HTTP/1.1 200 OK\r\n\r\n"); + status = codec_->dispatch(response); + EXPECT_TRUE(status.ok()); +} + // Multiple 100 responses are passed to the response encoder (who is responsible for coalescing). TEST_F(Http1ClientConnectionImplTest, MultipleContinueHeaders) { initialize(); @@ -2536,9 +2580,7 @@ TEST_F(Http1ClientConnectionImplTest, MultipleContinueHeaders) { EXPECT_TRUE(status.ok()); } -// 101/102 headers etc. are passed to the response encoder (who is responsibly for deciding to -// upgrade, ignore, etc.). -TEST_F(Http1ClientConnectionImplTest, 1xxNonContinueHeaders) { +TEST_F(Http1ClientConnectionImplTest, Unsupported1xxHeader) { initialize(); NiceMock response_decoder; @@ -2547,7 +2589,7 @@ TEST_F(Http1ClientConnectionImplTest, 1xxNonContinueHeaders) { EXPECT_TRUE(request_encoder.encodeHeaders(headers, true).ok()); EXPECT_CALL(response_decoder, decodeHeaders_(_, false)); - Buffer::OwnedImpl response("HTTP/1.1 102 Processing\r\n\r\n"); + Buffer::OwnedImpl response("HTTP/1.1 199 Unknown\r\n\r\n"); auto status = codec_->dispatch(response); EXPECT_TRUE(status.ok()); } diff --git a/test/common/http/http2/codec_impl_test.cc b/test/common/http/http2/codec_impl_test.cc index 9855f487fefe3..2cc18d87b36e4 100644 --- a/test/common/http/http2/codec_impl_test.cc +++ b/test/common/http/http2/codec_impl_test.cc @@ -527,9 +527,9 @@ TEST_P(Http2CodecImplTest, MultipleContinueHeaders) { response_encoder_->encodeHeaders(response_headers, true); }; -// 101/102 headers etc. are passed to the response encoder (who is responsibly for deciding to +// 104 headers etc. are passed to the response encoder (who is responsibly for deciding to // upgrade, ignore, etc.). -TEST_P(Http2CodecImplTest, 1xxNonContinueHeaders) { +TEST_P(Http2CodecImplTest, Unsupported1xxHeader) { initialize(); TestRequestHeaderMapImpl request_headers; @@ -537,7 +537,7 @@ TEST_P(Http2CodecImplTest, 1xxNonContinueHeaders) { EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); - TestResponseHeaderMapImpl other_headers{{":status", "102"}}; + TestResponseHeaderMapImpl other_headers{{":status", "104"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); response_encoder_->encodeHeaders(other_headers, false); }; diff --git a/test/common/quic/envoy_quic_client_stream_test.cc b/test/common/quic/envoy_quic_client_stream_test.cc index a2b8ac4da7d8c..7bead0f2972df 100644 --- a/test/common/quic/envoy_quic_client_stream_test.cc +++ b/test/common/quic/envoy_quic_client_stream_test.cc @@ -206,14 +206,14 @@ TEST_F(EnvoyQuicClientStreamTest, PostRequestAnd1xx) { })); EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) .WillOnce(Invoke([](const Http::ResponseHeaderMapPtr& headers, bool) { - EXPECT_EQ("103", headers->getStatusValue()); + EXPECT_EQ("199", headers->getStatusValue()); EXPECT_EQ("1", headers->get(Http::LowerCaseString("i"))[0]->value().getStringView()); })); size_t offset = 0; size_t i = 0; // Receive several 10x headers, only the first 100 Continue header should be // delivered. - for (const std::string& status : {"100", "103", "100"}) { + for (const std::string& status : {"100", "199", "100"}) { spdy::SpdyHeaderBlock continue_header; continue_header[":status"] = status; continue_header["i"] = absl::StrCat("", i++); diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index 814744f5ea4d4..0a387125542d2 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -971,7 +971,7 @@ void HttpIntegrationTest::testEnvoyHandling1xx(bool additional_continue_from_ups auto response = std::move(encoder_decoder.second); ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); // The continue headers should arrive immediately. - response->waitForContinueHeaders(); + response->waitFor1xxHeaders(); ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); // Send the rest of the request. @@ -994,7 +994,7 @@ void HttpIntegrationTest::testEnvoyHandling1xx(bool additional_continue_from_ups } if (disconnect_after_100) { - response->waitForContinueHeaders(); + response->waitFor1xxHeaders(); codec_client_->close(); EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("100")); ASSERT_TRUE(fake_upstream_connection_->close()); @@ -1020,7 +1020,8 @@ void HttpIntegrationTest::testEnvoyHandling1xx(bool additional_continue_from_ups void HttpIntegrationTest::testEnvoyProxying1xx(bool continue_before_upstream_complete, bool with_encoder_filter, - bool with_multiple_1xx_headers) { + bool with_multiple_1xx_headers, + absl::string_view initial_code) { if (with_encoder_filter) { // Add a filter to make sure 100s play well with them. config_helper_.prependFilter("name: passthrough-filter"); @@ -1044,37 +1045,39 @@ void HttpIntegrationTest::testEnvoyProxying1xx(bool continue_before_upstream_com ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); + // This case tests sending on 100-Continue headers before the client has sent all the + // request data. if (continue_before_upstream_complete) { + upstream_request_->encode1xxHeaders( + Http::TestResponseHeaderMapImpl{{":status", initial_code.data()}}); if (with_multiple_1xx_headers) { upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "102"}}, false); upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); } - // This case tests sending on 100-Continue headers before the client has sent all the - // request data. - upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); - response->waitForContinueHeaders(); + response->waitFor1xxHeaders(); } // Send all of the request data and wait for it to be received upstream. codec_client_->sendData(*request_encoder_, 10, true); ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); + // This case tests forwarding 100-Continue after the client has sent all data. if (!continue_before_upstream_complete) { + upstream_request_->encode1xxHeaders( + Http::TestResponseHeaderMapImpl{{":status", initial_code.data()}}); if (with_multiple_1xx_headers) { upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "102"}}, false); upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); } - // This case tests forwarding 100-Continue after the client has sent all data. - upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); - response->waitForContinueHeaders(); + response->waitFor1xxHeaders(); } // Now send the rest of the response. upstream_request_->encodeHeaders(default_response_headers_, true); ASSERT_TRUE(response->waitForEndStream()); EXPECT_TRUE(response->complete()); ASSERT(response->informationalHeaders() != nullptr); - EXPECT_EQ("100", response->informationalHeaders()->getStatusValue()); + EXPECT_EQ(initial_code, response->informationalHeaders()->getStatusValue()); EXPECT_EQ("200", response->headers().getStatusValue()); } diff --git a/test/integration/http_integration.h b/test/integration/http_integration.h index 7bdc2b749e2e6..c7fd491026ab2 100644 --- a/test/integration/http_integration.h +++ b/test/integration/http_integration.h @@ -252,7 +252,8 @@ class HttpIntegrationTest : public BaseIntegrationTest { const std::string& via = "", bool disconnect_after_100 = false); void testEnvoyProxying1xx(bool continue_before_upstream_complete = false, bool with_encoder_filter = false, - bool with_multiple_1xx_headers = false); + bool with_multiple_1xx_headers = false, + absl::string_view initial_code = "100"); void simultaneousRequest(uint32_t request1_bytes, uint32_t request2_bytes, uint32_t response1_bytes, uint32_t response2_bytes); diff --git a/test/integration/integration_stream_decoder.cc b/test/integration/integration_stream_decoder.cc index 8bf5307a268bb..775641b8d0c73 100644 --- a/test/integration/integration_stream_decoder.cc +++ b/test/integration/integration_stream_decoder.cc @@ -24,7 +24,7 @@ namespace Envoy { IntegrationStreamDecoder::IntegrationStreamDecoder(Event::Dispatcher& dispatcher) : dispatcher_(dispatcher) {} -void IntegrationStreamDecoder::waitForContinueHeaders() { +void IntegrationStreamDecoder::waitFor1xxHeaders() { if (!continue_headers_.get()) { waiting_for_continue_headers_ = true; dispatcher_.run(Event::Dispatcher::RunType::Block); diff --git a/test/integration/integration_stream_decoder.h b/test/integration/integration_stream_decoder.h index dbdacf6930dbf..e4e5b3c85b207 100644 --- a/test/integration/integration_stream_decoder.h +++ b/test/integration/integration_stream_decoder.h @@ -35,7 +35,7 @@ class IntegrationStreamDecoder : public Http::ResponseDecoder, public Http::Stre const Http::MetadataMap& metadataMap() { return *metadata_map_; } uint64_t keyCount(std::string key) { return duplicated_metadata_key_count_[key]; } uint32_t metadataMapsDecodedCount() const { return metadata_maps_decoded_count_; } - void waitForContinueHeaders(); + void waitFor1xxHeaders(); void waitForHeaders(); // This function waits until body_ has at least size bytes in it (it might have more). clearBody() // can be used if the previous body data is not relevant and the test wants to wait for a specific diff --git a/test/integration/multiplexed_integration_test.cc b/test/integration/multiplexed_integration_test.cc index df5622fff917c..726d7c5e331a3 100644 --- a/test/integration/multiplexed_integration_test.cc +++ b/test/integration/multiplexed_integration_test.cc @@ -507,7 +507,7 @@ TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { waitForNextUpstreamRequest(); upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); - response->waitForContinueHeaders(); + response->waitFor1xxHeaders(); upstream_request_->encodeHeaders(default_response_headers_, false); upstream_request_->encodeData(100, true); diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 72c79fea27df5..c43ac822ee2bc 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -1341,6 +1341,14 @@ TEST_P(ProtocolIntegrationTest, EnvoyProxyingLateMultiple1xx) { testEnvoyProxying1xx(false, false, true); } +TEST_P(ProtocolIntegrationTest, EnvoyProxying102) { + testEnvoyProxying1xx(false, false, false, "102"); +} + +TEST_P(ProtocolIntegrationTest, EnvoyProxying103) { + testEnvoyProxying1xx(false, false, false, "103"); +} + TEST_P(ProtocolIntegrationTest, TwoRequests) { testTwoRequests(); } TEST_P(ProtocolIntegrationTest, TwoRequestsWithForcedBackup) { testTwoRequests(true); } From 1eb1841f9cacb1e500b1300dbd966325d9489afb Mon Sep 17 00:00:00 2001 From: danzh Date: Wed, 17 Nov 2021 16:07:58 -0500 Subject: [PATCH 103/110] quiche: update QUICHE to 4f552f349b8df000af24bc6cfa0b78fdc2467fef (#19013) * Update QUICHE from 0b75841d5 to 4f552f349 https://github.com/google/quiche/compare/0b75841d5..4f552f349 $ git log 0b75841d5..4f552f349 --date=short --no-merges --format="%ad %al %s" 2021-11-15 wub Deprecate --gfe2_reloadable_flag_quic_bbr_start_new_aggregation_epoch_after_a_full_round 2021-11-15 wub Deprecate --gfe2_reloadable_flag_quic_bbr2_check_cwnd_limited_before_aggregation_epoch. 2021-11-15 fayang Make QUIC_BUG of "Trying to start blackhole detection without no bytes in flight" server side only. 2021-11-15 bnc Deprecate --gfe2_reloadable_flag_quic_reject_invalid_chars_in_field_value. 2021-11-15 quiche-dev Consolidates half_closed_local tracking. This is preparation for a refactoring of stream close events. 2021-11-12 quiche-dev Fixes stream unregistration in OgHttp2Session::CloseStream(). 2021-11-12 quiche-dev Adds unit tests demonstrating bugs in existing oghttp2 handling of stream close events. 2021-11-12 wub Change QuicCryptoServerConfig::ParseSourceAddressToken from taking a 'SourceAddressTokens*' to a 'SourceAddressTokens&'. The function dereferences this pointer without checking for nullptr, changing it to reference prevents caller from passing in a nullptr. 2021-11-12 wub Add a regression test for b/206077990. 2021-11-12 quiche-dev Factors out `MaybeSendBufferedData()`, and moves the functionality into `SendQueuedFrames()`. 2021-11-12 fayang Set chrome_value false for gfe2_reloadable_flag_quic_add_cached_network_parameters_to_address_token. 2021-11-12 quiche-dev Add missing nullptr check. 2021-11-11 wub Add retry_token, resumption_attempted and early_data_attempted to quic::ParsedClientHello. 2021-11-11 quiche-dev Another small fix from debugging: only mark a request stream ready to write if it includes a body. 2021-11-11 quiche-dev Fixes frame length calculations to use the actual serialized length. 2021-11-10 dschinazi Add support for draft-ietf-quic-version-negotiation-05 2021-11-10 vasilvv Make sure WebTransport over HTTP/3 is enabled on the client even when SETTINGS_ENABLE_CONNECT_PROTOCOL is not present. 2021-11-09 dschinazi Refactor QUIC version downgrade prevention, part 2 2021-11-09 dschinazi Refactor QUIC version downgrade prevention 2021-11-09 dschinazi Fix a QuicConnection connection close log 2021-11-08 danzh Internal change 2021-11-08 fayang Internal change 2021-11-08 fayang In QUIC, do not check amplification limit if there is pending timer credit. This would guarantee CRYPTO frame be retransmitted because of 1) PTO fires 2) bundled with outgoing ACKs. 2021-11-05 fayang Set gfe2_reloadable_flag_quic_verify_request_headers_2 chrome_value to true. 2021-11-05 wub Allow QuicToyClient to provide a client certificate to the server, if requested. 2021-11-04 quiche-dev Automated g4 rollback of changelist 386316152. 2021-11-04 danzh Validate QUIC request/response headers against invalid token and disallowed headers. Add empty string to disallow-list. Split --gfe2_reloadable_flag_quic_verify_request_headers into 2 flags: --gfe2_reloadable_flag_quic_verify_request_headers_2 to validate QUIC request/response headers against invalid request with ratio monitoring; mark H2 request with empty string header as invalid earlier in H2 stack. --gfe2_reloadable_flag_quic_act_upon_invalid_header return error response upon any invalid QUIC request header. 2021-11-04 quiche-dev Call visitor_.OnInvalidFrame() for oghttp2 header errors. 2021-11-04 wub Add CachedNetworkParameters to address token for IETF QUIC, and - min_rtt received from a validated token will be used to set the initial rtt, if connection option 'TRTT' is set. - Enable bandwidth resumption for IETF QUIC when connection options BWRE or BWMX exists. 2021-11-03 renjietang Add connection option to trigger path degrading on 1 PTO. 2021-11-03 quiche-dev Add default return statements to switch statements to appease GCC. 2021-11-03 quiche-dev Wrap OgHttp2Session callbacks with a latched_error_ check. 2021-11-03 quiche-dev Add mock methods to MockSpdyFramerVisitor. 2021-11-02 quiche-dev Change Http2VisitorInterface::OnInvalidFrame() to accept an InvalidFrameError enum. 2021-11-02 vasilvv Perform header-based draft version negotiation in WebTransport over HTTP/3. 2021-11-02 haoyuewang Use absl::optional in place of a separate boolean and token on QuicConnection::PathState. 2021-11-02 dschinazi Internal change 2021-11-02 wub Add mTLS support for IETF QUIC. 2021-11-02 wub Internal change 2021-11-02 vasilvv Internal change 2021-11-02 haoyuewang Update quic::IsAppleMobile to quic::IsAppleClient for better coverage of Apple related QUIC reverse path validation crash: 1) Added coverage for iOS Youtube (from cr/406134264) & iMM (from Sherlog) user agents. 2) Added coverage for Mac traffic. Signed-off-by: Dan Zhang --- bazel/external/quiche.BUILD | 13 ++++++ bazel/external/quiche.patch | 42 +------------------ bazel/repository_locations.bzl | 6 +-- .../common/quic/envoy_quic_dispatcher_test.cc | 4 +- .../quic/envoy_quic_session_cache_test.cc | 7 ++-- 5 files changed, 23 insertions(+), 49 deletions(-) diff --git a/bazel/external/quiche.BUILD b/bazel/external/quiche.BUILD index 8341f4832ec7b..a738127058bc9 100644 --- a/bazel/external/quiche.BUILD +++ b/bazel/external/quiche.BUILD @@ -83,6 +83,18 @@ envoy_cc_test_library( deps = [":http2_platform"], ) +envoy_cc_library( + name = "http2_event_forwarder", + srcs = ["quiche/http2/adapter/event_forwarder.cc"], + hdrs = ["quiche/http2/adapter/event_forwarder.h"], + copts = quiche_copts, + repository = "@envoy", + deps = [ + ":quiche_common_platform_export", + ":spdy_core_http2_deframer_lib", + ], +) + envoy_cc_library( name = "http2_adapter", srcs = [ @@ -129,6 +141,7 @@ envoy_cc_library( deps = [ ":http2_core_http2_trace_logging_lib", ":http2_core_priority_write_scheduler_lib", + ":http2_event_forwarder", ":spdy_core_framer_lib", ":spdy_core_header_block_lib", ":spdy_core_http2_deframer_lib", diff --git a/bazel/external/quiche.patch b/bazel/external/quiche.patch index 9a07b463b2aeb..8d9666a9adeb4 100644 --- a/bazel/external/quiche.patch +++ b/bazel/external/quiche.patch @@ -67,6 +67,7 @@ #include "absl/types/span.h" #include "http2/adapter/data_source.h" #include "http2/adapter/http2_protocol.h" + #include "http2/adapter/http2_visitor_interface.h" + +// Required to build on Windows. +typedef ptrdiff_t ssize_t; @@ -74,24 +75,6 @@ #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" #include "spdy/core/spdy_header_block.h" ---- http2/adapter/oghttp2_session.cc -+++ http2/adapter/oghttp2_session.cc -@@ -131,6 +131,7 @@ absl::string_view TracePerspectiveAsStri - case Perspective::kServer: - return "OGHTTP2_SERVER"; - } -+ return "OGHTTP2_SERVER"; - } - - class RunOnExit { -@@ -186,6 +187,7 @@ Http2ErrorCode GetHttp2ErrorCode(SpdyFra - case SpdyFramerError::LAST_ERROR: - return Http2ErrorCode::INTERNAL_ERROR; - } -+ return Http2ErrorCode::INTERNAL_ERROR; - } - - } // namespace --- http2/adapter/http2_util.cc +++ http2/adapter/http2_util.cc @@ -1,5 +1,7 @@ @@ -102,30 +85,7 @@ namespace http2 { namespace adapter { namespace { -@@ -42,6 +44,7 @@ spdy::SpdyErrorCode TranslateErrorCode(H - case Http2ErrorCode::HTTP_1_1_REQUIRED: - return spdy::ERROR_CODE_HTTP_1_1_REQUIRED; - } -+ return spdy::ERROR_CODE_INTERNAL_ERROR; - } - - Http2ErrorCode TranslateErrorCode(spdy::SpdyErrorCode code) { -@@ -76,6 +77,7 @@ Http2ErrorCode TranslateErrorCode(spdy:: - case spdy::ERROR_CODE_HTTP_1_1_REQUIRED: - return Http2ErrorCode::HTTP_1_1_REQUIRED; - } -+ return Http2ErrorCode::INTERNAL_ERROR; - } - absl::string_view ConnectionErrorToString(ConnectionError error) { -@@ -95,6 +96,7 @@ absl::string_view ConnectionErrorToStrin - case ConnectionError::kInvalidPushPromise: - return "InvalidPushPromise"; - } -+ return "UnknownConnectionError"; - } - - } // namespace adapter --- http2/adapter/oghttp2_session.cc +++ http2/adapter/oghttp2_session.cc @@ -11,6 +11,8 @@ diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 0e8c2e5ec66bd..b2ed497e3a18a 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -838,12 +838,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "QUICHE", project_desc = "QUICHE (QUIC, HTTP/2, Etc) is Google‘s implementation of QUIC and related protocols", project_url = "https://github.com/google/quiche", - version = "0b75841d5b5844c53f4399a41d64de7250c204d8", - sha256 = "a00b0671180fc79952baf754148e65364bfca9d35b988710594752fb7f9bf6a1", + version = "4f552f349b8df000af24bc6cfa0b78fdc2467fef", + sha256 = "355c80803698d2a44363ed304d250059cc5d7712281481803717f8d779229bd8", urls = ["https://github.com/google/quiche/archive/{version}.tar.gz"], strip_prefix = "quiche-{version}", use_category = ["dataplane_core"], - release_date = "2021-11-02", + release_date = "2021-11-15", cpe = "N/A", ), com_googlesource_googleurl = dict( diff --git a/test/common/quic/envoy_quic_dispatcher_test.cc b/test/common/quic/envoy_quic_dispatcher_test.cc index 7e7e4fe0e49fa..3481ebc731fa8 100644 --- a/test/common/quic/envoy_quic_dispatcher_test.cc +++ b/test/common/quic/envoy_quic_dispatcher_test.cc @@ -160,7 +160,7 @@ class EnvoyQuicDispatcherTest : public testing::TestWithParamIsEncryptionEstablished()); EXPECT_EQ(1u, connection_handler_.numConnections()); auto envoy_connection = static_cast(session); - EXPECT_EQ("test.example.org", envoy_connection->requestedServerName()); + EXPECT_EQ("test.example.com", envoy_connection->requestedServerName()); EXPECT_EQ(peer_addr, envoyIpAddressToQuicSocketAddress( envoy_connection->connectionInfoProvider().remoteAddress()->ip())); ASSERT(envoy_connection->connectionInfoProvider().localAddress() != nullptr); @@ -190,7 +190,7 @@ class EnvoyQuicDispatcherTest : public testing::TestWithParamfilterChain(); })); Network::MockTransportSocketFactory transport_socket_factory; diff --git a/test/common/quic/envoy_quic_session_cache_test.cc b/test/common/quic/envoy_quic_session_cache_test.cc index 5490b0d491afa..c7601331597fb 100644 --- a/test/common/quic/envoy_quic_session_cache_test.cc +++ b/test/common/quic/envoy_quic_session_cache_test.cc @@ -31,9 +31,10 @@ std::vector createFakeStatelessResetToken() { std::unique_ptr makeFakeTransportParams() { auto params = std::make_unique(); params->perspective = quic::Perspective::IS_CLIENT; - params->version = FakeVersionLabel; - params->supported_versions.push_back(FakeVersionLabel); - params->supported_versions.push_back(FakeVersionLabel2); + params->legacy_version_information = quic::TransportParameters::LegacyVersionInformation(); + params->legacy_version_information->version = FakeVersionLabel; + params->legacy_version_information->supported_versions.push_back(FakeVersionLabel); + params->legacy_version_information->supported_versions.push_back(FakeVersionLabel2); params->max_idle_timeout_ms.set_value(FakeIdleTimeoutMilliseconds); params->stateless_reset_token = createFakeStatelessResetToken(); params->max_udp_payload_size.set_value(FakeMaxPacketSize); From bd87e8c769b0741ca5ee5d63ae6e4af24e1e1c85 Mon Sep 17 00:00:00 2001 From: Douglas Reid Date: Wed, 17 Nov 2021 16:22:31 -0800 Subject: [PATCH 104/110] stats(prometheus): escape problematic chars in text serialization (#18978) This PR attempts to address #18589 and istio/istio#35575 by adding escaping logic to the prometheus stats code. An attempt was made to mirror the logic in the prometheus common golang code for text serialization. Fixes #18589 Signed-off-by: Douglas Reid --- source/server/admin/prometheus_stats.cc | 19 ++++++++++++++++++- test/server/admin/prometheus_stats_test.cc | 6 +++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/source/server/admin/prometheus_stats.cc b/source/server/admin/prometheus_stats.cc index a3d0c7657335a..b1cf89936c9ba 100644 --- a/source/server/admin/prometheus_stats.cc +++ b/source/server/admin/prometheus_stats.cc @@ -6,6 +6,7 @@ #include "source/common/stats/histogram_impl.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_replace.h" namespace Envoy { namespace Server { @@ -26,6 +27,22 @@ std::string sanitizeName(const absl::string_view name) { return promRegex().replaceAll(name, "_"); } +/** + * Take tag values and sanitize it for text serialization, according to + * Prometheus conventions. + */ +std::string sanitizeValue(const absl::string_view value) { + // Removes problematic characters from Prometheus tag values to prevent + // text serialization issues. This matches the prometheus text formatting code: + // https://github.com/prometheus/common/blob/88f1636b699ae4fb949d292ffb904c205bf542c9/expfmt/text_create.go#L419-L420. + // The goal is to replace '\' with "\\", newline with "\n", and '"' with "\"". + return absl::StrReplaceAll(value, { + {R"(\)", R"(\\)"}, + {"\n", R"(\n)"}, + {R"(")", R"(\")"}, + }); +} + /* * Determine whether a metric has never been emitted and choose to * not show it if we only wanted used metrics. @@ -188,7 +205,7 @@ std::string PrometheusStatsFormatter::formattedTags(const std::vector buf; buf.reserve(tags.size()); for (const Stats::Tag& tag : tags) { - buf.push_back(fmt::format("{}=\"{}\"", sanitizeName(tag.name_), tag.value_)); + buf.push_back(fmt::format("{}=\"{}\"", sanitizeName(tag.name_), sanitizeValue(tag.value_))); } return absl::StrJoin(buf, ","); } diff --git a/test/server/admin/prometheus_stats_test.cc b/test/server/admin/prometheus_stats_test.cc index 0c86261c5983e..2fb19f94e417b 100644 --- a/test/server/admin/prometheus_stats_test.cc +++ b/test/server/admin/prometheus_stats_test.cc @@ -148,9 +148,13 @@ TEST_F(PrometheusStatsFormatterTest, FormattedTags) { std::vector tags; Stats::Tag tag1 = {"a.tag-name", "a.tag-value"}; Stats::Tag tag2 = {"another_tag_name", "another_tag-value"}; + Stats::Tag tag3 = {"replace_problematic", R"(val"ue with\ some + issues)"}; tags.push_back(tag1); tags.push_back(tag2); - std::string expected = "a_tag_name=\"a.tag-value\",another_tag_name=\"another_tag-value\""; + tags.push_back(tag3); + std::string expected = "a_tag_name=\"a.tag-value\",another_tag_name=\"another_tag-value\"," + "replace_problematic=\"val\\\"ue with\\\\ some\\n issues\""; auto actual = PrometheusStatsFormatter::formattedTags(tags); EXPECT_EQ(expected, actual); } From aac4631c26745de1a39556abc68bd1d3ed95aa2f Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Wed, 17 Nov 2021 16:47:21 -0800 Subject: [PATCH 105/110] quic: Fix QuicFilterManagerConnectionImpl::ConnectionInfoProviderSharedPtr (#19008) If QuicFilterManagerConnectionImpl::ConnectionInfoProviderSharedPtr is called after the connection is closed, return nullptr. Risk Level: Low Testing: New unit tests Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A Signed-off-by: Ryan Hamilton --- .../quic_filter_manager_connection_impl.h | 5 ++ test/common/quic/BUILD | 14 ++++ ...uic_filter_manager_connection_impl_test.cc | 67 +++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 test/common/quic/quic_filter_manager_connection_impl_test.cc diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index 54eef33d7a236..7a613e4f6b620 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -61,9 +61,14 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, void detectEarlyCloseWhenReadDisabled(bool /*value*/) override { ASSERT(false); } bool readEnabled() const override { return true; } const Network::ConnectionInfoSetter& connectionInfoProvider() const override { + ENVOY_BUG(network_connection_ && network_connection_->connectionSocket(), + "No connection socket."); return network_connection_->connectionSocket()->connectionInfoProvider(); } Network::ConnectionInfoProviderSharedPtr connectionInfoProviderSharedPtr() const override { + if (!network_connection_ || !network_connection_->connectionSocket()) { + return nullptr; + } return network_connection_->connectionSocket()->connectionInfoProviderSharedPtr(); } absl::optional diff --git a/test/common/quic/BUILD b/test/common/quic/BUILD index 4c16057ecbba6..604eaf1cd8f24 100644 --- a/test/common/quic/BUILD +++ b/test/common/quic/BUILD @@ -54,6 +54,20 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "quic_filter_manager_connection_impl_test", + srcs = ["quic_filter_manager_connection_impl_test.cc"], + tags = ["nofips"], + deps = [ + "//source/common/quic:quic_filter_manager_connection_lib", + "//test/common/quic:quic_test_utils_for_envoy_lib", + "//test/mocks/event:event_mocks", + "//test/mocks/network:network_mocks", + "//test/test_common:utility_lib", + "@com_github_google_quiche//:quic_test_tools_test_utils_lib", + ], +) + envoy_cc_test( name = "quic_stat_names_test", srcs = ["quic_stat_names_test.cc"], diff --git a/test/common/quic/quic_filter_manager_connection_impl_test.cc b/test/common/quic/quic_filter_manager_connection_impl_test.cc new file mode 100644 index 0000000000000..aa6352b06b295 --- /dev/null +++ b/test/common/quic/quic_filter_manager_connection_impl_test.cc @@ -0,0 +1,67 @@ +#include "source/common/quic/quic_filter_manager_connection_impl.h" + +#include "test/mocks/event/mocks.h" +#include "test/mocks/network/mocks.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "quiche/quic/core/quic_connection.h" +#include "quiche/quic/test_tools/quic_test_utils.h" + +namespace Envoy { +namespace Quic { + +class TestQuicFilterManagerConnectionImpl : public QuicFilterManagerConnectionImpl { +public: + TestQuicFilterManagerConnectionImpl(QuicNetworkConnection& connection, + const quic::QuicConnectionId& connection_id, + Event::Dispatcher& dispatcher, uint32_t send_buffer_limit, + std::shared_ptr&& ssl_info) + : QuicFilterManagerConnectionImpl(connection, connection_id, dispatcher, send_buffer_limit, + std::move(ssl_info)) {} + + void dumpState(std::ostream& /*os*/, int /*indent_level = 0*/) const override {} + absl::string_view requestedServerName() const override { return {}; } + bool hasDataToWrite() override { return false; } + const quic::QuicConnection* quicConnection() const override { return nullptr; } + quic::QuicConnection* quicConnection() override { return nullptr; } + + void closeNow() { + const quic::QuicConnectionCloseFrame frame; + quic::ConnectionCloseSource source = quic::ConnectionCloseSource::FROM_SELF; + const quic::ParsedQuicVersion version = quic::AllSupportedVersions().front(); + onConnectionCloseEvent(frame, source, version); + } +}; + +class QuicFilterManagerConnectionImplTest : public ::testing::Test { +public: + QuicFilterManagerConnectionImplTest() + : socket_(std::make_unique>()), + connection_(std::move(socket_)), + quic_session_(new quic::test::MockQuicConnection(&helper_, &alarm_factory_, + quic::Perspective::IS_SERVER)), + ssl_info_(std::make_shared(quic_session_)), + impl_(connection_, connection_id_, dispatcher_, send_buffer_limit_, std::move(ssl_info_)) {} + +protected: + std::unique_ptr> socket_; + const quic::QuicConnectionId connection_id_; + Event::MockDispatcher dispatcher_; + uint32_t send_buffer_limit_ = 0; + quic::test::MockQuicConnectionHelper helper_; + quic::test::MockAlarmFactory alarm_factory_; + QuicNetworkConnection connection_; + quic::test::MockQuicSession quic_session_; + std::shared_ptr ssl_info_; + TestQuicFilterManagerConnectionImpl impl_; +}; + +TEST_F(QuicFilterManagerConnectionImplTest, ConnectionInfoProviderSharedPtr) { + EXPECT_TRUE(impl_.connectionInfoProviderSharedPtr() != nullptr); + impl_.closeNow(); + EXPECT_TRUE(impl_.connectionInfoProviderSharedPtr() == nullptr); +} + +} // namespace Quic +} // namespace Envoy From 7366317fa3d447ebe49cb4cb3de95190e5122e3c Mon Sep 17 00:00:00 2001 From: Dmitry Rozhkov Date: Thu, 18 Nov 2021 15:55:17 +0200 Subject: [PATCH 106/110] ci: disable multplexed integration test for coverage temporarily (#19041) Signed-off-by: Dmitry Rozhkov --- test/integration/multiplexed_integration_test.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/integration/multiplexed_integration_test.cc b/test/integration/multiplexed_integration_test.cc index 726d7c5e331a3..b1599264761f2 100644 --- a/test/integration/multiplexed_integration_test.cc +++ b/test/integration/multiplexed_integration_test.cc @@ -412,6 +412,8 @@ TEST_P(Http2MetadataIntegrationTest, ProxyMultipleMetadata) { EXPECT_EQ(response->metadataMap().size(), multiple_vecs.size()); } +// Disabled temporarily see #19040 +#if 0 TEST_P(Http2MetadataIntegrationTest, ProxyInvalidMetadata) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -440,6 +442,7 @@ TEST_P(Http2MetadataIntegrationTest, ProxyInvalidMetadata) { EXPECT_EQ(0, response->metadataMapsDecodedCount()); EXPECT_EQ(response->metadataMap().size(), 0); } +#endif void verifyExpectedMetadata(Http::MetadataMap metadata_map, std::set keys) { for (const auto& key : keys) { From ae56bb42102df22401c5c0df9d3f8341b18acdfc Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Thu, 18 Nov 2021 10:53:56 -0500 Subject: [PATCH 107/110] test: fixing a reverse bridge teardown flake (#19043) Signed-off-by: Alyssa Wilk --- .../reverse_bridge_integration_test.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/extensions/filters/http/grpc_http1_reverse_bridge/reverse_bridge_integration_test.cc b/test/extensions/filters/http/grpc_http1_reverse_bridge/reverse_bridge_integration_test.cc index 9a05608688b5d..029d1153477ce 100644 --- a/test/extensions/filters/http/grpc_http1_reverse_bridge/reverse_bridge_integration_test.cc +++ b/test/extensions/filters/http/grpc_http1_reverse_bridge/reverse_bridge_integration_test.cc @@ -56,8 +56,6 @@ name: grpc_http1_reverse_bridge HttpIntegrationTest::initialize(); } - void TearDown() override { fake_upstream_connection_.reset(); } - protected: Http::CodecType upstream_protocol_; }; From 5d1ed3f9e2e83cd4a773b6f60058c8c1960e6138 Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Thu, 18 Nov 2021 21:28:40 +0000 Subject: [PATCH 108/110] Remove envoy.reloadable_features.upstream_http2_flood_checks runtime override and legacy code paths (#19016) Remove envoy.reloadable_features.upstream_http2_flood_checks runtime override and legacy code paths. Risk Level: Low Testing: Unit Tests Docs Changes: proto comments updated Release Notes: Yes Platform Specific Features: N/A Fixes #18449 Signed-off-by: Yan Avlasov --- api/envoy/config/core/v3/protocol.proto | 10 --- docs/root/version_history/current.rst | 1 + source/common/http/http2/codec_impl.cc | 48 +++++--------- source/common/http/http2/codec_impl.h | 12 ---- source/common/runtime/runtime_features.cc | 1 - .../http2_flood_integration_test.cc | 66 +++++-------------- tools/code_format/check_format.py | 1 - 7 files changed, 32 insertions(+), 107 deletions(-) diff --git a/api/envoy/config/core/v3/protocol.proto b/api/envoy/config/core/v3/protocol.proto index 89f488130969a..70d6f3ad39afd 100644 --- a/api/envoy/config/core/v3/protocol.proto +++ b/api/envoy/config/core/v3/protocol.proto @@ -408,8 +408,6 @@ message Http2ProtocolOptions { // be written into the socket). Exceeding this limit triggers flood mitigation and connection is // terminated. The ``http2.outbound_flood`` stat tracks the number of terminated connections due // to flood mitigation. The default limit is 10000. - // NOTE: flood and abuse mitigation for upstream connections is presently enabled by the - // `envoy.reloadable_features.upstream_http2_flood_checks` flag. google.protobuf.UInt32Value max_outbound_frames = 7 [(validate.rules).uint32 = {gte: 1}]; // Limit the number of pending outbound downstream frames of types PING, SETTINGS and RST_STREAM, @@ -417,8 +415,6 @@ message Http2ProtocolOptions { // this limit triggers flood mitigation and connection is terminated. The // ``http2.outbound_control_flood`` stat tracks the number of terminated connections due to flood // mitigation. The default limit is 1000. - // NOTE: flood and abuse mitigation for upstream connections is presently enabled by the - // `envoy.reloadable_features.upstream_http2_flood_checks` flag. google.protobuf.UInt32Value max_outbound_control_frames = 8 [(validate.rules).uint32 = {gte: 1}]; // Limit the number of consecutive inbound frames of types HEADERS, CONTINUATION and DATA with an @@ -427,8 +423,6 @@ message Http2ProtocolOptions { // stat tracks the number of connections terminated due to flood mitigation. // Setting this to 0 will terminate connection upon receiving first frame with an empty payload // and no end stream flag. The default limit is 1. - // NOTE: flood and abuse mitigation for upstream connections is presently enabled by the - // `envoy.reloadable_features.upstream_http2_flood_checks` flag. google.protobuf.UInt32Value max_consecutive_inbound_frames_with_empty_payload = 9; // Limit the number of inbound PRIORITY frames allowed per each opened stream. If the number @@ -442,8 +436,6 @@ message Http2ProtocolOptions { // `opened_streams` is incremented when Envoy send the HEADERS frame for a new stream. The // ``http2.inbound_priority_frames_flood`` stat tracks // the number of connections terminated due to flood mitigation. The default limit is 100. - // NOTE: flood and abuse mitigation for upstream connections is presently enabled by the - // `envoy.reloadable_features.upstream_http2_flood_checks` flag. google.protobuf.UInt32Value max_inbound_priority_frames_per_stream = 10; // Limit the number of inbound WINDOW_UPDATE frames allowed per DATA frame sent. If the number @@ -460,8 +452,6 @@ message Http2ProtocolOptions { // flood mitigation. The default max_inbound_window_update_frames_per_data_frame_sent value is 10. // Setting this to 1 should be enough to support HTTP/2 implementations with basic flow control, // but more complex implementations that try to estimate available bandwidth require at least 2. - // NOTE: flood and abuse mitigation for upstream connections is presently enabled by the - // `envoy.reloadable_features.upstream_http2_flood_checks` flag. google.protobuf.UInt32Value max_inbound_window_update_frames_per_data_frame_sent = 11 [(validate.rules).uint32 = {gte: 1}]; diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 0da2fd5d1f46c..b9b9cbbf47171 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -47,6 +47,7 @@ Removed Config or Runtime * http: removed ``envoy.reloadable_features.return_502_for_upstream_protocol_errors``. Envoy will always return 502 code upon encountering upstream protocol error. * http: removed ``envoy.reloadable_features.treat_host_like_authority`` and legacy code paths. * http: removed ``envoy.reloadable_features.treat_upstream_connect_timeout_as_connect_failure`` and legacy code paths. +* http: removed ``envoy.reloadable_features.upstream_http2_flood_checks`` and legacy code paths. * upstream: removed ``envoy.reloadable_features.upstream_host_weight_change_causes_rebuild`` and legacy code paths. New Features diff --git a/source/common/http/http2/codec_impl.cc b/source/common/http/http2/codec_impl.cc index 694cbb7bc27ca..2193059f02d31 100644 --- a/source/common/http/http2/codec_impl.cc +++ b/source/common/http/http2/codec_impl.cc @@ -1210,7 +1210,8 @@ void ConnectionImpl::addOutboundFrameFragment(Buffer::OwnedImpl& output, const u // onBeforeFrameSend callback is not called for DATA frames. bool is_outbound_flood_monitored_control_frame = false; std::swap(is_outbound_flood_monitored_control_frame, is_outbound_flood_monitored_control_frame_); - auto releasor = trackOutboundFrames(is_outbound_flood_monitored_control_frame); + auto releasor = + protocol_constraints_.incrementOutboundFrameCount(is_outbound_flood_monitored_control_frame); output.add(data, length); output.addDrainTracker(releasor); } @@ -1864,8 +1865,7 @@ ClientConnectionImpl::ClientConnectionImpl( Nghttp2SessionFactory& http2_session_factory) : ConnectionImpl(connection, stats, random_generator, http2_options, max_response_headers_kb, max_response_headers_count), - callbacks_(callbacks), enable_upstream_http2_flood_checks_(Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.upstream_http2_flood_checks")) { + callbacks_(callbacks) { ClientHttp2Options client_http2_options(http2_options); if (use_new_codec_wrapper_) { adapter_ = http2_session_factory.create(http2_callbacks_.callbacks(), base(), @@ -1932,36 +1932,24 @@ int ClientConnectionImpl::onHeader(const nghttp2_frame* frame, HeaderString&& na Status ClientConnectionImpl::trackInboundFrames(const nghttp2_frame_hd* hd, uint32_t padding_length) { Status result; - if (enable_upstream_http2_flood_checks_) { - ENVOY_CONN_LOG(trace, "track inbound frame type={} flags={} length={} padding_length={}", - connection_, static_cast(hd->type), static_cast(hd->flags), - static_cast(hd->length), padding_length); - - result = protocol_constraints_.trackInboundFrames(hd, padding_length); - if (!result.ok()) { - ENVOY_CONN_LOG(trace, "error reading frame: {} received in this HTTP/2 session.", connection_, - result.message()); - if (isInboundFramesWithEmptyPayloadError(result)) { - ConnectionImpl::StreamImpl* stream = getStream(hd->stream_id); - if (stream) { - stream->setDetails(Http2ResponseCodeDetails::get().inbound_empty_frame_flood); - } + ENVOY_CONN_LOG(trace, "track inbound frame type={} flags={} length={} padding_length={}", + connection_, static_cast(hd->type), static_cast(hd->flags), + static_cast(hd->length), padding_length); + + result = protocol_constraints_.trackInboundFrames(hd, padding_length); + if (!result.ok()) { + ENVOY_CONN_LOG(trace, "error reading frame: {} received in this HTTP/2 session.", connection_, + result.message()); + if (isInboundFramesWithEmptyPayloadError(result)) { + ConnectionImpl::StreamImpl* stream = getStream(hd->stream_id); + if (stream) { + stream->setDetails(Http2ResponseCodeDetails::get().inbound_empty_frame_flood); } } } return result; } -// TODO(yanavlasov): move to the base class once the runtime flag is removed. -ProtocolConstraints::ReleasorProc -ClientConnectionImpl::trackOutboundFrames(bool is_outbound_flood_monitored_control_frame) { - if (enable_upstream_http2_flood_checks_) { - return protocol_constraints_.incrementOutboundFrameCount( - is_outbound_flood_monitored_control_frame); - } - return ProtocolConstraints::ReleasorProc([]() {}); -} - StreamResetReason ClientConnectionImpl::getMessagingErrorResetReason() const { connection_.streamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamProtocolError); @@ -2053,12 +2041,6 @@ Status ServerConnectionImpl::trackInboundFrames(const nghttp2_frame_hd* hd, return result; } -ProtocolConstraints::ReleasorProc -ServerConnectionImpl::trackOutboundFrames(bool is_outbound_flood_monitored_control_frame) { - return protocol_constraints_.incrementOutboundFrameCount( - is_outbound_flood_monitored_control_frame); -} - Http::Status ServerConnectionImpl::dispatch(Buffer::Instance& data) { // Make sure downstream outbound queue was not flooded by the upstream frames. RETURN_IF_ERROR(protocol_constraints_.checkOutboundFrameLimits()); diff --git a/source/common/http/http2/codec_impl.h b/source/common/http/http2/codec_impl.h index bf750fc543e5b..4f775f2cb7b4b 100644 --- a/source/common/http/http2/codec_impl.h +++ b/source/common/http/http2/codec_impl.h @@ -601,8 +601,6 @@ class ConnectionImpl : public virtual Connection, // Adds buffer fragment for a new outbound frame to the supplied Buffer::OwnedImpl. void addOutboundFrameFragment(Buffer::OwnedImpl& output, const uint8_t* data, size_t length); - virtual ProtocolConstraints::ReleasorProc - trackOutboundFrames(bool is_outbound_flood_monitored_control_frame) PURE; virtual Status trackInboundFrames(const nghttp2_frame_hd* hd, uint32_t padding_length) PURE; void onKeepaliveResponse(); void onKeepaliveResponseTimeout(); @@ -649,19 +647,11 @@ class ClientConnectionImpl : public ClientConnection, public ConnectionImpl { ConnectionCallbacks& callbacks() override { return callbacks_; } Status onBeginHeaders(const nghttp2_frame* frame) override; int onHeader(const nghttp2_frame* frame, HeaderString&& name, HeaderString&& value) override; - - // Tracking of frames for flood and abuse mitigation for upstream connections is presently enabled - // by the `envoy.reloadable_features.upstream_http2_flood_checks` flag. - // TODO(yanavlasov): move to the base class once the runtime flag is removed. - ProtocolConstraints::ReleasorProc trackOutboundFrames(bool) override; Status trackInboundFrames(const nghttp2_frame_hd*, uint32_t) override; - void dumpStreams(std::ostream& os, int indent_level) const override; StreamResetReason getMessagingErrorResetReason() const override; Http::ConnectionCallbacks& callbacks_; std::chrono::milliseconds idle_session_requires_ping_interval_; - // Latched value of "envoy.reloadable_features.upstream_http2_flood_checks" runtime feature. - bool enable_upstream_http2_flood_checks_; }; /** @@ -682,8 +672,6 @@ class ServerConnectionImpl : public ServerConnection, public ConnectionImpl { ConnectionCallbacks& callbacks() override { return callbacks_; } Status onBeginHeaders(const nghttp2_frame* frame) override; int onHeader(const nghttp2_frame* frame, HeaderString&& name, HeaderString&& value) override; - ProtocolConstraints::ReleasorProc - trackOutboundFrames(bool is_outbound_flood_monitored_control_frame) override; Status trackInboundFrames(const nghttp2_frame_hd* hd, uint32_t padding_length) override; absl::optional checkHeaderNameForUnderscores(absl::string_view header_name) override; StreamResetReason getMessagingErrorResetReason() const override { diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index cd5829e7779b9..c2559c63db5be 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -88,7 +88,6 @@ constexpr const char* runtime_features[] = { "envoy.reloadable_features.use_observable_cluster_name", "envoy.reloadable_features.validate_connect", "envoy.reloadable_features.vhds_heartbeats", - "envoy.reloadable_features.upstream_http2_flood_checks", "envoy.restart_features.explicit_wildcard_resource", "envoy.restart_features.use_apple_api_for_dns_lookups", // Misplaced flags: please do not add flags to this section. diff --git a/test/integration/http2_flood_integration_test.cc b/test/integration/http2_flood_integration_test.cc index f24d631531773..848ad5b9964f4 100644 --- a/test/integration/http2_flood_integration_test.cc +++ b/test/integration/http2_flood_integration_test.cc @@ -60,7 +60,7 @@ class Http2FloodMitigationTest } protected: - bool initializeUpstreamFloodTest(); + void initializeUpstreamFloodTest(); std::vector serializeFrames(const Http2Frame& frame, uint32_t num_frames); void floodServer(const Http2Frame& frame, const std::string& flood_stat, uint32_t num_frames); void floodServer(absl::string_view host, absl::string_view path, @@ -81,13 +81,12 @@ INSTANTIATE_TEST_SUITE_P( testing::ValuesIn({false, true})), testParamsToString); -bool Http2FloodMitigationTest::initializeUpstreamFloodTest() { +void Http2FloodMitigationTest::initializeUpstreamFloodTest() { setDownstreamProtocol(Http::CodecType::HTTP2); setUpstreamProtocol(Http::CodecType::HTTP2); // set lower upstream outbound frame limits to make tests run faster config_helper_.setUpstreamOutboundFramesLimits(AllFrameFloodLimit, ControlFrameFloodLimit); initialize(); - return true; } void Http2FloodMitigationTest::setNetworkConnectionBufferSize() { @@ -1139,28 +1138,19 @@ TEST_P(Http2FloodMitigationTest, ZerolenHeaderAllowed) { } TEST_P(Http2FloodMitigationTest, UpstreamPingFlood) { - if (!initializeUpstreamFloodTest()) { - return; - } - + initializeUpstreamFloodTest(); floodClient(Http2Frame::makePingFrame(), ControlFrameFloodLimit + 1, "cluster.cluster_0.http2.outbound_control_flood"); } TEST_P(Http2FloodMitigationTest, UpstreamSettings) { - if (!initializeUpstreamFloodTest()) { - return; - } - + initializeUpstreamFloodTest(); floodClient(Http2Frame::makeEmptySettingsFrame(), ControlFrameFloodLimit + 1, "cluster.cluster_0.http2.outbound_control_flood"); } TEST_P(Http2FloodMitigationTest, UpstreamWindowUpdate) { - if (!initializeUpstreamFloodTest()) { - return; - } - + initializeUpstreamFloodTest(); constexpr uint32_t max_allowed = 5 + 2 * (1 + Http2::Utility::OptionsLimits:: DEFAULT_MAX_INBOUND_WINDOW_UPDATE_FRAMES_PER_DATA_FRAME_SENT * @@ -1181,9 +1171,7 @@ TEST_P(Http2FloodMitigationTest, UpstreamEmptyHeaders) { ->set_value(0); ConfigHelper::setProtocolOptions(*cluster, protocol_options); }); - if (!initializeUpstreamFloodTest()) { - return; - } + initializeUpstreamFloodTest(); codec_client_ = makeHttpConnection(lookupPort("http")); auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); @@ -1202,10 +1190,7 @@ TEST_P(Http2FloodMitigationTest, UpstreamEmptyHeaders) { // Verify that the HTTP/2 connection is terminated upon receiving invalid HEADERS frame. TEST_P(Http2FloodMitigationTest, UpstreamZerolenHeader) { - if (!initializeUpstreamFloodTest()) { - return; - } - + initializeUpstreamFloodTest(); // Send client request which will send an upstream request. codec_client_ = makeHttpConnection(lookupPort("http")); auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); @@ -1238,9 +1223,7 @@ TEST_P(Http2FloodMitigationTest, UpstreamZerolenHeaderAllowed) { ConfigHelper::setProtocolOptions(*bootstrap.mutable_static_resources()->mutable_clusters(0), protocol_options); }); - if (!initializeUpstreamFloodTest()) { - return; - } + initializeUpstreamFloodTest(); // Send client request which will send an upstream request. codec_client_ = makeHttpConnection(lookupPort("http")); @@ -1280,9 +1263,7 @@ TEST_P(Http2FloodMitigationTest, UpstreamZerolenHeaderAllowed) { } TEST_P(Http2FloodMitigationTest, UpstreamEmptyData) { - if (!initializeUpstreamFloodTest()) { - return; - } + initializeUpstreamFloodTest(); // Send client request which will send an upstream request. codec_client_ = makeHttpConnection(lookupPort("http")); @@ -1309,9 +1290,7 @@ TEST_P(Http2FloodMitigationTest, UpstreamEmptyData) { } TEST_P(Http2FloodMitigationTest, UpstreamEmptyHeadersContinuation) { - if (!initializeUpstreamFloodTest()) { - return; - } + initializeUpstreamFloodTest(); codec_client_ = makeHttpConnection(lookupPort("http")); auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); @@ -1334,10 +1313,7 @@ TEST_P(Http2FloodMitigationTest, UpstreamEmptyHeadersContinuation) { } TEST_P(Http2FloodMitigationTest, UpstreamPriorityNoOpenStreams) { - if (!initializeUpstreamFloodTest()) { - return; - } - + initializeUpstreamFloodTest(); floodClient(Http2Frame::makePriorityFrame(Http2Frame::makeClientStreamId(1), Http2Frame::makeClientStreamId(2)), Http2::Utility::OptionsLimits::DEFAULT_MAX_INBOUND_PRIORITY_FRAMES_PER_STREAM * 2 + 1, @@ -1345,9 +1321,7 @@ TEST_P(Http2FloodMitigationTest, UpstreamPriorityNoOpenStreams) { } TEST_P(Http2FloodMitigationTest, UpstreamPriorityOneOpenStream) { - if (!initializeUpstreamFloodTest()) { - return; - } + initializeUpstreamFloodTest(); codec_client_ = makeHttpConnection(lookupPort("http")); auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); waitForNextUpstreamRequest(); @@ -1375,9 +1349,7 @@ TEST_P(Http2FloodMitigationTest, UpstreamPriorityOneOpenStream) { // Verify that protocol constraint tracker correctly applies limits to the CLOSED streams as well. TEST_P(Http2FloodMitigationTest, UpstreamPriorityOneClosedStream) { - if (!initializeUpstreamFloodTest()) { - return; - } + initializeUpstreamFloodTest(); codec_client_ = makeHttpConnection(lookupPort("http")); auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); @@ -1416,9 +1388,7 @@ TEST_P(Http2FloodMitigationTest, UpstreamRstStreamOnStreamIdleTimeout) { auto seconds = std::chrono::duration_cast(timeout); stream_idle_timeout->set_seconds(seconds.count()); }); - if (!initializeUpstreamFloodTest()) { - return; - } + initializeUpstreamFloodTest(); // pre-fill upstream connection 1 away from overflow auto response = prefillOutboundUpstreamQueue(ControlFrameFloodLimit); @@ -1435,9 +1405,7 @@ TEST_P(Http2FloodMitigationTest, UpstreamRstStreamOnStreamIdleTimeout) { // Verify that the server can detect flooding by the RST_STREAM sent to upstream when downstream // disconnects. TEST_P(Http2FloodMitigationTest, UpstreamRstStreamOnDownstreamRemoteClose) { - if (!initializeUpstreamFloodTest()) { - return; - } + initializeUpstreamFloodTest(); // pre-fill 1 away from overflow auto response = prefillOutboundUpstreamQueue(ControlFrameFloodLimit); @@ -1464,9 +1432,7 @@ TEST_P(Http2FloodMitigationTest, RequestMetadata) { [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { hcm.mutable_http2_protocol_options()->set_allow_metadata(true); }); - if (!initializeUpstreamFloodTest()) { - return; - } + initializeUpstreamFloodTest(); codec_client_ = makeHttpConnection(lookupPort("http")); auto encoder_decoder = codec_client_->startRequest(default_request_headers_); diff --git a/tools/code_format/check_format.py b/tools/code_format/check_format.py index 09744dc0e2831..ac6f2d3e9f898 100755 --- a/tools/code_format/check_format.py +++ b/tools/code_format/check_format.py @@ -267,7 +267,6 @@ UNSORTED_FLAGS = { "envoy.reloadable_features.activate_timers_next_event_loop", "envoy.reloadable_features.grpc_json_transcoder_adhere_to_buffer_limits", - "envoy.reloadable_features.upstream_http2_flood_checks", "envoy.reloadable_features.sanitize_http_header_referer", } # yapf: enable From 5c03ee0931fb557dbb9a65888a9cf4b62a59aa58 Mon Sep 17 00:00:00 2001 From: Rohit Agrawal Date: Thu, 18 Nov 2021 17:59:54 -0500 Subject: [PATCH 109/110] http: added support for %REQUESTED_SERVER_NAME% to extract SNI as a custom header (Fixes #19024) (#19038) This PR adds a new command operator in the header formatter called `%REQUESTED_SERVER_NAME%` which can be used to extract the SNI information from the stream and set it as a custom header. **Commit Message:** Added support for `%REQUESTED_SERVER_NAME%` to extract the SNI as a custom header. **Additional Description:** Adds a new command operator called `%REQUESTED_SERVER_NAME%` in the http header formatter which can be used to extract the SNI information from the stream and set it as a custom header. **Risk Level:** Low **Testing:** Unit Tests **Docs Changes:** Added description on `%REQUESTED_SERVER_NAME%` in the docs. **Release Notes:** Added **Platform Specific Features:** N/A Signed-off-by: Rohit Agrawal --- .../configuration/http/http_conn_man/headers.rst | 6 ++++++ docs/root/version_history/current.rst | 1 + source/common/router/header_formatter.cc | 4 ++++ test/common/router/header_formatter_test.cc | 14 ++++++++++++++ 4 files changed, 25 insertions(+) diff --git a/docs/root/configuration/http/http_conn_man/headers.rst b/docs/root/configuration/http/http_conn_man/headers.rst index de03c5e89ca20..599b0357c73fd 100644 --- a/docs/root/configuration/http/http_conn_man/headers.rst +++ b/docs/root/configuration/http/http_conn_man/headers.rst @@ -682,6 +682,12 @@ Supported variable names are: The original protocol which is already added by Envoy as a :ref:`x-forwarded-proto ` request header. +%REQUESTED_SERVER_NAME% + HTTP + String value set on ssl connection socket for Server Name Indication (SNI) + TCP + String value set on ssl connection socket for Server Name Indication (SNI) + %UPSTREAM_METADATA(["namespace", "key", ...])% Populates the header with :ref:`EDS endpoint metadata ` from the upstream host selected by the router. Metadata may be selected from any namespace. In general, diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index b9b9cbbf47171..71f0ae1b60688 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -64,6 +64,7 @@ New Features * dns_resolver: added :ref:`CaresDnsResolverConfig` to support c-ares DNS resolver as an extension. * dns_resolver: added :ref:`AppleDnsResolverConfig` to support apple DNS resolver as an extension. * ext_authz: added :ref:`query_parameters_to_set ` and :ref:`query_parameters_to_remove ` for adding and removing query string parameters when using a gRPC authorization server. +* http: added support for %REQUESTED_SERVER_NAME% to extract SNI as a custom header. * http: added support for :ref:`retriable health check status codes `. * http: added timing information about upstream connection and encryption establishment to stream info. These can currently be accessed via custom access loggers. * listener: added API for extensions to access :ref:`typed_filter_metadata ` configured in the listener's :ref:`metadata ` field. diff --git a/source/common/router/header_formatter.cc b/source/common/router/header_formatter.cc index bcc32857d5db5..009e706f5009c 100644 --- a/source/common/router/header_formatter.cc +++ b/source/common/router/header_formatter.cc @@ -241,6 +241,10 @@ StreamInfoHeaderFormatter::StreamInfoHeaderFormatter(absl::string_view field_nam return Envoy::Formatter::SubstitutionFormatUtils::protocolToStringOrDefault( stream_info.protocol()); }; + } else if (field_name == "REQUESTED_SERVER_NAME") { + field_extractor_ = [](const StreamInfo::StreamInfo& stream_info) -> std::string { + return std::string(stream_info.downstreamAddressProvider().requestedServerName()); + }; } else if (field_name == "DOWNSTREAM_REMOTE_ADDRESS") { field_extractor_ = [](const StreamInfo::StreamInfo& stream_info) { return stream_info.downstreamAddressProvider().remoteAddress()->asString(); diff --git a/test/common/router/header_formatter_test.cc b/test/common/router/header_formatter_test.cc index d1c262c284341..1970b16b2a4e3 100644 --- a/test/common/router/header_formatter_test.cc +++ b/test/common/router/header_formatter_test.cc @@ -141,6 +141,17 @@ TEST_F(StreamInfoHeaderFormatterTest, TestFormatWithProtocolVariable) { testFormatting(stream_info, "PROTOCOL", "HTTP/1.1"); } +TEST_F(StreamInfoHeaderFormatterTest, TestFormatWithRequestedServerNameVariable) { + NiceMock stream_info; + // Validate for empty Request Server Name + testFormatting(stream_info, "REQUESTED_SERVER_NAME", ""); + + // Validate for a valid Request Server Name + const std::string requested_server_name = "foo.bar"; + stream_info.downstream_connection_info_provider_->setRequestedServerName(requested_server_name); + testFormatting(stream_info, "REQUESTED_SERVER_NAME", requested_server_name); +} + TEST_F(StreamInfoHeaderFormatterTest, TestFormatWithDownstreamPeerUriSanVariableSingleSan) { NiceMock stream_info; auto connection_info = std::make_shared>(); @@ -835,6 +846,7 @@ TEST(HeaderParserTest, TestParseInternal) { {"%UPSTREAM_METADATA( \t [ \t \"ns\" \t , \t \"key\" \t ] \t )%", {"value"}, {}}, {R"EOF(%UPSTREAM_METADATA(["\"quoted\"", "\"key\""])%)EOF", {"value"}, {}}, {"%UPSTREAM_REMOTE_ADDRESS%", {"10.0.0.1:443"}, {}}, + {"%REQUESTED_SERVER_NAME%", {"foo.bar"}, {}}, {"%PER_REQUEST_STATE(testing)%", {"test_value"}, {}}, {"%REQ(x-request-id)%", {"123"}, {}}, {"%START_TIME%", {"2018-04-03T23:06:09.123Z"}, {}}, @@ -932,6 +944,8 @@ TEST(HeaderParserTest, TestParseInternal) { }; NiceMock stream_info; + const std::string requested_server_name = "foo.bar"; + stream_info.downstream_connection_info_provider_->setRequestedServerName(requested_server_name); absl::optional protocol = Envoy::Http::Protocol::Http11; ON_CALL(stream_info, protocol()).WillByDefault(ReturnPointee(&protocol)); From a18b323540a5c41c36762627f765710e1f280644 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Nov 2021 00:15:58 -0500 Subject: [PATCH 110/110] build(deps): bump grpcio in /examples/grpc-bridge/client (#19049) Bumps [grpcio](https://github.com/grpc/grpc) from 1.41.1 to 1.42.0. - [Release notes](https://github.com/grpc/grpc/releases) - [Changelog](https://github.com/grpc/grpc/blob/master/doc/grpc_release_schedule.md) - [Commits](https://github.com/grpc/grpc/compare/v1.41.1...v1.42.0) --- updated-dependencies: - dependency-name: grpcio dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/grpc-bridge/client/requirements.txt | 90 ++++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/examples/grpc-bridge/client/requirements.txt b/examples/grpc-bridge/client/requirements.txt index 87771cf2fb87c..e244653d8eb49 100644 --- a/examples/grpc-bridge/client/requirements.txt +++ b/examples/grpc-bridge/client/requirements.txt @@ -12,51 +12,51 @@ charset-normalizer==2.0.6 \ --hash=sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6 \ --hash=sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f # via requests -grpcio==1.41.1 \ - --hash=sha256:0aa1af3e1480b6dd3092ee67c4b67b1ea88d638fcdc4d1a611ae11e311800b34 \ - --hash=sha256:0c075616d5e86fb65fd4784d5a87d6e5a1882d277dce5c33d9b67cfc71d79899 \ - --hash=sha256:133fb9a3cf4519543e4e41eb18b5dac0da26941aeabca8122dbcf3decbad2d21 \ - --hash=sha256:23a3f03e1d9ac429ff78d23d2ab07756d3728ee1a68b5f244d8435006608b6aa \ - --hash=sha256:2a34c8979de10b04a44d2cad07d41d83643e65e49f84a05b1adf150aeb41c95f \ - --hash=sha256:2eb8180a6d9e47fc865a4e92a2678f3202145021ef2c1bccf165fa5744f6ec95 \ - --hash=sha256:2f2ee78a6ae88d668ceda56fa4a18d8a38b34c2f2e1332083dd1da1a92870703 \ - --hash=sha256:31a47af7356fb5ed3120636dd75c5efb571ecf15737484119e31286687f0e52a \ - --hash=sha256:3213dfe3abfc3fda7f30e86aa5967dce0c2eb4cc90a0504f95434091bf6b219a \ - --hash=sha256:32b7ca83f1a6929217098aaaac89fc49879ae714c95501d40df41a0e7506164c \ - --hash=sha256:3713e3918da6ae10812a64e75620a172f01af2ff0a1c99d6481c910e1d4a9053 \ - --hash=sha256:3b4b7c1ab18283eb64af5648d20eabef9237a2aec09e30a805f18adc9497258d \ - --hash=sha256:3f0b70cf8632028714a8341b841b011a47900b1c163bf5fababb4ab3888c9b6c \ - --hash=sha256:61aa02f4505c5bbbaeba80fef1bd6871d1aef05a8778a707ab91303ee0865ad0 \ - --hash=sha256:6ca497ccecaa8727f14c4ccc9ffb70a19c6413fe1d4650500c90a7febd662860 \ - --hash=sha256:71d9ed5a732a54b9c87764609f2fd2bc4ae72fa85e271038eb132ea723222209 \ - --hash=sha256:72d0bdc3605dc8f4187b302e1180643963896e3f2917a52becb51afb54448e3e \ - --hash=sha256:734690b3f35468f8ed4003ec7622d2d47567f1881f5fcdca34f1e52551c2ef55 \ - --hash=sha256:740f5b21a7108a8c08bf522434752dc1d306274d47ca8b4d51af5588a16b6113 \ - --hash=sha256:766f1b943abc3e27842b72fba6e28fb9f57c9b84029fd7e91146e4c37034d937 \ - --hash=sha256:788154b32bf712e9711d001df024af5f7b2522117876c129bb27b9ad6e5461fb \ - --hash=sha256:7a22a7378ea59ad1e6f2e79f9da6862eb9e1f6586253aee784d419a49e3f4bd9 \ - --hash=sha256:8487fb0649ebebc9c5dca1a6dc4eb7fddf701183426b3eefeb3584639d223d43 \ - --hash=sha256:8824b36e6b0e45fefe0b4eac5ad460830e0cbc856a0c794f711289b4b8933d53 \ - --hash=sha256:888d8519709652dd39415de5f79abd50257201b345dd4f40151feffc3dad3232 \ - --hash=sha256:9170b5d2082fc00c057c6ccd6b893033c1ade05717fcec1d63557c3bc7afdb1b \ - --hash=sha256:9b751271b029432a526a4970dc9b70d93eb6f0963b6a841b574f780b72651969 \ - --hash=sha256:9d1be99f216b18f8a9dbdfbdbcc9a6caee504d0d27295fdbb5c8da35f5254a69 \ - --hash=sha256:9e403d07d77ed4495ad3c18994191525b11274693e72e464241c9139e2f9cd7c \ - --hash=sha256:a3bb4302389b23f2006ecaaea5eb4a39cc80ea98d1964159e59c1c20ef39a483 \ - --hash=sha256:a5ac91db3c588296366554b2d91116fc3a9f05bae516cafae07220e1f05bfef7 \ - --hash=sha256:b1232c5efc8a9e4b7a13db235c51135412beb9e62e618a2a89dd0463edb3d929 \ - --hash=sha256:b8dd1b6456c6fb3681affe0f81dff4b3bc46f825fc05e086d64216545da9ad92 \ - --hash=sha256:c32c470e077b34a52e87e7de26644ad0f9e9ff89a785ff7e6466870869659e05 \ - --hash=sha256:c35b847bc6bd3c3a118a13277d91a772e7dd163ce7dd2791239f9941b6eaafe3 \ - --hash=sha256:c3a446b6a1f8077cc03d0d496fc1cecdd3d0b66860c0c5b65cc92d0549117840 \ - --hash=sha256:d1461672b2eaef9affb60a71014ebd2f789deea7c9acb1d4bd163de92dd8e044 \ - --hash=sha256:e156ea12adb7a7ca8d8280c9df850c15510b790c785fc26c9a3fb928cd221fd4 \ - --hash=sha256:ead9885b53777bed4b0694ff0baea9d2c519ff774b17b177bde43d73e2b4aa38 \ - --hash=sha256:ebbe9582ef06559a2358827a588ab4b92a2639517de8fe428288772820ab03b5 \ - --hash=sha256:f68aa98f5970eccb6c94456f3447a99916c42fbddae1971256bc4e7c40a6593b \ - --hash=sha256:fc2eadfb5ec956c556c138fab0dfc1d2395c57ae0bfea047edae1976a26b250c \ - --hash=sha256:fd11995e3402af0f838844194707da8b3235f1719bcac961493f0138f1325893 \ - --hash=sha256:fd570720871dc84d2adc8430ce287319c9238d1e2f70c140f9bc54c690fabd1b +grpcio==1.42.0 \ + --hash=sha256:0209f30741de1875413f40e89bec9c647e7afad4a3549a6a1682c1ee23da68ca \ + --hash=sha256:06d5364e85e0fa50ee68bffd9c93a6aff869a91c68f1fd7ba1b944e063a0ff9f \ + --hash=sha256:17433f7eb01737240581b33fbc2eae7b7fa6d3429571782580bceaf05ec56cb8 \ + --hash=sha256:21aa4a111b3381d3dd982a3df62348713b29f651aa9f6dfbc9415adbfe28d2ba \ + --hash=sha256:2956da789d74fc35d2c869b3aa45dbf41c5d862c056ca8b5e35a688347ede809 \ + --hash=sha256:29fc36c99161ff307c8ca438346b2e36f81dac5ecdbabc983d0b255d7913fb19 \ + --hash=sha256:2aba7f93671ec971c5c70db81633b49a2f974aa09a2d811aede344a32bad1896 \ + --hash=sha256:2b264cf303a22c46f8d455f42425c546ad6ce22f183debb8d64226ddf1e039f4 \ + --hash=sha256:3a13953e12dc40ee247b5fe6ef22b5fac8f040a76b814a11bf9f423e82402f28 \ + --hash=sha256:47ab65be9ba7a0beee94bbe2fb1dd03cb7832db9df4d1f8fae215a16b3edeb5e \ + --hash=sha256:4a8f2c7490fe3696e0cdd566e2f099fb91b51bc75446125175c55581c2f7bc11 \ + --hash=sha256:53e10d07e541073eb9a84d49ecffb831c3cbb970bcd8cd8de8431e935bf66c2e \ + --hash=sha256:5441d343602ce10ba48fcb36bb5de197a15a01dc9ee0f71c2a73cd5cd3d7f5ac \ + --hash=sha256:59163b8d2e0d85f0ecbee52b348f867eec7e0f909177564fb3956363f7e616e5 \ + --hash=sha256:5b9f0c4822e3a52a1663a315752c6bbdbed0ec15a660d3e64137335acbb5b7ce \ + --hash=sha256:603d71de14ab1f1fd1254b69ceda73545943461b1f51f82fda9477503330b6ea \ + --hash=sha256:64f2b3e6474e2ad865478b64f0850d15842acbb2623de5f78a60ceabe00c63e0 \ + --hash=sha256:65720d2bf05e2b78c4bffe372f13c41845bae5658fd3f5dd300c374dd240e5cb \ + --hash=sha256:6655df5f31664bac4cd6c9b52f389fd92cd10025504ad83685038f47e11e29d8 \ + --hash=sha256:66f910b6324ae69625e63db2eb29d833c307cfa36736fe13d2f841656c5f658f \ + --hash=sha256:6b69726d7bbb633133c1b0d780b1357aa9b7a7f714fead6470bab1feb8012806 \ + --hash=sha256:6e5eec67909795f7b1ff2bd941bd6c2661ca5217ea9c35003d73314100786f60 \ + --hash=sha256:6ef72f0abdb89fb7c366a99e04823ecae5cda9f762f2234f42fc280447277cd6 \ + --hash=sha256:76b5fa4c6d88f804456e763461cf7a1db38b200669f1ba00c579014ab5aa7965 \ + --hash=sha256:7742606ac2bc03ed10360f4f630e0cc01dce864fe63557254e9adea21bb51416 \ + --hash=sha256:7a3c9b8e13365529f9426d4754085e8a9c2ad718a41a46a97e4e30e87bb45eae \ + --hash=sha256:8e8cd9909fdd232ecffb954936fd90c935ebe0b5fce36c88813f8247ce54019c \ + --hash=sha256:a6f9ed5320b93c029615b75f6c8caf2c76aa6545d8845f3813908892cfc5f84e \ + --hash=sha256:b4d7115ee08a36f3f50a6233bd78280e40847e078d2a5bb39c0ab0db4490d58f \ + --hash=sha256:b74bbac7e039cf23ed0c8edd820c31e90a52a22e28a03d45274a0956addde8d2 \ + --hash=sha256:b781f412546830be55644f7c48251d30860f4725981862d4a1ea322f80d9cd34 \ + --hash=sha256:bf916ee93ea2fd52b5286ed4e19cbbde5e82754914379ea91dc5748550df3b4e \ + --hash=sha256:d08ce780bbd8d1a442d855bd681ed0f7483c65d2c8ed83701a9ea4f13678411f \ + --hash=sha256:d1451a8c0c01c5b5fdfeb8f777820cb277fb5d797d972f57a41bb82483c44a79 \ + --hash=sha256:d58b3774ee2084c31aad140586a42e18328d9823959ca006a0b85ad7937fe405 \ + --hash=sha256:d6c0b159b38fcc3bbc3331105197c1f58ac0d294eb83910d136a325a85def88f \ + --hash=sha256:d7f66eb220898787d7821a7931e35ae2512ed74f79f75adcd7ea2fb3119ca87d \ + --hash=sha256:d92c1721c7981812d0f42dfc8248b15d3b6a2ea79eb8870776364423de2aa245 \ + --hash=sha256:e2d9c6690d4c88cd51ee395d7ba5bd1d26d7c37e94cb59e7fd62ff21ecaf891d \ + --hash=sha256:e62140c46d8125927c673c72c960cb387c02b2a1a3c6985a8b0a3914d27c0018 \ + --hash=sha256:ea3560ffbfe08327024380508190103937fef25e355d2259f8b5c003a0732f55 \ + --hash=sha256:f2e3f250e5398bf474c6e140df1b67494bf1e31c5277b5bf93841a564cbc22d0 \ + --hash=sha256:f385e40846ff81d1c6dce98dcc192c7988a46540758804c4a2e6da5a0e3e3e05 \ + --hash=sha256:f721b42a20d886c03d9b1f461b228cdaf02ccf6c4550e263f7fd3ce3ff19a8f1 # via # -r requirements.in # grpcio-tools